From bebaa8a29da776739f7d507d4e6a679656e687f2 Mon Sep 17 00:00:00 2001 From: binarycat Date: Wed, 9 Apr 2025 12:43:06 -0500 Subject: [PATCH 001/682] add sitemap to rust docs --- src/bootstrap/src/core/build_steps/dist.rs | 1 + src/doc/robots.txt | 2 ++ src/doc/sitemap.txt | 4 ++++ 3 files changed, 7 insertions(+) create mode 100644 src/doc/sitemap.txt diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 83f71aeed720..6ec39ffc4366 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -82,6 +82,7 @@ impl Step for Docs { tarball.set_product_name("Rust Documentation"); tarball.add_bulk_dir(builder.doc_out(host), dest); tarball.add_file(builder.src.join("src/doc/robots.txt"), dest, FileType::Regular); + tarball.add_file(builder.src.join("src/doc/sitemap.txt"), dest, FileType::Regular); Some(tarball.generate()) } } diff --git a/src/doc/robots.txt b/src/doc/robots.txt index 3a2552e9a159..71a60f89c14c 100644 --- a/src/doc/robots.txt +++ b/src/doc/robots.txt @@ -9,3 +9,5 @@ Disallow: /beta/book/first-edition/ Disallow: /beta/book/second-edition/ Disallow: /nightly/book/first-edition/ Disallow: /nightly/book/second-edition/ + +Sitemap: https://doc.rust-lang.org/sitemap.txt diff --git a/src/doc/sitemap.txt b/src/doc/sitemap.txt new file mode 100644 index 000000000000..6e5f0fbad37f --- /dev/null +++ b/src/doc/sitemap.txt @@ -0,0 +1,4 @@ +https://doc.rust-lang.org/stable/ +https://doc.rust-lang.org/beta/ +https://doc.rust-lang.org/nightly/ + From a22c8ed6311979d292e835e5a5498d0e3cc95dc0 Mon Sep 17 00:00:00 2001 From: Thalia Archibald Date: Sat, 12 Apr 2025 16:33:38 -0700 Subject: [PATCH 002/682] zkVM: Fix env::ArgsOs The zkVM implementation of `env::ArgsOs` incorrectly reports the full length even after having iterated. Instead, use a range approach which works out to be simpler. Also, implement more iterator methods like the other platforms in #139847. --- library/std/src/sys/args/zkvm.rs | 71 ++++++++++++++++++++++++-------- 1 file changed, 54 insertions(+), 17 deletions(-) diff --git a/library/std/src/sys/args/zkvm.rs b/library/std/src/sys/args/zkvm.rs index 194ba7159d45..e4b1ad540ac0 100644 --- a/library/std/src/sys/args/zkvm.rs +++ b/library/std/src/sys/args/zkvm.rs @@ -1,18 +1,19 @@ use crate::ffi::OsString; use crate::fmt; +use crate::num::NonZero; use crate::sys::os_str; use crate::sys::pal::{WORD_SIZE, abi}; use crate::sys_common::FromInner; +#[derive(Clone)] pub struct Args { - i_forward: usize, - i_back: usize, - count: usize, + front: usize, + back: usize, } pub fn args() -> Args { let count = unsafe { abi::sys_argc() }; - Args { i_forward: 0, i_back: 0, count } + Args { front: 0, back: count } } impl Args { @@ -38,44 +39,80 @@ impl Args { } } +impl !Send for Args {} +impl !Sync for Args {} + impl fmt::Debug for Args { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().finish() + f.debug_list().entries(self.clone()).finish() } } impl Iterator for Args { type Item = OsString; + #[inline] fn next(&mut self) -> Option { - if self.i_forward >= self.count - self.i_back { + if self.front == self.back { None } else { - let arg = Self::argv(self.i_forward); - self.i_forward += 1; + let arg = Self::argv(self.front); + self.front += 1; Some(arg) } } + #[inline] fn size_hint(&self) -> (usize, Option) { - (self.count, Some(self.count)) + let len = self.len(); + (len, Some(len)) } -} -impl ExactSizeIterator for Args { - fn len(&self) -> usize { - self.count + #[inline] + fn count(self) -> usize { + self.len() + } + + #[inline] + fn last(mut self) -> Option { + self.next_back() + } + + #[inline] + fn advance_by(&mut self, n: usize) -> Result<(), NonZero> { + let step_size = self.len().min(n); + self.front += step_size; + NonZero::new(n - step_size).map_or(Ok(()), Err) } } impl DoubleEndedIterator for Args { + #[inline] fn next_back(&mut self) -> Option { - if self.i_back >= self.count - self.i_forward { + if self.back == self.front { None } else { - let arg = Self::argv(self.count - 1 - self.i_back); - self.i_back += 1; - Some(arg) + self.back -= 1; + Some(Self::argv(self.back)) } } + + #[inline] + fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { + let step_size = self.len().min(n); + self.back -= step_size; + NonZero::new(n - step_size).map_or(Ok(()), Err) + } +} + +impl ExactSizeIterator for Args { + #[inline] + fn len(&self) -> usize { + self.back - self.front + } + + #[inline] + fn is_empty(&self) -> bool { + self.front == self.back + } } From 00095982a0d2f6c8bfb5cb2706338ad2b14304f5 Mon Sep 17 00:00:00 2001 From: Thalia Archibald Date: Tue, 15 Apr 2025 02:41:51 -0700 Subject: [PATCH 003/682] zkVM: Cache args from host Retrieve argc/argv from the host once, on demand when the first `env::ArgsOs` is constructed, and globally cache it. Copy each argument to an `OsString` while iterating. --- library/std/src/sys/args/zkvm.rs | 86 ++++++++++++-------------------- 1 file changed, 31 insertions(+), 55 deletions(-) diff --git a/library/std/src/sys/args/zkvm.rs b/library/std/src/sys/args/zkvm.rs index e4b1ad540ac0..d26bf1eaff91 100644 --- a/library/std/src/sys/args/zkvm.rs +++ b/library/std/src/sys/args/zkvm.rs @@ -1,26 +1,20 @@ -use crate::ffi::OsString; -use crate::fmt; +use crate::ffi::{OsStr, OsString}; use crate::num::NonZero; -use crate::sys::os_str; +use crate::sync::OnceLock; use crate::sys::pal::{WORD_SIZE, abi}; -use crate::sys_common::FromInner; - -#[derive(Clone)] -pub struct Args { - front: usize, - back: usize, -} +use crate::{fmt, ptr, slice}; pub fn args() -> Args { - let count = unsafe { abi::sys_argc() }; - Args { front: 0, back: count } + Args { iter: ARGS.get_or_init(|| get_args()).iter() } } -impl Args { - /// Use sys_argv to get the arg at the requested index. Does not check that i is less than argc - /// and will not return if the index is out of bounds. - fn argv(i: usize) -> OsString { - let arg_len = unsafe { abi::sys_argv(crate::ptr::null_mut(), 0, i) }; +fn get_args() -> Vec<&'static OsStr> { + let argc = unsafe { abi::sys_argc() }; + let mut args = Vec::with_capacity(argc); + + for i in 0..argc { + // Get the size of the argument then the data. + let arg_len = unsafe { abi::sys_argv(ptr::null_mut(), 0, i) }; let arg_len_words = (arg_len + WORD_SIZE - 1) / WORD_SIZE; let words = unsafe { abi::sys_alloc_words(arg_len_words) }; @@ -28,15 +22,16 @@ impl Args { let arg_len2 = unsafe { abi::sys_argv(words, arg_len_words, i) }; debug_assert_eq!(arg_len, arg_len2); - // Convert to OsString. - // - // FIXME: We can probably get rid of the extra copy here if we - // reimplement "os_str" instead of just using the generic unix - // "os_str". - let arg_bytes: &[u8] = - unsafe { crate::slice::from_raw_parts(words.cast() as *const u8, arg_len) }; - OsString::from_inner(os_str::Buf { inner: arg_bytes.to_vec() }) + let arg_bytes = unsafe { slice::from_raw_parts(words.cast(), arg_len) }; + args.push(unsafe { OsStr::from_encoded_bytes_unchecked(arg_bytes) }); } + args +} + +static ARGS: OnceLock> = OnceLock::new(); + +pub struct Args { + iter: slice::Iter<'static, &'static OsStr>, } impl !Send for Args {} @@ -44,75 +39,56 @@ impl !Sync for Args {} impl fmt::Debug for Args { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_list().entries(self.clone()).finish() + self.iter.as_slice().fmt(f) } } impl Iterator for Args { type Item = OsString; - #[inline] fn next(&mut self) -> Option { - if self.front == self.back { - None - } else { - let arg = Self::argv(self.front); - self.front += 1; - Some(arg) - } + self.iter.next().map(|arg| arg.to_os_string()) } #[inline] fn size_hint(&self) -> (usize, Option) { - let len = self.len(); - (len, Some(len)) + self.iter.size_hint() } #[inline] fn count(self) -> usize { - self.len() + self.iter.len() } - #[inline] - fn last(mut self) -> Option { - self.next_back() + fn last(self) -> Option { + self.iter.last().map(|arg| arg.to_os_string()) } #[inline] fn advance_by(&mut self, n: usize) -> Result<(), NonZero> { - let step_size = self.len().min(n); - self.front += step_size; - NonZero::new(n - step_size).map_or(Ok(()), Err) + self.iter.advance_by(n) } } impl DoubleEndedIterator for Args { - #[inline] fn next_back(&mut self) -> Option { - if self.back == self.front { - None - } else { - self.back -= 1; - Some(Self::argv(self.back)) - } + self.iter.next_back().map(|arg| arg.to_os_string()) } #[inline] fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero> { - let step_size = self.len().min(n); - self.back -= step_size; - NonZero::new(n - step_size).map_or(Ok(()), Err) + self.iter.advance_back_by(n) } } impl ExactSizeIterator for Args { #[inline] fn len(&self) -> usize { - self.back - self.front + self.iter.len() } #[inline] fn is_empty(&self) -> bool { - self.front == self.back + self.iter.is_empty() } } From 4acf3baecda9c5080bcfdc1ebb4560fbb2a4567e Mon Sep 17 00:00:00 2001 From: Martin Pool Date: Fri, 20 Jun 2025 13:18:05 -0700 Subject: [PATCH 004/682] libtest: expose --fail-fast --- library/test/src/cli.rs | 4 +++- src/doc/rustc/src/tests/index.md | 10 ++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/library/test/src/cli.rs b/library/test/src/cli.rs index 8840714a6623..d19141b8c2c7 100644 --- a/library/test/src/cli.rs +++ b/library/test/src/cli.rs @@ -57,6 +57,7 @@ fn optgroups() -> getopts::Options { .optflag("", "test", "Run tests and not benchmarks") .optflag("", "bench", "Run benchmarks instead of tests") .optflag("", "list", "List all tests and benchmarks") + .optflag("", "fail-fast", "Don't start new tests after the first failure") .optflag("h", "help", "Display this message") .optopt("", "logfile", "Write logs to the specified file (deprecated)", "PATH") .optflag( @@ -270,6 +271,7 @@ fn parse_opts_impl(matches: getopts::Matches) -> OptRes { let exact = matches.opt_present("exact"); let list = matches.opt_present("list"); let skip = matches.opt_strs("skip"); + let fail_fast = matches.opt_present("fail-fast"); let bench_benchmarks = matches.opt_present("bench"); let run_tests = !bench_benchmarks || matches.opt_present("test"); @@ -307,7 +309,7 @@ fn parse_opts_impl(matches: getopts::Matches) -> OptRes { skip, time_options, options, - fail_fast: false, + fail_fast, }; Ok(test_opts) diff --git a/src/doc/rustc/src/tests/index.md b/src/doc/rustc/src/tests/index.md index 12de69a4c9ee..7033184e52aa 100644 --- a/src/doc/rustc/src/tests/index.md +++ b/src/doc/rustc/src/tests/index.md @@ -158,6 +158,16 @@ unstable-options` flag. See [tracking issue The following options affect how tests are executed. +#### `--fail-fast` + +Stops tests after the first failure. + +If running tests in parallel (which is the default), then tests that have already been started on +other threads will be allowed to run to completion before the process exits. + +Note that when running tests in parallel, the test execution order is non-deterministic: +if multiple tests would fail, the first failure encountered will be reported. + #### `--test-threads` _NUM_THREADS_ Sets the number of threads to use for running tests in parallel. By default, From 547c72901ad145b40d7271e3ddef2b4ac0c53619 Mon Sep 17 00:00:00 2001 From: Martin Pool Date: Fri, 20 Jun 2025 13:27:53 -0700 Subject: [PATCH 005/682] Make fail-fast unstable --- library/test/src/cli.rs | 2 +- src/doc/rustc/src/tests/index.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/library/test/src/cli.rs b/library/test/src/cli.rs index d19141b8c2c7..ea91ebc4e987 100644 --- a/library/test/src/cli.rs +++ b/library/test/src/cli.rs @@ -262,6 +262,7 @@ fn parse_opts_impl(matches: getopts::Matches) -> OptRes { // Unstable flags let force_run_in_process = unstable_optflag!(matches, allow_unstable, "force-run-in-process"); let exclude_should_panic = unstable_optflag!(matches, allow_unstable, "exclude-should-panic"); + let fail_fast = unstable_optflag!(matches, allow_unstable, "fail-fast"); let time_options = get_time_options(&matches, allow_unstable)?; let shuffle = get_shuffle(&matches, allow_unstable)?; let shuffle_seed = get_shuffle_seed(&matches, allow_unstable)?; @@ -271,7 +272,6 @@ fn parse_opts_impl(matches: getopts::Matches) -> OptRes { let exact = matches.opt_present("exact"); let list = matches.opt_present("list"); let skip = matches.opt_strs("skip"); - let fail_fast = matches.opt_present("fail-fast"); let bench_benchmarks = matches.opt_present("bench"); let run_tests = !bench_benchmarks || matches.opt_present("test"); diff --git a/src/doc/rustc/src/tests/index.md b/src/doc/rustc/src/tests/index.md index 7033184e52aa..ab96fbcd3178 100644 --- a/src/doc/rustc/src/tests/index.md +++ b/src/doc/rustc/src/tests/index.md @@ -168,6 +168,8 @@ other threads will be allowed to run to completion before the process exits. Note that when running tests in parallel, the test execution order is non-deterministic: if multiple tests would fail, the first failure encountered will be reported. +⚠️ 🚧 This requires the `-Z unstable-options` flag. + #### `--test-threads` _NUM_THREADS_ Sets the number of threads to use for running tests in parallel. By default, From c40e004a6bf4750c4bd03945852f2d5388d30d3b Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Wed, 2 Jul 2025 07:25:46 +0200 Subject: [PATCH 006/682] gai: remove typo from test file, and add a reference id --- .../generic_arg_infer/{parend_infer.rs => paren_infer.rs} | 1 + 1 file changed, 1 insertion(+) rename tests/ui/const-generics/generic_arg_infer/{parend_infer.rs => paren_infer.rs} (94%) diff --git a/tests/ui/const-generics/generic_arg_infer/parend_infer.rs b/tests/ui/const-generics/generic_arg_infer/paren_infer.rs similarity index 94% rename from tests/ui/const-generics/generic_arg_infer/parend_infer.rs rename to tests/ui/const-generics/generic_arg_infer/paren_infer.rs index 9d7df8016cb1..869683b70566 100644 --- a/tests/ui/const-generics/generic_arg_infer/parend_infer.rs +++ b/tests/ui/const-generics/generic_arg_infer/paren_infer.rs @@ -1,4 +1,5 @@ //@ check-pass +//@ reference: items.generics.const.inferred struct Foo; From 81e8c6797cc3c94134d6fce11ddb38e03be8c85a Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 15 Jul 2025 07:18:56 -0400 Subject: [PATCH 007/682] `redundant_clone`: split iterator checks into `redundant_iter_cloned` lint --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + .../src/methods/iter_overeager_cloned.rs | 5 ++-- clippy_lints/src/methods/mod.rs | 26 +++++++++++++++++++ tests/ui/iter_overeager_cloned.fixed | 12 ++++----- tests/ui/iter_overeager_cloned.rs | 12 ++++----- tests/ui/iter_overeager_cloned.stderr | 4 +-- 7 files changed, 44 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a92fbdc767bd..8d770ad2a338 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6304,6 +6304,7 @@ Released 2018-09-13 [`redundant_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_feature_names [`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names [`redundant_guards`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_guards +[`redundant_iter_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_iter_cloned [`redundant_locals`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_locals [`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern [`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index c3f8e02b4c06..233a1c42841b 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -449,6 +449,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::READONLY_WRITE_LOCK_INFO, crate::methods::READ_LINE_WITHOUT_TRIM_INFO, crate::methods::REDUNDANT_AS_STR_INFO, + crate::methods::REDUNDANT_ITER_CLONED_INFO, crate::methods::REPEAT_ONCE_INFO, crate::methods::RESULT_FILTER_MAP_INFO, crate::methods::RESULT_MAP_OR_INTO_OPTION_INFO, diff --git a/clippy_lints/src/methods/iter_overeager_cloned.rs b/clippy_lints/src/methods/iter_overeager_cloned.rs index f5fe4316eb0d..d43b22c2bd51 100644 --- a/clippy_lints/src/methods/iter_overeager_cloned.rs +++ b/clippy_lints/src/methods/iter_overeager_cloned.rs @@ -10,8 +10,7 @@ use rustc_middle::mir::{FakeReadCause, Mutability}; use rustc_middle::ty::{self, BorrowKind}; use rustc_span::{Symbol, sym}; -use super::ITER_OVEREAGER_CLONED; -use crate::redundant_clone::REDUNDANT_CLONE; +use super::{ITER_OVEREAGER_CLONED, REDUNDANT_ITER_CLONED}; #[derive(Clone, Copy)] pub(super) enum Op<'a> { @@ -96,7 +95,7 @@ pub(super) fn check<'tcx>( } let (lint, msg, trailing_clone) = match op { - Op::RmCloned | Op::NeedlessMove(_) => (REDUNDANT_CLONE, "unneeded cloning of iterator items", ""), + Op::RmCloned | Op::NeedlessMove(_) => (REDUNDANT_ITER_CLONED, "unneeded cloning of iterator items", ""), Op::LaterCloned | Op::FixClosure(_, _) => ( ITER_OVEREAGER_CLONED, "unnecessarily eager cloning of iterator items", diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index f2dabdd34387..c68caea89bbd 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -4565,6 +4565,31 @@ declare_clippy_lint! { "hardcoded localhost IP address" } +declare_clippy_lint! { + /// ### What it does + /// Checks for calls to `Iterator::cloned` where the original value could be used + /// instead. + /// + /// ### Why is this bad? + /// It is not always possible for the compiler to eliminate useless allocations and + /// deallocations generated by redundant `clone()`s. + /// + /// ### Example + /// ```no_run + /// let x = vec![String::new()]; + /// let _ = x.iter().cloned().map(|x| x.len()); + /// ``` + /// Use instead: + /// ```no_run + /// let x = vec![String::new()]; + /// let _ = x.iter().map(|x| x.len()); + /// ``` + #[clippy::version = "1.90.0"] + pub REDUNDANT_ITER_CLONED, + perf, + "detects redundant calls to `Iterator::cloned`" +} + #[expect(clippy::struct_excessive_bools)] pub struct Methods { avoid_breaking_exported_api: bool, @@ -4744,6 +4769,7 @@ impl_lint_pass!(Methods => [ IO_OTHER_ERROR, SWAP_WITH_TEMPORARY, IP_CONSTANT, + REDUNDANT_ITER_CLONED, ]); /// Extracts a method call name, args, and `Span` of the method name. diff --git a/tests/ui/iter_overeager_cloned.fixed b/tests/ui/iter_overeager_cloned.fixed index b0e548f17909..4171f19469a4 100644 --- a/tests/ui/iter_overeager_cloned.fixed +++ b/tests/ui/iter_overeager_cloned.fixed @@ -1,4 +1,4 @@ -#![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)] +#![warn(clippy::iter_overeager_cloned, clippy::redundant_iter_cloned, clippy::filter_next)] #![allow( dead_code, clippy::let_unit_value, @@ -16,7 +16,7 @@ fn main() { //~^ iter_overeager_cloned let _: usize = vec.iter().filter(|x| x == &"2").count(); - //~^ redundant_clone + //~^ redundant_iter_cloned let _: Vec<_> = vec.iter().take(2).cloned().collect(); //~^ iter_overeager_cloned @@ -77,19 +77,19 @@ fn main() { } let _ = vec.iter().map(|x| x.len()); - //~^ redundant_clone + //~^ redundant_iter_cloned // This would fail if changed. let _ = vec.iter().cloned().map(|x| x + "2"); let _ = vec.iter().for_each(|x| assert!(!x.is_empty())); - //~^ redundant_clone + //~^ redundant_iter_cloned let _ = vec.iter().all(|x| x.len() == 1); - //~^ redundant_clone + //~^ redundant_iter_cloned let _ = vec.iter().any(|x| x.len() == 1); - //~^ redundant_clone + //~^ redundant_iter_cloned // Should probably stay as it is. let _ = [0, 1, 2, 3, 4].iter().cloned().take(10); diff --git a/tests/ui/iter_overeager_cloned.rs b/tests/ui/iter_overeager_cloned.rs index cedf62a6b473..fe6aba24dd3e 100644 --- a/tests/ui/iter_overeager_cloned.rs +++ b/tests/ui/iter_overeager_cloned.rs @@ -1,4 +1,4 @@ -#![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)] +#![warn(clippy::iter_overeager_cloned, clippy::redundant_iter_cloned, clippy::filter_next)] #![allow( dead_code, clippy::let_unit_value, @@ -16,7 +16,7 @@ fn main() { //~^ iter_overeager_cloned let _: usize = vec.iter().filter(|x| x == &"2").cloned().count(); - //~^ redundant_clone + //~^ redundant_iter_cloned let _: Vec<_> = vec.iter().cloned().take(2).collect(); //~^ iter_overeager_cloned @@ -78,19 +78,19 @@ fn main() { } let _ = vec.iter().cloned().map(|x| x.len()); - //~^ redundant_clone + //~^ redundant_iter_cloned // This would fail if changed. let _ = vec.iter().cloned().map(|x| x + "2"); let _ = vec.iter().cloned().for_each(|x| assert!(!x.is_empty())); - //~^ redundant_clone + //~^ redundant_iter_cloned let _ = vec.iter().cloned().all(|x| x.len() == 1); - //~^ redundant_clone + //~^ redundant_iter_cloned let _ = vec.iter().cloned().any(|x| x.len() == 1); - //~^ redundant_clone + //~^ redundant_iter_cloned // Should probably stay as it is. let _ = [0, 1, 2, 3, 4].iter().cloned().take(10); diff --git a/tests/ui/iter_overeager_cloned.stderr b/tests/ui/iter_overeager_cloned.stderr index 1616dec95b79..f234d19e4aaa 100644 --- a/tests/ui/iter_overeager_cloned.stderr +++ b/tests/ui/iter_overeager_cloned.stderr @@ -25,8 +25,8 @@ LL | let _: usize = vec.iter().filter(|x| x == &"2").cloned().count(); | | | help: try: `.count()` | - = note: `-D clippy::redundant-clone` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::redundant_clone)]` + = note: `-D clippy::redundant-iter-cloned` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::redundant_iter_cloned)]` error: unnecessarily eager cloning of iterator items --> tests/ui/iter_overeager_cloned.rs:21:21 From 175afd76180d47b655377768a87a55f8581686f0 Mon Sep 17 00:00:00 2001 From: Thalia Archibald Date: Mon, 21 Apr 2025 23:27:01 -0700 Subject: [PATCH 008/682] Stabilize `new_zeroed_alloc` --- compiler/rustc_index/src/bit_set.rs | 4 ++-- compiler/rustc_index/src/lib.rs | 2 +- library/alloc/src/boxed.rs | 16 ++-------------- library/alloc/src/rc.rs | 16 ++-------------- library/alloc/src/sync.rs | 14 ++------------ library/std/src/lib.rs | 1 - 6 files changed, 9 insertions(+), 44 deletions(-) diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index 645d95b1dba9..0b3bc8963a3b 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -638,7 +638,7 @@ impl ChunkedBitSet { }; #[cfg(not(feature = "nightly"))] let mut words = { - // FIXME: unconditionally use `Rc::new_zeroed` once it is stable (#63291). + // FIXME: unconditionally use `Rc::new_zeroed` once it is stable (#129396). let words = mem::MaybeUninit::<[Word; CHUNK_WORDS]>::zeroed(); // SAFETY: `words` can safely be all zeroes. let words = unsafe { words.assume_init() }; @@ -704,7 +704,7 @@ impl ChunkedBitSet { }; #[cfg(not(feature = "nightly"))] let mut words = { - // FIXME: unconditionally use `Rc::new_zeroed` once it is stable (#63291). + // FIXME: unconditionally use `Rc::new_zeroed` once it is stable (#129396). let words = mem::MaybeUninit::<[Word; CHUNK_WORDS]>::zeroed(); // SAFETY: `words` can safely be all zeroes. let words = unsafe { words.assume_init() }; diff --git a/compiler/rustc_index/src/lib.rs b/compiler/rustc_index/src/lib.rs index cc680838e7e7..9d647209c6f8 100644 --- a/compiler/rustc_index/src/lib.rs +++ b/compiler/rustc_index/src/lib.rs @@ -1,9 +1,9 @@ // tidy-alphabetical-start #![cfg_attr(all(feature = "nightly", test), feature(stmt_expr_attributes))] +#![cfg_attr(bootstrap, feature(new_zeroed_alloc))] #![cfg_attr(feature = "nightly", allow(internal_features))] #![cfg_attr(feature = "nightly", feature(extend_one, step_trait, test))] #![cfg_attr(feature = "nightly", feature(new_range_api))] -#![cfg_attr(feature = "nightly", feature(new_zeroed_alloc))] // tidy-alphabetical-end pub mod bit_set; diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 3db37f1d16f3..173d0103c119 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -290,8 +290,6 @@ impl Box { /// # Examples /// /// ``` - /// #![feature(new_zeroed_alloc)] - /// /// let zero = Box::::new_zeroed(); /// let zero = unsafe { zero.assume_init() }; /// @@ -301,7 +299,7 @@ impl Box { /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] #[inline] - #[unstable(feature = "new_zeroed_alloc", issue = "129396")] + #[stable(feature = "new_zeroed_alloc", since = "CURRENT_RUSTC_VERSION")] #[must_use] pub fn new_zeroed() -> Box> { Self::new_zeroed_in(Global) @@ -358,7 +356,6 @@ impl Box { /// # Ok::<(), std::alloc::AllocError>(()) /// ``` #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn try_new_uninit() -> Result>, AllocError> { Box::try_new_uninit_in(Global) @@ -384,7 +381,6 @@ impl Box { /// /// [zeroed]: mem::MaybeUninit::zeroed #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn try_new_zeroed() -> Result>, AllocError> { Box::try_new_zeroed_in(Global) @@ -463,7 +459,6 @@ impl Box { #[unstable(feature = "allocator_api", issue = "32838")] #[cfg(not(no_global_oom_handling))] #[must_use] - // #[unstable(feature = "new_uninit", issue = "63291")] pub fn new_uninit_in(alloc: A) -> Box, A> where A: Allocator, @@ -496,7 +491,6 @@ impl Box { /// # Ok::<(), std::alloc::AllocError>(()) /// ``` #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] pub fn try_new_uninit_in(alloc: A) -> Result, A>, AllocError> where A: Allocator, @@ -532,7 +526,6 @@ impl Box { /// [zeroed]: mem::MaybeUninit::zeroed #[unstable(feature = "allocator_api", issue = "32838")] #[cfg(not(no_global_oom_handling))] - // #[unstable(feature = "new_uninit", issue = "63291")] #[must_use] pub fn new_zeroed_in(alloc: A) -> Box, A> where @@ -570,7 +563,6 @@ impl Box { /// /// [zeroed]: mem::MaybeUninit::zeroed #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] pub fn try_new_zeroed_in(alloc: A) -> Result, A>, AllocError> where A: Allocator, @@ -660,8 +652,6 @@ impl Box<[T]> { /// # Examples /// /// ``` - /// #![feature(new_zeroed_alloc)] - /// /// let values = Box::<[u32]>::new_zeroed_slice(3); /// let values = unsafe { values.assume_init() }; /// @@ -670,7 +660,7 @@ impl Box<[T]> { /// /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "new_zeroed_alloc", issue = "129396")] + #[stable(feature = "new_zeroed_alloc", since = "CURRENT_RUSTC_VERSION")] #[must_use] pub fn new_zeroed_slice(len: usize) -> Box<[mem::MaybeUninit]> { unsafe { RawVec::with_capacity_zeroed(len).into_box(len) } @@ -785,7 +775,6 @@ impl Box<[T], A> { /// ``` #[cfg(not(no_global_oom_handling))] #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[must_use] pub fn new_uninit_slice_in(len: usize, alloc: A) -> Box<[mem::MaybeUninit], A> { unsafe { RawVec::with_capacity_in(len, alloc).into_box(len) } @@ -813,7 +802,6 @@ impl Box<[T], A> { /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[must_use] pub fn new_zeroed_slice_in(len: usize, alloc: A) -> Box<[mem::MaybeUninit], A> { unsafe { RawVec::with_capacity_zeroed_in(len, alloc).into_box(len) } diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 5018ff4ad71f..0b5c9240db0f 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -515,8 +515,6 @@ impl Rc { /// # Examples /// /// ``` - /// #![feature(new_zeroed_alloc)] - /// /// use std::rc::Rc; /// /// let zero = Rc::::new_zeroed(); @@ -527,7 +525,7 @@ impl Rc { /// /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "new_zeroed_alloc", issue = "129396")] + #[stable(feature = "new_zeroed_alloc", since = "CURRENT_RUSTC_VERSION")] #[must_use] pub fn new_zeroed() -> Rc> { unsafe { @@ -589,7 +587,6 @@ impl Rc { /// # Ok::<(), std::alloc::AllocError>(()) /// ``` #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] pub fn try_new_uninit() -> Result>, AllocError> { unsafe { Ok(Rc::from_ptr(Rc::try_allocate_for_layout( @@ -622,7 +619,6 @@ impl Rc { /// /// [zeroed]: mem::MaybeUninit::zeroed #[unstable(feature = "allocator_api", issue = "32838")] - //#[unstable(feature = "new_uninit", issue = "63291")] pub fn try_new_zeroed() -> Result>, AllocError> { unsafe { Ok(Rc::from_ptr(Rc::try_allocate_for_layout( @@ -690,7 +686,6 @@ impl Rc { /// ``` #[cfg(not(no_global_oom_handling))] #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn new_uninit_in(alloc: A) -> Rc, A> { unsafe { @@ -728,7 +723,6 @@ impl Rc { /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn new_zeroed_in(alloc: A) -> Rc, A> { unsafe { @@ -873,7 +867,6 @@ impl Rc { /// # Ok::<(), std::alloc::AllocError>(()) /// ``` #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn try_new_uninit_in(alloc: A) -> Result, A>, AllocError> { unsafe { @@ -912,7 +905,6 @@ impl Rc { /// /// [zeroed]: mem::MaybeUninit::zeroed #[unstable(feature = "allocator_api", issue = "32838")] - //#[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn try_new_zeroed_in(alloc: A) -> Result, A>, AllocError> { unsafe { @@ -1054,8 +1046,6 @@ impl Rc<[T]> { /// # Examples /// /// ``` - /// #![feature(new_zeroed_alloc)] - /// /// use std::rc::Rc; /// /// let values = Rc::<[u32]>::new_zeroed_slice(3); @@ -1066,7 +1056,7 @@ impl Rc<[T]> { /// /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] - #[unstable(feature = "new_zeroed_alloc", issue = "129396")] + #[stable(feature = "new_zeroed_alloc", since = "CURRENT_RUSTC_VERSION")] #[must_use] pub fn new_zeroed_slice(len: usize) -> Rc<[mem::MaybeUninit]> { unsafe { @@ -1129,7 +1119,6 @@ impl Rc<[T], A> { /// ``` #[cfg(not(no_global_oom_handling))] #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn new_uninit_slice_in(len: usize, alloc: A) -> Rc<[mem::MaybeUninit], A> { unsafe { Rc::from_ptr_in(Rc::allocate_for_slice_in(len, &alloc), alloc) } @@ -1158,7 +1147,6 @@ impl Rc<[T], A> { /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn new_zeroed_slice_in(len: usize, alloc: A) -> Rc<[mem::MaybeUninit], A> { unsafe { diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index b8925f4544f4..4090a04f21a9 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -516,8 +516,6 @@ impl Arc { /// # Examples /// /// ``` - /// #![feature(new_zeroed_alloc)] - /// /// use std::sync::Arc; /// /// let zero = Arc::::new_zeroed(); @@ -529,7 +527,7 @@ impl Arc { /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] #[inline] - #[unstable(feature = "new_zeroed_alloc", issue = "129396")] + #[stable(feature = "new_zeroed_alloc", since = "CURRENT_RUSTC_VERSION")] #[must_use] pub fn new_zeroed() -> Arc> { unsafe { @@ -603,7 +601,6 @@ impl Arc { /// # Ok::<(), std::alloc::AllocError>(()) /// ``` #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] pub fn try_new_uninit() -> Result>, AllocError> { unsafe { Ok(Arc::from_ptr(Arc::try_allocate_for_layout( @@ -636,7 +633,6 @@ impl Arc { /// /// [zeroed]: mem::MaybeUninit::zeroed #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] pub fn try_new_zeroed() -> Result>, AllocError> { unsafe { Ok(Arc::from_ptr(Arc::try_allocate_for_layout( @@ -703,7 +699,6 @@ impl Arc { /// ``` #[cfg(not(no_global_oom_handling))] #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn new_uninit_in(alloc: A) -> Arc, A> { unsafe { @@ -741,7 +736,6 @@ impl Arc { /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn new_zeroed_in(alloc: A) -> Arc, A> { unsafe { @@ -927,7 +921,6 @@ impl Arc { /// # Ok::<(), std::alloc::AllocError>(()) /// ``` #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn try_new_uninit_in(alloc: A) -> Result, A>, AllocError> { unsafe { @@ -966,7 +959,6 @@ impl Arc { /// /// [zeroed]: mem::MaybeUninit::zeroed #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "new_uninit", issue = "63291")] #[inline] pub fn try_new_zeroed_in(alloc: A) -> Result, A>, AllocError> { unsafe { @@ -1197,8 +1189,6 @@ impl Arc<[T]> { /// # Examples /// /// ``` - /// #![feature(new_zeroed_alloc)] - /// /// use std::sync::Arc; /// /// let values = Arc::<[u32]>::new_zeroed_slice(3); @@ -1210,7 +1200,7 @@ impl Arc<[T]> { /// [zeroed]: mem::MaybeUninit::zeroed #[cfg(not(no_global_oom_handling))] #[inline] - #[unstable(feature = "new_zeroed_alloc", issue = "129396")] + #[stable(feature = "new_zeroed_alloc", since = "CURRENT_RUSTC_VERSION")] #[must_use] pub fn new_zeroed_slice(len: usize) -> Arc<[mem::MaybeUninit]> { unsafe { diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 311b2cb93239..2c0061dc4032 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -376,7 +376,6 @@ #![feature(allocator_api)] #![feature(get_mut_unchecked)] #![feature(map_try_insert)] -#![feature(new_zeroed_alloc)] #![feature(slice_concat_trait)] #![feature(thin_box)] #![feature(try_reserve_kind)] From 064825e734e9472925664ad681e055602ea62397 Mon Sep 17 00:00:00 2001 From: RunDevelopment Date: Fri, 1 Aug 2025 20:13:48 +0200 Subject: [PATCH 009/682] Add suggestion to `cast_sign_loss` and `cast_possible_wrap` using the `cast_{un,}signed()` methods --- clippy_lints/src/casts/cast_possible_wrap.rs | 30 ++++++- clippy_lints/src/casts/cast_sign_loss.rs | 29 ++++++- clippy_lints/src/casts/mod.rs | 4 +- clippy_lints/src/casts/utils.rs | 32 ++++++++ clippy_utils/src/msrvs.rs | 2 +- tests/ui/cast.stderr | 82 ++++++++++---------- 6 files changed, 131 insertions(+), 48 deletions(-) diff --git a/clippy_lints/src/casts/cast_possible_wrap.rs b/clippy_lints/src/casts/cast_possible_wrap.rs index e26c03ccda93..5a8b3e6d9a41 100644 --- a/clippy_lints/src/casts/cast_possible_wrap.rs +++ b/clippy_lints/src/casts/cast_possible_wrap.rs @@ -1,4 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::sugg::Sugg; +use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::Ty; @@ -16,7 +19,14 @@ enum EmitState { LintOnPtrSize(u64), } -pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { +pub(super) fn check( + cx: &LateContext<'_>, + expr: &Expr<'_>, + cast_op: &Expr<'_>, + cast_from: Ty<'_>, + cast_to: Ty<'_>, + msrv: Msrv, +) { let (Some(from_nbits), Some(to_nbits)) = ( utils::int_ty_to_nbits(cx.tcx, cast_from), utils::int_ty_to_nbits(cx.tcx, cast_to), @@ -85,5 +95,23 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, ca .note("`usize` and `isize` may be as small as 16 bits on some platforms") .note("for more information see https://doc.rust-lang.org/reference/types/numeric.html#machine-dependent-integer-types"); } + + if msrv.meets(cx, msrvs::INTEGER_SIGN_CAST) + && let Some(cast) = utils::is_signedness_cast(cast_from, cast_to) + { + let method = match cast { + utils::CastTo::Signed => "cast_signed()", + utils::CastTo::Unsigned => "cast_unsigned()", + }; + let mut app = Applicability::MaybeIncorrect; + let sugg = Sugg::hir_with_context(cx, cast_op, expr.span.ctxt(), "..", &mut app); + + diag.span_suggestion( + expr.span, + format!("if this is intentional, consider using `{method}` instead"), + format!("{}.{method}", sugg.maybe_paren()), + app, + ); + } }); } diff --git a/clippy_lints/src/casts/cast_sign_loss.rs b/clippy_lints/src/casts/cast_sign_loss.rs index a70bd8861919..757d53e394ff 100644 --- a/clippy_lints/src/casts/cast_sign_loss.rs +++ b/clippy_lints/src/casts/cast_sign_loss.rs @@ -2,15 +2,18 @@ use std::convert::Infallible; use std::ops::ControlFlow; use clippy_utils::consts::{ConstEvalCtxt, Constant}; -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::sugg::Sugg; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; use clippy_utils::{method_chain_args, sext, sym}; +use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; use rustc_span::Symbol; -use super::CAST_SIGN_LOSS; +use super::{CAST_SIGN_LOSS, utils}; /// A list of methods that can never return a negative value. /// Includes methods that panic rather than returning a negative value. @@ -42,13 +45,33 @@ pub(super) fn check<'cx>( cast_op: &Expr<'_>, cast_from: Ty<'cx>, cast_to: Ty<'_>, + msrv: Msrv, ) { if should_lint(cx, cast_op, cast_from, cast_to) { - span_lint( + span_lint_and_then( cx, CAST_SIGN_LOSS, expr.span, format!("casting `{cast_from}` to `{cast_to}` may lose the sign of the value"), + |diag| { + if msrv.meets(cx, msrvs::INTEGER_SIGN_CAST) + && let Some(cast) = utils::is_signedness_cast(cast_from, cast_to) + { + let method = match cast { + utils::CastTo::Signed => "cast_signed()", + utils::CastTo::Unsigned => "cast_unsigned()", + }; + let mut app = Applicability::MaybeIncorrect; + let sugg = Sugg::hir_with_context(cx, cast_op, expr.span.ctxt(), "..", &mut app); + + diag.span_suggestion( + expr.span, + format!("if this is intentional, consider using `{method}` instead"), + format!("{}.{method}", sugg.maybe_paren()), + app, + ); + } + }, ); } } diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 37accff5eaa8..4df2d81ea355 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -887,9 +887,9 @@ impl<'tcx> LateLintPass<'tcx> for Casts { if cast_to.is_numeric() { cast_possible_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to, cast_to_hir.span); if cast_from.is_numeric() { - cast_possible_wrap::check(cx, expr, cast_from, cast_to); + cast_possible_wrap::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv); cast_precision_loss::check(cx, expr, cast_from, cast_to); - cast_sign_loss::check(cx, expr, cast_from_expr, cast_from, cast_to); + cast_sign_loss::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv); cast_abs_to_unsigned::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv); cast_nan_to_int::check(cx, expr, cast_from_expr, cast_from, cast_to); } diff --git a/clippy_lints/src/casts/utils.rs b/clippy_lints/src/casts/utils.rs index d846d78b9ee7..8251e48da5cd 100644 --- a/clippy_lints/src/casts/utils.rs +++ b/clippy_lints/src/casts/utils.rs @@ -60,3 +60,35 @@ pub(super) fn enum_ty_to_nbits(adt: AdtDef<'_>, tcx: TyCtxt<'_>) -> u64 { neg_bits.max(pos_bits).into() } } + +pub(super) enum CastTo { + Signed, + Unsigned, +} +/// Returns `Some` if the type cast is between 2 integral types that differ +/// only in signedness, otherwise `None`. The value of `Some` is which +/// signedness is casted to. +pub(super) fn is_signedness_cast(cast_from: Ty<'_>, cast_to: Ty<'_>) -> Option { + if !cast_from.is_integral() || !cast_to.is_integral() { + return None; + } + if cast_from.is_signed() == cast_to.is_signed() { + return None; + } + if as_uint_ty(cast_from) != as_uint_ty(cast_to) { + return None; + } + + if cast_to.is_signed() { + Some(CastTo::Signed) + } else { + Some(CastTo::Unsigned) + } +} +fn as_uint_ty(ty: Ty<'_>) -> Option { + match ty.kind() { + ty::Uint(uint_ty) => Some(*uint_ty), + ty::Int(int_ty) => Some(int_ty.to_unsigned()), + _ => None, + } +} diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 24ed4c3a8bec..a38550a7b552 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -24,7 +24,7 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { 1,88,0 { LET_CHAINS } - 1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT, UNSIGNED_IS_MULTIPLE_OF } + 1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT, UNSIGNED_IS_MULTIPLE_OF, INTEGER_SIGN_CAST } 1,85,0 { UINT_FLOAT_MIDPOINT, CONST_SIZE_OF_VAL } 1,84,0 { CONST_OPTION_AS_SLICE, MANUAL_DANGLING_PTR } 1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP } diff --git a/tests/ui/cast.stderr b/tests/ui/cast.stderr index 1cb30d956679..13e0a5583cdc 100644 --- a/tests/ui/cast.stderr +++ b/tests/ui/cast.stderr @@ -194,7 +194,7 @@ error: casting `u8` to `i8` may wrap around the value --> tests/ui/cast.rs:88:5 | LL | 1u8 as i8; - | ^^^^^^^^^ + | ^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1u8.cast_signed()` | = note: `-D clippy::cast-possible-wrap` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::cast_possible_wrap)]` @@ -203,25 +203,25 @@ error: casting `u16` to `i16` may wrap around the value --> tests/ui/cast.rs:91:5 | LL | 1u16 as i16; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1u16.cast_signed()` error: casting `u32` to `i32` may wrap around the value --> tests/ui/cast.rs:94:5 | LL | 1u32 as i32; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1u32.cast_signed()` error: casting `u64` to `i64` may wrap around the value --> tests/ui/cast.rs:97:5 | LL | 1u64 as i64; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1u64.cast_signed()` error: casting `usize` to `isize` may wrap around the value --> tests/ui/cast.rs:100:5 | LL | 1usize as isize; - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1usize.cast_signed()` error: casting `usize` to `i8` may truncate the value --> tests/ui/cast.rs:104:5 @@ -321,43 +321,43 @@ error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:138:5 | LL | -1i32 as u32; - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-1i32).cast_unsigned()` error: casting `isize` to `usize` may lose the sign of the value --> tests/ui/cast.rs:142:5 | LL | -1isize as usize; - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-1isize).cast_unsigned()` error: casting `i8` to `u8` may lose the sign of the value --> tests/ui/cast.rs:154:5 | LL | (i8::MIN).abs() as u8; - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(i8::MIN).abs().cast_unsigned()` error: casting `i64` to `u64` may lose the sign of the value --> tests/ui/cast.rs:159:5 | LL | (-1i64).abs() as u64; - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-1i64).abs().cast_unsigned()` error: casting `isize` to `usize` may lose the sign of the value --> tests/ui/cast.rs:161:5 | LL | (-1isize).abs() as usize; - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-1isize).abs().cast_unsigned()` error: casting `i64` to `u64` may lose the sign of the value --> tests/ui/cast.rs:169:5 | LL | (unsafe { (-1i64).checked_abs().unwrap_unchecked() }) as u64; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(unsafe { (-1i64).checked_abs().unwrap_unchecked() }).cast_unsigned()` error: casting `i64` to `u64` may lose the sign of the value --> tests/ui/cast.rs:185:5 | LL | (unsafe { (-1i64).checked_isqrt().unwrap_unchecked() }) as u64; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(unsafe { (-1i64).checked_isqrt().unwrap_unchecked() }).cast_unsigned()` error: casting `i64` to `i8` may truncate the value --> tests/ui/cast.rs:237:5 @@ -495,79 +495,79 @@ error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:438:9 | LL | (x * x) as u32; - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(x * x).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:444:32 | LL | let _a = |x: i32| -> u32 { (x * x * x * x) as u32 }; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(x * x * x * x).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:447:5 | LL | (2_i32).checked_pow(3).unwrap() as u32; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(2_i32).checked_pow(3).unwrap().cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:449:5 | LL | (-2_i32).pow(3) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-2_i32).pow(3).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:454:5 | LL | (-5_i32 % 2) as u32; - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-5_i32 % 2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:457:5 | LL | (-5_i32 % -2) as u32; - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-5_i32 % -2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:461:5 | LL | (-2_i32 >> 1) as u32; - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-2_i32 >> 1).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:465:5 | LL | (x * x) as u32; - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(x * x).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:467:5 | LL | (x * x * x) as u32; - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(x * x * x).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:471:5 | LL | (y * y * y * y * -2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y * y * y * y * -2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:474:5 | LL | (y * y * y / y * 2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y * y * y / y * 2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:476:5 | LL | (y * y / y * 2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y * y / y * 2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:479:5 | LL | (y / y * y * -2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y / y * y * -2).cast_unsigned()` error: equal expressions as operands to `/` --> tests/ui/cast.rs:479:6 @@ -581,97 +581,97 @@ error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:483:5 | LL | (y + y + y + -2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y + y + y + -2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:486:5 | LL | (y + y + y + 2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y + y + y + 2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:490:5 | LL | (z + -2) as u16; - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(z + -2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:493:5 | LL | (z + z + 2) as u16; - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(z + z + 2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:497:9 | LL | (a * a * b * b * c * c) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * a * b * b * c * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:499:9 | LL | (a * b * c) as u32; - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * b * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:502:9 | LL | (a * -b * c) as u32; - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * -b * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:505:9 | LL | (a * b * c * c) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * b * c * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:507:9 | LL | (a * -2) as u32; - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * -2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:510:9 | LL | (a * b * c * -2) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * b * c * -2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:513:9 | LL | (a / b) as u32; - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a / b).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:515:9 | LL | (a / b * c) as u32; - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a / b * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:518:9 | LL | (a / b + b * c) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a / b + b * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:521:9 | LL | a.saturating_pow(3) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `a.saturating_pow(3).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:524:9 | LL | (a.abs() * b.pow(2) / c.abs()) as u32 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a.abs() * b.pow(2) / c.abs()).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:532:21 | LL | let _ = i32::MIN as u32; // cast_sign_loss - | ^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `i32::MIN.cast_unsigned()` ... LL | m!(); | ---- in this macro invocation From 0eb16ea97b5161da7874f772e20e49f02af20189 Mon Sep 17 00:00:00 2001 From: RunDevelopment Date: Sat, 2 Aug 2025 00:16:08 +0200 Subject: [PATCH 010/682] Add test to ensure that suggestions are only emitted if MSRV supports it --- tests/ui/cast.rs | 13 +++++++++++++ tests/ui/cast.stderr | 14 +++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/tests/ui/cast.rs b/tests/ui/cast.rs index 525be8216500..fab02bf7b24e 100644 --- a/tests/ui/cast.rs +++ b/tests/ui/cast.rs @@ -569,3 +569,16 @@ fn issue12721() { (255 % 999999u64) as u8; //~^ cast_possible_truncation } + +mod issue14150 { + #[clippy::msrv = "1.87"] + fn msrv_supports_cast_signed() { + _ = 1u8 as i8; + //~^ cast_possible_wrap + } + #[clippy::msrv = "1.86"] + fn msrv_doesnt_supports_cast_signed() { + _ = 1u8 as i8; + //~^ cast_possible_wrap + } +} diff --git a/tests/ui/cast.stderr b/tests/ui/cast.stderr index 13e0a5583cdc..f131369df241 100644 --- a/tests/ui/cast.stderr +++ b/tests/ui/cast.stderr @@ -752,5 +752,17 @@ LL - (255 % 999999u64) as u8; LL + u8::try_from(255 % 999999u64); | -error: aborting due to 92 previous errors +error: casting `u8` to `i8` may wrap around the value + --> tests/ui/cast.rs:576:13 + | +LL | _ = 1u8 as i8; + | ^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1u8.cast_signed()` + +error: casting `u8` to `i8` may wrap around the value + --> tests/ui/cast.rs:581:13 + | +LL | _ = 1u8 as i8; + | ^^^^^^^^^ + +error: aborting due to 94 previous errors From 4e286bb8fb305d6ba864ff8b2c806b4fd189657a Mon Sep 17 00:00:00 2001 From: RunDevelopment Date: Sun, 3 Aug 2025 13:44:56 +0200 Subject: [PATCH 011/682] Apply suggestions from code review --- clippy_lints/src/casts/cast_possible_wrap.rs | 2 +- clippy_lints/src/casts/cast_sign_loss.rs | 2 +- clippy_lints/src/casts/utils.rs | 23 +----- tests/ui/cast.stderr | 84 ++++++++++---------- 4 files changed, 47 insertions(+), 64 deletions(-) diff --git a/clippy_lints/src/casts/cast_possible_wrap.rs b/clippy_lints/src/casts/cast_possible_wrap.rs index 5a8b3e6d9a41..9eaa6e4cf26e 100644 --- a/clippy_lints/src/casts/cast_possible_wrap.rs +++ b/clippy_lints/src/casts/cast_possible_wrap.rs @@ -108,7 +108,7 @@ pub(super) fn check( diag.span_suggestion( expr.span, - format!("if this is intentional, consider using `{method}` instead"), + format!("if this is intentional, use `{method}` instead"), format!("{}.{method}", sugg.maybe_paren()), app, ); diff --git a/clippy_lints/src/casts/cast_sign_loss.rs b/clippy_lints/src/casts/cast_sign_loss.rs index 757d53e394ff..f870d27b796e 100644 --- a/clippy_lints/src/casts/cast_sign_loss.rs +++ b/clippy_lints/src/casts/cast_sign_loss.rs @@ -66,7 +66,7 @@ pub(super) fn check<'cx>( diag.span_suggestion( expr.span, - format!("if this is intentional, consider using `{method}` instead"), + format!("if this is intentional, use `{method}` instead"), format!("{}.{method}", sugg.maybe_paren()), app, ); diff --git a/clippy_lints/src/casts/utils.rs b/clippy_lints/src/casts/utils.rs index 8251e48da5cd..707fc2a8eed3 100644 --- a/clippy_lints/src/casts/utils.rs +++ b/clippy_lints/src/casts/utils.rs @@ -69,26 +69,9 @@ pub(super) enum CastTo { /// only in signedness, otherwise `None`. The value of `Some` is which /// signedness is casted to. pub(super) fn is_signedness_cast(cast_from: Ty<'_>, cast_to: Ty<'_>) -> Option { - if !cast_from.is_integral() || !cast_to.is_integral() { - return None; - } - if cast_from.is_signed() == cast_to.is_signed() { - return None; - } - if as_uint_ty(cast_from) != as_uint_ty(cast_to) { - return None; - } - - if cast_to.is_signed() { - Some(CastTo::Signed) - } else { - Some(CastTo::Unsigned) - } -} -fn as_uint_ty(ty: Ty<'_>) -> Option { - match ty.kind() { - ty::Uint(uint_ty) => Some(*uint_ty), - ty::Int(int_ty) => Some(int_ty.to_unsigned()), + match (cast_from.kind(), cast_to.kind()) { + (ty::Int(from), ty::Uint(to)) if from.to_unsigned() == *to => Some(CastTo::Unsigned), + (ty::Uint(from), ty::Int(to)) if *from == to.to_unsigned() => Some(CastTo::Signed), _ => None, } } diff --git a/tests/ui/cast.stderr b/tests/ui/cast.stderr index f131369df241..8c48855123f9 100644 --- a/tests/ui/cast.stderr +++ b/tests/ui/cast.stderr @@ -194,7 +194,7 @@ error: casting `u8` to `i8` may wrap around the value --> tests/ui/cast.rs:88:5 | LL | 1u8 as i8; - | ^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1u8.cast_signed()` + | ^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1u8.cast_signed()` | = note: `-D clippy::cast-possible-wrap` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::cast_possible_wrap)]` @@ -203,25 +203,25 @@ error: casting `u16` to `i16` may wrap around the value --> tests/ui/cast.rs:91:5 | LL | 1u16 as i16; - | ^^^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1u16.cast_signed()` + | ^^^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1u16.cast_signed()` error: casting `u32` to `i32` may wrap around the value --> tests/ui/cast.rs:94:5 | LL | 1u32 as i32; - | ^^^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1u32.cast_signed()` + | ^^^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1u32.cast_signed()` error: casting `u64` to `i64` may wrap around the value --> tests/ui/cast.rs:97:5 | LL | 1u64 as i64; - | ^^^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1u64.cast_signed()` + | ^^^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1u64.cast_signed()` error: casting `usize` to `isize` may wrap around the value --> tests/ui/cast.rs:100:5 | LL | 1usize as isize; - | ^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1usize.cast_signed()` + | ^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1usize.cast_signed()` error: casting `usize` to `i8` may truncate the value --> tests/ui/cast.rs:104:5 @@ -321,43 +321,43 @@ error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:138:5 | LL | -1i32 as u32; - | ^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-1i32).cast_unsigned()` + | ^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-1i32).cast_unsigned()` error: casting `isize` to `usize` may lose the sign of the value --> tests/ui/cast.rs:142:5 | LL | -1isize as usize; - | ^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-1isize).cast_unsigned()` + | ^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-1isize).cast_unsigned()` error: casting `i8` to `u8` may lose the sign of the value --> tests/ui/cast.rs:154:5 | LL | (i8::MIN).abs() as u8; - | ^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(i8::MIN).abs().cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(i8::MIN).abs().cast_unsigned()` error: casting `i64` to `u64` may lose the sign of the value --> tests/ui/cast.rs:159:5 | LL | (-1i64).abs() as u64; - | ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-1i64).abs().cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-1i64).abs().cast_unsigned()` error: casting `isize` to `usize` may lose the sign of the value --> tests/ui/cast.rs:161:5 | LL | (-1isize).abs() as usize; - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-1isize).abs().cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-1isize).abs().cast_unsigned()` error: casting `i64` to `u64` may lose the sign of the value --> tests/ui/cast.rs:169:5 | LL | (unsafe { (-1i64).checked_abs().unwrap_unchecked() }) as u64; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(unsafe { (-1i64).checked_abs().unwrap_unchecked() }).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(unsafe { (-1i64).checked_abs().unwrap_unchecked() }).cast_unsigned()` error: casting `i64` to `u64` may lose the sign of the value --> tests/ui/cast.rs:185:5 | LL | (unsafe { (-1i64).checked_isqrt().unwrap_unchecked() }) as u64; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(unsafe { (-1i64).checked_isqrt().unwrap_unchecked() }).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(unsafe { (-1i64).checked_isqrt().unwrap_unchecked() }).cast_unsigned()` error: casting `i64` to `i8` may truncate the value --> tests/ui/cast.rs:237:5 @@ -495,79 +495,79 @@ error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:438:9 | LL | (x * x) as u32; - | ^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(x * x).cast_unsigned()` + | ^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(x * x).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:444:32 | LL | let _a = |x: i32| -> u32 { (x * x * x * x) as u32 }; - | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(x * x * x * x).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(x * x * x * x).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:447:5 | LL | (2_i32).checked_pow(3).unwrap() as u32; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(2_i32).checked_pow(3).unwrap().cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(2_i32).checked_pow(3).unwrap().cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:449:5 | LL | (-2_i32).pow(3) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-2_i32).pow(3).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-2_i32).pow(3).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:454:5 | LL | (-5_i32 % 2) as u32; - | ^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-5_i32 % 2).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-5_i32 % 2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:457:5 | LL | (-5_i32 % -2) as u32; - | ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-5_i32 % -2).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-5_i32 % -2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:461:5 | LL | (-2_i32 >> 1) as u32; - | ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(-2_i32 >> 1).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-2_i32 >> 1).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:465:5 | LL | (x * x) as u32; - | ^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(x * x).cast_unsigned()` + | ^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(x * x).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:467:5 | LL | (x * x * x) as u32; - | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(x * x * x).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(x * x * x).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:471:5 | LL | (y * y * y * y * -2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y * y * y * y * -2).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y * y * y * y * -2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:474:5 | LL | (y * y * y / y * 2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y * y * y / y * 2).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y * y * y / y * 2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:476:5 | LL | (y * y / y * 2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y * y / y * 2).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y * y / y * 2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:479:5 | LL | (y / y * y * -2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y / y * y * -2).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y / y * y * -2).cast_unsigned()` error: equal expressions as operands to `/` --> tests/ui/cast.rs:479:6 @@ -581,97 +581,97 @@ error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:483:5 | LL | (y + y + y + -2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y + y + y + -2).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y + y + y + -2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:486:5 | LL | (y + y + y + 2) as u16; - | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(y + y + y + 2).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y + y + y + 2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:490:5 | LL | (z + -2) as u16; - | ^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(z + -2).cast_unsigned()` + | ^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(z + -2).cast_unsigned()` error: casting `i16` to `u16` may lose the sign of the value --> tests/ui/cast.rs:493:5 | LL | (z + z + 2) as u16; - | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(z + z + 2).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(z + z + 2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:497:9 | LL | (a * a * b * b * c * c) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * a * b * b * c * c).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * a * b * b * c * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:499:9 | LL | (a * b * c) as u32; - | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * b * c).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * b * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:502:9 | LL | (a * -b * c) as u32; - | ^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * -b * c).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * -b * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:505:9 | LL | (a * b * c * c) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * b * c * c).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * b * c * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:507:9 | LL | (a * -2) as u32; - | ^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * -2).cast_unsigned()` + | ^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * -2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:510:9 | LL | (a * b * c * -2) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a * b * c * -2).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * b * c * -2).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:513:9 | LL | (a / b) as u32; - | ^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a / b).cast_unsigned()` + | ^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a / b).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:515:9 | LL | (a / b * c) as u32; - | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a / b * c).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a / b * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:518:9 | LL | (a / b + b * c) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a / b + b * c).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a / b + b * c).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:521:9 | LL | a.saturating_pow(3) as u32; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `a.saturating_pow(3).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `a.saturating_pow(3).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:524:9 | LL | (a.abs() * b.pow(2) / c.abs()) as u32 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `(a.abs() * b.pow(2) / c.abs()).cast_unsigned()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a.abs() * b.pow(2) / c.abs()).cast_unsigned()` error: casting `i32` to `u32` may lose the sign of the value --> tests/ui/cast.rs:532:21 | LL | let _ = i32::MIN as u32; // cast_sign_loss - | ^^^^^^^^^^^^^^^ help: if this is intentional, consider using `cast_unsigned()` instead: `i32::MIN.cast_unsigned()` + | ^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `i32::MIN.cast_unsigned()` ... LL | m!(); | ---- in this macro invocation @@ -756,7 +756,7 @@ error: casting `u8` to `i8` may wrap around the value --> tests/ui/cast.rs:576:13 | LL | _ = 1u8 as i8; - | ^^^^^^^^^ help: if this is intentional, consider using `cast_signed()` instead: `1u8.cast_signed()` + | ^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1u8.cast_signed()` error: casting `u8` to `i8` may wrap around the value --> tests/ui/cast.rs:581:13 From b0bbfd1bb75794f6664334f60fa2bcf064ea3bd3 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 4 Aug 2025 01:53:10 +0800 Subject: [PATCH 012/682] Add assignment type analysis for ide-completion --- .../ide-completion/src/context/analysis.rs | 32 ++++++++++++ .../ide-completion/src/context/tests.rs | 50 +++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index ea5fb39338b2..67fc79e8032c 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -562,6 +562,7 @@ fn expected_type_and_name<'db>( token: &SyntaxToken, name_like: &ast::NameLike, ) -> (Option>, Option) { + let token = prev_assign_token_at_whitespace(token.clone()); let mut node = match token.parent() { Some(it) => it, None => return (None, None), @@ -632,6 +633,17 @@ fn expected_type_and_name<'db>( .map(TypeInfo::original); (ty, None) }, + ast::BinExpr(it) => { + if let Some(ast::BinaryOp::Assignment { op: None }) = it.op_kind() { + let ty = it.lhs() + .and_then(|lhs| sema.type_of_expr(&lhs)) + .or_else(|| it.rhs().and_then(|rhs| sema.type_of_expr(&rhs))) + .map(TypeInfo::original); + (ty, None) + } else { + (None, None) + } + }, ast::ArgList(_) => { cov_mark::hit!(expected_type_fn_param); ActiveParameter::at_token( @@ -1870,3 +1882,23 @@ fn next_non_trivia_sibling(ele: SyntaxElement) -> Option { } None } + +fn prev_assign_token_at_whitespace(mut token: SyntaxToken) -> SyntaxToken { + while token.kind() == SyntaxKind::WHITESPACE + && let Some(prev) = token.prev_token() + && let T![=] + | T![+=] + | T![/=] + | T![*=] + | T![%=] + | T![>>=] + | T![<<=] + | T![-=] + | T![|=] + | T![&=] + | T![^=] = prev.kind() + { + token = prev + } + token +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs index 75c20968e1e5..7a8c70f190ef 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs @@ -434,3 +434,53 @@ fn f(thing: u32) -> &u32 { expect!["ty: u32, name: ?"], ); } + +#[test] +fn expected_type_assign() { + check_expected_type_and_name( + r#" +enum State { Stop } +fn foo() { + let x: &mut State = &mut State::Stop; + x = $0; +} +"#, + expect![[r#"ty: &'_ mut State, name: ?"#]], + ); +} + +#[test] +fn expected_type_deref_assign() { + check_expected_type_and_name( + r#" +enum State { Stop } +fn foo() { + let x: &mut State = &mut State::Stop; + match x { + State::Stop => { + *x = $0; + }, + } +} +"#, + expect![[r#"ty: State, name: ?"#]], + ); +} + +#[test] +fn expected_type_deref_assign_at_block_end() { + check_expected_type_and_name( + r#" +enum State { Stop } +fn foo() { + let x: &mut State = &mut State::Stop; + match x { + State::Stop => { + *x = $0 + }, + } +} +"#, + expect![[r#"ty: State, name: ?"#]], + ); +} From b5a4e5d73fb7fd47caa1b22ffe8527c13f895c21 Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Mon, 4 Aug 2025 01:24:22 +0500 Subject: [PATCH 013/682] remove gates --- library/alloc/src/collections/btree/map/entry.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/library/alloc/src/collections/btree/map/entry.rs b/library/alloc/src/collections/btree/map/entry.rs index ea8fa363c380..b16aad0bb03d 100644 --- a/library/alloc/src/collections/btree/map/entry.rs +++ b/library/alloc/src/collections/btree/map/entry.rs @@ -275,7 +275,6 @@ impl<'a, K: Ord, V, A: Allocator + Clone> Entry<'a, K, V, A> { /// # Examples /// /// ``` - /// #![feature(btree_entry_insert)] /// use std::collections::BTreeMap; /// /// let mut map: BTreeMap<&str, String> = BTreeMap::new(); @@ -284,7 +283,7 @@ impl<'a, K: Ord, V, A: Allocator + Clone> Entry<'a, K, V, A> { /// assert_eq!(entry.key(), &"poneyland"); /// ``` #[inline] - #[unstable(feature = "btree_entry_insert", issue = "65225")] + #[stable(feature = "btree_entry_insert", since = "CURRENT_RUSTC_VERSION")] pub fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V, A> { match self { Occupied(mut entry) => { @@ -383,7 +382,6 @@ impl<'a, K: Ord, V, A: Allocator + Clone> VacantEntry<'a, K, V, A> { /// # Examples /// /// ``` - /// #![feature(btree_entry_insert)] /// use std::collections::BTreeMap; /// use std::collections::btree_map::Entry; /// @@ -395,7 +393,7 @@ impl<'a, K: Ord, V, A: Allocator + Clone> VacantEntry<'a, K, V, A> { /// } /// assert_eq!(map["poneyland"], 37); /// ``` - #[unstable(feature = "btree_entry_insert", issue = "65225")] + #[stable(feature = "btree_entry_insert", since = "CURRENT_RUSTC_VERSION")] pub fn insert_entry(mut self, value: V) -> OccupiedEntry<'a, K, V, A> { let handle = match self.handle { None => { From bf500ec822cb27ec82a259c3edc0cba86e7b1ac4 Mon Sep 17 00:00:00 2001 From: skewb1k Date: Mon, 4 Aug 2025 14:11:06 +0300 Subject: [PATCH 014/682] chore: fix crates/ide/src/folding_ranges.rs file perms 755 -> 644 --- src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs diff --git a/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs b/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs old mode 100755 new mode 100644 From 1d817dc7a782e011797de3225037241eaabdf631 Mon Sep 17 00:00:00 2001 From: skewb1k Date: Mon, 4 Aug 2025 14:42:45 +0300 Subject: [PATCH 015/682] fix(hover): unify horizontal rule formatting to `---` Replaces all `___` with `---` in hover documentation markups. Both styles are valid per the GitHub Flavored Markdown spec, but `---` is less ambiguous and already more widely used in rust-analyzer --- .../crates/ide/src/hover/render.rs | 8 +- .../crates/ide/src/hover/tests.rs | 76 +++++++++---------- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index 51b5900e8155..653465e9ed5b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -361,7 +361,7 @@ pub(super) fn try_for_lint(attr: &ast::Attr, token: &SyntaxToken) -> Option { let backtick_len = value.chars().filter(|c| *c == '`').count(); @@ -1026,7 +1026,7 @@ fn type_info( if let Some(extra) = render_notable_trait(db, ¬able_traits(db, &original), edition, display_target) { - desc.push_str("\n___\n"); + desc.push_str("\n---\n"); desc.push_str(&extra); }; desc.into() @@ -1094,7 +1094,7 @@ fn closure_ty( |_| None, |_| None, ) { - format_to!(markup, "\n___\n{layout}"); + format_to!(markup, "\n---\n{layout}"); } format_to!(markup, "{adjusted}\n\n## Captures\n{}", captures_rendered,); diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index c5480217a91e..187fd1222e89 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -357,7 +357,7 @@ fn main() { ```rust impl Fn(i32) -> i32 ``` - ___ + --- size = 8, align = 8, niches = 1 ## Captures @@ -380,7 +380,7 @@ fn main() { ```rust impl Fn(i32) -> i32 ``` - ___ + --- size = 0, align = 1 ## Captures @@ -414,7 +414,7 @@ fn main() { ```rust impl FnOnce() ``` - ___ + --- size = 16 (0x10), align = 8, niches = 1 ## Captures @@ -443,7 +443,7 @@ fn main() { ```rust impl FnMut() ``` - ___ + --- size = 8, align = 8, niches = 1 ## Captures @@ -468,7 +468,7 @@ fn main() { ```rust impl FnOnce() -> S2 ``` - ___ + --- size = 8, align = 8, niches = 1 Coerced to: &impl FnOnce() -> S2 @@ -6829,7 +6829,7 @@ fn hover_lint() { ``` arithmetic_overflow ``` - ___ + --- arithmetic operation overflows "#]], @@ -6841,7 +6841,7 @@ fn hover_lint() { ``` arithmetic_overflow ``` - ___ + --- arithmetic operation overflows "#]], @@ -6857,7 +6857,7 @@ fn hover_clippy_lint() { ``` clippy::almost_swapped ``` - ___ + --- Checks for `foo = bar; bar = foo` sequences. "#]], @@ -6869,7 +6869,7 @@ fn hover_clippy_lint() { ``` clippy::almost_swapped ``` - ___ + --- Checks for `foo = bar; bar = foo` sequences. "#]], @@ -8567,7 +8567,7 @@ fn main() { ```rust &'static str ``` - ___ + --- value of literal: ` 🦀🦀\A ` "#]], @@ -8583,7 +8583,7 @@ fn main() { ```rust &'static str ``` - ___ + --- value of literal: ` 🦀\u{1f980}\\\x41 ` "#]], @@ -8605,7 +8605,7 @@ fsdghs"; ```rust &'static str ``` - ___ + --- value of literal (truncated up to newline): ` 🦀\u{1f980}\\\x41 ` "#]], @@ -8625,7 +8625,7 @@ fn main() { ```rust &'static {unknown} ``` - ___ + --- value of literal: ` 🦀🦀\A ` "#]], @@ -8644,7 +8644,7 @@ fn main() { ```rust &'static str ``` - ___ + --- value of literal: ```` `[^`]*` ```` "#]], @@ -8659,7 +8659,7 @@ fn main() { ```rust &'static str ``` - ___ + --- value of literal: `` ` `` "#]], @@ -8674,7 +8674,7 @@ fn main() { ```rust &'static str ``` - ___ + --- value of literal: ` ` "#]], @@ -8690,7 +8690,7 @@ fn main() { ```rust &'static str ``` - ___ + --- value of literal: ` Hello World ` "#]], @@ -8710,7 +8710,7 @@ fn main() { ```rust &'static [u8; 5] ``` - ___ + --- value of literal: ` [240, 159, 166, 128, 92] ` "#]], @@ -8726,7 +8726,7 @@ fn main() { ```rust &'static [u8; 18] ``` - ___ + --- value of literal: ` [92, 120, 70, 48, 92, 120, 57, 70, 92, 120, 65, 54, 92, 120, 56, 48, 92, 92] ` "#]], @@ -8746,7 +8746,7 @@ fn main() { ```rust u8 ``` - ___ + --- value of literal: ` 0xF0 ` "#]], @@ -8762,7 +8762,7 @@ fn main() { ```rust u8 ``` - ___ + --- value of literal: ` 0x5C ` "#]], @@ -8782,7 +8782,7 @@ fn main() { ```rust char ``` - ___ + --- value of literal: ` A ` "#]], @@ -8798,7 +8798,7 @@ fn main() { ```rust char ``` - ___ + --- value of literal: ` \ ` "#]], @@ -8814,7 +8814,7 @@ fn main() { ```rust char ``` - ___ + --- value of literal: ` 🦀 ` "#]], @@ -8834,7 +8834,7 @@ fn main() { ```rust f64 ``` - ___ + --- value of literal: ` 1 (bits: 0x3FF0000000000000) ` "#]], @@ -8850,7 +8850,7 @@ fn main() { ```rust f16 ``` - ___ + --- value of literal: ` 1 (bits: 0x3C00) ` "#]], @@ -8866,7 +8866,7 @@ fn main() { ```rust f32 ``` - ___ + --- value of literal: ` 1 (bits: 0x3F800000) ` "#]], @@ -8882,7 +8882,7 @@ fn main() { ```rust f128 ``` - ___ + --- value of literal: ` 1 (bits: 0x3FFF0000000000000000000000000000) ` "#]], @@ -8898,7 +8898,7 @@ fn main() { ```rust f64 ``` - ___ + --- value of literal: ` 134000000000000 (bits: 0x42DE77D399980000) ` "#]], @@ -8914,7 +8914,7 @@ fn main() { ```rust f64 ``` - ___ + --- value of literal: ` 1523527134274733600000000 (bits: 0x44F429E9249F629B) ` "#]], @@ -8930,7 +8930,7 @@ fn main() { ```rust f64 ``` - ___ + --- invalid literal: invalid float literal "#]], @@ -8950,7 +8950,7 @@ fn main() { ```rust i32 ``` - ___ + --- value of literal: ` 34325236457856836345234 (0x744C659178614489D92|0b111010001001100011001011001000101111000011000010100010010001001110110010010) ` "#]], @@ -8966,7 +8966,7 @@ fn main() { ```rust i32 ``` - ___ + --- value of literal: ` 13412342421 (0x31F701A95|0b1100011111011100000001101010010101) ` "#]], @@ -8982,7 +8982,7 @@ fn main() { ```rust i32 ``` - ___ + --- value of literal: ` 306328611 (0x12423423|0b10010010000100011010000100011) ` "#]], @@ -8998,7 +8998,7 @@ fn main() { ```rust i32 ``` - ___ + --- value of literal: ` 255 (0xFF|0b11111111) ` "#]], @@ -9014,7 +9014,7 @@ fn main() { ```rust i32 ``` - ___ + --- value of literal: ` 5349 (0x14E5|0b1010011100101) ` "#]], @@ -9030,7 +9030,7 @@ fn main() { ```rust i32 ``` - ___ + --- invalid literal: number too large to fit in target type "#]], @@ -9186,7 +9186,7 @@ fn main() { ```rust S ``` - ___ + --- Implements notable traits: `Future`, `Iterator`, `Notable`"#]], ); } From 6bc1d4d2aa996918838e2e9e39290756a37b8cc0 Mon Sep 17 00:00:00 2001 From: Hmikihiro <34ttrweoewiwe28@gmail.com> Date: Mon, 4 Aug 2025 14:38:17 +0900 Subject: [PATCH 016/682] remvoe add_attr edit_in_place.rs because it use ted. --- .../src/handlers/convert_bool_to_enum.rs | 37 ++++----- .../src/handlers/convert_closure_to_fn.rs | 1 + .../src/handlers/convert_from_to_tryfrom.rs | 1 + .../src/handlers/extract_function.rs | 1 + .../src/handlers/extract_module.rs | 2 + .../src/handlers/extract_type_alias.rs | 5 +- .../src/handlers/extract_variable.rs | 2 +- .../src/handlers/generate_delegate_methods.rs | 2 + .../src/handlers/generate_delegate_trait.rs | 79 +++++++++++++++---- .../src/handlers/generate_derive.rs | 25 ++++-- .../src/handlers/generate_fn_type_alias.rs | 1 + .../src/handlers/generate_function.rs | 3 +- .../src/handlers/generate_getter_or_setter.rs | 2 + .../ide-assists/src/handlers/generate_impl.rs | 1 + .../ide-assists/src/handlers/generate_new.rs | 1 + .../generate_single_field_struct_from.rs | 10 ++- .../src/handlers/promote_local_to_const.rs | 2 +- .../src/handlers/unmerge_imports.rs | 10 +-- .../crates/ide-assists/src/utils.rs | 26 ++---- .../crates/ide-db/src/imports/insert_use.rs | 2 +- .../crates/syntax/src/ast/edit_in_place.rs | 22 ------ .../crates/syntax/src/ast/make.rs | 38 +++++++-- .../src/ast/syntax_factory/constructors.rs | 27 +++++-- .../crates/syntax/src/syntax_editor.rs | 1 + 24 files changed, 185 insertions(+), 116 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs index f73b8c4fd0f1..c208c8bf789a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs @@ -13,12 +13,7 @@ use ide_db::{ use itertools::Itertools; use syntax::{ AstNode, NodeOrToken, SyntaxKind, SyntaxNode, T, - ast::{ - self, HasName, - edit::IndentLevel, - edit_in_place::{AttrsOwnerEdit, Indent}, - make, - }, + ast::{self, HasName, edit::IndentLevel, edit_in_place::Indent, make}, }; use crate::{ @@ -506,18 +501,6 @@ fn node_to_insert_before(target_node: SyntaxNode) -> SyntaxNode { } fn make_bool_enum(make_pub: bool) -> ast::Enum { - let enum_def = make::enum_( - if make_pub { Some(make::visibility_pub()) } else { None }, - make::name("Bool"), - None, - None, - make::variant_list(vec![ - make::variant(None, make::name("True"), None, None), - make::variant(None, make::name("False"), None, None), - ]), - ) - .clone_for_update(); - let derive_eq = make::attr_outer(make::meta_token_tree( make::ext::ident_path("derive"), make::token_tree( @@ -529,11 +512,19 @@ fn make_bool_enum(make_pub: bool) -> ast::Enum { NodeOrToken::Token(make::tokens::ident("Eq")), ], ), - )) - .clone_for_update(); - enum_def.add_attr(derive_eq); - - enum_def + )); + make::enum_( + [derive_eq], + if make_pub { Some(make::visibility_pub()) } else { None }, + make::name("Bool"), + None, + None, + make::variant_list(vec![ + make::variant(None, make::name("True"), None, None), + make::variant(None, make::name("False"), None, None), + ]), + ) + .clone_for_update() } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs index 916bb67ebb40..5f526ec89940 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs @@ -235,6 +235,7 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>) Some(make::ret_type(make::ty(&ret_ty))) }; let mut fn_ = make::fn_( + None, None, closure_name_or_default.clone(), closure_type_params, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs index f1cc3d90b9c5..7d8b763d8b87 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs @@ -96,6 +96,7 @@ pub(crate) fn convert_from_to_tryfrom(acc: &mut Assists, ctx: &AssistContext<'_> } let error_type = ast::AssocItem::TypeAlias(make::ty_alias( + None, "Error", None, None, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs index 890b8dd64126..d88e3311bd7b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs @@ -1641,6 +1641,7 @@ fn format_function( let (generic_params, where_clause) = make_generic_params_and_where_clause(ctx, fun); make::fn_( + None, None, fun_name, generic_params, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs index c6a6b97df824..da91d0ac2809 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs @@ -585,6 +585,7 @@ impl Module { if (def_out_sel || !is_item) && use_stmt_not_in_sel { let use_ = make::use_( + None, None, make::use_tree(make::join_paths(use_tree_paths), None, None, false), ); @@ -599,6 +600,7 @@ impl Module { let super_path = make::ext::ident_path("super"); let node_path = make::ext::ident_path(&node_syntax.to_string()); let use_ = make::use_( + None, None, make::use_tree(make::join_paths(vec![super_path, node_path]), None, None, false), ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs index 79f22381952a..7f93506685e1 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs @@ -69,8 +69,9 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> edit.replace(ty.syntax(), new_ty.syntax()); // Insert new alias - let ty_alias = make::ty_alias("Type", generic_params, None, None, Some((ty, None))) - .clone_for_update(); + let ty_alias = + make::ty_alias(None, "Type", generic_params, None, None, Some((ty, None))) + .clone_for_update(); if let Some(cap) = ctx.config.snippet_cap && let Some(name) = ty_alias.name() diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs index c9c1969b9e02..bd88e8b09ced 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs @@ -200,7 +200,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op } ExtractionKind::Constant => { let ast_ty = make.ty(&ty_string); - ast::Item::Const(make.item_const(None, pat_name, ast_ty, initializer)) + ast::Item::Const(make.item_const(None, None, pat_name, ast_ty, initializer)) .into() } ExtractionKind::Static => { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs index 2c81e2883a34..d8a2e038d33c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs @@ -155,6 +155,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' let ret_type = method_source.ret_type(); let f = make::fn_( + None, vis, fn_name, type_params, @@ -195,6 +196,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' let assoc_item_list = make::assoc_item_list(Some(vec![item])); let impl_def = make::impl_( + None, ty_params, ty_args, make::ty_path(make::ext::ident_path(name)), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs index e96250f3c50a..e87dde5b8e42 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs @@ -20,7 +20,6 @@ use syntax::{ HasGenericParams, HasName, HasTypeBounds, HasVisibility as astHasVisibility, Path, WherePred, edit::{self, AstNodeEdit}, - edit_in_place::AttrsOwnerEdit, make, }, ted::{self, Position}, @@ -266,6 +265,7 @@ fn generate_impl( let bound_params = bound_def.generic_param_list(); let delegate = make::impl_trait( + None, delegee.is_unsafe(db), bound_params.clone(), bound_params.map(|params| params.to_generic_args()), @@ -379,6 +379,7 @@ fn generate_impl( let path_type = transform_impl(ctx, ast_strukt, &old_impl, &transform_args, path_type)?; // 3) Generate delegate trait impl let delegate = make::impl_trait( + None, trait_.is_unsafe(db), trait_gen_params, trait_gen_args, @@ -652,8 +653,7 @@ fn process_assoc_item( qual_path_ty: ast::Path, base_name: &str, ) -> Option { - let attrs = item.attrs(); - let assoc = match item { + match item { AssocItem::Const(c) => const_assoc_item(c, qual_path_ty), AssocItem::Fn(f) => func_assoc_item(f, qual_path_ty, base_name), AssocItem::MacroCall(_) => { @@ -662,18 +662,7 @@ fn process_assoc_item( None } AssocItem::TypeAlias(ta) => ty_assoc_item(ta, qual_path_ty), - }; - if let Some(assoc) = &assoc { - attrs.for_each(|attr| { - assoc.add_attr(attr.clone()); - // fix indentations - if let Some(tok) = attr.syntax().next_sibling_or_token() { - let pos = Position::after(tok); - ted::insert(pos, make::tokens::whitespace(" ")); - } - }) } - assoc } fn const_assoc_item(item: syntax::ast::Const, qual_path_ty: ast::Path) -> Option { @@ -687,6 +676,7 @@ fn const_assoc_item(item: syntax::ast::Const, qual_path_ty: ast::Path) -> Option // make::path_qualified(qual_path_ty, path_expr_segment.as_single_segment().unwrap()); let qualified_path = qualified_path(qual_path_ty, path_expr_segment); let inner = make::item_const( + item.attrs(), item.visibility(), item.name()?, item.ty()?, @@ -755,6 +745,7 @@ fn func_assoc_item( let body = make::block_expr(vec![], Some(call.into())).clone_for_update(); let func = make::fn_( + item.attrs(), item.visibility(), item.name()?, item.generic_param_list(), @@ -779,13 +770,14 @@ fn ty_assoc_item(item: syntax::ast::TypeAlias, qual_path_ty: Path) -> Option::t; + + #[cfg(not(test))] + type t = ::t; +} +"#, + ); + } + #[test] fn assoc_items_attributes_mutably_cloned() { check_assist( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs index 73a69c82fbcd..06fef4af2238 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs @@ -1,6 +1,8 @@ use syntax::{ + SyntaxKind::{ATTR, COMMENT, WHITESPACE}, T, - ast::{self, AstNode, HasAttrs, edit_in_place::AttrsOwnerEdit, make}, + ast::{self, AstNode, HasAttrs, edit::IndentLevel, make}, + syntax_editor::{Element, Position}, }; use crate::{AssistContext, AssistId, Assists}; @@ -48,8 +50,20 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt )) .clone_for_update(); - let nominal = edit.make_mut(nominal); - nominal.add_attr(derive.clone()); + let mut editor = edit.make_editor(nominal.syntax()); + let indent = IndentLevel::from_node(nominal.syntax()); + let after_attrs_and_comments = nominal + .syntax() + .children_with_tokens() + .find(|it| !matches!(it.kind(), WHITESPACE | COMMENT | ATTR)) + .map_or(Position::first_child_of(nominal.syntax()), Position::before); + editor.insert_all( + after_attrs_and_comments, + vec![ + derive.syntax().syntax_element(), + make::tokens::whitespace(&format!("\n{indent}")).syntax_element(), + ], + ); let delimiter = derive .meta() @@ -58,8 +72,9 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt .expect("failed to get token tree out of Meta") .r_paren_token() .expect("make::attr_outer was expected to have a R_PAREN"); - - edit.add_tabstop_before_token(cap, delimiter); + let tabstop_before = edit.make_tabstop_before(cap); + editor.add_annotation(delimiter, tabstop_before); + edit.add_file_edits(ctx.vfs_file_id(), editor); } Some(_) => { // Just move the cursor. diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs index 3c327a63b0f0..0b7eca2290f6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_fn_type_alias.rs @@ -94,6 +94,7 @@ pub(crate) fn generate_fn_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) // Insert new alias let ty_alias = make::ty_alias( + None, &alias_name, generic_params, None, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs index 613b32fcc165..efdb20c1e931 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs @@ -189,7 +189,7 @@ fn add_func_to_accumulator( ))); // FIXME: adt may have generic params. - let impl_ = make::impl_(None, None, name, None, None).clone_for_update(); + let impl_ = make::impl_(None, None, None, name, None, None).clone_for_update(); func.indent(IndentLevel(1)); impl_.get_or_create_assoc_item_list().add_item(func.into()); @@ -365,6 +365,7 @@ impl FunctionBuilder { Visibility::Pub => Some(make::visibility_pub()), }; let fn_def = make::fn_( + None, visibility, self.fn_name, self.generic_param_list, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs index 807b9194b2df..e42d0ed1b00b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs @@ -263,6 +263,7 @@ fn generate_getter_from_info( let body = make::block_expr([], Some(body)); make::fn_( + None, strukt.visibility(), fn_name, None, @@ -299,6 +300,7 @@ fn generate_setter_from_info(info: &AssistInfo, record_field_info: &RecordFieldI // Make the setter fn make::fn_( + None, strukt.visibility(), fn_name, None, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs index b38ee6f7dce8..77eb8efc6f6a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs @@ -174,6 +174,7 @@ pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> let make_impl_ = |body| { make::impl_trait( + None, trait_.unsafe_token().is_some(), None, trait_gen_args.clone(), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs index 351f134612f0..ac4a54a89ef1 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs @@ -134,6 +134,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option let ret_type = make::ret_type(make::ty_path(make::ext::ident_path("Self"))); let fn_ = make::fn_( + None, strukt.visibility(), make::name("new"), None, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs index 4e95ceb2e853..6076d5cb5ced 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs @@ -6,13 +6,12 @@ use ide_db::{ }; use syntax::{ TokenText, - ast::{self, AstNode, HasGenericParams, HasName, edit, edit_in_place::Indent}, + ast::{self, AstNode, HasAttrs, HasGenericParams, HasName, edit, edit_in_place::Indent}, }; use crate::{ AssistId, assist_context::{AssistContext, Assists}, - utils::add_cfg_attrs_to, }; // Assist: generate_single_field_struct_from @@ -89,6 +88,7 @@ pub(crate) fn generate_single_field_struct_from( let body = make::block_expr([], Some(constructor)); let fn_ = make::fn_( + None, None, make::name("from"), None, @@ -110,8 +110,12 @@ pub(crate) fn generate_single_field_struct_from( .clone_for_update(); fn_.indent(1.into()); + let cfg_attrs = strukt + .attrs() + .filter(|attr| attr.as_simple_call().is_some_and(|(name, _arg)| name == "cfg")); let impl_ = make::impl_trait( + cfg_attrs, false, None, trait_gen_args, @@ -128,8 +132,6 @@ pub(crate) fn generate_single_field_struct_from( impl_.get_or_create_assoc_item_list().add_item(fn_.into()); - add_cfg_attrs_to(&strukt, &impl_); - impl_.reindent_to(indent); builder.insert(strukt.syntax().text_range().end(), format!("\n\n{indent}{impl_}")); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs index 603be4d66733..547d3686e390 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs @@ -88,7 +88,7 @@ pub(crate) fn promote_local_to_const(acc: &mut Assists, ctx: &AssistContext<'_>) } } - let item = make.item_const(None, make.name(&name), make.ty(&ty), initializer); + let item = make.item_const(None, None, make.name(&name), make.ty(&ty), initializer); if let Some((cap, name)) = ctx.config.snippet_cap.zip(item.name()) { let tabstop = edit.make_tabstop_before(cap); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_imports.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_imports.rs index c066f41ca47b..accb5c28d6ed 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_imports.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_imports.rs @@ -1,9 +1,6 @@ use syntax::{ AstNode, SyntaxKind, - ast::{ - self, HasAttrs, HasVisibility, edit::IndentLevel, edit_in_place::AttrsOwnerEdit, make, - syntax_factory::SyntaxFactory, - }, + ast::{self, HasAttrs, HasVisibility, edit::IndentLevel, make, syntax_factory::SyntaxFactory}, syntax_editor::{Element, Position, Removable}, }; @@ -46,13 +43,10 @@ pub(crate) fn unmerge_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt acc.add(AssistId::refactor_rewrite("unmerge_imports"), label, target, |builder| { let make = SyntaxFactory::with_mappings(); let new_use = make.use_( + use_.attrs(), use_.visibility(), make.use_tree(path, tree.use_tree_list(), tree.rename(), tree.star_token().is_some()), ); - // Add any attributes that are present on the use tree - use_.attrs().for_each(|attr| { - new_use.add_attr(attr.clone_for_update()); - }); let mut editor = builder.make_editor(use_.syntax()); // Remove the use tree from the current use item diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index 91aac9cf7b60..20e0302b57d7 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -736,8 +736,11 @@ fn generate_impl_inner( generic_params.as_ref().map(|params| params.to_generic_args().clone_for_update()); let ty = make::ty_path(make::ext::ident_path(&adt.name().unwrap().text())); - let impl_ = match trait_ { + let cfg_attrs = + adt.attrs().filter(|attr| attr.as_simple_call().is_some_and(|(name, _arg)| name == "cfg")); + match trait_ { Some(trait_) => make::impl_trait( + cfg_attrs, is_unsafe, None, None, @@ -750,26 +753,9 @@ fn generate_impl_inner( adt.where_clause(), body, ), - None => make::impl_(generic_params, generic_args, ty, adt.where_clause(), body), - } - .clone_for_update(); - - // Copy any cfg attrs from the original adt - add_cfg_attrs_to(adt, &impl_); - - impl_ -} - -pub(crate) fn add_cfg_attrs_to(from: &T, to: &U) -where - T: HasAttrs, - U: AttrsOwnerEdit, -{ - let cfg_attrs = - from.attrs().filter(|attr| attr.as_simple_call().is_some_and(|(name, _arg)| name == "cfg")); - for attr in cfg_attrs { - to.add_attr(attr.clone_for_update()); + None => make::impl_(cfg_attrs, generic_params, generic_args, ty, adt.where_clause(), body), } + .clone_for_update() } pub(crate) fn add_method_to_adt( diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs index 08cd8f28608c..b174adfd7e44 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs @@ -194,7 +194,7 @@ fn insert_use_with_alias_option( use_tree = use_tree.clone_for_update(); use_tree.wrap_in_tree_list(); } - let use_item = make::use_(None, use_tree).clone_for_update(); + let use_item = make::use_(None, None, use_tree).clone_for_update(); for attr in scope.required_cfgs.iter().map(|attr| attr.syntax().clone_subtree().clone_for_update()) { diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs index b50ce6442432..160f000387b3 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs @@ -273,28 +273,6 @@ pub trait AttrsOwnerEdit: ast::HasAttrs { } } } - - fn add_attr(&self, attr: ast::Attr) { - add_attr(self.syntax(), attr); - - fn add_attr(node: &SyntaxNode, attr: ast::Attr) { - let indent = IndentLevel::from_node(node); - attr.reindent_to(indent); - - let after_attrs_and_comments = node - .children_with_tokens() - .find(|it| !matches!(it.kind(), WHITESPACE | COMMENT | ATTR)) - .map_or(Position::first_child_of(node), Position::before); - - ted::insert_all( - after_attrs_and_comments, - vec![ - attr.syntax().clone().into(), - make::tokens::whitespace(&format!("\n{indent}")).into(), - ], - ) - } - } } impl AttrsOwnerEdit for T {} diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs index daeb79cf081d..c5ca60976018 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs @@ -190,6 +190,7 @@ fn ty_from_text(text: &str) -> ast::Type { } pub fn ty_alias( + attrs: impl IntoIterator, ident: &str, generic_param_list: Option, type_param_bounds: Option, @@ -200,6 +201,7 @@ pub fn ty_alias( let assignment_where = assignment_where.flatten(); quote! { TypeAlias { + #(#attrs "\n")* [type] " " Name { [IDENT ident] } #generic_param_list @@ -277,12 +279,16 @@ fn merge_where_clause( } pub fn impl_( + attrs: impl IntoIterator, generic_params: Option, generic_args: Option, path_type: ast::Type, where_clause: Option, body: Option, ) -> ast::Impl { + let attrs = + attrs.into_iter().fold(String::new(), |mut acc, attr| format_to_acc!(acc, "{}\n", attr)); + let gen_args = generic_args.map_or_else(String::new, |it| it.to_string()); let gen_params = generic_params.map_or_else(String::new, |it| it.to_string()); @@ -295,10 +301,11 @@ pub fn impl_( }; let body = body.map_or_else(|| format!("{{{body_newline}}}"), |it| it.to_string()); - ast_from_text(&format!("impl{gen_params} {path_type}{gen_args}{where_clause}{body}")) + ast_from_text(&format!("{attrs}impl{gen_params} {path_type}{gen_args}{where_clause}{body}")) } pub fn impl_trait( + attrs: impl IntoIterator, is_unsafe: bool, trait_gen_params: Option, trait_gen_args: Option, @@ -311,6 +318,8 @@ pub fn impl_trait( ty_where_clause: Option, body: Option, ) -> ast::Impl { + let attrs = + attrs.into_iter().fold(String::new(), |mut acc, attr| format_to_acc!(acc, "{}\n", attr)); let is_unsafe = if is_unsafe { "unsafe " } else { "" }; let trait_gen_args = trait_gen_args.map(|args| args.to_string()).unwrap_or_default(); @@ -334,7 +343,7 @@ pub fn impl_trait( let body = body.map_or_else(|| format!("{{{body_newline}}}"), |it| it.to_string()); ast_from_text(&format!( - "{is_unsafe}impl{gen_params} {is_negative}{path_type}{trait_gen_args} for {ty}{type_gen_args}{where_clause}{body}" + "{attrs}{is_unsafe}impl{gen_params} {is_negative}{path_type}{trait_gen_args} for {ty}{type_gen_args}{where_clause}{body}" )) } @@ -452,12 +461,18 @@ pub fn use_tree_list(use_trees: impl IntoIterator) -> ast:: ast_from_text(&format!("use {{{use_trees}}};")) } -pub fn use_(visibility: Option, use_tree: ast::UseTree) -> ast::Use { +pub fn use_( + attrs: impl IntoIterator, + visibility: Option, + use_tree: ast::UseTree, +) -> ast::Use { + let attrs = + attrs.into_iter().fold(String::new(), |mut acc, attr| format_to_acc!(acc, "{}\n", attr)); let visibility = match visibility { None => String::new(), Some(it) => format!("{it} "), }; - ast_from_text(&format!("{visibility}use {use_tree};")) + ast_from_text(&format!("{attrs}{visibility}use {use_tree};")) } pub fn record_expr(path: ast::Path, fields: ast::RecordExprFieldList) -> ast::RecordExpr { @@ -946,16 +961,19 @@ pub fn expr_stmt(expr: ast::Expr) -> ast::ExprStmt { } pub fn item_const( + attrs: impl IntoIterator, visibility: Option, name: ast::Name, ty: ast::Type, expr: ast::Expr, ) -> ast::Const { + let attrs = + attrs.into_iter().fold(String::new(), |mut acc, attr| format_to_acc!(acc, "{}\n", attr)); let visibility = match visibility { None => String::new(), Some(it) => format!("{it} "), }; - ast_from_text(&format!("{visibility}const {name}: {ty} = {expr};")) + ast_from_text(&format!("{attrs}{visibility}const {name}: {ty} = {expr};")) } pub fn item_static( @@ -1162,6 +1180,7 @@ pub fn variant( } pub fn fn_( + attrs: impl IntoIterator, visibility: Option, fn_name: ast::Name, type_params: Option, @@ -1174,6 +1193,8 @@ pub fn fn_( is_unsafe: bool, is_gen: bool, ) -> ast::Fn { + let attrs = + attrs.into_iter().fold(String::new(), |mut acc, attr| format_to_acc!(acc, "{}\n", attr)); let type_params = match type_params { Some(type_params) => format!("{type_params}"), None => "".into(), @@ -1197,7 +1218,7 @@ pub fn fn_( let gen_literal = if is_gen { "gen " } else { "" }; ast_from_text(&format!( - "{visibility}{const_literal}{async_literal}{gen_literal}{unsafe_literal}fn {fn_name}{type_params}{params} {ret_type}{where_clause}{body}", + "{attrs}{visibility}{const_literal}{async_literal}{gen_literal}{unsafe_literal}fn {fn_name}{type_params}{params} {ret_type}{where_clause}{body}", )) } pub fn struct_( @@ -1217,12 +1238,15 @@ pub fn struct_( } pub fn enum_( + attrs: impl IntoIterator, visibility: Option, enum_name: ast::Name, generic_param_list: Option, where_clause: Option, variant_list: ast::VariantList, ) -> ast::Enum { + let attrs = + attrs.into_iter().fold(String::new(), |mut acc, attr| format_to_acc!(acc, "{}\n", attr)); let visibility = match visibility { None => String::new(), Some(it) => format!("{it} "), @@ -1232,7 +1256,7 @@ pub fn enum_( let where_clause = where_clause.map(|it| format!(" {it}")).unwrap_or_default(); ast_from_text(&format!( - "{visibility}enum {enum_name}{generic_params}{where_clause} {variant_list}" + "{attrs}{visibility}enum {enum_name}{generic_params}{where_clause} {variant_list}" )) } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs index 738a26fed5d8..8bf27f967482 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs @@ -2,8 +2,8 @@ use crate::{ AstNode, NodeOrToken, SyntaxKind, SyntaxNode, SyntaxToken, ast::{ - self, HasArgList, HasGenericArgs, HasGenericParams, HasLoopBody, HasName, HasTypeBounds, - HasVisibility, RangeItem, make, + self, HasArgList, HasAttrs, HasGenericArgs, HasGenericParams, HasLoopBody, HasName, + HasTypeBounds, HasVisibility, RangeItem, make, }, syntax_editor::SyntaxMappingBuilder, }; @@ -107,8 +107,13 @@ impl SyntaxFactory { ast } - pub fn use_(&self, visibility: Option, use_tree: ast::UseTree) -> ast::Use { - make::use_(visibility, use_tree).clone_for_update() + pub fn use_( + &self, + attrs: impl IntoIterator, + visibility: Option, + use_tree: ast::UseTree, + ) -> ast::Use { + make::use_(attrs, visibility, use_tree).clone_for_update() } pub fn use_tree( @@ -840,16 +845,20 @@ impl SyntaxFactory { pub fn item_const( &self, + attrs: impl IntoIterator, visibility: Option, name: ast::Name, ty: ast::Type, expr: ast::Expr, ) -> ast::Const { - let ast = make::item_const(visibility.clone(), name.clone(), ty.clone(), expr.clone()) - .clone_for_update(); + let (attrs, attrs_input) = iterator_input(attrs); + let ast = + make::item_const(attrs, visibility.clone(), name.clone(), ty.clone(), expr.clone()) + .clone_for_update(); if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_children(attrs_input, ast.attrs().map(|attr| attr.syntax().clone())); if let Some(visibility) = visibility { builder.map_node( visibility.syntax().clone(), @@ -1067,6 +1076,7 @@ impl SyntaxFactory { pub fn item_enum( &self, + attrs: impl IntoIterator, visibility: Option, name: ast::Name, generic_param_list: Option, @@ -1074,6 +1084,7 @@ impl SyntaxFactory { variant_list: ast::VariantList, ) -> ast::Enum { let ast = make::enum_( + attrs, visibility.clone(), name.clone(), generic_param_list.clone(), @@ -1182,6 +1193,7 @@ impl SyntaxFactory { pub fn fn_( &self, + attrs: impl IntoIterator, visibility: Option, fn_name: ast::Name, type_params: Option, @@ -1194,7 +1206,9 @@ impl SyntaxFactory { is_unsafe: bool, is_gen: bool, ) -> ast::Fn { + let (attrs, input) = iterator_input(attrs); let ast = make::fn_( + attrs, visibility.clone(), fn_name.clone(), type_params.clone(), @@ -1210,6 +1224,7 @@ impl SyntaxFactory { if let Some(mut mapping) = self.mappings() { let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_children(input, ast.attrs().map(|attr| attr.syntax().clone())); if let Some(visibility) = visibility { builder.map_node( diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs index 18f5015e9eab..0b358878fcf2 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs @@ -618,6 +618,7 @@ mod tests { #[test] fn test_replace_token_in_parent() { let parent_fn = make::fn_( + None, None, make::name("it"), None, From 0ef8c6f2add02bf66108f3b80c204ad2b2a2c7ff Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 4 Aug 2025 17:41:00 +0300 Subject: [PATCH 017/682] Correctly goto `From` impl when on `into()` even when the call is inside a macro Descend into macros first. --- .../crates/ide/src/goto_definition.rs | 34 ++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index 84e41277390f..f768d4b68f46 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -83,14 +83,14 @@ pub(crate) fn goto_definition( return Some(RangeInfo::new(original_token.text_range(), navs)); } - if let Some(navs) = find_definition_for_known_blanket_dual_impls(sema, &original_token) { - return Some(RangeInfo::new(original_token.text_range(), navs)); - } - let navs = sema .descend_into_macros_no_opaque(original_token.clone(), false) .into_iter() .filter_map(|token| { + if let Some(navs) = find_definition_for_known_blanket_dual_impls(sema, &token.value) { + return Some(navs); + } + let parent = token.value.parent()?; let token_file_id = token.file_id; @@ -3275,6 +3275,32 @@ impl From for B { } } +fn f() { + let a = A; + let b: B = a.into$0(); +} + "#, + ); + } + + #[test] + fn into_call_to_from_definition_within_macro() { + check( + r#" +//- proc_macros: identity +//- minicore: from +struct A; + +struct B; + +impl From for B { + fn from(value: A) -> Self { + //^^^^ + B + } +} + +#[proc_macros::identity] fn f() { let a = A; let b: B = a.into$0(); From 0e44b50dbef4c8ac2520576543fcbfff7d6d99e6 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 4 Aug 2025 22:44:55 +0800 Subject: [PATCH 018/682] Add if..else completions in LetStmt and ArgList Example === ```rust let x = $0; ``` Old completions: ```rust let x = if $1 { $0 }; ``` This PR current completions: ```rust let x = if $1 { $2 } else { $0 }; ``` --- .../ide-completion/src/completions/expr.rs | 9 +- .../ide-completion/src/completions/keyword.rs | 116 ++++++++++++++++++ .../crates/ide-completion/src/context.rs | 1 + .../ide-completion/src/context/analysis.rs | 2 + 4 files changed, 127 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs index 2133291b1de1..a84927f6e2c0 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs @@ -61,6 +61,7 @@ pub(crate) fn complete_expr_path( after_if_expr, in_condition, incomplete_let, + in_value, ref ref_expr_parent, after_amp, ref is_func_update, @@ -361,10 +362,16 @@ pub(crate) fn complete_expr_path( add_keyword("loop", "loop {\n $0\n}"); if in_match_guard { add_keyword("if", "if $0"); + } else if in_value { + add_keyword("if", "if $1 {\n $2\n} else {\n $0\n}"); } else { add_keyword("if", "if $1 {\n $0\n}"); } - add_keyword("if let", "if let $1 = $2 {\n $0\n}"); + if in_value { + add_keyword("if let", "if let $1 = $2 {\n $3\n} else {\n $0\n}"); + } else { + add_keyword("if let", "if let $1 = $2 {\n $0\n}"); + } add_keyword("for", "for $1 in $2 {\n $0\n}"); add_keyword("true", "true"); add_keyword("false", "false"); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs index 64bb1fce6ba0..aea4e119f207 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs @@ -238,6 +238,8 @@ fn main() { r#" fn main() { let x = if $1 { + $2 +} else { $0 }; let y = 92; @@ -335,6 +337,120 @@ fn main() { ) } + #[test] + fn if_completion_in_parameter() { + check_edit( + "if", + r" +fn main() { + foo($0) +} +", + r" +fn main() { + foo(if $1 { + $2 +} else { + $0 +}) +} +", + ); + + check_edit( + "if", + r" +fn main() { + foo($0, 2) +} +", + r" +fn main() { + foo(if $1 { + $2 +} else { + $0 +}, 2) +} +", + ); + + check_edit( + "if", + r" +fn main() { + foo(2, $0) +} +", + r" +fn main() { + foo(2, if $1 { + $2 +} else { + $0 +}) +} +", + ); + + check_edit( + "if let", + r" +fn main() { + foo(2, $0) +} +", + r" +fn main() { + foo(2, if let $1 = $2 { + $3 +} else { + $0 +}) +} +", + ); + } + + #[test] + fn if_completion_in_let_statement() { + check_edit( + "if", + r" +fn main() { + let x = $0; +} +", + r" +fn main() { + let x = if $1 { + $2 +} else { + $0 +}; +} +", + ); + + check_edit( + "if let", + r" +fn main() { + let x = $0; +} +", + r" +fn main() { + let x = if let $1 = $2 { + $3 +} else { + $0 +}; +} +", + ); + } + #[test] fn completes_let_in_block() { check_edit( diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index cfd7f80d40b3..bb6b13b8fb96 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -147,6 +147,7 @@ pub(crate) struct PathExprCtx<'db> { /// Whether this expression is the direct condition of an if or while expression pub(crate) in_condition: bool, pub(crate) incomplete_let: bool, + pub(crate) in_value: bool, pub(crate) ref_expr_parent: Option, pub(crate) after_amp: bool, /// The surrounding RecordExpression we are completing a functional update diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index ea5fb39338b2..59077237e753 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -1245,6 +1245,7 @@ fn classify_name_ref<'db>( .parent() .and_then(ast::LetStmt::cast) .is_some_and(|it| it.semicolon_token().is_none()); + let in_value = it.parent().and_then(Either::::cast).is_some(); let impl_ = fetch_immediate_impl(sema, original_file, expr.syntax()); let in_match_guard = match it.parent().and_then(ast::MatchArm::cast) { @@ -1265,6 +1266,7 @@ fn classify_name_ref<'db>( is_func_update, innermost_ret_ty, self_param, + in_value, incomplete_let, impl_, in_match_guard, From c91f9f094025d84f9b2e123b17309c2242607247 Mon Sep 17 00:00:00 2001 From: Hmikihiro <34ttrweoewiwe28@gmail.com> Date: Tue, 5 Aug 2025 00:36:14 +0900 Subject: [PATCH 019/682] remove `ted` from replace_named_generic_with_impl.rs --- .../replace_named_generic_with_impl.rs | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs index 3cd7b58f4ddd..df7057835c34 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs @@ -7,11 +7,8 @@ use ide_db::{ }; use syntax::{ AstNode, - ast::{ - self, HasGenericParams, HasName, HasTypeBounds, Name, NameLike, PathType, - make::impl_trait_type, - }, - match_ast, ted, + ast::{self, HasGenericParams, HasName, HasTypeBounds, Name, NameLike, PathType, make}, + match_ast, }; use crate::{AssistContext, AssistId, Assists}; @@ -74,26 +71,31 @@ pub(crate) fn replace_named_generic_with_impl( "Replace named generic with impl trait", target, |edit| { - let type_param = edit.make_mut(type_param); - let fn_ = edit.make_mut(fn_); - - let path_types_to_replace = path_types_to_replace - .into_iter() - .map(|param| edit.make_mut(param)) - .collect::>(); + let mut editor = edit.make_editor(type_param.syntax()); // remove trait from generic param list if let Some(generic_params) = fn_.generic_param_list() { - generic_params.remove_generic_param(ast::GenericParam::TypeParam(type_param)); - if generic_params.generic_params().count() == 0 { - ted::remove(generic_params.syntax()); + let params: Vec = generic_params + .clone() + .generic_params() + .filter(|it| it.syntax() != type_param.syntax()) + .collect(); + if params.is_empty() { + editor.delete(generic_params.syntax()); + } else { + let new_generic_param_list = make::generic_param_list(params); + editor.replace( + generic_params.syntax(), + new_generic_param_list.syntax().clone_for_update(), + ); } } - let new_bounds = impl_trait_type(type_bound_list); + let new_bounds = make::impl_trait_type(type_bound_list); for path_type in path_types_to_replace.iter().rev() { - ted::replace(path_type.syntax(), new_bounds.clone_for_update().syntax()); + editor.replace(path_type.syntax(), new_bounds.clone_for_update().syntax()); } + edit.add_file_edits(ctx.vfs_file_id(), editor); }, ) } From 67d099bf2f64715e37ee7efe1cf21c14f0d93316 Mon Sep 17 00:00:00 2001 From: Hmikihiro <34ttrweoewiwe28@gmail.com> Date: Tue, 5 Aug 2025 01:16:39 +0900 Subject: [PATCH 020/682] Migrate `expand_glob_import` assist to use `SyntaxEditor` --- .../ide-assists/src/handlers/expand_glob_import.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs index 66552dd65f56..4b6d0b37854c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs @@ -9,7 +9,6 @@ use stdx::never; use syntax::{ AstNode, Direction, SyntaxNode, SyntaxToken, T, ast::{self, Use, UseTree, VisibilityKind, make}, - ted, }; use crate::{ @@ -165,8 +164,6 @@ fn build_expanded_import( let filtered_defs = if reexport_public_items { refs_in_target } else { refs_in_target.used_refs(ctx) }; - let use_tree = builder.make_mut(use_tree); - let names_to_import = find_names_to_import(filtered_defs, imported_defs); let expanded = make::use_tree_list(names_to_import.iter().map(|n| { let path = make::ext::ident_path( @@ -176,22 +173,24 @@ fn build_expanded_import( })) .clone_for_update(); + let mut editor = builder.make_editor(use_tree.syntax()); match use_tree.star_token() { Some(star) => { let needs_braces = use_tree.path().is_some() && names_to_import.len() != 1; if needs_braces { - ted::replace(star, expanded.syntax()) + editor.replace(star, expanded.syntax()) } else { let without_braces = expanded .syntax() .children_with_tokens() .filter(|child| !matches!(child.kind(), T!['{'] | T!['}'])) .collect(); - ted::replace_with_many(star, without_braces) + editor.replace_with_many(star, without_braces) } } None => never!(), } + builder.add_file_edits(ctx.vfs_file_id(), editor); } fn get_export_visibility_kind(use_item: &Use) -> VisibilityKind { From 069c953bdf94e9a75662157953b44e8ef7e215a2 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 5 Aug 2025 00:10:21 +0300 Subject: [PATCH 021/682] Allow renaming when there are multiple definitions (due to macros) even when some cannot rename --- .../rust-analyzer/crates/ide/src/rename.rs | 118 ++++++++++-------- 1 file changed, 69 insertions(+), 49 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs index aea4ae0fd970..8922a8eb4858 100644 --- a/src/tools/rust-analyzer/crates/ide/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs @@ -27,6 +27,27 @@ pub use ide_db::rename::RenameError; type RenameResult = Result; +/// This is similar to `collect::, _>>`, but unlike it, it succeeds if there is *any* `Ok` item. +fn ok_if_any(iter: impl Iterator>) -> Result, E> { + let mut err = None; + let oks = iter + .filter_map(|item| match item { + Ok(it) => Some(it), + Err(it) => { + err = Some(it); + None + } + }) + .collect::>(); + if !oks.is_empty() { + Ok(oks) + } else if let Some(err) = err { + Err(err) + } else { + Ok(Vec::new()) + } +} + /// Prepares a rename. The sole job of this function is to return the TextRange of the thing that is /// being targeted for a rename. pub(crate) fn prepare_rename( @@ -95,58 +116,57 @@ pub(crate) fn rename( alias_fallback(syntax, position, &new_name.display(db, edition).to_string()); let ops: RenameResult> = match alias_fallback { - Some(_) => defs - // FIXME: This can use the `ide_db::rename_reference` (or def.rename) method once we can - // properly find "direct" usages/references. - .map(|(.., def, new_name, _)| { - match kind { - IdentifierKind::Ident => (), - IdentifierKind::Lifetime => { - bail!("Cannot alias reference to a lifetime identifier") - } - IdentifierKind::Underscore => bail!("Cannot alias reference to `_`"), - IdentifierKind::LowercaseSelf => { - bail!("Cannot rename alias reference to `self`") - } - }; - let mut usages = def.usages(&sema).all(); + Some(_) => ok_if_any( + defs + // FIXME: This can use the `ide_db::rename_reference` (or def.rename) method once we can + // properly find "direct" usages/references. + .map(|(.., def, new_name, _)| { + match kind { + IdentifierKind::Ident => (), + IdentifierKind::Lifetime => { + bail!("Cannot alias reference to a lifetime identifier") + } + IdentifierKind::Underscore => bail!("Cannot alias reference to `_`"), + IdentifierKind::LowercaseSelf => { + bail!("Cannot rename alias reference to `self`") + } + }; + let mut usages = def.usages(&sema).all(); - // FIXME: hack - removes the usage that triggered this rename operation. - match usages.references.get_mut(&file_id).and_then(|refs| { - refs.iter() - .position(|ref_| ref_.range.contains_inclusive(position.offset)) - .map(|idx| refs.remove(idx)) - }) { - Some(_) => (), - None => never!(), - }; + // FIXME: hack - removes the usage that triggered this rename operation. + match usages.references.get_mut(&file_id).and_then(|refs| { + refs.iter() + .position(|ref_| ref_.range.contains_inclusive(position.offset)) + .map(|idx| refs.remove(idx)) + }) { + Some(_) => (), + None => never!(), + }; - let mut source_change = SourceChange::default(); - source_change.extend(usages.references.get_mut(&file_id).iter().map(|refs| { - ( - position.file_id, - source_edit_from_references(db, refs, def, &new_name, edition), - ) - })); + let mut source_change = SourceChange::default(); + source_change.extend(usages.references.get_mut(&file_id).iter().map(|refs| { + ( + position.file_id, + source_edit_from_references(db, refs, def, &new_name, edition), + ) + })); - Ok(source_change) - }) - .collect(), - None => defs - .map(|(.., def, new_name, rename_def)| { - if let Definition::Local(local) = def { - if let Some(self_param) = local.as_self_param(sema.db) { - cov_mark::hit!(rename_self_to_param); - return rename_self_to_param(&sema, local, self_param, &new_name, kind); - } - if kind == IdentifierKind::LowercaseSelf { - cov_mark::hit!(rename_to_self); - return rename_to_self(&sema, local); - } + Ok(source_change) + }), + ), + None => ok_if_any(defs.map(|(.., def, new_name, rename_def)| { + if let Definition::Local(local) = def { + if let Some(self_param) = local.as_self_param(sema.db) { + cov_mark::hit!(rename_self_to_param); + return rename_self_to_param(&sema, local, self_param, &new_name, kind); } - def.rename(&sema, new_name.as_str(), rename_def) - }) - .collect(), + if kind == IdentifierKind::LowercaseSelf { + cov_mark::hit!(rename_to_self); + return rename_to_self(&sema, local); + } + } + def.rename(&sema, new_name.as_str(), rename_def) + })), }; ops?.into_iter() @@ -320,7 +340,7 @@ fn find_definitions( }) }); - let res: RenameResult> = symbols.filter_map(Result::transpose).collect(); + let res: RenameResult> = ok_if_any(symbols.filter_map(Result::transpose)); match res { Ok(v) => { // remove duplicates, comparing `Definition`s From d064aca3c4bb3a5b0e14b267bd859864257f2e6e Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 5 Aug 2025 01:04:21 +0300 Subject: [PATCH 022/682] Do not remove the original token when descending into derives This caused rename to remove both, because it couldn't rename the derive-expanded one. I spent some time trying to create a test for this, before giving up. But I checked manually that this works. --- .../rust-analyzer/crates/hir/src/semantics.rs | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index d207305b4c61..05f06f97fdc2 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1241,29 +1241,27 @@ impl<'db> SemanticsImpl<'db> { adt, )) })?; - let mut res = None; for (_, derive_attr, derives) in derives { // as there may be multiple derives registering the same helper // name, we gotta make sure to call this for all of them! // FIXME: We need to call `f` for all of them as well though! - res = res.or(process_expansion_for_token( - ctx, - &mut stack, - derive_attr, - )); + process_expansion_for_token(ctx, &mut stack, derive_attr); for derive in derives.into_iter().flatten() { - res = res - .or(process_expansion_for_token(ctx, &mut stack, derive)); + process_expansion_for_token(ctx, &mut stack, derive); } } // remove all tokens that are within the derives expansion filter_duplicates(tokens, adt.syntax().text_range()); - Some(res) + Some(()) }); // if we found derives, we can early exit. There is no way we can be in any // macro call at this point given we are not in a token tree - if let Some(res) = res { - return res; + if let Some(()) = res { + // Note: derives do not remap the original token. Furthermore, we want + // the original token to be before the derives in the list, because if they + // upmap to the same token and we deduplicate them (e.g. in rename), we + // want the original token to remain, not the derive. + return None; } } // Then check for token trees, that means we are either in a function-like macro or From a0ca79e2eff61e4aa5d9d31f7d7e008c6587ba5d Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 5 Aug 2025 10:45:54 +0200 Subject: [PATCH 023/682] Slim down compile time artifact progress reports --- .../crates/project-model/src/build_dependencies.rs | 8 ++------ .../rust-analyzer/crates/rust-analyzer/src/main_loop.rs | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs index 5bea74bed7ed..203173c11be4 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs @@ -347,9 +347,7 @@ impl WorkspaceBuildScripts { match message { Message::BuildScriptExecuted(mut message) => { with_output_for(&message.package_id.repr, &mut |name, data| { - progress(format!( - "building compile-time-deps: build script {name} run" - )); + progress(format!("build script {name} run")); let cfgs = { let mut acc = Vec::new(); for cfg in &message.cfgs { @@ -380,9 +378,7 @@ impl WorkspaceBuildScripts { } Message::CompilerArtifact(message) => { with_output_for(&message.package_id.repr, &mut |name, data| { - progress(format!( - "building compile-time-deps: proc-macro {name} built" - )); + progress(format!("proc-macro {name} built")); if data.proc_macro_dylib_path == ProcMacroDylibPath::NotBuilt { data.proc_macro_dylib_path = ProcMacroDylibPath::NotProcMacro; } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index 61c758d5e86e..f5932d8cff02 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -812,7 +812,7 @@ impl GlobalState { }; if let Some(state) = state { - self.report_progress("Building build-artifacts", state, msg, None, None); + self.report_progress("Building compile-time-deps", state, msg, None, None); } } Task::LoadProcMacros(progress) => { From b24866e619e83d1dae63ef1c2e16a91cc8e9c260 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 5 Aug 2025 20:54:38 +0800 Subject: [PATCH 024/682] Change prev whitespace to prev trivia --- .../crates/ide-completion/src/context/analysis.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 67fc79e8032c..6c2edc384998 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -562,7 +562,7 @@ fn expected_type_and_name<'db>( token: &SyntaxToken, name_like: &ast::NameLike, ) -> (Option>, Option) { - let token = prev_assign_token_at_whitespace(token.clone()); + let token = prev_assign_token_at_trivia(token.clone()); let mut node = match token.parent() { Some(it) => it, None => return (None, None), @@ -1883,8 +1883,8 @@ fn next_non_trivia_sibling(ele: SyntaxElement) -> Option { None } -fn prev_assign_token_at_whitespace(mut token: SyntaxToken) -> SyntaxToken { - while token.kind() == SyntaxKind::WHITESPACE +fn prev_assign_token_at_trivia(mut token: SyntaxToken) -> SyntaxToken { + while token.kind().is_trivia() && let Some(prev) = token.prev_token() && let T![=] | T![+=] From 6a20b6d0221e4a311d61ad4f39827d6c30dca48f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?vin=C3=ADcius=20x?= Date: Mon, 4 Aug 2025 13:13:07 -0300 Subject: [PATCH 025/682] fix external docs for exported macros add test --- .../rust-analyzer/crates/ide/src/doc_links.rs | 3 ++- .../crates/ide/src/doc_links/tests.rs | 24 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs index a5d9a10d2e5f..d47cc6507938 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -390,7 +390,8 @@ fn get_doc_links( let (mut web_url, mut local_url) = get_doc_base_urls(db, target, target_dir, sysroot); - if let Some(path) = mod_path_of_def(db, target) { + let append_mod = !matches!(def, Definition::Macro(m) if m.is_macro_export(db)); + if append_mod && let Some(path) = mod_path_of_def(db, target) { web_url = join_url(web_url, &path); local_url = join_url(local_url, &path); } diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs index 6af156fa668f..c2f08f8ca2e6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs @@ -414,6 +414,30 @@ fn foo() { ) } +#[test] +fn external_docs_macro_export() { + check_external_docs( + r#" +//- /lib.rs crate:foo +pub mod inner { + #[macro_export] + macro_rules! my_macro { + () => {}; + } +} + +//- /main.rs crate:bar deps:foo +fn main() { + foo::my_m$0acro!(); +} + "#, + Some("/home/user/project"), + Some(expect![[r#"https://docs.rs/foo/*/foo/macro.my_macro.html"#]]), + Some(expect![[r#"file:///home/user/project/doc/foo/macro.my_macro.html"#]]), + Some("/sysroot"), + ); +} + #[test] fn doc_links_items_simple() { check_doc_links( From 0aa105740c1f9341a816e7f34ec94a4855f6158a Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 1 Aug 2025 17:37:33 +0800 Subject: [PATCH 026/682] Add remove simple dbg stmt for remove_dbg Remove only contain literals dbg statement ```rust fn foo() { let n = 2; $0dbg!(3); dbg!(2.6); dbg!(1, 2.5); dbg!('x'); dbg!(&n); dbg!(n); // needless comment dbg!("foo");$0 } ``` -> ```rust fn foo() { // needless comment } ``` Old: ```rust fn foo() { 3; 2.6; (1, 2.5); 'x'; &n; n; // needless comment "foo"; } ``` --- .../ide-assists/src/handlers/remove_dbg.rs | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs index 9356d02706c9..414f6746d440 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs @@ -112,6 +112,16 @@ fn compute_dbg_replacement( } } } + // dbg!(2, 'x', &x, x, ...); + exprs if ast::ExprStmt::can_cast(parent.kind()) && exprs.iter().all(pure_expr) => { + let mut replace = vec![parent.clone().into()]; + if let Some(prev_sibling) = parent.prev_sibling_or_token() + && prev_sibling.kind() == syntax::SyntaxKind::WHITESPACE + { + replace.push(prev_sibling); + } + (replace, None) + } // dbg!(expr0) [expr] => { // dbg!(expr, &parent); @@ -163,6 +173,20 @@ fn compute_dbg_replacement( }) } +fn pure_expr(expr: &ast::Expr) -> bool { + match_ast! { + match (expr.syntax()) { + ast::Literal(_) => true, + ast::RefExpr(it) => { + matches!(it.expr(), Some(ast::Expr::PathExpr(p)) + if p.path().and_then(|p| p.as_single_name_ref()).is_some()) + }, + ast::PathExpr(it) => it.path().and_then(|it| it.as_single_name_ref()).is_some(), + _ => false, + } + } +} + fn replace_nested_dbgs(expanded: ast::Expr) -> ast::Expr { if let ast::Expr::MacroExpr(mac) = &expanded { // Special-case when `expanded` itself is `dbg!()` since we cannot replace the whole tree @@ -231,6 +255,32 @@ mod tests { check("dbg!{$01 + 1}", "1 + 1"); } + #[test] + fn test_remove_simple_dbg_statement() { + check_assist( + remove_dbg, + r#" +fn foo() { + let n = 2; + $0dbg!(3); + dbg!(2.6); + dbg!(1, 2.5); + dbg!('x'); + dbg!(&n); + dbg!(n); + // needless comment + dbg!("foo");$0 +} +"#, + r#" +fn foo() { + let n = 2; + // needless comment +} +"#, + ); + } + #[test] fn test_remove_dbg_not_applicable() { check_assist_not_applicable(remove_dbg, "fn main() {$0vec![1, 2, 3]}"); From d339009cbfc59f04be15a9ca6c8f2d9ff98aa0f7 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 6 Aug 2025 17:30:18 +0200 Subject: [PATCH 027/682] Report the incorrect payload when failing to deserialize lsp messages --- src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs | 4 ++-- .../rust-analyzer/crates/ide/src/inlay_hints/param_name.rs | 6 +++++- src/tools/rust-analyzer/lib/lsp-server/src/msg.rs | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index 7a8514c47af9..8c2a2f6f72f5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -228,9 +228,9 @@ fn hints( chaining::hints(hints, famous_defs, config, display_target, &expr); adjustment::hints(hints, famous_defs, config, display_target, &expr); match expr { - ast::Expr::CallExpr(it) => param_name::hints(hints, famous_defs, config, ast::Expr::from(it)), + ast::Expr::CallExpr(it) => param_name::hints(hints, famous_defs, config, file_id, ast::Expr::from(it)), ast::Expr::MethodCallExpr(it) => { - param_name::hints(hints, famous_defs, config, ast::Expr::from(it)) + param_name::hints(hints, famous_defs, config, file_id, ast::Expr::from(it)) } ast::Expr::ClosureExpr(it) => { closure_captures::hints(hints, famous_defs, config, it.clone()); diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs index ec0a4c46c7fe..754707784055 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs @@ -7,7 +7,7 @@ use std::iter::zip; use either::Either; -use hir::Semantics; +use hir::{EditionedFileId, Semantics}; use ide_db::{RootDatabase, famous_defs::FamousDefs}; use stdx::to_lower_snake_case; @@ -19,6 +19,7 @@ pub(super) fn hints( acc: &mut Vec, FamousDefs(sema, krate): &FamousDefs<'_, '_>, config: &InlayHintsConfig, + file_id: EditionedFileId, expr: ast::Expr, ) -> Option<()> { if !config.parameter_hints { @@ -39,6 +40,9 @@ pub(super) fn hints( .filter_map(|(p, arg)| { // Only annotate hints for expressions that exist in the original file let range = sema.original_range_opt(arg.syntax())?; + if range.file_id != file_id { + return None; + } let param_name = p.name(sema.db)?; Some((p, param_name, arg, range)) }) diff --git a/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs b/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs index 399d674e41d2..b20337fdbfff 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs +++ b/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs @@ -175,7 +175,7 @@ impl Message { let msg = match serde_json::from_str(&text) { Ok(msg) => msg, Err(e) => { - return Err(invalid_data!("malformed LSP payload: {:?}", e)); + return Err(invalid_data!("malformed LSP payload `{e:?}`: {text:?}")); } }; From 51c6272baee4e60a525959c2fc9b96103ad5de74 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 6 Aug 2025 17:46:39 +0200 Subject: [PATCH 028/682] Fix non-lsp compliant `Response` definition --- src/tools/rust-analyzer/Cargo.lock | 8 ++-- src/tools/rust-analyzer/Cargo.toml | 38 +++++++++---------- .../rust-analyzer/lib/lsp-server/Cargo.toml | 6 +-- .../rust-analyzer/lib/lsp-server/src/msg.rs | 6 +-- 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 5a29379ba481..80a311a74abe 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1294,7 +1294,7 @@ checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "lsp-server" -version = "0.7.8" +version = "0.7.9" dependencies = [ "anyhow", "crossbeam-channel", @@ -1310,9 +1310,9 @@ dependencies = [ [[package]] name = "lsp-server" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9462c4dc73e17f971ec1f171d44bfffb72e65a130117233388a0ebc7ec5656f9" +checksum = "7d6ada348dbc2703cbe7637b2dda05cff84d3da2819c24abcb305dd613e0ba2e" dependencies = [ "crossbeam-channel", "log", @@ -2007,7 +2007,7 @@ dependencies = [ "intern", "itertools 0.14.0", "load-cargo", - "lsp-server 0.7.8 (registry+https://github.com/rust-lang/crates.io-index)", + "lsp-server 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", "lsp-types", "memchr", "mimalloc", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index e7cf0212bf2a..01a13a39f7b4 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -100,7 +100,7 @@ ra-ap-rustc_pattern_analysis = { version = "0.123", default-features = false } # in-tree crates that are published separately and follow semver. See lib/README.md line-index = { version = "0.1.2" } la-arena = { version = "0.3.1" } -lsp-server = { version = "0.7.8" } +lsp-server = { version = "0.7.9" } # non-local crates anyhow = "1.0.98" @@ -125,11 +125,11 @@ memmap2 = "0.9.5" nohash-hasher = "0.2.0" oorandom = "11.1.5" object = { version = "0.36.7", default-features = false, features = [ - "std", - "read_core", - "elf", - "macho", - "pe", + "std", + "read_core", + "elf", + "macho", + "pe", ] } process-wrap = { version = "8.2.1", features = ["std"] } pulldown-cmark-to-cmark = "10.0.4" @@ -139,9 +139,9 @@ rowan = "=0.15.15" # Ideally we'd not enable the macros feature but unfortunately the `tracked` attribute does not work # on impls without it salsa = { version = "0.23.0", default-features = true, features = [ - "rayon", - "salsa_unstable", - "macros", + "rayon", + "salsa_unstable", + "macros", ] } salsa-macros = "0.23.0" semver = "1.0.26" @@ -151,9 +151,9 @@ serde_json = "1.0.140" rustc-hash = "2.1.1" rustc-literal-escaper = "0.0.4" smallvec = { version = "1.15.1", features = [ - "const_new", - "union", - "const_generics", + "const_new", + "union", + "const_generics", ] } smol_str = "0.3.2" temp-dir = "0.1.16" @@ -161,12 +161,12 @@ text-size = "1.1.1" tracing = "0.1.41" tracing-tree = "0.4.0" tracing-subscriber = { version = "0.3.19", default-features = false, features = [ - "registry", - "fmt", - "local-time", - "std", - "time", - "tracing-log", + "registry", + "fmt", + "local-time", + "std", + "time", + "tracing-log", ] } triomphe = { version = "0.1.14", default-features = false, features = ["std"] } url = "2.5.4" @@ -176,7 +176,7 @@ xshell = "0.2.7" dashmap = { version = "=6.1.0", features = ["raw-api", "inline"] } # We need to freeze the version of the crate, as it needs to match with dashmap hashbrown = { version = "0.14.*", features = [ - "inline-more", + "inline-more", ], default-features = false } [workspace.lints.rust] diff --git a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml index 1fc1da50a0a0..f56a0de61637 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml +++ b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lsp-server" -version = "0.7.8" +version = "0.7.9" description = "Generic LSP server scaffold." license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust-analyzer/tree/master/lib/lsp-server" @@ -16,9 +16,9 @@ crossbeam-channel.workspace = true [dev-dependencies] lsp-types = "=0.95" ctrlc = "3.4.7" -anyhow.workspace = true +anyhow.workspace = true rustc-hash.workspace = true -toolchain.workspace = true +toolchain.workspace = true [lints] workspace = true diff --git a/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs b/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs index 399d674e41d2..0b8f8da4c7d0 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs +++ b/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs @@ -84,9 +84,9 @@ pub struct Response { // request id. We fail deserialization in that case, so we just // make this field mandatory. pub id: RequestId, - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(skip_serializing_if = "Option::is_none", default)] pub result: Option, - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(skip_serializing_if = "Option::is_none", default)] pub error: Option, } @@ -94,7 +94,7 @@ pub struct Response { pub struct ResponseError { pub code: i32, pub message: String, - #[serde(skip_serializing_if = "Option::is_none")] + #[serde(skip_serializing_if = "Option::is_none", default)] pub data: Option, } From 92b8797a0968de124d120a77252b6df4939a1fce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Aug 2025 17:11:37 +0000 Subject: [PATCH 029/682] Bump tmp from 0.2.3 to 0.2.4 in /editors/code Bumps [tmp](https://github.com/raszi/node-tmp) from 0.2.3 to 0.2.4. - [Changelog](https://github.com/raszi/node-tmp/blob/master/CHANGELOG.md) - [Commits](https://github.com/raszi/node-tmp/compare/v0.2.3...v0.2.4) --- updated-dependencies: - dependency-name: tmp dependency-version: 0.2.4 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- src/tools/rust-analyzer/editors/code/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/editors/code/package-lock.json b/src/tools/rust-analyzer/editors/code/package-lock.json index 534c24be52e8..1dc11ee4e4b8 100644 --- a/src/tools/rust-analyzer/editors/code/package-lock.json +++ b/src/tools/rust-analyzer/editors/code/package-lock.json @@ -5588,9 +5588,9 @@ } }, "node_modules/tmp": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", - "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.4.tgz", + "integrity": "sha512-UdiSoX6ypifLmrfQ/XfiawN6hkjSBpCjhKxxZcWlUUmoXLaCKQU0bx4HF/tdDK2uzRuchf1txGvrWBzYREssoQ==", "dev": true, "license": "MIT", "engines": { From 95440527af50ed95e1c4f9fcf7b8a5ebf7a992a7 Mon Sep 17 00:00:00 2001 From: Hmikihiro <34ttrweoewiwe28@gmail.com> Date: Sun, 3 Aug 2025 23:32:35 +0900 Subject: [PATCH 030/682] In extract_module.rs, generate ast::Module instead of String --- .../src/handlers/extract_module.rs | 175 +++++++++++------- .../crates/syntax/src/ast/make.rs | 17 ++ 2 files changed, 126 insertions(+), 66 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs index da91d0ac2809..e783b8693456 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs @@ -1,6 +1,5 @@ -use std::iter; +use std::ops::RangeInclusive; -use either::Either; use hir::{HasSource, ModuleSource}; use ide_db::{ FileId, FxHashMap, FxHashSet, @@ -82,7 +81,15 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti curr_parent_module = ast::Module::cast(mod_syn_opt); } - let mut module = extract_target(&node, ctx.selection_trimmed())?; + let selection_range = ctx.selection_trimmed(); + let (mut module, module_text_range) = if let Some(item) = ast::Item::cast(node.clone()) { + let module = extract_single_target(&item); + (module, node.text_range()) + } else { + let (module, range) = extract_child_target(&node, selection_range)?; + let module_text_range = range.start().text_range().cover(range.end().text_range()); + (module, module_text_range) + }; if module.body_items.is_empty() { return None; } @@ -92,7 +99,7 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti acc.add( AssistId::refactor_extract("extract_module"), "Extract Module", - module.text_range, + module_text_range, |builder| { //This takes place in three steps: // @@ -110,17 +117,17 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti //for change_visibility and usages for first point mentioned above in the process let (usages_to_be_processed, record_fields, use_stmts_to_be_inserted) = - module.get_usages_and_record_fields(ctx); + module.get_usages_and_record_fields(ctx, module_text_range); builder.edit_file(ctx.vfs_file_id()); use_stmts_to_be_inserted.into_iter().for_each(|(_, use_stmt)| { builder.insert(ctx.selection_trimmed().end(), format!("\n{use_stmt}")); }); - let import_paths_to_be_removed = module.resolve_imports(curr_parent_module, ctx); + let import_items = module.resolve_imports(curr_parent_module, ctx); module.change_visibility(record_fields); - let module_def = generate_module_def(&impl_parent, &mut module, old_item_indent); + let module_def = generate_module_def(&impl_parent, module, old_item_indent).to_string(); let mut usages_to_be_processed_for_cur_file = vec![]; for (file_id, usages) in usages_to_be_processed { @@ -157,15 +164,12 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti builder.insert(impl_.syntax().text_range().end(), format!("\n\n{module_def}")); } else { - for import_path_text_range in import_paths_to_be_removed { - if module.text_range.intersect(import_path_text_range).is_some() { - module.text_range = module.text_range.cover(import_path_text_range); - } else { - builder.delete(import_path_text_range); + for import_item in import_items { + if !module_text_range.contains_range(import_item) { + builder.delete(import_item); } } - - builder.replace(module.text_range, module_def) + builder.replace(module_text_range, module_def) } }, ) @@ -173,38 +177,50 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti fn generate_module_def( parent_impl: &Option, - module: &mut Module, + module: Module, old_indent: IndentLevel, -) -> String { - let (items_to_be_processed, new_item_indent) = if parent_impl.is_some() { - (Either::Left(module.body_items.iter()), old_indent + 2) +) -> ast::Module { + let Module { name, body_items, use_items } = module; + let items = if let Some(self_ty) = parent_impl.as_ref().and_then(|imp| imp.self_ty()) { + let assoc_items = body_items + .into_iter() + .map(|item| item.syntax().clone()) + .filter_map(ast::AssocItem::cast) + .map(|it| it.indent(IndentLevel(1))) + .collect_vec(); + let assoc_item_list = make::assoc_item_list(Some(assoc_items)); + let impl_ = make::impl_(None, None, None, self_ty.clone(), None, Some(assoc_item_list)); + // Add the import for enum/struct corresponding to given impl block + let use_impl = make_use_stmt_of_node_with_super(self_ty.syntax()); + let mut module_body_items = use_items; + module_body_items.insert(0, use_impl); + module_body_items.push(ast::Item::Impl(impl_)); + module_body_items } else { - (Either::Right(module.use_items.iter().chain(module.body_items.iter())), old_indent + 1) + [use_items, body_items].concat() }; - let mut body = items_to_be_processed - .map(|item| item.indent(IndentLevel(1))) - .map(|item| format!("{new_item_indent}{item}")) - .join("\n\n"); + let items = items.into_iter().map(|it| it.reset_indent().indent(IndentLevel(1))).collect_vec(); + let module_body = make::item_list(Some(items)); - if let Some(self_ty) = parent_impl.as_ref().and_then(|imp| imp.self_ty()) { - let impl_indent = old_indent + 1; - body = format!("{impl_indent}impl {self_ty} {{\n{body}\n{impl_indent}}}"); + let module_name = make::name(name); + make::mod_(module_name, Some(module_body)).indent(old_indent) +} - // Add the import for enum/struct corresponding to given impl block - module.make_use_stmt_of_node_with_super(self_ty.syntax()); - for item in module.use_items.iter() { - body = format!("{impl_indent}{item}\n\n{body}"); - } - } +fn make_use_stmt_of_node_with_super(node_syntax: &SyntaxNode) -> ast::Item { + let super_path = make::ext::ident_path("super"); + let node_path = make::ext::ident_path(&node_syntax.to_string()); + let use_ = make::use_( + None, + None, + make::use_tree(make::join_paths(vec![super_path, node_path]), None, None, false), + ); - let module_name = module.name; - format!("mod {module_name} {{\n{body}\n{old_indent}}}") + ast::Item::from(use_) } #[derive(Debug)] struct Module { - text_range: TextRange, name: &'static str, /// All items except use items. body_items: Vec, @@ -214,22 +230,37 @@ struct Module { use_items: Vec, } -fn extract_target(node: &SyntaxNode, selection_range: TextRange) -> Option { +fn extract_single_target(node: &ast::Item) -> Module { + let (body_items, use_items) = if matches!(node, ast::Item::Use(_)) { + (Vec::new(), vec![node.clone()]) + } else { + (vec![node.clone()], Vec::new()) + }; + let name = "modname"; + Module { name, body_items, use_items } +} + +fn extract_child_target( + node: &SyntaxNode, + selection_range: TextRange, +) -> Option<(Module, RangeInclusive)> { let selected_nodes = node .children() .filter(|node| selection_range.contains_range(node.text_range())) - .chain(iter::once(node.clone())); - let (use_items, body_items) = selected_nodes .filter_map(ast::Item::cast) - .partition(|item| matches!(item, ast::Item::Use(..))); - - Some(Module { text_range: selection_range, name: "modname", body_items, use_items }) + .collect_vec(); + let start = selected_nodes.first()?.syntax().clone(); + let end = selected_nodes.last()?.syntax().clone(); + let (use_items, body_items): (Vec, Vec) = + selected_nodes.into_iter().partition(|item| matches!(item, ast::Item::Use(..))); + Some((Module { name: "modname", body_items, use_items }, start..=end)) } impl Module { fn get_usages_and_record_fields( &self, ctx: &AssistContext<'_>, + replace_range: TextRange, ) -> (FxHashMap>, Vec, FxHashMap) { let mut adt_fields = Vec::new(); @@ -247,7 +278,7 @@ impl Module { ast::Adt(it) => { if let Some( nod ) = ctx.sema.to_def(&it) { let node_def = Definition::Adt(nod); - self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs, &mut use_stmts_to_be_inserted); + self.expand_and_group_usages_file_wise(ctx, replace_range,node_def, &mut refs, &mut use_stmts_to_be_inserted); //Enum Fields are not allowed to explicitly specify pub, it is implied match it { @@ -281,30 +312,30 @@ impl Module { ast::TypeAlias(it) => { if let Some( nod ) = ctx.sema.to_def(&it) { let node_def = Definition::TypeAlias(nod); - self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs, &mut use_stmts_to_be_inserted); + self.expand_and_group_usages_file_wise(ctx,replace_range, node_def, &mut refs, &mut use_stmts_to_be_inserted); } }, ast::Const(it) => { if let Some( nod ) = ctx.sema.to_def(&it) { let node_def = Definition::Const(nod); - self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs, &mut use_stmts_to_be_inserted); + self.expand_and_group_usages_file_wise(ctx,replace_range, node_def, &mut refs, &mut use_stmts_to_be_inserted); } }, ast::Static(it) => { if let Some( nod ) = ctx.sema.to_def(&it) { let node_def = Definition::Static(nod); - self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs, &mut use_stmts_to_be_inserted); + self.expand_and_group_usages_file_wise(ctx,replace_range, node_def, &mut refs, &mut use_stmts_to_be_inserted); } }, ast::Fn(it) => { if let Some( nod ) = ctx.sema.to_def(&it) { let node_def = Definition::Function(nod); - self.expand_and_group_usages_file_wise(ctx, node_def, &mut refs, &mut use_stmts_to_be_inserted); + self.expand_and_group_usages_file_wise(ctx,replace_range, node_def, &mut refs, &mut use_stmts_to_be_inserted); } }, ast::Macro(it) => { if let Some(nod) = ctx.sema.to_def(&it) { - self.expand_and_group_usages_file_wise(ctx, Definition::Macro(nod), &mut refs, &mut use_stmts_to_be_inserted); + self.expand_and_group_usages_file_wise(ctx,replace_range, Definition::Macro(nod), &mut refs, &mut use_stmts_to_be_inserted); } }, _ => (), @@ -318,6 +349,7 @@ impl Module { fn expand_and_group_usages_file_wise( &self, ctx: &AssistContext<'_>, + replace_range: TextRange, node_def: Definition, refs_in_files: &mut FxHashMap>, use_stmts_to_be_inserted: &mut FxHashMap, @@ -327,7 +359,7 @@ impl Module { syntax::NodeOrToken::Node(node) => node, syntax::NodeOrToken::Token(tok) => tok.parent().unwrap(), // won't panic }; - let out_of_sel = |node: &SyntaxNode| !self.text_range.contains_range(node.text_range()); + let out_of_sel = |node: &SyntaxNode| !replace_range.contains_range(node.text_range()); let mut use_stmts_set = FxHashSet::default(); for (file_id, refs) in node_def.usages(&ctx.sema).all() { @@ -527,7 +559,8 @@ impl Module { // mod -> ust_stmt transversal // true | false -> super import insertion // true | true -> super import insertion - self.make_use_stmt_of_node_with_super(use_node); + let super_use_node = make_use_stmt_of_node_with_super(use_node); + self.use_items.insert(0, super_use_node); } None => {} } @@ -556,7 +589,8 @@ impl Module { use_tree_paths = Some(use_tree_str); } else if def_in_mod && def_out_sel { - self.make_use_stmt_of_node_with_super(use_node); + let super_use_node = make_use_stmt_of_node_with_super(use_node); + self.use_items.insert(0, super_use_node); } } @@ -596,20 +630,6 @@ impl Module { import_path_to_be_removed } - fn make_use_stmt_of_node_with_super(&mut self, node_syntax: &SyntaxNode) -> ast::Item { - let super_path = make::ext::ident_path("super"); - let node_path = make::ext::ident_path(&node_syntax.to_string()); - let use_ = make::use_( - None, - None, - make::use_tree(make::join_paths(vec![super_path, node_path]), None, None, false), - ); - - let item = ast::Item::from(use_); - self.use_items.insert(0, item.clone()); - item - } - fn process_use_stmt_for_import_resolve( &self, use_stmt: Option, @@ -1424,10 +1444,10 @@ $0fn foo(x: B) {}$0 struct B {} mod modname { - use super::B; - use super::A; + use super::B; + impl A { pub(crate) fn foo(x: B) {} } @@ -1739,4 +1759,27 @@ fn main() { "#, ); } + + #[test] + fn test_miss_select_item() { + check_assist( + extract_module, + r#" +mod foo { + mod $0bar { + fn foo(){}$0 + } +} +"#, + r#" +mod foo { + mod modname { + pub(crate) mod bar { + fn foo(){} + } + } +} +"#, + ) + } } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs index c5ca60976018..9897fd094157 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs @@ -231,6 +231,23 @@ pub fn ty_fn_ptr>( } } +pub fn item_list(body: Option>) -> ast::ItemList { + let is_break_braces = body.is_some(); + let body_newline = if is_break_braces { "\n" } else { "" }; + let body_indent = if is_break_braces { " " } else { "" }; + + let body = match body { + Some(bd) => bd.iter().map(|elem| elem.to_string()).join("\n\n "), + None => String::new(), + }; + ast_from_text(&format!("mod C {{{body_newline}{body_indent}{body}{body_newline}}}")) +} + +pub fn mod_(name: ast::Name, body: Option) -> ast::Module { + let body = body.map_or(";".to_owned(), |body| format!(" {body}")); + ast_from_text(&format!("mod {name}{body}")) +} + pub fn assoc_item_list(body: Option>) -> ast::AssocItemList { let is_break_braces = body.is_some(); let body_newline = if is_break_braces { "\n".to_owned() } else { String::new() }; From 47cc8b75f0148b1c4f6f95571fbfda7b2d8b3e9d Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 7 Aug 2025 15:12:26 +0200 Subject: [PATCH 031/682] Enable warning logs by default --- src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs index ab045e0bf9ff..7602d379c0df 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs @@ -160,9 +160,9 @@ fn setup_logging(log_file_flag: Option) -> anyhow::Result<()> { rust_analyzer::tracing::Config { writer, - // Deliberately enable all `error` logs if the user has not set RA_LOG, as there is usually + // Deliberately enable all `warn` logs if the user has not set RA_LOG, as there is usually // useful information in there for debugging. - filter: env::var("RA_LOG").ok().unwrap_or_else(|| "error".to_owned()), + filter: env::var("RA_LOG").ok().unwrap_or_else(|| "warn".to_owned()), chalk_filter: env::var("CHALK_DEBUG").ok(), profile_filter: env::var("RA_PROFILE").ok(), json_profile_filter: std::env::var("RA_PROFILE_JSON").ok(), From 1a73720b8051a533b402ac67e35f84d405ccb28d Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 7 Aug 2025 15:14:22 +0200 Subject: [PATCH 032/682] Disable error log for position clamping, its too noisy due to ease of triggering --- .../crates/rust-analyzer/src/lsp/from_proto.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/from_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/from_proto.rs index 02757616d4ff..333826a1790e 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/from_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/from_proto.rs @@ -40,12 +40,13 @@ pub(crate) fn offset( })?; let col = TextSize::from(line_col.col); let clamped_len = col.min(line_range.len()); - if clamped_len < col { - tracing::error!( - "Position {line_col:?} column exceeds line length {}, clamping it", - u32::from(line_range.len()), - ); - } + // FIXME: The cause for this is likely our request retrying. Commented out as this log is just too chatty and very easy to trigger. + // if clamped_len < col { + // tracing::error!( + // "Position {line_col:?} column exceeds line length {}, clamping it", + // u32::from(line_range.len()), + // ); + // } Ok(line_range.start() + clamped_len) } From 5b9e6f1b75f14d916bc3d43c21e1e845cc049dbf Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 8 Aug 2025 23:48:12 +0800 Subject: [PATCH 033/682] Add write! and writeln! to minicore --- .../crates/test-utils/src/minicore.rs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index 7b719b5dec75..c1f2b08be942 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -70,6 +70,7 @@ //! tuple: //! unpin: sized //! unsize: sized +//! write: fmt //! todo: panic //! unimplemented: panic //! column: @@ -1769,6 +1770,26 @@ mod macros { } // endregion:panic + // region:write + #[macro_export] + macro_rules! write { + ($dst:expr, $($arg:tt)*) => { + $dst.write_fmt($crate::format_args!($($arg)*)) + }; + } + + #[macro_export] + #[allow_internal_unstable(format_args_nl)] + macro_rules! writeln { + ($dst:expr $(,)?) => { + $crate::write!($dst, "\n") + }; + ($dst:expr, $($arg:tt)*) => { + $dst.write_fmt($crate::format_args_nl!($($arg)*)) + }; + } + // endregion:write + // region:assert #[macro_export] #[rustc_builtin_macro] From bb4fded8a35a961b749846df45f6caf55d98e5d3 Mon Sep 17 00:00:00 2001 From: BenjaminBrienen Date: Fri, 8 Aug 2025 23:46:28 +0200 Subject: [PATCH 034/682] remove duplicate field in Debug --- src/tools/rust-analyzer/crates/project-model/src/workspace.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index 5b36e10fd692..00032bcf1d2e 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -157,7 +157,6 @@ impl fmt::Debug for ProjectWorkspace { .field("file", &file) .field("cargo_script", &cargo_script.is_some()) .field("n_sysroot_crates", &sysroot.num_packages()) - .field("cargo_script", &cargo_script.is_some()) .field("n_rustc_cfg", &rustc_cfg.len()) .field("toolchain", &toolchain) .field("data_layout", &target_layout) From e3d99ddcbc010de4942df34e6d5491c4b664152c Mon Sep 17 00:00:00 2001 From: Hmikihiro <34ttrweoewiwe28@gmail.com> Date: Sat, 9 Aug 2025 14:53:34 +0900 Subject: [PATCH 035/682] fix: generate function by indet token --- .../src/handlers/generate_function.rs | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs index efdb20c1e931..88ed6f9ce124 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs @@ -316,7 +316,7 @@ impl FunctionBuilder { let current_module = ctx.sema.scope(call.syntax())?.module(); let visibility = calculate_necessary_visibility(current_module, target_module, ctx); - let fn_name = make::name(&name.text()); + let fn_name = make::name(name.ident_token()?.text()); let mut necessary_generic_params = FxHashSet::default(); necessary_generic_params.extend(receiver_ty.generic_params(ctx.db())); let params = fn_args( @@ -3131,4 +3131,32 @@ fn main() { "#, ) } + + #[test] + fn no_generate_method_by_keyword() { + check_assist_not_applicable( + generate_function, + r#" +fn main() { + s.super$0(); +} + "#, + ); + check_assist_not_applicable( + generate_function, + r#" +fn main() { + s.Self$0(); +} + "#, + ); + check_assist_not_applicable( + generate_function, + r#" +fn main() { + s.self$0(); +} + "#, + ); + } } From eb2bbbb9134ae3fc4d043ea12b3546b73cd90efd Mon Sep 17 00:00:00 2001 From: jackh726 Date: Fri, 1 Aug 2025 14:29:21 +0000 Subject: [PATCH 036/682] Implement next trait solver --- src/tools/rust-analyzer/Cargo.lock | 83 +- src/tools/rust-analyzer/Cargo.toml | 11 +- .../crates/hir-def/src/lang_item.rs | 7 +- .../crates/hir-def/src/signatures.rs | 6 +- .../rust-analyzer/crates/hir-ty/Cargo.toml | 10 +- .../crates/hir-ty/src/autoderef.rs | 4 +- .../crates/hir-ty/src/chalk_db.rs | 610 +---- .../crates/hir-ty/src/chalk_ext.rs | 9 +- .../crates/hir-ty/src/consteval/tests.rs | 3 +- .../crates/hir-ty/src/consteval_nextsolver.rs | 256 ++ .../rust-analyzer/crates/hir-ty/src/db.rs | 159 +- .../rust-analyzer/crates/hir-ty/src/drop.rs | 2 +- .../crates/hir-ty/src/dyn_compatibility.rs | 41 +- .../hir-ty/src/dyn_compatibility/tests.rs | 25 +- .../crates/hir-ty/src/generics.rs | 17 +- .../rust-analyzer/crates/hir-ty/src/infer.rs | 105 +- .../crates/hir-ty/src/infer/closure.rs | 9 +- .../crates/hir-ty/src/infer/coerce.rs | 31 +- .../crates/hir-ty/src/infer/expr.rs | 11 +- .../crates/hir-ty/src/infer/unify.rs | 192 +- .../crates/hir-ty/src/interner.rs | 25 +- .../rust-analyzer/crates/hir-ty/src/layout.rs | 364 +-- .../crates/hir-ty/src/layout/adt.rs | 46 +- .../crates/hir-ty/src/layout/tests.rs | 3 + .../rust-analyzer/crates/hir-ty/src/lib.rs | 135 +- .../rust-analyzer/crates/hir-ty/src/lower.rs | 49 +- .../crates/hir-ty/src/lower_nextsolver.rs | 1609 ++++++++++++ .../hir-ty/src/lower_nextsolver/path.rs | 1459 +++++++++++ .../crates/hir-ty/src/method_resolution.rs | 135 +- .../crates/hir-ty/src/mir/eval/tests.rs | 5 +- .../crates/hir-ty/src/mir/lower.rs | 4 +- .../crates/hir-ty/src/next_solver.rs | 46 + .../crates/hir-ty/src/next_solver/abi.rs | 68 + .../crates/hir-ty/src/next_solver/consts.rs | 404 +++ .../crates/hir-ty/src/next_solver/def_id.rs | 96 + .../crates/hir-ty/src/next_solver/fold.rs | 129 + .../crates/hir-ty/src/next_solver/fulfill.rs | 229 ++ .../hir-ty/src/next_solver/generic_arg.rs | 525 ++++ .../crates/hir-ty/src/next_solver/generics.rs | 147 ++ .../crates/hir-ty/src/next_solver/infer/at.rs | 333 +++ .../infer/canonical/instantiate.rs | 106 + .../src/next_solver/infer/canonical/mod.rs | 156 ++ .../hir-ty/src/next_solver/infer/context.rs | 316 +++ .../hir-ty/src/next_solver/infer/mod.rs | 1066 ++++++++ .../src/next_solver/infer/opaque_types/mod.rs | 56 + .../next_solver/infer/opaque_types/table.rs | 166 ++ .../infer/region_constraints/mod.rs | 640 +++++ .../next_solver/infer/relate/generalize.rs | 720 ++++++ .../next_solver/infer/relate/higher_ranked.rs | 89 + .../src/next_solver/infer/relate/mod.rs | 13 + .../hir-ty/src/next_solver/infer/resolve.rs | 62 + .../src/next_solver/infer/snapshot/mod.rs | 111 + .../next_solver/infer/snapshot/undo_log.rs | 198 ++ .../hir-ty/src/next_solver/infer/traits.rs | 175 ++ .../src/next_solver/infer/type_variable.rs | 268 ++ .../hir-ty/src/next_solver/infer/unify_key.rs | 176 ++ .../crates/hir-ty/src/next_solver/interner.rs | 2173 +++++++++++++++++ .../crates/hir-ty/src/next_solver/ir_print.rs | 267 ++ .../crates/hir-ty/src/next_solver/mapping.rs | 1368 +++++++++++ .../crates/hir-ty/src/next_solver/opaques.rs | 167 ++ .../hir-ty/src/next_solver/predicate.rs | 894 +++++++ .../crates/hir-ty/src/next_solver/project.rs | 3 + .../next_solver/project/solve_normalize.rs | 264 ++ .../crates/hir-ty/src/next_solver/region.rs | 332 +++ .../crates/hir-ty/src/next_solver/solver.rs | 289 +++ .../crates/hir-ty/src/next_solver/ty.rs | 943 +++++++ .../crates/hir-ty/src/next_solver/util.rs | 1064 ++++++++ .../rust-analyzer/crates/hir-ty/src/tests.rs | 23 +- .../hir-ty/src/tests/closure_captures.rs | 3 +- .../crates/hir-ty/src/tests/coercion.rs | 16 +- .../crates/hir-ty/src/tests/incremental.rs | 20 +- .../crates/hir-ty/src/tests/macros.rs | 24 +- .../hir-ty/src/tests/method_resolution.rs | 34 +- .../crates/hir-ty/src/tests/regression.rs | 37 +- .../crates/hir-ty/src/tests/simple.rs | 4 +- .../crates/hir-ty/src/tests/traits.rs | 100 +- .../rust-analyzer/crates/hir-ty/src/tls.rs | 2 + .../rust-analyzer/crates/hir-ty/src/traits.rs | 268 +- src/tools/rust-analyzer/crates/hir/Cargo.toml | 5 + src/tools/rust-analyzer/crates/hir/src/lib.rs | 172 +- .../handlers/generate_from_impl_for_enum.rs | 24 +- .../generate_single_field_struct_from.rs | 15 +- .../crates/ide-assists/src/lib.rs | 9 +- .../crates/ide-assists/src/tests.rs | 9 +- .../ide-completion/src/completions/dot.rs | 7 +- .../crates/ide-completion/src/lib.rs | 5 +- .../crates/ide-completion/src/tests.rs | 4 +- .../ide-completion/src/tests/flyimport.rs | 41 +- .../ide-completion/src/tests/special.rs | 3 + .../src/handlers/moved_out_of_ref.rs | 4 +- .../crates/ide-diagnostics/src/lib.rs | 12 +- .../crates/ide-diagnostics/src/tests.rs | 3 + .../crates/ide-ssr/src/resolving.rs | 26 +- .../crates/ide/src/doc_links/tests.rs | 7 +- .../crates/ide/src/goto_definition.rs | 4 +- .../crates/ide/src/goto_implementation.rs | 2 +- .../rust-analyzer/crates/ide/src/hover.rs | 5 +- .../crates/ide/src/hover/render.rs | 3 +- .../crates/ide/src/hover/tests.rs | 3 + .../crates/ide/src/inlay_hints.rs | 6 +- .../crates/ide/src/inlay_hints/adjustment.rs | 10 +- .../crates/ide/src/inlay_hints/bind_pat.rs | 2 +- .../crates/ide/src/signature_help.rs | 4 +- .../crates/ide/src/syntax_highlighting.rs | 22 +- .../test_data/highlight_general.html | 12 +- .../crates/intern/src/symbol/symbols.rs | 5 + .../crates/profile/src/stop_watch.rs | 8 +- .../crates/query-group-macro/src/queries.rs | 3 +- .../crates/test-utils/src/minicore.rs | 15 +- 109 files changed, 19148 insertions(+), 1317 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/abi.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generics.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/at.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/opaque_types/mod.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/opaque_types/table.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/generalize.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/higher_ranked.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/mod.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/resolve.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/type_variable.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/unify_key.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/opaques.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project/solve_normalize.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 80a311a74abe..f72e698f6063 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -259,9 +259,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chalk-derive" -version = "0.103.0" +version = "0.104.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb4899682de915ca7c0b025bdd0a3d34c75fe12184122fda6805a7baddaa293c" +checksum = "9ea9b1e80910f66ae87c772247591432032ef3f6a67367ff17f8343db05beafa" dependencies = [ "proc-macro2", "quote", @@ -271,9 +271,9 @@ dependencies = [ [[package]] name = "chalk-ir" -version = "0.103.0" +version = "0.104.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90a37d2ab99352b4caca135061e7b4ac67024b648c28ed0b787feec4bea4caed" +checksum = "7047a516de16226cd17344d41a319d0ea1064bf9e60bd612ab341ab4a34bbfa8" dependencies = [ "bitflags 2.9.1", "chalk-derive", @@ -281,9 +281,9 @@ dependencies = [ [[package]] name = "chalk-recursive" -version = "0.103.0" +version = "0.104.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c855be60e646664bc37c2496d3dc81ca5ef60520930e5e0f0057a0575aff6c19" +checksum = "882959c242558cc686de7ff0aa59860295598d119e84a4b100215f44c3d606c4" dependencies = [ "chalk-derive", "chalk-ir", @@ -294,9 +294,9 @@ dependencies = [ [[package]] name = "chalk-solve" -version = "0.103.0" +version = "0.104.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "477ac6cdfd2013e9f93b09b036c2b607a67b2e728f4777b8422d55a79e9e3a34" +checksum = "72860086494ccfa05bbd3779a74babb8ace27da9a0cbabffa1315223c7290927" dependencies = [ "chalk-derive", "chalk-ir", @@ -445,6 +445,17 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "derive-where" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "510c292c8cf384b1a340b816a9a6cf2599eb8f566a44949024af88418000c50b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "derive_arbitrary" version = "1.4.1" @@ -696,6 +707,7 @@ dependencies = [ "indexmap", "intern", "itertools 0.14.0", + "ra-ap-rustc_type_ir", "rustc-hash 2.1.1", "smallvec", "span", @@ -705,6 +717,8 @@ dependencies = [ "test-fixture", "test-utils", "tracing", + "tracing-subscriber", + "tracing-tree", "triomphe", "tt", ] @@ -801,8 +815,11 @@ dependencies = [ "project-model", "query-group-macro", "ra-ap-rustc_abi", + "ra-ap-rustc_ast_ir", "ra-ap-rustc_index", + "ra-ap-rustc_next_trait_solver", "ra-ap-rustc_pattern_analysis", + "ra-ap-rustc_type_ir", "rustc-hash 2.1.1", "rustc_apfloat", "salsa", @@ -1856,6 +1873,12 @@ dependencies = [ "tracing", ] +[[package]] +name = "ra-ap-rustc_ast_ir" +version = "0.123.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc17e8ce797f2a8d03b838fbf166749b876164432ce81e37d283bf69e3cf80" + [[package]] name = "ra-ap-rustc_hashes" version = "0.123.0" @@ -1908,6 +1931,19 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "ra-ap-rustc_next_trait_solver" +version = "0.123.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14f7dfbdf1d045ff4e385e1efdfc3799379895e9c3f3b9b379a0bef4cb238441" +dependencies = [ + "derive-where", + "ra-ap-rustc_index", + "ra-ap-rustc_type_ir", + "ra-ap-rustc_type_ir_macros", + "tracing", +] + [[package]] name = "ra-ap-rustc_parse_format" version = "0.121.0" @@ -1931,6 +1967,37 @@ dependencies = [ "tracing", ] +[[package]] +name = "ra-ap-rustc_type_ir" +version = "0.123.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bc59fb10a922c38a24cb8a1494f799b0af30bd041acbea689378d3bf330534b" +dependencies = [ + "bitflags 2.9.1", + "derive-where", + "ena", + "indexmap", + "ra-ap-rustc_ast_ir", + "ra-ap-rustc_index", + "ra-ap-rustc_type_ir_macros", + "rustc-hash 2.1.1", + "smallvec", + "thin-vec", + "tracing", +] + +[[package]] +name = "ra-ap-rustc_type_ir_macros" +version = "0.123.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58878914b6dac7499baeecc8dbb4b9d9dda88030e4ab90cd3b4e87523fbedafe" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "rayon" version = "1.10.0" diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 01a13a39f7b4..bb8444ed7342 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -94,6 +94,9 @@ ra-ap-rustc_parse_format = { version = "0.121", default-features = false } ra-ap-rustc_index = { version = "0.123", default-features = false } ra-ap-rustc_abi = { version = "0.123", default-features = false } ra-ap-rustc_pattern_analysis = { version = "0.123", default-features = false } +ra-ap-rustc_ast_ir = { version = "0.123", default-features = false } +ra-ap-rustc_type_ir = { version = "0.123", default-features = false } +ra-ap-rustc_next_trait_solver = { version = "0.123", default-features = false } # local crates that aren't published to crates.io. These should not have versions. @@ -108,10 +111,10 @@ arrayvec = "0.7.6" bitflags = "2.9.1" cargo_metadata = "0.21.0" camino = "1.1.10" -chalk-solve = { version = "0.103.0", default-features = false } -chalk-ir = "0.103.0" -chalk-recursive = { version = "0.103.0", default-features = false } -chalk-derive = "0.103.0" +chalk-solve = { version = "0.104.0", default-features = false } +chalk-ir = "0.104.0" +chalk-recursive = { version = "0.104.0", default-features = false } +chalk-derive = "0.104.0" crossbeam-channel = "0.5.15" dissimilar = "1.0.10" dot = "0.1.4" diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs index d431f2140165..a0be69cb2f96 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs @@ -383,12 +383,17 @@ language_item_table! { AsyncFnMut, sym::async_fn_mut, async_fn_mut_trait, Target::Trait, GenericRequirement::Exact(1); AsyncFnOnce, sym::async_fn_once, async_fn_once_trait, Target::Trait, GenericRequirement::Exact(1); - AsyncFnOnceOutput, sym::async_fn_once_output,async_fn_once_output, Target::AssocTy, GenericRequirement::None; + CallRefFuture, sym::call_ref_future, call_ref_future_ty, Target::AssocTy, GenericRequirement::None; + CallOnceFuture, sym::call_once_future, call_once_future_ty, Target::AssocTy, GenericRequirement::None; + AsyncFnOnceOutput, sym::async_fn_once_output, async_fn_once_output_ty, Target::AssocTy, GenericRequirement::None; + FnOnceOutput, sym::fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None; Future, sym::future_trait, future_trait, Target::Trait, GenericRequirement::Exact(0); CoroutineState, sym::coroutine_state, coroutine_state, Target::Enum, GenericRequirement::None; Coroutine, sym::coroutine, coroutine_trait, Target::Trait, GenericRequirement::Minimum(1); + CoroutineReturn, sym::coroutine_return, coroutine_return_ty, Target::AssocTy, GenericRequirement::None; + CoroutineYield, sym::coroutine_yield, coroutine_yield_ty, Target::AssocTy, GenericRequirement::None; Unpin, sym::unpin, unpin_trait, Target::Trait, GenericRequirement::None; Pin, sym::pin, pin_type, Target::Struct, GenericRequirement::None; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs index 92e610b36acd..598b2c0314a9 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs @@ -395,7 +395,7 @@ impl ImplSignature { bitflags::bitflags! { #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] - pub struct TraitFlags: u8 { + pub struct TraitFlags: u16 { const RUSTC_HAS_INCOHERENT_INHERENT_IMPLS = 1 << 1; const FUNDAMENTAL = 1 << 2; const UNSAFE = 1 << 3; @@ -403,6 +403,7 @@ bitflags::bitflags! { const SKIP_ARRAY_DURING_METHOD_DISPATCH = 1 << 5; const SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH = 1 << 6; const RUSTC_PAREN_SUGAR = 1 << 7; + const COINDUCTIVE = 1 << 8; } } @@ -436,6 +437,9 @@ impl TraitSignature { if attrs.by_key(sym::rustc_paren_sugar).exists() { flags |= TraitFlags::RUSTC_PAREN_SUGAR; } + if attrs.by_key(sym::rustc_coinductive).exists() { + flags |= TraitFlags::COINDUCTIVE; + } let mut skip_array_during_method_dispatch = attrs.by_key(sym::rustc_skip_array_during_method_dispatch).exists(); let mut skip_boxed_slice_during_method_dispatch = false; diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml index 7cc0a26d37c8..64182110e186 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml @@ -40,7 +40,14 @@ salsa-macros.workspace = true ra-ap-rustc_abi.workspace = true ra-ap-rustc_index.workspace = true ra-ap-rustc_pattern_analysis.workspace = true +ra-ap-rustc_ast_ir.workspace = true +ra-ap-rustc_type_ir.workspace = true +ra-ap-rustc_next_trait_solver.workspace = true +# These moved to dev deps if `setup_tracing` was a macro and dependents also +# included these +tracing-subscriber.workspace = true +tracing-tree.workspace = true # local deps stdx.workspace = true @@ -53,9 +60,6 @@ span.workspace = true [dev-dependencies] expect-test = "1.5.1" -tracing.workspace = true -tracing-subscriber.workspace = true -tracing-tree.workspace = true project-model.workspace = true # local deps diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs index 26ca7fb9a15e..4ea0156e1248 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs @@ -225,7 +225,9 @@ pub(crate) fn deref_by_trait( // Check that the type implements Deref at all let trait_ref = projection.trait_ref(db); let implements_goal: Goal = trait_ref.cast(Interner); - table.try_obligation(implements_goal.clone())?; + if table.try_obligation(implements_goal.clone()).no_solution() { + return None; + } table.register_obligation(implements_goal); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs index 3ba7c93d4fb7..f523a5e8bfa0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs @@ -1,47 +1,36 @@ //! The implementation of `RustIrDatabase` for Chalk, which provides information //! about the code that Chalk needs. -use core::ops; -use std::{iter, ops::ControlFlow, sync::Arc}; +use std::sync::Arc; -use hir_expand::name::Name; -use intern::sym; -use span::Edition; use tracing::debug; -use chalk_ir::{CanonicalVarKinds, cast::Caster, fold::shift::Shift}; -use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait}; +use chalk_ir::{cast::Caster, fold::shift::Shift}; +use chalk_solve::rust_ir::{self, WellKnownTrait}; use base_db::Crate; use hir_def::{ - AssocItemId, BlockId, CallableDefId, GenericDefId, HasModule, ItemContainerId, Lookup, - TypeAliasId, VariantId, - hir::Movability, + AssocItemId, CallableDefId, GenericDefId, HasModule, ItemContainerId, Lookup, TypeAliasId, + VariantId, lang_item::LangItem, signatures::{ImplFlags, StructFlags, TraitFlags}, }; use crate::{ - AliasEq, AliasTy, BoundVar, DebruijnIndex, Interner, ProjectionTy, ProjectionTyExt, - QuantifiedWhereClause, Substitution, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, - WhereClause, - db::{HirDatabase, InternedCoroutine}, - from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, + AliasEq, AliasTy, DebruijnIndex, Interner, ProjectionTyExt, QuantifiedWhereClause, + Substitution, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, WhereClause, + db::HirDatabase, + from_assoc_type_id, from_chalk_trait_id, generics::generics, lower::LifetimeElisionKind, - make_binders, make_single_type_binders, + make_binders, mapping::{ToChalk, TypeAliasAsValue, from_chalk}, - method_resolution::{ALL_FLOAT_FPS, ALL_INT_FPS, TraitImpls, TyFingerprint}, to_assoc_type_id, to_chalk_trait_id, - traits::ChalkContext, - utils::ClosureSubst, - wrap_empty_binders, }; pub(crate) type AssociatedTyDatum = chalk_solve::rust_ir::AssociatedTyDatum; pub(crate) type TraitDatum = chalk_solve::rust_ir::TraitDatum; pub(crate) type AdtDatum = chalk_solve::rust_ir::AdtDatum; pub(crate) type ImplDatum = chalk_solve::rust_ir::ImplDatum; -pub(crate) type OpaqueTyDatum = chalk_solve::rust_ir::OpaqueTyDatum; pub(crate) type AssocTypeId = chalk_ir::AssocTypeId; pub(crate) type TraitId = chalk_ir::TraitId; @@ -52,551 +41,6 @@ pub(crate) type AssociatedTyValue = chalk_solve::rust_ir::AssociatedTyValue; pub(crate) type Variances = chalk_ir::Variances; -impl chalk_solve::RustIrDatabase for ChalkContext<'_> { - fn associated_ty_data(&self, id: AssocTypeId) -> Arc { - self.db.associated_ty_data(from_assoc_type_id(id)) - } - fn associated_ty_from_impl( - &self, - impl_id: chalk_ir::ImplId, - assoc_type_id: chalk_ir::AssocTypeId, - ) -> Option> { - let alias_id = from_assoc_type_id(assoc_type_id); - let trait_sig = self.db.type_alias_signature(alias_id); - hir_def::ImplId::from_chalk(self.db, impl_id).impl_items(self.db).items.iter().find_map( - |(name, item)| match item { - AssocItemId::TypeAliasId(alias) if &trait_sig.name == name => { - Some(TypeAliasAsValue(*alias).to_chalk(self.db)) - } - _ => None, - }, - ) - } - fn trait_datum(&self, trait_id: TraitId) -> Arc { - self.db.trait_datum(self.krate, trait_id) - } - fn adt_datum(&self, struct_id: AdtId) -> Arc { - self.db.adt_datum(self.krate, struct_id) - } - fn adt_repr(&self, _struct_id: AdtId) -> Arc> { - // FIXME: keep track of these - Arc::new(rust_ir::AdtRepr { c: false, packed: false, int: None }) - } - fn discriminant_type(&self, ty: chalk_ir::Ty) -> chalk_ir::Ty { - if let chalk_ir::TyKind::Adt(id, _) = ty.kind(Interner) - && let hir_def::AdtId::EnumId(e) = id.0 - { - let enum_data = self.db.enum_signature(e); - let ty = enum_data.repr.unwrap_or_default().discr_type(); - return chalk_ir::TyKind::Scalar(match ty { - hir_def::layout::IntegerType::Pointer(is_signed) => match is_signed { - true => chalk_ir::Scalar::Int(chalk_ir::IntTy::Isize), - false => chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize), - }, - hir_def::layout::IntegerType::Fixed(size, is_signed) => match is_signed { - true => chalk_ir::Scalar::Int(match size { - hir_def::layout::Integer::I8 => chalk_ir::IntTy::I8, - hir_def::layout::Integer::I16 => chalk_ir::IntTy::I16, - hir_def::layout::Integer::I32 => chalk_ir::IntTy::I32, - hir_def::layout::Integer::I64 => chalk_ir::IntTy::I64, - hir_def::layout::Integer::I128 => chalk_ir::IntTy::I128, - }), - false => chalk_ir::Scalar::Uint(match size { - hir_def::layout::Integer::I8 => chalk_ir::UintTy::U8, - hir_def::layout::Integer::I16 => chalk_ir::UintTy::U16, - hir_def::layout::Integer::I32 => chalk_ir::UintTy::U32, - hir_def::layout::Integer::I64 => chalk_ir::UintTy::U64, - hir_def::layout::Integer::I128 => chalk_ir::UintTy::U128, - }), - }, - }) - .intern(Interner); - } - chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::U8)).intern(Interner) - } - fn impl_datum(&self, impl_id: ImplId) -> Arc { - self.db.impl_datum(self.krate, impl_id) - } - - fn fn_def_datum( - &self, - fn_def_id: chalk_ir::FnDefId, - ) -> Arc> { - self.db.fn_def_datum(from_chalk(self.db, fn_def_id)) - } - - fn impls_for_trait( - &self, - trait_id: TraitId, - parameters: &[chalk_ir::GenericArg], - binders: &CanonicalVarKinds, - ) -> Vec { - debug!("impls_for_trait {:?}", trait_id); - let trait_: hir_def::TraitId = from_chalk_trait_id(trait_id); - - let ty: Ty = parameters[0].assert_ty_ref(Interner).clone(); - - fn binder_kind( - ty: &Ty, - binders: &CanonicalVarKinds, - ) -> Option { - if let TyKind::BoundVar(bv) = ty.kind(Interner) { - let binders = binders.as_slice(Interner); - if bv.debruijn == DebruijnIndex::INNERMOST - && let chalk_ir::VariableKind::Ty(tk) = binders[bv.index].kind - { - return Some(tk); - } - } - None - } - - let self_ty_fp = TyFingerprint::for_trait_impl(&ty); - let fps: &[TyFingerprint] = match binder_kind(&ty, binders) { - Some(chalk_ir::TyVariableKind::Integer) => &ALL_INT_FPS, - Some(chalk_ir::TyVariableKind::Float) => &ALL_FLOAT_FPS, - _ => self_ty_fp.as_slice(), - }; - - let id_to_chalk = |id: hir_def::ImplId| id.to_chalk(self.db); - - let mut result = vec![]; - if fps.is_empty() { - debug!("Unrestricted search for {:?} impls...", trait_); - _ = self.for_trait_impls(trait_, self_ty_fp, |impls| { - result.extend(impls.for_trait(trait_).map(id_to_chalk)); - ControlFlow::Continue(()) - }); - } else { - _ = - self.for_trait_impls(trait_, self_ty_fp, |impls| { - result.extend(fps.iter().flat_map(move |fp| { - impls.for_trait_and_self_ty(trait_, *fp).map(id_to_chalk) - })); - ControlFlow::Continue(()) - }); - }; - - debug!("impls_for_trait returned {} impls", result.len()); - result - } - - fn impl_provided_for(&self, auto_trait_id: TraitId, kind: &chalk_ir::TyKind) -> bool { - debug!("impl_provided_for {:?}, {:?}", auto_trait_id, kind); - - let trait_id = from_chalk_trait_id(auto_trait_id); - let self_ty = kind.clone().intern(Interner); - // We cannot filter impls by `TyFingerprint` for the following types: - let self_ty_fp = match kind { - // because we need to find any impl whose Self type is a ref with the same mutability - // (we don't care about the inner type). - TyKind::Ref(..) => None, - // because we need to find any impl whose Self type is a tuple with the same arity. - TyKind::Tuple(..) => None, - _ => TyFingerprint::for_trait_impl(&self_ty), - }; - - let check_kind = |impl_id| { - let impl_self_ty = self.db.impl_self_ty(impl_id); - // NOTE(skip_binders): it's safe to skip binders here as we don't check substitutions. - let impl_self_kind = impl_self_ty.skip_binders().kind(Interner); - - match (kind, impl_self_kind) { - (TyKind::Adt(id_a, _), TyKind::Adt(id_b, _)) => id_a == id_b, - (TyKind::AssociatedType(id_a, _), TyKind::AssociatedType(id_b, _)) => id_a == id_b, - (TyKind::Scalar(scalar_a), TyKind::Scalar(scalar_b)) => scalar_a == scalar_b, - (TyKind::Error, TyKind::Error) - | (TyKind::Str, TyKind::Str) - | (TyKind::Slice(_), TyKind::Slice(_)) - | (TyKind::Never, TyKind::Never) - | (TyKind::Array(_, _), TyKind::Array(_, _)) => true, - (TyKind::Tuple(arity_a, _), TyKind::Tuple(arity_b, _)) => arity_a == arity_b, - (TyKind::OpaqueType(id_a, _), TyKind::OpaqueType(id_b, _)) => id_a == id_b, - (TyKind::FnDef(id_a, _), TyKind::FnDef(id_b, _)) => id_a == id_b, - (TyKind::Ref(id_a, _, _), TyKind::Ref(id_b, _, _)) - | (TyKind::Raw(id_a, _), TyKind::Raw(id_b, _)) => id_a == id_b, - (TyKind::Closure(id_a, _), TyKind::Closure(id_b, _)) => id_a == id_b, - (TyKind::Coroutine(id_a, _), TyKind::Coroutine(id_b, _)) - | (TyKind::CoroutineWitness(id_a, _), TyKind::CoroutineWitness(id_b, _)) => { - id_a == id_b - } - (TyKind::Foreign(id_a), TyKind::Foreign(id_b)) => id_a == id_b, - (_, _) => false, - } - }; - - if let Some(fp) = self_ty_fp { - self.for_trait_impls(trait_id, self_ty_fp, |impls| { - match impls.for_trait_and_self_ty(trait_id, fp).any(check_kind) { - true => ControlFlow::Break(()), - false => ControlFlow::Continue(()), - } - }) - } else { - self.for_trait_impls(trait_id, self_ty_fp, |impls| { - match impls.for_trait(trait_id).any(check_kind) { - true => ControlFlow::Break(()), - false => ControlFlow::Continue(()), - } - }) - } - .is_break() - } - - fn associated_ty_value(&self, id: AssociatedTyValueId) -> Arc { - self.db.associated_ty_value(self.krate, id) - } - - fn custom_clauses(&self) -> Vec> { - vec![] - } - fn local_impls_to_coherence_check(&self, _trait_id: TraitId) -> Vec { - // We don't do coherence checking (yet) - unimplemented!() - } - fn interner(&self) -> Interner { - Interner - } - fn well_known_trait_id( - &self, - well_known_trait: WellKnownTrait, - ) -> Option> { - let lang_item = lang_item_from_well_known_trait(well_known_trait); - let trait_ = lang_item.resolve_trait(self.db, self.krate)?; - Some(to_chalk_trait_id(trait_)) - } - fn well_known_assoc_type_id( - &self, - assoc_type: rust_ir::WellKnownAssocType, - ) -> Option> { - let lang_item = match assoc_type { - rust_ir::WellKnownAssocType::AsyncFnOnceOutput => LangItem::AsyncFnOnceOutput, - }; - let alias = lang_item.resolve_type_alias(self.db, self.krate)?; - Some(to_assoc_type_id(alias)) - } - - fn program_clauses_for_env( - &self, - environment: &chalk_ir::Environment, - ) -> chalk_ir::ProgramClauses { - self.db.program_clauses_for_chalk_env(self.krate, self.block, environment.clone()) - } - - fn opaque_ty_data(&self, id: chalk_ir::OpaqueTyId) -> Arc { - let full_id = self.db.lookup_intern_impl_trait_id(id.into()); - let bound = match full_id { - crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => { - let datas = self - .db - .return_type_impl_traits(func) - .expect("impl trait id without impl traits"); - let (datas, binders) = (*datas).as_ref().into_value_and_skipped_binders(); - let data = &datas.impl_traits[idx]; - let bound = OpaqueTyDatumBound { - bounds: make_single_type_binders(data.bounds.skip_binders().to_vec()), - where_clauses: chalk_ir::Binders::empty(Interner, vec![]), - }; - chalk_ir::Binders::new(binders, bound) - } - crate::ImplTraitId::TypeAliasImplTrait(alias, idx) => { - let datas = self - .db - .type_alias_impl_traits(alias) - .expect("impl trait id without impl traits"); - let (datas, binders) = (*datas).as_ref().into_value_and_skipped_binders(); - let data = &datas.impl_traits[idx]; - let bound = OpaqueTyDatumBound { - bounds: make_single_type_binders(data.bounds.skip_binders().to_vec()), - where_clauses: chalk_ir::Binders::empty(Interner, vec![]), - }; - chalk_ir::Binders::new(binders, bound) - } - crate::ImplTraitId::AsyncBlockTypeImplTrait(..) => { - if let Some((future_trait, future_output)) = - LangItem::Future.resolve_trait(self.db, self.krate).and_then(|trait_| { - let alias = trait_ - .trait_items(self.db) - .associated_type_by_name(&Name::new_symbol_root(sym::Output))?; - Some((trait_, alias)) - }) - { - // Making up Symbol’s value as variable is void: AsyncBlock: - // - // |--------------------OpaqueTyDatum-------------------| - // |-------------OpaqueTyDatumBound--------------| - // for [Future, Future::Output = T] - // ^1 ^0 ^0 ^0 ^1 - let impl_bound = WhereClause::Implemented(TraitRef { - trait_id: to_chalk_trait_id(future_trait), - // Self type as the first parameter. - substitution: Substitution::from1( - Interner, - TyKind::BoundVar(BoundVar { - debruijn: DebruijnIndex::INNERMOST, - index: 0, - }) - .intern(Interner), - ), - }); - let mut binder = vec![]; - binder.push(crate::wrap_empty_binders(impl_bound)); - let sized_trait = LangItem::Sized.resolve_trait(self.db, self.krate); - if let Some(sized_trait_) = sized_trait { - let sized_bound = WhereClause::Implemented(TraitRef { - trait_id: to_chalk_trait_id(sized_trait_), - // Self type as the first parameter. - substitution: Substitution::from1( - Interner, - TyKind::BoundVar(BoundVar { - debruijn: DebruijnIndex::INNERMOST, - index: 0, - }) - .intern(Interner), - ), - }); - binder.push(crate::wrap_empty_binders(sized_bound)); - } - let proj_bound = WhereClause::AliasEq(AliasEq { - alias: AliasTy::Projection(ProjectionTy { - associated_ty_id: to_assoc_type_id(future_output), - // Self type as the first parameter. - substitution: Substitution::from1( - Interner, - TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)) - .intern(Interner), - ), - }), - // The parameter of the opaque type. - ty: TyKind::BoundVar(BoundVar { debruijn: DebruijnIndex::ONE, index: 0 }) - .intern(Interner), - }); - binder.push(crate::wrap_empty_binders(proj_bound)); - let bound = OpaqueTyDatumBound { - bounds: make_single_type_binders(binder), - where_clauses: chalk_ir::Binders::empty(Interner, vec![]), - }; - // The opaque type has 1 parameter. - make_single_type_binders(bound) - } else { - // If failed to find Symbol’s value as variable is void: Future::Output, return empty bounds as fallback. - let bound = OpaqueTyDatumBound { - bounds: chalk_ir::Binders::empty(Interner, vec![]), - where_clauses: chalk_ir::Binders::empty(Interner, vec![]), - }; - // The opaque type has 1 parameter. - make_single_type_binders(bound) - } - } - }; - - Arc::new(OpaqueTyDatum { opaque_ty_id: id, bound }) - } - - fn hidden_opaque_type(&self, _id: chalk_ir::OpaqueTyId) -> chalk_ir::Ty { - // FIXME: actually provide the hidden type; it is relevant for auto traits - TyKind::Error.intern(Interner) - } - - // object safety was renamed to dyn-compatibility but still remains here in chalk. - // This will be removed since we are going to migrate to next-gen trait solver. - fn is_object_safe(&self, trait_id: chalk_ir::TraitId) -> bool { - let trait_ = from_chalk_trait_id(trait_id); - crate::dyn_compatibility::dyn_compatibility(self.db, trait_).is_none() - } - - fn closure_kind( - &self, - _closure_id: chalk_ir::ClosureId, - _substs: &chalk_ir::Substitution, - ) -> rust_ir::ClosureKind { - // Fn is the closure kind that implements all three traits - rust_ir::ClosureKind::Fn - } - fn closure_inputs_and_output( - &self, - _closure_id: chalk_ir::ClosureId, - substs: &chalk_ir::Substitution, - ) -> chalk_ir::Binders> { - let sig_ty = ClosureSubst(substs).sig_ty(); - let sig = &sig_ty.callable_sig(self.db).expect("first closure param should be fn ptr"); - let io = rust_ir::FnDefInputsAndOutputDatum { - argument_types: sig.params().to_vec(), - return_type: sig.ret().clone(), - }; - chalk_ir::Binders::empty(Interner, io.shifted_in(Interner)) - } - fn closure_upvars( - &self, - _closure_id: chalk_ir::ClosureId, - _substs: &chalk_ir::Substitution, - ) -> chalk_ir::Binders> { - let ty = TyBuilder::unit(); - chalk_ir::Binders::empty(Interner, ty) - } - fn closure_fn_substitution( - &self, - _closure_id: chalk_ir::ClosureId, - _substs: &chalk_ir::Substitution, - ) -> chalk_ir::Substitution { - Substitution::empty(Interner) - } - - fn trait_name(&self, trait_id: chalk_ir::TraitId) -> String { - let id = from_chalk_trait_id(trait_id); - self.db.trait_signature(id).name.display(self.db, self.edition()).to_string() - } - fn adt_name(&self, chalk_ir::AdtId(adt_id): AdtId) -> String { - let edition = self.edition(); - match adt_id { - hir_def::AdtId::StructId(id) => { - self.db.struct_signature(id).name.display(self.db, edition).to_string() - } - hir_def::AdtId::EnumId(id) => { - self.db.enum_signature(id).name.display(self.db, edition).to_string() - } - hir_def::AdtId::UnionId(id) => { - self.db.union_signature(id).name.display(self.db, edition).to_string() - } - } - } - fn adt_size_align(&self, _id: chalk_ir::AdtId) -> Arc { - // FIXME - Arc::new(rust_ir::AdtSizeAlign::from_one_zst(false)) - } - fn assoc_type_name(&self, assoc_ty_id: chalk_ir::AssocTypeId) -> String { - let id = self.db.associated_ty_data(from_assoc_type_id(assoc_ty_id)).name; - self.db.type_alias_signature(id).name.display(self.db, self.edition()).to_string() - } - fn opaque_type_name(&self, opaque_ty_id: chalk_ir::OpaqueTyId) -> String { - format!("Opaque_{:?}", opaque_ty_id.0) - } - fn fn_def_name(&self, fn_def_id: chalk_ir::FnDefId) -> String { - format!("fn_{:?}", fn_def_id.0) - } - fn coroutine_datum( - &self, - id: chalk_ir::CoroutineId, - ) -> Arc> { - let InternedCoroutine(parent, expr) = self.db.lookup_intern_coroutine(id.into()); - - // We fill substitution with unknown type, because we only need to know whether the generic - // params are types or consts to build `Binders` and those being filled up are for - // `resume_type`, `yield_type`, and `return_type` of the coroutine in question. - let subst = TyBuilder::subst_for_coroutine(self.db, parent).fill_with_unknown().build(); - - let len = subst.len(Interner); - let input_output = rust_ir::CoroutineInputOutputDatum { - resume_type: TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, len - 3)) - .intern(Interner), - yield_type: TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, len - 2)) - .intern(Interner), - return_type: TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, len - 1)) - .intern(Interner), - // FIXME: calculate upvars - upvars: vec![], - }; - - let it = subst - .iter(Interner) - .map(|it| it.constant(Interner).map(|c| c.data(Interner).ty.clone())); - let input_output = crate::make_type_and_const_binders(it, input_output); - - let movability = match self.db.body(parent)[expr] { - hir_def::hir::Expr::Closure { - closure_kind: hir_def::hir::ClosureKind::Coroutine(movability), - .. - } => movability, - _ => unreachable!("non coroutine expression interned as coroutine"), - }; - let movability = match movability { - Movability::Static => rust_ir::Movability::Static, - Movability::Movable => rust_ir::Movability::Movable, - }; - - Arc::new(rust_ir::CoroutineDatum { movability, input_output }) - } - fn coroutine_witness_datum( - &self, - id: chalk_ir::CoroutineId, - ) -> Arc> { - // FIXME: calculate inner types - let inner_types = - rust_ir::CoroutineWitnessExistential { types: wrap_empty_binders(vec![]) }; - - let InternedCoroutine(parent, _) = self.db.lookup_intern_coroutine(id.into()); - // See the comment in `coroutine_datum()` for unknown types. - let subst = TyBuilder::subst_for_coroutine(self.db, parent).fill_with_unknown().build(); - let it = subst - .iter(Interner) - .map(|it| it.constant(Interner).map(|c| c.data(Interner).ty.clone())); - let inner_types = crate::make_type_and_const_binders(it, inner_types); - - Arc::new(rust_ir::CoroutineWitnessDatum { inner_types }) - } - - fn unification_database(&self) -> &dyn chalk_ir::UnificationDatabase { - &self.db - } -} - -impl ChalkContext<'_> { - fn edition(&self) -> Edition { - self.krate.data(self.db).edition - } - - fn for_trait_impls( - &self, - trait_id: hir_def::TraitId, - self_ty_fp: Option, - mut f: impl FnMut(&TraitImpls) -> ControlFlow<()>, - ) -> ControlFlow<()> { - // Note: Since we're using `impls_for_trait` and `impl_provided_for`, - // only impls where the trait can be resolved should ever reach Chalk. - // `impl_datum` relies on that and will panic if the trait can't be resolved. - let in_deps = self.db.trait_impls_in_deps(self.krate); - let in_self = self.db.trait_impls_in_crate(self.krate); - let trait_module = trait_id.module(self.db); - let type_module = match self_ty_fp { - Some(TyFingerprint::Adt(adt_id)) => Some(adt_id.module(self.db)), - Some(TyFingerprint::ForeignType(type_id)) => { - Some(from_foreign_def_id(type_id).module(self.db)) - } - Some(TyFingerprint::Dyn(trait_id)) => Some(trait_id.module(self.db)), - _ => None, - }; - - let mut def_blocks = - [trait_module.containing_block(), type_module.and_then(|it| it.containing_block())]; - - let block_impls = iter::successors(self.block, |&block_id| { - cov_mark::hit!(block_local_impls); - block_id.loc(self.db).module.containing_block() - }) - .inspect(|&block_id| { - // make sure we don't search the same block twice - def_blocks.iter_mut().for_each(|block| { - if *block == Some(block_id) { - *block = None; - } - }); - }) - .filter_map(|block_id| self.db.trait_impls_in_block(block_id)); - f(&in_self)?; - for it in in_deps.iter().map(ops::Deref::deref) { - f(it)?; - } - for it in block_impls { - f(&it)?; - } - for it in def_blocks.into_iter().flatten().filter_map(|it| self.db.trait_impls_in_block(it)) - { - f(&it)?; - } - ControlFlow::Continue(()) - } -} - impl chalk_ir::UnificationDatabase for &dyn HirDatabase { fn fn_def_variance( &self, @@ -610,15 +54,6 @@ impl chalk_ir::UnificationDatabase for &dyn HirDatabase { } } -pub(crate) fn program_clauses_for_chalk_env_query( - db: &dyn HirDatabase, - krate: Crate, - block: Option, - environment: chalk_ir::Environment, -) -> chalk_ir::ProgramClauses { - chalk_solve::program_clauses_for_env(&ChalkContext { db, krate, block }, &environment) -} - pub(crate) fn associated_ty_data_query( db: &dyn HirDatabase, type_alias: TypeAliasId, @@ -749,31 +184,6 @@ fn well_known_trait_from_lang_item(item: LangItem) -> Option { }) } -fn lang_item_from_well_known_trait(trait_: WellKnownTrait) -> LangItem { - match trait_ { - WellKnownTrait::Clone => LangItem::Clone, - WellKnownTrait::CoerceUnsized => LangItem::CoerceUnsized, - WellKnownTrait::Copy => LangItem::Copy, - WellKnownTrait::DiscriminantKind => LangItem::DiscriminantKind, - WellKnownTrait::DispatchFromDyn => LangItem::DispatchFromDyn, - WellKnownTrait::Drop => LangItem::Drop, - WellKnownTrait::Fn => LangItem::Fn, - WellKnownTrait::FnMut => LangItem::FnMut, - WellKnownTrait::FnOnce => LangItem::FnOnce, - WellKnownTrait::AsyncFn => LangItem::AsyncFn, - WellKnownTrait::AsyncFnMut => LangItem::AsyncFnMut, - WellKnownTrait::AsyncFnOnce => LangItem::AsyncFnOnce, - WellKnownTrait::Coroutine => LangItem::Coroutine, - WellKnownTrait::Sized => LangItem::Sized, - WellKnownTrait::Tuple => LangItem::Tuple, - WellKnownTrait::Unpin => LangItem::Unpin, - WellKnownTrait::Unsize => LangItem::Unsize, - WellKnownTrait::Pointee => LangItem::PointeeTrait, - WellKnownTrait::FnPtr => LangItem::FnPtrTrait, - WellKnownTrait::Future => LangItem::Future, - } -} - pub(crate) fn adt_datum_query( db: &dyn HirDatabase, krate: Crate, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs index 836cc96233eb..8fa1aff0ef63 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs @@ -14,10 +14,9 @@ use hir_def::{ use crate::{ AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, ClosureId, DynTy, FnPointer, ImplTraitId, InEnvironment, Interner, Lifetime, ProjectionTy, - QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, TypeFlags, WhereClause, - db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, - from_placeholder_idx, generics::generics, mapping::ToChalk, to_chalk_trait_id, - utils::ClosureSubst, + QuantifiedWhereClause, Substitution, ToChalk, TraitRef, Ty, TyBuilder, TyKind, TypeFlags, + WhereClause, db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, + from_placeholder_idx, generics::generics, to_chalk_trait_id, utils::ClosureSubst, }; pub trait TyExt { @@ -371,7 +370,7 @@ impl TyExt for Ty { value: InEnvironment::new(&env.env, trait_ref.cast(Interner)), binders: CanonicalVarKinds::empty(Interner), }; - db.trait_solve(crate_id, None, goal).is_some() + !db.trait_solve(crate_id, None, goal).no_solution() } fn equals_ctor(&self, other: &Ty) -> bool { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs index 6449a4dc7e8c..22b152fe034a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs @@ -11,7 +11,7 @@ use test_utils::skip_slow_tests; use crate::{ Const, ConstScalar, Interner, MemoryMap, consteval::try_const_usize, db::HirDatabase, - display::DisplayTarget, mir::pad16, test_db::TestDB, + display::DisplayTarget, mir::pad16, setup_tracing, test_db::TestDB, }; use super::{ @@ -116,6 +116,7 @@ fn pretty_print_err(e: ConstEvalError, db: TestDB) -> String { } fn eval_goal(db: &TestDB, file_id: EditionedFileId) -> Result { + let _tracing = setup_tracing(); let module_id = db.module_for_file(file_id.file_id(db)); let def_map = module_id.def_map(db); let scope = &def_map[module_id.local_id].scope; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs new file mode 100644 index 000000000000..da4aff54de37 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs @@ -0,0 +1,256 @@ +//! Constant evaluation details +// FIXME(next-solver): this should get removed as things get moved to rustc_type_ir from chalk_ir +#![allow(unused)] + +use base_db::Crate; +use hir_def::{ + EnumVariantId, GeneralConstId, + expr_store::{Body, HygieneId, path::Path}, + hir::{Expr, ExprId}, + resolver::{Resolver, ValueNs}, + type_ref::LiteralConstRef, +}; +use hir_expand::Lookup; +use rustc_type_ir::{ + UnevaluatedConst, + inherent::{IntoKind, SliceLike}, +}; +use stdx::never; +use triomphe::Arc; + +use crate::{ + ConstScalar, Interner, MemoryMap, Substitution, TraitEnvironment, + consteval::ConstEvalError, + db::HirDatabase, + generics::Generics, + infer::InferenceContext, + next_solver::{ + Const, ConstBytes, ConstKind, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, + ParamConst, SolverDefId, Ty, ValueConst, + mapping::{ChalkToNextSolver, convert_args_for_result, convert_binder_to_early_binder}, + }, +}; + +use super::mir::{interpret_mir, lower_to_mir, pad16}; + +pub(crate) fn path_to_const<'a, 'g>( + db: &'a dyn HirDatabase, + resolver: &Resolver<'a>, + path: &Path, + args: impl FnOnce() -> &'g Generics, + expected_ty: Ty<'a>, +) -> Option> { + let interner = DbInterner::new_with(db, Some(resolver.krate()), None); + match resolver.resolve_path_in_value_ns_fully(db, path, HygieneId::ROOT) { + Some(ValueNs::GenericParam(p)) => { + let args = args(); + match args + .type_or_const_param(p.into()) + .and_then(|(idx, p)| p.const_param().map(|p| (idx, p.clone()))) + { + Some((idx, _param)) => { + Some(Const::new_param(interner, ParamConst { index: idx as u32 })) + } + None => { + never!( + "Generic list doesn't contain this param: {:?}, {:?}, {:?}", + args, + path, + p + ); + None + } + } + } + Some(ValueNs::ConstId(c)) => { + let args = GenericArgs::new_from_iter(interner, []); + Some(Const::new( + interner, + rustc_type_ir::ConstKind::Unevaluated(UnevaluatedConst::new( + SolverDefId::ConstId(c), + args, + )), + )) + } + _ => None, + } +} + +pub fn unknown_const<'db>(ty: Ty<'db>) -> Const<'db> { + Const::new(DbInterner::conjure(), rustc_type_ir::ConstKind::Error(ErrorGuaranteed)) +} + +pub fn unknown_const_as_generic<'db>(ty: Ty<'db>) -> GenericArg<'db> { + unknown_const(ty).into() +} + +/// Interns a constant scalar with the given type +pub fn intern_const_ref<'a>( + db: &'a dyn HirDatabase, + value: &LiteralConstRef, + ty: Ty<'a>, + krate: Crate, +) -> Const<'a> { + let interner = DbInterner::new_with(db, Some(krate), None); + let layout = db.layout_of_ty_ns(ty, TraitEnvironment::empty(krate)); + let kind = match value { + LiteralConstRef::Int(i) => { + // FIXME: We should handle failure of layout better. + let size = layout.map(|it| it.size.bytes_usize()).unwrap_or(16); + rustc_type_ir::ConstKind::Value(ValueConst::new( + ty, + ConstBytes(i.to_le_bytes()[0..size].into(), MemoryMap::default()), + )) + } + LiteralConstRef::UInt(i) => { + let size = layout.map(|it| it.size.bytes_usize()).unwrap_or(16); + rustc_type_ir::ConstKind::Value(ValueConst::new( + ty, + ConstBytes(i.to_le_bytes()[0..size].into(), MemoryMap::default()), + )) + } + LiteralConstRef::Bool(b) => rustc_type_ir::ConstKind::Value(ValueConst::new( + ty, + ConstBytes(Box::new([*b as u8]), MemoryMap::default()), + )), + LiteralConstRef::Char(c) => rustc_type_ir::ConstKind::Value(ValueConst::new( + ty, + ConstBytes((*c as u32).to_le_bytes().into(), MemoryMap::default()), + )), + LiteralConstRef::Unknown => rustc_type_ir::ConstKind::Error(ErrorGuaranteed), + }; + Const::new(interner, kind) +} + +/// Interns a possibly-unknown target usize +pub fn usize_const<'db>(db: &'db dyn HirDatabase, value: Option, krate: Crate) -> Const<'db> { + intern_const_ref( + db, + &value.map_or(LiteralConstRef::Unknown, LiteralConstRef::UInt), + Ty::new_uint(DbInterner::new_with(db, Some(krate), None), rustc_type_ir::UintTy::Usize), + krate, + ) +} + +pub fn try_const_usize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option { + let interner = DbInterner::new_with(db, None, None); + match (*c).kind() { + ConstKind::Param(_) => None, + ConstKind::Infer(_) => None, + ConstKind::Bound(_, _) => None, + ConstKind::Placeholder(_) => None, + ConstKind::Unevaluated(unevaluated_const) => { + let c = match unevaluated_const.def { + SolverDefId::ConstId(id) => GeneralConstId::ConstId(id), + SolverDefId::StaticId(id) => GeneralConstId::StaticId(id), + _ => unreachable!(), + }; + let subst = convert_args_for_result(interner, unevaluated_const.args.as_slice()); + let ec = db.const_eval(c, subst, None).ok()?.to_nextsolver(interner); + try_const_usize(db, &ec) + } + ConstKind::Value(val) => Some(u128::from_le_bytes(pad16(&val.value.inner().0, false))), + ConstKind::Error(_) => None, + ConstKind::Expr(_) => None, + } +} + +pub fn try_const_isize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option { + let interner = DbInterner::new_with(db, None, None); + match (*c).kind() { + ConstKind::Param(_) => None, + ConstKind::Infer(_) => None, + ConstKind::Bound(_, _) => None, + ConstKind::Placeholder(_) => None, + ConstKind::Unevaluated(unevaluated_const) => { + let c = match unevaluated_const.def { + SolverDefId::ConstId(id) => GeneralConstId::ConstId(id), + SolverDefId::StaticId(id) => GeneralConstId::StaticId(id), + _ => unreachable!(), + }; + let subst = convert_args_for_result(interner, unevaluated_const.args.as_slice()); + let ec = db.const_eval(c, subst, None).ok()?.to_nextsolver(interner); + try_const_isize(db, &ec) + } + ConstKind::Value(val) => Some(i128::from_le_bytes(pad16(&val.value.inner().0, true))), + ConstKind::Error(_) => None, + ConstKind::Expr(_) => None, + } +} + +pub(crate) fn const_eval_discriminant_variant( + db: &dyn HirDatabase, + variant_id: EnumVariantId, +) -> Result { + let interner = DbInterner::new_with(db, None, None); + let def = variant_id.into(); + let body = db.body(def); + let loc = variant_id.lookup(db); + if matches!(body[body.body_expr], Expr::Missing) { + let prev_idx = loc.index.checked_sub(1); + let value = match prev_idx { + Some(prev_idx) => { + 1 + db.const_eval_discriminant( + loc.parent.enum_variants(db).variants[prev_idx as usize].0, + )? + } + _ => 0, + }; + return Ok(value); + } + + let repr = db.enum_signature(loc.parent).repr; + let is_signed = repr.and_then(|repr| repr.int).is_none_or(|int| int.is_signed()); + + let mir_body = db.monomorphized_mir_body( + def, + Substitution::empty(Interner), + db.trait_environment_for_body(def), + )?; + let c = interpret_mir(db, mir_body, false, None)?.0?; + let c = c.to_nextsolver(interner); + let c = if is_signed { + try_const_isize(db, &c).unwrap() + } else { + try_const_usize(db, &c).unwrap() as i128 + }; + Ok(c) +} + +// FIXME: Ideally constants in const eval should have separate body (issue #7434), and this function should +// get an `InferenceResult` instead of an `InferenceContext`. And we should remove `ctx.clone().resolve_all()` here +// and make this function private. See the fixme comment on `InferenceContext::resolve_all`. +pub(crate) fn eval_to_const<'db>(expr: ExprId, ctx: &mut InferenceContext<'db>) -> Const<'db> { + let interner = DbInterner::new_with(ctx.db, None, None); + let infer = ctx.clone().resolve_all(); + fn has_closure(body: &Body, expr: ExprId) -> bool { + if matches!(body[expr], Expr::Closure { .. }) { + return true; + } + let mut r = false; + body.walk_child_exprs(expr, |idx| r |= has_closure(body, idx)); + r + } + if has_closure(ctx.body, expr) { + // Type checking clousres need an isolated body (See the above FIXME). Bail out early to prevent panic. + return unknown_const(infer[expr].clone().to_nextsolver(interner)); + } + if let Expr::Path(p) = &ctx.body[expr] { + let resolver = &ctx.resolver; + if let Some(c) = path_to_const( + ctx.db, + resolver, + p, + || ctx.generics(), + infer[expr].to_nextsolver(interner), + ) { + return c; + } + } + if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, ctx.body, &infer, expr) + && let Ok((Ok(result), _)) = interpret_mir(ctx.db, Arc::new(mir_body), true, None) + { + return result.to_nextsolver(interner); + } + unknown_const(infer[expr].to_nextsolver(interner)) +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index b3d46845c443..18b8db21161f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -17,7 +17,7 @@ use smallvec::SmallVec; use triomphe::Arc; use crate::{ - Binders, Const, ImplTraitId, ImplTraits, InferenceResult, Interner, PolyFnSig, Substitution, + Binders, Const, ImplTraitId, ImplTraits, InferenceResult, PolyFnSig, Substitution, TraitEnvironment, TraitRef, Ty, TyDefId, ValueTyDefId, chalk_db, consteval::ConstEvalError, drop::DropGlue, @@ -26,6 +26,7 @@ use crate::{ lower::{Diagnostics, GenericDefaults, GenericPredicates}, method_resolution::{InherentImpls, TraitImpls, TyFingerprint}, mir::{BorrowckResult, MirBody, MirLowerError}, + traits::NextTraitSolveResult, }; #[query_group::query_group] @@ -295,24 +296,162 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { ) -> Ty; #[salsa::invoke(crate::traits::trait_solve_query)] + #[salsa::transparent] fn trait_solve( &self, krate: Crate, block: Option, goal: crate::Canonical>, - ) -> Option; - - #[salsa::invoke(chalk_db::program_clauses_for_chalk_env_query)] - fn program_clauses_for_chalk_env( - &self, - krate: Crate, - block: Option, - env: chalk_ir::Environment, - ) -> chalk_ir::ProgramClauses; + ) -> NextTraitSolveResult; #[salsa::invoke(crate::drop::has_drop_glue)] #[salsa::cycle(cycle_result = crate::drop::has_drop_glue_cycle_result)] fn has_drop_glue(&self, ty: Ty, env: Arc) -> DropGlue; + + // next trait solver + + #[salsa::invoke(crate::layout::layout_of_adt_ns_query)] + #[salsa::cycle(cycle_result = crate::layout::layout_of_adt_ns_cycle_result)] + fn layout_of_adt_ns<'db>( + &'db self, + def: AdtId, + args: crate::next_solver::GenericArgs<'db>, + trait_env: Arc, + ) -> Result, LayoutError>; + + #[salsa::invoke(crate::layout::layout_of_ty_ns_query)] + #[salsa::cycle(cycle_result = crate::layout::layout_of_ty_ns_cycle_result)] + fn layout_of_ty_ns<'db>( + &'db self, + ty: crate::next_solver::Ty<'db>, + env: Arc, + ) -> Result, LayoutError>; + + #[salsa::invoke(crate::lower_nextsolver::ty_query)] + #[salsa::transparent] + fn ty_ns<'db>( + &'db self, + def: TyDefId, + ) -> crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>; + + #[salsa::invoke(crate::lower_nextsolver::type_for_type_alias_with_diagnostics_query)] + #[salsa::cycle(cycle_result = crate::lower_nextsolver::type_for_type_alias_with_diagnostics_cycle_result)] + fn type_for_type_alias_with_diagnostics_ns<'db>( + &'db self, + def: TypeAliasId, + ) -> (crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>, Diagnostics); + + #[salsa::invoke(crate::lower_nextsolver::impl_self_ty_with_diagnostics_query)] + #[salsa::cycle(cycle_result = crate::lower_nextsolver::impl_self_ty_with_diagnostics_cycle_result)] + fn impl_self_ty_with_diagnostics_ns<'db>( + &'db self, + def: ImplId, + ) -> (crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>, Diagnostics); + + #[salsa::invoke(crate::lower_nextsolver::impl_self_ty_query)] + #[salsa::transparent] + fn impl_self_ty_ns<'db>( + &'db self, + def: ImplId, + ) -> crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>; + + // FIXME: Make this a non-interned query. + #[salsa::invoke_interned(crate::lower_nextsolver::const_param_ty_with_diagnostics_query)] + fn const_param_ty_with_diagnostics_ns<'db>( + &'db self, + def: ConstParamId, + ) -> (crate::next_solver::Ty<'db>, Diagnostics); + + #[salsa::invoke(crate::lower_nextsolver::const_param_ty_query)] + #[salsa::transparent] + fn const_param_ty_ns<'db>(&'db self, def: ConstParamId) -> crate::next_solver::Ty<'db>; + + #[salsa::invoke(crate::lower_nextsolver::impl_trait_with_diagnostics_query)] + fn impl_trait_with_diagnostics_ns<'db>( + &'db self, + def: ImplId, + ) -> Option<( + crate::next_solver::EarlyBinder<'db, crate::next_solver::TraitRef<'db>>, + Diagnostics, + )>; + + #[salsa::invoke(crate::lower_nextsolver::impl_trait_query)] + #[salsa::transparent] + fn impl_trait_ns<'db>( + &'db self, + def: ImplId, + ) -> Option>>; + + #[salsa::invoke(crate::lower_nextsolver::field_types_with_diagnostics_query)] + fn field_types_with_diagnostics_ns<'db>( + &'db self, + var: VariantId, + ) -> ( + Arc< + ArenaMap< + LocalFieldId, + crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>, + >, + >, + Diagnostics, + ); + + #[salsa::invoke(crate::lower_nextsolver::field_types_query)] + #[salsa::transparent] + fn field_types_ns<'db>( + &'db self, + var: VariantId, + ) -> Arc< + ArenaMap>>, + >; + + #[salsa::invoke(crate::lower_nextsolver::callable_item_signature_query)] + fn callable_item_signature_ns<'db>( + &'db self, + def: CallableDefId, + ) -> crate::next_solver::EarlyBinder<'db, crate::next_solver::PolyFnSig<'db>>; + + #[salsa::invoke(crate::lower_nextsolver::return_type_impl_traits)] + fn return_type_impl_traits_ns<'db>( + &'db self, + def: FunctionId, + ) -> Option>>>; + + #[salsa::invoke(crate::lower_nextsolver::type_alias_impl_traits)] + fn type_alias_impl_traits_ns<'db>( + &'db self, + def: TypeAliasId, + ) -> Option>>>; + + #[salsa::invoke(crate::lower_nextsolver::generic_predicates_for_param_query)] + #[salsa::cycle(cycle_result = crate::lower_nextsolver::generic_predicates_for_param_cycle_result)] + fn generic_predicates_for_param_ns<'db>( + &'db self, + def: GenericDefId, + param_id: TypeOrConstParamId, + assoc_name: Option, + ) -> crate::lower_nextsolver::GenericPredicates<'db>; + + #[salsa::invoke(crate::lower_nextsolver::generic_predicates_query)] + fn generic_predicates_ns<'db>( + &'db self, + def: GenericDefId, + ) -> crate::lower_nextsolver::GenericPredicates<'db>; + + #[salsa::invoke( + crate::lower_nextsolver::generic_predicates_without_parent_with_diagnostics_query + )] + fn generic_predicates_without_parent_with_diagnostics_ns<'db>( + &'db self, + def: GenericDefId, + ) -> (crate::lower_nextsolver::GenericPredicates<'db>, Diagnostics); + + #[salsa::invoke(crate::lower_nextsolver::generic_predicates_without_parent_query)] + #[salsa::transparent] + fn generic_predicates_without_parent_ns<'db>( + &'db self, + def: GenericDefId, + ) -> crate::lower_nextsolver::GenericPredicates<'db>; } #[test] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs b/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs index 5577be890da3..a7e942d92442 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs @@ -187,7 +187,7 @@ fn is_copy(db: &dyn HirDatabase, ty: Ty, env: Arc) -> bool { value: InEnvironment::new(&env.env, trait_ref.cast(Interner)), binders: CanonicalVarKinds::empty(Interner), }; - db.trait_solve(env.krate, env.block, goal).is_some() + db.trait_solve(env.krate, env.block, goal).certain() } pub(crate) fn has_drop_glue_cycle_result( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index 6294d683e6c0..2d21947ec60f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -16,8 +16,8 @@ use rustc_hash::FxHashSet; use smallvec::SmallVec; use crate::{ - AliasEq, AliasTy, Binders, BoundVar, CallableSig, GoalData, ImplTraitId, Interner, OpaqueTyId, - ProjectionTyExt, Solution, Substitution, TraitRef, Ty, TyKind, WhereClause, all_super_traits, + AliasEq, AliasTy, Binders, BoundVar, CallableSig, DomainGoal, GoalData, ImplTraitId, Interner, + OpaqueTyId, ProjectionTyExt, Substitution, TraitRef, Ty, TyKind, WhereClause, all_super_traits, db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, generics::{generics, trait_self_param_idx}, @@ -510,6 +510,8 @@ fn receiver_is_dispatchable( trait_id: to_chalk_trait_id(unsize_did), substitution: Substitution::from_iter(Interner, [self_ty.clone(), unsized_self_ty.clone()]), }); + let unsized_predicate = + Binders::empty(Interner, unsized_predicate.cast::(Interner)); let trait_predicate = WhereClause::Implemented(TraitRef { trait_id: to_chalk_trait_id(trait_), substitution: Substitution::from_iter( @@ -518,18 +520,32 @@ fn receiver_is_dispatchable( .chain(placeholder_subst.iter(Interner).skip(1).cloned()), ), }); + let trait_predicate = Binders::empty(Interner, trait_predicate.cast::(Interner)); let generic_predicates = &*db.generic_predicates(func.into()); - let clauses = std::iter::once(unsized_predicate) - .chain(std::iter::once(trait_predicate)) - .chain(generic_predicates.iter().map(|pred| { - pred.clone().substitute(Interner, &placeholder_subst).into_value_and_skipped_binders().0 - })) - .map(|pred| { - pred.cast::>(Interner).into_from_env_clause(Interner) - }); - let env = chalk_ir::Environment::new(Interner).add_clauses(Interner, clauses); + let goals = std::iter::once(unsized_predicate).chain(std::iter::once(trait_predicate)).chain( + generic_predicates.iter().map(|pred| { + pred.clone() + .substitute(Interner, &placeholder_subst) + .map(|g| g.cast::(Interner)) + }), + ); + let clauses = chalk_ir::ProgramClauses::from_iter( + Interner, + goals.into_iter().map(|g| { + chalk_ir::ProgramClause::new( + Interner, + chalk_ir::ProgramClauseData(g.map(|g| chalk_ir::ProgramClauseImplication { + consequence: g, + conditions: chalk_ir::Goals::empty(Interner), + constraints: chalk_ir::Constraints::empty(Interner), + priority: chalk_ir::ClausePriority::High, + })), + ) + }), + ); + let env: chalk_ir::Environment = chalk_ir::Environment { clauses }; let obligation = WhereClause::Implemented(TraitRef { trait_id: to_chalk_trait_id(dispatch_from_dyn_did), @@ -541,9 +557,8 @@ fn receiver_is_dispatchable( let mut table = chalk_solve::infer::InferenceTable::::new(); let canonicalized = table.canonicalize(Interner, in_env); - let solution = db.trait_solve(krate, None, canonicalized.quantified); - matches!(solution, Some(Solution::Unique(_))) + db.trait_solve(krate, None, canonicalized.quantified).certain() } fn receiver_for_self_ty(db: &dyn HirDatabase, func: FunctionId, ty: Ty) -> Option { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility/tests.rs index 5078e8cfaa8b..4ffa45508497 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility/tests.rs @@ -56,18 +56,21 @@ fn check_dyn_compatibility<'a>( continue; }; let mut osvs = FxHashSet::default(); - _ = dyn_compatibility_with_callback(&db, trait_id, &mut |osv| { - osvs.insert(match osv { - DynCompatibilityViolation::SizedSelf => SizedSelf, - DynCompatibilityViolation::SelfReferential => SelfReferential, - DynCompatibilityViolation::Method(_, mvc) => Method(mvc), - DynCompatibilityViolation::AssocConst(_) => AssocConst, - DynCompatibilityViolation::GAT(_) => GAT, - DynCompatibilityViolation::HasNonCompatibleSuperTrait(_) => { - HasNonCompatibleSuperTrait - } + let db = &db; + salsa::attach(db, || { + _ = dyn_compatibility_with_callback(db, trait_id, &mut |osv| { + osvs.insert(match osv { + DynCompatibilityViolation::SizedSelf => SizedSelf, + DynCompatibilityViolation::SelfReferential => SelfReferential, + DynCompatibilityViolation::Method(_, mvc) => Method(mvc), + DynCompatibilityViolation::AssocConst(_) => AssocConst, + DynCompatibilityViolation::GAT(_) => GAT, + DynCompatibilityViolation::HasNonCompatibleSuperTrait(_) => { + HasNonCompatibleSuperTrait + } + }); + ControlFlow::Continue(()) }); - ControlFlow::Continue(()) }); assert_eq!(osvs, expected, "dyn-compatibility violations for `{name}` do not match;"); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs index f14872e68c3f..e6470e5272d0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs @@ -165,6 +165,21 @@ impl Generics { (parent_len, self_param, type_params, const_params, impl_trait_params, lifetime_params) } + pub(crate) fn type_or_const_param( + &self, + param: TypeOrConstParamId, + ) -> Option<(usize, TypeOrConstParamData)> { + let idx = self.find_type_or_const_param(param)?; + self.iter().nth(idx).and_then(|p| { + let data = match p.1 { + GenericParamDataRef::TypeParamData(p) => p.clone().into(), + GenericParamDataRef::ConstParamData(p) => p.clone().into(), + _ => return None, + }; + Some((idx, data)) + }) + } + pub fn type_or_const_param_idx(&self, param: TypeOrConstParamId) -> Option { self.find_type_or_const_param(param) } @@ -272,7 +287,7 @@ pub(crate) fn trait_self_param_idx(db: &dyn DefDatabase, def: GenericDefId) -> O } } -fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option { +pub(crate) fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option { let container = match def { GenericDefId::FunctionId(it) => it.lookup(db).container, GenericDefId::TypeAliasId(it) => it.lookup(db).container, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 86345b23364d..7a11ec660d04 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -88,52 +88,54 @@ pub(crate) use closure::{CaptureKind, CapturedItem, CapturedItemWithoutTy}; /// The entry point of type inference. pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc { - let _p = tracing::info_span!("infer_query").entered(); - let resolver = def.resolver(db); - let body = db.body(def); - let mut ctx = InferenceContext::new(db, def, &body, resolver); + crate::next_solver::with_new_cache(|| { + let _p = tracing::info_span!("infer_query").entered(); + let resolver = def.resolver(db); + let body = db.body(def); + let mut ctx = InferenceContext::new(db, def, &body, resolver); - match def { - DefWithBodyId::FunctionId(f) => { - ctx.collect_fn(f); - } - DefWithBodyId::ConstId(c) => ctx.collect_const(c, &db.const_signature(c)), - DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_signature(s)), - DefWithBodyId::VariantId(v) => { - ctx.return_ty = TyBuilder::builtin( - match db.enum_signature(v.lookup(db).parent).variant_body_type() { - hir_def::layout::IntegerType::Pointer(signed) => match signed { - true => BuiltinType::Int(BuiltinInt::Isize), - false => BuiltinType::Uint(BuiltinUint::Usize), + match def { + DefWithBodyId::FunctionId(f) => { + ctx.collect_fn(f); + } + DefWithBodyId::ConstId(c) => ctx.collect_const(c, &db.const_signature(c)), + DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_signature(s)), + DefWithBodyId::VariantId(v) => { + ctx.return_ty = TyBuilder::builtin( + match db.enum_signature(v.lookup(db).parent).variant_body_type() { + hir_def::layout::IntegerType::Pointer(signed) => match signed { + true => BuiltinType::Int(BuiltinInt::Isize), + false => BuiltinType::Uint(BuiltinUint::Usize), + }, + hir_def::layout::IntegerType::Fixed(size, signed) => match signed { + true => BuiltinType::Int(match size { + Integer::I8 => BuiltinInt::I8, + Integer::I16 => BuiltinInt::I16, + Integer::I32 => BuiltinInt::I32, + Integer::I64 => BuiltinInt::I64, + Integer::I128 => BuiltinInt::I128, + }), + false => BuiltinType::Uint(match size { + Integer::I8 => BuiltinUint::U8, + Integer::I16 => BuiltinUint::U16, + Integer::I32 => BuiltinUint::U32, + Integer::I64 => BuiltinUint::U64, + Integer::I128 => BuiltinUint::U128, + }), + }, }, - hir_def::layout::IntegerType::Fixed(size, signed) => match signed { - true => BuiltinType::Int(match size { - Integer::I8 => BuiltinInt::I8, - Integer::I16 => BuiltinInt::I16, - Integer::I32 => BuiltinInt::I32, - Integer::I64 => BuiltinInt::I64, - Integer::I128 => BuiltinInt::I128, - }), - false => BuiltinType::Uint(match size { - Integer::I8 => BuiltinUint::U8, - Integer::I16 => BuiltinUint::U16, - Integer::I32 => BuiltinUint::U32, - Integer::I64 => BuiltinUint::U64, - Integer::I128 => BuiltinUint::U128, - }), - }, - }, - ); + ); + } } - } - ctx.infer_body(); + ctx.infer_body(); - ctx.infer_mut_body(); + ctx.infer_mut_body(); - ctx.infer_closures(); + ctx.infer_closures(); - Arc::new(ctx.resolve_all()) + Arc::new(ctx.resolve_all()) + }) } pub(crate) fn infer_cycle_result(_: &dyn HirDatabase, _: DefWithBodyId) -> Arc { @@ -144,6 +146,7 @@ pub(crate) fn infer_cycle_result(_: &dyn HirDatabase, _: DefWithBodyId) -> Arc, ty: Ty) -> Ty { // FIXME: TypeFlags::HAS_CT_PROJECTION is not implemented in chalk, so TypeFlags::HAS_PROJECTION only // works for the type case, so we check array unconditionally. Remove the array part @@ -1077,14 +1080,22 @@ impl<'db> InferenceContext<'db> { // Functions might be defining usage sites of TAITs. // To define an TAITs, that TAIT must appear in the function's signatures. // So, it suffices to check for params and return types. - if self - .return_ty - .data(Interner) - .flags - .intersects(TypeFlags::HAS_TY_OPAQUE.union(TypeFlags::HAS_TY_INFER)) - { - tait_candidates.insert(self.return_ty.clone()); - } + fold_tys( + self.return_ty.clone(), + |ty, _| { + match ty.kind(Interner) { + TyKind::OpaqueType(..) + | TyKind::Alias(AliasTy::Opaque(..)) + | TyKind::InferenceVar(..) => { + tait_candidates.insert(self.return_ty.clone()); + } + _ => {} + } + ty + }, + DebruijnIndex::INNERMOST, + ); + self.make_tait_coercion_table(tait_candidates.iter()); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 8024c1a9a4e9..d8fc20e8741c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -3,12 +3,13 @@ use std::{cmp, convert::Infallible, mem, ops::ControlFlow}; use chalk_ir::{ - BoundVar, DebruijnIndex, FnSubst, Mutability, TyKind, + BoundVar, DebruijnIndex, FnSubst, GenericArg, Mutability, TyKind, cast::Cast, fold::{FallibleTypeFolder, Shift, TypeFoldable}, visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, }; use either::Either; +use hir_def::Lookup; use hir_def::{ DefWithBodyId, FieldId, HasModule, TupleFieldId, TupleId, VariantId, expr_store::path::Path, @@ -19,8 +20,8 @@ use hir_def::{ item_tree::FieldsShape, lang_item::LangItem, resolver::ValueNs, + type_ref::TypeRefId, }; -use hir_def::{Lookup, type_ref::TypeRefId}; use hir_expand::name::Name; use intern::sym; use rustc_hash::{FxHashMap, FxHashSet}; @@ -30,8 +31,8 @@ use syntax::utils::is_raw_identifier; use crate::{ Adjust, Adjustment, AliasEq, AliasTy, Binders, BindingMode, ChalkTraitId, ClosureId, DynTy, - DynTyExt, FnAbi, FnPointer, FnSig, GenericArg, Interner, OpaqueTy, ProjectionTy, - ProjectionTyExt, Substitution, Ty, TyBuilder, TyExt, WhereClause, + DynTyExt, FnAbi, FnPointer, FnSig, Interner, OpaqueTy, ProjectionTy, ProjectionTyExt, + Substitution, Ty, TyBuilder, TyExt, WhereClause, db::{HirDatabase, InternedClosure, InternedCoroutine}, error_lifetime, from_assoc_type_id, from_chalk_trait_id, from_placeholder_idx, generics::Generics, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index 761a2564aa79..631a91bac481 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -13,14 +13,15 @@ use stdx::always; use triomphe::Arc; use crate::{ - Canonical, DomainGoal, FnAbi, FnPointer, FnSig, Guidance, InEnvironment, Interner, Lifetime, - Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, + Canonical, DomainGoal, FnAbi, FnPointer, FnSig, InEnvironment, Interner, Lifetime, + Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, autoderef::{Autoderef, AutoderefKind}, db::HirDatabase, infer::{ Adjust, Adjustment, AutoBorrow, InferOk, InferenceContext, OverloadedDeref, PointerCast, TypeError, TypeMismatch, }, + traits::NextTraitSolveResult, utils::ClosureSubst, }; @@ -715,13 +716,18 @@ impl InferenceTable<'_> { // solve `CoerceUnsized` and `Unsize` goals at this point and leaves the // rest for later. Also, there's some logic about sized type variables. // Need to find out in what cases this is necessary - let solution = self - .db - .trait_solve(krate, self.trait_env.block, canonicalized.value.clone().cast(Interner)) - .ok_or(TypeError)?; + let solution = self.db.trait_solve( + krate, + self.trait_env.block, + canonicalized.value.clone().cast(Interner), + ); match solution { - Solution::Unique(v) => { + // FIXME: this is a weaker guarantee than Chalk's `Guidance::Unique` + // was. Chalk's unique guidance at least guarantees that the real solution + // is some "subset" of the solutions matching the guidance, but the + // substs for `Certainty::No` don't have that same guarantee (I think). + NextTraitSolveResult::Certain(v) => { canonicalized.apply_solution( self, Canonical { @@ -731,13 +737,12 @@ impl InferenceTable<'_> { }, ); } - Solution::Ambig(Guidance::Definite(subst)) => { - // FIXME need to record an obligation here - canonicalized.apply_solution(self, subst) + // ...so, should think about how to get some actually get some guidance here + NextTraitSolveResult::Uncertain(..) | NextTraitSolveResult::NoSolution => { + return Err(TypeError); } - // FIXME actually we maybe should also accept unknown guidance here - _ => return Err(TypeError), - }; + } + let unsize = Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target: to_ty.clone() }; let adjustments = match reborrow { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 16fc2bfc0631..261c02386822 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -278,6 +278,7 @@ impl InferenceContext<'_> { } } + #[tracing::instrument(level = "debug", skip(self, is_read), ret)] fn infer_expr_inner( &mut self, tgt_expr: ExprId, @@ -286,7 +287,9 @@ impl InferenceContext<'_> { ) -> Ty { self.db.unwind_if_revision_cancelled(); - let ty = match &self.body[tgt_expr] { + let expr = &self.body[tgt_expr]; + tracing::trace!(?expr); + let ty = match expr { Expr::Missing => self.err_ty(), &Expr::If { condition, then_branch, else_branch } => { let expected = &expected.adjust_for_branches(&mut self.table); @@ -1949,7 +1952,11 @@ impl InferenceContext<'_> { sig.ret().clone(), sig.is_varargs, ), - None => ((self.err_ty(), Vec::new()), self.err_ty(), true), + None => { + let formal_receiver_ty = self.table.new_type_var(); + let ret_ty = self.table.new_type_var(); + ((formal_receiver_ty, Vec::new()), ret_ty, true) + } }; self.unify(&formal_receiver_ty, &receiver_ty); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index c07755535f2a..a709aebfa9c1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -19,11 +19,13 @@ use triomphe::Arc; use super::{InferOk, InferResult, InferenceContext, TypeError}; use crate::{ AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue, DebruijnIndex, DomainGoal, - GenericArg, GenericArgData, Goal, GoalData, Guidance, InEnvironment, InferenceVar, Interner, - Lifetime, OpaqueTyId, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Solution, Substitution, - TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, VariableKind, WhereClause, - consteval::unknown_const, db::HirDatabase, fold_generic_args, fold_tys_and_consts, - to_chalk_trait_id, traits::FnTrait, + GenericArg, GenericArgData, Goal, GoalData, InEnvironment, InferenceVar, Interner, Lifetime, + OpaqueTyId, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Substitution, TraitEnvironment, + TraitRef, Ty, TyBuilder, TyExt, TyKind, VariableKind, WhereClause, + consteval::unknown_const, + db::HirDatabase, + fold_generic_args, fold_tys_and_consts, to_chalk_trait_id, + traits::{FnTrait, NextTraitSolveResult}, }; impl InferenceContext<'_> { @@ -116,9 +118,12 @@ impl> Canonicalized { // eagerly replace projections in the type; we may be getting types // e.g. from where clauses where this hasn't happened yet let ty = ctx.normalize_associated_types_in(new_vars.apply(ty.clone(), Interner)); + tracing::debug!("unifying {:?} {:?}", var, ty); ctx.unify(var.assert_ty_ref(Interner), &ty); } else { - let _ = ctx.try_unify(var, &new_vars.apply(v.clone(), Interner)); + let v = new_vars.apply(v.clone(), Interner); + tracing::debug!("try_unifying {:?} {:?}", var, v); + let _ = ctx.try_unify(var, &v); } } } @@ -326,6 +331,7 @@ impl<'a> InferenceTable<'a> { /// type annotation (e.g. from a let type annotation, field type or function /// call). `make_ty` handles this already, but e.g. for field types we need /// to do it as well. + #[tracing::instrument(skip(self), ret)] pub(crate) fn normalize_associated_types_in(&mut self, ty: T) -> T where T: HasInterner + TypeFoldable, @@ -333,12 +339,88 @@ impl<'a> InferenceTable<'a> { fold_tys_and_consts( ty, |e, _| match e { - Either::Left(ty) => Either::Left(match ty.kind(Interner) { - TyKind::Alias(AliasTy::Projection(proj_ty)) => { - self.normalize_projection_ty(proj_ty.clone()) - } - _ => ty, - }), + Either::Left(ty) => { + let ty = self.resolve_ty_shallow(&ty); + tracing::debug!(?ty); + Either::Left(match ty.kind(Interner) { + TyKind::Alias(AliasTy::Projection(proj_ty)) => { + let ty = self.normalize_projection_ty(proj_ty.clone()); + self.resolve_ty_shallow(&ty) + } + TyKind::AssociatedType(id, subst) => { + if ty.data(Interner).flags.intersects( + chalk_ir::TypeFlags::HAS_TY_INFER + | chalk_ir::TypeFlags::HAS_CT_INFER, + ) { + return Either::Left(ty); + } + let var = self.new_type_var(); + let proj_ty = chalk_ir::ProjectionTy { + associated_ty_id: *id, + substitution: subst.clone(), + }; + let normalize = chalk_ir::Normalize { + alias: AliasTy::Projection(proj_ty), + ty: var.clone(), + }; + let goal = chalk_ir::Goal::new( + Interner, + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Normalize( + normalize, + )), + ); + let in_env = InEnvironment::new(&self.trait_env.env, goal); + + let canonicalized = { + let result = + self.var_unification_table.canonicalize(Interner, in_env); + let free_vars = result + .free_vars + .into_iter() + .map(|free_var| free_var.to_generic_arg(Interner)) + .collect(); + Canonicalized { value: result.quantified, free_vars } + }; + let solution = self.db.trait_solve( + self.trait_env.krate, + self.trait_env.block, + canonicalized.value.clone(), + ); + if let NextTraitSolveResult::Certain(canonical_subst) = solution { + // This is not great :) But let's just assert this for now and come back to it later. + if canonical_subst.value.subst.len(Interner) != 1 { + ty + } else { + let normalized = canonical_subst.value.subst.as_slice(Interner) + [0] + .assert_ty_ref(Interner); + match normalized.kind(Interner) { + TyKind::Alias(AliasTy::Projection(proj_ty)) => { + if id == &proj_ty.associated_ty_id + && subst == &proj_ty.substitution + { + ty + } else { + normalized.clone() + } + } + TyKind::AssociatedType(new_id, new_subst) => { + if new_id == id && new_subst == subst { + ty + } else { + normalized.clone() + } + } + _ => normalized.clone(), + } + } + } else { + ty + } + } + _ => ty, + }) + } Either::Right(c) => Either::Right(match &c.data(Interner).value { chalk_ir::ConstValue::Concrete(cc) => match &cc.interned { crate::ConstScalar::UnevaluatedConst(c_id, subst) => { @@ -588,7 +670,6 @@ impl<'a> InferenceTable<'a> { } /// Unify two relatable values (e.g. `Ty`) and register new trait goals that arise from that. - #[tracing::instrument(skip_all)] pub(crate) fn unify>(&mut self, ty1: &T, ty2: &T) -> bool { let result = match self.try_unify(ty1, ty2) { Ok(r) => r, @@ -606,7 +687,7 @@ impl<'a> InferenceTable<'a> { }; result.goals.iter().all(|goal| { let canonicalized = self.canonicalize_with_free_vars(goal.clone()); - self.try_resolve_obligation(&canonicalized).is_some() + self.try_resolve_obligation(&canonicalized).certain() }) } @@ -633,6 +714,9 @@ impl<'a> InferenceTable<'a> { /// If `ty` is a type variable with known type, returns that type; /// otherwise, return ty. pub(crate) fn resolve_ty_shallow(&mut self, ty: &Ty) -> Ty { + if !ty.data(Interner).flags.intersects(chalk_ir::TypeFlags::HAS_FREE_LOCAL_NAMES) { + return ty.clone(); + } self.resolve_obligations_as_possible(); self.var_unification_table.normalize_ty_shallow(Interner, ty).unwrap_or_else(|| ty.clone()) } @@ -662,7 +746,8 @@ impl<'a> InferenceTable<'a> { /// Checks an obligation without registering it. Useful mostly to check /// whether a trait *might* be implemented before deciding to 'lock in' the /// choice (during e.g. method resolution or deref). - pub(crate) fn try_obligation(&mut self, goal: Goal) -> Option { + #[tracing::instrument(level = "debug", skip(self))] + pub(crate) fn try_obligation(&mut self, goal: Goal) -> NextTraitSolveResult { let in_env = InEnvironment::new(&self.trait_env.env, goal); let canonicalized = self.canonicalize(in_env); @@ -674,10 +759,45 @@ impl<'a> InferenceTable<'a> { self.register_obligation_in_env(in_env) } + #[tracing::instrument(level = "debug", skip(self))] fn register_obligation_in_env(&mut self, goal: InEnvironment) { - let canonicalized = self.canonicalize_with_free_vars(goal); + match goal.goal.data(Interner) { + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds( + chalk_ir::WhereClause::AliasEq(chalk_ir::AliasEq { alias, ty }), + )) => { + if ty.inference_var(Interner).is_some() { + match alias { + chalk_ir::AliasTy::Opaque(opaque) => { + if self.unify( + &chalk_ir::TyKind::OpaqueType( + opaque.opaque_ty_id, + opaque.substitution.clone(), + ) + .intern(Interner), + ty, + ) { + return; + } + } + _ => {} + } + } + } + _ => {} + } + let canonicalized = { + let result = self.var_unification_table.canonicalize(Interner, goal); + let free_vars = result + .free_vars + .into_iter() + .map(|free_var| free_var.to_generic_arg(Interner)) + .collect(); + Canonicalized { value: result.quantified, free_vars } + }; + tracing::debug!(?canonicalized); let solution = self.try_resolve_obligation(&canonicalized); - if matches!(solution, Some(Solution::Ambig(_))) { + tracing::debug!(?solution); + if solution.uncertain() { self.pending_obligations.push(canonicalized); } } @@ -694,7 +814,9 @@ impl<'a> InferenceTable<'a> { mem::swap(&mut self.pending_obligations, &mut obligations); for canonicalized in obligations.drain(..) { + tracing::debug!(obligation = ?canonicalized); if !self.check_changed(&canonicalized) { + tracing::debug!("not changed"); self.pending_obligations.push(canonicalized); continue; } @@ -799,37 +921,36 @@ impl<'a> InferenceTable<'a> { }) } + #[tracing::instrument(level = "debug", skip(self))] fn try_resolve_obligation( &mut self, canonicalized: &Canonicalized>, - ) -> Option> { + ) -> NextTraitSolveResult { let solution = self.db.trait_solve( self.trait_env.krate, self.trait_env.block, canonicalized.value.clone(), ); + tracing::debug!(?solution, ?canonicalized); match &solution { - Some(Solution::Unique(canonical_subst)) => { + NextTraitSolveResult::Certain(v) => { canonicalized.apply_solution( self, Canonical { - binders: canonical_subst.binders.clone(), - // FIXME: handle constraints - value: canonical_subst.value.subst.clone(), + binders: v.binders.clone(), + // FIXME handle constraints + value: v.value.subst.clone(), }, ); } - Some(Solution::Ambig(Guidance::Definite(substs))) => { - canonicalized.apply_solution(self, substs.clone()); - } - Some(_) => { - // FIXME use this when trying to resolve everything at the end - } - None => { - // FIXME obligation cannot be fulfilled => diagnostic + // ...so, should think about how to get some actually get some guidance here + NextTraitSolveResult::Uncertain(v) => { + canonicalized.apply_solution(self, v.clone()); } + NextTraitSolveResult::NoSolution => {} } + solution } @@ -896,7 +1017,10 @@ impl<'a> InferenceTable<'a> { environment: trait_env.clone(), }; let canonical = self.canonicalize(obligation.clone()); - if self.db.trait_solve(krate, self.trait_env.block, canonical.cast(Interner)).is_some() + if !self + .db + .trait_solve(krate, self.trait_env.block, canonical.cast(Interner)) + .no_solution() { self.register_obligation(obligation.goal); let return_ty = self.normalize_projection_ty(projection); @@ -909,10 +1033,10 @@ impl<'a> InferenceTable<'a> { environment: trait_env.clone(), }; let canonical = self.canonicalize(obligation.clone()); - if self + if !self .db .trait_solve(krate, self.trait_env.block, canonical.cast(Interner)) - .is_some() + .no_solution() { return Some((fn_x, arg_tys, return_ty)); } @@ -1032,7 +1156,7 @@ impl<'a> InferenceTable<'a> { substitution: Substitution::from1(Interner, ty), }); let goal = GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(sized_pred)).intern(Interner); - matches!(self.try_obligation(goal), Some(Solution::Unique(_))) + self.try_obligation(goal).certain() } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs index fecb3f4242a9..57ef5523b433 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs @@ -2,11 +2,10 @@ //! representation of the various objects Chalk deals with (types, goals etc.). use crate::{ - AliasTy, CanonicalVarKind, CanonicalVarKinds, ClosureId, Const, ConstData, ConstScalar, - Constraint, Constraints, FnAbi, FnDefId, GenericArg, GenericArgData, Goal, GoalData, Goals, - InEnvironment, Lifetime, LifetimeData, OpaqueTy, OpaqueTyId, ProgramClause, ProgramClauseData, - ProgramClauses, ProjectionTy, QuantifiedWhereClause, QuantifiedWhereClauses, Substitution, Ty, - TyData, TyKind, VariableKind, VariableKinds, chalk_db, tls, + AliasTy, CanonicalVarKind, CanonicalVarKinds, ClosureId, Const, ConstData, ConstScalar, FnAbi, + FnDefId, GenericArg, GenericArgData, Goal, GoalData, InEnvironment, Lifetime, LifetimeData, + OpaqueTy, OpaqueTyId, ProgramClause, ProjectionTy, QuantifiedWhereClause, + QuantifiedWhereClauses, Substitution, Ty, TyKind, VariableKind, chalk_db, tls, }; use chalk_ir::{ProgramClauseImplication, SeparatorTraitRef, Variance}; use hir_def::TypeAliasId; @@ -15,11 +14,19 @@ use smallvec::SmallVec; use std::fmt; use triomphe::Arc; +type TyData = chalk_ir::TyData; +type VariableKinds = chalk_ir::VariableKinds; +type Goals = chalk_ir::Goals; +type ProgramClauseData = chalk_ir::ProgramClauseData; +type Constraint = chalk_ir::Constraint; +type Constraints = chalk_ir::Constraints; +type ProgramClauses = chalk_ir::ProgramClauses; + #[derive(Debug, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)] pub struct Interner; -#[derive(PartialEq, Eq, Hash)] -pub struct InternedWrapper(T); +#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Clone)] +pub struct InternedWrapper(pub(crate) T); impl fmt::Debug for InternedWrapper { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -27,6 +34,9 @@ impl fmt::Debug for InternedWrapper { } } +#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Clone)] +pub struct InternedWrapperNoDebug(pub(crate) T); + impl std::ops::Deref for InternedWrapper { type Target = T; @@ -124,6 +134,7 @@ impl chalk_ir::interner::Interner for Interner { fmt: &mut fmt::Formatter<'_>, ) -> Option { tls::with_current_program(|prog| Some(prog?.debug_projection_ty(proj, fmt))) + .or_else(|| Some(fmt.write_str("ProjectionTy"))) } fn debug_opaque_ty(opaque_ty: &OpaqueTy, fmt: &mut fmt::Formatter<'_>) -> Option { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index 107da6a5af6d..819bd583deae 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -2,33 +2,43 @@ use std::fmt; -use chalk_ir::{AdtId, FloatTy, IntTy, TyKind, UintTy}; use hir_def::{ - LocalFieldId, StructId, - layout::{ - Float, Integer, LayoutCalculator, LayoutCalculatorError, LayoutData, Primitive, - ReprOptions, Scalar, StructKind, TargetDataLayout, WrappingRange, - }, + AdtId, LocalFieldId, StructId, + layout::{LayoutCalculatorError, LayoutData}, }; use la_arena::{Idx, RawIdx}; -use rustc_abi::AddressSpace; -use rustc_index::IndexVec; +use rustc_abi::{ + AddressSpace, Float, Integer, LayoutCalculator, Primitive, ReprOptions, Scalar, StructKind, + TargetDataLayout, WrappingRange, +}; +use rustc_index::IndexVec; +use rustc_type_ir::{ + FloatTy, IntTy, UintTy, + inherent::{IntoKind, SliceLike}, +}; use triomphe::Arc; use crate::{ - Interner, ProjectionTy, Substitution, TraitEnvironment, Ty, - consteval::try_const_usize, - db::{HirDatabase, InternedClosure}, - infer::normalize, - utils::ClosureSubst, + TraitEnvironment, + consteval_nextsolver::try_const_usize, + db::HirDatabase, + next_solver::{ + DbInterner, GenericArgs, ParamEnv, SolverDefId, Ty, TyKind, TypingMode, + infer::{DbInternerInferExt, traits::ObligationCause}, + mapping::{ChalkToNextSolver, convert_binder_to_early_binder}, + project::solve_normalize::deeply_normalize, + }, }; -pub(crate) use self::adt::layout_of_adt_cycle_result; -pub use self::{adt::layout_of_adt_query, target::target_data_layout_query}; +pub(crate) use self::adt::{layout_of_adt_cycle_result, layout_of_adt_ns_cycle_result}; +pub use self::{ + adt::{layout_of_adt_ns_query, layout_of_adt_query}, + target::target_data_layout_query, +}; -mod adt; -mod target; +pub(crate) mod adt; +pub(crate) mod target; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct RustcEnumVariantIdx(pub usize); @@ -119,11 +129,12 @@ impl<'a> LayoutCx<'a> { } } -fn layout_of_simd_ty( - db: &dyn HirDatabase, +// FIXME: move this to the `rustc_abi`. +fn layout_of_simd_ty<'db>( + db: &'db dyn HirDatabase, id: StructId, repr_packed: bool, - subst: &Substitution, + args: &GenericArgs<'db>, env: Arc, dl: &TargetDataLayout, ) -> Result, LayoutError> { @@ -132,18 +143,18 @@ fn layout_of_simd_ty( // * #[repr(simd)] struct S([T; 4]) // // where T is a primitive scalar (integer/float/pointer). - let fields = db.field_types(id.into()); + let fields = db.field_types_ns(id.into()); let mut fields = fields.iter(); let Some(TyKind::Array(e_ty, e_len)) = fields .next() .filter(|_| fields.next().is_none()) - .map(|f| f.1.clone().substitute(Interner, subst).kind(Interner).clone()) + .map(|f| (*f.1).instantiate(DbInterner::new_with(db, None, None), args).kind()) else { return Err(LayoutError::InvalidSimdType); }; let e_len = try_const_usize(db, &e_len).ok_or(LayoutError::HasErrorConst)? as u64; - let e_ly = db.layout_of_ty(e_ty, env)?; + let e_ly = db.layout_of_ty_ns(e_ty, env)?; let cx = LayoutCx::new(dl); Ok(Arc::new(cx.calc.simd_type(e_ly, e_len, repr_packed)?)) @@ -151,108 +162,122 @@ fn layout_of_simd_ty( pub fn layout_of_ty_query( db: &dyn HirDatabase, - ty: Ty, + ty: crate::Ty, trait_env: Arc, ) -> Result, LayoutError> { let krate = trait_env.krate; + let interner = DbInterner::new_with(db, Some(krate), trait_env.block); + db.layout_of_ty_ns(ty.to_nextsolver(interner), trait_env) +} + +pub fn layout_of_ty_ns_query<'db>( + db: &'db dyn HirDatabase, + ty: Ty<'db>, + trait_env: Arc, +) -> Result, LayoutError> { + let krate = trait_env.krate; + let interner = DbInterner::new_with(db, Some(krate), trait_env.block); let Ok(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable); }; let dl = &*target; let cx = LayoutCx::new(dl); - let ty = normalize(db, trait_env.clone(), ty); - let kind = ty.kind(Interner); - let result = match kind { - TyKind::Adt(AdtId(def), subst) => { - if let hir_def::AdtId::StructId(s) = def { - let data = db.struct_signature(*s); - let repr = data.repr.unwrap_or_default(); - if repr.simd() { - return layout_of_simd_ty(db, *s, repr.packed(), subst, trait_env, &target); + let infer_ctxt = interner.infer_ctxt().build(TypingMode::non_body_analysis()); + let cause = ObligationCause::dummy(); + let ty = deeply_normalize(infer_ctxt.at(&cause, ParamEnv::empty()), ty).unwrap_or(ty); + let result = match ty.kind() { + TyKind::Adt(def, args) => { + match def.inner().id { + hir_def::AdtId::StructId(s) => { + let data = db.struct_signature(s); + let repr = data.repr.unwrap_or_default(); + if repr.simd() { + return layout_of_simd_ty(db, s, repr.packed(), &args, trait_env, &target); + } } - }; - return db.layout_of_adt(*def, subst.clone(), trait_env); + _ => {} + } + return db.layout_of_adt_ns(def.inner().id, args, trait_env); } - TyKind::Scalar(s) => match s { - chalk_ir::Scalar::Bool => Layout::scalar( + TyKind::Bool => Layout::scalar( + dl, + Scalar::Initialized { + value: Primitive::Int(Integer::I8, false), + valid_range: WrappingRange { start: 0, end: 1 }, + }, + ), + TyKind::Char => Layout::scalar( + dl, + Scalar::Initialized { + value: Primitive::Int(Integer::I32, false), + valid_range: WrappingRange { start: 0, end: 0x10FFFF }, + }, + ), + TyKind::Int(i) => Layout::scalar( + dl, + scalar_unit( dl, - Scalar::Initialized { - value: Primitive::Int(Integer::I8, false), - valid_range: WrappingRange { start: 0, end: 1 }, - }, - ), - chalk_ir::Scalar::Char => Layout::scalar( - dl, - Scalar::Initialized { - value: Primitive::Int(Integer::I32, false), - valid_range: WrappingRange { start: 0, end: 0x10FFFF }, - }, - ), - chalk_ir::Scalar::Int(i) => Layout::scalar( - dl, - scalar_unit( - dl, - Primitive::Int( - match i { - IntTy::Isize => dl.ptr_sized_integer(), - IntTy::I8 => Integer::I8, - IntTy::I16 => Integer::I16, - IntTy::I32 => Integer::I32, - IntTy::I64 => Integer::I64, - IntTy::I128 => Integer::I128, - }, - true, - ), + Primitive::Int( + match i { + IntTy::Isize => dl.ptr_sized_integer(), + IntTy::I8 => Integer::I8, + IntTy::I16 => Integer::I16, + IntTy::I32 => Integer::I32, + IntTy::I64 => Integer::I64, + IntTy::I128 => Integer::I128, + }, + true, ), ), - chalk_ir::Scalar::Uint(i) => Layout::scalar( + ), + TyKind::Uint(i) => Layout::scalar( + dl, + scalar_unit( dl, - scalar_unit( - dl, - Primitive::Int( - match i { - UintTy::Usize => dl.ptr_sized_integer(), - UintTy::U8 => Integer::I8, - UintTy::U16 => Integer::I16, - UintTy::U32 => Integer::I32, - UintTy::U64 => Integer::I64, - UintTy::U128 => Integer::I128, - }, - false, - ), + Primitive::Int( + match i { + UintTy::Usize => dl.ptr_sized_integer(), + UintTy::U8 => Integer::I8, + UintTy::U16 => Integer::I16, + UintTy::U32 => Integer::I32, + UintTy::U64 => Integer::I64, + UintTy::U128 => Integer::I128, + }, + false, ), ), - chalk_ir::Scalar::Float(f) => Layout::scalar( + ), + TyKind::Float(f) => Layout::scalar( + dl, + scalar_unit( dl, - scalar_unit( - dl, - Primitive::Float(match f { - FloatTy::F16 => Float::F16, - FloatTy::F32 => Float::F32, - FloatTy::F64 => Float::F64, - FloatTy::F128 => Float::F128, - }), - ), + Primitive::Float(match f { + FloatTy::F16 => Float::F16, + FloatTy::F32 => Float::F32, + FloatTy::F64 => Float::F64, + FloatTy::F128 => Float::F128, + }), ), - }, - TyKind::Tuple(len, tys) => { - let kind = if *len == 0 { StructKind::AlwaysSized } else { StructKind::MaybeUnsized }; + ), + TyKind::Tuple(tys) => { + let kind = + if tys.len() == 0 { StructKind::AlwaysSized } else { StructKind::MaybeUnsized }; let fields = tys - .iter(Interner) - .map(|k| db.layout_of_ty(k.assert_ty_ref(Interner).clone(), trait_env.clone())) + .iter() + .map(|k| db.layout_of_ty_ns(k, trait_env.clone())) .collect::, _>>()?; let fields = fields.iter().map(|it| &**it).collect::>(); let fields = fields.iter().collect::>(); cx.calc.univariant(&fields, &ReprOptions::default(), kind)? } TyKind::Array(element, count) => { - let count = try_const_usize(db, count).ok_or(LayoutError::HasErrorConst)? as u64; - let element = db.layout_of_ty(element.clone(), trait_env)?; + let count = try_const_usize(db, &count).ok_or(LayoutError::HasErrorConst)? as u64; + let element = db.layout_of_ty_ns(element, trait_env)?; cx.calc.array_like::<_, _, ()>(&element, Some(count))? } TyKind::Slice(element) => { - let element = db.layout_of_ty(element.clone(), trait_env)?; + let element = db.layout_of_ty_ns(element, trait_env)?; cx.calc.array_like::<_, _, ()>(&element, None)? } TyKind::Str => { @@ -260,18 +285,21 @@ pub fn layout_of_ty_query( cx.calc.array_like::<_, _, ()>(&Layout::scalar(dl, element), None)? } // Potentially-wide pointers. - TyKind::Ref(_, _, pointee) | TyKind::Raw(_, pointee) => { + TyKind::Ref(_, pointee, _) | TyKind::RawPtr(pointee, _) => { let mut data_ptr = scalar_unit(dl, Primitive::Pointer(AddressSpace::ZERO)); - if matches!(ty.kind(Interner), TyKind::Ref(..)) { + if matches!(ty.kind(), TyKind::Ref(..)) { data_ptr.valid_range_mut().start = 1; } + // FIXME(next-solver) // let pointee = tcx.normalize_erasing_regions(param_env, pointee); // if pointee.is_sized(tcx.at(DUMMY_SP), param_env) { - // return Ok(tcx.mk_layout(LayoutData::scalar(cx, data_ptr))); + // return Ok(tcx.mk_layout(LayoutS::scalar(cx, data_ptr))); // } - let mut unsized_part = struct_tail_erasing_lifetimes(db, pointee.clone()); + let unsized_part = struct_tail_erasing_lifetimes(db, pointee); + // FIXME(next-solver) + /* if let TyKind::AssociatedType(id, subst) = unsized_part.kind(Interner) { unsized_part = TyKind::Alias(chalk_ir::AliasTy::Projection(ProjectionTy { associated_ty_id: *id, @@ -280,11 +308,12 @@ pub fn layout_of_ty_query( .intern(Interner); } unsized_part = normalize(db, trait_env, unsized_part); - let metadata = match unsized_part.kind(Interner) { + */ + let metadata = match unsized_part.kind() { TyKind::Slice(_) | TyKind::Str => { scalar_unit(dl, Primitive::Int(dl.ptr_sized_integer(), false)) } - TyKind::Dyn(..) => { + TyKind::Dynamic(..) => { let mut vtable = scalar_unit(dl, Primitive::Pointer(AddressSpace::ZERO)); vtable.valid_range_mut().start = 1; vtable @@ -299,97 +328,110 @@ pub fn layout_of_ty_query( LayoutData::scalar_pair(dl, data_ptr, metadata) } TyKind::Never => LayoutData::never_type(dl), - TyKind::FnDef(..) | TyKind::Dyn(_) | TyKind::Foreign(_) => { - let sized = matches!(kind, TyKind::FnDef(..)); - LayoutData::unit(dl, sized) - } - TyKind::Function(_) => { + TyKind::FnDef(..) => LayoutData::unit(dl, true), + TyKind::Dynamic(..) | TyKind::Foreign(_) => LayoutData::unit(dl, false), + TyKind::FnPtr(..) => { let mut ptr = scalar_unit(dl, Primitive::Pointer(dl.instruction_address_space)); ptr.valid_range_mut().start = 1; Layout::scalar(dl, ptr) } - TyKind::OpaqueType(opaque_ty_id, _) => { - let impl_trait_id = db.lookup_intern_impl_trait_id((*opaque_ty_id).into()); - match impl_trait_id { - crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => { - let infer = db.infer(func.into()); - return db.layout_of_ty(infer.type_of_rpit[idx].clone(), trait_env); - } - crate::ImplTraitId::TypeAliasImplTrait(..) => { - return Err(LayoutError::NotImplemented); - } - crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { - return Err(LayoutError::NotImplemented); + TyKind::Alias(_, ty) => match ty.def_id { + SolverDefId::TypeAliasId(_) => { + return Err(LayoutError::HasPlaceholder); + } + SolverDefId::InternedOpaqueTyId(opaque) => { + let impl_trait_id = db.lookup_intern_impl_trait_id(opaque); + match impl_trait_id { + crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => { + let infer = db.infer(func.into()); + return db.layout_of_ty(infer.type_of_rpit[idx].clone(), trait_env); + } + crate::ImplTraitId::TypeAliasImplTrait(..) => { + return Err(LayoutError::NotImplemented); + } + crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { + return Err(LayoutError::NotImplemented); + } } } - } - TyKind::Closure(c, subst) => { - let InternedClosure(def, _) = db.lookup_intern_closure((*c).into()); - let infer = db.infer(def); - let (captures, _) = infer.closure_info(c); + _ => unreachable!(), + }, + TyKind::Closure(c, args) => { + let id = match c { + SolverDefId::InternedClosureId(id) => id, + _ => unreachable!(), + }; + let def = db.lookup_intern_closure(id); + let infer = db.infer(def.0); + let (captures, _) = infer.closure_info(&id.into()); let fields = captures .iter() .map(|it| { - db.layout_of_ty( - it.ty.clone().substitute(Interner, ClosureSubst(subst).parent_subst()), - trait_env.clone(), - ) + let ty = + convert_binder_to_early_binder(interner, it.ty.to_nextsolver(interner)) + .instantiate(interner, args); + db.layout_of_ty_ns(ty, trait_env.clone()) }) .collect::, _>>()?; let fields = fields.iter().map(|it| &**it).collect::>(); let fields = fields.iter().collect::>(); cx.calc.univariant(&fields, &ReprOptions::default(), StructKind::AlwaysSized)? } - TyKind::Coroutine(_, _) | TyKind::CoroutineWitness(_, _) => { + + TyKind::Coroutine(_, _) + | TyKind::CoroutineWitness(_, _) + | TyKind::CoroutineClosure(_, _) => { return Err(LayoutError::NotImplemented); } - TyKind::Error => return Err(LayoutError::HasErrorType), - TyKind::AssociatedType(id, subst) => { - // Try again with `TyKind::Alias` to normalize the associated type. - // Usually we should not try to normalize `TyKind::AssociatedType`, but layout calculation is used - // in monomorphized MIR where this is okay. If outside monomorphization, this will lead to cycle, - // which we will recover from with an error. - let ty = TyKind::Alias(chalk_ir::AliasTy::Projection(ProjectionTy { - associated_ty_id: *id, - substitution: subst.clone(), - })) - .intern(Interner); - return db.layout_of_ty(ty, trait_env); + + TyKind::Pat(_, _) | TyKind::UnsafeBinder(_) => { + return Err(LayoutError::NotImplemented); + } + + TyKind::Error(_) => return Err(LayoutError::HasErrorType), + TyKind::Placeholder(_) | TyKind::Bound(..) | TyKind::Infer(..) | TyKind::Param(..) => { + return Err(LayoutError::HasPlaceholder); } - TyKind::Alias(_) - | TyKind::Placeholder(_) - | TyKind::BoundVar(_) - | TyKind::InferenceVar(_, _) => return Err(LayoutError::HasPlaceholder), }; Ok(Arc::new(result)) } pub(crate) fn layout_of_ty_cycle_result( _: &dyn HirDatabase, - _: Ty, + _: crate::Ty, _: Arc, ) -> Result, LayoutError> { Err(LayoutError::RecursiveTypeWithoutIndirection) } -fn struct_tail_erasing_lifetimes(db: &dyn HirDatabase, pointee: Ty) -> Ty { - match pointee.kind(Interner) { - &TyKind::Adt(AdtId(hir_def::AdtId::StructId(i)), ref subst) => { - let data = i.fields(db); +pub(crate) fn layout_of_ty_ns_cycle_result<'db>( + _: &dyn HirDatabase, + _: Ty<'db>, + _: Arc, +) -> Result, LayoutError> { + Err(LayoutError::RecursiveTypeWithoutIndirection) +} + +fn struct_tail_erasing_lifetimes<'a>(db: &'a dyn HirDatabase, pointee: Ty<'a>) -> Ty<'a> { + match pointee.kind() { + TyKind::Adt(def, args) => { + let struct_id = match def.inner().id { + AdtId::StructId(id) => id, + _ => return pointee, + }; + let data = struct_id.fields(db); let mut it = data.fields().iter().rev(); match it.next() { Some((f, _)) => { - let last_field_ty = field_ty(db, i.into(), f, subst); + let last_field_ty = field_ty(db, struct_id.into(), f, &args); struct_tail_erasing_lifetimes(db, last_field_ty) } None => pointee, } } - TyKind::Tuple(_, subst) => { - if let Some(last_field_ty) = - subst.iter(Interner).last().and_then(|arg| arg.ty(Interner)) - { - struct_tail_erasing_lifetimes(db, last_field_ty.clone()) + TyKind::Tuple(tys) => { + if let Some(last_field_ty) = tys.iter().last() { + struct_tail_erasing_lifetimes(db, last_field_ty) } else { pointee } @@ -398,13 +440,13 @@ fn struct_tail_erasing_lifetimes(db: &dyn HirDatabase, pointee: Ty) -> Ty { } } -fn field_ty( - db: &dyn HirDatabase, +fn field_ty<'a>( + db: &'a dyn HirDatabase, def: hir_def::VariantId, fd: LocalFieldId, - subst: &Substitution, -) -> Ty { - db.field_types(def)[fd].clone().substitute(Interner, subst) + args: &GenericArgs<'a>, +) -> Ty<'a> { + db.field_types_ns(def)[fd].instantiate(DbInterner::new_with(db, None, None), args) } fn scalar_unit(dl: &TargetDataLayout, value: Primitive) -> Scalar { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs index 3f310c26ec14..2fa01b6b41ab 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs @@ -4,10 +4,10 @@ use std::{cmp, ops::Bound}; use hir_def::{ AdtId, VariantId, - layout::{Integer, ReprOptions, TargetDataLayout}, signatures::{StructFlags, VariantFields}, }; use intern::sym; +use rustc_abi::{Integer, ReprOptions, TargetDataLayout}; use rustc_index::IndexVec; use smallvec::SmallVec; use triomphe::Arc; @@ -15,16 +15,25 @@ use triomphe::Arc; use crate::{ Substitution, TraitEnvironment, db::HirDatabase, - layout::{Layout, LayoutError, field_ty}, + layout::{Layout, LayoutCx, LayoutError, field_ty}, + next_solver::{DbInterner, GenericArgs, mapping::ChalkToNextSolver}, }; -use super::LayoutCx; - pub fn layout_of_adt_query( db: &dyn HirDatabase, def: AdtId, subst: Substitution, trait_env: Arc, +) -> Result, LayoutError> { + let interner = DbInterner::new_with(db, Some(trait_env.krate), trait_env.block); + db.layout_of_adt_ns(def, subst.to_nextsolver(interner), trait_env) +} + +pub fn layout_of_adt_ns_query<'db>( + db: &'db dyn HirDatabase, + def: AdtId, + args: GenericArgs<'db>, + trait_env: Arc, ) -> Result, LayoutError> { let krate = trait_env.krate; let Ok(target) = db.target_data_layout(krate) else { @@ -35,7 +44,7 @@ pub fn layout_of_adt_query( let handle_variant = |def: VariantId, var: &VariantFields| { var.fields() .iter() - .map(|(fd, _)| db.layout_of_ty(field_ty(db, def, fd, &subst), trait_env.clone())) + .map(|(fd, _)| db.layout_of_ty_ns(field_ty(db, def, fd, &args), trait_env.clone())) .collect::, _>>() }; let (variants, repr, is_special_no_niche) = match def { @@ -96,6 +105,24 @@ pub fn layout_of_adt_query( Ok(Arc::new(result)) } +pub(crate) fn layout_of_adt_cycle_result( + _: &dyn HirDatabase, + _: AdtId, + _: Substitution, + _: Arc, +) -> Result, LayoutError> { + Err(LayoutError::RecursiveTypeWithoutIndirection) +} + +pub(crate) fn layout_of_adt_ns_cycle_result<'db>( + _: &'db dyn HirDatabase, + _def: AdtId, + _args: GenericArgs<'db>, + _trait_env: Arc, +) -> Result, LayoutError> { + Err(LayoutError::RecursiveTypeWithoutIndirection) +} + fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound, Bound) { let attrs = db.attrs(def.into()); let get = |name| { @@ -120,15 +147,6 @@ fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound, (get(sym::rustc_layout_scalar_valid_range_start), get(sym::rustc_layout_scalar_valid_range_end)) } -pub(crate) fn layout_of_adt_cycle_result( - _: &dyn HirDatabase, - _: AdtId, - _: Substitution, - _: Arc, -) -> Result, LayoutError> { - Err(LayoutError::RecursiveTypeWithoutIndirection) -} - /// Finds the appropriate Integer type and signedness for the given /// signed discriminant range and `#[repr]` attribute. /// N.B.: `u128` values above `i128::MAX` will be treated as signed, but diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs index b3bc226ec93c..93f2e123dca5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs @@ -11,6 +11,7 @@ use crate::{ Interner, Substitution, db::HirDatabase, layout::{Layout, LayoutError}, + setup_tracing, test_db::TestDB, }; @@ -29,6 +30,7 @@ fn eval_goal( #[rust_analyzer::rust_fixture] ra_fixture: &str, minicore: &str, ) -> Result, LayoutError> { + let _tracing = setup_tracing(); let target_data_layout = current_machine_data_layout(); let ra_fixture = format!( "//- target_data_layout: {target_data_layout}\n{minicore}//- /main.rs crate:test\n{ra_fixture}", @@ -97,6 +99,7 @@ fn eval_expr( #[rust_analyzer::rust_fixture] ra_fixture: &str, minicore: &str, ) -> Result, LayoutError> { + let _tracing = setup_tracing(); let target_data_layout = current_machine_data_layout(); let ra_fixture = format!( "//- target_data_layout: {target_data_layout}\n{minicore}//- /main.rs crate:test\nfn main(){{let goal = {{{ra_fixture}}};}}", diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index e787fd9b1e58..2b0dc931eaf4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -21,6 +21,24 @@ extern crate rustc_pattern_analysis; #[cfg(not(feature = "in-rust-tree"))] extern crate ra_ap_rustc_pattern_analysis as rustc_pattern_analysis; +#[cfg(feature = "in-rust-tree")] +extern crate rustc_ast_ir; + +#[cfg(not(feature = "in-rust-tree"))] +extern crate ra_ap_rustc_ast_ir as rustc_ast_ir; + +#[cfg(feature = "in-rust-tree")] +extern crate rustc_type_ir; + +#[cfg(not(feature = "in-rust-tree"))] +extern crate ra_ap_rustc_type_ir as rustc_type_ir; + +#[cfg(feature = "in-rust-tree")] +extern crate rustc_next_trait_solver; + +#[cfg(not(feature = "in-rust-tree"))] +extern crate ra_ap_rustc_next_trait_solver as rustc_next_trait_solver; + mod builder; mod chalk_db; mod chalk_ext; @@ -29,13 +47,16 @@ mod infer; mod inhabitedness; mod interner; mod lower; +mod lower_nextsolver; mod mapping; +pub mod next_solver; mod target_feature; mod tls; mod utils; pub mod autoderef; pub mod consteval; +pub mod consteval_nextsolver; pub mod db; pub mod diagnostics; pub mod display; @@ -57,7 +78,7 @@ mod variance; use std::hash::Hash; use chalk_ir::{ - NoSolution, + NoSolution, VariableKinds, fold::{Shift, TypeFoldable}, interner::HasInterner, }; @@ -121,9 +142,9 @@ pub type ClosureId = chalk_ir::ClosureId; pub type OpaqueTyId = chalk_ir::OpaqueTyId; pub type PlaceholderIndex = chalk_ir::PlaceholderIndex; -pub type VariableKind = chalk_ir::VariableKind; -pub type VariableKinds = chalk_ir::VariableKinds; pub type CanonicalVarKinds = chalk_ir::CanonicalVarKinds; + +pub(crate) type VariableKind = chalk_ir::VariableKind; /// Represents generic parameters and an item bound by them. When the item has parent, the binders /// also contain the generic parameters for its parent. See chalk's documentation for details. /// @@ -145,52 +166,45 @@ pub type GenericArgData = chalk_ir::GenericArgData; pub type Ty = chalk_ir::Ty; pub type TyKind = chalk_ir::TyKind; pub type TypeFlags = chalk_ir::TypeFlags; -pub type DynTy = chalk_ir::DynTy; +pub(crate) type DynTy = chalk_ir::DynTy; pub type FnPointer = chalk_ir::FnPointer; -// pub type FnSubst = chalk_ir::FnSubst; // a re-export so we don't lose the tuple constructor -pub use chalk_ir::FnSubst; -pub type ProjectionTy = chalk_ir::ProjectionTy; -pub type AliasTy = chalk_ir::AliasTy; -pub type OpaqueTy = chalk_ir::OpaqueTy; -pub type InferenceVar = chalk_ir::InferenceVar; +pub(crate) use chalk_ir::FnSubst; // a re-export so we don't lose the tuple constructor -pub type Lifetime = chalk_ir::Lifetime; -pub type LifetimeData = chalk_ir::LifetimeData; -pub type LifetimeOutlives = chalk_ir::LifetimeOutlives; +pub type AliasTy = chalk_ir::AliasTy; + +pub type ProjectionTy = chalk_ir::ProjectionTy; +pub(crate) type OpaqueTy = chalk_ir::OpaqueTy; +pub(crate) type InferenceVar = chalk_ir::InferenceVar; + +pub(crate) type Lifetime = chalk_ir::Lifetime; +pub(crate) type LifetimeData = chalk_ir::LifetimeData; +pub(crate) type LifetimeOutlives = chalk_ir::LifetimeOutlives; + +pub type ConstValue = chalk_ir::ConstValue; pub type Const = chalk_ir::Const; -pub type ConstData = chalk_ir::ConstData; -pub type ConstValue = chalk_ir::ConstValue; -pub type ConcreteConst = chalk_ir::ConcreteConst; +pub(crate) type ConstData = chalk_ir::ConstData; +pub(crate) type ConcreteConst = chalk_ir::ConcreteConst; -pub type ChalkTraitId = chalk_ir::TraitId; pub type TraitRef = chalk_ir::TraitRef; pub type QuantifiedWhereClause = Binders; -pub type QuantifiedWhereClauses = chalk_ir::QuantifiedWhereClauses; pub type Canonical = chalk_ir::Canonical; -pub type FnSig = chalk_ir::FnSig; +pub(crate) type ChalkTraitId = chalk_ir::TraitId; +pub(crate) type QuantifiedWhereClauses = chalk_ir::QuantifiedWhereClauses; + +pub(crate) type FnSig = chalk_ir::FnSig; pub type InEnvironment = chalk_ir::InEnvironment; -pub type Environment = chalk_ir::Environment; -pub type DomainGoal = chalk_ir::DomainGoal; -pub type Goal = chalk_ir::Goal; pub type AliasEq = chalk_ir::AliasEq; -pub type Solution = chalk_solve::Solution; -pub type Constraint = chalk_ir::Constraint; -pub type Constraints = chalk_ir::Constraints; -pub type ConstrainedSubst = chalk_ir::ConstrainedSubst; -pub type Guidance = chalk_solve::Guidance; pub type WhereClause = chalk_ir::WhereClause; -pub type CanonicalVarKind = chalk_ir::CanonicalVarKind; -pub type GoalData = chalk_ir::GoalData; -pub type Goals = chalk_ir::Goals; -pub type ProgramClauseData = chalk_ir::ProgramClauseData; -pub type ProgramClause = chalk_ir::ProgramClause; -pub type ProgramClauses = chalk_ir::ProgramClauses; -pub type TyData = chalk_ir::TyData; -pub type Variances = chalk_ir::Variances; +pub(crate) type DomainGoal = chalk_ir::DomainGoal; +pub(crate) type Goal = chalk_ir::Goal; + +pub(crate) type CanonicalVarKind = chalk_ir::CanonicalVarKind; +pub(crate) type GoalData = chalk_ir::GoalData; +pub(crate) type ProgramClause = chalk_ir::ProgramClause; /// A constant can have reference to other things. Memory map job is holding /// the necessary bits of memory of the const eval session to keep the constant @@ -311,30 +325,11 @@ where Binders::empty(Interner, value.shifted_in_from(Interner, DebruijnIndex::ONE)) } -pub(crate) fn make_type_and_const_binders>( - which_is_const: impl Iterator>, - value: T, -) -> Binders { - Binders::new( - VariableKinds::from_iter( - Interner, - which_is_const.map(|x| { - if let Some(ty) = x { - chalk_ir::VariableKind::Const(ty) - } else { - chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General) - } - }), - ), - value, - ) -} - pub(crate) fn make_single_type_binders>( value: T, ) -> Binders { Binders::new( - VariableKinds::from_iter( + chalk_ir::VariableKinds::from_iter( Interner, std::iter::once(chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General)), ), @@ -353,7 +348,7 @@ pub(crate) fn make_binders>( pub(crate) fn variable_kinds_from_iter( db: &dyn HirDatabase, iter: impl Iterator, -) -> VariableKinds { +) -> VariableKinds { VariableKinds::from_iter( Interner, iter.map(|x| match x { @@ -918,7 +913,7 @@ pub fn callable_sig_from_fn_trait( let obligation = InEnvironment { goal: trait_ref.clone().cast(Interner), environment: trait_env.clone() }; let canonical = table.canonicalize(obligation.clone()); - if db.trait_solve(krate, block, canonical.cast(Interner)).is_some() { + if !db.trait_solve(krate, block, canonical.cast(Interner)).no_solution() { table.register_obligation(obligation.goal); let return_ty = table.normalize_projection_ty(projection); for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] { @@ -929,7 +924,7 @@ pub fn callable_sig_from_fn_trait( environment: trait_env.clone(), }; let canonical = table.canonicalize(obligation.clone()); - if db.trait_solve(krate, block, canonical.cast(Interner)).is_some() { + if !db.trait_solve(krate, block, canonical.cast(Interner)).no_solution() { let ret_ty = table.resolve_completely(return_ty); let args_ty = table.resolve_completely(args_ty); let params = args_ty @@ -985,7 +980,7 @@ impl TypeVisitor for PlaceholderCollector<'_> { outer_binder: DebruijnIndex, ) -> std::ops::ControlFlow { let has_placeholder_bits = TypeFlags::HAS_TY_PLACEHOLDER | TypeFlags::HAS_CT_PLACEHOLDER; - let TyData { kind, flags } = ty.data(Interner); + let chalk_ir::TyData { kind, flags } = ty.data(Interner); if let TyKind::Placeholder(idx) = kind { self.collect(*idx); @@ -1045,3 +1040,25 @@ pub(crate) enum DeclOrigin { pub(crate) struct DeclContext { pub(crate) origin: DeclOrigin, } + +pub fn setup_tracing() -> Option { + use std::env; + use std::sync::LazyLock; + use tracing_subscriber::{Registry, layer::SubscriberExt}; + use tracing_tree::HierarchicalLayer; + + static ENABLE: LazyLock = LazyLock::new(|| env::var("CHALK_DEBUG").is_ok()); + if !*ENABLE { + return None; + } + + let filter: tracing_subscriber::filter::Targets = + env::var("CHALK_DEBUG").ok().and_then(|it| it.parse().ok()).unwrap_or_default(); + let layer = HierarchicalLayer::default() + .with_indent_lines(true) + .with_ansi(false) + .with_indent_amount(2) + .with_writer(std::io::stderr); + let subscriber = Registry::default().with(filter).with(layer); + Some(tracing::subscriber::set_default(subscriber)) +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index afee9606bd5f..7b6b3c3f8181 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -46,9 +46,9 @@ use stdx::{impl_from, never}; use triomphe::{Arc, ThinArc}; use crate::{ - AliasTy, Binders, BoundVar, CallableSig, Const, DebruijnIndex, DynTy, FnAbi, FnPointer, FnSig, - FnSubst, ImplTrait, ImplTraitId, ImplTraits, Interner, Lifetime, LifetimeData, - LifetimeOutlives, PolyFnSig, ProgramClause, QuantifiedWhereClause, QuantifiedWhereClauses, + AliasTy, Binders, BoundVar, CallableSig, Const, DebruijnIndex, DomainGoal, DynTy, FnAbi, + FnPointer, FnSig, FnSubst, ImplTrait, ImplTraitId, ImplTraits, Interner, Lifetime, + LifetimeData, LifetimeOutlives, PolyFnSig, QuantifiedWhereClause, QuantifiedWhereClauses, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause, all_super_traits, consteval::{intern_const_ref, path_to_const, unknown_const, unknown_const_as_generic}, @@ -81,7 +81,7 @@ impl ImplTraitLoweringState { } } -pub(crate) struct PathDiagnosticCallbackData(TypeRefId); +pub(crate) struct PathDiagnosticCallbackData(pub(crate) TypeRefId); #[derive(Debug, Clone)] pub enum LifetimeElisionKind { @@ -889,7 +889,7 @@ fn named_associated_type_shorthand_candidates( pub(crate) type Diagnostics = Option>; -fn create_diagnostics(diagnostics: Vec) -> Diagnostics { +pub(crate) fn create_diagnostics(diagnostics: Vec) -> Diagnostics { (!diagnostics.is_empty()).then(|| ThinArc::from_header_and_iter((), diagnostics.into_iter())) } @@ -1105,8 +1105,9 @@ pub(crate) fn trait_environment_query( traits_in_scope .push((tr.self_type_parameter(Interner).clone(), tr.hir_trait_id())); } - let program_clause: chalk_ir::ProgramClause = pred.cast(Interner); - clauses.push(program_clause.into_from_env_clause(Interner)); + let program_clause: Binders = + pred.map(|pred| pred.into_from_env_goal(Interner).cast(Interner)); + clauses.push(program_clause); } } } @@ -1119,7 +1120,10 @@ pub(crate) fn trait_environment_query( let substs = TyBuilder::placeholder_subst(db, trait_id); let trait_ref = TraitRef { trait_id: to_chalk_trait_id(trait_id), substitution: substs }; let pred = WhereClause::Implemented(trait_ref); - clauses.push(pred.cast::(Interner).into_from_env_clause(Interner)); + clauses.push(Binders::empty( + Interner, + pred.cast::(Interner).into_from_env_goal(Interner), + )); } let subst = generics.placeholder_subst(db); @@ -1128,15 +1132,30 @@ pub(crate) fn trait_environment_query( if let Some(implicitly_sized_clauses) = implicitly_sized_clauses(db, def, &explicitly_unsized_tys, &subst, &resolver) { - clauses.extend( - implicitly_sized_clauses.map(|pred| { - pred.cast::(Interner).into_from_env_clause(Interner) - }), - ); + clauses.extend(implicitly_sized_clauses.map(|pred| { + Binders::empty( + Interner, + pred.into_from_env_goal(Interner).cast::(Interner), + ) + })); }; } - let env = chalk_ir::Environment::new(Interner).add_clauses(Interner, clauses); + let clauses = chalk_ir::ProgramClauses::from_iter( + Interner, + clauses.into_iter().map(|g| { + chalk_ir::ProgramClause::new( + Interner, + chalk_ir::ProgramClauseData(g.map(|g| chalk_ir::ProgramClauseImplication { + consequence: g, + conditions: chalk_ir::Goals::empty(Interner), + constraints: chalk_ir::Constraints::empty(Interner), + priority: chalk_ir::ClausePriority::High, + })), + ) + }), + ); + let env = chalk_ir::Environment { clauses }; TraitEnvironment::new(resolver.krate(), None, traits_in_scope.into_boxed_slice(), env) } @@ -1177,7 +1196,7 @@ pub(crate) fn generic_predicates_without_parent_with_diagnostics_query( } /// Resolve the where clause(s) of an item with generics, -/// except the ones inherited from the parent +/// with a given filter fn generic_predicates_filtered_by( db: &dyn HirDatabase, def: GenericDefId, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs new file mode 100644 index 000000000000..24bda43ca634 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -0,0 +1,1609 @@ +//! Methods for lowering the HIR to types. There are two main cases here: +//! +//! - Lowering a type reference like `&usize` or `Option` to a +//! type: The entry point for this is `TyLoweringContext::lower_ty`. +//! - Building the type for an item: This happens through the `ty` query. +//! +//! This usually involves resolving names, collecting generic arguments etc. +#![allow(unused)] +// FIXME(next-solver): this should get removed as things get moved to rustc_type_ir from chalk_ir +pub(crate) mod path; + +use std::{ + cell::OnceCell, + iter, mem, + ops::{self, Not as _}, +}; + +use base_db::Crate; +use either::Either; +use hir_def::{ + AdtId, AssocItemId, CallableDefId, ConstParamId, EnumVariantId, FunctionId, GenericDefId, + GenericParamId, ImplId, ItemContainerId, LocalFieldId, Lookup, StructId, TypeAliasId, + TypeOrConstParamId, VariantId, + expr_store::{ + ExpressionStore, + path::{GenericArg, Path}, + }, + hir::generics::{TypeOrConstParamData, WherePredicate}, + lang_item::LangItem, + resolver::{HasResolver, LifetimeNs, Resolver, TypeNs}, + signatures::{FunctionSignature, TraitFlags, TypeAliasFlags}, + type_ref::{ + ConstRef, LifetimeRefId, LiteralConstRef, PathId, TraitBoundModifier, + TraitRef as HirTraitRef, TypeBound, TypeRef, TypeRefId, + }, +}; +use hir_expand::name::Name; +use intern::sym; +use la_arena::{Arena, ArenaMap, Idx}; +use path::{PathDiagnosticCallback, PathLoweringContext, builtin}; +use rustc_ast_ir::Mutability; +use rustc_hash::FxHashSet; +use rustc_pattern_analysis::Captures; +use rustc_type_ir::{ + AliasTyKind, ConstKind, DebruijnIndex, ExistentialPredicate, ExistentialProjection, + ExistentialTraitRef, FnSig, OutlivesPredicate, + TyKind::{self}, + TypeVisitableExt, + inherent::{GenericArg as _, GenericArgs as _, IntoKind as _, Region as _, SliceLike, Ty as _}, +}; +use salsa::plumbing::AsId; +use stdx::never; +use triomphe::Arc; + +use crate::{ + FnAbi, ImplTraitId, Interner, ParamKind, TyDefId, TyLoweringDiagnostic, + TyLoweringDiagnosticKind, + consteval_nextsolver::{intern_const_ref, path_to_const, unknown_const_as_generic}, + db::HirDatabase, + generics::{Generics, generics, trait_self_param_idx}, + lower::{Diagnostics, PathDiagnosticCallbackData, create_diagnostics}, + next_solver::{ + AdtDef, AliasTy, Binder, BoundExistentialPredicates, BoundRegionKind, BoundTyKind, + BoundVarKind, BoundVarKinds, Clause, Const, DbInterner, EarlyBinder, EarlyParamRegion, + ErrorGuaranteed, GenericArgs, PolyFnSig, Predicate, Region, SolverDefId, TraitPredicate, + TraitRef, Ty, Tys, abi::Safety, generics::GenericParamDefKind, mapping::ChalkToNextSolver, + }, +}; + +#[derive(PartialEq, Eq, Debug, Hash)] +pub struct ImplTraits<'db> { + pub(crate) impl_traits: Arena>, +} + +#[derive(PartialEq, Eq, Debug, Hash)] +pub(crate) struct ImplTrait<'db> { + pub(crate) predicates: Vec>, +} + +pub(crate) type ImplTraitIdx<'db> = Idx>; + +#[derive(Debug, Default)] +struct ImplTraitLoweringState<'db> { + /// When turning `impl Trait` into opaque types, we have to collect the + /// bounds at the same time to get the IDs correct (without becoming too + /// complicated). + mode: ImplTraitLoweringMode, + // This is structured as a struct with fields and not as an enum because it helps with the borrow checker. + opaque_type_data: Arena>, + param_and_variable_counter: u16, +} +impl<'db> ImplTraitLoweringState<'db> { + fn new(mode: ImplTraitLoweringMode) -> ImplTraitLoweringState<'db> { + Self { mode, opaque_type_data: Arena::new(), param_and_variable_counter: 0 } + } +} + +#[derive(Debug, Clone)] +pub(crate) enum LifetimeElisionKind<'db> { + /// Create a new anonymous lifetime parameter and reference it. + /// + /// If `report_in_path`, report an error when encountering lifetime elision in a path: + /// ```compile_fail + /// struct Foo<'a> { x: &'a () } + /// async fn foo(x: Foo) {} + /// ``` + /// + /// Note: the error should not trigger when the elided lifetime is in a pattern or + /// expression-position path: + /// ``` + /// struct Foo<'a> { x: &'a () } + /// async fn foo(Foo { x: _ }: Foo<'_>) {} + /// ``` + AnonymousCreateParameter { report_in_path: bool }, + + /// Replace all anonymous lifetimes by provided lifetime. + Elided(Region<'db>), + + /// Give a hard error when either `&` or `'_` is written. Used to + /// rule out things like `where T: Foo<'_>`. Does not imply an + /// error on default object bounds (e.g., `Box`). + AnonymousReportError, + + /// Resolves elided lifetimes to `'static` if there are no other lifetimes in scope, + /// otherwise give a warning that the previous behavior of introducing a new early-bound + /// lifetime is a bug and will be removed (if `only_lint` is enabled). + StaticIfNoLifetimeInScope { only_lint: bool }, + + /// Signal we cannot find which should be the anonymous lifetime. + ElisionFailure, + + /// Infer all elided lifetimes. + Infer, +} + +impl<'db> LifetimeElisionKind<'db> { + #[inline] + pub(crate) fn for_const( + interner: DbInterner<'db>, + const_parent: ItemContainerId, + ) -> LifetimeElisionKind<'db> { + match const_parent { + ItemContainerId::ExternBlockId(_) | ItemContainerId::ModuleId(_) => { + LifetimeElisionKind::Elided(Region::new_static(interner)) + } + ItemContainerId::ImplId(_) => { + LifetimeElisionKind::StaticIfNoLifetimeInScope { only_lint: true } + } + ItemContainerId::TraitId(_) => { + LifetimeElisionKind::StaticIfNoLifetimeInScope { only_lint: false } + } + } + } + + #[inline] + pub(crate) fn for_fn_params(data: &FunctionSignature) -> LifetimeElisionKind<'db> { + LifetimeElisionKind::AnonymousCreateParameter { report_in_path: data.is_async() } + } + + #[inline] + pub(crate) fn for_fn_ret(interner: DbInterner<'db>) -> LifetimeElisionKind<'db> { + // FIXME: We should use the elided lifetime here, or `ElisionFailure`. + LifetimeElisionKind::Elided(Region::error(interner)) + } +} + +#[derive(Debug)] +pub(crate) struct TyLoweringContext<'db, 'a> { + pub db: &'db dyn HirDatabase, + interner: DbInterner<'db>, + resolver: &'a Resolver<'db>, + store: &'a ExpressionStore, + def: GenericDefId, + generics: OnceCell, + in_binders: DebruijnIndex, + impl_trait_mode: ImplTraitLoweringState<'db>, + /// Tracks types with explicit `?Sized` bounds. + pub(crate) unsized_types: FxHashSet>, + pub(crate) diagnostics: Vec, + lifetime_elision: LifetimeElisionKind<'db>, +} + +impl<'db, 'a> TyLoweringContext<'db, 'a> { + pub(crate) fn new( + db: &'db dyn HirDatabase, + resolver: &'a Resolver<'db>, + store: &'a ExpressionStore, + def: GenericDefId, + lifetime_elision: LifetimeElisionKind<'db>, + ) -> Self { + let impl_trait_mode = ImplTraitLoweringState::new(ImplTraitLoweringMode::Disallowed); + let in_binders = DebruijnIndex::ZERO; + Self { + db, + interner: DbInterner::new_with(db, Some(resolver.krate()), None), + resolver, + def, + generics: Default::default(), + store, + in_binders, + impl_trait_mode, + unsized_types: FxHashSet::default(), + diagnostics: Vec::new(), + lifetime_elision, + } + } + + pub(crate) fn with_debruijn( + &mut self, + debruijn: DebruijnIndex, + f: impl FnOnce(&mut TyLoweringContext<'db, '_>) -> T, + ) -> T { + let old_debruijn = mem::replace(&mut self.in_binders, debruijn); + let result = f(self); + self.in_binders = old_debruijn; + result + } + + pub(crate) fn with_shifted_in( + &mut self, + debruijn: DebruijnIndex, + f: impl FnOnce(&mut TyLoweringContext<'db, '_>) -> T, + ) -> T { + self.with_debruijn(self.in_binders.shifted_in(debruijn.as_u32()), f) + } + + pub(crate) fn with_impl_trait_mode(self, impl_trait_mode: ImplTraitLoweringMode) -> Self { + Self { impl_trait_mode: ImplTraitLoweringState::new(impl_trait_mode), ..self } + } + + pub(crate) fn impl_trait_mode(&mut self, impl_trait_mode: ImplTraitLoweringMode) -> &mut Self { + self.impl_trait_mode = ImplTraitLoweringState::new(impl_trait_mode); + self + } + + pub(crate) fn push_diagnostic(&mut self, type_ref: TypeRefId, kind: TyLoweringDiagnosticKind) { + self.diagnostics.push(TyLoweringDiagnostic { source: type_ref, kind }); + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)] +pub(crate) enum ImplTraitLoweringMode { + /// `impl Trait` gets lowered into an opaque type that doesn't unify with + /// anything except itself. This is used in places where values flow 'out', + /// i.e. for arguments of the function we're currently checking, and return + /// types of functions we're calling. + Opaque, + /// `impl Trait` is disallowed and will be an error. + #[default] + Disallowed, +} + +impl<'db, 'a> TyLoweringContext<'db, 'a> { + pub(crate) fn lower_ty(&mut self, type_ref: TypeRefId) -> Ty<'db> { + self.lower_ty_ext(type_ref).0 + } + + pub(crate) fn lower_const(&mut self, const_ref: &ConstRef, const_type: Ty<'db>) -> Const<'db> { + let const_ref = &self.store[const_ref.expr]; + match const_ref { + hir_def::hir::Expr::Path(path) => { + path_to_const(self.db, self.resolver, path, || self.generics(), const_type) + .unwrap_or_else(|| unknown_const(const_type)) + } + hir_def::hir::Expr::Literal(literal) => intern_const_ref( + self.db, + &match *literal { + hir_def::hir::Literal::Float(_, _) + | hir_def::hir::Literal::String(_) + | hir_def::hir::Literal::ByteString(_) + | hir_def::hir::Literal::CString(_) => LiteralConstRef::Unknown, + hir_def::hir::Literal::Char(c) => LiteralConstRef::Char(c), + hir_def::hir::Literal::Bool(b) => LiteralConstRef::Bool(b), + hir_def::hir::Literal::Int(val, _) => LiteralConstRef::Int(val), + hir_def::hir::Literal::Uint(val, _) => LiteralConstRef::UInt(val), + }, + const_type, + self.resolver.krate(), + ), + _ => unknown_const(const_type), + } + } + + pub(crate) fn lower_path_as_const(&mut self, path: &Path, const_type: Ty<'db>) -> Const<'db> { + path_to_const(self.db, self.resolver, path, || self.generics(), const_type) + .unwrap_or_else(|| unknown_const(const_type)) + } + + fn generics(&self) -> &Generics { + self.generics.get_or_init(|| generics(self.db, self.def)) + } + + #[tracing::instrument(skip(self), ret)] + pub(crate) fn lower_ty_ext(&mut self, type_ref_id: TypeRefId) -> (Ty<'db>, Option) { + let interner = self.interner; + let mut res = None; + let type_ref = &self.store[type_ref_id]; + tracing::debug!(?type_ref); + let ty = match type_ref { + TypeRef::Never => Ty::new(interner, TyKind::Never), + TypeRef::Tuple(inner) => { + let inner_tys = inner.iter().map(|&tr| self.lower_ty(tr)); + Ty::new_tup_from_iter(interner, inner_tys) + } + TypeRef::Path(path) => { + let (ty, res_) = + self.lower_path(path, PathId::from_type_ref_unchecked(type_ref_id)); + res = res_; + ty + } + &TypeRef::TypeParam(type_param_id) => { + res = Some(TypeNs::GenericParam(type_param_id)); + + let generics = self.generics(); + let (idx, data) = + generics.type_or_const_param(type_param_id.into()).expect("matching generics"); + let type_data = match data { + TypeOrConstParamData::TypeParamData(ty) => ty, + _ => unreachable!(), + }; + Ty::new_param( + self.interner, + idx as u32, + type_data + .name + .as_ref() + .map_or_else(|| sym::MISSING_NAME.clone(), |d| d.symbol().clone()), + ) + } + &TypeRef::RawPtr(inner, mutability) => { + let inner_ty = self.lower_ty(inner); + Ty::new(interner, TyKind::RawPtr(inner_ty, lower_mutability(mutability))) + } + TypeRef::Array(array) => { + let inner_ty = self.lower_ty(array.ty); + let const_len = self.lower_const(&array.len, Ty::new_usize(interner)); + Ty::new_array_with_const_len(interner, inner_ty, const_len) + } + &TypeRef::Slice(inner) => { + let inner_ty = self.lower_ty(inner); + Ty::new_slice(interner, inner_ty) + } + TypeRef::Reference(ref_) => { + let inner_ty = self.lower_ty(ref_.ty); + // FIXME: It should infer the eldided lifetimes instead of stubbing with error + let lifetime = ref_ + .lifetime + .map_or_else(|| Region::error(interner), |lr| self.lower_lifetime(lr)); + Ty::new_ref(interner, lifetime, inner_ty, lower_mutability(ref_.mutability)) + } + TypeRef::Placeholder => Ty::new_error(interner, ErrorGuaranteed), + TypeRef::Fn(fn_) => { + let substs = self.with_shifted_in( + DebruijnIndex::from_u32(1), + |ctx: &mut TyLoweringContext<'_, '_>| { + Tys::new_from_iter( + interner, + fn_.params.iter().map(|&(_, tr)| ctx.lower_ty(tr)), + ) + }, + ); + Ty::new_fn_ptr( + interner, + Binder::dummy(FnSig { + abi: fn_.abi.as_ref().map_or(FnAbi::Rust, FnAbi::from_symbol), + safety: if fn_.is_unsafe { Safety::Unsafe } else { Safety::Safe }, + c_variadic: fn_.is_varargs, + inputs_and_output: substs, + }), + ) + } + TypeRef::DynTrait(bounds) => self.lower_dyn_trait(bounds), + TypeRef::ImplTrait(bounds) => { + match self.impl_trait_mode.mode { + ImplTraitLoweringMode::Opaque => { + let origin = match self.resolver.generic_def() { + Some(GenericDefId::FunctionId(it)) => Either::Left(it), + Some(GenericDefId::TypeAliasId(it)) => Either::Right(it), + _ => panic!( + "opaque impl trait lowering must be in function or type alias" + ), + }; + + // this dance is to make sure the data is in the right + // place even if we encounter more opaque types while + // lowering the bounds + let idx = self + .impl_trait_mode + .opaque_type_data + .alloc(ImplTrait { predicates: Vec::default() }); + + // FIXME(next-solver): this from_raw/into_raw dance isn't nice, but it's minimal + let impl_trait_id = origin.either( + |f| ImplTraitId::ReturnTypeImplTrait(f, Idx::from_raw(idx.into_raw())), + |a| ImplTraitId::TypeAliasImplTrait(a, Idx::from_raw(idx.into_raw())), + ); + let opaque_ty_id: SolverDefId = + self.db.intern_impl_trait_id(impl_trait_id).into(); + + // We don't want to lower the bounds inside the binders + // we're currently in, because they don't end up inside + // those binders. E.g. when we have `impl Trait>`, the `impl OtherTrait` can't refer + // to the self parameter from `impl Trait`, and the + // bounds aren't actually stored nested within each + // other, but separately. So if the `T` refers to a type + // parameter of the outer function, it's just one binder + // away instead of two. + let actual_opaque_type_data = self + .with_debruijn(DebruijnIndex::ZERO, |ctx| { + ctx.lower_impl_trait(opaque_ty_id, bounds, self.resolver.krate()) + }); + self.impl_trait_mode.opaque_type_data[idx] = actual_opaque_type_data; + + let args = GenericArgs::identity_for_item(self.interner, opaque_ty_id); + Ty::new_alias( + self.interner, + AliasTyKind::Opaque, + AliasTy::new_from_args(self.interner, opaque_ty_id, args), + ) + } + ImplTraitLoweringMode::Disallowed => { + // FIXME: report error + Ty::new_error(self.interner, ErrorGuaranteed) + } + } + } + TypeRef::Error => Ty::new_error(self.interner, ErrorGuaranteed), + }; + (ty, res) + } + + /// This is only for `generic_predicates_for_param`, where we can't just + /// lower the self types of the predicates since that could lead to cycles. + /// So we just check here if the `type_ref` resolves to a generic param, and which. + fn lower_ty_only_param(&self, type_ref: TypeRefId) -> Option { + let type_ref = &self.store[type_ref]; + let path = match type_ref { + TypeRef::Path(path) => path, + &TypeRef::TypeParam(idx) => return Some(idx.into()), + _ => return None, + }; + if path.type_anchor().is_some() { + return None; + } + if path.segments().len() > 1 { + return None; + } + let resolution = match self.resolver.resolve_path_in_type_ns(self.db, path) { + Some((it, None, _)) => it, + _ => return None, + }; + match resolution { + TypeNs::GenericParam(param_id) => Some(param_id.into()), + _ => None, + } + } + + #[inline] + fn on_path_diagnostic_callback(type_ref: TypeRefId) -> PathDiagnosticCallback<'static, 'db> { + PathDiagnosticCallback { + data: Either::Left(PathDiagnosticCallbackData(type_ref)), + callback: |data, this, diag| { + let type_ref = data.as_ref().left().unwrap().0; + this.push_diagnostic(type_ref, TyLoweringDiagnosticKind::PathDiagnostic(diag)) + }, + } + } + + #[inline] + fn at_path(&mut self, path_id: PathId) -> PathLoweringContext<'_, 'a, 'db> { + PathLoweringContext::new( + self, + Self::on_path_diagnostic_callback(path_id.type_ref()), + &self.store[path_id], + ) + } + + pub(crate) fn lower_path(&mut self, path: &Path, path_id: PathId) -> (Ty<'db>, Option) { + // Resolve the path (in type namespace) + if let Some(type_ref) = path.type_anchor() { + let (ty, res) = self.lower_ty_ext(type_ref); + let mut ctx = self.at_path(path_id); + return ctx.lower_ty_relative_path(ty, res); + } + + let mut ctx = self.at_path(path_id); + let (resolution, remaining_index) = match ctx.resolve_path_in_type_ns() { + Some(it) => it, + None => return (Ty::new_error(self.interner, ErrorGuaranteed), None), + }; + + if matches!(resolution, TypeNs::TraitId(_)) && remaining_index.is_none() { + // trait object type without dyn + let bound = TypeBound::Path(path_id, TraitBoundModifier::None); + let ty = self.lower_dyn_trait(&[bound]); + return (ty, None); + } + + ctx.lower_partly_resolved_path(resolution, false) + } + + fn lower_trait_ref_from_path( + &mut self, + path_id: PathId, + explicit_self_ty: Ty<'db>, + ) -> Option<(TraitRef<'db>, PathLoweringContext<'_, 'a, 'db>)> { + let mut ctx = self.at_path(path_id); + let resolved = match ctx.resolve_path_in_type_ns_fully()? { + // FIXME(trait_alias): We need to handle trait alias here. + TypeNs::TraitId(tr) => tr, + _ => return None, + }; + Some((ctx.lower_trait_ref_from_resolved_path(resolved, explicit_self_ty), ctx)) + } + + fn lower_trait_ref( + &mut self, + trait_ref: &HirTraitRef, + explicit_self_ty: Ty<'db>, + ) -> Option> { + self.lower_trait_ref_from_path(trait_ref.path, explicit_self_ty).map(|it| it.0) + } + + pub(crate) fn lower_where_predicate<'b>( + &'b mut self, + where_predicate: &'b WherePredicate, + ignore_bindings: bool, + generics: &Generics, + predicate_filter: PredicateFilter, + ) -> impl Iterator> + use<'a, 'b, 'db> { + match where_predicate { + WherePredicate::ForLifetime { target, bound, .. } + | WherePredicate::TypeBound { target, bound } => { + if let PredicateFilter::SelfTrait = predicate_filter { + let target_type = &self.store[*target]; + let self_type = 'is_self: { + if let TypeRef::Path(path) = target_type + && path.is_self_type() + { + break 'is_self true; + } + if let TypeRef::TypeParam(param) = target_type + && generics[param.local_id()].is_trait_self() + { + break 'is_self true; + } + false + }; + if !self_type { + return Either::Left(Either::Left(iter::empty())); + } + } + let self_ty = self.lower_ty(*target); + Either::Left(Either::Right(self.lower_type_bound(bound, self_ty, ignore_bindings))) + } + &WherePredicate::Lifetime { bound, target } => { + Either::Right(iter::once(Clause(Predicate::new( + self.interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::RegionOutlives(OutlivesPredicate( + self.lower_lifetime(bound), + self.lower_lifetime(target), + )), + )), + )))) + } + } + .into_iter() + } + + pub(crate) fn lower_type_bound<'b>( + &'b mut self, + bound: &'b TypeBound, + self_ty: Ty<'db>, + ignore_bindings: bool, + ) -> impl Iterator> + use<'b, 'a, 'db> { + let interner = self.interner; + let mut assoc_bounds = None; + let mut clause = None; + match bound { + &TypeBound::Path(path, TraitBoundModifier::None) | &TypeBound::ForLifetime(_, path) => { + // FIXME Don't silently drop the hrtb lifetimes here + if let Some((trait_ref, mut ctx)) = self.lower_trait_ref_from_path(path, self_ty) { + // FIXME(sized-hierarchy): Remove this bound modifications once we have implemented + // sized-hierarchy correctly. + let meta_sized = LangItem::MetaSized + .resolve_trait(ctx.ty_ctx().db, ctx.ty_ctx().resolver.krate()); + let pointee_sized = LangItem::PointeeSized + .resolve_trait(ctx.ty_ctx().db, ctx.ty_ctx().resolver.krate()); + if meta_sized.is_some_and(|it| SolverDefId::TraitId(it) == trait_ref.def_id) { + // Ignore this bound + } else if pointee_sized + .is_some_and(|it| SolverDefId::TraitId(it) == trait_ref.def_id) + { + // Regard this as `?Sized` bound + ctx.ty_ctx().unsized_types.insert(self_ty); + } else { + if !ignore_bindings { + assoc_bounds = ctx.assoc_type_bindings_from_type_bound(trait_ref); + } + clause = Some(Clause(Predicate::new( + interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity: rustc_type_ir::PredicatePolarity::Positive, + }), + )), + ))); + } + } + } + &TypeBound::Path(path, TraitBoundModifier::Maybe) => { + let sized_trait = LangItem::Sized.resolve_trait(self.db, self.resolver.krate()); + // Don't lower associated type bindings as the only possible relaxed trait bound + // `?Sized` has no of them. + // If we got another trait here ignore the bound completely. + let trait_id = + self.lower_trait_ref_from_path(path, self_ty).map(|(trait_ref, _)| { + match trait_ref.def_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + } + }); + if trait_id == sized_trait { + self.unsized_types.insert(self_ty); + } + } + &TypeBound::Lifetime(l) => { + let lifetime = self.lower_lifetime(l); + clause = Some(Clause(Predicate::new( + self.interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::TypeOutlives(OutlivesPredicate( + self_ty, lifetime, + )), + )), + ))); + } + TypeBound::Use(_) | TypeBound::Error => {} + } + clause.into_iter().chain(assoc_bounds.into_iter().flatten()) + } + + fn lower_dyn_trait(&mut self, bounds: &[TypeBound]) -> Ty<'db> { + let interner = self.interner; + // FIXME: we should never create non-existential predicates in the first place + // For now, use an error type so we don't run into dummy binder issues + let self_ty = Ty::new_error(interner, ErrorGuaranteed); + // INVARIANT: The principal trait bound, if present, must come first. Others may be in any + // order but should be in the same order for the same set but possibly different order of + // bounds in the input. + // INVARIANT: If this function returns `DynTy`, there should be at least one trait bound. + // These invariants are utilized by `TyExt::dyn_trait()` and chalk. + let mut lifetime = None; + let bounds = self.with_shifted_in(DebruijnIndex::from_u32(1), |ctx| { + let mut lowered_bounds: Vec< + rustc_type_ir::Binder, ExistentialPredicate>>, + > = Vec::new(); + for b in bounds { + let db = ctx.db; + ctx.lower_type_bound(b, self_ty, false).for_each(|b| { + if let Some(bound) = b + .kind() + .map_bound(|c| match c { + rustc_type_ir::ClauseKind::Trait(t) => { + let id = t.def_id(); + let id = match id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + let is_auto = + db.trait_signature(id).flags.contains(TraitFlags::AUTO); + if is_auto { + Some(ExistentialPredicate::AutoTrait(t.def_id())) + } else { + Some(ExistentialPredicate::Trait( + ExistentialTraitRef::new_from_args( + interner, + t.def_id(), + GenericArgs::new_from_iter( + interner, + t.trait_ref.args.iter().skip(1), + ), + ), + )) + } + } + rustc_type_ir::ClauseKind::Projection(p) => { + Some(ExistentialPredicate::Projection( + ExistentialProjection::new_from_args( + interner, + p.def_id(), + GenericArgs::new_from_iter( + interner, + p.projection_term.args.iter().skip(1), + ), + p.term, + ), + )) + } + rustc_type_ir::ClauseKind::TypeOutlives(outlives_predicate) => { + lifetime = Some(outlives_predicate.1); + None + } + rustc_type_ir::ClauseKind::RegionOutlives(_) + | rustc_type_ir::ClauseKind::ConstArgHasType(_, _) + | rustc_type_ir::ClauseKind::WellFormed(_) + | rustc_type_ir::ClauseKind::ConstEvaluatable(_) + | rustc_type_ir::ClauseKind::HostEffect(_) + | rustc_type_ir::ClauseKind::UnstableFeature(_) => unreachable!(), + }) + .transpose() + { + lowered_bounds.push(bound); + } + }) + } + + let mut multiple_regular_traits = false; + let mut multiple_same_projection = false; + lowered_bounds.sort_unstable_by(|lhs, rhs| { + use std::cmp::Ordering; + match ((*lhs).skip_binder(), (*rhs).skip_binder()) { + (ExistentialPredicate::Trait(_), ExistentialPredicate::Trait(_)) => { + multiple_regular_traits = true; + // Order doesn't matter - we error + Ordering::Equal + } + ( + ExistentialPredicate::AutoTrait(lhs_id), + ExistentialPredicate::AutoTrait(rhs_id), + ) => { + let lhs_id = match lhs_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + let rhs_id = match rhs_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + lhs_id.cmp(&rhs_id) + } + (ExistentialPredicate::Trait(_), _) => Ordering::Less, + (_, ExistentialPredicate::Trait(_)) => Ordering::Greater, + (ExistentialPredicate::AutoTrait(_), _) => Ordering::Less, + (_, ExistentialPredicate::AutoTrait(_)) => Ordering::Greater, + ( + ExistentialPredicate::Projection(lhs), + ExistentialPredicate::Projection(rhs), + ) => { + let lhs_id = match lhs.def_id { + SolverDefId::TypeAliasId(id) => id, + _ => unreachable!(), + }; + let rhs_id = match rhs.def_id { + SolverDefId::TypeAliasId(id) => id, + _ => unreachable!(), + }; + // We only compare the `associated_ty_id`s. We shouldn't have + // multiple bounds for an associated type in the correct Rust code, + // and if we do, we error out. + if lhs_id == rhs_id { + multiple_same_projection = true; + } + lhs_id.as_id().index().cmp(&rhs_id.as_id().index()) + } + } + }); + + if multiple_regular_traits || multiple_same_projection { + return None; + } + + if !lowered_bounds.first().map_or(false, |b| { + matches!( + b.as_ref().skip_binder(), + ExistentialPredicate::Trait(_) | ExistentialPredicate::AutoTrait(_) + ) + }) { + return None; + } + + // As multiple occurrences of the same auto traits *are* permitted, we deduplicate the + // bounds. We shouldn't have repeated elements besides auto traits at this point. + lowered_bounds.dedup(); + + Some(BoundExistentialPredicates::new_from_iter(interner, lowered_bounds)) + }); + + if let Some(bounds) = bounds { + let region = match lifetime { + Some(it) => match it.kind() { + rustc_type_ir::RegionKind::ReBound(db, var) => Region::new_bound( + self.interner, + db.shifted_out_to_binder(DebruijnIndex::from_u32(2)), + var, + ), + _ => it, + }, + None => Region::new_static(self.interner), + }; + Ty::new_dynamic(self.interner, bounds, region, rustc_type_ir::DynKind::Dyn) + } else { + // FIXME: report error + // (additional non-auto traits, associated type rebound, or no resolved trait) + Ty::new_error(self.interner, ErrorGuaranteed) + } + } + + fn lower_impl_trait( + &mut self, + def_id: SolverDefId, + bounds: &[TypeBound], + krate: Crate, + ) -> ImplTrait<'db> { + let interner = self.interner; + cov_mark::hit!(lower_rpit); + let args = GenericArgs::identity_for_item(interner, def_id); + let self_ty = Ty::new_alias( + self.interner, + rustc_type_ir::AliasTyKind::Opaque, + AliasTy::new_from_args(interner, def_id, args), + ); + let predicates = self.with_shifted_in(DebruijnIndex::from_u32(1), |ctx| { + let mut predicates = Vec::new(); + for b in bounds { + predicates.extend(ctx.lower_type_bound(b, self_ty, false)); + } + + if !ctx.unsized_types.contains(&self_ty) { + let sized_trait = LangItem::Sized.resolve_trait(self.db, krate); + let sized_clause = sized_trait.map(|trait_id| { + let trait_ref = TraitRef::new_from_args( + interner, + trait_id.into(), + GenericArgs::new_from_iter(interner, [self_ty.into()]), + ); + Clause(Predicate::new( + interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity: rustc_type_ir::PredicatePolarity::Positive, + }), + )), + )) + }); + predicates.extend(sized_clause); + } + predicates.shrink_to_fit(); + predicates + }); + ImplTrait { predicates } + } + + pub(crate) fn lower_lifetime(&self, lifetime: LifetimeRefId) -> Region<'db> { + match self.resolver.resolve_lifetime(&self.store[lifetime]) { + Some(resolution) => match resolution { + LifetimeNs::Static => Region::new_static(self.interner), + LifetimeNs::LifetimeParam(id) => { + let idx = match self.generics().lifetime_idx(id) { + None => return Region::error(self.interner), + Some(idx) => idx, + }; + Region::new_early_param(self.interner, EarlyParamRegion { index: idx as u32 }) + } + }, + None => Region::error(self.interner), + } + } +} + +pub(crate) fn lower_mutability(m: hir_def::type_ref::Mutability) -> Mutability { + match m { + hir_def::type_ref::Mutability::Shared => Mutability::Not, + hir_def::type_ref::Mutability::Mut => Mutability::Mut, + } +} + +fn unknown_const(_ty: Ty<'_>) -> Const<'_> { + Const::new(DbInterner::conjure(), ConstKind::Error(ErrorGuaranteed)) +} + +pub(crate) fn impl_trait_query<'db>( + db: &'db dyn HirDatabase, + impl_id: ImplId, +) -> Option>> { + db.impl_trait_with_diagnostics_ns(impl_id).map(|it| it.0) +} + +pub(crate) fn impl_trait_with_diagnostics_query<'db>( + db: &'db dyn HirDatabase, + impl_id: ImplId, +) -> Option<(EarlyBinder<'db, TraitRef<'db>>, Diagnostics)> { + let impl_data = db.impl_signature(impl_id); + let resolver = impl_id.resolver(db); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &impl_data.store, + impl_id.into(), + LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true }, + ); + let self_ty = db.impl_self_ty_ns(impl_id).skip_binder(); + let target_trait = impl_data.target_trait.as_ref()?; + let trait_ref = EarlyBinder::bind(ctx.lower_trait_ref(target_trait, self_ty)?); + Some((trait_ref, create_diagnostics(ctx.diagnostics))) +} + +pub(crate) fn return_type_impl_traits<'db>( + db: &'db dyn HirDatabase, + def: hir_def::FunctionId, +) -> Option>>> { + // FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe + let data = db.function_signature(def); + let resolver = def.resolver(db); + let mut ctx_ret = + TyLoweringContext::new(db, &resolver, &data.store, def.into(), LifetimeElisionKind::Infer) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); + if let Some(ret_type) = data.ret_type { + let _ret = ctx_ret.lower_ty(ret_type); + } + let return_type_impl_traits = + ImplTraits { impl_traits: ctx_ret.impl_trait_mode.opaque_type_data }; + if return_type_impl_traits.impl_traits.is_empty() { + None + } else { + Some(Arc::new(EarlyBinder::bind(return_type_impl_traits))) + } +} + +pub(crate) fn type_alias_impl_traits<'db>( + db: &'db dyn HirDatabase, + def: hir_def::TypeAliasId, +) -> Option>>> { + let data = db.type_alias_signature(def); + let resolver = def.resolver(db); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &data.store, + def.into(), + LifetimeElisionKind::AnonymousReportError, + ) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); + if let Some(type_ref) = data.ty { + let _ty = ctx.lower_ty(type_ref); + } + let type_alias_impl_traits = ImplTraits { impl_traits: ctx.impl_trait_mode.opaque_type_data }; + if type_alias_impl_traits.impl_traits.is_empty() { + None + } else { + Some(Arc::new(EarlyBinder::bind(type_alias_impl_traits))) + } +} + +/// Build the declared type of an item. This depends on the namespace; e.g. for +/// `struct Foo(usize)`, we have two types: The type of the struct itself, and +/// the constructor function `(usize) -> Foo` which lives in the values +/// namespace. +pub(crate) fn ty_query<'db>(db: &'db dyn HirDatabase, def: TyDefId) -> EarlyBinder<'db, Ty<'db>> { + let interner = DbInterner::new_with(db, None, None); + match def { + TyDefId::BuiltinType(it) => EarlyBinder::bind(builtin(interner, it)), + TyDefId::AdtId(it) => EarlyBinder::bind(Ty::new_adt( + interner, + AdtDef::new(it, interner), + GenericArgs::identity_for_item(interner, it.into()), + )), + TyDefId::TypeAliasId(it) => db.type_for_type_alias_with_diagnostics_ns(it).0, + } +} + +pub(crate) fn type_for_type_alias_with_diagnostics_query<'db>( + db: &'db dyn HirDatabase, + t: TypeAliasId, +) -> (EarlyBinder<'db, Ty<'db>>, Diagnostics) { + let type_alias_data = db.type_alias_signature(t); + let mut diags = None; + let resolver = t.resolver(db); + let interner = DbInterner::new_with(db, Some(resolver.krate()), None); + let inner = if type_alias_data.flags.contains(TypeAliasFlags::IS_EXTERN) { + EarlyBinder::bind(Ty::new_foreign(interner, t.into())) + } else { + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &type_alias_data.store, + t.into(), + LifetimeElisionKind::AnonymousReportError, + ) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); + let res = EarlyBinder::bind( + type_alias_data + .ty + .map(|type_ref| ctx.lower_ty(type_ref)) + .unwrap_or_else(|| Ty::new_error(interner, ErrorGuaranteed)), + ); + diags = create_diagnostics(ctx.diagnostics); + res + }; + (inner, diags) +} + +pub(crate) fn type_for_type_alias_with_diagnostics_cycle_result<'db>( + db: &'db dyn HirDatabase, + _adt: TypeAliasId, +) -> (EarlyBinder<'db, Ty<'db>>, Diagnostics) { + (EarlyBinder::bind(Ty::new_error(DbInterner::new_with(db, None, None), ErrorGuaranteed)), None) +} + +pub(crate) fn impl_self_ty_query<'db>( + db: &'db dyn HirDatabase, + impl_id: ImplId, +) -> EarlyBinder<'db, Ty<'db>> { + db.impl_self_ty_with_diagnostics_ns(impl_id).0 +} + +pub(crate) fn impl_self_ty_with_diagnostics_query<'db>( + db: &'db dyn HirDatabase, + impl_id: ImplId, +) -> (EarlyBinder<'db, Ty<'db>>, Diagnostics) { + let resolver = impl_id.resolver(db); + let interner = DbInterner::new_with(db, Some(resolver.krate()), None); + + let impl_data = db.impl_signature(impl_id); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &impl_data.store, + impl_id.into(), + LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true }, + ); + let ty = ctx.lower_ty(impl_data.self_ty); + assert!(!ty.has_escaping_bound_vars()); + (EarlyBinder::bind(ty), create_diagnostics(ctx.diagnostics)) +} + +pub(crate) fn impl_self_ty_with_diagnostics_cycle_result( + db: &dyn HirDatabase, + _impl_id: ImplId, +) -> (EarlyBinder<'_, Ty<'_>>, Diagnostics) { + (EarlyBinder::bind(Ty::new_error(DbInterner::new_with(db, None, None), ErrorGuaranteed)), None) +} + +pub(crate) fn const_param_ty_query<'db>(db: &'db dyn HirDatabase, def: ConstParamId) -> Ty<'db> { + db.const_param_ty_with_diagnostics_ns(def).0 +} + +// returns None if def is a type arg +pub(crate) fn const_param_ty_with_diagnostics_query<'db>( + db: &'db dyn HirDatabase, + def: ConstParamId, +) -> (Ty<'db>, Diagnostics) { + let (parent_data, store) = db.generic_params_and_store(def.parent()); + let data = &parent_data[def.local_id()]; + let resolver = def.parent().resolver(db); + let interner = DbInterner::new_with(db, Some(resolver.krate()), None); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &store, + def.parent(), + LifetimeElisionKind::AnonymousReportError, + ); + let ty = match data { + TypeOrConstParamData::TypeParamData(_) => { + never!(); + Ty::new_error(interner, ErrorGuaranteed) + } + TypeOrConstParamData::ConstParamData(d) => ctx.lower_ty(d.ty), + }; + (ty, create_diagnostics(ctx.diagnostics)) +} + +pub(crate) fn field_types_query<'db>( + db: &'db dyn HirDatabase, + variant_id: VariantId, +) -> Arc>>> { + db.field_types_with_diagnostics_ns(variant_id).0 +} + +/// Build the type of all specific fields of a struct or enum variant. +pub(crate) fn field_types_with_diagnostics_query<'db>( + db: &'db dyn HirDatabase, + variant_id: VariantId, +) -> (Arc>>>, Diagnostics) { + let var_data = variant_id.fields(db); + let fields = var_data.fields(); + if fields.is_empty() { + return (Arc::new(ArenaMap::default()), None); + } + + let (resolver, def): (_, GenericDefId) = match variant_id { + VariantId::StructId(it) => (it.resolver(db), it.into()), + VariantId::UnionId(it) => (it.resolver(db), it.into()), + VariantId::EnumVariantId(it) => (it.resolver(db), it.lookup(db).parent.into()), + }; + let mut res = ArenaMap::default(); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &var_data.store, + def, + LifetimeElisionKind::AnonymousReportError, + ); + for (field_id, field_data) in var_data.fields().iter() { + res.insert(field_id, EarlyBinder::bind(ctx.lower_ty(field_data.type_ref))); + } + (Arc::new(res), create_diagnostics(ctx.diagnostics)) +} + +/// This query exists only to be used when resolving short-hand associated types +/// like `T::Item`. +/// +/// See the analogous query in rustc and its comment: +/// +/// This is a query mostly to handle cycles somewhat gracefully; e.g. the +/// following bounds are disallowed: `T: Foo, U: Foo`, but +/// these are fine: `T: Foo, U: Foo<()>`. +#[tracing::instrument(skip(db), ret)] +pub(crate) fn generic_predicates_for_param_query<'db>( + db: &'db dyn HirDatabase, + def: GenericDefId, + param_id: TypeOrConstParamId, + assoc_name: Option, +) -> GenericPredicates<'db> { + let generics = generics(db, def); + let interner = DbInterner::new_with(db, None, None); + let resolver = def.resolver(db); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + generics.store(), + def, + LifetimeElisionKind::AnonymousReportError, + ); + + // we have to filter out all other predicates *first*, before attempting to lower them + let predicate = |pred: &_, ctx: &mut TyLoweringContext<'_, '_>| match pred { + WherePredicate::ForLifetime { target, bound, .. } + | WherePredicate::TypeBound { target, bound, .. } => { + let invalid_target = { ctx.lower_ty_only_param(*target) != Some(param_id) }; + if invalid_target { + // FIXME(sized-hierarchy): Revisit and adjust this properly once we have implemented + // sized-hierarchy correctly. + // If this is filtered out without lowering, `?Sized` or `PointeeSized` is not gathered into + // `ctx.unsized_types` + let lower = || -> bool { + match bound { + TypeBound::Path(_, TraitBoundModifier::Maybe) => true, + TypeBound::Path(path, _) | TypeBound::ForLifetime(_, path) => { + let TypeRef::Path(path) = &ctx.store[path.type_ref()] else { + return false; + }; + let Some(pointee_sized) = + LangItem::PointeeSized.resolve_trait(ctx.db, ctx.resolver.krate()) + else { + return false; + }; + // Lower the path directly with `Resolver` instead of PathLoweringContext` + // to prevent diagnostics duplications. + ctx.resolver.resolve_path_in_type_ns_fully(ctx.db, path).is_some_and( + |it| matches!(it, TypeNs::TraitId(tr) if tr == pointee_sized), + ) + } + _ => false, + } + }(); + if lower { + ctx.lower_where_predicate(pred, true, &generics, PredicateFilter::All) + .for_each(drop); + } + return false; + } + + match bound { + &TypeBound::ForLifetime(_, path) | &TypeBound::Path(path, _) => { + // Only lower the bound if the trait could possibly define the associated + // type we're looking for. + let path = &ctx.store[path]; + + let Some(assoc_name) = &assoc_name else { return true }; + let Some(TypeNs::TraitId(tr)) = + resolver.resolve_path_in_type_ns_fully(db, path) + else { + return false; + }; + + rustc_type_ir::elaborate::supertrait_def_ids(interner, tr.into()).any(|tr| { + let tr = match tr { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + tr.trait_items(db).items.iter().any(|(name, item)| { + matches!(item, AssocItemId::TypeAliasId(_)) && name == assoc_name + }) + }) + } + TypeBound::Use(_) | TypeBound::Lifetime(_) | TypeBound::Error => false, + } + } + WherePredicate::Lifetime { .. } => false, + }; + let mut predicates = Vec::new(); + for maybe_parent_generics in + std::iter::successors(Some(&generics), |generics| generics.parent_generics()) + { + ctx.store = maybe_parent_generics.store(); + for pred in maybe_parent_generics.where_predicates() { + if predicate(pred, &mut ctx) { + predicates.extend(ctx.lower_where_predicate( + pred, + true, + maybe_parent_generics, + PredicateFilter::All, + )); + } + } + } + + let args = GenericArgs::identity_for_item(interner, def.into()); + if !args.is_empty() { + let explicitly_unsized_tys = ctx.unsized_types; + if let Some(implicitly_sized_predicates) = + implicitly_sized_clauses(db, param_id.parent, &explicitly_unsized_tys, &args, &resolver) + { + predicates.extend(implicitly_sized_predicates); + }; + } + GenericPredicates(predicates.is_empty().not().then(|| predicates.into())) +} + +pub(crate) fn generic_predicates_for_param_cycle_result( + _db: &dyn HirDatabase, + _def: GenericDefId, + _param_id: TypeOrConstParamId, + _assoc_name: Option, +) -> GenericPredicates<'_> { + GenericPredicates(None) +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct GenericPredicates<'db>(Option]>>); + +impl<'db> ops::Deref for GenericPredicates<'db> { + type Target = [Clause<'db>]; + + fn deref(&self) -> &Self::Target { + self.0.as_deref().unwrap_or(&[]) + } +} + +#[derive(Copy, Clone, Debug)] +pub(crate) enum PredicateFilter { + SelfTrait, + All, +} + +/// Resolve the where clause(s) of an item with generics. +#[tracing::instrument(skip(db))] +pub(crate) fn generic_predicates_query<'db>( + db: &'db dyn HirDatabase, + def: GenericDefId, +) -> GenericPredicates<'db> { + generic_predicates_filtered_by(db, def, PredicateFilter::All, |_| true).0 +} + +pub(crate) fn generic_predicates_without_parent_query<'db>( + db: &'db dyn HirDatabase, + def: GenericDefId, +) -> GenericPredicates<'db> { + generic_predicates_filtered_by(db, def, PredicateFilter::All, |d| d == def).0 +} + +/// Resolve the where clause(s) of an item with generics, +/// except the ones inherited from the parent +pub(crate) fn generic_predicates_without_parent_with_diagnostics_query<'db>( + db: &'db dyn HirDatabase, + def: GenericDefId, +) -> (GenericPredicates<'db>, Diagnostics) { + generic_predicates_filtered_by(db, def, PredicateFilter::All, |d| d == def) +} + +/// Resolve the where clause(s) of an item with generics, +/// with a given filter +#[tracing::instrument(skip(db, filter), ret)] +pub(crate) fn generic_predicates_filtered_by<'db, F>( + db: &'db dyn HirDatabase, + def: GenericDefId, + predicate_filter: PredicateFilter, + filter: F, +) -> (GenericPredicates<'db>, Diagnostics) +where + F: Fn(GenericDefId) -> bool, +{ + let generics = generics(db, def); + let resolver = def.resolver(db); + let interner = DbInterner::new_with(db, Some(resolver.krate()), None); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + generics.store(), + def, + LifetimeElisionKind::AnonymousReportError, + ); + + let mut predicates = Vec::new(); + for maybe_parent_generics in + std::iter::successors(Some(&generics), |generics| generics.parent_generics()) + { + ctx.store = maybe_parent_generics.store(); + for pred in maybe_parent_generics.where_predicates() { + tracing::debug!(?pred); + if filter(maybe_parent_generics.def()) { + // We deliberately use `generics` and not `maybe_parent_generics` here. This is not a mistake! + // If we use the parent generics + predicates.extend(ctx.lower_where_predicate( + pred, + false, + maybe_parent_generics, + predicate_filter, + )); + } + } + } + + let explicitly_unsized_tys = ctx.unsized_types; + + let sized_trait = LangItem::Sized.resolve_trait(db, resolver.krate()); + if let Some(sized_trait) = sized_trait { + let (mut generics, mut def_id) = + (crate::next_solver::generics::generics(db, def.into()), def); + loop { + if filter(def_id) { + let self_idx = trait_self_param_idx(db, def_id); + for (idx, p) in generics.own_params.iter().enumerate() { + if let Some(self_idx) = self_idx + && p.index() as usize == self_idx + { + continue; + } + let GenericParamDefKind::Type = p.kind else { + continue; + }; + let idx = idx as u32 + generics.parent_count as u32; + let param_ty = Ty::new_param(interner, idx, p.name.clone()); + if explicitly_unsized_tys.contains(¶m_ty) { + continue; + } + let trait_ref = TraitRef::new_from_args( + interner, + sized_trait.into(), + GenericArgs::new_from_iter(interner, [param_ty.into()]), + ); + let clause = Clause(Predicate::new( + interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity: rustc_type_ir::PredicatePolarity::Positive, + }), + )), + )); + predicates.push(clause); + } + } + + if let Some(g) = generics.parent { + generics = crate::next_solver::generics::generics(db, g.into()); + def_id = g; + } else { + break; + } + } + } + + ( + GenericPredicates(predicates.is_empty().not().then(|| predicates.into())), + create_diagnostics(ctx.diagnostics), + ) +} + +/// Generate implicit `: Sized` predicates for all generics that has no `?Sized` bound. +/// Exception is Self of a trait def. +fn implicitly_sized_clauses<'a, 'subst, 'db>( + db: &'db dyn HirDatabase, + def: GenericDefId, + explicitly_unsized_tys: &'a FxHashSet>, + args: &'subst GenericArgs<'db>, + resolver: &Resolver<'db>, +) -> Option> + Captures<'a> + Captures<'subst>> { + let interner = DbInterner::new_with(db, Some(resolver.krate()), None); + let sized_trait = LangItem::Sized.resolve_trait(db, resolver.krate())?; + + let trait_self_idx = trait_self_param_idx(db, def); + + Some( + args.iter() + .enumerate() + .filter_map( + move |(idx, generic_arg)| { + if Some(idx) == trait_self_idx { None } else { Some(generic_arg) } + }, + ) + .filter_map(|generic_arg| generic_arg.as_type()) + .filter(move |self_ty| !explicitly_unsized_tys.contains(self_ty)) + .map(move |self_ty| { + let trait_ref = TraitRef::new_from_args( + interner, + sized_trait.into(), + GenericArgs::new_from_iter(interner, [self_ty.into()]), + ); + Clause(Predicate::new( + interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity: rustc_type_ir::PredicatePolarity::Positive, + }), + )), + )) + }), + ) +} + +pub(crate) fn make_binders<'db, T: rustc_type_ir::TypeVisitable>>( + interner: DbInterner<'db>, + generics: &Generics, + value: T, +) -> Binder<'db, T> { + Binder::bind_with_vars( + value, + BoundVarKinds::new_from_iter( + interner, + generics.iter_id().map(|x| match x { + hir_def::GenericParamId::ConstParamId(_) => BoundVarKind::Const, + hir_def::GenericParamId::TypeParamId(_) => BoundVarKind::Ty(BoundTyKind::Anon), + hir_def::GenericParamId::LifetimeParamId(_) => { + BoundVarKind::Region(BoundRegionKind::Anon) + } + }), + ), + ) +} + +/// Checks if the provided generic arg matches its expected kind, then lower them via +/// provided closures. Use unknown if there was kind mismatch. +/// +pub(crate) fn lower_generic_arg<'a, 'db, T>( + db: &'db dyn HirDatabase, + kind_id: GenericParamId, + arg: &'a GenericArg, + this: &mut T, + store: &ExpressionStore, + for_type: impl FnOnce(&mut T, TypeRefId) -> Ty<'db> + 'a, + for_const: impl FnOnce(&mut T, &ConstRef, Ty<'db>) -> Const<'db> + 'a, + for_const_ty_path_fallback: impl FnOnce(&mut T, &Path, Ty<'db>) -> Const<'db> + 'a, + for_lifetime: impl FnOnce(&mut T, &LifetimeRefId) -> Region<'db> + 'a, +) -> crate::next_solver::GenericArg<'db> { + let interner = DbInterner::new_with(db, None, None); + let kind = match kind_id { + GenericParamId::TypeParamId(_) => ParamKind::Type, + GenericParamId::ConstParamId(id) => { + let ty = db.const_param_ty(id); + ParamKind::Const(ty) + } + GenericParamId::LifetimeParamId(_) => ParamKind::Lifetime, + }; + match (arg, kind) { + (GenericArg::Type(type_ref), ParamKind::Type) => for_type(this, *type_ref).into(), + (GenericArg::Const(c), ParamKind::Const(c_ty)) => { + for_const(this, c, c_ty.to_nextsolver(interner)).into() + } + (GenericArg::Lifetime(lifetime_ref), ParamKind::Lifetime) => { + for_lifetime(this, lifetime_ref).into() + } + (GenericArg::Const(_), ParamKind::Type) => Ty::new_error(interner, ErrorGuaranteed).into(), + (GenericArg::Lifetime(_), ParamKind::Type) => { + Ty::new_error(interner, ErrorGuaranteed).into() + } + (GenericArg::Type(t), ParamKind::Const(c_ty)) => match &store[*t] { + TypeRef::Path(p) => { + for_const_ty_path_fallback(this, p, c_ty.to_nextsolver(interner)).into() + } + _ => unknown_const_as_generic(c_ty.to_nextsolver(interner)), + }, + (GenericArg::Lifetime(_), ParamKind::Const(c_ty)) => { + unknown_const(c_ty.to_nextsolver(interner)).into() + } + (GenericArg::Type(_), ParamKind::Lifetime) => Region::error(interner).into(), + (GenericArg::Const(_), ParamKind::Lifetime) => Region::error(interner).into(), + } +} + +/// Build the signature of a callable item (function, struct or enum variant). +pub(crate) fn callable_item_signature_query<'db>( + db: &'db dyn HirDatabase, + def: CallableDefId, +) -> EarlyBinder<'db, PolyFnSig<'db>> { + match def { + CallableDefId::FunctionId(f) => fn_sig_for_fn(db, f), + CallableDefId::StructId(s) => fn_sig_for_struct_constructor(db, s), + CallableDefId::EnumVariantId(e) => fn_sig_for_enum_variant_constructor(db, e), + } +} + +fn fn_sig_for_fn<'db>( + db: &'db dyn HirDatabase, + def: FunctionId, +) -> EarlyBinder<'db, PolyFnSig<'db>> { + let data = db.function_signature(def); + let resolver = def.resolver(db); + let interner = DbInterner::new_with(db, Some(resolver.krate()), None); + let mut ctx_params = TyLoweringContext::new( + db, + &resolver, + &data.store, + def.into(), + LifetimeElisionKind::for_fn_params(&data), + ); + let params = data.params.iter().map(|&tr| ctx_params.lower_ty(tr)); + + let ret = match data.ret_type { + Some(ret_type) => { + let mut ctx_ret = TyLoweringContext::new( + db, + &resolver, + &data.store, + def.into(), + LifetimeElisionKind::for_fn_ret(interner), + ) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); + ctx_ret.lower_ty(ret_type) + } + None => Ty::new_tup(interner, &[]), + }; + + let inputs_and_output = Tys::new_from_iter(interner, params.chain(Some(ret))); + // If/when we track late bound vars, we need to switch this to not be `dummy` + EarlyBinder::bind(rustc_type_ir::Binder::dummy(FnSig { + abi: data.abi.as_ref().map_or(FnAbi::Rust, FnAbi::from_symbol), + c_variadic: data.is_varargs(), + safety: if data.is_unsafe() { Safety::Unsafe } else { Safety::Safe }, + inputs_and_output, + })) +} + +fn type_for_adt<'db>(db: &'db dyn HirDatabase, adt: AdtId) -> EarlyBinder<'db, Ty<'db>> { + let interner = DbInterner::new_with(db, None, None); + let args = GenericArgs::identity_for_item(interner, adt.into()); + let ty = Ty::new_adt(interner, AdtDef::new(adt, interner), args); + EarlyBinder::bind(ty) +} + +fn fn_sig_for_struct_constructor<'db>( + db: &'db dyn HirDatabase, + def: StructId, +) -> EarlyBinder<'db, PolyFnSig<'db>> { + let field_tys = db.field_types_ns(def.into()); + let params = field_tys.iter().map(|(_, ty)| ty.skip_binder()); + let ret = type_for_adt(db, def.into()).skip_binder(); + + let inputs_and_output = + Tys::new_from_iter(DbInterner::new_with(db, None, None), params.chain(Some(ret))); + EarlyBinder::bind(Binder::dummy(FnSig { + abi: FnAbi::RustCall, + c_variadic: false, + safety: Safety::Safe, + inputs_and_output, + })) +} + +fn fn_sig_for_enum_variant_constructor<'db>( + db: &'db dyn HirDatabase, + def: EnumVariantId, +) -> EarlyBinder<'db, PolyFnSig<'db>> { + let field_tys = db.field_types_ns(def.into()); + let params = field_tys.iter().map(|(_, ty)| ty.skip_binder()); + let parent = def.lookup(db).parent; + let ret = type_for_adt(db, parent.into()).skip_binder(); + + let inputs_and_output = + Tys::new_from_iter(DbInterner::new_with(db, None, None), params.chain(Some(ret))); + EarlyBinder::bind(Binder::dummy(FnSig { + abi: FnAbi::RustCall, + c_variadic: false, + safety: Safety::Safe, + inputs_and_output, + })) +} + +pub(crate) fn associated_type_by_name_including_super_traits<'db>( + db: &'db dyn HirDatabase, + trait_ref: TraitRef<'db>, + name: &Name, +) -> Option<(TraitRef<'db>, TypeAliasId)> { + let interner = DbInterner::new_with(db, None, None); + rustc_type_ir::elaborate::supertraits(interner, Binder::dummy(trait_ref)).find_map(|t| { + let trait_id = match t.as_ref().skip_binder().def_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + let assoc_type = trait_id.trait_items(db).associated_type_by_name(name)?; + Some((t.skip_binder(), assoc_type)) + }) +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs new file mode 100644 index 000000000000..b95bb1130fa2 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs @@ -0,0 +1,1459 @@ +//! A wrapper around [`TyLoweringContext`] specifically for lowering paths. + +use std::ops::Deref; + +use either::Either; +use hir_def::{ + AssocItemId, GenericDefId, GenericParamId, Lookup, TraitId, + builtin_type::BuiltinType, + expr_store::{ + ExpressionStore, HygieneId, + path::{GenericArg, GenericArgs, GenericArgsParentheses, Path, PathSegment, PathSegments}, + }, + hir::generics::{ + GenericParamDataRef, TypeOrConstParamData, TypeParamData, TypeParamProvenance, + }, + resolver::{ResolveValueResult, TypeNs, ValueNs}, + signatures::TraitFlags, + type_ref::{TypeRef, TypeRefId}, +}; +use intern::sym; +use rustc_hash::FxHashSet; +use rustc_type_ir::{ + AliasTerm, AliasTy, AliasTyKind, TypeVisitableExt, + inherent::{GenericArgs as _, IntoKind, Region as _, SliceLike, Ty as _}, +}; +use smallvec::{SmallVec, smallvec}; +use stdx::never; + +use crate::{ + GenericArgsProhibitedReason, IncorrectGenericsLenKind, PathGenericsSource, + PathLoweringDiagnostic, TyDefId, ValueTyDefId, + consteval_nextsolver::{unknown_const, unknown_const_as_generic}, + db::HirDatabase, + generics::{Generics, generics}, + lower::PathDiagnosticCallbackData, + lower_nextsolver::{LifetimeElisionKind, PredicateFilter, generic_predicates_filtered_by}, + next_solver::{ + AdtDef, Binder, Clause, Const, DbInterner, ErrorGuaranteed, Predicate, ProjectionPredicate, + Region, SolverDefId, TraitRef, Ty, + mapping::{ChalkToNextSolver, convert_binder_to_early_binder}, + }, + primitive, +}; + +use super::{ + ImplTraitLoweringMode, TyLoweringContext, associated_type_by_name_including_super_traits, + const_param_ty_query, ty_query, +}; + +type CallbackData<'a> = + Either>; + +// We cannot use `&mut dyn FnMut()` because of lifetime issues, and we don't want to use `Box` +// because of the allocation, so we create a lifetime-less callback, tailored for our needs. +pub(crate) struct PathDiagnosticCallback<'a, 'db> { + pub(crate) data: CallbackData<'a>, + pub(crate) callback: + fn(&CallbackData<'_>, &mut TyLoweringContext<'db, '_>, PathLoweringDiagnostic), +} + +pub(crate) struct PathLoweringContext<'a, 'b, 'db> { + ctx: &'a mut TyLoweringContext<'db, 'b>, + on_diagnostic: PathDiagnosticCallback<'a, 'db>, + path: &'a Path, + segments: PathSegments<'a>, + current_segment_idx: usize, + /// Contains the previous segment if `current_segment_idx == segments.len()` + current_or_prev_segment: PathSegment<'a>, +} + +impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { + #[inline] + pub(crate) fn new( + ctx: &'a mut TyLoweringContext<'db, 'b>, + on_diagnostic: PathDiagnosticCallback<'a, 'db>, + path: &'a Path, + ) -> Self { + let segments = path.segments(); + let first_segment = segments.first().unwrap_or(PathSegment::MISSING); + Self { + ctx, + on_diagnostic, + path, + segments, + current_segment_idx: 0, + current_or_prev_segment: first_segment, + } + } + + #[inline] + #[cold] + fn on_diagnostic(&mut self, diag: PathLoweringDiagnostic) { + (self.on_diagnostic.callback)(&self.on_diagnostic.data, self.ctx, diag); + } + + #[inline] + pub(crate) fn ty_ctx(&mut self) -> &mut TyLoweringContext<'db, 'b> { + self.ctx + } + + #[inline] + fn current_segment_u32(&self) -> u32 { + self.current_segment_idx as u32 + } + + #[inline] + fn skip_resolved_segment(&mut self) { + if !matches!(self.path, Path::LangItem(..)) { + // In lang items, the resolved "segment" is not one of the segments. Perhaps we should've put it + // point at -1, but I don't feel this is clearer. + self.current_segment_idx += 1; + } + self.update_current_segment(); + } + + #[inline] + fn update_current_segment(&mut self) { + self.current_or_prev_segment = + self.segments.get(self.current_segment_idx).unwrap_or(self.current_or_prev_segment); + } + + #[inline] + pub(crate) fn ignore_last_segment(&mut self) { + self.segments = self.segments.strip_last(); + } + + #[inline] + pub(crate) fn set_current_segment(&mut self, segment: usize) { + self.current_segment_idx = segment; + self.current_or_prev_segment = self + .segments + .get(segment) + .expect("invalid segment passed to PathLoweringContext::set_current_segment()"); + } + + #[inline] + fn with_lifetime_elision( + &mut self, + lifetime_elision: LifetimeElisionKind<'db>, + f: impl FnOnce(&mut PathLoweringContext<'_, '_, 'db>) -> T, + ) -> T { + let old_lifetime_elision = + std::mem::replace(&mut self.ctx.lifetime_elision, lifetime_elision); + let result = f(self); + self.ctx.lifetime_elision = old_lifetime_elision; + result + } + + pub(crate) fn lower_ty_relative_path( + &mut self, + ty: Ty<'db>, + // We need the original resolution to lower `Self::AssocTy` correctly + res: Option, + ) -> (Ty<'db>, Option) { + let remaining_segments = self.segments.len() - self.current_segment_idx; + match remaining_segments { + 0 => (ty, res), + 1 => { + // resolve unselected assoc types + (self.select_associated_type(res), None) + } + _ => { + // FIXME report error (ambiguous associated type) + (Ty::new_error(self.ctx.interner, ErrorGuaranteed), None) + } + } + } + + fn prohibit_parenthesized_generic_args(&mut self) -> bool { + if let Some(generic_args) = self.current_or_prev_segment.args_and_bindings { + match generic_args.parenthesized { + GenericArgsParentheses::No => {} + GenericArgsParentheses::ReturnTypeNotation | GenericArgsParentheses::ParenSugar => { + let segment = self.current_segment_u32(); + self.on_diagnostic( + PathLoweringDiagnostic::ParenthesizedGenericArgsWithoutFnTrait { segment }, + ); + return true; + } + } + } + false + } + + // When calling this, the current segment is the resolved segment (we don't advance it yet). + pub(crate) fn lower_partly_resolved_path( + &mut self, + resolution: TypeNs, + infer_args: bool, + ) -> (Ty<'db>, Option) { + let remaining_segments = self.segments.skip(self.current_segment_idx + 1); + tracing::debug!(?remaining_segments); + let rem_seg_len = remaining_segments.len(); + tracing::debug!(?rem_seg_len); + + let ty = match resolution { + TypeNs::TraitId(trait_) => { + let ty = match remaining_segments.len() { + 1 => { + let trait_ref = self.lower_trait_ref_from_resolved_path( + trait_, + Ty::new_error(self.ctx.interner, ErrorGuaranteed), + ); + tracing::debug!(?trait_ref); + self.skip_resolved_segment(); + let segment = self.current_or_prev_segment; + let trait_id = match trait_ref.def_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + let found = + trait_id.trait_items(self.ctx.db).associated_type_by_name(segment.name); + + tracing::debug!(?found); + match found { + Some(associated_ty) => { + // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent + // generic params. It's inefficient to splice the `Substitution`s, so we may want + // that method to optionally take parent `Substitution` as we already know them at + // this point (`trait_ref.substitution`). + let substitution = self.substs_from_path_segment( + associated_ty.into(), + false, + None, + true, + ); + let args = crate::next_solver::GenericArgs::new_from_iter( + self.ctx.interner, + trait_ref + .args + .iter() + .chain(substitution.iter().skip(trait_ref.args.len())), + ); + Ty::new_alias( + self.ctx.interner, + AliasTyKind::Projection, + AliasTy::new_from_args( + self.ctx.interner, + associated_ty.into(), + args, + ), + ) + } + None => { + // FIXME: report error (associated type not found) + Ty::new_error(self.ctx.interner, ErrorGuaranteed) + } + } + } + 0 => { + // Trait object type without dyn; this should be handled in upstream. See + // `lower_path()`. + stdx::never!("unexpected fully resolved trait path"); + Ty::new_error(self.ctx.interner, ErrorGuaranteed) + } + _ => { + // FIXME report error (ambiguous associated type) + Ty::new_error(self.ctx.interner, ErrorGuaranteed) + } + }; + return (ty, None); + } + TypeNs::TraitAliasId(_) => { + // FIXME(trait_alias): Implement trait alias. + return (Ty::new_error(self.ctx.interner, ErrorGuaranteed), None); + } + TypeNs::GenericParam(param_id) => { + let generics = self.ctx.generics(); + let idx = generics.type_or_const_param_idx(param_id.into()); + match idx { + None => { + never!("no matching generics"); + Ty::new_error(self.ctx.interner, ErrorGuaranteed) + } + Some(idx) => { + let (pidx, param) = generics.iter().nth(idx).unwrap(); + assert_eq!(pidx, param_id.into()); + let p = match param { + GenericParamDataRef::TypeParamData(p) => p, + _ => unreachable!(), + }; + Ty::new_param( + self.ctx.interner, + idx as u32, + p.name + .as_ref() + .map_or_else(|| sym::MISSING_NAME.clone(), |p| p.symbol().clone()), + ) + } + } + } + TypeNs::SelfType(impl_id) => self.ctx.db.impl_self_ty_ns(impl_id).skip_binder(), + TypeNs::AdtSelfType(adt) => { + let args = crate::next_solver::GenericArgs::identity_for_item( + self.ctx.interner, + adt.into(), + ); + Ty::new_adt(self.ctx.interner, AdtDef::new(adt, self.ctx.interner), args) + } + + TypeNs::AdtId(it) => self.lower_path_inner(it.into(), infer_args), + TypeNs::BuiltinType(it) => self.lower_path_inner(it.into(), infer_args), + TypeNs::TypeAliasId(it) => self.lower_path_inner(it.into(), infer_args), + // FIXME: report error + TypeNs::EnumVariantId(_) | TypeNs::ModuleId(_) => { + return (Ty::new_error(self.ctx.interner, ErrorGuaranteed), None); + } + }; + + tracing::debug!(?ty); + + self.skip_resolved_segment(); + self.lower_ty_relative_path(ty, Some(resolution)) + } + + fn handle_type_ns_resolution(&mut self, resolution: &TypeNs) { + let mut prohibit_generics_on_resolved = |reason| { + if self.current_or_prev_segment.args_and_bindings.is_some() { + let segment = self.current_segment_u32(); + self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { + segment, + reason, + }); + } + }; + + match resolution { + TypeNs::SelfType(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy) + } + TypeNs::GenericParam(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::TyParam) + } + TypeNs::AdtSelfType(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy) + } + TypeNs::BuiltinType(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::PrimitiveTy) + } + TypeNs::ModuleId(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::Module) + } + TypeNs::AdtId(_) + | TypeNs::EnumVariantId(_) + | TypeNs::TypeAliasId(_) + | TypeNs::TraitId(_) + | TypeNs::TraitAliasId(_) => {} + } + } + + pub(crate) fn resolve_path_in_type_ns_fully(&mut self) -> Option { + let (res, unresolved) = self.resolve_path_in_type_ns()?; + if unresolved.is_some() { + return None; + } + Some(res) + } + + #[tracing::instrument(skip(self), ret)] + pub(crate) fn resolve_path_in_type_ns(&mut self) -> Option<(TypeNs, Option)> { + let (resolution, remaining_index, _, prefix_info) = + self.ctx.resolver.resolve_path_in_type_ns_with_prefix_info(self.ctx.db, self.path)?; + + let segments = self.segments; + if segments.is_empty() || matches!(self.path, Path::LangItem(..)) { + // `segments.is_empty()` can occur with `self`. + return Some((resolution, remaining_index)); + } + + let (module_segments, resolved_segment_idx, enum_segment) = match remaining_index { + None if prefix_info.enum_variant => { + (segments.strip_last_two(), segments.len() - 1, Some(segments.len() - 2)) + } + None => (segments.strip_last(), segments.len() - 1, None), + Some(i) => (segments.take(i - 1), i - 1, None), + }; + + self.current_segment_idx = resolved_segment_idx; + self.current_or_prev_segment = + segments.get(resolved_segment_idx).expect("should have resolved segment"); + + if matches!(self.path, Path::BarePath(..)) { + // Bare paths cannot have generics, so skip them as an optimization. + return Some((resolution, remaining_index)); + } + + for (i, mod_segment) in module_segments.iter().enumerate() { + if mod_segment.args_and_bindings.is_some() { + self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { + segment: i as u32, + reason: GenericArgsProhibitedReason::Module, + }); + } + } + + if let Some(enum_segment) = enum_segment + && segments.get(enum_segment).is_some_and(|it| it.args_and_bindings.is_some()) + && segments.get(enum_segment + 1).is_some_and(|it| it.args_and_bindings.is_some()) + { + self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { + segment: (enum_segment + 1) as u32, + reason: GenericArgsProhibitedReason::EnumVariant, + }); + } + + self.handle_type_ns_resolution(&resolution); + + Some((resolution, remaining_index)) + } + + pub(crate) fn resolve_path_in_value_ns( + &mut self, + hygiene_id: HygieneId, + ) -> Option { + let (res, prefix_info) = self.ctx.resolver.resolve_path_in_value_ns_with_prefix_info( + self.ctx.db, + self.path, + hygiene_id, + )?; + + let segments = self.segments; + if segments.is_empty() || matches!(self.path, Path::LangItem(..)) { + // `segments.is_empty()` can occur with `self`. + return Some(res); + } + + let (mod_segments, enum_segment, resolved_segment_idx) = match res { + ResolveValueResult::Partial(_, unresolved_segment, _) => { + (segments.take(unresolved_segment - 1), None, unresolved_segment - 1) + } + ResolveValueResult::ValueNs(ValueNs::EnumVariantId(_), _) + if prefix_info.enum_variant => + { + (segments.strip_last_two(), segments.len().checked_sub(2), segments.len() - 1) + } + ResolveValueResult::ValueNs(..) => (segments.strip_last(), None, segments.len() - 1), + }; + + self.current_segment_idx = resolved_segment_idx; + self.current_or_prev_segment = + segments.get(resolved_segment_idx).expect("should have resolved segment"); + + for (i, mod_segment) in mod_segments.iter().enumerate() { + if mod_segment.args_and_bindings.is_some() { + self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { + segment: i as u32, + reason: GenericArgsProhibitedReason::Module, + }); + } + } + + if let Some(enum_segment) = enum_segment + && segments.get(enum_segment).is_some_and(|it| it.args_and_bindings.is_some()) + && segments.get(enum_segment + 1).is_some_and(|it| it.args_and_bindings.is_some()) + { + self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { + segment: (enum_segment + 1) as u32, + reason: GenericArgsProhibitedReason::EnumVariant, + }); + } + + match &res { + ResolveValueResult::ValueNs(resolution, _) => { + let resolved_segment_idx = self.current_segment_u32(); + let resolved_segment = self.current_or_prev_segment; + + let mut prohibit_generics_on_resolved = |reason| { + if resolved_segment.args_and_bindings.is_some() { + self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { + segment: resolved_segment_idx, + reason, + }); + } + }; + + match resolution { + ValueNs::ImplSelf(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy) + } + // FIXME: rustc generates E0107 (incorrect number of generic arguments) and not + // E0109 (generic arguments provided for a type that doesn't accept them) for + // consts and statics, presumably as a defense against future in which consts + // and statics can be generic, or just because it was easier for rustc implementors. + // That means we'll show the wrong error code. Because of us it's easier to do it + // this way :) + ValueNs::GenericParam(_) | ValueNs::ConstId(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::Const) + } + ValueNs::StaticId(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::Static) + } + ValueNs::FunctionId(_) | ValueNs::StructId(_) | ValueNs::EnumVariantId(_) => {} + ValueNs::LocalBinding(_) => {} + } + } + ResolveValueResult::Partial(resolution, _, _) => { + self.handle_type_ns_resolution(resolution); + } + }; + Some(res) + } + + #[tracing::instrument(skip(self), ret)] + fn select_associated_type(&mut self, res: Option) -> Ty<'db> { + let interner = self.ctx.interner; + let Some(res) = res else { + return Ty::new_error(self.ctx.interner, ErrorGuaranteed); + }; + let segment = self.current_or_prev_segment; + let assoc_name = segment.name; + let db = self.ctx.db; + let def = self.ctx.def; + let mut search = |t: TraitRef<'db>| { + let trait_id = match t.def_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + let mut checked_traits = FxHashSet::default(); + let mut check_trait = |trait_id: TraitId| { + let name = &db.trait_signature(trait_id).name; + tracing::debug!(?trait_id, ?name); + if !checked_traits.insert(trait_id) { + return None; + } + let data = trait_id.trait_items(db); + + tracing::debug!(?data.items); + for (name, assoc_id) in &data.items { + if let &AssocItemId::TypeAliasId(alias) = assoc_id { + if name != assoc_name { + continue; + } + + // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent + // generic params. It's inefficient to splice the `Substitution`s, so we may want + // that method to optionally take parent `Substitution` as we already know them at + // this point (`t.substitution`). + let substs = self.substs_from_path_segment(alias.into(), false, None, true); + + let substs = crate::next_solver::GenericArgs::new_from_iter( + interner, + t.args.iter().chain(substs.iter().skip(t.args.len())), + ); + + return Some(Ty::new_alias( + interner, + AliasTyKind::Projection, + AliasTy::new(interner, alias.into(), substs), + )); + } + } + None + }; + let mut stack: SmallVec<[_; 4]> = smallvec![trait_id]; + while let Some(trait_def_id) = stack.pop() { + if let Some(alias) = check_trait(trait_def_id) { + return alias; + } + for pred in generic_predicates_filtered_by( + db, + GenericDefId::TraitId(trait_def_id), + PredicateFilter::SelfTrait, + |pred| pred == GenericDefId::TraitId(trait_def_id), + ) + .0 + .deref() + { + tracing::debug!(?pred); + let trait_id = match pred.kind().skip_binder() { + rustc_type_ir::ClauseKind::Trait(pred) => pred.def_id(), + _ => continue, + }; + let trait_id = match trait_id { + SolverDefId::TraitId(trait_id) => trait_id, + _ => continue, + }; + stack.push(trait_id); + } + tracing::debug!(?stack); + } + + Ty::new_error(interner, ErrorGuaranteed) + }; + + match res { + TypeNs::SelfType(impl_id) => { + let trait_ref = db.impl_trait_ns(impl_id); + let Some(trait_ref) = trait_ref else { + return Ty::new_error(interner, ErrorGuaranteed); + }; + + // we're _in_ the impl -- the binders get added back later. Correct, + // but it would be nice to make this more explicit + search(trait_ref.skip_binder()) + } + TypeNs::GenericParam(param_id) => { + // Handle `Self::Type` referring to own associated type in trait definitions + // This *must* be done first to avoid cycles with + // `generic_predicates_for_param`, but not sure that it's sufficient, + // see FIXME in `search`. + if let GenericDefId::TraitId(trait_id) = param_id.parent() { + let trait_name = &db.trait_signature(trait_id).name; + tracing::debug!(?trait_name); + let trait_generics = generics(db, trait_id.into()); + tracing::debug!(?trait_generics); + if trait_generics[param_id.local_id()].is_trait_self() { + let args = crate::next_solver::GenericArgs::identity_for_item( + interner, + trait_id.into(), + ); + let trait_ref = TraitRef::new_from_args(interner, trait_id.into(), args); + tracing::debug!(?args, ?trait_ref); + return search(trait_ref); + } + } + + let predicates = db.generic_predicates_for_param_ns( + def, + param_id.into(), + Some(segment.name.clone()), + ); + predicates + .iter() + .find_map(|pred| match (*pred).kind().skip_binder() { + rustc_type_ir::ClauseKind::Trait(trait_predicate) => Some(trait_predicate), + _ => None, + }) + .map(|trait_predicate| { + let trait_ref = trait_predicate.trait_ref; + assert!( + !trait_ref.has_escaping_bound_vars(), + "FIXME unexpected higher-ranked trait bound" + ); + search(trait_ref) + }) + .unwrap_or_else(|| Ty::new_error(interner, ErrorGuaranteed)) + } + _ => Ty::new_error(interner, ErrorGuaranteed), + } + } + + fn lower_path_inner(&mut self, typeable: TyDefId, infer_args: bool) -> Ty<'db> { + let generic_def = match typeable { + TyDefId::BuiltinType(builtinty) => return builtin(self.ctx.interner, builtinty), + TyDefId::AdtId(it) => it.into(), + TyDefId::TypeAliasId(it) => it.into(), + }; + let args = self.substs_from_path_segment(generic_def, infer_args, None, false); + let ty = ty_query(self.ctx.db, typeable); + ty.instantiate(self.ctx.interner, args) + } + + /// Collect generic arguments from a path into a `Substs`. See also + /// `create_substs_for_ast_path` and `def_to_ty` in rustc. + pub(crate) fn substs_from_path( + &mut self, + // Note that we don't call `db.value_type(resolved)` here, + // `ValueTyDefId` is just a convenient way to pass generics and + // special-case enum variants + resolved: ValueTyDefId, + infer_args: bool, + lowering_assoc_type_generics: bool, + ) -> crate::next_solver::GenericArgs<'db> { + let interner = self.ctx.interner; + let prev_current_segment_idx = self.current_segment_idx; + let prev_current_segment = self.current_or_prev_segment; + + let generic_def = match resolved { + ValueTyDefId::FunctionId(it) => it.into(), + ValueTyDefId::StructId(it) => it.into(), + ValueTyDefId::UnionId(it) => it.into(), + ValueTyDefId::ConstId(it) => it.into(), + ValueTyDefId::StaticId(_) => { + return crate::next_solver::GenericArgs::new_from_iter(interner, []); + } + ValueTyDefId::EnumVariantId(var) => { + // the generic args for an enum variant may be either specified + // on the segment referring to the enum, or on the segment + // referring to the variant. So `Option::::None` and + // `Option::None::` are both allowed (though the former is + // FIXME: This isn't strictly correct, enum variants may be used not through the enum + // (via `use Enum::Variant`). The resolver returns whether they were, but we don't have its result + // available here. The worst that can happen is that we will show some confusing diagnostics to the user, + // if generics exist on the module and they don't match with the variant. + // preferred). See also `def_ids_for_path_segments` in rustc. + // + // `wrapping_sub(1)` will return a number which `get` will return None for if current_segment_idx<2. + // This simplifies the code a bit. + let penultimate_idx = self.current_segment_idx.wrapping_sub(1); + let penultimate = self.segments.get(penultimate_idx); + if let Some(penultimate) = penultimate + && self.current_or_prev_segment.args_and_bindings.is_none() + && penultimate.args_and_bindings.is_some() + { + self.current_segment_idx = penultimate_idx; + self.current_or_prev_segment = penultimate; + } + var.lookup(self.ctx.db).parent.into() + } + }; + let result = self.substs_from_path_segment( + generic_def, + infer_args, + None, + lowering_assoc_type_generics, + ); + self.current_segment_idx = prev_current_segment_idx; + self.current_or_prev_segment = prev_current_segment; + result + } + + pub(crate) fn substs_from_path_segment( + &mut self, + def: GenericDefId, + infer_args: bool, + explicit_self_ty: Option>, + lowering_assoc_type_generics: bool, + ) -> crate::next_solver::GenericArgs<'db> { + let mut lifetime_elision = self.ctx.lifetime_elision.clone(); + + if let Some(args) = self.current_or_prev_segment.args_and_bindings + && args.parenthesized != GenericArgsParentheses::No + { + let prohibit_parens = match def { + GenericDefId::TraitId(trait_) => { + // RTN is prohibited anyways if we got here. + let is_rtn = args.parenthesized == GenericArgsParentheses::ReturnTypeNotation; + let is_fn_trait = self + .ctx + .db + .trait_signature(trait_) + .flags + .contains(TraitFlags::RUSTC_PAREN_SUGAR); + is_rtn || !is_fn_trait + } + _ => true, + }; + + if prohibit_parens { + let segment = self.current_segment_u32(); + self.on_diagnostic( + PathLoweringDiagnostic::ParenthesizedGenericArgsWithoutFnTrait { segment }, + ); + + return unknown_subst(self.ctx.interner, def); + } + + // `Fn()`-style generics are treated like functions for the purpose of lifetime elision. + lifetime_elision = + LifetimeElisionKind::AnonymousCreateParameter { report_in_path: false }; + } + + self.substs_from_args_and_bindings( + self.current_or_prev_segment.args_and_bindings, + def, + infer_args, + explicit_self_ty, + PathGenericsSource::Segment(self.current_segment_u32()), + lowering_assoc_type_generics, + lifetime_elision, + ) + } + + pub(super) fn substs_from_args_and_bindings( + &mut self, + args_and_bindings: Option<&GenericArgs>, + def: GenericDefId, + infer_args: bool, + explicit_self_ty: Option>, + generics_source: PathGenericsSource, + lowering_assoc_type_generics: bool, + lifetime_elision: LifetimeElisionKind<'db>, + ) -> crate::next_solver::GenericArgs<'db> { + struct LowererCtx<'a, 'b, 'c, 'db> { + ctx: &'a mut PathLoweringContext<'b, 'c, 'db>, + generics_source: PathGenericsSource, + } + + impl<'db> GenericArgsLowerer<'db> for LowererCtx<'_, '_, '_, 'db> { + fn report_len_mismatch( + &mut self, + def: GenericDefId, + provided_count: u32, + expected_count: u32, + kind: IncorrectGenericsLenKind, + ) { + self.ctx.on_diagnostic(PathLoweringDiagnostic::IncorrectGenericsLen { + generics_source: self.generics_source, + provided_count, + expected_count, + kind, + def, + }); + } + + fn report_arg_mismatch( + &mut self, + param_id: GenericParamId, + arg_idx: u32, + has_self_arg: bool, + ) { + self.ctx.on_diagnostic(PathLoweringDiagnostic::IncorrectGenericsOrder { + generics_source: self.generics_source, + param_id, + arg_idx, + has_self_arg, + }); + } + + fn provided_kind( + &mut self, + param_id: GenericParamId, + param: GenericParamDataRef<'_>, + arg: &GenericArg, + ) -> crate::next_solver::GenericArg<'db> { + match (param, arg) { + (GenericParamDataRef::LifetimeParamData(_), GenericArg::Lifetime(lifetime)) => { + self.ctx.ctx.lower_lifetime(*lifetime).into() + } + (GenericParamDataRef::TypeParamData(_), GenericArg::Type(type_ref)) => { + self.ctx.ctx.lower_ty(*type_ref).into() + } + (GenericParamDataRef::ConstParamData(_), GenericArg::Const(konst)) => { + let GenericParamId::ConstParamId(const_id) = param_id else { + unreachable!("non-const param ID for const param"); + }; + self.ctx + .ctx + .lower_const(konst, const_param_ty_query(self.ctx.ctx.db, const_id)) + .into() + } + _ => unreachable!("unmatching param kinds were passed to `provided_kind()`"), + } + } + + fn provided_type_like_const( + &mut self, + const_ty: Ty<'db>, + arg: TypeLikeConst<'_>, + ) -> crate::next_solver::Const<'db> { + match arg { + TypeLikeConst::Path(path) => self.ctx.ctx.lower_path_as_const(path, const_ty), + TypeLikeConst::Infer => unknown_const(const_ty), + } + } + + fn inferred_kind( + &mut self, + def: GenericDefId, + param_id: GenericParamId, + param: GenericParamDataRef<'_>, + infer_args: bool, + preceding_args: &[crate::next_solver::GenericArg<'db>], + ) -> crate::next_solver::GenericArg<'db> { + let default = || { + self.ctx.ctx.db.generic_defaults(def).get(preceding_args.len()).map(|default| { + convert_binder_to_early_binder( + self.ctx.ctx.interner, + default.to_nextsolver(self.ctx.ctx.interner), + ) + .instantiate(self.ctx.ctx.interner, preceding_args) + }) + }; + match param { + GenericParamDataRef::LifetimeParamData(_) => { + Region::new(self.ctx.ctx.interner, rustc_type_ir::ReError(ErrorGuaranteed)) + .into() + } + GenericParamDataRef::TypeParamData(param) => { + if !infer_args + && param.default.is_some() + && let Some(default) = default() + { + return default; + } + Ty::new_error(self.ctx.ctx.interner, ErrorGuaranteed).into() + } + GenericParamDataRef::ConstParamData(param) => { + if !infer_args + && param.default.is_some() + && let Some(default) = default() + { + return default; + } + let GenericParamId::ConstParamId(const_id) = param_id else { + unreachable!("non-const param ID for const param"); + }; + unknown_const_as_generic(const_param_ty_query(self.ctx.ctx.db, const_id)) + } + } + } + + fn parent_arg( + &mut self, + param_id: GenericParamId, + ) -> crate::next_solver::GenericArg<'db> { + match param_id { + GenericParamId::TypeParamId(_) => { + Ty::new_error(self.ctx.ctx.interner, ErrorGuaranteed).into() + } + GenericParamId::ConstParamId(const_id) => { + unknown_const_as_generic(const_param_ty_query(self.ctx.ctx.db, const_id)) + } + GenericParamId::LifetimeParamId(_) => { + Region::new(self.ctx.ctx.interner, rustc_type_ir::ReError(ErrorGuaranteed)) + .into() + } + } + } + + fn report_elided_lifetimes_in_path( + &mut self, + def: GenericDefId, + expected_count: u32, + hard_error: bool, + ) { + self.ctx.on_diagnostic(PathLoweringDiagnostic::ElidedLifetimesInPath { + generics_source: self.generics_source, + def, + expected_count, + hard_error, + }); + } + + fn report_elision_failure(&mut self, def: GenericDefId, expected_count: u32) { + self.ctx.on_diagnostic(PathLoweringDiagnostic::ElisionFailure { + generics_source: self.generics_source, + def, + expected_count, + }); + } + + fn report_missing_lifetime(&mut self, def: GenericDefId, expected_count: u32) { + self.ctx.on_diagnostic(PathLoweringDiagnostic::MissingLifetime { + generics_source: self.generics_source, + def, + expected_count, + }); + } + } + + substs_from_args_and_bindings( + self.ctx.db, + self.ctx.store, + args_and_bindings, + def, + infer_args, + lifetime_elision, + lowering_assoc_type_generics, + explicit_self_ty, + &mut LowererCtx { ctx: self, generics_source }, + ) + } + + pub(crate) fn lower_trait_ref_from_resolved_path( + &mut self, + resolved: TraitId, + explicit_self_ty: Ty<'db>, + ) -> TraitRef<'db> { + let args = self.trait_ref_substs_from_path(resolved, explicit_self_ty); + TraitRef::new_from_args(self.ctx.interner, resolved.into(), args) + } + + fn trait_ref_substs_from_path( + &mut self, + resolved: TraitId, + explicit_self_ty: Ty<'db>, + ) -> crate::next_solver::GenericArgs<'db> { + self.substs_from_path_segment(resolved.into(), false, Some(explicit_self_ty), false) + } + + pub(super) fn assoc_type_bindings_from_type_bound<'c>( + mut self, + trait_ref: TraitRef<'db>, + ) -> Option> + use<'a, 'b, 'c, 'db>> { + let interner = self.ctx.interner; + self.current_or_prev_segment.args_and_bindings.map(|args_and_bindings| { + args_and_bindings.bindings.iter().enumerate().flat_map(move |(binding_idx, binding)| { + let found = associated_type_by_name_including_super_traits( + self.ctx.db, + trait_ref, + &binding.name, + ); + let (super_trait_ref, associated_ty) = match found { + None => return SmallVec::new(), + Some(t) => t, + }; + let args = + self.with_lifetime_elision(LifetimeElisionKind::AnonymousReportError, |this| { + // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent + // generic params. It's inefficient to splice the `Substitution`s, so we may want + // that method to optionally take parent `Substitution` as we already know them at + // this point (`super_trait_ref.substitution`). + this.substs_from_args_and_bindings( + binding.args.as_ref(), + associated_ty.into(), + false, // this is not relevant + Some(super_trait_ref.self_ty()), + PathGenericsSource::AssocType { + segment: this.current_segment_u32(), + assoc_type: binding_idx as u32, + }, + false, + this.ctx.lifetime_elision.clone(), + ) + }); + let args = crate::next_solver::GenericArgs::new_from_iter( + interner, + super_trait_ref.args.iter().chain(args.iter().skip(super_trait_ref.args.len())), + ); + let projection_term = + AliasTerm::new_from_args(interner, associated_ty.into(), args); + let mut predicates: SmallVec<[_; 1]> = SmallVec::with_capacity( + binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(), + ); + if let Some(type_ref) = binding.type_ref { + match (&self.ctx.store[type_ref], self.ctx.impl_trait_mode.mode) { + (TypeRef::ImplTrait(_), ImplTraitLoweringMode::Disallowed) => (), + (_, ImplTraitLoweringMode::Disallowed | ImplTraitLoweringMode::Opaque) => { + let ty = self.ctx.lower_ty(type_ref); + let pred = Clause(Predicate::new( + interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Projection(ProjectionPredicate { + projection_term, + term: ty.into(), + }), + )), + )); + predicates.push(pred); + } + } + } + for bound in binding.bounds.iter() { + predicates.extend(self.ctx.lower_type_bound( + bound, + Ty::new_alias( + self.ctx.interner, + AliasTyKind::Projection, + AliasTy::new_from_args(self.ctx.interner, associated_ty.into(), args), + ), + false, + )); + } + predicates + }) + }) + } +} + +/// A const that were parsed like a type. +pub(crate) enum TypeLikeConst<'a> { + Infer, + Path(&'a Path), +} + +pub(crate) trait GenericArgsLowerer<'db> { + fn report_elided_lifetimes_in_path( + &mut self, + def: GenericDefId, + expected_count: u32, + hard_error: bool, + ); + + fn report_elision_failure(&mut self, def: GenericDefId, expected_count: u32); + + fn report_missing_lifetime(&mut self, def: GenericDefId, expected_count: u32); + + fn report_len_mismatch( + &mut self, + def: GenericDefId, + provided_count: u32, + expected_count: u32, + kind: IncorrectGenericsLenKind, + ); + + fn report_arg_mismatch(&mut self, param_id: GenericParamId, arg_idx: u32, has_self_arg: bool); + + fn provided_kind( + &mut self, + param_id: GenericParamId, + param: GenericParamDataRef<'_>, + arg: &GenericArg, + ) -> crate::next_solver::GenericArg<'db>; + + fn provided_type_like_const(&mut self, const_ty: Ty<'db>, arg: TypeLikeConst<'_>) + -> Const<'db>; + + fn inferred_kind( + &mut self, + def: GenericDefId, + param_id: GenericParamId, + param: GenericParamDataRef<'_>, + infer_args: bool, + preceding_args: &[crate::next_solver::GenericArg<'db>], + ) -> crate::next_solver::GenericArg<'db>; + + fn parent_arg(&mut self, param_id: GenericParamId) -> crate::next_solver::GenericArg<'db>; +} + +/// Returns true if there was an error. +fn check_generic_args_len<'db>( + args_and_bindings: Option<&GenericArgs>, + def: GenericDefId, + def_generics: &Generics, + infer_args: bool, + lifetime_elision: &LifetimeElisionKind<'db>, + lowering_assoc_type_generics: bool, + ctx: &mut impl GenericArgsLowerer<'db>, +) -> bool { + let mut had_error = false; + + let (mut provided_lifetimes_count, mut provided_types_and_consts_count) = (0usize, 0usize); + if let Some(args_and_bindings) = args_and_bindings { + let args_no_self = &args_and_bindings.args[usize::from(args_and_bindings.has_self_type)..]; + for arg in args_no_self { + match arg { + GenericArg::Lifetime(_) => provided_lifetimes_count += 1, + GenericArg::Type(_) | GenericArg::Const(_) => provided_types_and_consts_count += 1, + } + } + } + + let lifetime_args_len = def_generics.len_lifetimes_self(); + if provided_lifetimes_count == 0 && lifetime_args_len > 0 && !lowering_assoc_type_generics { + // In generic associated types, we never allow inferring the lifetimes. + match lifetime_elision { + &LifetimeElisionKind::AnonymousCreateParameter { report_in_path } => { + ctx.report_elided_lifetimes_in_path(def, lifetime_args_len as u32, report_in_path); + had_error |= report_in_path; + } + LifetimeElisionKind::AnonymousReportError => { + ctx.report_missing_lifetime(def, lifetime_args_len as u32); + had_error = true + } + LifetimeElisionKind::ElisionFailure => { + ctx.report_elision_failure(def, lifetime_args_len as u32); + had_error = true; + } + LifetimeElisionKind::StaticIfNoLifetimeInScope { only_lint: _ } => { + // FIXME: Check there are other lifetimes in scope, and error/lint. + } + LifetimeElisionKind::Elided(_) => { + ctx.report_elided_lifetimes_in_path(def, lifetime_args_len as u32, false); + } + LifetimeElisionKind::Infer => { + // Allow eliding lifetimes. + } + } + } else if lifetime_args_len != provided_lifetimes_count { + ctx.report_len_mismatch( + def, + provided_lifetimes_count as u32, + lifetime_args_len as u32, + IncorrectGenericsLenKind::Lifetimes, + ); + had_error = true; + } + + let defaults_count = + def_generics.iter_self_type_or_consts().filter(|(_, param)| param.has_default()).count(); + let named_type_and_const_params_count = def_generics + .iter_self_type_or_consts() + .filter(|(_, param)| match param { + TypeOrConstParamData::TypeParamData(param) => { + param.provenance == TypeParamProvenance::TypeParamList + } + TypeOrConstParamData::ConstParamData(_) => true, + }) + .count(); + let expected_max = named_type_and_const_params_count; + let expected_min = + if infer_args { 0 } else { named_type_and_const_params_count - defaults_count }; + if provided_types_and_consts_count < expected_min + || expected_max < provided_types_and_consts_count + { + ctx.report_len_mismatch( + def, + provided_types_and_consts_count as u32, + named_type_and_const_params_count as u32, + IncorrectGenericsLenKind::TypesAndConsts, + ); + had_error = true; + } + + had_error +} + +pub(crate) fn substs_from_args_and_bindings<'db>( + db: &'db dyn HirDatabase, + store: &ExpressionStore, + args_and_bindings: Option<&GenericArgs>, + def: GenericDefId, + mut infer_args: bool, + lifetime_elision: LifetimeElisionKind<'db>, + lowering_assoc_type_generics: bool, + explicit_self_ty: Option>, + ctx: &mut impl GenericArgsLowerer<'db>, +) -> crate::next_solver::GenericArgs<'db> { + let interner = DbInterner::new_with(db, None, None); + + tracing::debug!(?args_and_bindings); + + // Order is + // - Parent parameters + // - Optional Self parameter + // - Lifetime parameters + // - Type or Const parameters + let def_generics = generics(db, def); + let args_slice = args_and_bindings.map(|it| &*it.args).unwrap_or_default(); + + // We do not allow inference if there are specified args, i.e. we do not allow partial inference. + let has_non_lifetime_args = + args_slice.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_))); + infer_args &= !has_non_lifetime_args; + + let had_count_error = check_generic_args_len( + args_and_bindings, + def, + &def_generics, + infer_args, + &lifetime_elision, + lowering_assoc_type_generics, + ctx, + ); + + let mut substs = Vec::with_capacity(def_generics.len()); + + substs.extend(def_generics.iter_parent_id().map(|id| ctx.parent_arg(id))); + + let mut args = args_slice.iter().enumerate().peekable(); + let mut params = def_generics.iter_self().peekable(); + + // If we encounter a type or const when we expect a lifetime, we infer the lifetimes. + // If we later encounter a lifetime, we know that the arguments were provided in the + // wrong order. `force_infer_lt` records the type or const that forced lifetimes to be + // inferred, so we can use it for diagnostics later. + let mut force_infer_lt = None; + + let has_self_arg = args_and_bindings.is_some_and(|it| it.has_self_type); + // First, handle `Self` parameter. Consume it from the args if provided, otherwise from `explicit_self_ty`, + // and lastly infer it. + if let Some(&( + self_param_id, + self_param @ GenericParamDataRef::TypeParamData(TypeParamData { + provenance: TypeParamProvenance::TraitSelf, + .. + }), + )) = params.peek() + { + let self_ty = if has_self_arg { + let (_, self_ty) = args.next().expect("has_self_type=true, should have Self type"); + ctx.provided_kind(self_param_id, self_param, self_ty) + } else { + explicit_self_ty.map(|it| it.into()).unwrap_or_else(|| { + ctx.inferred_kind(def, self_param_id, self_param, infer_args, &substs) + }) + }; + params.next(); + substs.push(self_ty); + } + + loop { + // We're going to iterate through the generic arguments that the user + // provided, matching them with the generic parameters we expect. + // Mismatches can occur as a result of elided lifetimes, or for malformed + // input. We try to handle both sensibly. + match (args.peek(), params.peek()) { + (Some(&(arg_idx, arg)), Some(&(param_id, param))) => match (arg, param) { + (GenericArg::Type(_), GenericParamDataRef::TypeParamData(type_param)) + if type_param.provenance == TypeParamProvenance::ArgumentImplTrait => + { + // Do not allow specifying `impl Trait` explicitly. We already err at that, but if we won't handle it here + // we will handle it as if it was specified, instead of inferring it. + substs.push(ctx.inferred_kind(def, param_id, param, infer_args, &substs)); + params.next(); + } + (GenericArg::Lifetime(_), GenericParamDataRef::LifetimeParamData(_)) + | (GenericArg::Type(_), GenericParamDataRef::TypeParamData(_)) + | (GenericArg::Const(_), GenericParamDataRef::ConstParamData(_)) => { + substs.push(ctx.provided_kind(param_id, param, arg)); + args.next(); + params.next(); + } + ( + GenericArg::Type(_) | GenericArg::Const(_), + GenericParamDataRef::LifetimeParamData(_), + ) => { + // We expected a lifetime argument, but got a type or const + // argument. That means we're inferring the lifetime. + substs.push(ctx.inferred_kind(def, param_id, param, infer_args, &substs)); + params.next(); + force_infer_lt = Some((arg_idx as u32, param_id)); + } + (GenericArg::Type(type_ref), GenericParamDataRef::ConstParamData(_)) => { + if let Some(konst) = type_looks_like_const(store, *type_ref) { + let GenericParamId::ConstParamId(param_id) = param_id else { + panic!("unmatching param kinds"); + }; + let const_ty = const_param_ty_query(db, param_id); + substs.push(ctx.provided_type_like_const(const_ty, konst).into()); + args.next(); + params.next(); + } else { + // See the `_ => { ... }` branch. + if !had_count_error { + ctx.report_arg_mismatch(param_id, arg_idx as u32, has_self_arg); + } + while args.next().is_some() {} + } + } + _ => { + // We expected one kind of parameter, but the user provided + // another. This is an error. However, if we already know that + // the arguments don't match up with the parameters, we won't issue + // an additional error, as the user already knows what's wrong. + if !had_count_error { + ctx.report_arg_mismatch(param_id, arg_idx as u32, has_self_arg); + } + + // We've reported the error, but we want to make sure that this + // problem doesn't bubble down and create additional, irrelevant + // errors. In this case, we're simply going to ignore the argument + // and any following arguments. The rest of the parameters will be + // inferred. + while args.next().is_some() {} + } + }, + + (Some(&(_, arg)), None) => { + // We should never be able to reach this point with well-formed input. + // There are two situations in which we can encounter this issue. + // + // 1. The number of arguments is incorrect. In this case, an error + // will already have been emitted, and we can ignore it. + // 2. We've inferred some lifetimes, which have been provided later (i.e. + // after a type or const). We want to throw an error in this case. + if !had_count_error { + assert!( + matches!(arg, GenericArg::Lifetime(_)), + "the only possible situation here is incorrect lifetime order" + ); + let (provided_arg_idx, param_id) = + force_infer_lt.expect("lifetimes ought to have been inferred"); + ctx.report_arg_mismatch(param_id, provided_arg_idx, has_self_arg); + } + + break; + } + + (None, Some(&(param_id, param))) => { + // If there are fewer arguments than parameters, it means we're inferring the remaining arguments. + let param = if let GenericParamId::LifetimeParamId(_) = param_id { + match &lifetime_elision { + LifetimeElisionKind::ElisionFailure + | LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true } + | LifetimeElisionKind::AnonymousReportError => { + assert!(had_count_error); + ctx.inferred_kind(def, param_id, param, infer_args, &substs) + } + LifetimeElisionKind::StaticIfNoLifetimeInScope { only_lint: _ } => { + Region::new_static(interner).into() + } + LifetimeElisionKind::Elided(lifetime) => (*lifetime).into(), + LifetimeElisionKind::AnonymousCreateParameter { report_in_path: false } + | LifetimeElisionKind::Infer => { + // FIXME: With `AnonymousCreateParameter`, we need to create a new lifetime parameter here + // (but this will probably be done in hir-def lowering instead). + ctx.inferred_kind(def, param_id, param, infer_args, &substs) + } + } + } else { + ctx.inferred_kind(def, param_id, param, infer_args, &substs) + }; + substs.push(param); + params.next(); + } + + (None, None) => break, + } + } + + crate::next_solver::GenericArgs::new_from_iter(interner, substs) +} + +fn type_looks_like_const( + store: &ExpressionStore, + type_ref: TypeRefId, +) -> Option> { + // A path/`_` const will be parsed as a type, instead of a const, because when parsing/lowering + // in hir-def we don't yet know the expected argument kind. rustc does this a bit differently, + // when lowering to HIR it resolves the path, and if it doesn't resolve to the type namespace + // it is lowered as a const. Our behavior could deviate from rustc when the value is resolvable + // in both the type and value namespaces, but I believe we only allow more code. + let type_ref = &store[type_ref]; + match type_ref { + TypeRef::Path(path) => Some(TypeLikeConst::Path(path)), + TypeRef::Placeholder => Some(TypeLikeConst::Infer), + _ => None, + } +} + +fn unknown_subst<'db>( + interner: DbInterner<'db>, + def: impl Into, +) -> crate::next_solver::GenericArgs<'db> { + let params = generics(interner.db(), def.into()); + crate::next_solver::GenericArgs::new_from_iter( + interner, + params.iter_id().map(|id| match id { + GenericParamId::TypeParamId(_) => Ty::new_error(interner, ErrorGuaranteed).into(), + GenericParamId::ConstParamId(id) => { + unknown_const_as_generic(const_param_ty_query(interner.db(), id)) + } + GenericParamId::LifetimeParamId(_) => { + crate::next_solver::Region::error(interner).into() + } + }), + ) +} + +pub(crate) fn builtin<'db>(interner: DbInterner<'db>, builtin: BuiltinType) -> Ty<'db> { + match builtin { + BuiltinType::Char => Ty::new(interner, rustc_type_ir::TyKind::Char), + BuiltinType::Bool => Ty::new_bool(interner), + BuiltinType::Str => Ty::new(interner, rustc_type_ir::TyKind::Str), + BuiltinType::Int(t) => { + let int_ty = match primitive::int_ty_from_builtin(t) { + chalk_ir::IntTy::Isize => rustc_type_ir::IntTy::Isize, + chalk_ir::IntTy::I8 => rustc_type_ir::IntTy::I8, + chalk_ir::IntTy::I16 => rustc_type_ir::IntTy::I16, + chalk_ir::IntTy::I32 => rustc_type_ir::IntTy::I32, + chalk_ir::IntTy::I64 => rustc_type_ir::IntTy::I64, + chalk_ir::IntTy::I128 => rustc_type_ir::IntTy::I128, + }; + Ty::new_int(interner, int_ty) + } + BuiltinType::Uint(t) => { + let uint_ty = match primitive::uint_ty_from_builtin(t) { + chalk_ir::UintTy::Usize => rustc_type_ir::UintTy::Usize, + chalk_ir::UintTy::U8 => rustc_type_ir::UintTy::U8, + chalk_ir::UintTy::U16 => rustc_type_ir::UintTy::U16, + chalk_ir::UintTy::U32 => rustc_type_ir::UintTy::U32, + chalk_ir::UintTy::U64 => rustc_type_ir::UintTy::U64, + chalk_ir::UintTy::U128 => rustc_type_ir::UintTy::U128, + }; + Ty::new_uint(interner, uint_ty) + } + BuiltinType::Float(t) => { + let float_ty = match primitive::float_ty_from_builtin(t) { + chalk_ir::FloatTy::F16 => rustc_type_ir::FloatTy::F16, + chalk_ir::FloatTy::F32 => rustc_type_ir::FloatTy::F32, + chalk_ir::FloatTy::F64 => rustc_type_ir::FloatTy::F64, + chalk_ir::FloatTy::F128 => rustc_type_ir::FloatTy::F128, + }; + Ty::new_float(interner, float_ty) + } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index b22781e94701..49438151bbff 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -16,22 +16,24 @@ use hir_def::{ use hir_expand::name::Name; use intern::sym; use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_type_ir::inherent::{IntoKind, SliceLike}; use smallvec::{SmallVec, smallvec}; use stdx::never; use triomphe::Arc; use crate::{ AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, GenericArgData, - Goal, Guidance, InEnvironment, Interner, Mutability, Scalar, Solution, Substitution, - TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, TyVariableKind, - VariableKind, WhereClause, + Goal, InEnvironment, Interner, Mutability, Scalar, Substitution, TraitEnvironment, TraitRef, + TraitRefExt, Ty, TyBuilder, TyExt, TyKind, TyVariableKind, VariableKind, WhereClause, autoderef::{self, AutoderefKind}, db::HirDatabase, error_lifetime, from_chalk_trait_id, from_foreign_def_id, infer::{Adjust, Adjustment, OverloadedDeref, PointerCast, unify::InferenceTable}, lang_items::is_box, + next_solver::SolverDefId, primitive::{FloatTy, IntTy, UintTy}, to_chalk_trait_id, + traits::NextTraitSolveResult, utils::all_super_traits, }; @@ -43,6 +45,7 @@ pub enum TyFingerprint { Slice, Array, Never, + Ref(Mutability), RawPtr(Mutability), Scalar(Scalar), // These can have user-defined impls: @@ -88,7 +91,7 @@ impl TyFingerprint { TyKind::Raw(mutability, ..) => TyFingerprint::RawPtr(*mutability), TyKind::Foreign(alias_id, ..) => TyFingerprint::ForeignType(*alias_id), TyKind::Dyn(_) => ty.dyn_trait().map(TyFingerprint::Dyn)?, - TyKind::Ref(_, _, ty) => return TyFingerprint::for_trait_impl(ty), + TyKind::Ref(mutability, _, _) => TyFingerprint::Ref(*mutability), TyKind::Tuple(_, subst) => { let first_ty = subst.interned().first().map(|arg| arg.assert_ty_ref(Interner)); match first_ty { @@ -113,6 +116,94 @@ impl TyFingerprint { }; Some(fp) } + + /// Creates a TyFingerprint for looking up a trait impl. + pub fn for_trait_impl_ns<'db>(ty: &crate::next_solver::Ty<'db>) -> Option { + use rustc_type_ir::TyKind; + let fp = match (*ty).kind() { + TyKind::Str => TyFingerprint::Str, + TyKind::Never => TyFingerprint::Never, + TyKind::Slice(..) => TyFingerprint::Slice, + TyKind::Array(..) => TyFingerprint::Array, + TyKind::Int(int) => TyFingerprint::Scalar(Scalar::Int(match int { + rustc_type_ir::IntTy::Isize => IntTy::Isize, + rustc_type_ir::IntTy::I8 => IntTy::I8, + rustc_type_ir::IntTy::I16 => IntTy::I16, + rustc_type_ir::IntTy::I32 => IntTy::I32, + rustc_type_ir::IntTy::I64 => IntTy::I64, + rustc_type_ir::IntTy::I128 => IntTy::I128, + })), + TyKind::Uint(uint) => TyFingerprint::Scalar(Scalar::Uint(match uint { + rustc_type_ir::UintTy::Usize => UintTy::Usize, + rustc_type_ir::UintTy::U8 => UintTy::U8, + rustc_type_ir::UintTy::U16 => UintTy::U16, + rustc_type_ir::UintTy::U32 => UintTy::U32, + rustc_type_ir::UintTy::U64 => UintTy::U64, + rustc_type_ir::UintTy::U128 => UintTy::U128, + })), + TyKind::Float(float) => TyFingerprint::Scalar(Scalar::Float(match float { + rustc_type_ir::FloatTy::F16 => FloatTy::F16, + rustc_type_ir::FloatTy::F32 => FloatTy::F32, + rustc_type_ir::FloatTy::F64 => FloatTy::F64, + rustc_type_ir::FloatTy::F128 => FloatTy::F128, + })), + TyKind::Bool => TyFingerprint::Scalar(Scalar::Bool), + TyKind::Char => TyFingerprint::Scalar(Scalar::Char), + TyKind::Adt(def, _) => TyFingerprint::Adt(def.inner().id), + TyKind::RawPtr(.., mutability) => match mutability { + rustc_ast_ir::Mutability::Mut => TyFingerprint::RawPtr(Mutability::Mut), + rustc_ast_ir::Mutability::Not => TyFingerprint::RawPtr(Mutability::Not), + }, + TyKind::Foreign(def) => { + let SolverDefId::ForeignId(def) = def else { unreachable!() }; + TyFingerprint::ForeignType(crate::to_foreign_def_id(def)) + } + TyKind::Dynamic(bounds, _, _) => { + let trait_ref = bounds + .as_slice() + .iter() + .map(|b| (*b).skip_binder()) + .filter_map(|b| match b { + rustc_type_ir::ExistentialPredicate::Trait(t) => Some(t.def_id), + _ => None, + }) + .next()?; + let trait_id = match trait_ref { + SolverDefId::TraitId(id) => id, + _ => panic!("Bad GenericDefId in trait ref"), + }; + TyFingerprint::Dyn(trait_id) + } + TyKind::Ref(_, _, mutability) => match mutability { + rustc_ast_ir::Mutability::Mut => TyFingerprint::Ref(Mutability::Mut), + rustc_ast_ir::Mutability::Not => TyFingerprint::Ref(Mutability::Not), + }, + TyKind::Tuple(tys) => { + let first_ty = tys.as_slice().iter().next(); + match first_ty { + Some(ty) => return TyFingerprint::for_trait_impl_ns(ty), + None => TyFingerprint::Unit, + } + } + TyKind::FnDef(_, _) + | TyKind::Closure(_, _) + | TyKind::Coroutine(..) + | TyKind::CoroutineWitness(..) + | TyKind::Pat(..) + | TyKind::CoroutineClosure(..) => TyFingerprint::Unnameable, + TyKind::FnPtr(sig, _) => { + TyFingerprint::Function(sig.inputs().skip_binder().len() as u32) + } + TyKind::Alias(..) + | TyKind::Placeholder(_) + | TyKind::Bound(..) + | TyKind::Infer(_) + | TyKind::Error(_) + | TyKind::Param(..) + | TyKind::UnsafeBinder(..) => return None, + }; + Some(fp) + } } pub(crate) const ALL_INT_FPS: [TyFingerprint; 12] = [ @@ -807,10 +898,13 @@ fn find_matching_impl( let wcs = crate::chalk_db::convert_where_clauses(db, impl_.into(), &impl_substs) .into_iter() - .map(|b| b.cast(Interner)); - let goal = crate::Goal::all(Interner, wcs); - table.try_obligation(goal.clone())?; - table.register_obligation(goal); + .map(|b| -> Goal { b.cast(Interner) }); + for goal in wcs { + if table.try_obligation(goal.clone()).no_solution() { + return None; + } + table.register_obligation(goal); + } Some((impl_.impl_items(db), table.resolve_completely(impl_substs))) }) }) @@ -1313,7 +1407,7 @@ fn iterate_trait_method_candidates( }; if !known_implemented { let goal = generic_implements_goal(db, &table.trait_env, t, &canonical_self_ty); - if db.trait_solve(krate, block, goal.cast(Interner)).is_none() { + if db.trait_solve(krate, block, goal.cast(Interner)).no_solution() { continue 'traits; } } @@ -1496,9 +1590,9 @@ pub(crate) fn resolve_indexing_op( let deref_chain = autoderef_method_receiver(&mut table, ty); for (ty, adj) in deref_chain { let goal = generic_implements_goal(db, &table.trait_env, index_trait, &ty); - if db + if !db .trait_solve(table.trait_env.krate, table.trait_env.block, goal.cast(Interner)) - .is_some() + .no_solution() { return Some(adj); } @@ -1692,7 +1786,7 @@ fn is_valid_impl_fn_candidate( ); match solution { - Some(Solution::Unique(canonical_subst)) => { + NextTraitSolveResult::Certain(canonical_subst) => { canonicalized.apply_solution( table, Canonical { @@ -1701,16 +1795,13 @@ fn is_valid_impl_fn_candidate( }, ); } - Some(Solution::Ambig(Guidance::Definite(substs))) => { - canonicalized.apply_solution(table, substs); - } - Some(_) => (), - None => return IsValidCandidate::No, + NextTraitSolveResult::Uncertain(..) => {} + NextTraitSolveResult::NoSolution => return IsValidCandidate::No, } } for goal in goals { - if table.try_obligation(goal).is_none() { + if table.try_obligation(goal).no_solution() { return IsValidCandidate::No; } } @@ -1726,9 +1817,7 @@ pub fn implements_trait( trait_: TraitId, ) -> bool { let goal = generic_implements_goal(db, env, trait_, ty); - let solution = db.trait_solve(env.krate, env.block, goal.cast(Interner)); - - solution.is_some() + !db.trait_solve(env.krate, env.block, goal.cast(Interner)).no_solution() } pub fn implements_trait_unique( @@ -1738,9 +1827,7 @@ pub fn implements_trait_unique( trait_: TraitId, ) -> bool { let goal = generic_implements_goal(db, env, trait_, ty); - let solution = db.trait_solve(env.krate, env.block, goal.cast(Interner)); - - matches!(solution, Some(crate::Solution::Unique(_))) + db.trait_solve(env.krate, env.block, goal.cast(Interner)).certain() } /// This creates Substs for a trait with the given Self type and type variables diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs index c1f86960e154..eb5af58f2ea1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs @@ -5,7 +5,9 @@ use syntax::{TextRange, TextSize}; use test_fixture::WithFixture; use crate::display::DisplayTarget; -use crate::{Interner, Substitution, db::HirDatabase, mir::MirLowerError, test_db::TestDB}; +use crate::{ + Interner, Substitution, db::HirDatabase, mir::MirLowerError, setup_tracing, test_db::TestDB, +}; use super::{MirEvalError, interpret_mir}; @@ -49,6 +51,7 @@ fn check_pass_and_stdio( expected_stdout: &str, expected_stderr: &str, ) { + let _tracing = setup_tracing(); let (db, file_ids) = TestDB::with_many_files(ra_fixture); let file_id = *file_ids.last().unwrap(); let x = eval_main(&db, file_id); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index eb80e8706fa0..0bb8e6fe79d6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -2163,7 +2163,9 @@ pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result = rustc_type_ir::Binder, T>; +pub type EarlyBinder<'db, T> = rustc_type_ir::EarlyBinder, T>; +pub type Canonical<'db, T> = rustc_type_ir::Canonical, T>; +pub type CanonicalVarValues<'db> = rustc_type_ir::CanonicalVarValues>; +pub type CanonicalVarKind<'db> = rustc_type_ir::CanonicalVarKind>; +pub type CanonicalQueryInput<'db, V> = rustc_type_ir::CanonicalQueryInput, V>; +pub type AliasTy<'db> = rustc_type_ir::AliasTy>; +pub type PolyFnSig<'db> = Binder<'db, rustc_type_ir::FnSig>>; +pub type TypingMode<'db> = rustc_type_ir::TypingMode>; + +pub type FxIndexMap = + indexmap::IndexMap>; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/abi.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/abi.rs new file mode 100644 index 000000000000..80d1ea4aa4d0 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/abi.rs @@ -0,0 +1,68 @@ +//! ABI-related things in the next-trait-solver. +use rustc_type_ir::{error::TypeError, relate::Relate}; + +use crate::FnAbi; + +use super::interner::DbInterner; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub enum Safety { + Unsafe, + Safe, +} + +impl<'db> Relate> for Safety { + fn relate>>( + _relation: &mut R, + a: Self, + b: Self, + ) -> rustc_type_ir::relate::RelateResult, Self> { + if a != b { + Err(TypeError::SafetyMismatch(rustc_type_ir::error::ExpectedFound::new(a, b))) + } else { + Ok(a) + } + } +} + +impl<'db> rustc_type_ir::inherent::Safety> for Safety { + fn safe() -> Self { + Self::Safe + } + + fn is_safe(self) -> bool { + matches!(self, Safety::Safe) + } + + fn prefix_str(self) -> &'static str { + match self { + Self::Unsafe => "unsafe ", + Self::Safe => "", + } + } +} + +impl<'db> Relate> for FnAbi { + fn relate>>( + _relation: &mut R, + a: Self, + b: Self, + ) -> rustc_type_ir::relate::RelateResult, Self> { + if a == b { + Ok(a) + } else { + Err(TypeError::AbiMismatch(rustc_type_ir::error::ExpectedFound::new(a, b))) + } + } +} + +impl<'db> rustc_type_ir::inherent::Abi> for FnAbi { + fn rust() -> Self { + FnAbi::Rust + } + + fn is_rust(self) -> bool { + // TODO: rustc does not consider `RustCall` to be true here, but Chalk does + matches!(self, FnAbi::Rust | FnAbi::RustCall) + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs new file mode 100644 index 000000000000..5698ff290f74 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs @@ -0,0 +1,404 @@ +//! Things related to consts in the next-trait-solver. + +use std::hash::Hash; + +use intern::{Interned, Symbol}; +use rustc_ast_ir::try_visit; +use rustc_ast_ir::visit::VisitorResult; +use rustc_type_ir::{ + BoundVar, FlagComputation, Flags, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, + TypeVisitable, WithCachedTypeInfo, + inherent::{IntoKind, PlaceholderLike}, + relate::Relate, +}; + +use crate::{ConstScalar, MemoryMap, interner::InternedWrapperNoDebug}; + +use super::{BoundVarKind, DbInterner, ErrorGuaranteed, GenericArgs, Placeholder, Ty}; + +pub type ConstKind<'db> = rustc_type_ir::ConstKind>; +pub type UnevaluatedConst<'db> = rustc_type_ir::UnevaluatedConst>; + +#[salsa::interned(constructor = new_, debug)] +pub struct Const<'db> { + #[returns(ref)] + kind_: InternedWrapperNoDebug>>, +} + +impl<'db> Const<'db> { + pub fn new(interner: DbInterner<'db>, kind: ConstKind<'db>) -> Self { + let flags = FlagComputation::for_const_kind(&kind); + let cached = WithCachedTypeInfo { + internee: kind, + flags: flags.flags, + outer_exclusive_binder: flags.outer_exclusive_binder, + }; + Const::new_(interner.db(), InternedWrapperNoDebug(cached)) + } + + pub fn inner(&self) -> &WithCachedTypeInfo> { + salsa::with_attached_database(|db| { + let inner = &self.kind_(db).0; + // SAFETY: The caller already has access to a `Const<'db>`, so borrowchecking will + // make sure that our returned value is valid for the lifetime `'db`. + unsafe { std::mem::transmute(inner) } + }) + .unwrap() + } + + pub fn error(interner: DbInterner<'db>) -> Self { + Const::new(interner, rustc_type_ir::ConstKind::Error(ErrorGuaranteed)) + } + + pub fn new_param(interner: DbInterner<'db>, param: ParamConst) -> Self { + Const::new(interner, rustc_type_ir::ConstKind::Param(param)) + } + + pub fn new_placeholder(interner: DbInterner<'db>, placeholder: PlaceholderConst) -> Self { + Const::new(interner, ConstKind::Placeholder(placeholder)) + } + + pub fn is_ct_infer(&self) -> bool { + matches!(&self.inner().internee, ConstKind::Infer(_)) + } + + pub fn is_trivially_wf(self) -> bool { + match self.kind() { + ConstKind::Param(_) | ConstKind::Placeholder(_) | ConstKind::Bound(..) => true, + ConstKind::Infer(_) + | ConstKind::Unevaluated(..) + | ConstKind::Value(_) + | ConstKind::Error(_) + | ConstKind::Expr(_) => false, + } + } +} + +impl<'db> std::fmt::Debug for InternedWrapperNoDebug>> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.internee.fmt(f) + } +} + +pub type PlaceholderConst = Placeholder; + +#[derive(Copy, Clone, Hash, Eq, PartialEq)] +pub struct ParamConst { + pub index: u32, +} + +impl std::fmt::Debug for ParamConst { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "#{}", self.index) + } +} + +/// A type-level constant value. +/// +/// Represents a typed, fully evaluated constant. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct ValueConst<'db> { + pub(crate) ty: Ty<'db>, + pub(crate) value: Valtree<'db>, +} + +impl<'db> ValueConst<'db> { + pub fn new(ty: Ty<'db>, bytes: ConstBytes) -> Self { + let value = Valtree::new(bytes); + ValueConst { ty, value } + } +} + +impl<'db> rustc_type_ir::inherent::ValueConst> for ValueConst<'db> { + fn ty(self) -> Ty<'db> { + self.ty + } + + fn valtree(self) -> Valtree<'db> { + self.value + } +} + +impl<'db> rustc_type_ir::TypeVisitable> for ValueConst<'db> { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + self.ty.visit_with(visitor) + } +} + +impl<'db> rustc_type_ir::TypeFoldable> for ValueConst<'db> { + fn fold_with>>(self, folder: &mut F) -> Self { + ValueConst { ty: self.ty.fold_with(folder), value: self.value } + } + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + Ok(ValueConst { ty: self.ty.try_fold_with(folder)?, value: self.value }) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ConstBytes(pub Box<[u8]>, pub MemoryMap); + +impl Hash for ConstBytes { + fn hash(&self, state: &mut H) { + self.0.hash(state) + } +} + +#[salsa::interned(constructor = new_, debug)] +pub struct Valtree<'db> { + #[returns(ref)] + bytes_: ConstBytes, +} + +impl<'db> Valtree<'db> { + pub fn new(bytes: ConstBytes) -> Self { + salsa::with_attached_database(|db| unsafe { + // SAFETY: ¯\_(ツ)_/¯ + std::mem::transmute(Valtree::new_(db, bytes)) + }) + .unwrap() + } + + pub fn inner(&self) -> &ConstBytes { + salsa::with_attached_database(|db| { + let inner = self.bytes_(db); + // SAFETY: The caller already has access to a `Valtree<'db>`, so borrowchecking will + // make sure that our returned value is valid for the lifetime `'db`. + unsafe { std::mem::transmute(inner) } + }) + .unwrap() + } +} + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub struct ExprConst; + +impl rustc_type_ir::inherent::ParamLike for ParamConst { + fn index(self) -> u32 { + self.index + } +} + +impl<'db> IntoKind for Const<'db> { + type Kind = ConstKind<'db>; + + fn kind(self) -> Self::Kind { + self.inner().internee + } +} + +impl<'db> TypeVisitable> for Const<'db> { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + visitor.visit_const(*self) + } +} + +impl<'db> TypeSuperVisitable> for Const<'db> { + fn super_visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + match self.kind() { + ConstKind::Unevaluated(uv) => uv.visit_with(visitor), + ConstKind::Value(v) => v.visit_with(visitor), + ConstKind::Expr(e) => e.visit_with(visitor), + ConstKind::Error(e) => e.visit_with(visitor), + + ConstKind::Param(_) + | ConstKind::Infer(_) + | ConstKind::Bound(..) + | ConstKind::Placeholder(_) => V::Result::output(), + } + } +} + +impl<'db> TypeFoldable> for Const<'db> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + folder.try_fold_const(self) + } + fn fold_with>>(self, folder: &mut F) -> Self { + folder.fold_const(self) + } +} + +impl<'db> TypeSuperFoldable> for Const<'db> { + fn try_super_fold_with>>( + self, + folder: &mut F, + ) -> Result { + let kind = match self.kind() { + ConstKind::Unevaluated(uv) => ConstKind::Unevaluated(uv.try_fold_with(folder)?), + ConstKind::Value(v) => ConstKind::Value(v.try_fold_with(folder)?), + ConstKind::Expr(e) => ConstKind::Expr(e.try_fold_with(folder)?), + + ConstKind::Param(_) + | ConstKind::Infer(_) + | ConstKind::Bound(..) + | ConstKind::Placeholder(_) + | ConstKind::Error(_) => return Ok(self), + }; + if kind != self.kind() { Ok(Const::new(folder.cx(), kind)) } else { Ok(self) } + } + fn super_fold_with>>( + self, + folder: &mut F, + ) -> Self { + let kind = match self.kind() { + ConstKind::Unevaluated(uv) => ConstKind::Unevaluated(uv.fold_with(folder)), + ConstKind::Value(v) => ConstKind::Value(v.fold_with(folder)), + ConstKind::Expr(e) => ConstKind::Expr(e.fold_with(folder)), + + ConstKind::Param(_) + | ConstKind::Infer(_) + | ConstKind::Bound(..) + | ConstKind::Placeholder(_) + | ConstKind::Error(_) => return self, + }; + if kind != self.kind() { Const::new(folder.cx(), kind) } else { self } + } +} + +impl<'db> Relate> for Const<'db> { + fn relate>>( + relation: &mut R, + a: Self, + b: Self, + ) -> rustc_type_ir::relate::RelateResult, Self> { + relation.consts(a, b) + } +} + +impl<'db> Flags for Const<'db> { + fn flags(&self) -> rustc_type_ir::TypeFlags { + self.inner().flags + } + + fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex { + self.inner().outer_exclusive_binder + } +} + +impl<'db> rustc_type_ir::inherent::Const> for Const<'db> { + fn new_infer(interner: DbInterner<'db>, var: rustc_type_ir::InferConst) -> Self { + Const::new(interner, ConstKind::Infer(var)) + } + + fn new_var(interner: DbInterner<'db>, var: rustc_type_ir::ConstVid) -> Self { + Const::new(interner, ConstKind::Infer(rustc_type_ir::InferConst::Var(var))) + } + + fn new_bound( + interner: DbInterner<'db>, + debruijn: rustc_type_ir::DebruijnIndex, + var: BoundVar, + ) -> Self { + Const::new(interner, ConstKind::Bound(debruijn, var)) + } + + fn new_anon_bound( + interner: DbInterner<'db>, + debruijn: rustc_type_ir::DebruijnIndex, + var: rustc_type_ir::BoundVar, + ) -> Self { + Const::new(interner, ConstKind::Bound(debruijn, var)) + } + + fn new_unevaluated( + interner: DbInterner<'db>, + uv: rustc_type_ir::UnevaluatedConst>, + ) -> Self { + Const::new(interner, ConstKind::Unevaluated(uv)) + } + + fn new_expr(interner: DbInterner<'db>, expr: ExprConst) -> Self { + Const::new(interner, ConstKind::Expr(expr)) + } + + fn new_error(interner: DbInterner<'db>, guar: ErrorGuaranteed) -> Self { + Const::new(interner, ConstKind::Error(guar)) + } + + fn new_placeholder( + interner: DbInterner<'db>, + param: as rustc_type_ir::Interner>::PlaceholderConst, + ) -> Self { + Const::new(interner, ConstKind::Placeholder(param)) + } +} + +impl<'db> PlaceholderLike> for PlaceholderConst { + type Bound = rustc_type_ir::BoundVar; + + fn universe(self) -> rustc_type_ir::UniverseIndex { + self.universe + } + + fn var(self) -> rustc_type_ir::BoundVar { + self.bound + } + + fn with_updated_universe(self, ui: rustc_type_ir::UniverseIndex) -> Self { + Placeholder { universe: ui, bound: self.bound } + } + + fn new(ui: rustc_type_ir::UniverseIndex, var: rustc_type_ir::BoundVar) -> Self { + Placeholder { universe: ui, bound: var } + } + fn new_anon(ui: rustc_type_ir::UniverseIndex, var: rustc_type_ir::BoundVar) -> Self { + Placeholder { universe: ui, bound: var } + } +} + +impl<'db> TypeVisitable> for ExprConst { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + // Ensure we get back to this when we fill in the fields + let ExprConst = &self; + V::Result::output() + } +} + +impl<'db> TypeFoldable> for ExprConst { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + Ok(ExprConst) + } + fn fold_with>>(self, folder: &mut F) -> Self { + ExprConst + } +} + +impl<'db> Relate> for ExprConst { + fn relate>>( + relation: &mut R, + a: Self, + b: Self, + ) -> rustc_type_ir::relate::RelateResult, Self> { + // Ensure we get back to this when we fill in the fields + let ExprConst = b; + Ok(a) + } +} + +impl<'db> rustc_type_ir::inherent::ExprConst> for ExprConst { + fn args(self) -> as rustc_type_ir::Interner>::GenericArgs { + // Ensure we get back to this when we fill in the fields + let ExprConst = self; + GenericArgs::default() + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs new file mode 100644 index 000000000000..cfbc10e740e4 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs @@ -0,0 +1,96 @@ +//! Definition of `SolverDefId` + +use hir_def::{ + AdtId, ConstId, EnumId, EnumVariantId, FunctionId, GenericDefId, ImplId, StaticId, StructId, + TraitAliasId, TraitId, TypeAliasId, UnionId, +}; +use rustc_type_ir::inherent; +use stdx::impl_from; + +use crate::db::{InternedClosureId, InternedCoroutineId, InternedOpaqueTyId}; + +use super::DbInterner; + +#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)] +pub enum Ctor { + Struct(StructId), + Enum(EnumVariantId), +} + +#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)] +pub enum SolverDefId { + AdtId(AdtId), + ConstId(ConstId), + FunctionId(FunctionId), + ImplId(ImplId), + StaticId(StaticId), + TraitAliasId(TraitAliasId), + TraitId(TraitId), + TypeAliasId(TypeAliasId), + ForeignId(TypeAliasId), + InternedClosureId(InternedClosureId), + InternedCoroutineId(InternedCoroutineId), + InternedOpaqueTyId(InternedOpaqueTyId), + Ctor(Ctor), +} + +impl_from!( + AdtId(StructId, EnumId, UnionId), + ConstId, + FunctionId, + ImplId, + StaticId, + TraitAliasId, + TraitId, + TypeAliasId, + InternedClosureId, + InternedCoroutineId, + InternedOpaqueTyId + for SolverDefId +); + +impl From for SolverDefId { + fn from(value: GenericDefId) -> Self { + match value { + GenericDefId::AdtId(adt_id) => SolverDefId::AdtId(adt_id), + GenericDefId::ConstId(const_id) => SolverDefId::ConstId(const_id), + GenericDefId::FunctionId(function_id) => SolverDefId::FunctionId(function_id), + GenericDefId::ImplId(impl_id) => SolverDefId::ImplId(impl_id), + GenericDefId::StaticId(static_id) => SolverDefId::StaticId(static_id), + GenericDefId::TraitAliasId(trait_alias_id) => SolverDefId::TraitAliasId(trait_alias_id), + GenericDefId::TraitId(trait_id) => SolverDefId::TraitId(trait_id), + GenericDefId::TypeAliasId(type_alias_id) => SolverDefId::TypeAliasId(type_alias_id), + } + } +} + +impl TryFrom for GenericDefId { + type Error = SolverDefId; + + fn try_from(value: SolverDefId) -> Result { + Ok(match value { + SolverDefId::AdtId(adt_id) => GenericDefId::AdtId(adt_id), + SolverDefId::ConstId(const_id) => GenericDefId::ConstId(const_id), + SolverDefId::FunctionId(function_id) => GenericDefId::FunctionId(function_id), + SolverDefId::ImplId(impl_id) => GenericDefId::ImplId(impl_id), + SolverDefId::StaticId(static_id) => GenericDefId::StaticId(static_id), + SolverDefId::TraitAliasId(trait_alias_id) => GenericDefId::TraitAliasId(trait_alias_id), + SolverDefId::TraitId(trait_id) => GenericDefId::TraitId(trait_id), + SolverDefId::TypeAliasId(type_alias_id) => GenericDefId::TypeAliasId(type_alias_id), + SolverDefId::ForeignId(_) => return Err(value), + SolverDefId::InternedClosureId(_) => return Err(value), + SolverDefId::InternedCoroutineId(_) => return Err(value), + SolverDefId::InternedOpaqueTyId(_) => return Err(value), + SolverDefId::Ctor(_) => return Err(value), + }) + } +} + +impl<'db> inherent::DefId> for SolverDefId { + fn as_local(self) -> Option { + Some(self) + } + fn is_local(self) -> bool { + true + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs new file mode 100644 index 000000000000..3cc1e64b6add --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs @@ -0,0 +1,129 @@ +//! Fold impls for the next-trait-solver. + +use rustc_type_ir::{ + BoundVar, DebruijnIndex, RegionKind, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitableExt, + inherent::{IntoKind, Region as _}, +}; + +use super::{ + Binder, BoundRegion, BoundTy, Const, ConstKind, DbInterner, Predicate, Region, Ty, TyKind, +}; + +/// A delegate used when instantiating bound vars. +/// +/// Any implementation must make sure that each bound variable always +/// gets mapped to the same result. `BoundVarReplacer` caches by using +/// a `DelayedMap` which does not cache the first few types it encounters. +pub trait BoundVarReplacerDelegate<'db> { + fn replace_region(&mut self, br: BoundRegion) -> Region<'db>; + fn replace_ty(&mut self, bt: BoundTy) -> Ty<'db>; + fn replace_const(&mut self, bv: BoundVar) -> Const<'db>; +} + +/// A simple delegate taking 3 mutable functions. The used functions must +/// always return the same result for each bound variable, no matter how +/// frequently they are called. +pub struct FnMutDelegate<'db, 'a> { + pub regions: &'a mut (dyn FnMut(BoundRegion) -> Region<'db> + 'a), + pub types: &'a mut (dyn FnMut(BoundTy) -> Ty<'db> + 'a), + pub consts: &'a mut (dyn FnMut(BoundVar) -> Const<'db> + 'a), +} + +impl<'db, 'a> BoundVarReplacerDelegate<'db> for FnMutDelegate<'db, 'a> { + fn replace_region(&mut self, br: BoundRegion) -> Region<'db> { + (self.regions)(br) + } + fn replace_ty(&mut self, bt: BoundTy) -> Ty<'db> { + (self.types)(bt) + } + fn replace_const(&mut self, bv: BoundVar) -> Const<'db> { + (self.consts)(bv) + } +} + +/// Replaces the escaping bound vars (late bound regions or bound types) in a type. +pub(crate) struct BoundVarReplacer<'db, D> { + interner: DbInterner<'db>, + /// As with `RegionFolder`, represents the index of a binder *just outside* + /// the ones we have visited. + current_index: DebruijnIndex, + + delegate: D, +} + +impl<'db, D: BoundVarReplacerDelegate<'db>> BoundVarReplacer<'db, D> { + pub fn new(tcx: DbInterner<'db>, delegate: D) -> Self { + BoundVarReplacer { interner: tcx, current_index: DebruijnIndex::ZERO, delegate } + } +} + +impl<'db, D> TypeFolder> for BoundVarReplacer<'db, D> +where + D: BoundVarReplacerDelegate<'db>, +{ + fn cx(&self) -> DbInterner<'db> { + self.interner + } + + fn fold_binder>>( + &mut self, + t: Binder<'db, T>, + ) -> Binder<'db, T> { + self.current_index.shift_in(1); + let t = t.super_fold_with(self); + self.current_index.shift_out(1); + t + } + + fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> { + match t.kind() { + TyKind::Bound(debruijn, bound_ty) if debruijn == self.current_index => { + let ty = self.delegate.replace_ty(bound_ty); + debug_assert!(!ty.has_vars_bound_above(DebruijnIndex::ZERO)); + rustc_type_ir::shift_vars(self.interner, ty, self.current_index.as_u32()) + } + _ => { + if !t.has_vars_bound_at_or_above(self.current_index) { + t + } else { + t.super_fold_with(self) + } + } + } + } + + fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { + match r.kind() { + RegionKind::ReBound(debruijn, br) if debruijn == self.current_index => { + let region = self.delegate.replace_region(br); + if let RegionKind::ReBound(debruijn1, br) = region.kind() { + // If the callback returns a bound region, + // that region should always use the INNERMOST + // debruijn index. Then we adjust it to the + // correct depth. + assert_eq!(debruijn1, DebruijnIndex::ZERO); + Region::new_bound(self.interner, debruijn, br) + } else { + region + } + } + _ => r, + } + } + + fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> { + match ct.kind() { + ConstKind::Bound(debruijn, bound_const) if debruijn == self.current_index => { + let ct = self.delegate.replace_const(bound_const); + debug_assert!(!ct.has_vars_bound_above(DebruijnIndex::ZERO)); + rustc_type_ir::shift_vars(self.interner, ct, self.current_index.as_u32()) + } + _ => ct.super_fold_with(self), + } + } + + fn fold_predicate(&mut self, p: Predicate<'db>) -> Predicate<'db> { + if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs new file mode 100644 index 000000000000..007a674ad399 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs @@ -0,0 +1,229 @@ +//! Fulfill loop for next-solver. + +use std::marker::PhantomData; +use std::mem; +use std::ops::ControlFlow; +use std::vec::ExtractIf; + +use rustc_next_trait_solver::delegate::SolverDelegate; +use rustc_next_trait_solver::solve::{ + GoalEvaluation, GoalStalledOn, HasChanged, SolverDelegateEvalExt, +}; +use rustc_type_ir::Interner; +use rustc_type_ir::inherent::Span as _; +use rustc_type_ir::solve::{Certainty, NoSolution}; + +use crate::next_solver::infer::InferCtxt; +use crate::next_solver::infer::traits::{PredicateObligation, PredicateObligations}; +use crate::next_solver::{DbInterner, SolverContext, Span, TypingMode}; + +type PendingObligations<'db> = + Vec<(PredicateObligation<'db>, Option>>)>; + +/// A trait engine using the new trait solver. +/// +/// This is mostly identical to how `evaluate_all` works inside of the +/// solver, except that the requirements are slightly different. +/// +/// Unlike `evaluate_all` it is possible to add new obligations later on +/// and we also have to track diagnostics information by using `Obligation` +/// instead of `Goal`. +/// +/// It is also likely that we want to use slightly different datastructures +/// here as this will have to deal with far more root goals than `evaluate_all`. +pub struct FulfillmentCtxt<'db> { + obligations: ObligationStorage<'db>, + + /// The snapshot in which this context was created. Using the context + /// outside of this snapshot leads to subtle bugs if the snapshot + /// gets rolled back. Because of this we explicitly check that we only + /// use the context in exactly this snapshot. + usable_in_snapshot: usize, +} + +#[derive(Default, Debug)] +struct ObligationStorage<'db> { + /// Obligations which resulted in an overflow in fulfillment itself. + /// + /// We cannot eagerly return these as error so we instead store them here + /// to avoid recomputing them each time `select_where_possible` is called. + /// This also allows us to return the correct `FulfillmentError` for them. + overflowed: Vec>, + pending: PendingObligations<'db>, +} + +impl<'db> ObligationStorage<'db> { + fn register( + &mut self, + obligation: PredicateObligation<'db>, + stalled_on: Option>>, + ) { + self.pending.push((obligation, stalled_on)); + } + + fn has_pending_obligations(&self) -> bool { + !self.pending.is_empty() || !self.overflowed.is_empty() + } + + fn clone_pending(&self) -> PredicateObligations<'db> { + let mut obligations: PredicateObligations<'db> = + self.pending.iter().map(|(o, _)| o.clone()).collect(); + obligations.extend(self.overflowed.iter().cloned()); + obligations + } + + fn drain_pending( + &mut self, + cond: impl Fn(&PredicateObligation<'db>) -> bool, + ) -> PendingObligations<'db> { + let (not_stalled, pending) = + mem::take(&mut self.pending).into_iter().partition(|(o, _)| cond(o)); + self.pending = pending; + not_stalled + } + + fn on_fulfillment_overflow(&mut self, infcx: &InferCtxt<'db>) { + infcx.probe(|_| { + // IMPORTANT: we must not use solve any inference variables in the obligations + // as this is all happening inside of a probe. We use a probe to make sure + // we get all obligations involved in the overflow. We pretty much check: if + // we were to do another step of `select_where_possible`, which goals would + // change. + // FIXME: is merged, this can be removed. + self.overflowed.extend( + self.pending + .extract_if(.., |(o, stalled_on)| { + let goal = o.as_goal(); + let result = <&SolverContext<'db>>::from(infcx).evaluate_root_goal( + goal, + Span::dummy(), + stalled_on.take(), + ); + matches!(result, Ok(GoalEvaluation { has_changed: HasChanged::Yes, .. })) + }) + .map(|(o, _)| o), + ); + }) + } +} + +impl<'db> FulfillmentCtxt<'db> { + pub fn new(infcx: &InferCtxt<'db>) -> FulfillmentCtxt<'db> { + FulfillmentCtxt { + obligations: Default::default(), + usable_in_snapshot: infcx.num_open_snapshots(), + } + } +} + +impl<'db> FulfillmentCtxt<'db> { + #[tracing::instrument(level = "trace", skip(self, infcx))] + pub(crate) fn register_predicate_obligation( + &mut self, + infcx: &InferCtxt<'db>, + obligation: PredicateObligation<'db>, + ) { + assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); + self.obligations.register(obligation, None); + } + + pub(crate) fn collect_remaining_errors( + &mut self, + infcx: &InferCtxt<'db>, + ) -> Vec> { + self.obligations + .pending + .drain(..) + .map(|(obligation, _)| NextSolverError::Ambiguity(obligation)) + .chain(self.obligations.overflowed.drain(..).map(NextSolverError::Overflow)) + .collect() + } + + pub(crate) fn select_where_possible( + &mut self, + infcx: &InferCtxt<'db>, + ) -> Vec> { + assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); + let mut errors = Vec::new(); + loop { + let mut any_changed = false; + for (mut obligation, stalled_on) in self.obligations.drain_pending(|_| true) { + if obligation.recursion_depth >= infcx.interner.recursion_limit() { + self.obligations.on_fulfillment_overflow(infcx); + // Only return true errors that we have accumulated while processing. + return errors; + } + + let goal = obligation.as_goal(); + let delegate = <&SolverContext<'db>>::from(infcx); + if let Some(certainty) = delegate.compute_goal_fast_path(goal, Span::dummy()) { + match certainty { + Certainty::Yes => {} + Certainty::Maybe(_) => { + self.obligations.register(obligation, None); + } + } + continue; + } + + let result = delegate.evaluate_root_goal(goal, Span::dummy(), stalled_on); + let GoalEvaluation { certainty, has_changed, stalled_on } = match result { + Ok(result) => result, + Err(NoSolution) => { + errors.push(NextSolverError::TrueError(obligation)); + continue; + } + }; + + if has_changed == HasChanged::Yes { + // We increment the recursion depth here to track the number of times + // this goal has resulted in inference progress. This doesn't precisely + // model the way that we track recursion depth in the old solver due + // to the fact that we only process root obligations, but it is a good + // approximation and should only result in fulfillment overflow in + // pathological cases. + obligation.recursion_depth += 1; + any_changed = true; + } + + match certainty { + Certainty::Yes => {} + Certainty::Maybe(_) => self.obligations.register(obligation, stalled_on), + } + } + + if !any_changed { + break; + } + } + + errors + } + + pub(crate) fn select_all_or_error( + &mut self, + infcx: &InferCtxt<'db>, + ) -> Vec> { + let errors = self.select_where_possible(infcx); + if !errors.is_empty() { + return errors; + } + + self.collect_remaining_errors(infcx) + } + + fn has_pending_obligations(&self) -> bool { + self.obligations.has_pending_obligations() + } + + fn pending_obligations(&self) -> PredicateObligations<'db> { + self.obligations.clone_pending() + } +} + +#[derive(Debug)] +pub enum NextSolverError<'db> { + TrueError(PredicateObligation<'db>), + Ambiguity(PredicateObligation<'db>), + Overflow(PredicateObligation<'db>), +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs new file mode 100644 index 000000000000..85a79923a729 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs @@ -0,0 +1,525 @@ +//! Things related to generic args in the next-trait-solver. + +use intern::{Interned, Symbol}; +use rustc_type_ir::{ + ClosureArgs, CollectAndApply, ConstVid, CoroutineArgs, CoroutineClosureArgs, FnSig, FnSigTys, + GenericArgKind, IntTy, Interner, TermKind, TyKind, TyVid, TypeFoldable, TypeVisitable, + Variance, + inherent::{GenericArg as _, GenericsOf, IntoKind, SliceLike, Term as _, Ty as _}, + relate::{Relate, VarianceDiagInfo}, +}; +use smallvec::SmallVec; + +use crate::db::HirDatabase; + +use super::{ + Const, DbInterner, EarlyParamRegion, ErrorGuaranteed, ParamConst, Region, SolverDefId, Ty, Tys, + generics::{GenericParamDef, GenericParamDefKind, Generics}, + interned_vec_db, +}; + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub enum GenericArg<'db> { + Ty(Ty<'db>), + Lifetime(Region<'db>), + Const(Const<'db>), +} + +impl<'db> std::fmt::Debug for GenericArg<'db> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Ty(t) => std::fmt::Debug::fmt(t, f), + Self::Lifetime(r) => std::fmt::Debug::fmt(r, f), + Self::Const(c) => std::fmt::Debug::fmt(c, f), + } + } +} + +impl<'db> From> for GenericArg<'db> { + fn from(value: Term<'db>) -> Self { + match value { + Term::Ty(ty) => GenericArg::Ty(ty), + Term::Const(c) => GenericArg::Const(c), + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub enum Term<'db> { + Ty(Ty<'db>), + Const(Const<'db>), +} + +impl<'db> std::fmt::Debug for Term<'db> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Ty(t) => std::fmt::Debug::fmt(t, f), + Self::Const(c) => std::fmt::Debug::fmt(c, f), + } + } +} + +impl<'db> Term<'db> { + pub fn expect_type(&self) -> Ty<'db> { + self.as_type().expect("expected a type, but found a const") + } + + pub fn is_trivially_wf(&self, tcx: DbInterner<'db>) -> bool { + match self.kind() { + TermKind::Ty(ty) => ty.is_trivially_wf(tcx), + TermKind::Const(ct) => ct.is_trivially_wf(), + } + } +} + +impl<'db> From> for GenericArg<'db> { + fn from(value: Ty<'db>) -> Self { + Self::Ty(value) + } +} + +impl<'db> From> for GenericArg<'db> { + fn from(value: Region<'db>) -> Self { + Self::Lifetime(value) + } +} + +impl<'db> From> for GenericArg<'db> { + fn from(value: Const<'db>) -> Self { + Self::Const(value) + } +} + +impl<'db> IntoKind for GenericArg<'db> { + type Kind = GenericArgKind>; + + fn kind(self) -> Self::Kind { + match self { + GenericArg::Ty(ty) => GenericArgKind::Type(ty), + GenericArg::Lifetime(region) => GenericArgKind::Lifetime(region), + GenericArg::Const(c) => GenericArgKind::Const(c), + } + } +} + +impl<'db> TypeVisitable> for GenericArg<'db> { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + match self { + GenericArg::Lifetime(lt) => lt.visit_with(visitor), + GenericArg::Ty(ty) => ty.visit_with(visitor), + GenericArg::Const(ct) => ct.visit_with(visitor), + } + } +} + +impl<'db> TypeFoldable> for GenericArg<'db> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + match self.kind() { + GenericArgKind::Lifetime(lt) => lt.try_fold_with(folder).map(Into::into), + GenericArgKind::Type(ty) => ty.try_fold_with(folder).map(Into::into), + GenericArgKind::Const(ct) => ct.try_fold_with(folder).map(Into::into), + } + } + fn fold_with>>(self, folder: &mut F) -> Self { + match self.kind() { + GenericArgKind::Lifetime(lt) => lt.fold_with(folder).into(), + GenericArgKind::Type(ty) => ty.fold_with(folder).into(), + GenericArgKind::Const(ct) => ct.fold_with(folder).into(), + } + } +} + +impl<'db> Relate> for GenericArg<'db> { + fn relate>>( + relation: &mut R, + a: Self, + b: Self, + ) -> rustc_type_ir::relate::RelateResult, Self> { + match (a.kind(), b.kind()) { + (GenericArgKind::Lifetime(a_lt), GenericArgKind::Lifetime(b_lt)) => { + Ok(relation.relate(a_lt, b_lt)?.into()) + } + (GenericArgKind::Type(a_ty), GenericArgKind::Type(b_ty)) => { + Ok(relation.relate(a_ty, b_ty)?.into()) + } + (GenericArgKind::Const(a_ct), GenericArgKind::Const(b_ct)) => { + Ok(relation.relate(a_ct, b_ct)?.into()) + } + (GenericArgKind::Lifetime(unpacked), x) => { + unreachable!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) + } + (GenericArgKind::Type(unpacked), x) => { + unreachable!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) + } + (GenericArgKind::Const(unpacked), x) => { + unreachable!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) + } + } + } +} + +interned_vec_db!(GenericArgs, GenericArg); + +impl<'db> rustc_type_ir::inherent::GenericArg> for GenericArg<'db> {} + +impl<'db> GenericArgs<'db> { + /// Creates an `GenericArgs` for generic parameter definitions, + /// by calling closures to obtain each kind. + /// The closures get to observe the `GenericArgs` as they're + /// being built, which can be used to correctly + /// replace defaults of generic parameters. + pub fn for_item( + interner: DbInterner<'db>, + def_id: SolverDefId, + mut mk_kind: F, + ) -> GenericArgs<'db> + where + F: FnMut(&Symbol, u32, GenericParamDefKind, &[GenericArg<'db>]) -> GenericArg<'db>, + { + let defs = interner.generics_of(def_id); + let count = defs.count(); + let mut args = SmallVec::with_capacity(count); + Self::fill_item(&mut args, interner, defs, &mut mk_kind); + interner.mk_args(&args) + } + + fn fill_item( + args: &mut SmallVec<[GenericArg<'db>; 8]>, + interner: DbInterner<'_>, + defs: Generics, + mk_kind: &mut F, + ) where + F: FnMut(&Symbol, u32, GenericParamDefKind, &[GenericArg<'db>]) -> GenericArg<'db>, + { + let self_len = defs.own_params.len() as u32; + if let Some(def_id) = defs.parent { + let parent_defs = interner.generics_of(def_id.into()); + Self::fill_item(args, interner, parent_defs, mk_kind); + } + Self::fill_single(args, &defs, mk_kind); + } + + fn fill_single(args: &mut SmallVec<[GenericArg<'db>; 8]>, defs: &Generics, mk_kind: &mut F) + where + F: FnMut(&Symbol, u32, GenericParamDefKind, &[GenericArg<'db>]) -> GenericArg<'db>, + { + let start_len = args.len(); + args.reserve(defs.own_params.len()); + for param in &defs.own_params { + let kind = mk_kind(¶m.name, args.len() as u32, param.kind, args); + args.push(kind); + } + } +} + +impl<'db> rustc_type_ir::relate::Relate> for GenericArgs<'db> { + fn relate>>( + relation: &mut R, + a: Self, + b: Self, + ) -> rustc_type_ir::relate::RelateResult, Self> { + let interner = relation.cx(); + CollectAndApply::collect_and_apply( + std::iter::zip(a.iter(), b.iter()).map(|(a, b)| { + relation.relate_with_variance( + Variance::Invariant, + VarianceDiagInfo::default(), + a, + b, + ) + }), + |g| GenericArgs::new_from_iter(interner, g.iter().cloned()), + ) + } +} + +impl<'db> rustc_type_ir::inherent::GenericArgs> for GenericArgs<'db> { + fn as_closure(self) -> ClosureArgs> { + ClosureArgs { args: self } + } + fn as_coroutine(self) -> CoroutineArgs> { + CoroutineArgs { args: self } + } + fn as_coroutine_closure(self) -> CoroutineClosureArgs> { + CoroutineClosureArgs { args: self } + } + fn rebase_onto( + self, + interner: DbInterner<'db>, + source_def_id: as rustc_type_ir::Interner>::DefId, + target: as rustc_type_ir::Interner>::GenericArgs, + ) -> as rustc_type_ir::Interner>::GenericArgs { + let defs = interner.generics_of(source_def_id); + interner.mk_args_from_iter(target.iter().chain(self.iter().skip(defs.count()))) + } + + fn identity_for_item( + interner: DbInterner<'db>, + def_id: as rustc_type_ir::Interner>::DefId, + ) -> as rustc_type_ir::Interner>::GenericArgs { + Self::for_item(interner, def_id, |name, index, kind, _| mk_param(index, name, kind)) + } + + fn extend_with_error( + interner: DbInterner<'db>, + def_id: as rustc_type_ir::Interner>::DefId, + original_args: &[ as rustc_type_ir::Interner>::GenericArg], + ) -> as rustc_type_ir::Interner>::GenericArgs { + Self::for_item(interner, def_id, |name, index, kind, _| { + if let Some(arg) = original_args.get(index as usize) { + *arg + } else { + error_for_param_kind(kind, interner) + } + }) + } + fn type_at(self, i: usize) -> as rustc_type_ir::Interner>::Ty { + self.inner() + .get(i) + .and_then(|g| g.as_type()) + .unwrap_or_else(|| Ty::new_error(DbInterner::conjure(), ErrorGuaranteed)) + } + + fn region_at(self, i: usize) -> as rustc_type_ir::Interner>::Region { + self.inner() + .get(i) + .and_then(|g| g.as_region()) + .unwrap_or_else(|| Region::error(DbInterner::conjure())) + } + + fn const_at(self, i: usize) -> as rustc_type_ir::Interner>::Const { + self.inner() + .get(i) + .and_then(|g| g.as_const()) + .unwrap_or_else(|| Const::error(DbInterner::conjure())) + } + + fn split_closure_args(self) -> rustc_type_ir::ClosureArgsParts> { + // FIXME: should use `ClosureSubst` when possible + match self.inner().as_slice() { + [parent_args @ .., sig_ty] => { + let interner = DbInterner::conjure(); + // This is stupid, but the next solver expects the first input to actually be a tuple + let sig_ty = match sig_ty.expect_ty().kind() { + TyKind::FnPtr(sig_tys, header) => Ty::new( + interner, + TyKind::FnPtr( + sig_tys.map_bound(|s| { + let inputs = Ty::new_tup_from_iter(interner, s.inputs().iter()); + let output = s.output(); + FnSigTys { + inputs_and_output: Tys::new_from_iter( + interner, + [inputs, output], + ), + } + }), + header, + ), + ), + _ => unreachable!("sig_ty should be last"), + }; + rustc_type_ir::ClosureArgsParts { + parent_args: GenericArgs::new_from_iter(interner, parent_args.iter().cloned()), + closure_sig_as_fn_ptr_ty: sig_ty, + closure_kind_ty: Ty::new(interner, TyKind::Int(IntTy::I8)), + tupled_upvars_ty: Ty::new_unit(interner), + } + } + _ => { + unreachable!("unexpected closure sig"); + } + } + } + + fn split_coroutine_closure_args( + self, + ) -> rustc_type_ir::CoroutineClosureArgsParts> { + match self.inner().as_slice() { + [ + parent_args @ .., + closure_kind_ty, + signature_parts_ty, + tupled_upvars_ty, + coroutine_captures_by_ref_ty, + coroutine_witness_ty, + ] => rustc_type_ir::CoroutineClosureArgsParts { + parent_args: GenericArgs::new_from_iter( + DbInterner::conjure(), + parent_args.iter().cloned(), + ), + closure_kind_ty: closure_kind_ty.expect_ty(), + signature_parts_ty: signature_parts_ty.expect_ty(), + tupled_upvars_ty: tupled_upvars_ty.expect_ty(), + coroutine_captures_by_ref_ty: coroutine_captures_by_ref_ty.expect_ty(), + coroutine_witness_ty: coroutine_witness_ty.expect_ty(), + }, + _ => panic!("GenericArgs were likely not for a CoroutineClosure."), + } + } + + fn split_coroutine_args(self) -> rustc_type_ir::CoroutineArgsParts> { + let interner = DbInterner::conjure(); + match self.inner().as_slice() { + [parent_args @ .., resume_ty, yield_ty, return_ty] => { + rustc_type_ir::CoroutineArgsParts { + parent_args: GenericArgs::new_from_iter(interner, parent_args.iter().cloned()), + kind_ty: Ty::new_unit(interner), + resume_ty: resume_ty.expect_ty(), + yield_ty: yield_ty.expect_ty(), + return_ty: return_ty.expect_ty(), + witness: Ty::new_unit(interner), + tupled_upvars_ty: Ty::new_unit(interner), + } + } + _ => panic!("GenericArgs were likely not for a Coroutine."), + } + } +} + +pub fn mk_param<'db>(index: u32, name: &Symbol, kind: GenericParamDefKind) -> GenericArg<'db> { + let name = name.clone(); + match kind { + GenericParamDefKind::Lifetime => { + Region::new_early_param(DbInterner::conjure(), EarlyParamRegion { index }).into() + } + GenericParamDefKind::Type => Ty::new_param(DbInterner::conjure(), index, name).into(), + GenericParamDefKind::Const => { + Const::new_param(DbInterner::conjure(), ParamConst { index }).into() + } + } +} + +pub fn error_for_param_kind<'db>( + kind: GenericParamDefKind, + interner: DbInterner<'db>, +) -> GenericArg<'db> { + match kind { + GenericParamDefKind::Lifetime => Region::error(interner).into(), + GenericParamDefKind::Type => Ty::new_error(interner, ErrorGuaranteed).into(), + GenericParamDefKind::Const => Const::error(interner).into(), + } +} + +impl<'db> IntoKind for Term<'db> { + type Kind = TermKind>; + + fn kind(self) -> Self::Kind { + match self { + Term::Ty(ty) => TermKind::Ty(ty), + Term::Const(c) => TermKind::Const(c), + } + } +} + +impl<'db> From> for Term<'db> { + fn from(value: Ty<'db>) -> Self { + Self::Ty(value) + } +} + +impl<'db> From> for Term<'db> { + fn from(value: Const<'db>) -> Self { + Self::Const(value) + } +} + +impl<'db> TypeVisitable> for Term<'db> { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + match self { + Term::Ty(ty) => ty.visit_with(visitor), + Term::Const(ct) => ct.visit_with(visitor), + } + } +} + +impl<'db> TypeFoldable> for Term<'db> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + match self.kind() { + TermKind::Ty(ty) => ty.try_fold_with(folder).map(Into::into), + TermKind::Const(ct) => ct.try_fold_with(folder).map(Into::into), + } + } + fn fold_with>>(self, folder: &mut F) -> Self { + match self.kind() { + TermKind::Ty(ty) => ty.fold_with(folder).into(), + TermKind::Const(ct) => ct.fold_with(folder).into(), + } + } +} + +impl<'db> Relate> for Term<'db> { + fn relate>>( + relation: &mut R, + a: Self, + b: Self, + ) -> rustc_type_ir::relate::RelateResult, Self> { + match (a.kind(), b.kind()) { + (TermKind::Ty(a_ty), TermKind::Ty(b_ty)) => Ok(relation.relate(a_ty, b_ty)?.into()), + (TermKind::Const(a_ct), TermKind::Const(b_ct)) => { + Ok(relation.relate(a_ct, b_ct)?.into()) + } + (TermKind::Ty(unpacked), x) => { + unreachable!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) + } + (TermKind::Const(unpacked), x) => { + unreachable!("impossible case reached: can't relate: {:?} with {:?}", unpacked, x) + } + } + } +} + +impl<'db> rustc_type_ir::inherent::Term> for Term<'db> {} + +#[derive(Clone, Eq, PartialEq, Debug)] +pub enum TermVid { + Ty(TyVid), + Const(ConstVid), +} + +impl From for TermVid { + fn from(value: TyVid) -> Self { + TermVid::Ty(value) + } +} + +impl From for TermVid { + fn from(value: ConstVid) -> Self { + TermVid::Const(value) + } +} + +impl<'db> DbInterner<'db> { + pub(super) fn mk_args(self, args: &[GenericArg<'db>]) -> GenericArgs<'db> { + GenericArgs::new_from_iter(self, args.iter().cloned()) + } + + pub(super) fn mk_args_from_iter(self, iter: I) -> T::Output + where + I: Iterator, + T: rustc_type_ir::CollectAndApply, GenericArgs<'db>>, + { + T::collect_and_apply(iter, |xs| self.mk_args(xs)) + } + + pub(super) fn check_args_compatible(self, def_id: SolverDefId, args: GenericArgs<'db>) -> bool { + // TODO + true + } + + pub(super) fn debug_assert_args_compatible(self, def_id: SolverDefId, args: GenericArgs<'db>) { + // TODO + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generics.rs new file mode 100644 index 000000000000..48f5e73f2562 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generics.rs @@ -0,0 +1,147 @@ +//! Things related to generics in the next-trait-solver. + +use hir_def::{ + ConstParamId, GenericDefId, GenericParamId, ItemContainerId, LifetimeParamId, Lookup, + TypeOrConstParamId, TypeParamId, + db::DefDatabase, + expr_store::ExpressionStore, + hir::generics::{ + GenericParamDataRef, GenericParams, LifetimeParamData, LocalLifetimeParamId, + LocalTypeOrConstParamId, TypeOrConstParamData, TypeParamData, TypeParamProvenance, + WherePredicate, + }, +}; +use hir_expand::name::Name; +use intern::Symbol; +use la_arena::Arena; +use rustc_type_ir::inherent::Ty as _; +use triomphe::Arc; + +use crate::{db::HirDatabase, generics::parent_generic_def, next_solver::Ty}; + +use super::{Const, EarlyParamRegion, ErrorGuaranteed, ParamConst, Region, SolverDefId}; + +use super::{DbInterner, GenericArg}; + +pub(crate) fn generics(db: &dyn HirDatabase, def: SolverDefId) -> Generics { + let mk_lt = |(index, (_, lt)): (usize, (_, &LifetimeParamData))| { + let name = lt.name.symbol().clone(); + let index = index as u32; + let kind = GenericParamDefKind::Lifetime; + GenericParamDef { name, index, kind } + }; + let mk_ty = |len_lt, (index, p): (usize, &TypeOrConstParamData)| { + let name = p + .name() + .map(|n| n.symbol().clone()) + .unwrap_or_else(|| Name::missing().symbol().clone()); + let index = (len_lt + index) as u32; + let kind = match p { + TypeOrConstParamData::TypeParamData(_) => GenericParamDefKind::Type, + TypeOrConstParamData::ConstParamData(_) => GenericParamDefKind::Const, + }; + GenericParamDef { name, index, kind } + }; + let own_params_for_generic_params = |params: &GenericParams| { + if params.trait_self_param().is_some() { + let len_lt = params.len_lifetimes() + 1; + params + .iter_type_or_consts() + .take(1) + .enumerate() + .map(|t| mk_ty(0, (t.0, t.1.1))) + .chain(params.iter_lt().enumerate().map(mk_lt)) + .chain( + params + .iter_type_or_consts() + .skip(1) + .enumerate() + .map(|t| mk_ty(len_lt, (t.0, t.1.1))), + ) + .collect() + } else { + let len_lt = params.len_lifetimes(); + params + .iter_lt() + .enumerate() + .map(mk_lt) + .chain( + params.iter_type_or_consts().enumerate().map(|t| mk_ty(len_lt, (t.0, t.1.1))), + ) + .collect() + } + }; + + let (parent, own_params) = match (def.try_into(), def) { + (Ok(def), _) => { + (parent_generic_def(db, def), own_params_for_generic_params(&db.generic_params(def))) + } + (_, SolverDefId::InternedOpaqueTyId(id)) => { + match db.lookup_intern_impl_trait_id(id) { + crate::ImplTraitId::ReturnTypeImplTrait(function_id, _) => { + // The opaque type itself does not have generics - only the parent function + (Some(GenericDefId::FunctionId(function_id)), vec![]) + } + crate::ImplTraitId::TypeAliasImplTrait(type_alias_id, _) => ( + None, + own_params_for_generic_params( + &db.generic_params(GenericDefId::TypeAliasId(type_alias_id)), + ), + ), + crate::ImplTraitId::AsyncBlockTypeImplTrait(def, _) => { + let param = TypeOrConstParamData::TypeParamData(TypeParamData { + name: None, + default: None, + provenance: TypeParamProvenance::TypeParamList, + }); + // Yes, there is a parent but we don't include it in the generics + (None, vec![mk_ty(0, (0, ¶m))]) + } + } + } + _ => panic!("No generics for {def:?}"), + }; + let parent_generics = parent.map(|def| Box::new(generics(db, def.into()))); + + Generics { + parent, + parent_count: parent_generics.map_or(0, |g| g.parent_count + g.own_params.len()), + own_params, + } +} + +#[derive(Debug)] +pub struct Generics { + pub parent: Option, + pub parent_count: usize, + pub own_params: Vec, +} + +#[derive(Debug)] +pub struct GenericParamDef { + pub(crate) name: Symbol, + //def_id: GenericDefId, + index: u32, + pub(crate) kind: GenericParamDefKind, +} + +impl GenericParamDef { + /// Returns the index of the param on the self generics only + /// (i.e. not including parent generics) + pub fn index(&self) -> u32 { + self.index + } +} + +#[derive(Copy, Clone, Debug)] +pub enum GenericParamDefKind { + Lifetime, + Type, + Const, +} + +impl<'db> rustc_type_ir::inherent::GenericsOf> for Generics { + fn count(&self) -> usize { + self.parent_count + self.own_params.len() + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/at.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/at.rs new file mode 100644 index 000000000000..d64c7ed626eb --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/at.rs @@ -0,0 +1,333 @@ +//! A nice interface for working with the infcx. The basic idea is to +//! do `infcx.at(cause, param_env)`, which sets the "cause" of the +//! operation as well as the surrounding parameter environment. Then +//! you can do something like `.sub(a, b)` or `.eq(a, b)` to create a +//! subtype or equality relationship respectively. The first argument +//! is always the "expected" output from the POV of diagnostics. +//! +//! Examples: +//! ```ignore (fragment) +//! infcx.at(cause, param_env).sub(a, b) +//! // requires that `a <: b`, with `a` considered the "expected" type +//! +//! infcx.at(cause, param_env).sup(a, b) +//! // requires that `b <: a`, with `a` considered the "expected" type +//! +//! infcx.at(cause, param_env).eq(a, b) +//! // requires that `a == b`, with `a` considered the "expected" type +//! ``` +//! For finer-grained control, you can also do use `trace`: +//! ```ignore (fragment) +//! infcx.at(...).trace(a, b).sub(&c, &d) +//! ``` +//! This will set `a` and `b` as the "root" values for +//! error-reporting, but actually operate on `c` and `d`. This is +//! sometimes useful when the types of `c` and `d` are not traceable +//! things. (That system should probably be refactored.) + +use rustc_type_ir::{ + FnSig, GenericArgKind, TypingMode, Variance, + error::ExpectedFound, + inherent::{IntoKind, Span as _}, + relate::{Relate, TypeRelation, solver_relating::RelateExt}, +}; + +use crate::next_solver::{ + AliasTerm, AliasTy, Binder, Const, DbInterner, GenericArg, Goal, ParamEnv, + PolyExistentialProjection, PolyExistentialTraitRef, PolyFnSig, Predicate, Region, Span, Term, + TraitRef, Ty, +}; + +use super::{ + InferCtxt, InferOk, InferResult, TypeTrace, ValuePairs, + traits::{Obligation, ObligationCause}, +}; + +/// Whether we should define opaque types or just treat them opaquely. +/// +/// Currently only used to prevent predicate matching from matching anything +/// against opaque types. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum DefineOpaqueTypes { + Yes, + No, +} + +#[derive(Clone, Copy)] +pub struct At<'a, 'db> { + pub infcx: &'a InferCtxt<'db>, + pub cause: &'a ObligationCause, + pub param_env: ParamEnv<'db>, +} + +impl<'db> InferCtxt<'db> { + #[inline] + pub fn at<'a>(&'a self, cause: &'a ObligationCause, param_env: ParamEnv<'db>) -> At<'a, 'db> { + At { infcx: self, cause, param_env } + } + + /// Forks the inference context, creating a new inference context with the same inference + /// variables in the same state. This can be used to "branch off" many tests from the same + /// common state. + pub fn fork(&self) -> Self { + Self { + interner: self.interner, + typing_mode: self.typing_mode, + inner: self.inner.clone(), + tainted_by_errors: self.tainted_by_errors.clone(), + universe: self.universe.clone(), + } + } + + /// Forks the inference context, creating a new inference context with the same inference + /// variables in the same state, except possibly changing the intercrate mode. This can be + /// used to "branch off" many tests from the same common state. Used in negative coherence. + pub fn fork_with_typing_mode(&self, typing_mode: TypingMode>) -> Self { + // Unlike `fork`, this invalidates all cache entries as they may depend on the + // typing mode. + + Self { + interner: self.interner, + typing_mode, + inner: self.inner.clone(), + tainted_by_errors: self.tainted_by_errors.clone(), + universe: self.universe.clone(), + } + } +} + +pub trait ToTrace<'db>: Relate> { + fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db>; +} + +impl<'a, 'db> At<'a, 'db> { + /// Makes `actual <: expected`. For example, if type-checking a + /// call like `foo(x)`, where `foo: fn(i32)`, you might have + /// `sup(i32, x)`, since the "expected" type is the type that + /// appears in the signature. + pub fn sup( + self, + define_opaque_types: DefineOpaqueTypes, + expected: T, + actual: T, + ) -> InferResult<'db, ()> + where + T: ToTrace<'db>, + { + RelateExt::relate( + self.infcx, + self.param_env, + expected, + Variance::Contravariant, + actual, + Span::dummy(), + ) + .map(|goals| self.goals_to_obligations(goals)) + } + + /// Makes `expected <: actual`. + pub fn sub( + self, + define_opaque_types: DefineOpaqueTypes, + expected: T, + actual: T, + ) -> InferResult<'db, ()> + where + T: ToTrace<'db>, + { + RelateExt::relate( + self.infcx, + self.param_env, + expected, + Variance::Covariant, + actual, + Span::dummy(), + ) + .map(|goals| self.goals_to_obligations(goals)) + } + + /// Makes `expected == actual`. + pub fn eq( + self, + define_opaque_types: DefineOpaqueTypes, + expected: T, + actual: T, + ) -> InferResult<'db, ()> + where + T: ToTrace<'db>, + { + self.eq_trace( + define_opaque_types, + ToTrace::to_trace(self.cause, expected, actual), + expected, + actual, + ) + } + + /// Makes `expected == actual`. + pub fn eq_trace( + self, + define_opaque_types: DefineOpaqueTypes, + trace: TypeTrace<'db>, + expected: T, + actual: T, + ) -> InferResult<'db, ()> + where + T: Relate>, + { + RelateExt::relate( + self.infcx, + self.param_env, + expected, + Variance::Invariant, + actual, + Span::dummy(), + ) + .map(|goals| self.goals_to_obligations(goals)) + } + + pub fn relate( + self, + define_opaque_types: DefineOpaqueTypes, + expected: T, + variance: Variance, + actual: T, + ) -> InferResult<'db, ()> + where + T: ToTrace<'db>, + { + match variance { + Variance::Covariant => self.sub(define_opaque_types, expected, actual), + Variance::Invariant => self.eq(define_opaque_types, expected, actual), + Variance::Contravariant => self.sup(define_opaque_types, expected, actual), + + // We could make this make sense but it's not readily + // exposed and I don't feel like dealing with it. Note + // that bivariance in general does a bit more than just + // *nothing*, it checks that the types are the same + // "modulo variance" basically. + Variance::Bivariant => panic!("Bivariant given to `relate()`"), + } + } + + fn goals_to_obligations(&self, goals: Vec>>) -> InferOk<'db, ()> { + InferOk { + value: (), + obligations: goals + .into_iter() + .map(|goal| { + Obligation::new( + self.infcx.interner, + self.cause.clone(), + goal.param_env, + goal.predicate, + ) + }) + .collect(), + } + } +} + +impl<'db> ToTrace<'db> for Ty<'db> { + fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { + TypeTrace { + cause: cause.clone(), + values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())), + } + } +} + +impl<'db> ToTrace<'db> for Region<'db> { + fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { + TypeTrace { cause: cause.clone(), values: ValuePairs::Regions(ExpectedFound::new(a, b)) } + } +} + +impl<'db> ToTrace<'db> for Const<'db> { + fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { + TypeTrace { + cause: cause.clone(), + values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())), + } + } +} + +impl<'db> ToTrace<'db> for GenericArg<'db> { + fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { + TypeTrace { + cause: cause.clone(), + values: match (a.kind(), b.kind()) { + (GenericArgKind::Lifetime(a), GenericArgKind::Lifetime(b)) => { + ValuePairs::Regions(ExpectedFound::new(a, b)) + } + (GenericArgKind::Type(a), GenericArgKind::Type(b)) => { + ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())) + } + (GenericArgKind::Const(a), GenericArgKind::Const(b)) => { + ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())) + } + _ => panic!("relating different kinds: {a:?} {b:?}"), + }, + } + } +} + +impl<'db> ToTrace<'db> for Term<'db> { + fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { + TypeTrace { cause: cause.clone(), values: ValuePairs::Terms(ExpectedFound::new(a, b)) } + } +} + +impl<'db> ToTrace<'db> for TraitRef<'db> { + fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { + TypeTrace { cause: cause.clone(), values: ValuePairs::TraitRefs(ExpectedFound::new(a, b)) } + } +} + +impl<'db> ToTrace<'db> for AliasTy<'db> { + fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { + TypeTrace { + cause: cause.clone(), + values: ValuePairs::Aliases(ExpectedFound::new(a.into(), b.into())), + } + } +} + +impl<'db> ToTrace<'db> for AliasTerm<'db> { + fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { + TypeTrace { cause: cause.clone(), values: ValuePairs::Aliases(ExpectedFound::new(a, b)) } + } +} + +impl<'db> ToTrace<'db> for FnSig> { + fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { + TypeTrace { + cause: cause.clone(), + values: ValuePairs::PolySigs(ExpectedFound::new(Binder::dummy(a), Binder::dummy(b))), + } + } +} + +impl<'db> ToTrace<'db> for PolyFnSig<'db> { + fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { + TypeTrace { cause: cause.clone(), values: ValuePairs::PolySigs(ExpectedFound::new(a, b)) } + } +} + +impl<'db> ToTrace<'db> for PolyExistentialTraitRef<'db> { + fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { + TypeTrace { + cause: cause.clone(), + values: ValuePairs::ExistentialTraitRef(ExpectedFound::new(a, b)), + } + } +} + +impl<'db> ToTrace<'db> for PolyExistentialProjection<'db> { + fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> { + TypeTrace { + cause: cause.clone(), + values: ValuePairs::ExistentialProjection(ExpectedFound::new(a, b)), + } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs new file mode 100644 index 000000000000..0448f03463e4 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs @@ -0,0 +1,106 @@ +//! This module contains code to instantiate new values into a +//! `Canonical<'tcx, T>`. +//! +//! For an overview of what canonicalization is and how it fits into +//! rustc, check out the [chapter in the rustc dev guide][c]. +//! +//! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html + +use crate::next_solver::{ + AliasTy, Binder, BoundRegion, BoundTy, Canonical, CanonicalVarValues, Const, DbInterner, Goal, + ParamEnv, Predicate, PredicateKind, Region, Ty, TyKind, + fold::FnMutDelegate, + infer::{ + DefineOpaqueTypes, InferCtxt, TypeTrace, + traits::{Obligation, PredicateObligations}, + }, +}; +use rustc_type_ir::{ + AliasRelationDirection, AliasTyKind, BoundVar, GenericArgKind, InferTy, TypeFoldable, Upcast, + Variance, + inherent::{IntoKind, SliceLike}, + relate::{ + Relate, TypeRelation, VarianceDiagInfo, + combine::{super_combine_consts, super_combine_tys}, + }, +}; + +pub trait CanonicalExt<'db, V> { + fn instantiate(&self, tcx: DbInterner<'db>, var_values: &CanonicalVarValues<'db>) -> V + where + V: TypeFoldable>; + fn instantiate_projected( + &self, + tcx: DbInterner<'db>, + var_values: &CanonicalVarValues<'db>, + projection_fn: impl FnOnce(&V) -> T, + ) -> T + where + T: TypeFoldable>; +} + +/// FIXME(-Znext-solver): This or public because it is shared with the +/// new trait solver implementation. We should deduplicate canonicalization. +impl<'db, V> CanonicalExt<'db, V> for Canonical<'db, V> { + /// Instantiate the wrapped value, replacing each canonical value + /// with the value given in `var_values`. + fn instantiate(&self, tcx: DbInterner<'db>, var_values: &CanonicalVarValues<'db>) -> V + where + V: TypeFoldable>, + { + self.instantiate_projected(tcx, var_values, |value| value.clone()) + } + + /// Allows one to apply a instantiation to some subset of + /// `self.value`. Invoke `projection_fn` with `self.value` to get + /// a value V that is expressed in terms of the same canonical + /// variables bound in `self` (usually this extracts from subset + /// of `self`). Apply the instantiation `var_values` to this value + /// V, replacing each of the canonical variables. + fn instantiate_projected( + &self, + tcx: DbInterner<'db>, + var_values: &CanonicalVarValues<'db>, + projection_fn: impl FnOnce(&V) -> T, + ) -> T + where + T: TypeFoldable>, + { + assert_eq!(self.variables.len(), var_values.len()); + let value = projection_fn(&self.value); + instantiate_value(tcx, var_values, value) + } +} + +/// Instantiate the values from `var_values` into `value`. `var_values` +/// must be values for the set of canonical variables that appear in +/// `value`. +pub(super) fn instantiate_value<'db, T>( + tcx: DbInterner<'db>, + var_values: &CanonicalVarValues<'db>, + value: T, +) -> T +where + T: TypeFoldable>, +{ + if var_values.var_values.is_empty() { + value + } else { + let delegate = FnMutDelegate { + regions: &mut |br: BoundRegion| match var_values[br.var].kind() { + GenericArgKind::Lifetime(l) => l, + r => panic!("{br:?} is a region but value is {r:?}"), + }, + types: &mut |bound_ty: BoundTy| match var_values[bound_ty.var].kind() { + GenericArgKind::Type(ty) => ty, + r => panic!("{bound_ty:?} is a type but value is {r:?}"), + }, + consts: &mut |bound_ct: BoundVar| match var_values[bound_ct].kind() { + GenericArgKind::Const(ct) => ct, + c => panic!("{bound_ct:?} is a const but value is {c:?}"), + }, + }; + + tcx.replace_escaping_bound_vars_uncached(value, delegate) + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs new file mode 100644 index 000000000000..4457e1340d58 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs @@ -0,0 +1,156 @@ +//! **Canonicalization** is the key to constructing a query in the +//! middle of type inference. Ordinarily, it is not possible to store +//! types from type inference in query keys, because they contain +//! references to inference variables whose lifetimes are too short +//! and so forth. Canonicalizing a value T1 using `canonicalize_query` +//! produces two things: +//! +//! - a value T2 where each unbound inference variable has been +//! replaced with a **canonical variable**; +//! - a map M (of type `CanonicalVarValues`) from those canonical +//! variables back to the original. +//! +//! We can then do queries using T2. These will give back constraints +//! on the canonical variables which can be translated, using the map +//! M, into constraints in our source context. This process of +//! translating the results back is done by the +//! `instantiate_query_result` method. +//! +//! For a more detailed look at what is happening here, check +//! out the [chapter in the rustc dev guide][c]. +//! +//! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html + +use crate::next_solver::{ + AliasTy, Binder, Canonical, CanonicalVarValues, CanonicalVars, Const, DbInterner, GenericArg, + Goal, ParamEnv, PlaceholderConst, PlaceholderRegion, PlaceholderTy, Predicate, PredicateKind, + Region, Ty, TyKind, + infer::{ + DefineOpaqueTypes, InferCtxt, TypeTrace, + traits::{Obligation, PredicateObligations}, + }, +}; +use instantiate::CanonicalExt; +use rustc_index::IndexVec; +use rustc_type_ir::{ + AliasRelationDirection, AliasTyKind, CanonicalTyVarKind, CanonicalVarKind, InferTy, + TypeFoldable, UniverseIndex, Upcast, Variance, + inherent::{SliceLike, Ty as _}, + relate::{ + Relate, TypeRelation, VarianceDiagInfo, + combine::{super_combine_consts, super_combine_tys}, + }, +}; + +pub mod instantiate; + +impl<'db> InferCtxt<'db> { + /// Creates an instantiation S for the canonical value with fresh inference + /// variables and placeholders then applies it to the canonical value. + /// Returns both the instantiated result *and* the instantiation S. + /// + /// This can be invoked as part of constructing an + /// inference context at the start of a query (see + /// `InferCtxtBuilder::build_with_canonical`). It basically + /// brings the canonical value "into scope" within your new infcx. + /// + /// At the end of processing, the instantiation S (once + /// canonicalized) then represents the values that you computed + /// for each of the canonical inputs to your query. + pub fn instantiate_canonical( + &self, + canonical: &Canonical<'db, T>, + ) -> (T, CanonicalVarValues<'db>) + where + T: TypeFoldable>, + { + // For each universe that is referred to in the incoming + // query, create a universe in our local inference context. In + // practice, as of this writing, all queries have no universes + // in them, so this code has no effect, but it is looking + // forward to the day when we *do* want to carry universes + // through into queries. + // + // Instantiate the root-universe content into the current universe, + // and create fresh universes for the higher universes. + let universes: IndexVec = std::iter::once(self.universe()) + .chain((1..=canonical.max_universe.as_u32()).map(|_| self.create_next_universe())) + .collect(); + + let canonical_inference_vars = + self.instantiate_canonical_vars(canonical.variables, |ui| universes[ui]); + let result = canonical.instantiate(self.interner, &canonical_inference_vars); + (result, canonical_inference_vars) + } + + /// Given the "infos" about the canonical variables from some + /// canonical, creates fresh variables with the same + /// characteristics (see `instantiate_canonical_var` for + /// details). You can then use `instantiate` to instantiate the + /// canonical variable with these inference variables. + fn instantiate_canonical_vars( + &self, + variables: CanonicalVars<'db>, + universe_map: impl Fn(UniverseIndex) -> UniverseIndex, + ) -> CanonicalVarValues<'db> { + CanonicalVarValues { + var_values: self.interner.mk_args_from_iter( + variables.iter().map(|info| self.instantiate_canonical_var(info, &universe_map)), + ), + } + } + + /// Given the "info" about a canonical variable, creates a fresh + /// variable for it. If this is an existentially quantified + /// variable, then you'll get a new inference variable; if it is a + /// universally quantified variable, you get a placeholder. + /// + /// FIXME(-Znext-solver): This is public because it's used by the + /// new trait solver which has a different canonicalization routine. + /// We should somehow deduplicate all of this. + pub fn instantiate_canonical_var( + &self, + cv_info: CanonicalVarKind>, + universe_map: impl Fn(UniverseIndex) -> UniverseIndex, + ) -> GenericArg<'db> { + match cv_info { + CanonicalVarKind::Ty(ty_kind) => { + let ty = match ty_kind { + CanonicalTyVarKind::General(ui) => { + self.next_ty_var_in_universe(universe_map(ui)) + } + + CanonicalTyVarKind::Int => self.next_int_var(), + + CanonicalTyVarKind::Float => self.next_float_var(), + }; + ty.into() + } + + CanonicalVarKind::PlaceholderTy(PlaceholderTy { universe, bound }) => { + let universe_mapped = universe_map(universe); + let placeholder_mapped = PlaceholderTy { universe: universe_mapped, bound }; + Ty::new_placeholder(self.interner, placeholder_mapped).into() + } + + CanonicalVarKind::Region(ui) => { + self.next_region_var_in_universe(universe_map(ui)).into() + } + + CanonicalVarKind::PlaceholderRegion(PlaceholderRegion { universe, bound }) => { + let universe_mapped = universe_map(universe); + let placeholder_mapped: crate::next_solver::Placeholder< + crate::next_solver::BoundRegion, + > = PlaceholderRegion { universe: universe_mapped, bound }; + Region::new_placeholder(self.interner, placeholder_mapped).into() + } + + CanonicalVarKind::Const(ui) => self.next_const_var_in_universe(universe_map(ui)).into(), + CanonicalVarKind::PlaceholderConst(PlaceholderConst { universe, bound }) => { + let universe_mapped = universe_map(universe); + let placeholder_mapped = PlaceholderConst { universe: universe_mapped, bound }; + Const::new_placeholder(self.interner, placeholder_mapped).into() + } + } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs new file mode 100644 index 000000000000..93fd6eeab34e --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs @@ -0,0 +1,316 @@ +//! Definition of `InferCtxtLike` from the librarified type layer. + +use rustc_type_ir::{ + ConstVid, FloatVarValue, FloatVid, GenericArgKind, InferConst, InferTy, IntTy, IntVarValue, + IntVid, RegionVid, TyVid, TypeFoldable, TypingMode, UniverseIndex, + inherent::{Const as _, IntoKind, Span as _, Ty as _}, + relate::combine::PredicateEmittingRelation, +}; + +use crate::next_solver::{ + Binder, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArgs, OpaqueTypeKey, ParamEnv, + Region, SolverDefId, Span, Ty, TyKind, + infer::opaque_types::{OpaqueHiddenType, table::OpaqueTypeStorageEntries}, +}; + +use super::{BoundRegionConversionTime, InferCtxt, relate::RelateResult, traits::ObligationCause}; + +impl<'db> rustc_type_ir::InferCtxtLike for InferCtxt<'db> { + type Interner = DbInterner<'db>; + + fn cx(&self) -> DbInterner<'db> { + self.interner + } + + fn next_trait_solver(&self) -> bool { + true + } + + fn typing_mode(&self) -> TypingMode> { + self.typing_mode() + } + + fn universe(&self) -> UniverseIndex { + self.universe() + } + + fn create_next_universe(&self) -> UniverseIndex { + self.create_next_universe() + } + + fn universe_of_ty(&self, vid: TyVid) -> Option { + self.probe_ty_var(vid).err() + } + + fn universe_of_lt(&self, lt: RegionVid) -> Option { + self.inner.borrow_mut().unwrap_region_constraints().probe_value(lt).err() + } + + fn universe_of_ct(&self, ct: ConstVid) -> Option { + self.probe_const_var(ct).err() + } + + fn root_ty_var(&self, var: TyVid) -> TyVid { + self.root_var(var) + } + + fn root_const_var(&self, var: ConstVid) -> ConstVid { + self.root_const_var(var) + } + + fn opportunistic_resolve_ty_var(&self, vid: TyVid) -> Ty<'db> { + match self.probe_ty_var(vid) { + Ok(ty) => ty, + Err(_) => Ty::new_var(self.interner, self.root_var(vid)), + } + } + + fn opportunistic_resolve_int_var(&self, vid: IntVid) -> Ty<'db> { + self.opportunistic_resolve_int_var(vid) + } + + fn opportunistic_resolve_float_var(&self, vid: FloatVid) -> Ty<'db> { + self.opportunistic_resolve_float_var(vid) + } + + fn opportunistic_resolve_ct_var(&self, vid: ConstVid) -> Const<'db> { + match self.probe_const_var(vid) { + Ok(ct) => ct, + Err(_) => Const::new_var(self.interner, self.root_const_var(vid)), + } + } + + fn opportunistic_resolve_lt_var(&self, vid: RegionVid) -> Region<'db> { + self.inner + .borrow_mut() + .unwrap_region_constraints() + .opportunistic_resolve_var(self.interner, vid) + } + + fn is_changed_arg(&self, arg: ::GenericArg) -> bool { + match arg.kind() { + GenericArgKind::Lifetime(_) => { + // Lifetimes should not change affect trait selection. + false + } + GenericArgKind::Type(ty) => { + if let TyKind::Infer(infer_ty) = ty.kind() { + match infer_ty { + InferTy::TyVar(vid) => { + !self.probe_ty_var(vid).is_err_and(|_| self.root_var(vid) == vid) + } + InferTy::IntVar(vid) => { + let mut inner = self.inner.borrow_mut(); + !matches!( + inner.int_unification_table().probe_value(vid), + IntVarValue::Unknown + if inner.int_unification_table().find(vid) == vid + ) + } + InferTy::FloatVar(vid) => { + let mut inner = self.inner.borrow_mut(); + !matches!( + inner.float_unification_table().probe_value(vid), + FloatVarValue::Unknown + if inner.float_unification_table().find(vid) == vid + ) + } + InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_) => { + true + } + } + } else { + true + } + } + GenericArgKind::Const(ct) => { + if let ConstKind::Infer(infer_ct) = ct.kind() { + match infer_ct { + InferConst::Var(vid) => !self + .probe_const_var(vid) + .is_err_and(|_| self.root_const_var(vid) == vid), + InferConst::Fresh(_) => true, + } + } else { + true + } + } + } + } + + fn next_ty_infer(&self) -> Ty<'db> { + self.next_ty_var() + } + + fn next_region_infer(&self) -> ::Region { + self.next_region_var() + } + + fn next_const_infer(&self) -> Const<'db> { + self.next_const_var() + } + + fn fresh_args_for_item(&self, def_id: SolverDefId) -> GenericArgs<'db> { + self.fresh_args_for_item(def_id) + } + + fn instantiate_binder_with_infer> + Clone>( + &self, + value: Binder<'db, T>, + ) -> T { + self.instantiate_binder_with_fresh_vars(BoundRegionConversionTime::HigherRankedType, value) + } + + fn enter_forall> + Clone, U>( + &self, + value: Binder<'db, T>, + f: impl FnOnce(T) -> U, + ) -> U { + self.enter_forall(value, f) + } + + fn equate_ty_vids_raw(&self, a: rustc_type_ir::TyVid, b: rustc_type_ir::TyVid) { + self.inner.borrow_mut().type_variables().equate(a, b); + } + + fn equate_int_vids_raw(&self, a: rustc_type_ir::IntVid, b: rustc_type_ir::IntVid) { + self.inner.borrow_mut().int_unification_table().union(a, b); + } + + fn equate_float_vids_raw(&self, a: rustc_type_ir::FloatVid, b: rustc_type_ir::FloatVid) { + self.inner.borrow_mut().float_unification_table().union(a, b); + } + + fn equate_const_vids_raw(&self, a: rustc_type_ir::ConstVid, b: rustc_type_ir::ConstVid) { + self.inner.borrow_mut().const_unification_table().union(a, b); + } + + fn instantiate_ty_var_raw>( + &self, + relation: &mut R, + target_is_expected: bool, + target_vid: rustc_type_ir::TyVid, + instantiation_variance: rustc_type_ir::Variance, + source_ty: Ty<'db>, + ) -> RelateResult<'db, ()> { + self.instantiate_ty_var( + relation, + target_is_expected, + target_vid, + instantiation_variance, + source_ty, + ) + } + + fn instantiate_int_var_raw( + &self, + vid: rustc_type_ir::IntVid, + value: rustc_type_ir::IntVarValue, + ) { + self.inner.borrow_mut().int_unification_table().union_value(vid, value); + } + + fn instantiate_float_var_raw( + &self, + vid: rustc_type_ir::FloatVid, + value: rustc_type_ir::FloatVarValue, + ) { + self.inner.borrow_mut().float_unification_table().union_value(vid, value); + } + + fn instantiate_const_var_raw>( + &self, + relation: &mut R, + target_is_expected: bool, + target_vid: rustc_type_ir::ConstVid, + source_ct: Const<'db>, + ) -> RelateResult<'db, ()> { + self.instantiate_const_var(relation, target_is_expected, target_vid, source_ct) + } + + fn set_tainted_by_errors(&self, e: ErrorGuaranteed) { + self.set_tainted_by_errors(e) + } + + fn shallow_resolve(&self, ty: Ty<'db>) -> Ty<'db> { + self.shallow_resolve(ty) + } + fn shallow_resolve_const(&self, ct: Const<'db>) -> Const<'db> { + self.shallow_resolve_const(ct) + } + + fn resolve_vars_if_possible(&self, value: T) -> T + where + T: TypeFoldable>, + { + self.resolve_vars_if_possible(value) + } + + fn probe(&self, probe: impl FnOnce() -> T) -> T { + self.probe(|_| probe()) + } + + fn sub_regions(&self, sub: Region<'db>, sup: Region<'db>, span: Span) { + self.inner.borrow_mut().unwrap_region_constraints().make_subregion(sub, sup); + } + + fn equate_regions(&self, a: Region<'db>, b: Region<'db>, span: Span) { + self.inner.borrow_mut().unwrap_region_constraints().make_eqregion(a, b); + } + + fn register_ty_outlives(&self, ty: Ty<'db>, r: Region<'db>, span: Span) { + //self.register_region_obligation_with_cause(ty, r, &ObligationCause::dummy_with_span(Span::dummy())); + } + + type OpaqueTypeStorageEntries = OpaqueTypeStorageEntries; + + fn opaque_types_storage_num_entries(&self) -> OpaqueTypeStorageEntries { + self.inner.borrow_mut().opaque_types().num_entries() + } + fn clone_opaque_types_lookup_table(&self) -> Vec<(OpaqueTypeKey<'db>, Ty<'db>)> { + self.inner.borrow_mut().opaque_types().iter_lookup_table().map(|(k, h)| (k, h.ty)).collect() + } + fn clone_duplicate_opaque_types(&self) -> Vec<(OpaqueTypeKey<'db>, Ty<'db>)> { + self.inner + .borrow_mut() + .opaque_types() + .iter_duplicate_entries() + .map(|(k, h)| (k, h.ty)) + .collect() + } + fn clone_opaque_types_added_since( + &self, + prev_entries: OpaqueTypeStorageEntries, + ) -> Vec<(OpaqueTypeKey<'db>, Ty<'db>)> { + self.inner + .borrow_mut() + .opaque_types() + .opaque_types_added_since(prev_entries) + .map(|(k, h)| (k, h.ty)) + .collect() + } + + fn register_hidden_type_in_storage( + &self, + opaque_type_key: OpaqueTypeKey<'db>, + hidden_ty: Ty<'db>, + _span: Span, + ) -> Option> { + self.register_hidden_type_in_storage(opaque_type_key, OpaqueHiddenType { ty: hidden_ty }) + } + fn add_duplicate_opaque_type( + &self, + opaque_type_key: OpaqueTypeKey<'db>, + hidden_ty: Ty<'db>, + _span: Span, + ) { + self.inner + .borrow_mut() + .opaque_types() + .add_duplicate(opaque_type_key, OpaqueHiddenType { ty: hidden_ty }) + } + + fn reset_opaque_types(&self) { + let _ = self.take_opaque_types(); + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs new file mode 100644 index 000000000000..19ae76f7e358 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs @@ -0,0 +1,1066 @@ +//! Infer context the next-trait-solver. + +use std::cell::{Cell, RefCell}; +use std::fmt; +use std::sync::Arc; + +pub use BoundRegionConversionTime::*; +pub use at::DefineOpaqueTypes; +use ena::undo_log::UndoLogs; +use ena::unify as ut; +use intern::Symbol; +use opaque_types::{OpaqueHiddenType, OpaqueTypeStorage}; +use region_constraints::{ + GenericKind, RegionConstraintCollector, RegionConstraintStorage, UndoLog, VarInfos, VerifyBound, +}; +pub use relate::StructurallyRelateAliases; +pub use relate::combine::PredicateEmittingRelation; +use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_pattern_analysis::Captures; +use rustc_type_ir::error::{ExpectedFound, TypeError}; +use rustc_type_ir::inherent::{ + Const as _, GenericArg as _, GenericArgs as _, IntoKind, ParamEnv as _, SliceLike, Term as _, + Ty as _, +}; +use rustc_type_ir::{ + BoundVar, ClosureKind, ConstVid, FloatTy, FloatVarValue, FloatVid, GenericArgKind, InferConst, + InferTy, IntTy, IntVarValue, IntVid, OutlivesPredicate, RegionVid, TyVid, UniverseIndex, +}; +use rustc_type_ir::{TermKind, TypeVisitableExt}; +use rustc_type_ir::{TypeFoldable, TypeFolder, TypeSuperFoldable}; +use snapshot::undo_log::InferCtxtUndoLogs; +use tracing::{debug, instrument}; +use traits::{ObligationCause, PredicateObligations}; +use type_variable::TypeVariableOrigin; +use unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey}; + +use crate::next_solver::fold::BoundVarReplacerDelegate; +use crate::next_solver::infer::opaque_types::table::OpaqueTypeStorageEntries; +use crate::next_solver::{BoundRegion, BoundTy, BoundVarKind}; + +use super::generics::{GenericParamDef, GenericParamDefKind}; +use super::{ + AliasTerm, Binder, BoundRegionKind, CanonicalQueryInput, CanonicalVarValues, Const, ConstKind, + DbInterner, ErrorGuaranteed, FxIndexMap, GenericArg, GenericArgs, OpaqueTypeKey, ParamEnv, + PlaceholderRegion, PolyCoercePredicate, PolyExistentialProjection, PolyExistentialTraitRef, + PolyFnSig, PolyRegionOutlivesPredicate, PolySubtypePredicate, Predicate, Region, SolverDefId, + SubtypePredicate, Term, TraitPredicate, TraitRef, Ty, TyKind, TypingMode, +}; + +pub mod at; +pub mod canonical; +mod context; +mod opaque_types; +pub mod region_constraints; +pub mod relate; +pub mod resolve; +pub(crate) mod snapshot; +pub(crate) mod traits; +mod type_variable; +mod unify_key; + +/// `InferOk<'tcx, ()>` is used a lot. It may seem like a useless wrapper +/// around `PredicateObligations`, but it has one important property: +/// because `InferOk` is marked with `#[must_use]`, if you have a method +/// `InferCtxt::f` that returns `InferResult<()>` and you call it with +/// `infcx.f()?;` you'll get a warning about the obligations being discarded +/// without use, which is probably unintentional and has been a source of bugs +/// in the past. +#[must_use] +#[derive(Debug)] +pub struct InferOk<'db, T> { + pub value: T, + pub obligations: PredicateObligations<'db>, +} +pub type InferResult<'db, T> = Result, TypeError>>; + +pub(crate) type FixupResult = Result; // "fixup result" + +pub(crate) type UnificationTable<'a, 'db, T> = ut::UnificationTable< + ut::InPlace, &'a mut InferCtxtUndoLogs<'db>>, +>; + +/// This type contains all the things within `InferCtxt` that sit within a +/// `RefCell` and are involved with taking/rolling back snapshots. Snapshot +/// operations are hot enough that we want only one call to `borrow_mut` per +/// call to `start_snapshot` and `rollback_to`. +#[derive(Clone)] +pub struct InferCtxtInner<'db> { + pub(crate) undo_log: InferCtxtUndoLogs<'db>, + + /// We instantiate `UnificationTable` with `bounds` because the types + /// that might instantiate a general type variable have an order, + /// represented by its upper and lower bounds. + pub(crate) type_variable_storage: type_variable::TypeVariableStorage<'db>, + + /// Map from const parameter variable to the kind of const it represents. + pub(crate) const_unification_storage: ut::UnificationTableStorage>, + + /// Map from integral variable to the kind of integer it represents. + pub(crate) int_unification_storage: ut::UnificationTableStorage, + + /// Map from floating variable to the kind of float it represents. + pub(crate) float_unification_storage: ut::UnificationTableStorage, + + /// Tracks the set of region variables and the constraints between them. + /// + /// This is initially `Some(_)` but when + /// `resolve_regions_and_report_errors` is invoked, this gets set to `None` + /// -- further attempts to perform unification, etc., may fail if new + /// region constraints would've been added. + pub(crate) region_constraint_storage: Option>, + + /// A set of constraints that regionck must validate. + /// + /// Each constraint has the form `T:'a`, meaning "some type `T` must + /// outlive the lifetime 'a". These constraints derive from + /// instantiated type parameters. So if you had a struct defined + /// like the following: + /// ```ignore (illustrative) + /// struct Foo { ... } + /// ``` + /// In some expression `let x = Foo { ... }`, it will + /// instantiate the type parameter `T` with a fresh type `$0`. At + /// the same time, it will record a region obligation of + /// `$0: 'static`. This will get checked later by regionck. (We + /// can't generally check these things right away because we have + /// to wait until types are resolved.) + /// + /// These are stored in a map keyed to the id of the innermost + /// enclosing fn body / static initializer expression. This is + /// because the location where the obligation was incurred can be + /// relevant with respect to which sublifetime assumptions are in + /// place. The reason that we store under the fn-id, and not + /// something more fine-grained, is so that it is easier for + /// regionck to be sure that it has found *all* the region + /// obligations (otherwise, it's easy to fail to walk to a + /// particular node-id). + /// + /// Before running `resolve_regions_and_report_errors`, the creator + /// of the inference context is expected to invoke + /// [`InferCtxt::process_registered_region_obligations`] + /// for each body-id in this map, which will process the + /// obligations within. This is expected to be done 'late enough' + /// that all type inference variables have been bound and so forth. + pub(crate) region_obligations: Vec>, + + /// Caches for opaque type inference. + pub(crate) opaque_type_storage: OpaqueTypeStorage<'db>, +} + +impl<'db> InferCtxtInner<'db> { + fn new() -> InferCtxtInner<'db> { + InferCtxtInner { + undo_log: InferCtxtUndoLogs::default(), + + type_variable_storage: Default::default(), + const_unification_storage: Default::default(), + int_unification_storage: Default::default(), + float_unification_storage: Default::default(), + region_constraint_storage: Some(Default::default()), + region_obligations: vec![], + opaque_type_storage: Default::default(), + } + } + + #[inline] + pub fn region_obligations(&self) -> &[RegionObligation<'db>] { + &self.region_obligations + } + + #[inline] + fn try_type_variables_probe_ref( + &self, + vid: TyVid, + ) -> Option<&type_variable::TypeVariableValue<'db>> { + // Uses a read-only view of the unification table, this way we don't + // need an undo log. + self.type_variable_storage.eq_relations_ref().try_probe_value(vid) + } + + #[inline] + fn type_variables(&mut self) -> type_variable::TypeVariableTable<'_, 'db> { + self.type_variable_storage.with_log(&mut self.undo_log) + } + + #[inline] + pub(crate) fn opaque_types(&mut self) -> opaque_types::OpaqueTypeTable<'_, 'db> { + self.opaque_type_storage.with_log(&mut self.undo_log) + } + + #[inline] + fn int_unification_table(&mut self) -> UnificationTable<'_, 'db, IntVid> { + self.int_unification_storage.with_log(&mut self.undo_log) + } + + #[inline] + fn float_unification_table(&mut self) -> UnificationTable<'_, 'db, FloatVid> { + self.float_unification_storage.with_log(&mut self.undo_log) + } + + #[inline] + fn const_unification_table(&mut self) -> UnificationTable<'_, 'db, ConstVidKey<'db>> { + self.const_unification_storage.with_log(&mut self.undo_log) + } + + #[inline] + pub fn unwrap_region_constraints(&mut self) -> RegionConstraintCollector<'db, '_> { + self.region_constraint_storage + .as_mut() + .expect("region constraints already solved") + .with_log(&mut self.undo_log) + } +} + +pub struct InferCtxt<'db> { + pub interner: DbInterner<'db>, + + /// The mode of this inference context, see the struct documentation + /// for more details. + typing_mode: TypingMode<'db>, + + pub inner: RefCell>, + + /// When an error occurs, we want to avoid reporting "derived" + /// errors that are due to this original failure. We have this + /// flag that one can set whenever one creates a type-error that + /// is due to an error in a prior pass. + /// + /// Don't read this flag directly, call `is_tainted_by_errors()` + /// and `set_tainted_by_errors()`. + tainted_by_errors: Cell>, + + /// What is the innermost universe we have created? Starts out as + /// `UniverseIndex::root()` but grows from there as we enter + /// universal quantifiers. + /// + /// N.B., at present, we exclude the universal quantifiers on the + /// item we are type-checking, and just consider those names as + /// part of the root universe. So this would only get incremented + /// when we enter into a higher-ranked (`for<..>`) type or trait + /// bound. + universe: Cell, +} + +/// See the `error_reporting` module for more details. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum ValuePairs<'db> { + Regions(ExpectedFound>), + Terms(ExpectedFound>), + Aliases(ExpectedFound>), + TraitRefs(ExpectedFound>), + PolySigs(ExpectedFound>), + ExistentialTraitRef(ExpectedFound>), + ExistentialProjection(ExpectedFound>), +} + +impl<'db> ValuePairs<'db> { + pub fn ty(&self) -> Option<(Ty<'db>, Ty<'db>)> { + if let ValuePairs::Terms(ExpectedFound { expected, found }) = self + && let Some(expected) = expected.as_type() + && let Some(found) = found.as_type() + { + return Some((expected, found)); + } + None + } +} + +/// The trace designates the path through inference that we took to +/// encounter an error or subtyping constraint. +/// +/// See the `error_reporting` module for more details. +#[derive(Clone, Debug)] +pub struct TypeTrace<'db> { + pub cause: ObligationCause, + pub values: ValuePairs<'db>, +} + +/// Times when we replace bound regions with existentials: +#[derive(Clone, Copy, Debug)] +pub enum BoundRegionConversionTime { + /// when a fn is called + FnCall, + + /// when two higher-ranked types are compared + HigherRankedType, + + /// when projecting an associated type + AssocTypeProjection(SolverDefId), +} + +#[derive(Copy, Clone, Debug)] +pub struct FixupError { + unresolved: TyOrConstInferVar, +} + +impl fmt::Display for FixupError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use TyOrConstInferVar::*; + + match self.unresolved { + TyInt(_) => write!( + f, + "cannot determine the type of this integer; \ + add a suffix to specify the type explicitly" + ), + TyFloat(_) => write!( + f, + "cannot determine the type of this number; \ + add a suffix to specify the type explicitly" + ), + Ty(_) => write!(f, "unconstrained type"), + Const(_) => write!(f, "unconstrained const value"), + } + } +} + +/// See the `region_obligations` field for more information. +#[derive(Clone, Debug)] +pub struct RegionObligation<'db> { + pub sub_region: Region<'db>, + pub sup_type: Ty<'db>, +} + +/// Used to configure inference contexts before their creation. +pub struct InferCtxtBuilder<'db> { + interner: DbInterner<'db>, +} + +pub trait DbInternerInferExt<'db> { + fn infer_ctxt(self) -> InferCtxtBuilder<'db>; +} + +impl<'db> DbInternerInferExt<'db> for DbInterner<'db> { + fn infer_ctxt(self) -> InferCtxtBuilder<'db> { + InferCtxtBuilder { interner: self } + } +} + +impl<'db> InferCtxtBuilder<'db> { + /// Given a canonical value `C` as a starting point, create an + /// inference context that contains each of the bound values + /// within instantiated as a fresh variable. The `f` closure is + /// invoked with the new infcx, along with the instantiated value + /// `V` and a instantiation `S`. This instantiation `S` maps from + /// the bound values in `C` to their instantiated values in `V` + /// (in other words, `S(C) = V`). + pub fn build_with_canonical( + mut self, + input: &CanonicalQueryInput<'db, T>, + ) -> (InferCtxt<'db>, T, CanonicalVarValues<'db>) + where + T: TypeFoldable>, + { + let infcx = self.build(input.typing_mode); + let (value, args) = infcx.instantiate_canonical(&input.canonical); + (infcx, value, args) + } + + pub fn build(&mut self, typing_mode: TypingMode<'db>) -> InferCtxt<'db> { + let InferCtxtBuilder { interner } = *self; + InferCtxt { + interner, + typing_mode, + inner: RefCell::new(InferCtxtInner::new()), + tainted_by_errors: Cell::new(None), + universe: Cell::new(UniverseIndex::ROOT), + } + } +} + +impl<'db> InferOk<'db, ()> { + pub fn into_obligations(self) -> PredicateObligations<'db> { + self.obligations + } +} + +impl<'db> InferCtxt<'db> { + #[inline(always)] + pub fn typing_mode(&self) -> TypingMode<'db> { + self.typing_mode + } + + #[inline(always)] + pub fn typing_mode_unchecked(&self) -> TypingMode<'db> { + self.typing_mode + } + + pub fn unresolved_variables(&self) -> Vec> { + let mut inner = self.inner.borrow_mut(); + let mut vars: Vec> = inner + .type_variables() + .unresolved_variables() + .into_iter() + .map(|t| Ty::new_var(self.interner, t)) + .collect(); + vars.extend( + (0..inner.int_unification_table().len()) + .map(IntVid::from_usize) + .filter(|&vid| inner.int_unification_table().probe_value(vid).is_unknown()) + .map(|v| Ty::new_int_var(self.interner, v)), + ); + vars.extend( + (0..inner.float_unification_table().len()) + .map(FloatVid::from_usize) + .filter(|&vid| inner.float_unification_table().probe_value(vid).is_unknown()) + .map(|v| Ty::new_float_var(self.interner, v)), + ); + vars + } + + #[instrument(skip(self), level = "debug")] + pub fn sub_regions(&self, a: Region<'db>, b: Region<'db>) { + self.inner.borrow_mut().unwrap_region_constraints().make_subregion(a, b); + } + + /// Processes a `Coerce` predicate from the fulfillment context. + /// This is NOT the preferred way to handle coercion, which is to + /// invoke `FnCtxt::coerce` or a similar method (see `coercion.rs`). + /// + /// This method here is actually a fallback that winds up being + /// invoked when `FnCtxt::coerce` encounters unresolved type variables + /// and records a coercion predicate. Presently, this method is equivalent + /// to `subtype_predicate` -- that is, "coercing" `a` to `b` winds up + /// actually requiring `a <: b`. This is of course a valid coercion, + /// but it's not as flexible as `FnCtxt::coerce` would be. + /// + /// (We may refactor this in the future, but there are a number of + /// practical obstacles. Among other things, `FnCtxt::coerce` presently + /// records adjustments that are required on the HIR in order to perform + /// the coercion, and we don't currently have a way to manage that.) + pub fn coerce_predicate( + &self, + cause: &ObligationCause, + param_env: ParamEnv<'db>, + predicate: PolyCoercePredicate<'db>, + ) -> Result, (TyVid, TyVid)> { + let subtype_predicate = predicate.map_bound(|p| SubtypePredicate { + a_is_expected: false, // when coercing from `a` to `b`, `b` is expected + a: p.a, + b: p.b, + }); + self.subtype_predicate(cause, param_env, subtype_predicate) + } + + pub fn subtype_predicate( + &self, + cause: &ObligationCause, + param_env: ParamEnv<'db>, + predicate: PolySubtypePredicate<'db>, + ) -> Result, (TyVid, TyVid)> { + // Check for two unresolved inference variables, in which case we can + // make no progress. This is partly a micro-optimization, but it's + // also an opportunity to "sub-unify" the variables. This isn't + // *necessary* to prevent cycles, because they would eventually be sub-unified + // anyhow during generalization, but it helps with diagnostics (we can detect + // earlier that they are sub-unified). + // + // Note that we can just skip the binders here because + // type variables can't (at present, at + // least) capture any of the things bound by this binder. + // + // Note that this sub here is not just for diagnostics - it has semantic + // effects as well. + let r_a = self.shallow_resolve(predicate.skip_binder().a); + let r_b = self.shallow_resolve(predicate.skip_binder().b); + match (r_a.kind(), r_b.kind()) { + (TyKind::Infer(InferTy::TyVar(a_vid)), TyKind::Infer(InferTy::TyVar(b_vid))) => { + return Err((a_vid, b_vid)); + } + _ => {} + } + + self.enter_forall(predicate, |SubtypePredicate { a_is_expected, a, b }| { + if a_is_expected { + Ok(self.at(cause, param_env).sub(DefineOpaqueTypes::Yes, a, b)) + } else { + Ok(self.at(cause, param_env).sup(DefineOpaqueTypes::Yes, b, a)) + } + }) + } + + pub fn region_outlives_predicate( + &self, + cause: &traits::ObligationCause, + predicate: PolyRegionOutlivesPredicate<'db>, + ) { + self.enter_forall(predicate, |OutlivesPredicate(r_a, r_b)| { + self.sub_regions(r_b, r_a); // `b : a` ==> `a <= b` + }) + } + + /// Number of type variables created so far. + pub fn num_ty_vars(&self) -> usize { + self.inner.borrow_mut().type_variables().num_vars() + } + + pub fn next_ty_var(&self) -> Ty<'db> { + self.next_ty_var_with_origin(TypeVariableOrigin { param_def_id: None }) + } + + pub fn next_ty_var_with_origin(&self, origin: TypeVariableOrigin) -> Ty<'db> { + let vid = self.inner.borrow_mut().type_variables().new_var(self.universe(), origin); + Ty::new_var(self.interner, vid) + } + + pub fn next_ty_var_id_in_universe(&self, universe: UniverseIndex) -> TyVid { + let origin = TypeVariableOrigin { param_def_id: None }; + self.inner.borrow_mut().type_variables().new_var(universe, origin) + } + + pub fn next_ty_var_in_universe(&self, universe: UniverseIndex) -> Ty<'db> { + let vid = self.next_ty_var_id_in_universe(universe); + Ty::new_var(self.interner, vid) + } + + pub fn next_const_var(&self) -> Const<'db> { + self.next_const_var_with_origin(ConstVariableOrigin { param_def_id: None }) + } + + pub fn next_const_var_with_origin(&self, origin: ConstVariableOrigin) -> Const<'db> { + let vid = self + .inner + .borrow_mut() + .const_unification_table() + .new_key(ConstVariableValue::Unknown { origin, universe: self.universe() }) + .vid; + Const::new_var(self.interner, vid) + } + + pub fn next_const_var_in_universe(&self, universe: UniverseIndex) -> Const<'db> { + let origin = ConstVariableOrigin { param_def_id: None }; + let vid = self + .inner + .borrow_mut() + .const_unification_table() + .new_key(ConstVariableValue::Unknown { origin, universe }) + .vid; + Const::new_var(self.interner, vid) + } + + pub fn next_int_var(&self) -> Ty<'db> { + let next_int_var_id = + self.inner.borrow_mut().int_unification_table().new_key(IntVarValue::Unknown); + Ty::new_int_var(self.interner, next_int_var_id) + } + + pub fn next_float_var(&self) -> Ty<'db> { + let next_float_var_id = + self.inner.borrow_mut().float_unification_table().new_key(FloatVarValue::Unknown); + Ty::new_float_var(self.interner, next_float_var_id) + } + + /// Creates a fresh region variable with the next available index. + /// The variable will be created in the maximum universe created + /// thus far, allowing it to name any region created thus far. + pub fn next_region_var(&self) -> Region<'db> { + self.next_region_var_in_universe(self.universe()) + } + + /// Creates a fresh region variable with the next available index + /// in the given universe; typically, you can use + /// `next_region_var` and just use the maximal universe. + pub fn next_region_var_in_universe(&self, universe: UniverseIndex) -> Region<'db> { + let region_var = + self.inner.borrow_mut().unwrap_region_constraints().new_region_var(universe); + Region::new_var(self.interner, region_var) + } + + pub fn next_term_var_of_kind(&self, term: Term<'db>) -> Term<'db> { + match term.kind() { + TermKind::Ty(_) => self.next_ty_var().into(), + TermKind::Const(_) => self.next_const_var().into(), + } + } + + /// Return the universe that the region `r` was created in. For + /// most regions (e.g., `'static`, named regions from the user, + /// etc) this is the root universe U0. For inference variables or + /// placeholders, however, it will return the universe which they + /// are associated. + pub fn universe_of_region(&self, r: Region<'db>) -> UniverseIndex { + self.inner.borrow_mut().unwrap_region_constraints().universe(r) + } + + /// Number of region variables created so far. + pub fn num_region_vars(&self) -> usize { + self.inner.borrow_mut().unwrap_region_constraints().num_region_vars() + } + + /// Just a convenient wrapper of `next_region_var` for using during NLL. + #[instrument(skip(self), level = "debug")] + pub fn next_nll_region_var(&self) -> Region<'db> { + self.next_region_var() + } + + /// Just a convenient wrapper of `next_region_var` for using during NLL. + #[instrument(skip(self), level = "debug")] + pub fn next_nll_region_var_in_universe(&self, universe: UniverseIndex) -> Region<'db> { + self.next_region_var_in_universe(universe) + } + + fn var_for_def(&self, kind: GenericParamDefKind, name: &Symbol) -> GenericArg<'db> { + match kind { + GenericParamDefKind::Lifetime => { + // Create a region inference variable for the given + // region parameter definition. + self.next_region_var().into() + } + GenericParamDefKind::Type => { + // Create a type inference variable for the given + // type parameter definition. The generic parameters are + // for actual parameters that may be referred to by + // the default of this type parameter, if it exists. + // e.g., `struct Foo(...);` when + // used in a path such as `Foo::::new()` will + // use an inference variable for `C` with `[T, U]` + // as the generic parameters for the default, `(T, U)`. + let ty_var_id = self + .inner + .borrow_mut() + .type_variables() + .new_var(self.universe(), TypeVariableOrigin { param_def_id: None }); + + Ty::new_var(self.interner, ty_var_id).into() + } + GenericParamDefKind::Const => { + let origin = ConstVariableOrigin { param_def_id: None }; + let const_var_id = self + .inner + .borrow_mut() + .const_unification_table() + .new_key(ConstVariableValue::Unknown { origin, universe: self.universe() }) + .vid; + Const::new_var(self.interner, const_var_id).into() + } + } + } + + /// Given a set of generics defined on a type or impl, returns the generic parameters mapping + /// each type/region parameter to a fresh inference variable. + pub fn fresh_args_for_item(&self, def_id: SolverDefId) -> GenericArgs<'db> { + GenericArgs::for_item(self.interner, def_id, |name, index, kind, _| { + self.var_for_def(kind, name) + }) + } + + /// Returns `true` if errors have been reported since this infcx was + /// created. This is sometimes used as a heuristic to skip + /// reporting errors that often occur as a result of earlier + /// errors, but where it's hard to be 100% sure (e.g., unresolved + /// inference variables, regionck errors). + #[must_use = "this method does not have any side effects"] + pub fn tainted_by_errors(&self) -> Option { + self.tainted_by_errors.get() + } + + /// Set the "tainted by errors" flag to true. We call this when we + /// observe an error from a prior pass. + pub fn set_tainted_by_errors(&self, e: ErrorGuaranteed) { + debug!("set_tainted_by_errors(ErrorGuaranteed)"); + self.tainted_by_errors.set(Some(e)); + } + + #[instrument(level = "debug", skip(self), ret)] + pub fn take_opaque_types(&self) -> Vec<(OpaqueTypeKey<'db>, OpaqueHiddenType<'db>)> { + self.inner.borrow_mut().opaque_type_storage.take_opaque_types().collect() + } + + #[instrument(level = "debug", skip(self), ret)] + pub fn clone_opaque_types(&self) -> Vec<(OpaqueTypeKey<'db>, OpaqueHiddenType<'db>)> { + self.inner.borrow_mut().opaque_type_storage.iter_opaque_types().collect() + } + + #[inline(always)] + pub fn can_define_opaque_ty(&self, id: impl Into) -> bool { + match self.typing_mode_unchecked() { + TypingMode::Analysis { defining_opaque_types_and_generators } => { + defining_opaque_types_and_generators.contains(&id.into()) + } + TypingMode::Coherence | TypingMode::PostAnalysis => false, + TypingMode::Borrowck { defining_opaque_types } => unimplemented!(), + TypingMode::PostBorrowckAnalysis { defined_opaque_types } => unimplemented!(), + } + } + + /// If `TyVar(vid)` resolves to a type, return that type. Else, return the + /// universe index of `TyVar(vid)`. + pub fn probe_ty_var(&self, vid: TyVid) -> Result, UniverseIndex> { + use self::type_variable::TypeVariableValue; + + match self.inner.borrow_mut().type_variables().probe(vid) { + TypeVariableValue::Known { value } => Ok(value), + TypeVariableValue::Unknown { universe } => Err(universe), + } + } + + pub fn shallow_resolve(&self, ty: Ty<'db>) -> Ty<'db> { + if let TyKind::Infer(v) = ty.kind() { + match v { + InferTy::TyVar(v) => { + // Not entirely obvious: if `typ` is a type variable, + // it can be resolved to an int/float variable, which + // can then be recursively resolved, hence the + // recursion. Note though that we prevent type + // variables from unifying to other type variables + // directly (though they may be embedded + // structurally), and we prevent cycles in any case, + // so this recursion should always be of very limited + // depth. + // + // Note: if these two lines are combined into one we get + // dynamic borrow errors on `self.inner`. + let known = self.inner.borrow_mut().type_variables().probe(v).known(); + known.map_or(ty, |t| self.shallow_resolve(t)) + } + + InferTy::IntVar(v) => { + match self.inner.borrow_mut().int_unification_table().probe_value(v) { + IntVarValue::IntType(ty) => Ty::new_int(self.interner, ty), + IntVarValue::UintType(ty) => Ty::new_uint(self.interner, ty), + IntVarValue::Unknown => ty, + } + } + + InferTy::FloatVar(v) => { + match self.inner.borrow_mut().float_unification_table().probe_value(v) { + FloatVarValue::Known(ty) => Ty::new_float(self.interner, ty), + FloatVarValue::Unknown => ty, + } + } + + InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_) => ty, + } + } else { + ty + } + } + + pub fn shallow_resolve_const(&self, ct: Const<'db>) -> Const<'db> { + match ct.kind() { + ConstKind::Infer(infer_ct) => match infer_ct { + InferConst::Var(vid) => self + .inner + .borrow_mut() + .const_unification_table() + .probe_value(vid) + .known() + .unwrap_or(ct), + InferConst::Fresh(_) => ct, + }, + ConstKind::Param(_) + | ConstKind::Bound(_, _) + | ConstKind::Placeholder(_) + | ConstKind::Unevaluated(_) + | ConstKind::Value(_) + | ConstKind::Error(_) + | ConstKind::Expr(_) => ct, + } + } + + pub fn root_var(&self, var: TyVid) -> TyVid { + self.inner.borrow_mut().type_variables().root_var(var) + } + + pub fn root_const_var(&self, var: ConstVid) -> ConstVid { + self.inner.borrow_mut().const_unification_table().find(var).vid + } + + /// Resolves an int var to a rigid int type, if it was constrained to one, + /// or else the root int var in the unification table. + pub fn opportunistic_resolve_int_var(&self, vid: IntVid) -> Ty<'db> { + let mut inner = self.inner.borrow_mut(); + let value = inner.int_unification_table().probe_value(vid); + match value { + IntVarValue::IntType(ty) => Ty::new_int(self.interner, ty), + IntVarValue::UintType(ty) => Ty::new_uint(self.interner, ty), + IntVarValue::Unknown => { + Ty::new_int_var(self.interner, inner.int_unification_table().find(vid)) + } + } + } + + /// Resolves a float var to a rigid int type, if it was constrained to one, + /// or else the root float var in the unification table. + pub fn opportunistic_resolve_float_var(&self, vid: FloatVid) -> Ty<'db> { + let mut inner = self.inner.borrow_mut(); + let value = inner.float_unification_table().probe_value(vid); + match value { + FloatVarValue::Known(ty) => Ty::new_float(self.interner, ty), + FloatVarValue::Unknown => { + Ty::new_float_var(self.interner, inner.float_unification_table().find(vid)) + } + } + } + + /// Where possible, replaces type/const variables in + /// `value` with their final value. Note that region variables + /// are unaffected. If a type/const variable has not been unified, it + /// is left as is. This is an idempotent operation that does + /// not affect inference state in any way and so you can do it + /// at will. + pub fn resolve_vars_if_possible(&self, value: T) -> T + where + T: TypeFoldable>, + { + if let Err(guar) = value.error_reported() { + self.set_tainted_by_errors(guar); + } + if !value.has_non_region_infer() { + return value; + } + let mut r = resolve::OpportunisticVarResolver::new(self); + value.fold_with(&mut r) + } + + pub fn probe_const_var(&self, vid: ConstVid) -> Result, UniverseIndex> { + match self.inner.borrow_mut().const_unification_table().probe_value(vid) { + ConstVariableValue::Known { value } => Ok(value), + ConstVariableValue::Unknown { origin: _, universe } => Err(universe), + } + } + + // Instantiates the bound variables in a given binder with fresh inference + // variables in the current universe. + // + // Use this method if you'd like to find some generic parameters of the binder's + // variables (e.g. during a method call). If there isn't a [`BoundRegionConversionTime`] + // that corresponds to your use case, consider whether or not you should + // use [`InferCtxt::enter_forall`] instead. + pub fn instantiate_binder_with_fresh_vars( + &self, + lbrct: BoundRegionConversionTime, + value: Binder<'db, T>, + ) -> T + where + T: TypeFoldable> + Clone, + { + if let Some(inner) = value.clone().no_bound_vars() { + return inner; + } + + let bound_vars = value.clone().bound_vars(); + let mut args = Vec::with_capacity(bound_vars.len()); + + for bound_var_kind in bound_vars { + let arg: GenericArg<'db> = match bound_var_kind { + BoundVarKind::Ty(_) => self.next_ty_var().into(), + BoundVarKind::Region(br) => self.next_region_var().into(), + BoundVarKind::Const => self.next_const_var().into(), + }; + args.push(arg); + } + + struct ToFreshVars<'db> { + args: Vec>, + } + + impl<'db> BoundVarReplacerDelegate<'db> for ToFreshVars<'db> { + fn replace_region(&mut self, br: BoundRegion) -> Region<'db> { + self.args[br.var.index()].expect_region() + } + fn replace_ty(&mut self, bt: BoundTy) -> Ty<'db> { + self.args[bt.var.index()].expect_ty() + } + fn replace_const(&mut self, bv: BoundVar) -> Const<'db> { + self.args[bv.index()].expect_const() + } + } + let delegate = ToFreshVars { args }; + self.interner.replace_bound_vars_uncached(value, delegate) + } + + /// Obtains the latest type of the given closure; this may be a + /// closure in the current function, in which case its + /// `ClosureKind` may not yet be known. + pub fn closure_kind(&self, closure_ty: Ty<'db>) -> Option { + let unresolved_kind_ty = match closure_ty.kind() { + TyKind::Closure(_, args) => args.as_closure().kind_ty(), + TyKind::CoroutineClosure(_, args) => args.as_coroutine_closure().kind_ty(), + _ => panic!("unexpected type {closure_ty:?}"), + }; + let closure_kind_ty = self.shallow_resolve(unresolved_kind_ty); + closure_kind_ty.to_opt_closure_kind() + } + + pub fn universe(&self) -> UniverseIndex { + self.universe.get() + } + + /// Creates and return a fresh universe that extends all previous + /// universes. Updates `self.universe` to that new universe. + pub fn create_next_universe(&self) -> UniverseIndex { + let u = self.universe.get().next_universe(); + debug!("create_next_universe {u:?}"); + self.universe.set(u); + u + } + + /// The returned function is used in a fast path. If it returns `true` the variable is + /// unchanged, `false` indicates that the status is unknown. + #[inline] + pub fn is_ty_infer_var_definitely_unchanged<'a>( + &'a self, + ) -> (impl Fn(TyOrConstInferVar) -> bool + Captures<'db> + 'a) { + // This hoists the borrow/release out of the loop body. + let inner = self.inner.try_borrow(); + + move |infer_var: TyOrConstInferVar| match (infer_var, &inner) { + (TyOrConstInferVar::Ty(ty_var), Ok(inner)) => { + use self::type_variable::TypeVariableValue; + + matches!( + inner.try_type_variables_probe_ref(ty_var), + Some(TypeVariableValue::Unknown { .. }) + ) + } + _ => false, + } + } + + /// `ty_or_const_infer_var_changed` is equivalent to one of these two: + /// * `shallow_resolve(ty) != ty` (where `ty.kind = Infer(_)`) + /// * `shallow_resolve(ct) != ct` (where `ct.kind = ConstKind::Infer(_)`) + /// + /// However, `ty_or_const_infer_var_changed` is more efficient. It's always + /// inlined, despite being large, because it has only two call sites that + /// are extremely hot (both in `traits::fulfill`'s checking of `stalled_on` + /// inference variables), and it handles both `Ty` and `Const` without + /// having to resort to storing full `GenericArg`s in `stalled_on`. + #[inline(always)] + pub fn ty_or_const_infer_var_changed(&self, infer_var: TyOrConstInferVar) -> bool { + match infer_var { + TyOrConstInferVar::Ty(v) => { + use self::type_variable::TypeVariableValue; + + // If `inlined_probe` returns a `Known` value, it never equals + // `Infer(TyVar(v))`. + match self.inner.borrow_mut().type_variables().inlined_probe(v) { + TypeVariableValue::Unknown { .. } => false, + TypeVariableValue::Known { .. } => true, + } + } + + TyOrConstInferVar::TyInt(v) => { + // If `inlined_probe_value` returns a value it's always a + // `Int(_)` or `UInt(_)`, which never matches a + // `Infer(_)`. + self.inner.borrow_mut().int_unification_table().inlined_probe_value(v).is_known() + } + + TyOrConstInferVar::TyFloat(v) => { + // If `probe_value` returns a value it's always a + // `Float(_)`, which never matches a `Infer(_)`. + // + // Not `inlined_probe_value(v)` because this call site is colder. + self.inner.borrow_mut().float_unification_table().probe_value(v).is_known() + } + + TyOrConstInferVar::Const(v) => { + // If `probe_value` returns a `Known` value, it never equals + // `ConstKind::Infer(InferConst::Var(v))`. + // + // Not `inlined_probe_value(v)` because this call site is colder. + match self.inner.borrow_mut().const_unification_table().probe_value(v) { + ConstVariableValue::Unknown { .. } => false, + ConstVariableValue::Known { .. } => true, + } + } + } + } +} + +/// Helper for [InferCtxt::ty_or_const_infer_var_changed] (see comment on that), currently +/// used only for `traits::fulfill`'s list of `stalled_on` inference variables. +#[derive(Copy, Clone, Debug)] +pub enum TyOrConstInferVar { + /// Equivalent to `Infer(TyVar(_))`. + Ty(TyVid), + /// Equivalent to `Infer(IntVar(_))`. + TyInt(IntVid), + /// Equivalent to `Infer(FloatVar(_))`. + TyFloat(FloatVid), + + /// Equivalent to `ConstKind::Infer(InferConst::Var(_))`. + Const(ConstVid), +} + +impl TyOrConstInferVar { + /// Tries to extract an inference variable from a type or a constant, returns `None` + /// for types other than `Infer(_)` (or `InferTy::Fresh*`) and + /// for constants other than `ConstKind::Infer(_)` (or `InferConst::Fresh`). + pub fn maybe_from_generic_arg<'db>(arg: GenericArg<'db>) -> Option { + match arg.kind() { + GenericArgKind::Type(ty) => Self::maybe_from_ty(ty), + GenericArgKind::Const(ct) => Self::maybe_from_const(ct), + GenericArgKind::Lifetime(_) => None, + } + } + + /// Tries to extract an inference variable from a type, returns `None` + /// for types other than `Infer(_)` (or `InferTy::Fresh*`). + fn maybe_from_ty<'db>(ty: Ty<'db>) -> Option { + match ty.kind() { + TyKind::Infer(InferTy::TyVar(v)) => Some(TyOrConstInferVar::Ty(v)), + TyKind::Infer(InferTy::IntVar(v)) => Some(TyOrConstInferVar::TyInt(v)), + TyKind::Infer(InferTy::FloatVar(v)) => Some(TyOrConstInferVar::TyFloat(v)), + _ => None, + } + } + + /// Tries to extract an inference variable from a constant, returns `None` + /// for constants other than `ConstKind::Infer(_)` (or `InferConst::Fresh`). + fn maybe_from_const<'db>(ct: Const<'db>) -> Option { + match ct.kind() { + ConstKind::Infer(InferConst::Var(v)) => Some(TyOrConstInferVar::Const(v)), + _ => None, + } + } +} + +impl<'db> TypeTrace<'db> { + pub fn types(cause: &ObligationCause, a: Ty<'db>, b: Ty<'db>) -> TypeTrace<'db> { + TypeTrace { + cause: cause.clone(), + values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())), + } + } + + pub fn trait_refs( + cause: &ObligationCause, + a: TraitRef<'db>, + b: TraitRef<'db>, + ) -> TypeTrace<'db> { + TypeTrace { cause: cause.clone(), values: ValuePairs::TraitRefs(ExpectedFound::new(a, b)) } + } + + pub fn consts(cause: &ObligationCause, a: Const<'db>, b: Const<'db>) -> TypeTrace<'db> { + TypeTrace { + cause: cause.clone(), + values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())), + } + } +} + +/// Requires that `region` must be equal to one of the regions in `choice_regions`. +/// We often denote this using the syntax: +/// +/// ```text +/// R0 member of [O1..On] +/// ``` +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct MemberConstraint<'db> { + /// The `DefId` and args of the opaque type causing this constraint. + /// Used for error reporting. + pub key: OpaqueTypeKey<'db>, + + /// The hidden type in which `member_region` appears: used for error reporting. + pub hidden_ty: Ty<'db>, + + /// The region `R0`. + pub member_region: Region<'db>, + + /// The options `O1..On`. + pub choice_regions: Arc>>, +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/opaque_types/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/opaque_types/mod.rs new file mode 100644 index 000000000000..0f68ec8cdb5b --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/opaque_types/mod.rs @@ -0,0 +1,56 @@ +//! Things related to the infer context of the next-trait-solver. + +use std::sync::Arc; + +use tracing::{debug, instrument}; + +use crate::next_solver::{ + Clause, ClauseKind, FxIndexMap, GenericArgs, OpaqueTypeKey, ProjectionPredicate, SolverDefId, + TypingMode, util::BottomUpFolder, +}; + +pub(crate) mod table; + +pub(crate) use table::{OpaqueTypeStorage, OpaqueTypeTable}; + +use crate::next_solver::{ + AliasTy, Binder, BoundRegion, BoundTy, Canonical, CanonicalVarValues, Const, DbInterner, Goal, + ParamEnv, Predicate, PredicateKind, Region, Ty, TyKind, + fold::FnMutDelegate, + infer::{ + DefineOpaqueTypes, InferCtxt, TypeTrace, + traits::{Obligation, PredicateObligations}, + }, +}; +use rustc_type_ir::{ + AliasRelationDirection, AliasTyKind, BoundConstness, BoundVar, Flags, GenericArgKind, InferTy, + Interner, RegionKind, TypeFlags, TypeFoldable, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt, TypeVisitor, Upcast, Variance, + error::{ExpectedFound, TypeError}, + inherent::{DefId, GenericArgs as _, IntoKind, SliceLike}, + relate::{ + Relate, TypeRelation, VarianceDiagInfo, + combine::{super_combine_consts, super_combine_tys}, + }, +}; + +use super::{InferOk, traits::ObligationCause}; + +#[derive(Copy, Clone, Debug)] +pub struct OpaqueHiddenType<'db> { + pub ty: Ty<'db>, +} + +impl<'db> InferCtxt<'db> { + /// Insert a hidden type into the opaque type storage, making sure + /// it hasn't previously been defined. This does not emit any + /// constraints and it's the responsibility of the caller to make + /// sure that the item bounds of the opaque are checked. + pub fn register_hidden_type_in_storage( + &self, + opaque_type_key: OpaqueTypeKey<'db>, + hidden_ty: OpaqueHiddenType<'db>, + ) -> Option> { + self.inner.borrow_mut().opaque_types().register(opaque_type_key, hidden_ty) + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/opaque_types/table.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/opaque_types/table.rs new file mode 100644 index 000000000000..8ab409d78281 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/opaque_types/table.rs @@ -0,0 +1,166 @@ +//! Things related to storage opaques in the infer context of the next-trait-solver. + +use std::ops::Deref; + +use ena::undo_log::UndoLogs; +use tracing::instrument; + +use super::OpaqueHiddenType; +use crate::next_solver::{ + FxIndexMap, OpaqueTypeKey, Ty, + infer::snapshot::undo_log::{InferCtxtUndoLogs, UndoLog}, +}; + +#[derive(Default, Debug, Clone)] +pub(crate) struct OpaqueTypeStorage<'db> { + opaque_types: FxIndexMap, OpaqueHiddenType<'db>>, + duplicate_entries: Vec<(OpaqueTypeKey<'db>, OpaqueHiddenType<'db>)>, +} + +/// The number of entries in the opaque type storage at a given point. +/// +/// Used to check that we haven't added any new opaque types after checking +/// the opaque types currently in the storage. +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] +pub struct OpaqueTypeStorageEntries { + opaque_types: usize, + duplicate_entries: usize, +} + +impl rustc_type_ir::inherent::OpaqueTypeStorageEntries for OpaqueTypeStorageEntries { + fn needs_reevaluation(self, canonicalized: usize) -> bool { + self.opaque_types != canonicalized + } +} + +impl<'db> OpaqueTypeStorage<'db> { + #[instrument(level = "debug")] + pub(crate) fn remove(&mut self, key: OpaqueTypeKey<'db>, prev: Option>) { + if let Some(prev) = prev { + *self.opaque_types.get_mut(&key).unwrap() = prev; + } else { + // FIXME(#120456) - is `swap_remove` correct? + match self.opaque_types.swap_remove(&key) { + None => { + panic!("reverted opaque type inference that was never registered: {key:?}") + } + Some(_) => {} + } + } + } + + pub(crate) fn pop_duplicate_entry(&mut self) { + let entry = self.duplicate_entries.pop(); + assert!(entry.is_some()); + } + + pub fn is_empty(&self) -> bool { + let OpaqueTypeStorage { opaque_types, duplicate_entries } = self; + opaque_types.is_empty() && duplicate_entries.is_empty() + } + + pub(crate) fn take_opaque_types( + &mut self, + ) -> impl Iterator, OpaqueHiddenType<'db>)> { + let OpaqueTypeStorage { opaque_types, duplicate_entries } = self; + std::mem::take(opaque_types).into_iter().chain(std::mem::take(duplicate_entries)) + } + + pub fn num_entries(&self) -> OpaqueTypeStorageEntries { + OpaqueTypeStorageEntries { + opaque_types: self.opaque_types.len(), + duplicate_entries: self.duplicate_entries.len(), + } + } + + pub fn opaque_types_added_since( + &self, + prev_entries: OpaqueTypeStorageEntries, + ) -> impl Iterator, OpaqueHiddenType<'db>)> { + self.opaque_types + .iter() + .skip(prev_entries.opaque_types) + .map(|(k, v)| (*k, *v)) + .chain(self.duplicate_entries.iter().skip(prev_entries.duplicate_entries).copied()) + } + + /// Only returns the opaque types from the lookup table. These are used + /// when normalizing opaque types and have a unique key. + /// + /// Outside of canonicalization one should generally use `iter_opaque_types` + /// to also consider duplicate entries. + pub fn iter_lookup_table( + &self, + ) -> impl Iterator, OpaqueHiddenType<'db>)> { + self.opaque_types.iter().map(|(k, v)| (*k, *v)) + } + + /// Only returns the opaque types which are stored in `duplicate_entries`. + /// + /// These have to considered when checking all opaque type uses but are e.g. + /// irrelevant for canonical inputs as nested queries never meaningfully + /// accesses them. + pub fn iter_duplicate_entries( + &self, + ) -> impl Iterator, OpaqueHiddenType<'db>)> { + self.duplicate_entries.iter().copied() + } + + pub fn iter_opaque_types( + &self, + ) -> impl Iterator, OpaqueHiddenType<'db>)> { + let OpaqueTypeStorage { opaque_types, duplicate_entries } = self; + opaque_types.iter().map(|(k, v)| (*k, *v)).chain(duplicate_entries.iter().copied()) + } + + #[inline] + pub(crate) fn with_log<'a>( + &'a mut self, + undo_log: &'a mut InferCtxtUndoLogs<'db>, + ) -> OpaqueTypeTable<'a, 'db> { + OpaqueTypeTable { storage: self, undo_log } + } +} + +impl<'db> Drop for OpaqueTypeStorage<'db> { + fn drop(&mut self) { + if !self.opaque_types.is_empty() { + panic!("{:?}", self.opaque_types) + } + } +} + +pub(crate) struct OpaqueTypeTable<'a, 'db> { + storage: &'a mut OpaqueTypeStorage<'db>, + + undo_log: &'a mut InferCtxtUndoLogs<'db>, +} +impl<'db> Deref for OpaqueTypeTable<'_, 'db> { + type Target = OpaqueTypeStorage<'db>; + fn deref(&self) -> &Self::Target { + self.storage + } +} + +impl<'a, 'db> OpaqueTypeTable<'a, 'db> { + #[instrument(skip(self), level = "debug")] + pub fn register( + &mut self, + key: OpaqueTypeKey<'db>, + hidden_type: OpaqueHiddenType<'db>, + ) -> Option> { + if let Some(entry) = self.storage.opaque_types.get_mut(&key) { + let prev = std::mem::replace(entry, hidden_type); + self.undo_log.push(UndoLog::OpaqueTypes(key, Some(prev))); + return Some(prev.ty); + } + self.storage.opaque_types.insert(key, hidden_type); + self.undo_log.push(UndoLog::OpaqueTypes(key, None)); + None + } + + pub fn add_duplicate(&mut self, key: OpaqueTypeKey<'db>, hidden_type: OpaqueHiddenType<'db>) { + self.storage.duplicate_entries.push((key, hidden_type)); + self.undo_log.push(UndoLog::DuplicateOpaqueType); + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs new file mode 100644 index 000000000000..50549694c3f2 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs @@ -0,0 +1,640 @@ +//! See `README.md`. + +use std::ops::Range; +use std::sync::Arc; +use std::{cmp, fmt, mem}; + +use ena::undo_log::{Rollback, UndoLogs}; +use ena::unify as ut; +use rustc_hash::FxHashMap; +use rustc_index::IndexVec; +use rustc_type_ir::inherent::IntoKind; +use rustc_type_ir::{RegionKind, RegionVid, UniverseIndex}; +use tracing::{debug, instrument}; + +use self::CombineMapType::*; +use self::UndoLog::*; +use super::MemberConstraint; +use super::unify_key::RegionVidKey; +use crate::next_solver::infer::snapshot::undo_log::{InferCtxtUndoLogs, Snapshot}; +use crate::next_solver::infer::unify_key::RegionVariableValue; +use crate::next_solver::{ + AliasTy, Binder, DbInterner, OpaqueTypeKey, ParamTy, PlaceholderTy, Region, Ty, +}; + +#[derive(Clone, Default)] +pub struct RegionConstraintStorage<'db> { + /// For each `RegionVid`, the corresponding `RegionVariableOrigin`. + pub(super) var_infos: IndexVec, + + pub(super) data: RegionConstraintData<'db>, + + /// For a given pair of regions (R1, R2), maps to a region R3 that + /// is designated as their LUB (edges R1 <= R3 and R2 <= R3 + /// exist). This prevents us from making many such regions. + lubs: CombineMap<'db>, + + /// For a given pair of regions (R1, R2), maps to a region R3 that + /// is designated as their GLB (edges R3 <= R1 and R3 <= R2 + /// exist). This prevents us from making many such regions. + glbs: CombineMap<'db>, + + /// When we add a R1 == R2 constraint, we currently add (a) edges + /// R1 <= R2 and R2 <= R1 and (b) we unify the two regions in this + /// table. You can then call `opportunistic_resolve_var` early + /// which will map R1 and R2 to some common region (i.e., either + /// R1 or R2). This is important when fulfillment, dropck and other such + /// code is iterating to a fixed point, because otherwise we sometimes + /// would wind up with a fresh stream of region variables that have been + /// equated but appear distinct. + pub(super) unification_table: ut::UnificationTableStorage>, + + /// a flag set to true when we perform any unifications; this is used + /// to micro-optimize `take_and_reset_data` + any_unifications: bool, +} + +pub struct RegionConstraintCollector<'db, 'a> { + storage: &'a mut RegionConstraintStorage<'db>, + undo_log: &'a mut InferCtxtUndoLogs<'db>, +} + +pub type VarInfos = IndexVec; + +/// The full set of region constraints gathered up by the collector. +/// Describes constraints between the region variables and other +/// regions, as well as other conditions that must be verified, or +/// assumptions that can be made. +#[derive(Debug, Default, Clone)] +pub struct RegionConstraintData<'db> { + /// Constraints of the form `A <= B`, where either `A` or `B` can + /// be a region variable (or neither, as it happens). + pub constraints: Vec>, + + /// Constraints of the form `R0 member of [R1, ..., Rn]`, meaning that + /// `R0` must be equal to one of the regions `R1..Rn`. These occur + /// with `impl Trait` quite frequently. + pub member_constraints: Vec>, + + /// A "verify" is something that we need to verify after inference + /// is done, but which does not directly affect inference in any + /// way. + /// + /// An example is a `A <= B` where neither `A` nor `B` are + /// inference variables. + pub verifys: Vec>, +} + +/// Represents a constraint that influences the inference process. +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub enum Constraint<'db> { + /// A region variable is a subregion of another. + VarSubVar(RegionVid, RegionVid), + + /// A concrete region is a subregion of region variable. + RegSubVar(Region<'db>, RegionVid), + + /// A region variable is a subregion of a concrete region. This does not + /// directly affect inference, but instead is checked after + /// inference is complete. + VarSubReg(RegionVid, Region<'db>), + + /// A constraint where neither side is a variable. This does not + /// directly affect inference, but instead is checked after + /// inference is complete. + RegSubReg(Region<'db>, Region<'db>), +} + +impl<'db> Constraint<'db> { + pub fn involves_placeholders(&self) -> bool { + match self { + Constraint::VarSubVar(_, _) => false, + Constraint::VarSubReg(_, r) | Constraint::RegSubVar(r, _) => r.is_placeholder(), + Constraint::RegSubReg(r, s) => r.is_placeholder() || s.is_placeholder(), + } + } +} + +#[derive(Debug, Clone)] +pub struct Verify<'db> { + pub kind: GenericKind<'db>, + pub region: Region<'db>, + pub bound: VerifyBound<'db>, +} + +#[derive(Clone, PartialEq, Eq, Hash)] +pub enum GenericKind<'db> { + Param(ParamTy), + Placeholder(PlaceholderTy), + Alias(AliasTy<'db>), +} + +/// Describes the things that some `GenericKind` value `G` is known to +/// outlive. Each variant of `VerifyBound` can be thought of as a +/// function: +/// ```ignore (pseudo-rust) +/// fn(min: Region) -> bool { .. } +/// ``` +/// where `true` means that the region `min` meets that `G: min`. +/// (False means nothing.) +/// +/// So, for example, if we have the type `T` and we have in scope that +/// `T: 'a` and `T: 'b`, then the verify bound might be: +/// ```ignore (pseudo-rust) +/// fn(min: Region) -> bool { +/// ('a: min) || ('b: min) +/// } +/// ``` +/// This is described with an `AnyRegion('a, 'b)` node. +#[derive(Debug, Clone)] +pub enum VerifyBound<'db> { + /// See [`VerifyIfEq`] docs + IfEq(Binder<'db, VerifyIfEq<'db>>), + + /// Given a region `R`, expands to the function: + /// + /// ```ignore (pseudo-rust) + /// fn(min) -> bool { + /// R: min + /// } + /// ``` + /// + /// This is used when we can establish that `G: R` -- therefore, + /// if `R: min`, then by transitivity `G: min`. + OutlivedBy(Region<'db>), + + /// Given a region `R`, true if it is `'empty`. + IsEmpty, + + /// Given a set of bounds `B`, expands to the function: + /// + /// ```ignore (pseudo-rust) + /// fn(min) -> bool { + /// exists (b in B) { b(min) } + /// } + /// ``` + /// + /// In other words, if we meet some bound in `B`, that suffices. + /// This is used when all the bounds in `B` are known to apply to `G`. + AnyBound(Vec>), + + /// Given a set of bounds `B`, expands to the function: + /// + /// ```ignore (pseudo-rust) + /// fn(min) -> bool { + /// forall (b in B) { b(min) } + /// } + /// ``` + /// + /// In other words, if we meet *all* bounds in `B`, that suffices. + /// This is used when *some* bound in `B` is known to suffice, but + /// we don't know which. + AllBounds(Vec>), +} + +/// This is a "conditional bound" that checks the result of inference +/// and supplies a bound if it ended up being relevant. It's used in situations +/// like this: +/// +/// ```rust,ignore (pseudo-Rust) +/// fn foo<'a, 'b, T: SomeTrait<'a>> +/// where +/// >::Item: 'b +/// ``` +/// +/// If we have an obligation like `>::Item: 'c`, then +/// we don't know yet whether it suffices to show that `'b: 'c`. If `'?x` winds +/// up being equal to `'a`, then the where-clauses on function applies, and +/// in that case we can show `'b: 'c`. But if `'?x` winds up being something +/// else, the bound isn't relevant. +/// +/// In the [`VerifyBound`], this struct is enclosed in `Binder` to account +/// for cases like +/// +/// ```rust,ignore (pseudo-Rust) +/// where for<'a> ::Item: 'a +/// ``` +/// +/// The idea is that we have to find some instantiation of `'a` that can +/// make `>::Item` equal to the final value of `G`, +/// the generic we are checking. +/// +/// ```ignore (pseudo-rust) +/// fn(min) -> bool { +/// exists<'a> { +/// if G == K { +/// B(min) +/// } else { +/// false +/// } +/// } +/// } +/// ``` +#[derive(Debug, Clone)] +pub struct VerifyIfEq<'db> { + /// Type which must match the generic `G` + pub ty: Ty<'db>, + + /// Bound that applies if `ty` is equal. + pub bound: Region<'db>, +} + +#[derive(Clone, PartialEq, Eq, Hash)] +pub(crate) struct TwoRegions<'db> { + a: Region<'db>, + b: Region<'db>, +} + +#[derive(Clone, PartialEq)] +pub(crate) enum UndoLog<'db> { + /// We added `RegionVid`. + AddVar(RegionVid), + + /// We added the given `constraint`. + AddConstraint(usize), + + /// We added the given `verify`. + AddVerify(usize), + + /// We added a GLB/LUB "combination variable". + AddCombination(CombineMapType, TwoRegions<'db>), +} + +#[derive(Clone, PartialEq)] +pub(crate) enum CombineMapType { + Lub, + Glb, +} + +type CombineMap<'db> = FxHashMap, RegionVid>; + +#[derive(Debug, Clone)] +pub struct RegionVariableInfo { + // FIXME: This is only necessary for `fn take_and_reset_data` and + // `lexical_region_resolve`. We should rework `lexical_region_resolve` + // in the near/medium future anyways and could move the unverse info + // for `fn take_and_reset_data` into a separate table which is + // only populated when needed. + // + // For both of these cases it is fine that this can diverge from the + // actual universe of the variable, which is directly stored in the + // unification table for unknown region variables. At some point we could + // stop emitting bidirectional outlives constraints if equate succeeds. + // This would be currently unsound as it would cause us to drop the universe + // changes in `lexical_region_resolve`. + pub universe: UniverseIndex, +} + +pub(crate) struct RegionSnapshot { + any_unifications: bool, +} + +impl<'db> RegionConstraintStorage<'db> { + #[inline] + pub(crate) fn with_log<'a>( + &'a mut self, + undo_log: &'a mut InferCtxtUndoLogs<'db>, + ) -> RegionConstraintCollector<'db, 'a> { + RegionConstraintCollector { storage: self, undo_log } + } +} + +impl<'db> RegionConstraintCollector<'db, '_> { + pub fn num_region_vars(&self) -> usize { + self.storage.var_infos.len() + } + + pub fn region_constraint_data(&self) -> &RegionConstraintData<'db> { + &self.storage.data + } + + /// Takes (and clears) the current set of constraints. Note that + /// the set of variables remains intact, but all relationships + /// between them are reset. This is used during NLL checking to + /// grab the set of constraints that arose from a particular + /// operation. + /// + /// We don't want to leak relationships between variables between + /// points because just because (say) `r1 == r2` was true at some + /// point P in the graph doesn't imply that it will be true at + /// some other point Q, in NLL. + /// + /// Not legal during a snapshot. + pub fn take_and_reset_data(&mut self) -> RegionConstraintData<'db> { + assert!(!UndoLogs::>::in_snapshot(&self.undo_log)); + + // If you add a new field to `RegionConstraintCollector`, you + // should think carefully about whether it needs to be cleared + // or updated in some way. + let RegionConstraintStorage { + var_infos: _, + data, + lubs, + glbs, + unification_table: _, + any_unifications, + } = self.storage; + + // Clear the tables of (lubs, glbs), so that we will create + // fresh regions if we do a LUB operation. As it happens, + // LUB/GLB are not performed by the MIR type-checker, which is + // the one that uses this method, but it's good to be correct. + lubs.clear(); + glbs.clear(); + + let data = mem::take(data); + + // Clear all unifications and recreate the variables a "now + // un-unified" state. Note that when we unify `a` and `b`, we + // also insert `a <= b` and a `b <= a` edges, so the + // `RegionConstraintData` contains the relationship here. + if *any_unifications { + *any_unifications = false; + // Manually inlined `self.unification_table_mut()` as `self` is used in the closure. + ut::UnificationTable::with_log(&mut self.storage.unification_table, &mut self.undo_log) + .reset_unifications(|key| RegionVariableValue::Unknown { + universe: self.storage.var_infos[key.vid].universe, + }); + } + + data + } + + pub fn data(&self) -> &RegionConstraintData<'db> { + &self.storage.data + } + + pub(super) fn start_snapshot(&self) -> RegionSnapshot { + debug!("RegionConstraintCollector: start_snapshot"); + RegionSnapshot { any_unifications: self.storage.any_unifications } + } + + pub(super) fn rollback_to(&mut self, snapshot: RegionSnapshot) { + debug!("RegionConstraintCollector: rollback_to({:?})", snapshot); + self.storage.any_unifications = snapshot.any_unifications; + } + + pub(super) fn new_region_var(&mut self, universe: UniverseIndex) -> RegionVid { + let vid = self.storage.var_infos.push(RegionVariableInfo { universe }); + + let u_vid = self.unification_table_mut().new_key(RegionVariableValue::Unknown { universe }); + assert_eq!(vid, u_vid.vid); + self.undo_log.push(AddVar(vid)); + debug!("created new region variable {:?} in {:?}", vid, universe); + vid + } + + fn add_constraint(&mut self, constraint: Constraint<'db>) { + // cannot add constraints once regions are resolved + debug!("RegionConstraintCollector: add_constraint({:?})", constraint); + + let index = self.storage.data.constraints.len(); + self.storage.data.constraints.push(constraint); + self.undo_log.push(AddConstraint(index)); + } + + pub(super) fn make_eqregion(&mut self, a: Region<'db>, b: Region<'db>) { + if a != b { + // Eventually, it would be nice to add direct support for + // equating regions. + self.make_subregion(a, b); + self.make_subregion(b, a); + + match (a.kind(), b.kind()) { + (RegionKind::ReVar(a), RegionKind::ReVar(b)) => { + debug!("make_eqregion: unifying {:?} with {:?}", a, b); + if self.unification_table_mut().unify_var_var(a, b).is_ok() { + self.storage.any_unifications = true; + } + } + (RegionKind::ReVar(vid), _) => { + debug!("make_eqregion: unifying {:?} with {:?}", vid, b); + if self + .unification_table_mut() + .unify_var_value(vid, RegionVariableValue::Known { value: b }) + .is_ok() + { + self.storage.any_unifications = true; + }; + } + (_, RegionKind::ReVar(vid)) => { + debug!("make_eqregion: unifying {:?} with {:?}", a, vid); + if self + .unification_table_mut() + .unify_var_value(vid, RegionVariableValue::Known { value: a }) + .is_ok() + { + self.storage.any_unifications = true; + }; + } + (_, _) => {} + } + } + } + + #[instrument(skip(self), level = "debug")] + pub(super) fn make_subregion(&mut self, sub: Region<'db>, sup: Region<'db>) { + // cannot add constraints once regions are resolved + + match (sub.kind(), sup.kind()) { + (RegionKind::ReBound(..), _) | (_, RegionKind::ReBound(..)) => { + panic!("cannot relate bound region: {sub:?} <= {sup:?}"); + } + (_, RegionKind::ReStatic) => { + // all regions are subregions of static, so we can ignore this + } + (RegionKind::ReVar(sub_id), RegionKind::ReVar(sup_id)) => { + self.add_constraint(Constraint::VarSubVar(sub_id, sup_id)); + } + (_, RegionKind::ReVar(sup_id)) => { + self.add_constraint(Constraint::RegSubVar(sub, sup_id)); + } + (RegionKind::ReVar(sub_id), _) => { + self.add_constraint(Constraint::VarSubReg(sub_id, sup)); + } + _ => { + self.add_constraint(Constraint::RegSubReg(sub, sup)); + } + } + } + + /// Resolves a region var to its value in the unification table, if it exists. + /// Otherwise, it is resolved to the root `ReVar` in the table. + pub fn opportunistic_resolve_var( + &mut self, + cx: DbInterner<'db>, + vid: RegionVid, + ) -> Region<'db> { + let mut ut = self.unification_table_mut(); + let root_vid = ut.find(vid).vid; + match ut.probe_value(root_vid) { + RegionVariableValue::Known { value } => value, + RegionVariableValue::Unknown { .. } => Region::new_var(cx, root_vid), + } + } + + pub fn probe_value(&mut self, vid: RegionVid) -> Result, UniverseIndex> { + match self.unification_table_mut().probe_value(vid) { + RegionVariableValue::Known { value } => Ok(value), + RegionVariableValue::Unknown { universe } => Err(universe), + } + } + + fn combine_map(&mut self, t: CombineMapType) -> &mut CombineMap<'db> { + match t { + Glb => &mut self.storage.glbs, + Lub => &mut self.storage.lubs, + } + } + + fn combine_vars( + &mut self, + cx: DbInterner<'db>, + t: CombineMapType, + a: Region<'db>, + b: Region<'db>, + ) -> Region<'db> { + let vars = TwoRegions { a, b }; + if let Some(c) = self.combine_map(t.clone()).get(&vars) { + return Region::new_var(cx, *c); + } + let a_universe = self.universe(a); + let b_universe = self.universe(b); + let c_universe = cmp::max(a_universe, b_universe); + let c = self.new_region_var(c_universe); + self.combine_map(t.clone()).insert(vars.clone(), c); + self.undo_log.push(AddCombination(t.clone(), vars)); + let new_r = Region::new_var(cx, c); + for old_r in [a, b] { + match t { + Glb => self.make_subregion(new_r, old_r), + Lub => self.make_subregion(old_r, new_r), + } + } + debug!("combine_vars() c={:?}", c); + new_r + } + + pub fn universe(&mut self, region: Region<'db>) -> UniverseIndex { + match region.kind() { + RegionKind::ReStatic + | RegionKind::ReErased + | RegionKind::ReLateParam(..) + | RegionKind::ReEarlyParam(..) + | RegionKind::ReError(_) => UniverseIndex::ROOT, + RegionKind::RePlaceholder(placeholder) => placeholder.universe, + RegionKind::ReVar(vid) => match self.probe_value(vid) { + Ok(value) => self.universe(value), + Err(universe) => universe, + }, + RegionKind::ReBound(..) => panic!("universe(): encountered bound region {region:?}"), + } + } + + #[inline] + fn unification_table_mut(&mut self) -> super::UnificationTable<'_, 'db, RegionVidKey<'db>> { + ut::UnificationTable::with_log(&mut self.storage.unification_table, self.undo_log) + } +} + +impl fmt::Debug for RegionSnapshot { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "RegionSnapshot") + } +} + +impl<'db> fmt::Debug for GenericKind<'db> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + GenericKind::Param(ref p) => write!(f, "{p:?}"), + GenericKind::Placeholder(ref p) => write!(f, "{p:?}"), + GenericKind::Alias(ref p) => write!(f, "{p:?}"), + } + } +} + +impl<'db> fmt::Display for GenericKind<'db> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + GenericKind::Param(ref p) => write!(f, "{p:?}"), + GenericKind::Placeholder(ref p) => write!(f, "{p:?}"), + GenericKind::Alias(ref p) => write!(f, "{p}"), + } + } +} + +impl<'db> GenericKind<'db> { + pub fn to_ty(&self, interner: DbInterner<'db>) -> Ty<'db> { + match *self { + GenericKind::Param(ref p) => (*p).to_ty(interner), + GenericKind::Placeholder(ref p) => Ty::new_placeholder(interner, *p), + GenericKind::Alias(ref p) => (*p).to_ty(interner), + } + } +} + +impl<'db> VerifyBound<'db> { + pub fn must_hold(&self) -> bool { + match self { + VerifyBound::IfEq(..) => false, + VerifyBound::OutlivedBy(re) => re.is_static(), + VerifyBound::IsEmpty => false, + VerifyBound::AnyBound(bs) => bs.iter().any(|b| b.must_hold()), + VerifyBound::AllBounds(bs) => bs.iter().all(|b| b.must_hold()), + } + } + + pub fn cannot_hold(&self) -> bool { + match self { + VerifyBound::IfEq(..) => false, + VerifyBound::IsEmpty => false, + VerifyBound::OutlivedBy(_) => false, + VerifyBound::AnyBound(bs) => bs.iter().all(|b| b.cannot_hold()), + VerifyBound::AllBounds(bs) => bs.iter().any(|b| b.cannot_hold()), + } + } + + pub fn or(self, vb: VerifyBound<'db>) -> VerifyBound<'db> { + if self.must_hold() || vb.cannot_hold() { + self + } else if self.cannot_hold() || vb.must_hold() { + vb + } else { + VerifyBound::AnyBound(vec![self, vb]) + } + } +} + +impl<'db> RegionConstraintData<'db> { + /// Returns `true` if this region constraint data contains no constraints, and `false` + /// otherwise. + pub fn is_empty(&self) -> bool { + let RegionConstraintData { constraints, member_constraints, verifys } = self; + constraints.is_empty() && member_constraints.is_empty() && verifys.is_empty() + } +} + +impl<'db> Rollback> for RegionConstraintStorage<'db> { + fn reverse(&mut self, undo: UndoLog<'db>) { + match undo { + AddVar(vid) => { + self.var_infos.pop().unwrap(); + assert_eq!(self.var_infos.len(), vid.index()); + } + AddConstraint(index) => { + self.data.constraints.pop().unwrap(); + assert_eq!(self.data.constraints.len(), index); + } + AddVerify(index) => { + self.data.verifys.pop(); + assert_eq!(self.data.verifys.len(), index); + } + AddCombination(Glb, ref regions) => { + self.glbs.remove(regions); + } + AddCombination(Lub, ref regions) => { + self.lubs.remove(regions); + } + } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/generalize.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/generalize.rs new file mode 100644 index 000000000000..de336c69b318 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/generalize.rs @@ -0,0 +1,720 @@ +//! Type generation code. + +use std::mem; + +use rustc_hash::FxHashMap; +use rustc_type_ir::error::TypeError; +use rustc_type_ir::inherent::{Const as _, IntoKind, Ty as _}; +use rustc_type_ir::relate::VarianceDiagInfo; +use rustc_type_ir::{ + AliasRelationDirection, AliasTyKind, ConstVid, InferConst, InferCtxtLike, InferTy, RegionKind, + TermKind, TyVid, UniverseIndex, Variance, +}; +use rustc_type_ir::{Interner, TypeVisitable, TypeVisitableExt}; +use tracing::{debug, instrument, warn}; + +use super::{ + PredicateEmittingRelation, Relate, RelateResult, StructurallyRelateAliases, TypeRelation, +}; +use crate::next_solver::infer::type_variable::TypeVariableValue; +use crate::next_solver::infer::unify_key::ConstVariableValue; +use crate::next_solver::infer::{InferCtxt, relate}; +use crate::next_solver::util::MaxUniverse; +use crate::next_solver::{ + AliasTy, Binder, ClauseKind, Const, ConstKind, DbInterner, GenericArgs, PredicateKind, + ProjectionPredicate, Region, SolverDefId, Term, TermVid, Ty, TyKind, TypingMode, + UnevaluatedConst, +}; + +impl<'db> InferCtxt<'db> { + /// The idea is that we should ensure that the type variable `target_vid` + /// is equal to, a subtype of, or a supertype of `source_ty`. + /// + /// For this, we will instantiate `target_vid` with a *generalized* version + /// of `source_ty`. Generalization introduces other inference variables wherever + /// subtyping could occur. This also does the occurs checks, detecting whether + /// instantiating `target_vid` would result in a cyclic type. We eagerly error + /// in this case. + /// + /// This is *not* expected to be used anywhere except for an implementation of + /// `TypeRelation`. Do not use this, and instead please use `At::eq`, for all + /// other usecases (i.e. setting the value of a type var). + #[instrument(level = "debug", skip(self, relation))] + pub fn instantiate_ty_var>>( + &self, + relation: &mut R, + target_is_expected: bool, + target_vid: TyVid, + instantiation_variance: Variance, + source_ty: Ty<'db>, + ) -> RelateResult<'db, ()> { + debug_assert!(self.inner.borrow_mut().type_variables().probe(target_vid).is_unknown()); + + // Generalize `source_ty` depending on the current variance. As an example, assume + // `?target <: &'x ?1`, where `'x` is some free region and `?1` is an inference + // variable. + // + // Then the `generalized_ty` would be `&'?2 ?3`, where `'?2` and `?3` are fresh + // region/type inference variables. + // + // We then relate `generalized_ty <: source_ty`, adding constraints like `'x: '?2` and + // `?1 <: ?3`. + let Generalization { value_may_be_infer: generalized_ty, has_unconstrained_ty_var } = self + .generalize( + relation.structurally_relate_aliases(), + target_vid, + instantiation_variance, + source_ty, + )?; + + // Constrain `b_vid` to the generalized type `generalized_ty`. + if let TyKind::Infer(InferTy::TyVar(generalized_vid)) = generalized_ty.kind() { + self.inner.borrow_mut().type_variables().equate(target_vid, generalized_vid); + } else { + self.inner.borrow_mut().type_variables().instantiate(target_vid, generalized_ty); + } + + // See the comment on `Generalization::has_unconstrained_ty_var`. + if has_unconstrained_ty_var { + relation.register_predicates([ClauseKind::WellFormed(generalized_ty.into())]); + } + + // Finally, relate `generalized_ty` to `source_ty`, as described in previous comment. + // + // FIXME(#16847): This code is non-ideal because all these subtype + // relations wind up attributed to the same spans. We need + // to associate causes/spans with each of the relations in + // the stack to get this right. + if generalized_ty.is_ty_var() { + // This happens for cases like `::Assoc == ?0`. + // We can't instantiate `?0` here as that would result in a + // cyclic type. We instead delay the unification in case + // the alias can be normalized to something which does not + // mention `?0`. + let (lhs, rhs, direction) = match instantiation_variance { + Variance::Invariant => { + (generalized_ty.into(), source_ty.into(), AliasRelationDirection::Equate) + } + Variance::Covariant => { + (generalized_ty.into(), source_ty.into(), AliasRelationDirection::Subtype) + } + Variance::Contravariant => { + (source_ty.into(), generalized_ty.into(), AliasRelationDirection::Subtype) + } + Variance::Bivariant => unreachable!("bivariant generalization"), + }; + + relation.register_predicates([PredicateKind::AliasRelate(lhs, rhs, direction)]); + } else { + // NOTE: The `instantiation_variance` is not the same variance as + // used by the relation. When instantiating `b`, `target_is_expected` + // is flipped and the `instantiation_variance` is also flipped. To + // constrain the `generalized_ty` while using the original relation, + // we therefore only have to flip the arguments. + // + // ```ignore (not code) + // ?a rel B + // instantiate_ty_var(?a, B) # expected and variance not flipped + // B' rel B + // ``` + // or + // ```ignore (not code) + // A rel ?b + // instantiate_ty_var(?b, A) # expected and variance flipped + // A rel A' + // ``` + if target_is_expected { + relation.relate(generalized_ty, source_ty)?; + } else { + debug!("flip relation"); + relation.relate(source_ty, generalized_ty)?; + } + } + + Ok(()) + } + + /// Instantiates the const variable `target_vid` with the given constant. + /// + /// This also tests if the given const `ct` contains an inference variable which was previously + /// unioned with `target_vid`. If this is the case, inferring `target_vid` to `ct` + /// would result in an infinite type as we continuously replace an inference variable + /// in `ct` with `ct` itself. + /// + /// This is especially important as unevaluated consts use their parents generics. + /// They therefore often contain unused args, making these errors far more likely. + /// + /// A good example of this is the following: + /// + /// ```compile_fail,E0308 + /// #![feature(generic_const_exprs)] + /// + /// fn bind(value: [u8; N]) -> [u8; 3 + 4] { + /// todo!() + /// } + /// + /// fn main() { + /// let mut arr = Default::default(); + /// arr = bind(arr); + /// } + /// ``` + /// + /// Here `3 + 4` ends up as `ConstKind::Unevaluated` which uses the generics + /// of `fn bind` (meaning that its args contain `N`). + /// + /// `bind(arr)` now infers that the type of `arr` must be `[u8; N]`. + /// The assignment `arr = bind(arr)` now tries to equate `N` with `3 + 4`. + /// + /// As `3 + 4` contains `N` in its args, this must not succeed. + /// + /// See `tests/ui/const-generics/occurs-check/` for more examples where this is relevant. + #[instrument(level = "debug", skip(self, relation))] + pub(crate) fn instantiate_const_var>>( + &self, + relation: &mut R, + target_is_expected: bool, + target_vid: ConstVid, + source_ct: Const<'db>, + ) -> RelateResult<'db, ()> { + // FIXME(generic_const_exprs): Occurs check failures for unevaluated + // constants and generic expressions are not yet handled correctly. + let Generalization { value_may_be_infer: generalized_ct, has_unconstrained_ty_var } = self + .generalize( + relation.structurally_relate_aliases(), + target_vid, + Variance::Invariant, + source_ct, + )?; + + debug_assert!(!generalized_ct.is_ct_infer()); + if has_unconstrained_ty_var { + panic!("unconstrained ty var when generalizing `{source_ct:?}`"); + } + + self.inner + .borrow_mut() + .const_unification_table() + .union_value(target_vid, ConstVariableValue::Known { value: generalized_ct }); + + // Make sure that the order is correct when relating the + // generalized const and the source. + if target_is_expected { + relation.relate_with_variance( + Variance::Invariant, + VarianceDiagInfo::default(), + generalized_ct, + source_ct, + )?; + } else { + relation.relate_with_variance( + Variance::Invariant, + VarianceDiagInfo::default(), + source_ct, + generalized_ct, + )?; + } + + Ok(()) + } + + /// Attempts to generalize `source_term` for the type variable `target_vid`. + /// This checks for cycles -- that is, whether `source_term` references `target_vid`. + fn generalize> + Relate>>( + &self, + structurally_relate_aliases: StructurallyRelateAliases, + target_vid: impl Into, + ambient_variance: Variance, + source_term: T, + ) -> RelateResult<'db, Generalization> { + assert!(!source_term.clone().has_escaping_bound_vars()); + let (for_universe, root_vid) = match target_vid.into() { + TermVid::Ty(ty_vid) => { + (self.probe_ty_var(ty_vid).unwrap_err(), TermVid::Ty(self.root_var(ty_vid))) + } + TermVid::Const(ct_vid) => ( + self.probe_const_var(ct_vid).unwrap_err(), + TermVid::Const(self.inner.borrow_mut().const_unification_table().find(ct_vid).vid), + ), + }; + + let mut generalizer = Generalizer { + infcx: self, + structurally_relate_aliases, + root_vid, + for_universe, + root_term: source_term.into(), + ambient_variance, + in_alias: false, + cache: Default::default(), + has_unconstrained_ty_var: false, + }; + + let value_may_be_infer = generalizer.relate(source_term, source_term)?; + let has_unconstrained_ty_var = generalizer.has_unconstrained_ty_var; + Ok(Generalization { value_may_be_infer, has_unconstrained_ty_var }) + } +} + +/// The "generalizer" is used when handling inference variables. +/// +/// The basic strategy for handling a constraint like `?A <: B` is to +/// apply a "generalization strategy" to the term `B` -- this replaces +/// all the lifetimes in the term `B` with fresh inference variables. +/// (You can read more about the strategy in this [blog post].) +/// +/// As an example, if we had `?A <: &'x u32`, we would generalize `&'x +/// u32` to `&'0 u32` where `'0` is a fresh variable. This becomes the +/// value of `A`. Finally, we relate `&'0 u32 <: &'x u32`, which +/// establishes `'0: 'x` as a constraint. +/// +/// [blog post]: https://is.gd/0hKvIr +struct Generalizer<'me, 'db> { + infcx: &'me InferCtxt<'db>, + + /// Whether aliases should be related structurally. If not, we have to + /// be careful when generalizing aliases. + structurally_relate_aliases: StructurallyRelateAliases, + + /// The vid of the type variable that is in the process of being + /// instantiated. If we find this within the value we are folding, + /// that means we would have created a cyclic value. + root_vid: TermVid, + + /// The universe of the type variable that is in the process of being + /// instantiated. If we find anything that this universe cannot name, + /// we reject the relation. + for_universe: UniverseIndex, + + /// The root term (const or type) we're generalizing. Used for cycle errors. + root_term: Term<'db>, + + /// After we generalize this type, we are going to relate it to + /// some other type. What will be the variance at this point? + ambient_variance: Variance, + + /// This is set once we're generalizing the arguments of an alias. + /// + /// This is necessary to correctly handle + /// `::Assoc>::Assoc == ?0`. This equality can + /// hold by either normalizing the outer or the inner associated type. + in_alias: bool, + + cache: FxHashMap<(Ty<'db>, Variance, bool), Ty<'db>>, + + /// See the field `has_unconstrained_ty_var` in `Generalization`. + has_unconstrained_ty_var: bool, +} + +impl<'db> Generalizer<'_, 'db> { + /// Create an error that corresponds to the term kind in `root_term` + fn cyclic_term_error(&self) -> TypeError> { + match self.root_term.kind() { + TermKind::Ty(ty) => TypeError::CyclicTy(ty), + TermKind::Const(ct) => TypeError::CyclicConst(ct), + } + } + + /// Create a new type variable in the universe of the target when + /// generalizing an alias. This has to set `has_unconstrained_ty_var` + /// if we're currently in a bivariant context. + fn next_ty_var_for_alias(&mut self) -> Ty<'db> { + self.has_unconstrained_ty_var |= self.ambient_variance == Variance::Bivariant; + self.infcx.next_ty_var_in_universe(self.for_universe) + } + + /// An occurs check failure inside of an alias does not mean + /// that the types definitely don't unify. We may be able + /// to normalize the alias after all. + /// + /// We handle this by lazily equating the alias and generalizing + /// it to an inference variable. In the new solver, we always + /// generalize to an infer var unless the alias contains escaping + /// bound variables. + /// + /// Correctly handling aliases with escaping bound variables is + /// difficult and currently incomplete in two opposite ways: + /// - if we get an occurs check failure in the alias, replace it with a new infer var. + /// This causes us to later emit an alias-relate goal and is incomplete in case the + /// alias normalizes to type containing one of the bound variables. + /// - if the alias contains an inference variable not nameable by `for_universe`, we + /// continue generalizing the alias. This ends up pulling down the universe of the + /// inference variable and is incomplete in case the alias would normalize to a type + /// which does not mention that inference variable. + fn generalize_alias_ty( + &mut self, + alias: AliasTy<'db>, + ) -> Result, TypeError>> { + // We do not eagerly replace aliases with inference variables if they have + // escaping bound vars, see the method comment for details. However, when we + // are inside of an alias with escaping bound vars replacing nested aliases + // with inference variables can cause incorrect ambiguity. + // + // cc trait-system-refactor-initiative#110 + if self.infcx.next_trait_solver() && !alias.has_escaping_bound_vars() && !self.in_alias { + return Ok(self.next_ty_var_for_alias()); + } + + let is_nested_alias = mem::replace(&mut self.in_alias, true); + let result = match self.relate(alias, alias) { + Ok(alias) => Ok(alias.to_ty(self.cx())), + Err(e) => { + if is_nested_alias { + return Err(e); + } else { + let mut visitor = MaxUniverse::new(); + alias.visit_with(&mut visitor); + let infer_replacement_is_complete = + self.for_universe.can_name(visitor.max_universe()) + && !alias.has_escaping_bound_vars(); + if !infer_replacement_is_complete { + warn!("may incompletely handle alias type: {alias:?}"); + } + + debug!("generalization failure in alias"); + Ok(self.next_ty_var_for_alias()) + } + } + }; + self.in_alias = is_nested_alias; + result + } +} + +impl<'db> TypeRelation> for Generalizer<'_, 'db> { + fn cx(&self) -> DbInterner<'db> { + self.infcx.interner + } + + fn relate_item_args( + &mut self, + item_def_id: SolverDefId, + a_arg: GenericArgs<'db>, + b_arg: GenericArgs<'db>, + ) -> RelateResult<'db, GenericArgs<'db>> { + if self.ambient_variance == Variance::Invariant { + // Avoid fetching the variance if we are in an invariant + // context; no need, and it can induce dependency cycles + // (e.g., #41849). + relate::relate_args_invariantly(self, a_arg, b_arg) + } else { + let tcx = self.cx(); + let opt_variances = tcx.variances_of(item_def_id); + relate::relate_args_with_variances( + self, + item_def_id, + opt_variances, + a_arg, + b_arg, + false, + ) + } + } + + #[instrument(level = "debug", skip(self, variance, b), ret)] + fn relate_with_variance>>( + &mut self, + variance: Variance, + _info: VarianceDiagInfo>, + a: T, + b: T, + ) -> RelateResult<'db, T> { + let old_ambient_variance = self.ambient_variance; + self.ambient_variance = self.ambient_variance.xform(variance); + debug!(?self.ambient_variance, "new ambient variance"); + // Recursive calls to `relate` can overflow the stack. For example a deeper version of + // `ui/associated-consts/issue-93775.rs`. + let r = self.relate(a, b); + self.ambient_variance = old_ambient_variance; + r + } + + #[instrument(level = "debug", skip(self, t2), ret)] + fn tys(&mut self, t: Ty<'db>, t2: Ty<'db>) -> RelateResult<'db, Ty<'db>> { + assert_eq!(t, t2); // we are misusing TypeRelation here; both LHS and RHS ought to be == + + if let Some(result) = self.cache.get(&(t, self.ambient_variance, self.in_alias)) { + return Ok(*result); + } + + // Check to see whether the type we are generalizing references + // any other type variable related to `vid` via + // subtyping. This is basically our "occurs check", preventing + // us from creating infinitely sized types. + let g = match t.kind() { + TyKind::Infer( + InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_), + ) => { + panic!("unexpected infer type: {t:?}") + } + + TyKind::Infer(InferTy::TyVar(vid)) => { + let mut inner = self.infcx.inner.borrow_mut(); + let vid = inner.type_variables().root_var(vid); + if TermVid::Ty(vid) == self.root_vid { + // If sub-roots are equal, then `root_vid` and + // `vid` are related via subtyping. + Err(self.cyclic_term_error()) + } else { + let probe = inner.type_variables().probe(vid); + match probe { + TypeVariableValue::Known { value: u } => { + drop(inner); + self.relate(u, u) + } + TypeVariableValue::Unknown { universe } => { + match self.ambient_variance { + // Invariant: no need to make a fresh type variable + // if we can name the universe. + Variance::Invariant => { + if self.for_universe.can_name(universe) { + return Ok(t); + } + } + + // Bivariant: make a fresh var, but remember that + // it is unconstrained. See the comment in + // `Generalization`. + Variance::Bivariant => self.has_unconstrained_ty_var = true, + + // Co/contravariant: this will be + // sufficiently constrained later on. + Variance::Covariant | Variance::Contravariant => (), + } + + let origin = inner.type_variables().var_origin(vid); + let new_var_id = + inner.type_variables().new_var(self.for_universe, origin); + // If we're in the new solver and create a new inference + // variable inside of an alias we eagerly constrain that + // inference variable to prevent unexpected ambiguity errors. + // + // This is incomplete as it pulls down the universe of the + // original inference variable, even though the alias could + // normalize to a type which does not refer to that type at + // all. I don't expect this to cause unexpected errors in + // practice. + // + // We only need to do so for type and const variables, as + // region variables do not impact normalization, and will get + // correctly constrained by `AliasRelate` later on. + // + // cc trait-system-refactor-initiative#108 + if self.infcx.next_trait_solver() + && !matches!( + self.infcx.typing_mode_unchecked(), + TypingMode::Coherence + ) + && self.in_alias + { + inner.type_variables().equate(vid, new_var_id); + } + + debug!("replacing original vid={:?} with new={:?}", vid, new_var_id); + Ok(Ty::new_var(self.infcx.interner, new_var_id)) + } + } + } + } + + TyKind::Infer(InferTy::IntVar(_) | InferTy::FloatVar(_)) => { + // No matter what mode we are in, + // integer/floating-point types must be equal to be + // relatable. + Ok(t) + } + + TyKind::Placeholder(placeholder) => { + if self.for_universe.can_name(placeholder.universe) { + Ok(t) + } else { + debug!( + "root universe {:?} cannot name placeholder in universe {:?}", + self.for_universe, placeholder.universe + ); + Err(TypeError::Mismatch) + } + } + + TyKind::Alias(_, data) => match self.structurally_relate_aliases { + StructurallyRelateAliases::No => self.generalize_alias_ty(data), + StructurallyRelateAliases::Yes => relate::structurally_relate_tys(self, t, t), + }, + + _ => relate::structurally_relate_tys(self, t, t), + }?; + + self.cache.insert((t, self.ambient_variance, self.in_alias), g); + Ok(g) + } + + #[instrument(level = "debug", skip(self, r2), ret)] + fn regions(&mut self, r: Region<'db>, r2: Region<'db>) -> RelateResult<'db, Region<'db>> { + assert_eq!(r, r2); // we are misusing TypeRelation here; both LHS and RHS ought to be == + + match r.kind() { + // Never make variables for regions bound within the type itself, + // nor for erased regions. + RegionKind::ReBound(..) | RegionKind::ReErased => { + return Ok(r); + } + + // It doesn't really matter for correctness if we generalize ReError, + // since we're already on a doomed compilation path. + RegionKind::ReError(_) => { + return Ok(r); + } + + RegionKind::RePlaceholder(..) + | RegionKind::ReVar(..) + | RegionKind::ReStatic + | RegionKind::ReEarlyParam(..) + | RegionKind::ReLateParam(..) => { + // see common code below + } + } + + // If we are in an invariant context, we can re-use the region + // as is, unless it happens to be in some universe that we + // can't name. + if let Variance::Invariant = self.ambient_variance { + let r_universe = self.infcx.universe_of_region(r); + if self.for_universe.can_name(r_universe) { + return Ok(r); + } + } + + Ok(self.infcx.next_region_var_in_universe(self.for_universe)) + } + + #[instrument(level = "debug", skip(self, c2), ret)] + fn consts(&mut self, c: Const<'db>, c2: Const<'db>) -> RelateResult<'db, Const<'db>> { + assert_eq!(c, c2); // we are misusing TypeRelation here; both LHS and RHS ought to be == + + match c.kind() { + ConstKind::Infer(InferConst::Var(vid)) => { + // If root const vids are equal, then `root_vid` and + // `vid` are related and we'd be inferring an infinitely + // deep const. + if TermVid::Const( + self.infcx.inner.borrow_mut().const_unification_table().find(vid).vid, + ) == self.root_vid + { + return Err(self.cyclic_term_error()); + } + + let mut inner = self.infcx.inner.borrow_mut(); + let variable_table = &mut inner.const_unification_table(); + match variable_table.probe_value(vid) { + ConstVariableValue::Known { value: u } => { + drop(inner); + self.relate(u, u) + } + ConstVariableValue::Unknown { origin, universe } => { + if self.for_universe.can_name(universe) { + Ok(c) + } else { + let new_var_id = variable_table + .new_key(ConstVariableValue::Unknown { + origin, + universe: self.for_universe, + }) + .vid; + + // See the comment for type inference variables + // for more details. + if self.infcx.next_trait_solver() + && !matches!( + self.infcx.typing_mode_unchecked(), + TypingMode::Coherence + ) + && self.in_alias + { + variable_table.union(vid, new_var_id); + } + Ok(Const::new_var(self.infcx.interner, new_var_id)) + } + } + } + } + // FIXME: Unevaluated constants are also not rigid, so the current + // approach of always relating them structurally is incomplete. + // + // FIXME: remove this branch once `structurally_relate_consts` is fully + // structural. + ConstKind::Unevaluated(UnevaluatedConst { def, args }) => { + let args = self.relate_with_variance( + Variance::Invariant, + VarianceDiagInfo::default(), + args, + args, + )?; + Ok(Const::new_unevaluated(self.infcx.interner, UnevaluatedConst { def, args })) + } + ConstKind::Placeholder(placeholder) => { + if self.for_universe.can_name(placeholder.universe) { + Ok(c) + } else { + debug!( + "root universe {:?} cannot name placeholder in universe {:?}", + self.for_universe, placeholder.universe + ); + Err(TypeError::Mismatch) + } + } + _ => relate::structurally_relate_consts(self, c, c), + } + } + + #[instrument(level = "debug", skip(self), ret)] + fn binders( + &mut self, + a: Binder<'db, T>, + _: Binder<'db, T>, + ) -> RelateResult<'db, Binder<'db, T>> + where + T: Relate>, + { + let result = self.relate(a.skip_binder(), a.skip_binder())?; + Ok(a.rebind(result)) + } +} + +/// Result from a generalization operation. This includes +/// not only the generalized type, but also a bool flag +/// indicating whether further WF checks are needed. +#[derive(Debug)] +struct Generalization { + /// When generalizing `::Assoc` or + /// `::Assoc>>::Assoc` + /// for `?0` generalization returns an inference + /// variable. + /// + /// This has to be handled wotj care as it can + /// otherwise very easily result in infinite + /// recursion. + pub value_may_be_infer: T, + + /// In general, we do not check whether all types which occur during + /// type checking are well-formed. We only check wf of user-provided types + /// and when actually using a type, e.g. for method calls. + /// + /// This means that when subtyping, we may end up with unconstrained + /// inference variables if a generalized type has bivariant parameters. + /// A parameter may only be bivariant if it is constrained by a projection + /// bound in a where-clause. As an example, imagine a type: + /// + /// struct Foo where A: Iterator { + /// data: A + /// } + /// + /// here, `A` will be covariant, but `B` is unconstrained. + /// + /// However, whatever it is, for `Foo` to be WF, it must be equal to `A::Item`. + /// If we have an input `Foo`, then after generalization we will wind + /// up with a type like `Foo`. When we enforce `Foo <: Foo`, + /// we will wind up with the requirement that `?A <: ?C`, but no particular + /// relationship between `?B` and `?D` (after all, these types may be completely + /// different). If we do nothing else, this may mean that `?D` goes unconstrained + /// (as in #41677). To avoid this we emit a `WellFormed` obligation in these cases. + pub has_unconstrained_ty_var: bool, +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/higher_ranked.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/higher_ranked.rs new file mode 100644 index 000000000000..bb80c5157109 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/higher_ranked.rs @@ -0,0 +1,89 @@ +//! Helper routines for higher-ranked things. See the `doc` module at +//! the end of the file for details. + +use rustc_type_ir::TypeFoldable; +use rustc_type_ir::{BoundVar, UniverseIndex}; +use tracing::{debug, instrument}; + +use super::RelateResult; +use crate::next_solver::fold::FnMutDelegate; +use crate::next_solver::infer::InferCtxt; +use crate::next_solver::infer::snapshot::CombinedSnapshot; +use crate::next_solver::{ + Binder, BoundRegion, BoundTy, Const, DbInterner, PlaceholderConst, PlaceholderRegion, + PlaceholderTy, Region, Ty, +}; + +impl<'db> InferCtxt<'db> { + /// Replaces all bound variables (lifetimes, types, and constants) bound by + /// `binder` with placeholder variables in a new universe. This means that the + /// new placeholders can only be named by inference variables created after + /// this method has been called. + /// + /// This is the first step of checking subtyping when higher-ranked things are involved. + /// For more details visit the relevant sections of the [rustc dev guide]. + /// + /// `fn enter_forall` should be preferred over this method. + /// + /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html + #[instrument(level = "debug", skip(self), ret)] + pub fn enter_forall_and_leak_universe(&self, binder: Binder<'db, T>) -> T + where + T: TypeFoldable> + Clone, + { + if let Some(inner) = binder.clone().no_bound_vars() { + return inner; + } + + let next_universe = self.create_next_universe(); + + let delegate = FnMutDelegate { + regions: &mut |br: BoundRegion| { + Region::new_placeholder( + self.interner, + PlaceholderRegion { universe: next_universe, bound: br }, + ) + }, + types: &mut |bound_ty: BoundTy| { + Ty::new_placeholder( + self.interner, + PlaceholderTy { universe: next_universe, bound: bound_ty }, + ) + }, + consts: &mut |bound_var: BoundVar| { + Const::new_placeholder( + self.interner, + PlaceholderConst { universe: next_universe, bound: bound_var }, + ) + }, + }; + + debug!(?next_universe); + self.interner.replace_bound_vars_uncached(binder, delegate) + } + + /// Replaces all bound variables (lifetimes, types, and constants) bound by + /// `binder` with placeholder variables in a new universe and then calls the + /// closure `f` with the instantiated value. The new placeholders can only be + /// named by inference variables created inside of the closure `f` or afterwards. + /// + /// This is the first step of checking subtyping when higher-ranked things are involved. + /// For more details visit the relevant sections of the [rustc dev guide]. + /// + /// This method should be preferred over `fn enter_forall_and_leak_universe`. + /// + /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html + #[instrument(level = "debug", skip(self, f))] + pub fn enter_forall(&self, forall: Binder<'db, T>, f: impl FnOnce(T) -> U) -> U + where + T: TypeFoldable> + Clone, + { + // FIXME: currently we do nothing to prevent placeholders with the new universe being + // used after exiting `f`. For example region subtyping can result in outlives constraints + // that name placeholders created in this function. Nested goals from type relations can + // also contain placeholders created by this function. + let value = self.enter_forall_and_leak_universe(forall); + debug!(?value); + f(value) + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/mod.rs new file mode 100644 index 000000000000..836ae39dc525 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/mod.rs @@ -0,0 +1,13 @@ +//! This module contains the definitions of most `TypeRelation`s in the type system +//! (except for some relations used for diagnostics and heuristics in the compiler). +//! As well as the implementation of `Relate` for interned things (`Ty`/`Const`/etc). + +pub use rustc_type_ir::relate::combine::PredicateEmittingRelation; +pub use rustc_type_ir::relate::*; + +use crate::next_solver::DbInterner; + +mod generalize; +mod higher_ranked; + +pub type RelateResult<'db, T> = rustc_type_ir::relate::RelateResult, T>; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/resolve.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/resolve.rs new file mode 100644 index 000000000000..84338ade6e35 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/resolve.rs @@ -0,0 +1,62 @@ +//! Things for resolving vars in the infer context of the next-trait-solver. + +use rustc_type_ir::{ + ConstKind, FallibleTypeFolder, InferConst, InferTy, RegionKind, TyKind, TypeFoldable, + TypeFolder, TypeSuperFoldable, TypeVisitableExt, data_structures::DelayedMap, + inherent::IntoKind, +}; + +use crate::next_solver::{Const, DbInterner, Region, Ty}; + +use super::{FixupError, FixupResult, InferCtxt}; + +/////////////////////////////////////////////////////////////////////////// +// OPPORTUNISTIC VAR RESOLVER + +/// The opportunistic resolver can be used at any time. It simply replaces +/// type/const variables that have been unified with the things they have +/// been unified with (similar to `shallow_resolve`, but deep). This is +/// useful for printing messages etc but also required at various +/// points for correctness. +pub struct OpportunisticVarResolver<'a, 'db> { + infcx: &'a InferCtxt<'db>, + /// We're able to use a cache here as the folder does + /// not have any mutable state. + cache: DelayedMap, Ty<'db>>, +} + +impl<'a, 'db> OpportunisticVarResolver<'a, 'db> { + #[inline] + pub fn new(infcx: &'a InferCtxt<'db>) -> Self { + OpportunisticVarResolver { infcx, cache: Default::default() } + } +} + +impl<'a, 'db> TypeFolder> for OpportunisticVarResolver<'a, 'db> { + fn cx(&self) -> DbInterner<'db> { + self.infcx.interner + } + + #[inline] + fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> { + if !t.has_non_region_infer() { + t // micro-optimize -- if there is nothing in this type that this fold affects... + } else if let Some(ty) = self.cache.get(&t) { + *ty + } else { + let shallow = self.infcx.shallow_resolve(t); + let res = shallow.super_fold_with(self); + assert!(self.cache.insert(t, res)); + res + } + } + + fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> { + if !ct.has_non_region_infer() { + ct // micro-optimize -- if there is nothing in this const that this fold affects... + } else { + let ct = self.infcx.shallow_resolve_const(ct); + ct.super_fold_with(self) + } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs new file mode 100644 index 000000000000..eb426205575a --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs @@ -0,0 +1,111 @@ +//! Snapshotting in the infer ctxt of the next-trait-solver. + +use ena::undo_log::UndoLogs; +use rustc_type_ir::UniverseIndex; +use tracing::{debug, instrument}; + +use super::InferCtxt; +use super::region_constraints::RegionSnapshot; + +pub(crate) mod undo_log; + +use undo_log::{Snapshot, UndoLog}; + +#[must_use = "once you start a snapshot, you should always consume it"] +pub struct CombinedSnapshot { + pub(super) undo_snapshot: Snapshot, + region_constraints_snapshot: RegionSnapshot, + universe: UniverseIndex, +} + +struct VariableLengths { + region_constraints_len: usize, + type_var_len: usize, + int_var_len: usize, + float_var_len: usize, + const_var_len: usize, +} + +impl<'db> InferCtxt<'db> { + fn variable_lengths(&self) -> VariableLengths { + let mut inner = self.inner.borrow_mut(); + VariableLengths { + region_constraints_len: inner.unwrap_region_constraints().num_region_vars(), + type_var_len: inner.type_variables().num_vars(), + int_var_len: inner.int_unification_table().len(), + float_var_len: inner.float_unification_table().len(), + const_var_len: inner.const_unification_table().len(), + } + } + + pub fn in_snapshot(&self) -> bool { + UndoLogs::>::in_snapshot(&self.inner.borrow_mut().undo_log) + } + + pub fn num_open_snapshots(&self) -> usize { + UndoLogs::>::num_open_snapshots(&self.inner.borrow_mut().undo_log) + } + + fn start_snapshot(&self) -> CombinedSnapshot { + debug!("start_snapshot()"); + + let mut inner = self.inner.borrow_mut(); + + CombinedSnapshot { + undo_snapshot: inner.undo_log.start_snapshot(), + region_constraints_snapshot: inner.unwrap_region_constraints().start_snapshot(), + universe: self.universe(), + } + } + + #[instrument(skip(self, snapshot), level = "debug")] + fn rollback_to(&self, snapshot: CombinedSnapshot) { + let CombinedSnapshot { undo_snapshot, region_constraints_snapshot, universe } = snapshot; + + self.universe.set(universe); + + let mut inner = self.inner.borrow_mut(); + inner.rollback_to(undo_snapshot); + inner.unwrap_region_constraints().rollback_to(region_constraints_snapshot); + } + + #[instrument(skip(self, snapshot), level = "debug")] + fn commit_from(&self, snapshot: CombinedSnapshot) { + let CombinedSnapshot { undo_snapshot, region_constraints_snapshot: _, universe: _ } = + snapshot; + + self.inner.borrow_mut().commit(undo_snapshot); + } + + /// Execute `f` and commit the bindings if closure `f` returns `Ok(_)`. + #[instrument(skip(self, f), level = "debug")] + pub fn commit_if_ok(&self, f: F) -> Result + where + F: FnOnce(&CombinedSnapshot) -> Result, + { + let snapshot = self.start_snapshot(); + let r = f(&snapshot); + debug!("commit_if_ok() -- r.is_ok() = {}", r.is_ok()); + match r { + Ok(_) => { + self.commit_from(snapshot); + } + Err(_) => { + self.rollback_to(snapshot); + } + } + r + } + + /// Execute `f` then unroll any bindings it creates. + #[instrument(skip(self, f), level = "debug")] + pub fn probe(&self, f: F) -> R + where + F: FnOnce(&CombinedSnapshot) -> R, + { + let snapshot = self.start_snapshot(); + let r = f(&snapshot); + self.rollback_to(snapshot); + r + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs new file mode 100644 index 000000000000..0fa6421f517d --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs @@ -0,0 +1,198 @@ +//! Snapshotting in the infer ctxt of the next-trait-solver. + +use std::marker::PhantomData; + +use ena::snapshot_vec as sv; +use ena::undo_log::{Rollback, UndoLogs}; +use ena::unify as ut; +use rustc_type_ir::FloatVid; +use rustc_type_ir::IntVid; +use tracing::debug; + +use crate::next_solver::OpaqueTypeKey; +use crate::next_solver::infer::opaque_types::OpaqueHiddenType; +use crate::next_solver::infer::unify_key::ConstVidKey; +use crate::next_solver::infer::unify_key::RegionVidKey; +use crate::next_solver::infer::{InferCtxtInner, region_constraints, type_variable}; +use crate::traits; + +pub struct Snapshot { + pub(crate) undo_len: usize, +} + +/// Records the "undo" data for a single operation that affects some form of inference variable. +#[derive(Clone)] +pub(crate) enum UndoLog<'db> { + DuplicateOpaqueType, + OpaqueTypes(OpaqueTypeKey<'db>, Option>), + TypeVariables(sv::UndoLog>>), + ConstUnificationTable(sv::UndoLog>>), + IntUnificationTable(sv::UndoLog>), + FloatUnificationTable(sv::UndoLog>), + RegionConstraintCollector(region_constraints::UndoLog<'db>), + RegionUnificationTable(sv::UndoLog>>), + PushRegionObligation, +} + +macro_rules! impl_from { + ($($ctor:ident ($ty:ty),)*) => { + $( + impl<'db> From<$ty> for UndoLog<'db> { + fn from(x: $ty) -> Self { + UndoLog::$ctor(x.into()) + } + } + )* + } +} + +// Upcast from a single kind of "undoable action" to the general enum +impl_from! { + RegionConstraintCollector(region_constraints::UndoLog<'db>), + + TypeVariables(sv::UndoLog>>), + IntUnificationTable(sv::UndoLog>), + FloatUnificationTable(sv::UndoLog>), + + ConstUnificationTable(sv::UndoLog>>), + + RegionUnificationTable(sv::UndoLog>>), +} + +/// The Rollback trait defines how to rollback a particular action. +impl<'db> Rollback> for InferCtxtInner<'db> { + fn reverse(&mut self, undo: UndoLog<'db>) { + match undo { + UndoLog::DuplicateOpaqueType => self.opaque_type_storage.pop_duplicate_entry(), + UndoLog::OpaqueTypes(key, idx) => self.opaque_type_storage.remove(key, idx), + UndoLog::TypeVariables(undo) => self.type_variable_storage.reverse(undo), + UndoLog::ConstUnificationTable(undo) => self.const_unification_storage.reverse(undo), + UndoLog::IntUnificationTable(undo) => self.int_unification_storage.reverse(undo), + UndoLog::FloatUnificationTable(undo) => self.float_unification_storage.reverse(undo), + UndoLog::RegionConstraintCollector(undo) => { + self.region_constraint_storage.as_mut().unwrap().reverse(undo) + } + UndoLog::RegionUnificationTable(undo) => { + self.region_constraint_storage.as_mut().unwrap().unification_table.reverse(undo) + } + UndoLog::PushRegionObligation => { + self.region_obligations.pop(); + } + } + } +} + +/// The combined undo log for all the various unification tables. For each change to the storage +/// for any kind of inference variable, we record an UndoLog entry in the vector here. +#[derive(Clone, Default)] +pub(crate) struct InferCtxtUndoLogs<'db> { + logs: Vec>, + num_open_snapshots: usize, +} + +/// The UndoLogs trait defines how we undo a particular kind of action (of type T). We can undo any +/// action that is convertible into an UndoLog (per the From impls above). +impl<'db, T> UndoLogs for InferCtxtUndoLogs<'db> +where + UndoLog<'db>: From, +{ + #[inline] + fn num_open_snapshots(&self) -> usize { + self.num_open_snapshots + } + + #[inline] + fn push(&mut self, undo: T) { + if self.in_snapshot() { + self.logs.push(undo.into()) + } + } + + fn clear(&mut self) { + self.logs.clear(); + self.num_open_snapshots = 0; + } + + fn extend(&mut self, undos: J) + where + Self: Sized, + J: IntoIterator, + { + if self.in_snapshot() { + self.logs.extend(undos.into_iter().map(UndoLog::from)) + } + } +} + +impl<'db> InferCtxtInner<'db> { + pub fn rollback_to(&mut self, snapshot: Snapshot) { + debug!("rollback_to({})", snapshot.undo_len); + self.undo_log.assert_open_snapshot(&snapshot); + + while self.undo_log.logs.len() > snapshot.undo_len { + let undo = self.undo_log.logs.pop().unwrap(); + self.reverse(undo); + } + + self.type_variable_storage.finalize_rollback(); + + if self.undo_log.num_open_snapshots == 1 { + // After the root snapshot the undo log should be empty. + assert!(snapshot.undo_len == 0); + assert!(self.undo_log.logs.is_empty()); + } + + self.undo_log.num_open_snapshots -= 1; + } + + pub fn commit(&mut self, snapshot: Snapshot) { + debug!("commit({})", snapshot.undo_len); + + if self.undo_log.num_open_snapshots == 1 { + // The root snapshot. It's safe to clear the undo log because + // there's no snapshot further out that we might need to roll back + // to. + assert!(snapshot.undo_len == 0); + self.undo_log.logs.clear(); + } + + self.undo_log.num_open_snapshots -= 1; + } +} + +impl<'db> InferCtxtUndoLogs<'db> { + pub(crate) fn start_snapshot(&mut self) -> Snapshot { + self.num_open_snapshots += 1; + Snapshot { undo_len: self.logs.len() } + } + + pub(crate) fn region_constraints_in_snapshot( + &self, + s: &Snapshot, + ) -> impl Iterator> + Clone { + self.logs[s.undo_len..].iter().filter_map(|log| match log { + UndoLog::RegionConstraintCollector(log) => Some(log), + _ => None, + }) + } + + fn assert_open_snapshot(&self, snapshot: &Snapshot) { + // Failures here may indicate a failure to follow a stack discipline. + assert!(self.logs.len() >= snapshot.undo_len); + assert!(self.num_open_snapshots > 0); + } +} + +impl<'db> std::ops::Index for InferCtxtUndoLogs<'db> { + type Output = UndoLog<'db>; + + fn index(&self, key: usize) -> &Self::Output { + &self.logs[key] + } +} + +impl<'db> std::ops::IndexMut for InferCtxtUndoLogs<'db> { + fn index_mut(&mut self, key: usize) -> &mut Self::Output { + &mut self.logs[key] + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs new file mode 100644 index 000000000000..f1df806ab318 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs @@ -0,0 +1,175 @@ +//! Trait Resolution. See the [rustc-dev-guide] for more information on how this works. +//! +//! [rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html + +use std::{ + cmp, + hash::{Hash, Hasher}, +}; + +use rustc_type_ir::{ + PredicatePolarity, Upcast, + solve::{Certainty, NoSolution}, +}; + +use crate::next_solver::{ + Binder, DbInterner, Goal, ParamEnv, PolyTraitPredicate, Predicate, SolverDefId, TraitPredicate, + Ty, +}; + +use super::InferCtxt; + +/// The reason why we incurred this obligation; used for error reporting. +/// +/// Non-misc `ObligationCauseCode`s are stored on the heap. This gives the +/// best trade-off between keeping the type small (which makes copies cheaper) +/// while not doing too many heap allocations. +/// +/// We do not want to intern this as there are a lot of obligation causes which +/// only live for a short period of time. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ObligationCause { + /// The ID of the fn body that triggered this obligation. This is + /// used for region obligations to determine the precise + /// environment in which the region obligation should be evaluated + /// (in particular, closures can add new assumptions). See the + /// field `region_obligations` of the `FulfillmentContext` for more + /// information. + pub body_id: Option, +} + +impl ObligationCause { + #[inline] + pub fn new(body_id: SolverDefId) -> ObligationCause { + ObligationCause { body_id: Some(body_id) } + } + + #[inline(always)] + pub fn dummy() -> ObligationCause { + ObligationCause { body_id: None } + } +} + +/// An `Obligation` represents some trait reference (e.g., `i32: Eq`) for +/// which the "impl_source" must be found. The process of finding an "impl_source" is +/// called "resolving" the `Obligation`. This process consists of +/// either identifying an `impl` (e.g., `impl Eq for i32`) that +/// satisfies the obligation, or else finding a bound that is in +/// scope. The eventual result is usually a `Selection` (defined below). +#[derive(Clone, Debug)] +pub struct Obligation<'db, T> { + /// The reason we have to prove this thing. + pub cause: ObligationCause, + + /// The environment in which we should prove this thing. + pub param_env: ParamEnv<'db>, + + /// The thing we are trying to prove. + pub predicate: T, + + /// If we started proving this as a result of trying to prove + /// something else, track the total depth to ensure termination. + /// If this goes over a certain threshold, we abort compilation -- + /// in such cases, we can not say whether or not the predicate + /// holds for certain. Stupid halting problem; such a drag. + pub recursion_depth: usize, +} + +impl<'db, T: Copy> Obligation<'db, T> { + pub fn as_goal(&self) -> Goal<'db, T> { + Goal { param_env: self.param_env, predicate: self.predicate } + } +} + +impl<'db, T: PartialEq> PartialEq> for Obligation<'db, T> { + #[inline] + fn eq(&self, other: &Obligation<'db, T>) -> bool { + // Ignore `cause` and `recursion_depth`. This is a small performance + // win for a few crates, and a huge performance win for the crate in + // https://github.com/rust-lang/rustc-perf/pull/1680, which greatly + // stresses the trait system. + self.param_env == other.param_env && self.predicate == other.predicate + } +} + +impl<'db, T: Eq> Eq for Obligation<'db, T> {} + +impl<'db, T: Hash> Hash for Obligation<'db, T> { + fn hash(&self, state: &mut H) { + // See the comment on `Obligation::eq`. + self.param_env.hash(state); + self.predicate.hash(state); + } +} + +impl<'db, P> From> for Goal<'db, P> { + fn from(value: Obligation<'db, P>) -> Self { + Goal { param_env: value.param_env, predicate: value.predicate } + } +} + +pub type PredicateObligation<'db> = Obligation<'db, Predicate<'db>>; +pub type TraitObligation<'db> = Obligation<'db, TraitPredicate<'db>>; + +pub type PredicateObligations<'db> = Vec>; + +impl<'db> PredicateObligation<'db> { + /// Flips the polarity of the inner predicate. + /// + /// Given `T: Trait` predicate it returns `T: !Trait` and given `T: !Trait` returns `T: Trait`. + pub fn flip_polarity(&self, tcx: DbInterner<'db>) -> Option> { + Some(PredicateObligation { + cause: self.cause.clone(), + param_env: self.param_env, + predicate: self.predicate.flip_polarity()?, + recursion_depth: self.recursion_depth, + }) + } +} + +impl<'db, O> Obligation<'db, O> { + pub fn new( + tcx: DbInterner<'db>, + cause: ObligationCause, + param_env: ParamEnv<'db>, + predicate: impl Upcast, O>, + ) -> Obligation<'db, O> { + Self::with_depth(tcx, cause, 0, param_env, predicate) + } + + /// We often create nested obligations without setting the correct depth. + /// + /// To deal with this evaluate and fulfill explicitly update the depth + /// of nested obligations using this function. + pub fn set_depth_from_parent(&mut self, parent_depth: usize) { + self.recursion_depth = cmp::max(parent_depth + 1, self.recursion_depth); + } + + pub fn with_depth( + tcx: DbInterner<'db>, + cause: ObligationCause, + recursion_depth: usize, + param_env: ParamEnv<'db>, + predicate: impl Upcast, O>, + ) -> Obligation<'db, O> { + let predicate = predicate.upcast(tcx); + Obligation { cause, param_env, recursion_depth, predicate } + } + + pub fn misc( + tcx: DbInterner<'db>, + body_id: SolverDefId, + param_env: ParamEnv<'db>, + trait_ref: impl Upcast, O>, + ) -> Obligation<'db, O> { + Obligation::new(tcx, ObligationCause::new(body_id), param_env, trait_ref) + } + + pub fn with

Bar

+ +

Bar

+ diff --git a/src/tools/linkchecker/tests/valid/inner/foo.html b/src/tools/linkchecker/tests/valid/inner/foo.html index 3c6a7483bcd4..f30bf7182051 100644 --- a/src/tools/linkchecker/tests/valid/inner/foo.html +++ b/src/tools/linkchecker/tests/valid/inner/foo.html @@ -8,7 +8,15 @@
external links not validated Redirect + + + + +

Local

+ +

Local

+ diff --git a/src/tools/linkchecker/tests/valid/inner/redir-target.html b/src/tools/linkchecker/tests/valid/inner/redir-target.html index bd59884a01ec..ac1dec6d5b4a 100644 --- a/src/tools/linkchecker/tests/valid/inner/redir-target.html +++ b/src/tools/linkchecker/tests/valid/inner/redir-target.html @@ -1,5 +1,8 @@

Redir

+ + +

Redir

From 224428d8fd5f4d2ad28b833d6e4502d4d7952f9b Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Mon, 8 Sep 2025 16:45:58 +0100 Subject: [PATCH 277/682] Clarify intro in README and manual The first sentence a new user should see should ideally answer the questions: * What is rust-analyzer? * Why might I want to use it? The vast majority of users will be interested in using rust-analyzer inside their favourite editor. We should clarify that rust-analyzer is an LSP implementation and that it supports all the classic IDE features. Whilst it's also true that rust-analyzer is modular and organised into libraries, the first impression should (I think) focus on an overview and the primary use case. --- src/tools/rust-analyzer/README.md | 17 +++++++++++++-- .../rust-analyzer/docs/book/src/README.md | 21 ++++++++++++------- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/tools/rust-analyzer/README.md b/src/tools/rust-analyzer/README.md index 4360dea4a113..cb3a41eec5a6 100644 --- a/src/tools/rust-analyzer/README.md +++ b/src/tools/rust-analyzer/README.md @@ -4,8 +4,21 @@ alt="rust-analyzer logo">

-rust-analyzer is a modular compiler frontend for the Rust language. -It is a part of a larger rls-2.0 effort to create excellent IDE support for Rust. +rust-analyzer is a language server that provides IDE functionality for +writing Rust programs. You can use it with any editor that supports +the [Language Server +Protocol](https://microsoft.github.io/language-server-protocol/) (VS +Code, Vim, Emacs, Zed, etc). + +rust-analyzer features include go-to-definition, find-all-references, +refactorings and code completion. rust-analyzer also supports +integrated formatting (with rustfmt) and integrated diagnostics (with +rustc and clippy). + +Internally, rust-analyzer is structured as a set of libraries for +analyzing Rust code. See +[Architecture](https://rust-analyzer.github.io/book/contributing/architecture.html) +in the manual. ## Quick Start diff --git a/src/tools/rust-analyzer/docs/book/src/README.md b/src/tools/rust-analyzer/docs/book/src/README.md index 71f34e034664..060bcf0cea39 100644 --- a/src/tools/rust-analyzer/docs/book/src/README.md +++ b/src/tools/rust-analyzer/docs/book/src/README.md @@ -1,13 +1,20 @@ # rust-analyzer -At its core, rust-analyzer is a **library** for semantic analysis of -Rust code as it changes over time. This manual focuses on a specific -usage of the library -- running it as part of a server that implements +rust-analyzer is a language server that provides IDE functionality for +writing Rust programs. You can use it with any editor that supports the [Language Server -Protocol](https://microsoft.github.io/language-server-protocol/) (LSP). -The LSP allows various code editors, like VS Code, Emacs or Vim, to -implement semantic features like completion or goto definition by -talking to an external language server process. +Protocol](https://microsoft.github.io/language-server-protocol/) (VS +Code, Vim, Emacs, Zed, etc). + +rust-analyzer features include go-to-definition, find-all-references, +refactorings and code completion. rust-analyzer also supports +integrated formatting (with rustfmt) and integrated diagnostics (with +rustc and clippy). + +Internally, rust-analyzer is structured as a set of libraries for +analyzing Rust code. See +[Architecture](https://rust-analyzer.github.io/book/contributing/architecture.html) +for more details. To improve this document, send a pull request: [https://github.com/rust-lang/rust-analyzer](https://github.com/rust-lang/rust-analyzer/blob/master/docs/book/README.md) From d51f0ea172290f2e99e98a061c326af12edfbd3c Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Mon, 8 Sep 2025 19:06:24 +0200 Subject: [PATCH 278/682] Reorder test to make failures clearer --- library/std/src/sys/platform_version/darwin/tests.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/std/src/sys/platform_version/darwin/tests.rs b/library/std/src/sys/platform_version/darwin/tests.rs index 76dc4482c983..eecd58ec79e3 100644 --- a/library/std/src/sys/platform_version/darwin/tests.rs +++ b/library/std/src/sys/platform_version/darwin/tests.rs @@ -28,6 +28,9 @@ fn compare_against_sw_vers() { let subminor: i32 = sw_vers.next().unwrap_or("0").parse().unwrap(); assert_eq!(sw_vers.count(), 0); + // Test directly against the lookup + assert_eq!(lookup_version().get(), pack_os_version(major as _, minor as _, subminor as _)); + // Current version is available assert_eq!(__isOSVersionAtLeast(major, minor, subminor), 1); @@ -40,9 +43,6 @@ fn compare_against_sw_vers() { assert_eq!(__isOSVersionAtLeast(major, minor, subminor + 1), 0); assert_eq!(__isOSVersionAtLeast(major, minor + 1, subminor), 0); assert_eq!(__isOSVersionAtLeast(major + 1, minor, subminor), 0); - - // Test directly against the lookup - assert_eq!(lookup_version().get(), pack_os_version(major as _, minor as _, subminor as _)); } #[test] From fe6f8cc6f5eef1e0dc5e51ca621b32208af0da3c Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Mon, 8 Sep 2025 20:10:08 +0200 Subject: [PATCH 279/682] Weakly export platform_version symbols The symbols __isPlatformVersionAtLeast and __isOSVersionAtLeast. This allows the user to link both compiler_rt and std. --- library/std/src/sys/platform_version/darwin/public_extern.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/library/std/src/sys/platform_version/darwin/public_extern.rs b/library/std/src/sys/platform_version/darwin/public_extern.rs index 967cdb4920fd..c0848d94798f 100644 --- a/library/std/src/sys/platform_version/darwin/public_extern.rs +++ b/library/std/src/sys/platform_version/darwin/public_extern.rs @@ -77,6 +77,10 @@ use super::{current_version, pack_i32_os_version}; // NOTE: This symbol has a workaround in the compiler's symbol mangling to avoid mangling it, while // still not exposing it from non-cdylib (like `#[no_mangle]` would). #[rustc_std_internal_symbol] +// NOTE: Making this a weak symbol might not be entirely the right solution for this, `compiler_rt` +// doesn't do that, it instead makes the symbol have "hidden" visibility. But since this is placed +// in `libstd`, which might be used as a dylib, we cannot do the same here. +#[linkage = "weak"] // extern "C" is correct, Clang assumes the function cannot unwind: // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.0/clang/lib/CodeGen/CGObjC.cpp#L3980 // @@ -145,6 +149,7 @@ pub(super) extern "C" fn __isPlatformVersionAtLeast( /// Old entry point for availability. Used when compiling with older Clang versions. // SAFETY: Same as for `__isPlatformVersionAtLeast`. #[rustc_std_internal_symbol] +#[linkage = "weak"] pub(super) extern "C" fn __isOSVersionAtLeast(major: i32, minor: i32, subminor: i32) -> i32 { let version = pack_i32_os_version(major, minor, subminor); (version <= current_version()) as i32 From a6a53ace86ad7ff23b6d84379391099862f3bd1a Mon Sep 17 00:00:00 2001 From: Ryan Mehri Date: Mon, 8 Sep 2025 14:10:25 -0400 Subject: [PATCH 280/682] impl TryToNav for BuiltinType instead --- .../crates/ide-db/src/famous_defs.rs | 4 +++ .../crates/ide/src/goto_definition.rs | 27 ++-------------- .../crates/ide/src/navigation_target.rs | 31 ++++++++++++++++--- 3 files changed, 32 insertions(+), 30 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs index 8e687385086f..8eea2b81bab6 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs @@ -210,6 +210,10 @@ impl FamousDefs<'_, '_> { fn find_lang_crate(&self, origin: LangCrateOrigin) -> Option { let krate = self.1; let db = self.0.db; + if krate.origin(db) == CrateOrigin::Lang(origin) { + return Some(krate); + } + let res = krate .dependencies(db) .into_iter() diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index 002f5b3fe8f4..f768d4b68f46 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -6,13 +6,12 @@ use crate::{ navigation_target::{self, ToNav}, }; use hir::{ - AsAssocItem, AssocItem, CallableKind, FileRange, HasCrate, InFile, ModuleDef, PathResolution, - Semantics, sym, + AsAssocItem, AssocItem, CallableKind, FileRange, HasCrate, InFile, ModuleDef, Semantics, sym, }; use ide_db::{ RootDatabase, SymbolKind, base_db::{AnchoredPath, SourceDatabase}, - defs::{Definition, IdentClass, find_std_module}, + defs::{Definition, IdentClass}, famous_defs::FamousDefs, helpers::pick_best_token, }; @@ -91,9 +90,6 @@ pub(crate) fn goto_definition( if let Some(navs) = find_definition_for_known_blanket_dual_impls(sema, &token.value) { return Some(navs); } - if let Some(navs) = find_definition_for_builtin_types(sema, &token.value, edition) { - return Some(navs); - } let parent = token.value.parent()?; @@ -208,25 +204,6 @@ fn find_definition_for_known_blanket_dual_impls( Some(def_to_nav(sema.db, def)) } -// If the token is a builtin type search the definition from the rustdoc module shims. -fn find_definition_for_builtin_types( - sema: &Semantics<'_, RootDatabase>, - original_token: &SyntaxToken, - edition: Edition, -) -> Option> { - let path = original_token.parent_ancestors().find_map(ast::Path::cast)?; - let res = sema.resolve_path(&path)?; - let PathResolution::Def(ModuleDef::BuiltinType(builtin)) = res else { - return None; - }; - - let fd = FamousDefs(sema, sema.scope(path.syntax())?.krate()); - let primitive_mod = format!("prim_{}", builtin.name().display(fd.0.db, edition)); - let doc_owner = find_std_module(&fd, &primitive_mod, edition)?; - - Some(def_to_nav(sema.db, doc_owner.into())) -} - fn try_lookup_include_path( sema: &Semantics<'_, RootDatabase>, token: InFile, diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index 1b6a4b726e83..641bde5e2b82 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -5,14 +5,15 @@ use std::fmt; use arrayvec::ArrayVec; use either::Either; use hir::{ - AssocItem, FieldSource, HasContainer, HasCrate, HasSource, HirDisplay, HirFileId, InFile, - LocalSource, ModuleSource, db::ExpandDatabase, symbols::FileSymbol, + AssocItem, Crate, FieldSource, HasContainer, HasCrate, HasSource, HirDisplay, HirFileId, + InFile, LocalSource, ModuleSource, Semantics, db::ExpandDatabase, symbols::FileSymbol, }; use ide_db::{ FileId, FileRange, RootDatabase, SymbolKind, - base_db::salsa, - defs::Definition, + base_db::{CrateOrigin, LangCrateOrigin, RootQueryDb, salsa}, + defs::{Definition, find_std_module}, documentation::{Documentation, HasDocs}, + famous_defs::FamousDefs, }; use span::Edition; use stdx::never; @@ -262,8 +263,8 @@ impl TryToNav for Definition { Definition::TypeAlias(it) => it.try_to_nav(db), Definition::ExternCrateDecl(it) => it.try_to_nav(db), Definition::InlineAsmOperand(it) => it.try_to_nav(db), + Definition::BuiltinType(it) => it.try_to_nav(db), Definition::BuiltinLifetime(_) - | Definition::BuiltinType(_) | Definition::TupleField(_) | Definition::ToolModule(_) | Definition::InlineAsmRegOrRegClass(_) @@ -746,6 +747,26 @@ impl TryToNav for hir::InlineAsmOperand { } } +impl TryToNav for hir::BuiltinType { + fn try_to_nav(&self, db: &RootDatabase) -> Option> { + let sema = Semantics::new(db); + + let krate = db + .all_crates() + .iter() + .copied() + .find(|&krate| matches!(krate.data(db).origin, CrateOrigin::Lang(LangCrateOrigin::Std))) + .map(Crate::from)?; + let edition = krate.edition(db); + + let fd = FamousDefs(&sema, krate); + let primitive_mod = format!("prim_{}", self.name().display(fd.0.db, edition)); + let doc_owner = find_std_module(&fd, &primitive_mod, edition)?; + + Some(doc_owner.to_nav(db)) + } +} + #[derive(Debug)] pub struct UpmappingResult { /// The macro call site. From d80db5b814f594f2e1ecea2e375990287ba4d4df Mon Sep 17 00:00:00 2001 From: Jens Reidel Date: Mon, 8 Sep 2025 20:20:03 +0200 Subject: [PATCH 281/682] tests/codegen-llvm: Make rust-abi-arch-specific-adjustment portable This test currently only runs on RISC-V and loongarch hosts, but assumes that the host target is the -gnu target. By using minicore, we can run this test on all host targets, regardless of architecture, as long as the LLVM components are built. This also fixes this test on musl hosts of these architectures. Signed-off-by: Jens Reidel --- tests/codegen-llvm/rust-abi-arch-specific-adjustment.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/codegen-llvm/rust-abi-arch-specific-adjustment.rs b/tests/codegen-llvm/rust-abi-arch-specific-adjustment.rs index 561f081c700e..ffff4b359947 100644 --- a/tests/codegen-llvm/rust-abi-arch-specific-adjustment.rs +++ b/tests/codegen-llvm/rust-abi-arch-specific-adjustment.rs @@ -1,15 +1,19 @@ +//@ add-core-stubs //@ compile-flags: -Copt-level=3 -C no-prepopulate-passes //@ revisions: riscv64 loongarch64 -//@[riscv64] only-riscv64 //@[riscv64] compile-flags: --target riscv64gc-unknown-linux-gnu //@[riscv64] needs-llvm-components: riscv -//@[loongarch64] only-loongarch64 //@[loongarch64] compile-flags: --target loongarch64-unknown-linux-gnu //@[loongarch64] needs-llvm-components: loongarch #![crate_type = "lib"] +#![feature(no_core)] +#![no_core] + +extern crate minicore; +use minicore::*; #[no_mangle] // riscv64: define noundef i8 @arg_attr_u8(i8 noundef zeroext %x) From fc4450b1f1dedd1328bc9dabad55afc3fda872b7 Mon Sep 17 00:00:00 2001 From: Ryan Mehri Date: Mon, 8 Sep 2025 14:33:25 -0400 Subject: [PATCH 282/682] add test --- .../crates/ide/src/goto_definition.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index f768d4b68f46..d50d7f602fcb 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -3944,6 +3944,20 @@ fn main() { _ => {} } } +"#, + ); + } + + #[test] + fn goto_builtin_type() { + check( + r#" +//- /main.rs crate:main deps:std +const _: &str$0 = ""; } + +//- /libstd.rs crate:std +mod prim_str {} +// ^^^^^^^^ "#, ); } From eba09340e043262e30c80f2de12d7038389262ad Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Mon, 8 Sep 2025 21:03:06 +0200 Subject: [PATCH 283/682] report duplicate symbol added by the driver --- compiler/rustc_span/src/symbol.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index e5108d8b7e92..e1768b5cde9d 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -7,7 +7,7 @@ use std::ops::Deref; use std::{fmt, str}; use rustc_arena::DroplessArena; -use rustc_data_structures::fx::FxIndexSet; +use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_data_structures::stable_hasher::{ HashStable, StableCompare, StableHasher, ToStableHashKey, }; @@ -2868,11 +2868,20 @@ impl Interner { let byte_strs = FxIndexSet::from_iter( init.iter().copied().chain(extra.iter().copied()).map(|str| str.as_bytes()), ); - assert_eq!( - byte_strs.len(), - init.len() + extra.len(), - "duplicate symbols in the rustc symbol list and the extra symbols added by the driver", - ); + + // The order in which duplicates are reported is irrelevant. + #[expect(rustc::potential_query_instability)] + if byte_strs.len() != init.len() + extra.len() { + panic!( + "duplicate symbols in the rustc symbol list and the extra symbols added by the driver: {:?}", + FxHashSet::intersection( + &init.iter().copied().collect(), + &extra.iter().copied().collect(), + ) + .collect::>() + ) + } + Interner(Lock::new(InternerInner { arena: Default::default(), byte_strs })) } From 36c2c0a8bab6fb5c588ee6980fd8c1be659567c2 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 2 Aug 2025 10:50:04 +0200 Subject: [PATCH 284/682] fix(semicolon_inside_block): don't lint if block is in parens --- clippy_lints/src/semicolon_block.rs | 14 ++++++++++++++ tests/ui/semicolon_inside_block.fixed | 17 ++++++++++++++++- tests/ui/semicolon_inside_block.rs | 17 ++++++++++++++++- tests/ui/semicolon_inside_block.stderr | 8 ++++---- 4 files changed, 50 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/semicolon_block.rs b/clippy_lints/src/semicolon_block.rs index 1dea8f17c34b..371d62a06849 100644 --- a/clippy_lints/src/semicolon_block.rs +++ b/clippy_lints/src/semicolon_block.rs @@ -1,5 +1,6 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::SpanRangeExt; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -82,6 +83,19 @@ impl SemicolonBlock { let insert_span = tail.span.source_callsite().shrink_to_hi(); let remove_span = semi_span.with_lo(block.span.hi()); + // If the block is surrounded by parens (`({ 0 });`), the author probably knows what + // they're doing and why, so don't get in their way. + // + // This has the additional benefit of stopping the block being parsed as a function call: + // ``` + // fn foo() { + // ({ 0 }); // if we remove this `;`, this will parse as a `({ 0 })(5);` function call + // (5); + // } + if remove_span.check_source_text(cx, |src| src.contains(')')) { + return; + } + if self.semicolon_inside_block_ignore_singleline && get_line(cx, remove_span) == get_line(cx, insert_span) { return; } diff --git a/tests/ui/semicolon_inside_block.fixed b/tests/ui/semicolon_inside_block.fixed index 7308e78aae26..468f0a5b1e47 100644 --- a/tests/ui/semicolon_inside_block.fixed +++ b/tests/ui/semicolon_inside_block.fixed @@ -3,7 +3,8 @@ clippy::unused_unit, clippy::unnecessary_operation, clippy::no_effect, - clippy::single_element_loop + clippy::single_element_loop, + clippy::double_parens )] #![warn(clippy::semicolon_inside_block)] @@ -87,6 +88,20 @@ fn main() { unit_fn_block() } +#[rustfmt::skip] +fn issue15380() { + ( {0;0}); + + ({ + 0; + 0 + }); + + (({ 0 })) ; + + ( ( { 0 } ) ) ; +} + pub fn issue15388() { #[rustfmt::skip] {0; 0}; diff --git a/tests/ui/semicolon_inside_block.rs b/tests/ui/semicolon_inside_block.rs index 467bf4d779f2..101374af2647 100644 --- a/tests/ui/semicolon_inside_block.rs +++ b/tests/ui/semicolon_inside_block.rs @@ -3,7 +3,8 @@ clippy::unused_unit, clippy::unnecessary_operation, clippy::no_effect, - clippy::single_element_loop + clippy::single_element_loop, + clippy::double_parens )] #![warn(clippy::semicolon_inside_block)] @@ -87,6 +88,20 @@ fn main() { unit_fn_block() } +#[rustfmt::skip] +fn issue15380() { + ( {0;0}); + + ({ + 0; + 0 + }); + + (({ 0 })) ; + + ( ( { 0 } ) ) ; +} + pub fn issue15388() { #[rustfmt::skip] {0; 0}; diff --git a/tests/ui/semicolon_inside_block.stderr b/tests/ui/semicolon_inside_block.stderr index 23433f4e7ef9..2046dd1c36be 100644 --- a/tests/ui/semicolon_inside_block.stderr +++ b/tests/ui/semicolon_inside_block.stderr @@ -1,5 +1,5 @@ error: consider moving the `;` inside the block for consistent formatting - --> tests/ui/semicolon_inside_block.rs:38:5 + --> tests/ui/semicolon_inside_block.rs:39:5 | LL | { unit_fn_block() }; | ^^^^^^^^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL + { unit_fn_block(); } | error: consider moving the `;` inside the block for consistent formatting - --> tests/ui/semicolon_inside_block.rs:40:5 + --> tests/ui/semicolon_inside_block.rs:41:5 | LL | unsafe { unit_fn_block() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL + unsafe { unit_fn_block(); } | error: consider moving the `;` inside the block for consistent formatting - --> tests/ui/semicolon_inside_block.rs:49:5 + --> tests/ui/semicolon_inside_block.rs:50:5 | LL | / { LL | | @@ -41,7 +41,7 @@ LL ~ } | error: consider moving the `;` inside the block for consistent formatting - --> tests/ui/semicolon_inside_block.rs:63:5 + --> tests/ui/semicolon_inside_block.rs:64:5 | LL | { m!(()) }; | ^^^^^^^^^^^ From 2ffa8510a7f29af51b976d98584d7330eadbeee7 Mon Sep 17 00:00:00 2001 From: Ryan Mehri Date: Mon, 8 Sep 2025 15:59:46 -0400 Subject: [PATCH 285/682] make TryToNav take Semantics instead of RootDatabase --- .../crates/ide/src/call_hierarchy.rs | 10 +- .../crates/ide/src/doc_links/tests.rs | 2 +- .../crates/ide/src/goto_declaration.rs | 10 +- .../crates/ide/src/goto_definition.rs | 20 +- .../crates/ide/src/goto_implementation.rs | 6 +- .../crates/ide/src/goto_type_definition.rs | 2 +- .../crates/ide/src/highlight_related.rs | 2 +- .../rust-analyzer/crates/ide/src/hover.rs | 34 +-- .../crates/ide/src/hover/render.rs | 12 +- .../crates/ide/src/inlay_hints.rs | 6 +- .../crates/ide/src/inlay_hints/bounds.rs | 2 +- src/tools/rust-analyzer/crates/ide/src/lib.rs | 2 +- .../crates/ide/src/navigation_target.rs | 196 ++++++++++++------ .../crates/ide/src/references.rs | 2 +- .../rust-analyzer/crates/ide/src/runnables.rs | 15 +- .../crates/ide/src/static_index.rs | 4 +- .../crates/ide/src/test_explorer.rs | 2 +- 17 files changed, 203 insertions(+), 124 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs index 7a0405939d10..f42cead3501d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs +++ b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs @@ -67,7 +67,7 @@ pub(crate) fn incoming_calls( let def = ast::Fn::cast(node).and_then(|fn_| sema.to_def(&fn_))?; // We should return def before check if it is a test, so that we // will not continue to search for outer fn in nested fns - def.try_to_nav(sema.db).map(|nav| (def, nav)) + def.try_to_nav(sema).map(|nav| (def, nav)) }); if let Some((def, nav)) = def_nav { @@ -122,10 +122,10 @@ pub(crate) fn outgoing_calls( if exclude_tests && it.is_test(db) { return None; } - it.try_to_nav(db) + it.try_to_nav(&sema) } - hir::CallableKind::TupleEnumVariant(it) => it.try_to_nav(db), - hir::CallableKind::TupleStruct(it) => it.try_to_nav(db), + hir::CallableKind::TupleEnumVariant(it) => it.try_to_nav(&sema), + hir::CallableKind::TupleStruct(it) => it.try_to_nav(&sema), _ => None, } .zip(Some(sema.original_range(expr.syntax()))) @@ -136,7 +136,7 @@ pub(crate) fn outgoing_calls( return None; } function - .try_to_nav(db) + .try_to_nav(&sema) .zip(Some(sema.original_range(expr.name_ref()?.syntax()))) } }?; diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs index b004c0757674..11518d1fbf30 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs @@ -68,7 +68,7 @@ fn check_doc_links(#[rust_analyzer::rust_fixture] ra_fixture: &str) { resolve_doc_path_for_def(sema.db, cursor_def, &link, ns, is_inner_attr) .unwrap_or_else(|| panic!("Failed to resolve {link}")) }); - def.try_to_nav(sema.db).unwrap().into_iter().zip(iter::repeat(link)) + def.try_to_nav(sema).unwrap().into_iter().zip(iter::repeat(link)) }) .map(|(nav_target, link)| { let range = diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs b/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs index 267e8ff7128b..686dbe241293 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs @@ -38,14 +38,14 @@ pub(crate) fn goto_declaration( ast::NameRef(name_ref) => match NameRefClass::classify(&sema, &name_ref)? { NameRefClass::Definition(it, _) => Some(it), NameRefClass::FieldShorthand { field_ref, .. } => - return field_ref.try_to_nav(db), + return field_ref.try_to_nav(&sema), NameRefClass::ExternCrateShorthand { decl, .. } => - return decl.try_to_nav(db), + return decl.try_to_nav(&sema), }, ast::Name(name) => match NameClass::classify(&sema, &name)? { NameClass::Definition(it) | NameClass::ConstReference(it) => Some(it), NameClass::PatFieldShorthand { field_ref, .. } => - return field_ref.try_to_nav(db), + return field_ref.try_to_nav(&sema), }, _ => None } @@ -57,14 +57,14 @@ pub(crate) fn goto_declaration( Definition::Const(c) => c.as_assoc_item(db), Definition::TypeAlias(ta) => ta.as_assoc_item(db), Definition::Function(f) => f.as_assoc_item(db), - Definition::ExternCrateDecl(it) => return it.try_to_nav(db), + Definition::ExternCrateDecl(it) => return it.try_to_nav(&sema), _ => None, }?; let trait_ = assoc.implemented_trait(db)?; let name = Some(assoc.name(db)?); let item = trait_.items(db).into_iter().find(|it| it.name(db) == name)?; - item.try_to_nav(db) + item.try_to_nav(&sema) }) .flatten() .collect(); diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index d50d7f602fcb..2dcb13d9e7aa 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -62,7 +62,7 @@ pub(crate) fn goto_definition( })?; if let Some(doc_comment) = token_as_doc_comment(&original_token) { return doc_comment.get_definition_with_descend_at(sema, offset, |def, _, link_range| { - let nav = def.try_to_nav(db)?; + let nav = def.try_to_nav(sema)?; Some(RangeInfo::new(link_range, nav.collect())) }); } @@ -73,7 +73,7 @@ pub(crate) fn goto_definition( return Some(RangeInfo::new( range, match resolution { - Some(res) => def_to_nav(db, Definition::from(res)), + Some(res) => def_to_nav(sema, Definition::from(res)), None => vec![], }, )); @@ -121,7 +121,7 @@ pub(crate) fn goto_definition( .collect(); } try_filter_trait_item_definition(sema, &def) - .unwrap_or_else(|| def_to_nav(sema.db, def)) + .unwrap_or_else(|| def_to_nav(sema, def)) }) .collect(), ) @@ -160,7 +160,7 @@ fn find_definition_for_known_blanket_dual_impls( t_f, [return_type.type_arguments().next()?], ) - .map(|f| def_to_nav(sema.db, f.into())); + .map(|f| def_to_nav(sema, f.into())); } hir::AssocItemContainer::Impl(_) => return None, }; @@ -201,7 +201,7 @@ fn find_definition_for_known_blanket_dual_impls( // succeed let _t = f.as_assoc_item(sema.db)?.implemented_trait(sema.db)?; let def = Definition::from(f); - Some(def_to_nav(sema.db, def)) + Some(def_to_nav(sema, def)) } fn try_lookup_include_path( @@ -246,7 +246,7 @@ fn try_lookup_macro_def_in_macro_use( for mod_def in krate.root_module().declarations(sema.db) { if let ModuleDef::Macro(mac) = mod_def && mac.name(sema.db).as_str() == token.text() - && let Some(nav) = mac.try_to_nav(sema.db) + && let Some(nav) = mac.try_to_nav(sema) { return Some(nav.call_site); } @@ -278,7 +278,7 @@ fn try_filter_trait_item_definition( .items(db) .iter() .filter(|itm| discriminant(*itm) == discriminant_value) - .find_map(|itm| (itm.name(db)? == name).then(|| itm.try_to_nav(db)).flatten()) + .find_map(|itm| (itm.name(db)? == name).then(|| itm.try_to_nav(sema)).flatten()) .map(|it| it.collect()) } } @@ -347,7 +347,7 @@ fn nav_for_exit_points( match_ast! { match node { ast::Fn(fn_) => { - let mut nav = sema.to_def(&fn_)?.try_to_nav(db)?; + let mut nav = sema.to_def(&fn_)?.try_to_nav(sema)?; // For async token, we navigate to itself, which triggers // VSCode to find the references let focus_token = if matches!(token_kind, T![async]) { @@ -564,8 +564,8 @@ fn nav_for_break_points( Some(navs) } -fn def_to_nav(db: &RootDatabase, def: Definition) -> Vec { - def.try_to_nav(db).map(|it| it.collect()).unwrap_or_default() +fn def_to_nav(sema: &Semantics<'_, RootDatabase>, def: Definition) -> Vec { + def.try_to_nav(sema).map(|it| it.collect()).unwrap_or_default() } fn expr_to_nav( diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs index 4f172621908c..875403c4e32a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs @@ -86,7 +86,7 @@ pub(crate) fn goto_implementation( fn impls_for_ty(sema: &Semantics<'_, RootDatabase>, ty: hir::Type<'_>) -> Vec { Impl::all_for_type(sema.db, ty) .into_iter() - .filter_map(|imp| imp.try_to_nav(sema.db)) + .filter_map(|imp| imp.try_to_nav(sema)) .flatten() .collect() } @@ -97,7 +97,7 @@ fn impls_for_trait( ) -> Vec { Impl::all_for_trait(sema.db, trait_) .into_iter() - .filter_map(|imp| imp.try_to_nav(sema.db)) + .filter_map(|imp| imp.try_to_nav(sema)) .flatten() .collect() } @@ -114,7 +114,7 @@ fn impls_for_trait_item( let itm_name = itm.name(sema.db)?; (itm_name == fun_name).then_some(*itm) })?; - item.try_to_nav(sema.db) + item.try_to_nav(sema) }) .flatten() .collect() diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs index b80e81d39c6d..ffd144a827e3 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs @@ -30,7 +30,7 @@ pub(crate) fn goto_type_definition( let mut res = Vec::new(); let mut push = |def: Definition| { - if let Some(navs) = def.try_to_nav(db) { + if let Some(navs) = def.try_to_nav(&sema) { for nav in navs { if !res.contains(&nav) { res.push(nav); diff --git a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs index af1557f5a9a1..04ce5a7567f3 100644 --- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs +++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs @@ -277,7 +277,7 @@ fn highlight_references( Definition::Module(module) => { NavigationTarget::from_module_to_decl(sema.db, module) } - def => match def.try_to_nav(sema.db) { + def => match def.try_to_nav(sema) { Some(it) => it, None => continue, }, diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index 44c98a43f694..3f52303d74e4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -85,10 +85,11 @@ pub enum HoverAction { impl HoverAction { fn goto_type_from_targets( - db: &RootDatabase, + sema: &Semantics<'_, RootDatabase>, targets: Vec, edition: Edition, ) -> Option { + let db = sema.db; let targets = targets .into_iter() .filter_map(|it| { @@ -99,7 +100,7 @@ impl HoverAction { it.name(db).map(|name| name.display(db, edition).to_string()), edition, ), - nav: it.try_to_nav(db)?.call_site(), + nav: it.try_to_nav(sema)?.call_site(), }) }) .collect::>(); @@ -467,10 +468,10 @@ pub(crate) fn hover_for_definition( HoverResult { markup: render::process_markup(sema.db, def, &markup, range_map, config), actions: [ - show_fn_references_action(sema.db, def), - show_implementations_action(sema.db, def), + show_fn_references_action(sema, def), + show_implementations_action(sema, def), runnable_action(sema, def, file_id), - goto_type_action_for_def(sema.db, def, ¬able_traits, subst_types, edition), + goto_type_action_for_def(sema, def, ¬able_traits, subst_types, edition), ] .into_iter() .flatten() @@ -505,7 +506,10 @@ fn notable_traits<'db>( .collect::>() } -fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option { +fn show_implementations_action( + sema: &Semantics<'_, RootDatabase>, + def: Definition, +) -> Option { fn to_action(nav_target: NavigationTarget) -> HoverAction { HoverAction::Implementation(FilePosition { file_id: nav_target.file_id, @@ -515,19 +519,22 @@ fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option { - return it.try_to_nav(db).map(UpmappingResult::call_site).map(to_action); + return it.try_to_nav(sema).map(UpmappingResult::call_site).map(to_action); } Definition::Adt(it) => Some(it), - Definition::SelfType(it) => it.self_ty(db).as_adt(), + Definition::SelfType(it) => it.self_ty(sema.db).as_adt(), _ => None, }?; - adt.try_to_nav(db).map(UpmappingResult::call_site).map(to_action) + adt.try_to_nav(sema).map(UpmappingResult::call_site).map(to_action) } -fn show_fn_references_action(db: &RootDatabase, def: Definition) -> Option { +fn show_fn_references_action( + sema: &Semantics<'_, RootDatabase>, + def: Definition, +) -> Option { match def { Definition::Function(it) => { - it.try_to_nav(db).map(UpmappingResult::call_site).map(|nav_target| { + it.try_to_nav(sema).map(UpmappingResult::call_site).map(|nav_target| { HoverAction::Reference(FilePosition { file_id: nav_target.file_id, offset: nav_target.focus_or_full_range().start(), @@ -560,12 +567,13 @@ fn runnable_action( } fn goto_type_action_for_def( - db: &RootDatabase, + sema: &Semantics<'_, RootDatabase>, def: Definition, notable_traits: &[(hir::Trait, Vec<(Option>, hir::Name)>)], subst_types: Option)>>, edition: Edition, ) -> Option { + let db = sema.db; let mut targets: Vec = Vec::new(); let mut push_new_def = |item: hir::ModuleDef| { if !targets.contains(&item) { @@ -612,7 +620,7 @@ fn goto_type_action_for_def( } } - HoverAction::goto_type_from_targets(db, targets, edition) + HoverAction::goto_type_from_targets(sema, targets, edition) } fn walk_and_push_ty( diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index 2adafbb7af6f..451260640f84 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -128,7 +128,7 @@ pub(super) fn try_expr( }; walk_and_push_ty(sema.db, &inner_ty, &mut push_new_def); walk_and_push_ty(sema.db, &body_ty, &mut push_new_def); - if let Some(actions) = HoverAction::goto_type_from_targets(sema.db, targets, edition) { + if let Some(actions) = HoverAction::goto_type_from_targets(sema, targets, edition) { res.actions.push(actions); } @@ -210,7 +210,7 @@ pub(super) fn deref_expr( ) .into() }; - if let Some(actions) = HoverAction::goto_type_from_targets(sema.db, targets, edition) { + if let Some(actions) = HoverAction::goto_type_from_targets(sema, targets, edition) { res.actions.push(actions); } @@ -323,7 +323,7 @@ pub(super) fn struct_rest_pat( Markup::fenced_block(&s) }; - if let Some(actions) = HoverAction::goto_type_from_targets(sema.db, targets, edition) { + if let Some(actions) = HoverAction::goto_type_from_targets(sema, targets, edition) { res.actions.push(actions); } res @@ -1030,7 +1030,7 @@ fn type_info( }; desc.into() }; - if let Some(actions) = HoverAction::goto_type_from_targets(db, targets, edition) { + if let Some(actions) = HoverAction::goto_type_from_targets(sema, targets, edition) { res.actions.push(actions); } Some(res) @@ -1098,7 +1098,7 @@ fn closure_ty( format_to!(markup, "{adjusted}\n\n## Captures\n{}", captures_rendered,); let mut res = HoverResult::default(); - if let Some(actions) = HoverAction::goto_type_from_targets(sema.db, targets, edition) { + if let Some(actions) = HoverAction::goto_type_from_targets(sema, targets, edition) { res.actions.push(actions); } res.markup = markup.into(); @@ -1302,7 +1302,7 @@ fn keyword_hints( KeywordHint { description, keyword_mod, - actions: HoverAction::goto_type_from_targets(sema.db, targets, edition) + actions: HoverAction::goto_type_from_targets(sema, targets, edition) .into_iter() .collect(), } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index cc44ae8fc8b9..507af41d8446 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -672,7 +672,7 @@ impl fmt::Debug for InlayHintLabelPart { #[derive(Debug)] struct InlayHintLabelBuilder<'a> { - db: &'a RootDatabase, + sema: &'a Semantics<'a, RootDatabase>, result: InlayHintLabel, last_part: String, resolve: bool, @@ -694,7 +694,7 @@ impl HirWrite for InlayHintLabelBuilder<'_> { LazyProperty::Lazy } else { LazyProperty::Computed({ - let Some(location) = ModuleDef::from(def).try_to_nav(self.db) else { return }; + let Some(location) = ModuleDef::from(def).try_to_nav(self.sema) else { return }; let location = location.call_site(); FileRange { file_id: location.file_id, range: location.focus_or_full_range() } }) @@ -782,7 +782,7 @@ fn label_of_ty( } let mut label_builder = InlayHintLabelBuilder { - db: sema.db, + sema, last_part: String::new(), location: None, result: InlayHintLabel::default(), diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs index f0003dae3f36..4abd67b91f5e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs @@ -44,7 +44,7 @@ pub(super) fn hints( text: "Sized".to_owned(), linked_location: sized_trait.and_then(|it| { config.lazy_location_opt(|| { - it.try_to_nav(sema.db).map(|it| { + it.try_to_nav(sema).map(|it| { let n = it.call_site(); FileRange { file_id: n.file_id, diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index cddf5f04f244..3a6b3247d65c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -483,7 +483,7 @@ impl Analysis { salsa::attach(&self.db, || { symbols .into_iter() - .filter_map(|s| s.try_to_nav(&self.db)) + .filter_map(|s| s.try_to_nav(&Semantics::new(&self.db))) .take(limit) .map(UpmappingResult::call_site) .collect::>() diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index 641bde5e2b82..46ff16f97262 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -83,14 +83,20 @@ pub(crate) trait ToNav { } pub trait TryToNav { - fn try_to_nav(&self, db: &RootDatabase) -> Option>; + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option>; } impl TryToNav for Either { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { match self { - Either::Left(it) => it.try_to_nav(db), - Either::Right(it) => it.try_to_nav(db), + Either::Left(it) => it.try_to_nav(sema), + Either::Right(it) => it.try_to_nav(sema), } } } @@ -185,7 +191,11 @@ impl NavigationTarget { } impl TryToNav for FileSymbol { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { + let db = sema.db; let edition = self.def.module(db).map(|it| it.krate().edition(db)).unwrap_or(Edition::CURRENT); let display_target = self.def.krate(db).to_display_target(db); @@ -244,49 +254,55 @@ impl TryToNav for FileSymbol { } impl TryToNav for Definition { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { match self { - Definition::Local(it) => Some(it.to_nav(db)), - Definition::Label(it) => it.try_to_nav(db), - Definition::Module(it) => Some(it.to_nav(db)), - Definition::Crate(it) => Some(it.to_nav(db)), - Definition::Macro(it) => it.try_to_nav(db), - Definition::Field(it) => it.try_to_nav(db), - Definition::SelfType(it) => it.try_to_nav(db), - Definition::GenericParam(it) => it.try_to_nav(db), - Definition::Function(it) => it.try_to_nav(db), - Definition::Adt(it) => it.try_to_nav(db), - Definition::Variant(it) => it.try_to_nav(db), - Definition::Const(it) => it.try_to_nav(db), - Definition::Static(it) => it.try_to_nav(db), - Definition::Trait(it) => it.try_to_nav(db), - Definition::TypeAlias(it) => it.try_to_nav(db), - Definition::ExternCrateDecl(it) => it.try_to_nav(db), - Definition::InlineAsmOperand(it) => it.try_to_nav(db), - Definition::BuiltinType(it) => it.try_to_nav(db), + Definition::Local(it) => Some(it.to_nav(sema.db)), + Definition::Label(it) => it.try_to_nav(sema), + Definition::Module(it) => Some(it.to_nav(sema.db)), + Definition::Crate(it) => Some(it.to_nav(sema.db)), + Definition::Macro(it) => it.try_to_nav(sema), + Definition::Field(it) => it.try_to_nav(sema), + Definition::SelfType(it) => it.try_to_nav(sema), + Definition::GenericParam(it) => it.try_to_nav(sema), + Definition::Function(it) => it.try_to_nav(sema), + Definition::Adt(it) => it.try_to_nav(sema), + Definition::Variant(it) => it.try_to_nav(sema), + Definition::Const(it) => it.try_to_nav(sema), + Definition::Static(it) => it.try_to_nav(sema), + Definition::Trait(it) => it.try_to_nav(sema), + Definition::TypeAlias(it) => it.try_to_nav(sema), + Definition::ExternCrateDecl(it) => it.try_to_nav(sema), + Definition::InlineAsmOperand(it) => it.try_to_nav(sema), + Definition::BuiltinType(it) => it.try_to_nav(sema), Definition::BuiltinLifetime(_) | Definition::TupleField(_) | Definition::ToolModule(_) | Definition::InlineAsmRegOrRegClass(_) | Definition::BuiltinAttr(_) => None, // FIXME: The focus range should be set to the helper declaration - Definition::DeriveHelper(it) => it.derive().try_to_nav(db), + Definition::DeriveHelper(it) => it.derive().try_to_nav(sema), } } } impl TryToNav for hir::ModuleDef { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { match self { - hir::ModuleDef::Module(it) => Some(it.to_nav(db)), - hir::ModuleDef::Function(it) => it.try_to_nav(db), - hir::ModuleDef::Adt(it) => it.try_to_nav(db), - hir::ModuleDef::Variant(it) => it.try_to_nav(db), - hir::ModuleDef::Const(it) => it.try_to_nav(db), - hir::ModuleDef::Static(it) => it.try_to_nav(db), - hir::ModuleDef::Trait(it) => it.try_to_nav(db), - hir::ModuleDef::TypeAlias(it) => it.try_to_nav(db), - hir::ModuleDef::Macro(it) => it.try_to_nav(db), + hir::ModuleDef::Module(it) => Some(it.to_nav(sema.db)), + hir::ModuleDef::Function(it) => it.try_to_nav(sema), + hir::ModuleDef::Adt(it) => it.try_to_nav(sema), + hir::ModuleDef::Variant(it) => it.try_to_nav(sema), + hir::ModuleDef::Const(it) => it.try_to_nav(sema), + hir::ModuleDef::Static(it) => it.try_to_nav(sema), + hir::ModuleDef::Trait(it) => it.try_to_nav(sema), + hir::ModuleDef::TypeAlias(it) => it.try_to_nav(sema), + hir::ModuleDef::Macro(it) => it.try_to_nav(sema), hir::ModuleDef::BuiltinType(_) => None, } } @@ -369,7 +385,11 @@ where D: HasSource + ToNavFromAst + Copy + HasDocs + HirDisplay + HasCrate, D::Ast: ast::HasName, { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { + let db = sema.db; let src = self.source(db)?; Some( NavigationTarget::from_named( @@ -423,7 +443,11 @@ impl ToNav for hir::Crate { } impl TryToNav for hir::Impl { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { + let db = sema.db; let InFile { file_id, value } = self.source(db)?; let derive_path = self.as_builtin_derive_path(db); @@ -447,7 +471,11 @@ impl TryToNav for hir::Impl { } impl TryToNav for hir::ExternCrateDecl { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { + let db = sema.db; let src = self.source(db)?; let InFile { file_id, value } = src; let focus = value @@ -479,7 +507,11 @@ impl TryToNav for hir::ExternCrateDecl { } impl TryToNav for hir::Field { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { + let db = sema.db; let src = self.source(db)?; let krate = self.parent_def(db).module(db).krate(); @@ -512,7 +544,11 @@ impl TryToNav for hir::Field { } impl TryToNav for hir::Macro { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { + let db = sema.db; let src = self.source(db)?; let name_owner: &dyn ast::HasName = match &src.value { Either::Left(it) => it, @@ -533,31 +569,40 @@ impl TryToNav for hir::Macro { } impl TryToNav for hir::Adt { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { match self { - hir::Adt::Struct(it) => it.try_to_nav(db), - hir::Adt::Union(it) => it.try_to_nav(db), - hir::Adt::Enum(it) => it.try_to_nav(db), + hir::Adt::Struct(it) => it.try_to_nav(sema), + hir::Adt::Union(it) => it.try_to_nav(sema), + hir::Adt::Enum(it) => it.try_to_nav(sema), } } } impl TryToNav for hir::AssocItem { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { match self { - AssocItem::Function(it) => it.try_to_nav(db), - AssocItem::Const(it) => it.try_to_nav(db), - AssocItem::TypeAlias(it) => it.try_to_nav(db), + AssocItem::Function(it) => it.try_to_nav(sema), + AssocItem::Const(it) => it.try_to_nav(sema), + AssocItem::TypeAlias(it) => it.try_to_nav(sema), } } } impl TryToNav for hir::GenericParam { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { match self { - hir::GenericParam::TypeParam(it) => it.try_to_nav(db), - hir::GenericParam::ConstParam(it) => it.try_to_nav(db), - hir::GenericParam::LifetimeParam(it) => it.try_to_nav(db), + hir::GenericParam::TypeParam(it) => it.try_to_nav(sema), + hir::GenericParam::ConstParam(it) => it.try_to_nav(sema), + hir::GenericParam::LifetimeParam(it) => it.try_to_nav(sema), } } } @@ -606,7 +651,11 @@ impl ToNav for hir::Local { } impl TryToNav for hir::Label { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { + let db = sema.db; let InFile { file_id, value } = self.source(db)?; // Labels can't be keywords, so no escaping needed. let name = self.name(db).display_no_db(Edition::Edition2015).to_smolstr(); @@ -628,7 +677,11 @@ impl TryToNav for hir::Label { } impl TryToNav for hir::TypeParam { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { + let db = sema.db; let InFile { file_id, value } = self.merge().source(db)?; let edition = self.module(db).krate().edition(db); let name = self.name(db).display_no_db(edition).to_smolstr(); @@ -665,13 +718,20 @@ impl TryToNav for hir::TypeParam { } impl TryToNav for hir::TypeOrConstParam { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { - self.split(db).try_to_nav(db) + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { + self.split(sema.db).try_to_nav(sema) } } impl TryToNav for hir::LifetimeParam { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { + let db = sema.db; let InFile { file_id, value } = self.source(db)?; // Lifetimes cannot be keywords, so not escaping needed. let name = self.name(db).display_no_db(Edition::Edition2015).to_smolstr(); @@ -693,7 +753,11 @@ impl TryToNav for hir::LifetimeParam { } impl TryToNav for hir::ConstParam { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { + let db = sema.db; let InFile { file_id, value } = self.merge().source(db)?; let edition = self.module(db).krate().edition(db); let name = self.name(db).display_no_db(edition).to_smolstr(); @@ -723,7 +787,11 @@ impl TryToNav for hir::ConstParam { } impl TryToNav for hir::InlineAsmOperand { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { + let db = sema.db; let InFile { file_id, value } = &self.source(db)?; let file_id = *file_id; Some(orig_range_with_focus(db, file_id, value.syntax(), value.name()).map( @@ -748,9 +816,11 @@ impl TryToNav for hir::InlineAsmOperand { } impl TryToNav for hir::BuiltinType { - fn try_to_nav(&self, db: &RootDatabase) -> Option> { - let sema = Semantics::new(db); - + fn try_to_nav( + &self, + sema: &Semantics<'_, RootDatabase>, + ) -> Option> { + let db = sema.db; let krate = db .all_crates() .iter() @@ -759,7 +829,7 @@ impl TryToNav for hir::BuiltinType { .map(Crate::from)?; let edition = krate.edition(db); - let fd = FamousDefs(&sema, krate); + let fd = FamousDefs(sema, krate); let primitive_mod = format!("prim_{}", self.name().display(fd.0.db, edition)); let doc_owner = find_std_module(&fd, &primitive_mod, edition)?; diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index dc509b385838..0189939eac31 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -138,7 +138,7 @@ pub(crate) fn find_all_refs( Definition::Module(module) => { Some(NavigationTarget::from_module_to_decl(sema.db, module)) } - def => def.try_to_nav(sema.db), + def => def.try_to_nav(sema), } .map(|nav| { let (nav, extra_ref) = match nav.def_site { diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs index 6d33ddcde047..6ad9c7884db9 100644 --- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs +++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs @@ -158,15 +158,15 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec { Definition::SelfType(impl_) => runnable_impl(&sema, &impl_), _ => None, }; - add_opt(runnable.or_else(|| module_def_doctest(sema.db, def)), Some(def)); + add_opt(runnable.or_else(|| module_def_doctest(&sema, def)), Some(def)); if let Definition::SelfType(impl_) = def { impl_.items(db).into_iter().for_each(|assoc| { let runnable = match assoc { hir::AssocItem::Function(it) => { - runnable_fn(&sema, it).or_else(|| module_def_doctest(sema.db, it.into())) + runnable_fn(&sema, it).or_else(|| module_def_doctest(&sema, it.into())) } - hir::AssocItem::Const(it) => module_def_doctest(sema.db, it.into()), - hir::AssocItem::TypeAlias(it) => module_def_doctest(sema.db, it.into()), + hir::AssocItem::Const(it) => module_def_doctest(&sema, it.into()), + hir::AssocItem::TypeAlias(it) => module_def_doctest(&sema, it.into()), }; add_opt(runnable, Some(assoc.into())) }); @@ -410,7 +410,7 @@ pub(crate) fn runnable_impl( return None; } let cfg = attrs.cfg(); - let nav = def.try_to_nav(sema.db)?.call_site(); + let nav = def.try_to_nav(sema)?.call_site(); let ty = def.self_ty(sema.db); let adt_name = ty.as_adt()?.name(sema.db); let mut ty_args = ty.generic_parameters(sema.db, display_target).peekable(); @@ -486,7 +486,8 @@ fn runnable_mod_outline_definition( }) } -fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option { +fn module_def_doctest(sema: &Semantics<'_, RootDatabase>, def: Definition) -> Option { + let db = sema.db; let attrs = match def { Definition::Module(it) => it.attrs(db), Definition::Function(it) => it.attrs(db), @@ -540,7 +541,7 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option { let mut nav = match def { Definition::Module(def) => NavigationTarget::from_module_to_decl(db, def), - def => def.try_to_nav(db)?, + def => def.try_to_nav(sema)?, } .call_site(); nav.focus_range = None; diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs index 8c3275df1baa..8214b4d1de22 100644 --- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs +++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs @@ -243,7 +243,7 @@ impl StaticIndex<'_> { edition, display_target, )), - definition: def.try_to_nav(self.db).map(UpmappingResult::call_site).map( + definition: def.try_to_nav(&sema).map(UpmappingResult::call_site).map( |it| FileRange { file_id: it.file_id, range: it.focus_or_full_range() }, ), references: vec![], @@ -261,7 +261,7 @@ impl StaticIndex<'_> { let token = self.tokens.get_mut(id).unwrap(); token.references.push(ReferenceData { range: FileRange { range, file_id }, - is_definition: match def.try_to_nav(self.db).map(UpmappingResult::call_site) { + is_definition: match def.try_to_nav(&sema).map(UpmappingResult::call_site) { Some(it) => it.file_id == file_id && it.focus_or_full_range() == range, None => false, }, diff --git a/src/tools/rust-analyzer/crates/ide/src/test_explorer.rs b/src/tools/rust-analyzer/crates/ide/src/test_explorer.rs index 06cbd50e946a..bd60ffe55912 100644 --- a/src/tools/rust-analyzer/crates/ide/src/test_explorer.rs +++ b/src/tools/rust-analyzer/crates/ide/src/test_explorer.rs @@ -94,7 +94,7 @@ fn discover_tests_in_module( if !f.is_test(db) { continue; } - let nav = f.try_to_nav(db).map(|r| r.call_site); + let nav = f.try_to_nav(&sema).map(|r| r.call_site); let fn_name = f.name(db).as_str().to_owned(); r.push(TestItem { id: format!("{prefix_id}::{fn_name}"), From 9899ab3fa4559461c1ddae641565995acf9c2223 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Mon, 8 Sep 2025 21:21:55 +0200 Subject: [PATCH 286/682] simplify the declaration of the legacy integer modules (`std::u32` etc.) --- library/core/src/lib.rs | 51 +++---------- library/core/src/num/shells/i128.rs | 11 --- library/core/src/num/shells/i16.rs | 11 --- library/core/src/num/shells/i32.rs | 11 --- library/core/src/num/shells/i64.rs | 11 --- library/core/src/num/shells/i8.rs | 11 --- library/core/src/num/shells/int_macros.rs | 46 ------------ library/core/src/num/shells/isize.rs | 11 --- .../core/src/num/shells/legacy_int_modules.rs | 71 +++++++++++++++++++ library/core/src/num/shells/u128.rs | 11 --- library/core/src/num/shells/u16.rs | 11 --- library/core/src/num/shells/u32.rs | 11 --- library/core/src/num/shells/u64.rs | 11 --- library/core/src/num/shells/u8.rs | 11 --- library/core/src/num/shells/usize.rs | 11 --- 15 files changed, 81 insertions(+), 219 deletions(-) delete mode 100644 library/core/src/num/shells/i128.rs delete mode 100644 library/core/src/num/shells/i16.rs delete mode 100644 library/core/src/num/shells/i32.rs delete mode 100644 library/core/src/num/shells/i64.rs delete mode 100644 library/core/src/num/shells/i8.rs delete mode 100644 library/core/src/num/shells/int_macros.rs delete mode 100644 library/core/src/num/shells/isize.rs create mode 100644 library/core/src/num/shells/legacy_int_modules.rs delete mode 100644 library/core/src/num/shells/u128.rs delete mode 100644 library/core/src/num/shells/u16.rs delete mode 100644 library/core/src/num/shells/u32.rs delete mode 100644 library/core/src/num/shells/u64.rs delete mode 100644 library/core/src/num/shells/u8.rs delete mode 100644 library/core/src/num/shells/usize.rs diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index c306011bdda3..86a68e18b0af 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -252,47 +252,16 @@ pub use crate::macros::cfg_select; #[macro_use] mod internal_macros; -#[path = "num/shells/int_macros.rs"] -#[macro_use] -mod int_macros; - -#[rustc_diagnostic_item = "i128_legacy_mod"] -#[path = "num/shells/i128.rs"] -pub mod i128; -#[rustc_diagnostic_item = "i16_legacy_mod"] -#[path = "num/shells/i16.rs"] -pub mod i16; -#[rustc_diagnostic_item = "i32_legacy_mod"] -#[path = "num/shells/i32.rs"] -pub mod i32; -#[rustc_diagnostic_item = "i64_legacy_mod"] -#[path = "num/shells/i64.rs"] -pub mod i64; -#[rustc_diagnostic_item = "i8_legacy_mod"] -#[path = "num/shells/i8.rs"] -pub mod i8; -#[rustc_diagnostic_item = "isize_legacy_mod"] -#[path = "num/shells/isize.rs"] -pub mod isize; - -#[rustc_diagnostic_item = "u128_legacy_mod"] -#[path = "num/shells/u128.rs"] -pub mod u128; -#[rustc_diagnostic_item = "u16_legacy_mod"] -#[path = "num/shells/u16.rs"] -pub mod u16; -#[rustc_diagnostic_item = "u32_legacy_mod"] -#[path = "num/shells/u32.rs"] -pub mod u32; -#[rustc_diagnostic_item = "u64_legacy_mod"] -#[path = "num/shells/u64.rs"] -pub mod u64; -#[rustc_diagnostic_item = "u8_legacy_mod"] -#[path = "num/shells/u8.rs"] -pub mod u8; -#[rustc_diagnostic_item = "usize_legacy_mod"] -#[path = "num/shells/usize.rs"] -pub mod usize; +#[path = "num/shells/legacy_int_modules.rs"] +mod legacy_int_modules; +#[stable(feature = "rust1", since = "1.0.0")] +#[allow(clippy::useless_attribute)] // FIXME false positive (https://github.com/rust-lang/rust-clippy/issues/15636) +#[allow(deprecated_in_future)] +pub use legacy_int_modules::{i8, i16, i32, i64, isize, u8, u16, u32, u64, usize}; +#[stable(feature = "i128", since = "1.26.0")] +#[allow(clippy::useless_attribute)] // FIXME false positive (https://github.com/rust-lang/rust-clippy/issues/15636) +#[allow(deprecated_in_future)] +pub use legacy_int_modules::{i128, u128}; #[path = "num/f128.rs"] pub mod f128; diff --git a/library/core/src/num/shells/i128.rs b/library/core/src/num/shells/i128.rs deleted file mode 100644 index b3b3d3b4875a..000000000000 --- a/library/core/src/num/shells/i128.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! Redundant constants module for the [`i128` primitive type][i128]. -//! -//! New code should use the associated constants directly on the primitive type. - -#![stable(feature = "i128", since = "1.26.0")] -#![deprecated( - since = "TBD", - note = "all constants in this module replaced by associated constants on `i128`" -)] - -int_module! { i128, #[stable(feature = "i128", since="1.26.0")] } diff --git a/library/core/src/num/shells/i16.rs b/library/core/src/num/shells/i16.rs deleted file mode 100644 index 70a452e19398..000000000000 --- a/library/core/src/num/shells/i16.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! Redundant constants module for the [`i16` primitive type][i16]. -//! -//! New code should use the associated constants directly on the primitive type. - -#![stable(feature = "rust1", since = "1.0.0")] -#![deprecated( - since = "TBD", - note = "all constants in this module replaced by associated constants on `i16`" -)] - -int_module! { i16 } diff --git a/library/core/src/num/shells/i32.rs b/library/core/src/num/shells/i32.rs deleted file mode 100644 index c30849e2591c..000000000000 --- a/library/core/src/num/shells/i32.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! Redundant constants module for the [`i32` primitive type][i32]. -//! -//! New code should use the associated constants directly on the primitive type. - -#![stable(feature = "rust1", since = "1.0.0")] -#![deprecated( - since = "TBD", - note = "all constants in this module replaced by associated constants on `i32`" -)] - -int_module! { i32 } diff --git a/library/core/src/num/shells/i64.rs b/library/core/src/num/shells/i64.rs deleted file mode 100644 index 77d95d712506..000000000000 --- a/library/core/src/num/shells/i64.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! Redundant constants module for the [`i64` primitive type][i64]. -//! -//! New code should use the associated constants directly on the primitive type. - -#![stable(feature = "rust1", since = "1.0.0")] -#![deprecated( - since = "TBD", - note = "all constants in this module replaced by associated constants on `i64`" -)] - -int_module! { i64 } diff --git a/library/core/src/num/shells/i8.rs b/library/core/src/num/shells/i8.rs deleted file mode 100644 index 516ba8cdef3b..000000000000 --- a/library/core/src/num/shells/i8.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! Redundant constants module for the [`i8` primitive type][i8]. -//! -//! New code should use the associated constants directly on the primitive type. - -#![stable(feature = "rust1", since = "1.0.0")] -#![deprecated( - since = "TBD", - note = "all constants in this module replaced by associated constants on `i8`" -)] - -int_module! { i8 } diff --git a/library/core/src/num/shells/int_macros.rs b/library/core/src/num/shells/int_macros.rs deleted file mode 100644 index 8ae9b7abae3b..000000000000 --- a/library/core/src/num/shells/int_macros.rs +++ /dev/null @@ -1,46 +0,0 @@ -#![doc(hidden)] - -macro_rules! int_module { - ($T:ident) => (int_module!($T, #[stable(feature = "rust1", since = "1.0.0")]);); - ($T:ident, #[$attr:meta]) => ( - #[doc = concat!( - "The smallest value that can be represented by this integer type. Use ", - "[`", stringify!($T), "::MIN", "`] instead." - )] - /// - /// # Examples - /// - /// ```rust - /// // deprecated way - #[doc = concat!("let min = std::", stringify!($T), "::MIN;")] - /// - /// // intended way - #[doc = concat!("let min = ", stringify!($T), "::MIN;")] - /// ``` - /// - #[$attr] - #[deprecated(since = "TBD", note = "replaced by the `MIN` associated constant on this type")] - #[rustc_diagnostic_item = concat!(stringify!($T), "_legacy_const_min")] - pub const MIN: $T = $T::MIN; - - #[doc = concat!( - "The largest value that can be represented by this integer type. Use ", - "[`", stringify!($T), "::MAX", "`] instead." - )] - /// - /// # Examples - /// - /// ```rust - /// // deprecated way - #[doc = concat!("let max = std::", stringify!($T), "::MAX;")] - /// - /// // intended way - #[doc = concat!("let max = ", stringify!($T), "::MAX;")] - /// ``` - /// - #[$attr] - #[deprecated(since = "TBD", note = "replaced by the `MAX` associated constant on this type")] - #[rustc_diagnostic_item = concat!(stringify!($T), "_legacy_const_max")] - pub const MAX: $T = $T::MAX; - ) -} diff --git a/library/core/src/num/shells/isize.rs b/library/core/src/num/shells/isize.rs deleted file mode 100644 index 828f7345bafb..000000000000 --- a/library/core/src/num/shells/isize.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! Redundant constants module for the [`isize` primitive type][isize]. -//! -//! New code should use the associated constants directly on the primitive type. - -#![stable(feature = "rust1", since = "1.0.0")] -#![deprecated( - since = "TBD", - note = "all constants in this module replaced by associated constants on `isize`" -)] - -int_module! { isize } diff --git a/library/core/src/num/shells/legacy_int_modules.rs b/library/core/src/num/shells/legacy_int_modules.rs new file mode 100644 index 000000000000..6b4f25391115 --- /dev/null +++ b/library/core/src/num/shells/legacy_int_modules.rs @@ -0,0 +1,71 @@ +#![doc(hidden)] + +macro_rules! legacy_int_module { + ($T:ident) => (legacy_int_module!($T, #[stable(feature = "rust1", since = "1.0.0")]);); + ($T:ident, #[$attr:meta]) => ( + #[$attr] + #[deprecated( + since = "TBD", + note = "all constants in this module replaced by associated constants on the type" + )] + #[rustc_diagnostic_item = concat!(stringify!($T), "_legacy_mod")] + pub mod $T { + #![doc = concat!("Redundant constants module for the [`", stringify!($T), "` primitive type][", stringify!($T), "].")] + //! + //! New code should use the associated constants directly on the primitive type. + + #[doc = concat!( + "The smallest value that can be represented by this integer type. Use ", + "[`", stringify!($T), "::MIN", "`] instead." + )] + /// + /// # Examples + /// + /// ```rust + /// // deprecated way + #[doc = concat!("let min = std::", stringify!($T), "::MIN;")] + /// + /// // intended way + #[doc = concat!("let min = ", stringify!($T), "::MIN;")] + /// ``` + /// + #[$attr] + #[deprecated(since = "TBD", note = "replaced by the `MIN` associated constant on this type")] + #[rustc_diagnostic_item = concat!(stringify!($T), "_legacy_const_min")] + pub const MIN: $T = $T::MIN; + + #[doc = concat!( + "The largest value that can be represented by this integer type. Use ", + "[`", stringify!($T), "::MAX", "`] instead." + )] + /// + /// # Examples + /// + /// ```rust + /// // deprecated way + #[doc = concat!("let max = std::", stringify!($T), "::MAX;")] + /// + /// // intended way + #[doc = concat!("let max = ", stringify!($T), "::MAX;")] + /// ``` + /// + #[$attr] + #[deprecated(since = "TBD", note = "replaced by the `MAX` associated constant on this type")] + #[rustc_diagnostic_item = concat!(stringify!($T), "_legacy_const_max")] + pub const MAX: $T = $T::MAX; + } + ) +} + +legacy_int_module! { i128, #[stable(feature = "i128", since = "1.26.0")] } +legacy_int_module! { i16 } +legacy_int_module! { i32 } +legacy_int_module! { i64 } +legacy_int_module! { i8 } +legacy_int_module! { isize } +legacy_int_module! { u128, #[stable(feature = "i128", since = "1.26.0")] } +legacy_int_module! { u16 } +legacy_int_module! { u32 } +legacy_int_module! { u64 } +legacy_int_module! { u8 } +legacy_int_module! { usize } diff --git a/library/core/src/num/shells/u128.rs b/library/core/src/num/shells/u128.rs deleted file mode 100644 index b1e30e384352..000000000000 --- a/library/core/src/num/shells/u128.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! Redundant constants module for the [`u128` primitive type][u128]. -//! -//! New code should use the associated constants directly on the primitive type. - -#![stable(feature = "i128", since = "1.26.0")] -#![deprecated( - since = "TBD", - note = "all constants in this module replaced by associated constants on `u128`" -)] - -int_module! { u128, #[stable(feature = "i128", since="1.26.0")] } diff --git a/library/core/src/num/shells/u16.rs b/library/core/src/num/shells/u16.rs deleted file mode 100644 index 7394977e5078..000000000000 --- a/library/core/src/num/shells/u16.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! Redundant constants module for the [`u16` primitive type][u16]. -//! -//! New code should use the associated constants directly on the primitive type. - -#![stable(feature = "rust1", since = "1.0.0")] -#![deprecated( - since = "TBD", - note = "all constants in this module replaced by associated constants on `u16`" -)] - -int_module! { u16 } diff --git a/library/core/src/num/shells/u32.rs b/library/core/src/num/shells/u32.rs deleted file mode 100644 index 4c84274e752e..000000000000 --- a/library/core/src/num/shells/u32.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! Redundant constants module for the [`u32` primitive type][u32]. -//! -//! New code should use the associated constants directly on the primitive type. - -#![stable(feature = "rust1", since = "1.0.0")] -#![deprecated( - since = "TBD", - note = "all constants in this module replaced by associated constants on `u32`" -)] - -int_module! { u32 } diff --git a/library/core/src/num/shells/u64.rs b/library/core/src/num/shells/u64.rs deleted file mode 100644 index 47a95c6820f2..000000000000 --- a/library/core/src/num/shells/u64.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! Redundant constants module for the [`u64` primitive type][u64]. -//! -//! New code should use the associated constants directly on the primitive type. - -#![stable(feature = "rust1", since = "1.0.0")] -#![deprecated( - since = "TBD", - note = "all constants in this module replaced by associated constants on `u64`" -)] - -int_module! { u64 } diff --git a/library/core/src/num/shells/u8.rs b/library/core/src/num/shells/u8.rs deleted file mode 100644 index 360baef72286..000000000000 --- a/library/core/src/num/shells/u8.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! Redundant constants module for the [`u8` primitive type][u8]. -//! -//! New code should use the associated constants directly on the primitive type. - -#![stable(feature = "rust1", since = "1.0.0")] -#![deprecated( - since = "TBD", - note = "all constants in this module replaced by associated constants on `u8`" -)] - -int_module! { u8 } diff --git a/library/core/src/num/shells/usize.rs b/library/core/src/num/shells/usize.rs deleted file mode 100644 index 44c24dfc2cf5..000000000000 --- a/library/core/src/num/shells/usize.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! Redundant constants module for the [`usize` primitive type][usize]. -//! -//! New code should use the associated constants directly on the primitive type. - -#![stable(feature = "rust1", since = "1.0.0")] -#![deprecated( - since = "TBD", - note = "all constants in this module replaced by associated constants on `usize`" -)] - -int_module! { usize } From 17943cc47a42472fed79d92cdabeff55f021d6e3 Mon Sep 17 00:00:00 2001 From: Ifeanyi Orizu Date: Mon, 8 Sep 2025 13:29:06 -0500 Subject: [PATCH 287/682] Skip flycheck for workspace if it is already being checked --- .../src/handlers/notification.rs | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs index e193ff77743d..fb743e73c74a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs @@ -303,11 +303,11 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { let world = state.snapshot(); let invocation_strategy_once = state.config.flycheck(None).invocation_strategy_once(); let may_flycheck_workspace = state.config.flycheck_workspace(None); - let mut updated = false; + let mut workspace_check_triggered = false; let task = move || -> std::result::Result<(), Cancelled> { + let saved_file = vfs_path.as_path().map(|p| p.to_owned()); if invocation_strategy_once { - let saved_file = vfs_path.as_path().map(|p| p.to_owned()); - world.flycheck[0].restart_workspace(saved_file); + world.flycheck[0].restart_workspace(saved_file.clone()); } let target = TargetSpec::for_file(&world, file_id)?.and_then(|it| { @@ -330,6 +330,7 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { tracing::debug!(?target, "flycheck target"); // we have a specific non-library target, attempt to only check that target, nothing // else will be affected + let mut package_workspace_idx = None; if let Some((target, root, package)) = target { // trigger a package check if we have a non-library target as that can't affect // anything else in the workspace OR if we're not allowed to check the workspace as @@ -345,6 +346,7 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { _ => false, }); if let Some(idx) = workspace { + package_workspace_idx = Some(idx); world.flycheck[idx].restart_for_package(package, target); } } @@ -381,8 +383,15 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { tracing::debug!(?crate_root_paths, "flycheck crate roots"); // Find all workspaces that have at least one target containing the saved file - let workspace_ids = - world.workspaces.iter().enumerate().filter(|(_, ws)| match &ws.kind { + let workspace_ids = world + .workspaces + .iter() + .enumerate() + .filter(|&(idx, _)| match package_workspace_idx { + Some(pkg_idx) => idx != pkg_idx, + None => true, + }) + .filter(|&(_, ws)| match &ws.kind { project_model::ProjectWorkspaceKind::Cargo { cargo, .. } | project_model::ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _, _)), @@ -399,20 +408,18 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { project_model::ProjectWorkspaceKind::DetachedFile { .. } => false, }); - let saved_file = vfs_path.as_path().map(|p| p.to_owned()); - // Find and trigger corresponding flychecks 'flychecks: for flycheck in world.flycheck.iter() { for (id, _) in workspace_ids.clone() { if id == flycheck.id() { - updated = true; + workspace_check_triggered = true; flycheck.restart_workspace(saved_file.clone()); continue 'flychecks; } } } // No specific flycheck was triggered, so let's trigger all of them. - if !updated { + if !workspace_check_triggered && package_workspace_idx.is_none() { for flycheck in world.flycheck.iter() { flycheck.restart_workspace(saved_file.clone()); } From 92f24020967e4e1f1375dbb2d1ba85cd4430fc78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Sun, 24 Aug 2025 10:56:41 +0200 Subject: [PATCH 288/682] port `#[recursion_limit]` to the new attribute parsing infrastructure --- compiler/rustc_attr_parsing/messages.ftl | 4 ++ .../src/attributes/crate_level.rs | 62 ++++++++++++++++++- .../src/attributes/prelude.rs | 2 +- compiler/rustc_attr_parsing/src/context.rs | 3 +- .../src/session_diagnostics.rs | 10 +++ .../rustc_hir/src/attrs/data_structures.rs | 3 + .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + .../rustc_hir/src/attrs/pretty_printing.rs | 2 +- compiler/rustc_passes/src/check_attr.rs | 1 + 9 files changed, 84 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index 7fa1293463cc..839a5d23c3b4 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -247,3 +247,7 @@ attr_parsing_raw_dylib_only_windows = attr_parsing_whole_archive_needs_static = linking modifier `whole-archive` is only compatible with `static` linking kind + +attr_parsing_limit_invalid = + `limit` must be a non-negative integer + .label = {$error_str} diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index 2fed09b85e87..e3654d495801 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -1,7 +1,40 @@ -use rustc_feature::AttributeType; +use std::num::IntErrorKind; + +use crate::session_diagnostics::LimitInvalid; use super::prelude::*; +impl AcceptContext<'_, '_, S> { + fn parse_limit_int(&self, nv: &NameValueParser) -> Option { + let Some(limit) = nv.value_as_str() else { + self.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + return None; + }; + + let error_str = match limit.as_str().parse() { + Ok(i) => return Some(i), + Err(e) => match e.kind() { + IntErrorKind::PosOverflow => "`limit` is too large", + IntErrorKind::Empty => "`limit` must be a non-negative integer", + IntErrorKind::InvalidDigit => "not a valid integer", + IntErrorKind::NegOverflow => { + panic!( + "`limit` should never negatively overflow since we're parsing into a usize and we'd get Empty instead" + ) + } + IntErrorKind::Zero => { + panic!("zero is a valid `limit` so should have returned Ok() when parsing") + } + kind => panic!("unimplemented IntErrorKind variant: {:?}", kind), + }, + }; + + self.emit_err(LimitInvalid { span: self.attr_span, value_span: nv.value_span, error_str }); + + None + } +} + pub(crate) struct CrateNameParser; impl SingleAttributeParser for CrateNameParser { @@ -34,3 +67,30 @@ impl SingleAttributeParser for CrateNameParser { }) } } + +pub(crate) struct RecursionLimitParser; + +impl SingleAttributeParser for RecursionLimitParser { + const PATH: &[Symbol] = &[sym::recursion_limit]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; + const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N", "https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute"); + const TYPE: AttributeType = AttributeType::CrateLevel; + + // FIXME: recursion limit is allowed on all targets and ignored, + // even though it should only be valid on crates of course + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + let ArgParser::NameValue(nv) = args else { + cx.expected_name_value(cx.attr_span, None); + return None; + }; + + Some(AttributeKind::RecursionLimit { + limit: cx.parse_limit_int(nv)?, + attr_span: cx.attr_span, + limit_span: nv.value_span, + }) + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/prelude.rs b/compiler/rustc_attr_parsing/src/attributes/prelude.rs index 6aef7e7a67be..e53a43661318 100644 --- a/compiler/rustc_attr_parsing/src/attributes/prelude.rs +++ b/compiler/rustc_attr_parsing/src/attributes/prelude.rs @@ -1,6 +1,6 @@ // templates #[doc(hidden)] -pub(super) use rustc_feature::{AttributeTemplate, template}; +pub(super) use rustc_feature::{AttributeTemplate, AttributeType, template}; // data structures #[doc(hidden)] pub(super) use rustc_hir::attrs::AttributeKind; diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 7f5b810f244f..5700668145bf 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -24,7 +24,7 @@ use crate::attributes::codegen_attrs::{ UsedParser, }; use crate::attributes::confusables::ConfusablesParser; -use crate::attributes::crate_level::CrateNameParser; +use crate::attributes::crate_level::{CrateNameParser, RecursionLimitParser}; use crate::attributes::deprecation::DeprecationParser; use crate::attributes::dummy::DummyParser; use crate::attributes::inline::{InlineParser, RustcForceInlineParser}; @@ -185,6 +185,7 @@ attribute_parsers!( Single, Single, Single, + Single, Single, Single, Single, diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index a9dee23bf6a3..32ea9005a97d 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -930,3 +930,13 @@ pub(crate) struct ImportNameTypeRaw { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(attr_parsing_limit_invalid)] +pub(crate) struct LimitInvalid<'a> { + #[primary_span] + pub span: Span, + #[label] + pub value_span: Span, + pub error_str: &'a str, +} diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index dd5565d6f909..bfc7613342a0 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -611,6 +611,9 @@ pub enum AttributeKind { /// Represents `#[rustc_pub_transparent]` (used by the `repr_transparent_external_private_fields` lint). PubTransparent(Span), + /// Represents [`#[recursion_limit]`](https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute) + RecursionLimit { attr_span: Span, limit_span: Span, limit: usize }, + /// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations). Repr { reprs: ThinVec<(ReprAttr, Span)>, first_span: Span }, diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 3810bb6d0038..26929e805a52 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -75,6 +75,7 @@ impl AttributeKind { ProcMacroAttribute(..) => No, ProcMacroDerive { .. } => No, PubTransparent(..) => Yes, + RecursionLimit { .. } => No, Repr { .. } => No, RustcBuiltinMacro { .. } => Yes, RustcLayoutScalarValidRangeEnd(..) => Yes, diff --git a/compiler/rustc_hir/src/attrs/pretty_printing.rs b/compiler/rustc_hir/src/attrs/pretty_printing.rs index e65de25b4511..a4c2beb689dc 100644 --- a/compiler/rustc_hir/src/attrs/pretty_printing.rs +++ b/compiler/rustc_hir/src/attrs/pretty_printing.rs @@ -146,7 +146,7 @@ macro_rules! print_tup { print_tup!(A B C D E F G H); print_skip!(Span, (), ErrorGuaranteed); -print_disp!(u16, bool, NonZero); +print_disp!(u16, bool, NonZero, usize); print_debug!( Symbol, Ident, diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 2cd4830b5d93..9a94f849a089 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -270,6 +270,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::Linkage(..) | AttributeKind::MustUse { .. } | AttributeKind::CrateName { .. } + | AttributeKind::RecursionLimit { .. } ) => { /* do nothing */ } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); From a38288bbe00f64204967819fd2cfe107a7902404 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Sun, 24 Aug 2025 13:14:27 +0200 Subject: [PATCH 289/682] port `#[move_size_limit]` to the new attribute parsing infrastructure --- .../src/attributes/crate_level.rs | 26 +++++++++++++++++++ compiler/rustc_attr_parsing/src/context.rs | 3 ++- .../rustc_hir/src/attrs/data_structures.rs | 3 +++ .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_passes/src/check_attr.rs | 1 + 5 files changed, 33 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index e3654d495801..af19b9f552da 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -94,3 +94,29 @@ impl SingleAttributeParser for RecursionLimitParser { }) } } + +pub(crate) struct MoveSizeLimitParser; + +impl SingleAttributeParser for MoveSizeLimitParser { + const PATH: &[Symbol] = &[sym::move_size_limit]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N"); + + // FIXME: move size limit is allowed on all targets and ignored, + // even though it should only be valid on crates of course + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + let ArgParser::NameValue(nv) = args else { + cx.expected_name_value(cx.attr_span, None); + return None; + }; + + Some(AttributeKind::MoveSizeLimit { + limit: cx.parse_limit_int(nv)?, + attr_span: cx.attr_span, + limit_span: nv.value_span, + }) + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 5700668145bf..4ec9df88e827 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -24,7 +24,7 @@ use crate::attributes::codegen_attrs::{ UsedParser, }; use crate::attributes::confusables::ConfusablesParser; -use crate::attributes::crate_level::{CrateNameParser, RecursionLimitParser}; +use crate::attributes::crate_level::{CrateNameParser, MoveSizeLimitParser, RecursionLimitParser}; use crate::attributes::deprecation::DeprecationParser; use crate::attributes::dummy::DummyParser; use crate::attributes::inline::{InlineParser, RustcForceInlineParser}; @@ -181,6 +181,7 @@ attribute_parsers!( Single, Single, Single, + Single, Single, Single, Single, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index bfc7613342a0..c565b8226197 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -565,6 +565,9 @@ pub enum AttributeKind { /// Represents [`#[may_dangle]`](https://std-dev-guide.rust-lang.org/tricky/may-dangle.html). MayDangle(Span), + /// Represents `#[move_size_limit]` + MoveSizeLimit { attr_span: Span, limit_span: Span, limit: usize }, + /// Represents `#[must_use]`. MustUse { span: Span, diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 26929e805a52..4df77c901557 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -61,6 +61,7 @@ impl AttributeKind { MacroUse { .. } => No, Marker(..) => No, MayDangle(..) => No, + MoveSizeLimit { .. } => No, MustUse { .. } => Yes, Naked(..) => No, NoImplicitPrelude(..) => No, diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 9a94f849a089..08c3162b8317 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -271,6 +271,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::MustUse { .. } | AttributeKind::CrateName { .. } | AttributeKind::RecursionLimit { .. } + | AttributeKind::MoveSizeLimit { .. } ) => { /* do nothing */ } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); From 5dbe099dd3b9abb13cd63f8655e495f7a8d1bfcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Sun, 24 Aug 2025 13:18:51 +0200 Subject: [PATCH 290/682] port `#[type_length_limit]` to the new attribute parsing infrastructure --- .../src/attributes/crate_level.rs | 27 +++++++++++++++++++ compiler/rustc_attr_parsing/src/context.rs | 5 +++- .../rustc_hir/src/attrs/data_structures.rs | 3 +++ .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_passes/src/check_attr.rs | 1 + 5 files changed, 36 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index af19b9f552da..34793a7f403f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -120,3 +120,30 @@ impl SingleAttributeParser for MoveSizeLimitParser { }) } } + +pub(crate) struct TypeLengthLimitParser; + +impl SingleAttributeParser for TypeLengthLimitParser { + const PATH: &[Symbol] = &[sym::type_length_limit]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; + const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N"); + const TYPE: AttributeType = AttributeType::CrateLevel; + + // FIXME: recursion limit is allowed on all targets and ignored, + // even though it should only be valid on crates of course + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[Target::Crate]); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + let ArgParser::NameValue(nv) = args else { + cx.expected_name_value(cx.attr_span, None); + return None; + }; + + Some(AttributeKind::TypeLengthLimit { + limit: cx.parse_limit_int(nv)?, + attr_span: cx.attr_span, + limit_span: nv.value_span, + }) + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 4ec9df88e827..25a1993d0896 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -24,7 +24,9 @@ use crate::attributes::codegen_attrs::{ UsedParser, }; use crate::attributes::confusables::ConfusablesParser; -use crate::attributes::crate_level::{CrateNameParser, MoveSizeLimitParser, RecursionLimitParser}; +use crate::attributes::crate_level::{ + CrateNameParser, MoveSizeLimitParser, RecursionLimitParser, TypeLengthLimitParser, +}; use crate::attributes::deprecation::DeprecationParser; use crate::attributes::dummy::DummyParser; use crate::attributes::inline::{InlineParser, RustcForceInlineParser}; @@ -196,6 +198,7 @@ attribute_parsers!( Single, Single, Single, + Single, Single>, Single>, Single>, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index c565b8226197..22b39cb19e11 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -667,6 +667,9 @@ pub enum AttributeKind { /// Represents `#[type_const]`. TypeConst(Span), + /// Represents `#[type_length_limit]` + TypeLengthLimit { attr_span: Span, limit_span: Span, limit: usize }, + /// Represents `#[rustc_unsafe_specialization_marker]`. UnsafeSpecializationMarker(Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 4df77c901557..e1f2d6d36403 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -91,6 +91,7 @@ impl AttributeKind { TargetFeature { .. } => No, TrackCaller(..) => Yes, TypeConst(..) => Yes, + TypeLengthLimit { .. } => Yes, UnsafeSpecializationMarker(..) => No, UnstableFeatureBound(..) => No, Used { .. } => No, diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 08c3162b8317..e69071c73e1e 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -272,6 +272,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::CrateName { .. } | AttributeKind::RecursionLimit { .. } | AttributeKind::MoveSizeLimit { .. } + | AttributeKind::TypeLengthLimit { .. } ) => { /* do nothing */ } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); From b82171de5fe2a13a12f35ce4368ef34a477f55c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Sun, 24 Aug 2025 13:19:31 +0200 Subject: [PATCH 291/682] port `#[pattern_complexity_limit]` to the new attribute parsing infrastructure --- .../src/attributes/crate_level.rs | 27 +++++++++++++++++++ compiler/rustc_attr_parsing/src/context.rs | 4 ++- .../rustc_hir/src/attrs/data_structures.rs | 3 +++ .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_passes/src/check_attr.rs | 1 + 5 files changed, 35 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index 34793a7f403f..6fd4a4a753b6 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -147,3 +147,30 @@ impl SingleAttributeParser for TypeLengthLimitParser { }) } } + +pub(crate) struct PatternComplexityLimitParser; + +impl SingleAttributeParser for PatternComplexityLimitParser { + const PATH: &[Symbol] = &[sym::pattern_complexity_limit]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N"); + const TYPE: AttributeType = AttributeType::CrateLevel; + + // FIXME: recursion limit is allowed on all targets and ignored, + // even though it should only be valid on crates of course + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + let ArgParser::NameValue(nv) = args else { + cx.expected_name_value(cx.attr_span, None); + return None; + }; + + Some(AttributeKind::PatternComplexityLimit { + limit: cx.parse_limit_int(nv)?, + attr_span: cx.attr_span, + limit_span: nv.value_span, + }) + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 25a1993d0896..fc41e38c797b 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -25,7 +25,8 @@ use crate::attributes::codegen_attrs::{ }; use crate::attributes::confusables::ConfusablesParser; use crate::attributes::crate_level::{ - CrateNameParser, MoveSizeLimitParser, RecursionLimitParser, TypeLengthLimitParser, + CrateNameParser, MoveSizeLimitParser, PatternComplexityLimitParser, RecursionLimitParser, + TypeLengthLimitParser, }; use crate::attributes::deprecation::DeprecationParser; use crate::attributes::dummy::DummyParser; @@ -187,6 +188,7 @@ attribute_parsers!( Single, Single, Single, + Single, Single, Single, Single, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 22b39cb19e11..c4e068ad4257 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -599,6 +599,9 @@ pub enum AttributeKind { /// Represents `#[path]` Path(Symbol, Span), + /// Represents `#[pattern_complexity_limit]` + PatternComplexityLimit { attr_span: Span, limit_span: Span, limit: usize }, + /// Represents `#[pointee]` Pointee(Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index e1f2d6d36403..89ba1e0d7de4 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -71,6 +71,7 @@ impl AttributeKind { ParenSugar(..) => No, PassByValue(..) => Yes, Path(..) => No, + PatternComplexityLimit { .. } => No, Pointee(..) => No, ProcMacro(..) => No, ProcMacroAttribute(..) => No, diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index e69071c73e1e..487bdd2a888e 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -273,6 +273,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RecursionLimit { .. } | AttributeKind::MoveSizeLimit { .. } | AttributeKind::TypeLengthLimit { .. } + | AttributeKind::PatternComplexityLimit { .. } ) => { /* do nothing */ } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); From 6087d89004eda5e1f6406182b9866497ecb22e99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Sun, 24 Aug 2025 13:25:36 +0200 Subject: [PATCH 292/682] fixup limit handling code --- Cargo.lock | 1 - .../src/attributes/crate_level.rs | 34 +-- .../src/attributes/prelude.rs | 3 +- compiler/rustc_attr_parsing/src/context.rs | 13 +- .../src/interpret/eval_context.rs | 2 +- compiler/rustc_expand/src/base.rs | 3 +- compiler/rustc_expand/src/errors.rs | 2 +- compiler/rustc_expand/src/expand.rs | 4 +- .../rustc_hir/src/attrs/data_structures.rs | 9 +- .../rustc_hir/src/attrs/encode_cross_crate.rs | 2 +- .../rustc_hir/src/attrs/pretty_printing.rs | 4 +- compiler/rustc_hir/src/lib.rs | 1 + compiler/rustc_hir/src/limit.rs | 63 +++++ compiler/rustc_hir_analysis/src/autoderef.rs | 2 +- compiler/rustc_hir_analysis/src/errors.rs | 3 +- compiler/rustc_interface/messages.ftl | 4 - compiler/rustc_interface/src/errors.rs | 10 - compiler/rustc_interface/src/limits.rs | 88 ++---- compiler/rustc_interface/src/passes.rs | 54 ++-- compiler/rustc_middle/src/error.rs | 2 +- compiler/rustc_middle/src/ty/context.rs | 3 +- compiler/rustc_middle/src/ty/error.rs | 3 +- compiler/rustc_middle/src/ty/print/pretty.rs | 2 +- compiler/rustc_middle/src/ty/util.rs | 2 +- .../rustc_mir_transform/src/inline/cycle.rs | 2 +- compiler/rustc_monomorphize/src/collector.rs | 2 +- .../src/mono_checks/move_check.rs | 2 +- compiler/rustc_query_impl/Cargo.toml | 1 - compiler/rustc_query_impl/src/plumbing.rs | 2 +- compiler/rustc_query_system/src/error.rs | 2 +- compiler/rustc_session/src/session.rs | 62 +---- .../src/error_reporting/traits/overflow.rs | 4 +- compiler/rustc_ty_utils/src/needs_drop.rs | 2 +- .../issue-43106-gating-of-builtin-attrs.rs | 24 +- ...issue-43106-gating-of-builtin-attrs.stderr | 262 ++++++++++-------- .../lint/unused/unused-attr-duplicate.stderr | 52 ++-- .../unused/unused-attr-macro-rules.stderr | 35 +-- tests/ui/recursion_limit/empty.rs | 8 +- tests/ui/recursion_limit/empty.stderr | 12 +- tests/ui/recursion_limit/invalid_digit.rs | 8 +- tests/ui/recursion_limit/invalid_digit.stderr | 12 +- .../recursion_limit/invalid_digit_type.stderr | 8 +- tests/ui/recursion_limit/invalid_macro.rs | 2 +- tests/ui/recursion_limit/invalid_macro.stderr | 8 +- tests/ui/recursion_limit/no-value.stderr | 3 +- tests/ui/recursion_limit/overflow.rs | 3 - tests/ui/recursion_limit/overflow.stderr | 12 +- 47 files changed, 385 insertions(+), 457 deletions(-) create mode 100644 compiler/rustc_hir/src/limit.rs diff --git a/Cargo.lock b/Cargo.lock index 81f19668c24b..78be59b3091b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4409,7 +4409,6 @@ dependencies = [ "rustc_middle", "rustc_query_system", "rustc_serialize", - "rustc_session", "rustc_span", "tracing", ] diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index 6fd4a4a753b6..d23a7ae72f8c 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -1,18 +1,19 @@ use std::num::IntErrorKind; -use crate::session_diagnostics::LimitInvalid; +use rustc_hir::limit::Limit; use super::prelude::*; +use crate::session_diagnostics::LimitInvalid; impl AcceptContext<'_, '_, S> { - fn parse_limit_int(&self, nv: &NameValueParser) -> Option { + fn parse_limit_int(&self, nv: &NameValueParser) -> Option { let Some(limit) = nv.value_as_str() else { self.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); return None; }; let error_str = match limit.as_str().parse() { - Ok(i) => return Some(i), + Ok(i) => return Some(Limit::new(i)), Err(e) => match e.kind() { IntErrorKind::PosOverflow => "`limit` is too large", IntErrorKind::Empty => "`limit` must be a non-negative integer", @@ -44,8 +45,8 @@ impl SingleAttributeParser for CrateNameParser { const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name"); const TYPE: AttributeType = AttributeType::CrateLevel; - // FIXME: crate name is allowed on all targets and ignored, - // even though it should only be valid on crates of course + // because it's a crate-level attribute, we already warn about it. + // Putting target limitations here would give duplicate warnings const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { @@ -77,8 +78,8 @@ impl SingleAttributeParser for RecursionLimitParser { const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N", "https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute"); const TYPE: AttributeType = AttributeType::CrateLevel; - // FIXME: recursion limit is allowed on all targets and ignored, - // even though it should only be valid on crates of course + // because it's a crate-level attribute, we already warn about it. + // Putting target limitations here would give duplicate warnings const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { @@ -102,10 +103,11 @@ impl SingleAttributeParser for MoveSizeLimitParser { const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N"); + const TYPE: AttributeType = AttributeType::CrateLevel; - // FIXME: move size limit is allowed on all targets and ignored, - // even though it should only be valid on crates of course - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + // because it's a crate-level attribute, we already warn about it. + // Putting target limitations here would give duplicate warnings + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { let ArgParser::NameValue(nv) = args else { @@ -130,9 +132,9 @@ impl SingleAttributeParser for TypeLengthLimitParser { const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N"); const TYPE: AttributeType = AttributeType::CrateLevel; - // FIXME: recursion limit is allowed on all targets and ignored, - // even though it should only be valid on crates of course - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[Target::Crate]); + // because it's a crate-level attribute, we already warn about it. + // Putting target limitations here would give duplicate warnings + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { let ArgParser::NameValue(nv) = args else { @@ -157,9 +159,9 @@ impl SingleAttributeParser for PatternComplexityLimitParser { const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N"); const TYPE: AttributeType = AttributeType::CrateLevel; - // FIXME: recursion limit is allowed on all targets and ignored, - // even though it should only be valid on crates of course - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + // because it's a crate-level attribute, we already warn about it. + // Putting target limitations here would give duplicate warnings + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { let ArgParser::NameValue(nv) = args else { diff --git a/compiler/rustc_attr_parsing/src/attributes/prelude.rs b/compiler/rustc_attr_parsing/src/attributes/prelude.rs index e53a43661318..8f040ffb9d41 100644 --- a/compiler/rustc_attr_parsing/src/attributes/prelude.rs +++ b/compiler/rustc_attr_parsing/src/attributes/prelude.rs @@ -1,7 +1,6 @@ -// templates +// data structures #[doc(hidden)] pub(super) use rustc_feature::{AttributeTemplate, AttributeType, template}; -// data structures #[doc(hidden)] pub(super) use rustc_hir::attrs::AttributeKind; #[doc(hidden)] diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index fc41e38c797b..98e86838a3a1 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -353,7 +353,10 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> { /// must be delayed until after HIR is built. This method will take care of the details of /// that. pub(crate) fn emit_lint(&mut self, lint: AttributeLintKind, span: Span) { - if matches!(self.stage.should_emit(), ShouldEmit::Nothing) { + if !matches!( + self.stage.should_emit(), + ShouldEmit::ErrorsAndLints | ShouldEmit::EarlyFatal { also_emit_lints: true } + ) { return; } let id = self.target_id; @@ -677,20 +680,20 @@ pub enum ShouldEmit { /// /// Only relevant when early parsing, in late parsing equivalent to `ErrorsAndLints`. /// Late parsing is never fatal, and instead tries to emit as many diagnostics as possible. - EarlyFatal, + EarlyFatal { also_emit_lints: bool }, /// The operation will emit errors and lints. /// This is usually what you need. ErrorsAndLints, /// The operation will emit *not* errors and lints. - /// Use this if you are *sure* that this operation will be called at a different time with `ShouldEmit::Emit`. + /// Use this if you are *sure* that this operation will be called at a different time with `ShouldEmit::ErrorsAndLints`. Nothing, } impl ShouldEmit { pub(crate) fn emit_err(&self, diag: Diag<'_>) -> ErrorGuaranteed { match self { - ShouldEmit::EarlyFatal if diag.level() == Level::DelayedBug => diag.emit(), - ShouldEmit::EarlyFatal => diag.upgrade_to_fatal().emit(), + ShouldEmit::EarlyFatal { .. } if diag.level() == Level::DelayedBug => diag.emit(), + ShouldEmit::EarlyFatal { .. } => diag.upgrade_to_fatal().emit(), ShouldEmit::ErrorsAndLints => diag.emit(), ShouldEmit::Nothing => diag.delay_as_bug(), } diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 9681d89ce35f..91ed71ac3e54 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -4,6 +4,7 @@ use either::{Left, Right}; use rustc_abi::{Align, HasDataLayout, Size, TargetDataLayout}; use rustc_errors::DiagCtxtHandle; use rustc_hir::def_id::DefId; +use rustc_hir::limit::Limit; use rustc_middle::mir::interpret::{ErrorHandled, InvalidMetaKind, ReportedErrorInfo}; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::layout::{ @@ -12,7 +13,6 @@ use rustc_middle::ty::layout::{ }; use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeFoldable, TypingEnv, Variance}; use rustc_middle::{mir, span_bug}; -use rustc_session::Limit; use rustc_span::Span; use rustc_target::callconv::FnAbi; use tracing::{debug, trace}; diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 8ff21509f4a9..88f88f30a8c6 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -18,13 +18,14 @@ use rustc_feature::Features; use rustc_hir as hir; use rustc_hir::attrs::{AttributeKind, CfgEntry, Deprecation}; use rustc_hir::def::MacroKinds; +use rustc_hir::limit::Limit; use rustc_hir::{Stability, find_attr}; use rustc_lint_defs::RegisteredTools; use rustc_parse::MACRO_ARGUMENTS; use rustc_parse::parser::{ForceCollect, Parser}; +use rustc_session::Session; use rustc_session::config::CollapseMacroDebuginfo; use rustc_session::parse::ParseSess; -use rustc_session::{Limit, Session}; use rustc_span::def_id::{CrateNum, DefId, LocalDefId}; use rustc_span::edition::Edition; use rustc_span::hygiene::{AstPass, ExpnData, ExpnKind, LocalExpnId, MacroKind}; diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index ba9d76970f0c..6eb5cd65846e 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -2,8 +2,8 @@ use std::borrow::Cow; use rustc_ast::ast; use rustc_errors::codes::*; +use rustc_hir::limit::Limit; use rustc_macros::{Diagnostic, Subdiagnostic}; -use rustc_session::Limit; use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol}; #[derive(Diagnostic)] diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index dbb4a9de9e03..38e057d2776e 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -19,14 +19,15 @@ use rustc_errors::PResult; use rustc_feature::Features; use rustc_hir::Target; use rustc_hir::def::MacroKinds; +use rustc_hir::limit::Limit; use rustc_parse::parser::{ AttemptLocalParseRecovery, CommaRecoveryMode, ForceCollect, Parser, RecoverColon, RecoverComma, token_descr, }; +use rustc_session::Session; use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::{UNUSED_ATTRIBUTES, UNUSED_DOC_COMMENTS}; use rustc_session::parse::feature_err; -use rustc_session::{Limit, Session}; use rustc_span::hygiene::SyntaxContext; use rustc_span::{ErrorGuaranteed, FileName, Ident, LocalExpnId, Span, Symbol, sym}; use smallvec::SmallVec; @@ -2529,6 +2530,7 @@ impl ExpansionConfig<'_> { ExpansionConfig { crate_name, features, + // FIXME should this limit be configurable? recursion_limit: Limit::new(1024), trace_mac: false, should_test: false, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index c4e068ad4257..b4c1b61b9b58 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -14,6 +14,7 @@ pub use rustc_target::spec::SanitizerSet; use thin_vec::ThinVec; use crate::attrs::pretty_printing::PrintAttribute; +use crate::limit::Limit; use crate::{DefaultBodyStability, PartialConstStability, RustcVersion, Stability}; #[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, PrintAttribute)] @@ -566,7 +567,7 @@ pub enum AttributeKind { MayDangle(Span), /// Represents `#[move_size_limit]` - MoveSizeLimit { attr_span: Span, limit_span: Span, limit: usize }, + MoveSizeLimit { attr_span: Span, limit_span: Span, limit: Limit }, /// Represents `#[must_use]`. MustUse { @@ -600,7 +601,7 @@ pub enum AttributeKind { Path(Symbol, Span), /// Represents `#[pattern_complexity_limit]` - PatternComplexityLimit { attr_span: Span, limit_span: Span, limit: usize }, + PatternComplexityLimit { attr_span: Span, limit_span: Span, limit: Limit }, /// Represents `#[pointee]` Pointee(Span), @@ -618,7 +619,7 @@ pub enum AttributeKind { PubTransparent(Span), /// Represents [`#[recursion_limit]`](https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute) - RecursionLimit { attr_span: Span, limit_span: Span, limit: usize }, + RecursionLimit { attr_span: Span, limit_span: Span, limit: Limit }, /// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations). Repr { reprs: ThinVec<(ReprAttr, Span)>, first_span: Span }, @@ -671,7 +672,7 @@ pub enum AttributeKind { TypeConst(Span), /// Represents `#[type_length_limit]` - TypeLengthLimit { attr_span: Span, limit_span: Span, limit: usize }, + TypeLengthLimit { attr_span: Span, limit_span: Span, limit: Limit }, /// Represents `#[rustc_unsafe_specialization_marker]`. UnsafeSpecializationMarker(Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 89ba1e0d7de4..0e208be3497a 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -92,7 +92,7 @@ impl AttributeKind { TargetFeature { .. } => No, TrackCaller(..) => Yes, TypeConst(..) => Yes, - TypeLengthLimit { .. } => Yes, + TypeLengthLimit { .. } => No, UnsafeSpecializationMarker(..) => No, UnstableFeatureBound(..) => No, Used { .. } => No, diff --git a/compiler/rustc_hir/src/attrs/pretty_printing.rs b/compiler/rustc_hir/src/attrs/pretty_printing.rs index a4c2beb689dc..d13cd3fd1dab 100644 --- a/compiler/rustc_hir/src/attrs/pretty_printing.rs +++ b/compiler/rustc_hir/src/attrs/pretty_printing.rs @@ -9,6 +9,8 @@ use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol}; use rustc_target::spec::SanitizerSet; use thin_vec::ThinVec; +use crate::limit::Limit; + /// This trait is used to print attributes in `rustc_hir_pretty`. /// /// For structs and enums it can be derived using [`rustc_macros::PrintAttribute`]. @@ -146,7 +148,7 @@ macro_rules! print_tup { print_tup!(A B C D E F G H); print_skip!(Span, (), ErrorGuaranteed); -print_disp!(u16, bool, NonZero, usize); +print_disp!(u16, bool, NonZero, Limit); print_debug!( Symbol, Ident, diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index 78fc63753a2e..eb630fc80186 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -25,6 +25,7 @@ mod hir; pub use rustc_hir_id::{self as hir_id, *}; pub mod intravisit; pub mod lang_items; +pub mod limit; pub mod lints; pub mod pat_util; mod stability; diff --git a/compiler/rustc_hir/src/limit.rs b/compiler/rustc_hir/src/limit.rs new file mode 100644 index 000000000000..7e6d23f51bdb --- /dev/null +++ b/compiler/rustc_hir/src/limit.rs @@ -0,0 +1,63 @@ +use std::fmt; +use std::ops::{Div, Mul}; + +use rustc_error_messages::{DiagArgValue, IntoDiagArg}; +use rustc_macros::{Decodable, Encodable, HashStable_Generic}; + +/// New-type wrapper around `usize` for representing limits. Ensures that comparisons against +/// limits are consistent throughout the compiler. +#[derive(Clone, Copy, Debug, HashStable_Generic, Encodable, Decodable)] +pub struct Limit(pub usize); + +impl Limit { + /// Create a new limit from a `usize`. + pub fn new(value: usize) -> Self { + Limit(value) + } + + /// Create a new unlimited limit. + pub fn unlimited() -> Self { + Limit(usize::MAX) + } + + /// Check that `value` is within the limit. Ensures that the same comparisons are used + /// throughout the compiler, as mismatches can cause ICEs, see #72540. + #[inline] + pub fn value_within_limit(&self, value: usize) -> bool { + value <= self.0 + } +} + +impl From for Limit { + fn from(value: usize) -> Self { + Self::new(value) + } +} + +impl fmt::Display for Limit { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl Div for Limit { + type Output = Limit; + + fn div(self, rhs: usize) -> Self::Output { + Limit::new(self.0 / rhs) + } +} + +impl Mul for Limit { + type Output = Limit; + + fn mul(self, rhs: usize) -> Self::Output { + Limit::new(self.0 * rhs) + } +} + +impl IntoDiagArg for Limit { + fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { + self.to_string().into_diag_arg(&mut None) + } +} diff --git a/compiler/rustc_hir_analysis/src/autoderef.rs b/compiler/rustc_hir_analysis/src/autoderef.rs index e27e68d36624..e8237471e1b6 100644 --- a/compiler/rustc_hir_analysis/src/autoderef.rs +++ b/compiler/rustc_hir_analysis/src/autoderef.rs @@ -1,7 +1,7 @@ +use rustc_hir::limit::Limit; use rustc_infer::infer::InferCtxt; use rustc_infer::traits::PredicateObligations; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; -use rustc_session::Limit; use rustc_span::def_id::{LOCAL_CRATE, LocalDefId}; use rustc_span::{ErrorGuaranteed, Span}; use rustc_trait_selection::traits::ObligationCtxt; diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 6565ad7cf536..3b6367219b7f 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -6,6 +6,7 @@ use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, DiagSymbolList, Diagnostic, EmissionGuarantee, Level, MultiSpan, }; +use rustc_hir::limit::Limit; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::Ty; use rustc_span::{Ident, Span, Symbol}; @@ -557,7 +558,7 @@ pub(crate) struct AutoDerefReachedRecursionLimit<'a> { #[label] pub span: Span, pub ty: Ty<'a>, - pub suggested_limit: rustc_session::Limit, + pub suggested_limit: Limit, pub crate_name: Symbol, } diff --git a/compiler/rustc_interface/messages.ftl b/compiler/rustc_interface/messages.ftl index 4b9c71d71725..d83b18cbc2df 100644 --- a/compiler/rustc_interface/messages.ftl +++ b/compiler/rustc_interface/messages.ftl @@ -30,10 +30,6 @@ interface_ignoring_out_dir = ignoring --out-dir flag due to -o flag interface_input_file_would_be_overwritten = the input file "{$path}" would be overwritten by the generated executable -interface_limit_invalid = - `limit` must be a non-negative integer - .label = {$error_str} - interface_mixed_bin_crate = cannot mix `bin` crate type with others diff --git a/compiler/rustc_interface/src/errors.rs b/compiler/rustc_interface/src/errors.rs index 6b39b4f18911..d1082eaf6173 100644 --- a/compiler/rustc_interface/src/errors.rs +++ b/compiler/rustc_interface/src/errors.rs @@ -108,13 +108,3 @@ pub(crate) struct AbiRequiredTargetFeature<'a> { pub feature: &'a str, pub enabled: &'a str, } - -#[derive(Diagnostic)] -#[diag(interface_limit_invalid)] -pub(crate) struct LimitInvalid<'a> { - #[primary_span] - pub span: Span, - #[label] - pub value_span: Span, - pub error_str: &'a str, -} diff --git a/compiler/rustc_interface/src/limits.rs b/compiler/rustc_interface/src/limits.rs index 8f01edec09f3..10e58f32256f 100644 --- a/compiler/rustc_interface/src/limits.rs +++ b/compiler/rustc_interface/src/limits.rs @@ -8,78 +8,32 @@ //! Users can override these limits via an attribute on the crate like //! `#![recursion_limit="22"]`. This pass just looks for those attributes. -use std::num::IntErrorKind; - -use rustc_ast::attr::AttributeExt; -use rustc_middle::bug; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::limit::Limit; +use rustc_hir::{Attribute, find_attr}; use rustc_middle::query::Providers; -use rustc_session::{Limit, Limits, Session}; -use rustc_span::{Symbol, sym}; - -use crate::errors::LimitInvalid; +use rustc_session::Limits; pub(crate) fn provide(providers: &mut Providers) { - providers.limits = |tcx, ()| Limits { - recursion_limit: get_recursion_limit(tcx.hir_krate_attrs(), tcx.sess), - move_size_limit: get_limit( - tcx.hir_krate_attrs(), - tcx.sess, - sym::move_size_limit, - Limit::new(tcx.sess.opts.unstable_opts.move_size_limit.unwrap_or(0)), - ), - type_length_limit: get_limit( - tcx.hir_krate_attrs(), - tcx.sess, - sym::type_length_limit, - Limit::new(2usize.pow(24)), - ), - pattern_complexity_limit: get_limit( - tcx.hir_krate_attrs(), - tcx.sess, - sym::pattern_complexity_limit, - Limit::unlimited(), - ), + providers.limits = |tcx, ()| { + let attrs = tcx.hir_krate_attrs(); + Limits { + recursion_limit: get_recursion_limit(tcx.hir_krate_attrs()), + move_size_limit: + find_attr!(attrs, AttributeKind::MoveSizeLimit { limit, .. } => *limit) + .unwrap_or(Limit::new(tcx.sess.opts.unstable_opts.move_size_limit.unwrap_or(0))), + type_length_limit: + find_attr!(attrs, AttributeKind::TypeLengthLimit { limit, .. } => *limit) + .unwrap_or(Limit::new(2usize.pow(24))), + pattern_complexity_limit: + find_attr!(attrs, AttributeKind::PatternComplexityLimit { limit, .. } => *limit) + .unwrap_or(Limit::unlimited()), + } } } // This one is separate because it must be read prior to macro expansion. -pub(crate) fn get_recursion_limit(krate_attrs: &[impl AttributeExt], sess: &Session) -> Limit { - get_limit(krate_attrs, sess, sym::recursion_limit, Limit::new(128)) -} - -fn get_limit( - krate_attrs: &[impl AttributeExt], - sess: &Session, - name: Symbol, - default: Limit, -) -> Limit { - for attr in krate_attrs { - if !attr.has_name(name) { - continue; - } - - if let Some(sym) = attr.value_str() { - match sym.as_str().parse() { - Ok(n) => return Limit::new(n), - Err(e) => { - let error_str = match e.kind() { - IntErrorKind::PosOverflow => "`limit` is too large", - IntErrorKind::Empty => "`limit` must be a non-negative integer", - IntErrorKind::InvalidDigit => "not a valid integer", - IntErrorKind::NegOverflow => { - bug!("`limit` should never negatively overflow") - } - IntErrorKind::Zero => bug!("zero is a valid `limit`"), - kind => bug!("unimplemented IntErrorKind variant: {:?}", kind), - }; - sess.dcx().emit_err(LimitInvalid { - span: attr.span(), - value_span: attr.value_span().unwrap(), - error_str, - }); - } - } - } - } - default +pub(crate) fn get_recursion_limit(attrs: &[Attribute]) -> Limit { + find_attr!(attrs, AttributeKind::RecursionLimit { limit, .. } => *limit) + .unwrap_or(Limit::new(128)) } diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index ca8c10311fb8..6d9751d7d4d6 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -6,7 +6,7 @@ use std::sync::{Arc, LazyLock, OnceLock}; use std::{env, fs, iter}; use rustc_ast as ast; -use rustc_attr_parsing::{AttributeParser, ShouldEmit, validate_attr}; +use rustc_attr_parsing::{AttributeParser, ShouldEmit}; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_data_structures::jobserver::Proxy; use rustc_data_structures::steal::Steal; @@ -19,6 +19,7 @@ use rustc_fs_util::try_canonicalize; use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::{LOCAL_CRATE, StableCrateId, StableCrateIdMap}; use rustc_hir::definitions::Definitions; +use rustc_hir::limit::Limit; use rustc_incremental::setup_dep_graph; use rustc_lint::{BufferedEarlyLint, EarlyCheckNode, LintStore, unerased_lint_store}; use rustc_metadata::EncodedMetadata; @@ -30,12 +31,12 @@ use rustc_middle::util::Providers; use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal}; use rustc_passes::{abi_test, input_stats, layout_test}; use rustc_resolve::{Resolver, ResolverOutputs}; +use rustc_session::Session; use rustc_session::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType}; use rustc_session::cstore::Untracked; use rustc_session::output::{collect_crate_types, filename_for_input}; use rustc_session::parse::feature_err; use rustc_session::search_paths::PathKind; -use rustc_session::{Limit, Session}; use rustc_span::{ DUMMY_SP, ErrorGuaranteed, ExpnKind, FileName, SourceFileHash, SourceFileHashAlgorithm, Span, Symbol, sym, @@ -1246,7 +1247,8 @@ pub fn get_crate_name(sess: &Session, krate_attrs: &[ast::Attribute]) -> Symbol // in all code paths that require the crate name very early on, namely before // macro expansion. - let attr_crate_name = parse_crate_name(sess, krate_attrs, ShouldEmit::EarlyFatal); + let attr_crate_name = + parse_crate_name(sess, krate_attrs, ShouldEmit::EarlyFatal { also_emit_lints: true }); let validate = |name, span| { rustc_session::output::validate_crate_name(sess, name, span); @@ -1307,36 +1309,18 @@ pub(crate) fn parse_crate_name( } fn get_recursion_limit(krate_attrs: &[ast::Attribute], sess: &Session) -> Limit { - // We don't permit macro calls inside of the attribute (e.g., #![recursion_limit = `expand!()`]) - // because that would require expanding this while in the middle of expansion, which needs to - // know the limit before expanding. - let _ = validate_and_find_value_str_builtin_attr(sym::recursion_limit, sess, krate_attrs); - crate::limits::get_recursion_limit(krate_attrs, sess) -} - -/// Validate *all* occurrences of the given "[value-str]" built-in attribute and return the first find. -/// -/// This validator is intended for built-in attributes whose value needs to be known very early -/// during compilation (namely, before macro expansion) and it mainly exists to reject macro calls -/// inside of the attributes, such as in `#![name = expand!()]`. Normal attribute validation happens -/// during semantic analysis via [`TyCtxt::check_mod_attrs`] which happens *after* macro expansion -/// when such macro calls (here: `expand`) have already been expanded and we can no longer check for -/// their presence. -/// -/// [value-str]: ast::Attribute::value_str -fn validate_and_find_value_str_builtin_attr( - name: Symbol, - sess: &Session, - krate_attrs: &[ast::Attribute], -) -> Option<(Symbol, Span)> { - let mut result = None; - // Validate *all* relevant attributes, not just the first occurrence. - for attr in ast::attr::filter_by_name(krate_attrs, name) { - let Some(value) = attr.value_str() else { - validate_attr::emit_fatal_malformed_builtin_attribute(&sess.psess, attr, name) - }; - // Choose the first occurrence as our result. - result.get_or_insert((value, attr.span)); - } - result + let attr = AttributeParser::parse_limited_should_emit( + sess, + &krate_attrs, + sym::recursion_limit, + DUMMY_SP, + rustc_ast::node_id::CRATE_NODE_ID, + None, + // errors are fatal here, but lints aren't. + // If things aren't fatal we continue, and will parse this again. + // That makes the same lint trigger again. + // So, no lints here to avoid duplicates. + ShouldEmit::EarlyFatal { also_emit_lints: false }, + ); + crate::limits::get_recursion_limit(attr.as_slice()) } diff --git a/compiler/rustc_middle/src/error.rs b/compiler/rustc_middle/src/error.rs index 7520bc262c6b..dad402ec6961 100644 --- a/compiler/rustc_middle/src/error.rs +++ b/compiler/rustc_middle/src/error.rs @@ -72,7 +72,7 @@ pub enum TypeMismatchReason { #[help] pub(crate) struct RecursionLimitReached<'tcx> { pub ty: Ty<'tcx>, - pub suggested_limit: rustc_session::Limit, + pub suggested_limit: rustc_hir::limit::Limit, } #[derive(Diagnostic)] diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index de6fa4c344e6..218ac2cfbc19 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -38,6 +38,7 @@ use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::definitions::{DefPathData, Definitions, DisambiguatorState}; use rustc_hir::intravisit::VisitorExt; use rustc_hir::lang_items::LangItem; +use rustc_hir::limit::Limit; use rustc_hir::{self as hir, Attribute, HirId, Node, TraitCandidate, find_attr}; use rustc_index::IndexVec; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; @@ -45,10 +46,10 @@ use rustc_query_system::cache::WithDepNode; use rustc_query_system::dep_graph::DepNodeIndex; use rustc_query_system::ich::StableHashingContext; use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; +use rustc_session::Session; use rustc_session::config::CrateType; use rustc_session::cstore::{CrateStoreDyn, Untracked}; use rustc_session::lint::Lint; -use rustc_session::{Limit, Session}; use rustc_span::def_id::{CRATE_DEF_ID, DefPathHash, StableCrateId}; use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; use rustc_type_ir::TyKind::*; diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 3f854038651a..66542525d284 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -7,6 +7,7 @@ use std::path::PathBuf; use rustc_errors::pluralize; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind}; +use rustc_hir::limit::Limit; use rustc_macros::extension; pub use rustc_type_ir::error::ExpectedFound; @@ -233,7 +234,7 @@ impl<'tcx> TyCtxt<'tcx> { loop { // Look for the longest properly trimmed path that still fits in length_limit. short = with_forced_trimmed_paths!({ - let mut p = FmtPrinter::new_with_limit(self, ns, rustc_session::Limit(type_limit)); + let mut p = FmtPrinter::new_with_limit(self, ns, Limit(type_limit)); self.lift(t) .expect("could not lift for printing") .print(&mut p) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 74caee7336a5..8101cabccdd0 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -13,8 +13,8 @@ use rustc_hir::LangItem; use rustc_hir::def::{self, CtorKind, DefKind, Namespace}; use rustc_hir::def_id::{DefIdMap, DefIdSet, LOCAL_CRATE, ModDefId}; use rustc_hir::definitions::{DefKey, DefPathDataName}; +use rustc_hir::limit::Limit; use rustc_macros::{Lift, extension}; -use rustc_session::Limit; use rustc_session::cstore::{ExternCrate, ExternCrateSource}; use rustc_span::{FileNameDisplayPreference, Ident, Symbol, kw, sym}; use rustc_type_ir::{Upcast as _, elaborate}; diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index a7d07adf78f0..b8c99e83a8f9 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -12,9 +12,9 @@ use rustc_hashes::Hash128; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId}; +use rustc_hir::limit::Limit; use rustc_index::bit_set::GrowableBitSet; use rustc_macros::{HashStable, TyDecodable, TyEncodable, extension}; -use rustc_session::Limit; use rustc_span::sym; use rustc_type_ir::solve::SizedTraitKind; use smallvec::{SmallVec, smallvec}; diff --git a/compiler/rustc_mir_transform/src/inline/cycle.rs b/compiler/rustc_mir_transform/src/inline/cycle.rs index 7f9234d1dc89..25a9baffe582 100644 --- a/compiler/rustc_mir_transform/src/inline/cycle.rs +++ b/compiler/rustc_mir_transform/src/inline/cycle.rs @@ -2,9 +2,9 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::unord::UnordSet; use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::limit::Limit; use rustc_middle::mir::TerminatorKind; use rustc_middle::ty::{self, GenericArgsRef, InstanceKind, TyCtxt, TypeVisitableExt}; -use rustc_session::Limit; use rustc_span::sym; use tracing::{instrument, trace}; diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index cffeb6f98075..3169f2d4e1a4 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -217,6 +217,7 @@ use rustc_hir::attrs::InlineAttr; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId}; use rustc_hir::lang_items::LangItem; +use rustc_hir::limit::Limit; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::interpret::{AllocId, ErrorHandled, GlobalAlloc, Scalar}; use rustc_middle::mir::mono::{CollectionMode, InstantiationMode, MonoItem}; @@ -231,7 +232,6 @@ use rustc_middle::ty::{ }; use rustc_middle::util::Providers; use rustc_middle::{bug, span_bug}; -use rustc_session::Limit; use rustc_session::config::{DebugInfo, EntryFnType}; use rustc_span::source_map::{Spanned, dummy_spanned, respan}; use rustc_span::{DUMMY_SP, Span}; diff --git a/compiler/rustc_monomorphize/src/mono_checks/move_check.rs b/compiler/rustc_monomorphize/src/mono_checks/move_check.rs index 7251ef478c6f..0adf1b089b53 100644 --- a/compiler/rustc_monomorphize/src/mono_checks/move_check.rs +++ b/compiler/rustc_monomorphize/src/mono_checks/move_check.rs @@ -1,10 +1,10 @@ use rustc_abi::Size; use rustc_data_structures::fx::FxIndexSet; use rustc_hir::def_id::DefId; +use rustc_hir::limit::Limit; use rustc_middle::mir::visit::Visitor as MirVisitor; use rustc_middle::mir::{self, Location, traversal}; use rustc_middle::ty::{self, AssocTag, Instance, Ty, TyCtxt, TypeFoldable}; -use rustc_session::Limit; use rustc_session::lint::builtin::LARGE_ASSIGNMENTS; use rustc_span::source_map::Spanned; use rustc_span::{Ident, Span, sym}; diff --git a/compiler/rustc_query_impl/Cargo.toml b/compiler/rustc_query_impl/Cargo.toml index e5cceacf15da..257785b10c8b 100644 --- a/compiler/rustc_query_impl/Cargo.toml +++ b/compiler/rustc_query_impl/Cargo.toml @@ -13,7 +13,6 @@ rustc_index = { path = "../rustc_index" } rustc_middle = { path = "../rustc_middle" } rustc_query_system = { path = "../rustc_query_system" } rustc_serialize = { path = "../rustc_serialize" } -rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 55549cba737e..4e601a6c5944 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -9,6 +9,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::{DynSend, DynSync}; use rustc_data_structures::unord::UnordMap; use rustc_hashes::Hash64; +use rustc_hir::limit::Limit; use rustc_index::Idx; use rustc_middle::bug; use rustc_middle::dep_graph::{ @@ -31,7 +32,6 @@ use rustc_query_system::query::{ }; use rustc_query_system::{QueryOverflow, QueryOverflowNote}; use rustc_serialize::{Decodable, Encodable}; -use rustc_session::Limit; use rustc_span::def_id::LOCAL_CRATE; use crate::QueryConfigRestored; diff --git a/compiler/rustc_query_system/src/error.rs b/compiler/rustc_query_system/src/error.rs index 5108ecaeea3a..2778b24e7741 100644 --- a/compiler/rustc_query_system/src/error.rs +++ b/compiler/rustc_query_system/src/error.rs @@ -1,6 +1,6 @@ use rustc_errors::codes::*; +use rustc_hir::limit::Limit; use rustc_macros::{Diagnostic, Subdiagnostic}; -use rustc_session::Limit; use rustc_span::{Span, Symbol}; #[derive(Subdiagnostic)] diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 96767d054398..3525c7c1d1a9 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1,10 +1,9 @@ use std::any::Any; -use std::ops::{Div, Mul}; use std::path::{Path, PathBuf}; use std::str::FromStr; use std::sync::Arc; use std::sync::atomic::AtomicBool; -use std::{env, fmt, io}; +use std::{env, io}; use rand::{RngCore, rng}; use rustc_ast::NodeId; @@ -25,6 +24,7 @@ use rustc_errors::{ Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, Diagnostic, ErrorGuaranteed, FatalAbort, LintEmitter, TerminalUrl, fallback_fluent_bundle, }; +use rustc_hir::limit::Limit; use rustc_macros::HashStable_Generic; pub use rustc_span::def_id::StableCrateId; use rustc_span::edition::Edition; @@ -62,64 +62,6 @@ pub enum CtfeBacktrace { Immediate, } -/// New-type wrapper around `usize` for representing limits. Ensures that comparisons against -/// limits are consistent throughout the compiler. -#[derive(Clone, Copy, Debug, HashStable_Generic)] -pub struct Limit(pub usize); - -impl Limit { - /// Create a new limit from a `usize`. - pub fn new(value: usize) -> Self { - Limit(value) - } - - /// Create a new unlimited limit. - pub fn unlimited() -> Self { - Limit(usize::MAX) - } - - /// Check that `value` is within the limit. Ensures that the same comparisons are used - /// throughout the compiler, as mismatches can cause ICEs, see #72540. - #[inline] - pub fn value_within_limit(&self, value: usize) -> bool { - value <= self.0 - } -} - -impl From for Limit { - fn from(value: usize) -> Self { - Self::new(value) - } -} - -impl fmt::Display for Limit { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -impl Div for Limit { - type Output = Limit; - - fn div(self, rhs: usize) -> Self::Output { - Limit::new(self.0 / rhs) - } -} - -impl Mul for Limit { - type Output = Limit; - - fn mul(self, rhs: usize) -> Self::Output { - Limit::new(self.0 * rhs) - } -} - -impl rustc_errors::IntoDiagArg for Limit { - fn into_diag_arg(self, _: &mut Option) -> rustc_errors::DiagArgValue { - self.to_string().into_diag_arg(&mut None) - } -} - #[derive(Clone, Copy, Debug, HashStable_Generic)] pub struct Limits { /// The maximum recursion limit for potentially infinitely recursive diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs index 4f1f5c330e5f..a0876d8fe759 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs @@ -3,10 +3,10 @@ use std::fmt; use rustc_errors::{Diag, E0275, EmissionGuarantee, ErrorGuaranteed, struct_span_code_err}; use rustc_hir::def::Namespace; use rustc_hir::def_id::LOCAL_CRATE; +use rustc_hir::limit::Limit; use rustc_infer::traits::{Obligation, PredicateObligation}; use rustc_middle::ty::print::{FmtPrinter, Print}; use rustc_middle::ty::{self, TyCtxt, Upcast}; -use rustc_session::Limit; use rustc_span::Span; use tracing::debug; @@ -67,7 +67,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // We don't need to save the type to a file, we will be talking about this type already // in a separate note when we explain the obligation, so it will be available that way. let mut p: FmtPrinter<'_, '_> = - FmtPrinter::new_with_limit(tcx, Namespace::TypeNS, rustc_session::Limit(6)); + FmtPrinter::new_with_limit(tcx, Namespace::TypeNS, Limit(6)); value.print(&mut p).unwrap(); p.into_buffer() } else { diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs index 13d56889bd12..0ef435b1a0e2 100644 --- a/compiler/rustc_ty_utils/src/needs_drop.rs +++ b/compiler/rustc_ty_utils/src/needs_drop.rs @@ -2,11 +2,11 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::DefId; +use rustc_hir::limit::Limit; use rustc_middle::bug; use rustc_middle::query::Providers; use rustc_middle::ty::util::{AlwaysRequiresDrop, needs_drop_components}; use rustc_middle::ty::{self, EarlyBinder, GenericArgsRef, Ty, TyCtxt}; -use rustc_session::Limit; use rustc_span::sym; use tracing::{debug, instrument}; diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs index e5dae4c0069e..c2653dd82a9f 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs @@ -917,50 +917,50 @@ mod no_builtins { #[recursion_limit="0200"] //~^ WARN crate-level attribute should be an inner attribute -//~| HELP add a `!` mod recursion_limit { + //~^ NOTE This attribute does not have an `!`, which means it is applied to this module mod inner { #![recursion_limit="0200"] } -//~^ WARN crate-level attribute should be in the root module +//~^ WARN the `#![recursion_limit]` attribute can only be used at the crate root #[recursion_limit="0200"] fn f() { } //~^ WARN crate-level attribute should be an inner attribute - //~| HELP add a `!` + //~| NOTE This attribute does not have an `!`, which means it is applied to this function #[recursion_limit="0200"] struct S; //~^ WARN crate-level attribute should be an inner attribute - //~| HELP add a `!` + //~| NOTE This attribute does not have an `!`, which means it is applied to this struct #[recursion_limit="0200"] type T = S; //~^ WARN crate-level attribute should be an inner attribute - //~| HELP add a `!` + //~| NOTE This attribute does not have an `!`, which means it is applied to this type alias #[recursion_limit="0200"] impl S { } //~^ WARN crate-level attribute should be an inner attribute - //~| HELP add a `!` + //~| NOTE This attribute does not have an `!`, which means it is applied to this implementation block } #[type_length_limit="0100"] //~^ WARN crate-level attribute should be an inner attribute -//~| HELP add a `!` mod type_length_limit { + //~^ NOTE This attribute does not have an `!`, which means it is applied to this module mod inner { #![type_length_limit="0100"] } -//~^ WARN crate-level attribute should be in the root module +//~^ WARN the `#![type_length_limit]` attribute can only be used at the crate root #[type_length_limit="0100"] fn f() { } //~^ WARN crate-level attribute should be an inner attribute - //~| HELP add a `!` + //~| NOTE This attribute does not have an `!`, which means it is applied to this function #[type_length_limit="0100"] struct S; //~^ WARN crate-level attribute should be an inner attribute - //~| HELP add a `!` + //~| NOTE This attribute does not have an `!`, which means it is applied to this struct #[type_length_limit="0100"] type T = S; //~^ WARN crate-level attribute should be an inner attribute - //~| HELP add a `!` + //~| NOTE This attribute does not have an `!`, which means it is applied to this type alias #[type_length_limit="0100"] impl S { } //~^ WARN crate-level attribute should be an inner attribute - //~| HELP add a `!` + //~| NOTE This attribute does not have an `!`, which means it is applied to this implementation block } fn main() {} diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr index ef74a00e5a1c..4a3520972bf5 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr @@ -291,28 +291,6 @@ help: add a `!` LL | #![no_builtins] | + -warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:918:1 - | -LL | #[recursion_limit="0200"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: add a `!` - | -LL | #![recursion_limit="0200"] - | + - -warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:942:1 - | -LL | #[type_length_limit="0100"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: add a `!` - | -LL | #![type_length_limit="0100"] - | + - warning: attribute should be applied to an `extern` block with non-Rust ABI --> $DIR/issue-43106-gating-of-builtin-attrs.rs:72:1 | @@ -757,106 +735,6 @@ help: add a `!` LL | #![no_builtins] impl S { } | + -warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:922:17 - | -LL | mod inner { #![recursion_limit="0200"] } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:925:5 - | -LL | #[recursion_limit="0200"] fn f() { } - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: add a `!` - | -LL | #![recursion_limit="0200"] fn f() { } - | + - -warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:929:5 - | -LL | #[recursion_limit="0200"] struct S; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: add a `!` - | -LL | #![recursion_limit="0200"] struct S; - | + - -warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:933:5 - | -LL | #[recursion_limit="0200"] type T = S; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: add a `!` - | -LL | #![recursion_limit="0200"] type T = S; - | + - -warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:937:5 - | -LL | #[recursion_limit="0200"] impl S { } - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: add a `!` - | -LL | #![recursion_limit="0200"] impl S { } - | + - -warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:946:17 - | -LL | mod inner { #![type_length_limit="0100"] } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:949:5 - | -LL | #[type_length_limit="0100"] fn f() { } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: add a `!` - | -LL | #![type_length_limit="0100"] fn f() { } - | + - -warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:953:5 - | -LL | #[type_length_limit="0100"] struct S; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: add a `!` - | -LL | #![type_length_limit="0100"] struct S; - | + - -warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:957:5 - | -LL | #[type_length_limit="0100"] type T = S; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: add a `!` - | -LL | #![type_length_limit="0100"] type T = S; - | + - -warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:961:5 - | -LL | #[type_length_limit="0100"] impl S { } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: add a `!` - | -LL | #![type_length_limit="0100"] impl S { } - | + - warning: `#[macro_use]` attribute cannot be used on functions --> $DIR/issue-43106-gating-of-builtin-attrs.rs:191:5 | @@ -1476,6 +1354,146 @@ note: This attribute does not have an `!`, which means it is applied to this imp LL | #[crate_name = "0900"] impl S { } | ^^^^^^^^^^ +warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]` + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:918:1 + | +LL | #[recursion_limit="0200"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: This attribute does not have an `!`, which means it is applied to this module + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:920:1 + | +LL | / mod recursion_limit { +LL | | +LL | | mod inner { #![recursion_limit="0200"] } +... | +LL | | } + | |_^ + +warning: the `#![recursion_limit]` attribute can only be used at the crate root + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:922:17 + | +LL | mod inner { #![recursion_limit="0200"] } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]` + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:925:5 + | +LL | #[recursion_limit="0200"] fn f() { } + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: This attribute does not have an `!`, which means it is applied to this function + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:925:31 + | +LL | #[recursion_limit="0200"] fn f() { } + | ^^^^^^^^^^ + +warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]` + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:929:5 + | +LL | #[recursion_limit="0200"] struct S; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: This attribute does not have an `!`, which means it is applied to this struct + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:929:31 + | +LL | #[recursion_limit="0200"] struct S; + | ^^^^^^^^^ + +warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]` + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:933:5 + | +LL | #[recursion_limit="0200"] type T = S; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: This attribute does not have an `!`, which means it is applied to this type alias + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:933:31 + | +LL | #[recursion_limit="0200"] type T = S; + | ^^^^^^^^^^^ + +warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]` + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:937:5 + | +LL | #[recursion_limit="0200"] impl S { } + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: This attribute does not have an `!`, which means it is applied to this implementation block + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:937:31 + | +LL | #[recursion_limit="0200"] impl S { } + | ^^^^^^^^^^ + +warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![type_length_limit]` + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:942:1 + | +LL | #[type_length_limit="0100"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: This attribute does not have an `!`, which means it is applied to this module + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:944:1 + | +LL | / mod type_length_limit { +LL | | +LL | | mod inner { #![type_length_limit="0100"] } +... | +LL | | } + | |_^ + +warning: the `#![type_length_limit]` attribute can only be used at the crate root + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:946:17 + | +LL | mod inner { #![type_length_limit="0100"] } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![type_length_limit]` + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:949:5 + | +LL | #[type_length_limit="0100"] fn f() { } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: This attribute does not have an `!`, which means it is applied to this function + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:949:33 + | +LL | #[type_length_limit="0100"] fn f() { } + | ^^^^^^^^^^ + +warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![type_length_limit]` + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:953:5 + | +LL | #[type_length_limit="0100"] struct S; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: This attribute does not have an `!`, which means it is applied to this struct + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:953:33 + | +LL | #[type_length_limit="0100"] struct S; + | ^^^^^^^^^ + +warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![type_length_limit]` + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:957:5 + | +LL | #[type_length_limit="0100"] type T = S; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: This attribute does not have an `!`, which means it is applied to this type alias + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:957:33 + | +LL | #[type_length_limit="0100"] type T = S; + | ^^^^^^^^^^^ + +warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![type_length_limit]` + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:961:5 + | +LL | #[type_length_limit="0100"] impl S { } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: This attribute does not have an `!`, which means it is applied to this implementation block + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:961:33 + | +LL | #[type_length_limit="0100"] impl S { } + | ^^^^^^^^^^ + warning: `#[should_panic]` attribute cannot be used on crates --> $DIR/issue-43106-gating-of-builtin-attrs.rs:50:1 | diff --git a/tests/ui/lint/unused/unused-attr-duplicate.stderr b/tests/ui/lint/unused/unused-attr-duplicate.stderr index 203211d0d562..076a08ac6f24 100644 --- a/tests/ui/lint/unused/unused-attr-duplicate.stderr +++ b/tests/ui/lint/unused/unused-attr-duplicate.stderr @@ -28,32 +28,6 @@ note: attribute also specified here LL | #[no_link] | ^^^^^^^^^^ -error: unused attribute - --> $DIR/unused-attr-duplicate.rs:21:1 - | -LL | #![recursion_limit = "256"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute - | -note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:20:1 - | -LL | #![recursion_limit = "128"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - -error: unused attribute - --> $DIR/unused-attr-duplicate.rs:24:1 - | -LL | #![type_length_limit = "1"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute - | -note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:23:1 - | -LL | #![type_length_limit = "1048576"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - error: unused attribute --> $DIR/unused-attr-duplicate.rs:27:1 | @@ -304,6 +278,32 @@ LL | #![crate_name = "unused_attr_duplicate"] = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +error: unused attribute + --> $DIR/unused-attr-duplicate.rs:21:1 + | +LL | #![recursion_limit = "256"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/unused-attr-duplicate.rs:20:1 + | +LL | #![recursion_limit = "128"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: unused attribute + --> $DIR/unused-attr-duplicate.rs:24:1 + | +LL | #![type_length_limit = "1"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/unused-attr-duplicate.rs:23:1 + | +LL | #![type_length_limit = "1048576"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + error: unused attribute --> $DIR/unused-attr-duplicate.rs:29:1 | diff --git a/tests/ui/lint/unused/unused-attr-macro-rules.stderr b/tests/ui/lint/unused/unused-attr-macro-rules.stderr index af64be8f6e9b..0c6825026ed3 100644 --- a/tests/ui/lint/unused/unused-attr-macro-rules.stderr +++ b/tests/ui/lint/unused/unused-attr-macro-rules.stderr @@ -1,19 +1,3 @@ -error: crate-level attribute should be an inner attribute - --> $DIR/unused-attr-macro-rules.rs:11:1 - | -LL | #[recursion_limit="1"] - | ^^^^^^^^^^^^^^^^^^^^^^ - | -note: the lint level is defined here - --> $DIR/unused-attr-macro-rules.rs:1:9 - | -LL | #![deny(unused_attributes)] - | ^^^^^^^^^^^^^^^^^ -help: add a `!` - | -LL | #![recursion_limit="1"] - | + - error: `#[macro_use]` attribute cannot be used on macro defs --> $DIR/unused-attr-macro-rules.rs:7:1 | @@ -22,6 +6,11 @@ LL | #[macro_use] | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = help: `#[macro_use]` can be applied to modules, extern crates, and crates +note: the lint level is defined here + --> $DIR/unused-attr-macro-rules.rs:1:9 + | +LL | #![deny(unused_attributes)] + | ^^^^^^^^^^^^^^^^^ error: `#[path]` attribute cannot be used on macro defs --> $DIR/unused-attr-macro-rules.rs:9:1 @@ -32,5 +21,19 @@ LL | #[path="foo"] = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = help: `#[path]` can only be applied to modules +error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]` + --> $DIR/unused-attr-macro-rules.rs:11:1 + | +LL | #[recursion_limit="1"] + | ^^^^^^^^^^^^^^^^^^^^^^ + | +note: This attribute does not have an `!`, which means it is applied to this macro def + --> $DIR/unused-attr-macro-rules.rs:12:1 + | +LL | / macro_rules! foo { +LL | | () => {}; +LL | | } + | |_^ + error: aborting due to 3 previous errors diff --git a/tests/ui/recursion_limit/empty.rs b/tests/ui/recursion_limit/empty.rs index 5987fa2f881c..a901267d847c 100644 --- a/tests/ui/recursion_limit/empty.rs +++ b/tests/ui/recursion_limit/empty.rs @@ -1,9 +1,7 @@ // Test the parse error for an empty recursion_limit -#![recursion_limit = ""] //~ ERROR `limit` must be a non-negative integer - //~| NOTE `limit` must be a non-negative integer - //~| ERROR `limit` must be a non-negative integer - //~| NOTE `limit` must be a non-negative integer - //~| NOTE duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +#![recursion_limit = ""] +//~^ ERROR `limit` must be a non-negative integer +//~| NOTE `limit` must be a non-negative integer fn main() {} diff --git a/tests/ui/recursion_limit/empty.stderr b/tests/ui/recursion_limit/empty.stderr index 2f7306775070..9afe9e6db565 100644 --- a/tests/ui/recursion_limit/empty.stderr +++ b/tests/ui/recursion_limit/empty.stderr @@ -6,15 +6,5 @@ LL | #![recursion_limit = ""] | | | `limit` must be a non-negative integer -error: `limit` must be a non-negative integer - --> $DIR/empty.rs:3:1 - | -LL | #![recursion_limit = ""] - | ^^^^^^^^^^^^^^^^^^^^^--^ - | | - | `limit` must be a non-negative integer - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error diff --git a/tests/ui/recursion_limit/invalid_digit.rs b/tests/ui/recursion_limit/invalid_digit.rs index 79d8f3708bac..1d0e6a18227c 100644 --- a/tests/ui/recursion_limit/invalid_digit.rs +++ b/tests/ui/recursion_limit/invalid_digit.rs @@ -1,8 +1,6 @@ // Test the parse error for an invalid digit in recursion_limit -#![recursion_limit = "-100"] //~ ERROR `limit` must be a non-negative integer - //~| NOTE not a valid integer - //~| ERROR `limit` must be a non-negative integer - //~| NOTE not a valid integer - //~| NOTE duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +#![recursion_limit = "-100"] +//~^ ERROR `limit` must be a non-negative integer +//~| NOTE not a valid integer fn main() {} diff --git a/tests/ui/recursion_limit/invalid_digit.stderr b/tests/ui/recursion_limit/invalid_digit.stderr index 4fda3039032a..9c2d1422df28 100644 --- a/tests/ui/recursion_limit/invalid_digit.stderr +++ b/tests/ui/recursion_limit/invalid_digit.stderr @@ -6,15 +6,5 @@ LL | #![recursion_limit = "-100"] | | | not a valid integer -error: `limit` must be a non-negative integer - --> $DIR/invalid_digit.rs:3:1 - | -LL | #![recursion_limit = "-100"] - | ^^^^^^^^^^^^^^^^^^^^^------^ - | | - | not a valid integer - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error diff --git a/tests/ui/recursion_limit/invalid_digit_type.stderr b/tests/ui/recursion_limit/invalid_digit_type.stderr index a122262f1df4..489e8bd82c2d 100644 --- a/tests/ui/recursion_limit/invalid_digit_type.stderr +++ b/tests/ui/recursion_limit/invalid_digit_type.stderr @@ -1,10 +1,14 @@ -error: malformed `recursion_limit` attribute input +error[E0539]: malformed `recursion_limit` attribute input --> $DIR/invalid_digit_type.rs:1:1 | LL | #![recursion_limit = 123] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![recursion_limit = "N"]` + | ^^^^^^^^^^^^^^^^^^^^^---^ + | | | + | | expected a string literal here + | help: must be of the form: `#![recursion_limit = "N"]` | = note: for more information, visit error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0539`. diff --git a/tests/ui/recursion_limit/invalid_macro.rs b/tests/ui/recursion_limit/invalid_macro.rs index 7db67a8d1628..eae348523dbc 100644 --- a/tests/ui/recursion_limit/invalid_macro.rs +++ b/tests/ui/recursion_limit/invalid_macro.rs @@ -1,4 +1,4 @@ -#![recursion_limit = foo!()] //~ ERROR malformed `recursion_limit` attribute +#![recursion_limit = foo!()] //~ ERROR attribute value must be a literal macro_rules! foo { () => {"128"}; diff --git a/tests/ui/recursion_limit/invalid_macro.stderr b/tests/ui/recursion_limit/invalid_macro.stderr index b4dbc9fcb134..de1df3e9a358 100644 --- a/tests/ui/recursion_limit/invalid_macro.stderr +++ b/tests/ui/recursion_limit/invalid_macro.stderr @@ -1,10 +1,8 @@ -error: malformed `recursion_limit` attribute input - --> $DIR/invalid_macro.rs:1:1 +error: attribute value must be a literal + --> $DIR/invalid_macro.rs:1:22 | LL | #![recursion_limit = foo!()] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![recursion_limit = "N"]` - | - = note: for more information, visit + | ^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/recursion_limit/no-value.stderr b/tests/ui/recursion_limit/no-value.stderr index 4a0ad04f2715..eafc50bafb49 100644 --- a/tests/ui/recursion_limit/no-value.stderr +++ b/tests/ui/recursion_limit/no-value.stderr @@ -1,4 +1,4 @@ -error: malformed `recursion_limit` attribute input +error[E0539]: malformed `recursion_limit` attribute input --> $DIR/no-value.rs:3:1 | LL | #![recursion_limit] @@ -8,3 +8,4 @@ LL | #![recursion_limit] error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0539`. diff --git a/tests/ui/recursion_limit/overflow.rs b/tests/ui/recursion_limit/overflow.rs index 7cd1d572e097..24cb8e286a89 100644 --- a/tests/ui/recursion_limit/overflow.rs +++ b/tests/ui/recursion_limit/overflow.rs @@ -3,8 +3,5 @@ #![recursion_limit = "999999999999999999999999"] //~^ ERROR `limit` must be a non-negative integer //~| NOTE `limit` is too large -//~| ERROR `limit` must be a non-negative integer -//~| NOTE `limit` is too large -//~| NOTE duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` fn main() {} diff --git a/tests/ui/recursion_limit/overflow.stderr b/tests/ui/recursion_limit/overflow.stderr index 6057177deb2f..5a8548b7c7f1 100644 --- a/tests/ui/recursion_limit/overflow.stderr +++ b/tests/ui/recursion_limit/overflow.stderr @@ -6,15 +6,5 @@ LL | #![recursion_limit = "999999999999999999999999"] | | | `limit` is too large -error: `limit` must be a non-negative integer - --> $DIR/overflow.rs:3:1 - | -LL | #![recursion_limit = "999999999999999999999999"] - | ^^^^^^^^^^^^^^^^^^^^^--------------------------^ - | | - | `limit` is too large - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error From 8d0c07f1a1ccc46f58d2c6dbf0dbf32dc0b0d6b9 Mon Sep 17 00:00:00 2001 From: Jeremy Smart Date: Thu, 31 Jul 2025 23:27:02 -0400 Subject: [PATCH 293/682] change end to last --- compiler/rustc_ast_lowering/src/expr.rs | 29 +++- compiler/rustc_hir/src/hir.rs | 15 +- compiler/rustc_hir/src/lang_items.rs | 1 + compiler/rustc_index/src/idx.rs | 15 ++ compiler/rustc_span/src/symbol.rs | 2 + library/core/src/range.rs | 140 +++++++++++++++--- library/core/src/range/iter.rs | 2 +- library/core/src/range/legacy.rs | 4 +- library/core/src/slice/index.rs | 41 +++++ library/core/src/str/traits.rs | 8 +- src/tools/clippy/clippy_utils/src/sym.rs | 1 - .../std/const-generics-range.full.stderr | 2 +- .../std/const-generics-range.min.stderr | 2 +- .../std/const-generics-range.rs | 14 +- tests/ui/iterators/ranges.stderr | 6 +- tests/ui/new-range/disabled.rs | 4 +- tests/ui/new-range/enabled.rs | 4 +- tests/ui/range/issue-54505-no-literals.stderr | 4 +- tests/ui/range/issue-54505-no-std.stderr | 2 +- tests/ui/range/issue-54505.stderr | 2 +- 20 files changed, 245 insertions(+), 53 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 3674814b796c..bb6b25baf013 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1536,7 +1536,13 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::LangItem::Range } } - (None, Some(..), Closed) => hir::LangItem::RangeToInclusive, + (None, Some(..), Closed) => { + if self.tcx.features().new_range() { + hir::LangItem::RangeToInclusiveCopy + } else { + hir::LangItem::RangeToInclusive + } + } (Some(e1), Some(e2), Closed) => { if self.tcx.features().new_range() { hir::LangItem::RangeInclusiveCopy @@ -1560,13 +1566,26 @@ impl<'hir> LoweringContext<'_, 'hir> { }; let fields = self.arena.alloc_from_iter( - e1.iter().map(|e| (sym::start, e)).chain(e2.iter().map(|e| (sym::end, e))).map( - |(s, e)| { + e1.iter() + .map(|e| (sym::start, e)) + .chain(e2.iter().map(|e| { + ( + if matches!( + lang_item, + hir::LangItem::RangeInclusiveCopy | hir::LangItem::RangeToInclusiveCopy + ) { + sym::last + } else { + sym::end + }, + e, + ) + })) + .map(|(s, e)| { let expr = self.lower_expr(e); let ident = Ident::new(s, self.lower_span(e.span)); self.expr_field(ident, expr, e.span) - }, - ), + }), ); hir::ExprKind::Struct( diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index ae03121e5f70..75551fe4c196 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2614,6 +2614,18 @@ impl Expr<'_> { StructTailExpr::None, ), ) + | ( + ExprKind::Struct( + QPath::LangItem(LangItem::RangeToInclusiveCopy, _), + [val1], + StructTailExpr::None, + ), + ExprKind::Struct( + QPath::LangItem(LangItem::RangeToInclusiveCopy, _), + [val2], + StructTailExpr::None, + ), + ) | ( ExprKind::Struct( QPath::LangItem(LangItem::RangeFrom, _), @@ -2705,7 +2717,8 @@ pub fn is_range_literal(expr: &Expr<'_>) -> bool { | LangItem::RangeToInclusive | LangItem::RangeCopy | LangItem::RangeFromCopy - | LangItem::RangeInclusiveCopy, + | LangItem::RangeInclusiveCopy + | LangItem::RangeToInclusiveCopy, .. ) ), diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 0464665b41fc..67d2f15d4147 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -422,6 +422,7 @@ language_item_table! { RangeFromCopy, sym::RangeFromCopy, range_from_copy_struct, Target::Struct, GenericRequirement::None; RangeCopy, sym::RangeCopy, range_copy_struct, Target::Struct, GenericRequirement::None; RangeInclusiveCopy, sym::RangeInclusiveCopy, range_inclusive_copy_struct, Target::Struct, GenericRequirement::None; + RangeToInclusiveCopy, sym::RangeToInclusiveCopy, range_to_inclusive_copy_struct, Target::Struct, GenericRequirement::None; String, sym::String, string, Target::Struct, GenericRequirement::None; CStr, sym::CStr, c_str, Target::Struct, GenericRequirement::None; diff --git a/compiler/rustc_index/src/idx.rs b/compiler/rustc_index/src/idx.rs index 33f406e21137..9cd7134659c5 100644 --- a/compiler/rustc_index/src/idx.rs +++ b/compiler/rustc_index/src/idx.rs @@ -130,7 +130,22 @@ impl IntoSliceIdx for core::range::RangeFrom { impl IntoSliceIdx for core::range::RangeInclusive { type Output = core::range::RangeInclusive; #[inline] + #[cfg(bootstrap)] fn into_slice_idx(self) -> Self::Output { core::range::RangeInclusive { start: self.start.index(), end: self.end.index() } } + #[inline] + #[cfg(not(bootstrap))] + fn into_slice_idx(self) -> Self::Output { + core::range::RangeInclusive { start: self.start.index(), last: self.last.index() } + } +} + +#[cfg(all(feature = "nightly", not(bootstrap)))] +impl IntoSliceIdx for core::range::RangeToInclusive { + type Output = core::range::RangeToInclusive; + #[inline] + fn into_slice_idx(self) -> Self::Output { + core::range::RangeToInclusive { last: self.last.index() } + } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 77260d07c995..f6bb5be025f5 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -326,6 +326,7 @@ symbols! { RangeSub, RangeTo, RangeToInclusive, + RangeToInclusiveCopy, Rc, RcWeak, Ready, @@ -1280,6 +1281,7 @@ symbols! { lang, lang_items, large_assignments, + last, lateout, lazy_normalization_consts, lazy_type_alias, diff --git a/library/core/src/range.rs b/library/core/src/range.rs index 7158fa0fcf06..cca7f47d9b94 100644 --- a/library/core/src/range.rs +++ b/library/core/src/range.rs @@ -31,9 +31,7 @@ pub use iter::{IterRange, IterRangeFrom, IterRangeInclusive}; #[doc(inline)] pub use crate::iter::Step; #[doc(inline)] -pub use crate::ops::{ - Bound, IntoBounds, OneSidedRange, RangeBounds, RangeFull, RangeTo, RangeToInclusive, -}; +pub use crate::ops::{Bound, IntoBounds, OneSidedRange, RangeBounds, RangeFull, RangeTo}; /// A (half-open) range bounded inclusively below and exclusively above /// (`start..end` in a future edition). @@ -209,20 +207,20 @@ impl const From> for Range { } } -/// A range bounded inclusively below and above (`start..=end`). +/// A range bounded inclusively below and above (`start..=last`). /// -/// The `RangeInclusive` `start..=end` contains all values with `x >= start` -/// and `x <= end`. It is empty unless `start <= end`. +/// The `RangeInclusive` `start..=last` contains all values with `x >= start` +/// and `x <= last`. It is empty unless `start <= last`. /// /// # Examples /// -/// The `start..=end` syntax is a `RangeInclusive`: +/// The `start..=last` syntax is a `RangeInclusive`: /// /// ``` /// #![feature(new_range_api)] /// use core::range::RangeInclusive; /// -/// assert_eq!(RangeInclusive::from(3..=5), RangeInclusive { start: 3, end: 5 }); +/// assert_eq!(RangeInclusive::from(3..=5), RangeInclusive { start: 3, last: 5 }); /// assert_eq!(3 + 4 + 5, RangeInclusive::from(3..=5).into_iter().sum()); /// ``` #[lang = "RangeInclusiveCopy"] @@ -234,7 +232,7 @@ pub struct RangeInclusive { pub start: Idx, /// The upper bound of the range (inclusive). #[unstable(feature = "new_range_api", issue = "125687")] - pub end: Idx, + pub last: Idx, } #[unstable(feature = "new_range_api", issue = "125687")] @@ -242,7 +240,7 @@ impl fmt::Debug for RangeInclusive { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { self.start.fmt(fmt)?; write!(fmt, "..=")?; - self.end.fmt(fmt)?; + self.last.fmt(fmt)?; Ok(()) } } @@ -306,7 +304,7 @@ impl> RangeInclusive { #[unstable(feature = "new_range_api", issue = "125687")] #[inline] pub fn is_empty(&self) -> bool { - !(self.start <= self.end) + !(self.start <= self.last) } } @@ -335,10 +333,10 @@ impl RangeInclusive { impl RangeInclusive { /// Converts to an exclusive `Range` for `SliceIndex` implementations. - /// The caller is responsible for dealing with `end == usize::MAX`. + /// The caller is responsible for dealing with `last == usize::MAX`. #[inline] pub(crate) const fn into_slice_range(self) -> Range { - Range { start: self.start, end: self.end + 1 } + Range { start: self.start, end: self.last + 1 } } } @@ -348,7 +346,7 @@ impl RangeBounds for RangeInclusive { Included(&self.start) } fn end_bound(&self) -> Bound<&T> { - Included(&self.end) + Included(&self.last) } } @@ -364,7 +362,7 @@ impl RangeBounds for RangeInclusive<&T> { Included(self.start) } fn end_bound(&self) -> Bound<&T> { - Included(self.end) + Included(self.last) } } @@ -372,7 +370,7 @@ impl RangeBounds for RangeInclusive<&T> { #[unstable(feature = "new_range_api", issue = "125687")] impl IntoBounds for RangeInclusive { fn into_bounds(self) -> (Bound, Bound) { - (Included(self.start), Included(self.end)) + (Included(self.start), Included(self.last)) } } @@ -381,7 +379,7 @@ impl IntoBounds for RangeInclusive { impl const From> for legacy::RangeInclusive { #[inline] fn from(value: RangeInclusive) -> Self { - Self::new(value.start, value.end) + Self::new(value.start, value.last) } } #[unstable(feature = "new_range_api", issue = "125687")] @@ -393,8 +391,8 @@ impl From> for RangeInclusive { "attempted to convert from an exhausted `legacy::RangeInclusive` (unspecified behavior)" ); - let (start, end) = value.into_inner(); - RangeInclusive { start, end } + let (start, last) = value.into_inner(); + RangeInclusive { start, last } } } @@ -543,3 +541,107 @@ impl const From> for RangeFrom { Self { start: value.start } } } + +/// A range only bounded inclusively above (`..=last`). +/// +/// The `RangeToInclusive` `..=last` contains all values with `x <= last`. +/// It cannot serve as an [`Iterator`] because it doesn't have a starting point. +/// +/// # Examples +/// +/// The `..=last` syntax is a `RangeToInclusive`: +/// +/// ``` +/// #![feature(new_range_api)] +/// #![feature(new_range)] +/// assert_eq!((..=5), std::range::RangeToInclusive{ last: 5 }); +/// ``` +/// +/// It does not have an [`IntoIterator`] implementation, so you can't use it in a +/// `for` loop directly. This won't compile: +/// +/// ```compile_fail,E0277 +/// // error[E0277]: the trait bound `std::range::RangeToInclusive<{integer}>: +/// // std::iter::Iterator` is not satisfied +/// for i in ..=5 { +/// // ... +/// } +/// ``` +/// +/// When used as a [slicing index], `RangeToInclusive` produces a slice of all +/// array elements up to and including the index indicated by `last`. +/// +/// ``` +/// let arr = [0, 1, 2, 3, 4]; +/// assert_eq!(arr[ .. ], [0, 1, 2, 3, 4]); +/// assert_eq!(arr[ .. 3], [0, 1, 2 ]); +/// assert_eq!(arr[ ..=3], [0, 1, 2, 3 ]); // This is a `RangeToInclusive` +/// assert_eq!(arr[1.. ], [ 1, 2, 3, 4]); +/// assert_eq!(arr[1.. 3], [ 1, 2 ]); +/// assert_eq!(arr[1..=3], [ 1, 2, 3 ]); +/// ``` +/// +/// [slicing index]: crate::slice::SliceIndex +#[lang = "RangeToInclusiveCopy"] +#[doc(alias = "..=")] +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +#[unstable(feature = "new_range_api", issue = "125687")] +pub struct RangeToInclusive { + /// The upper bound of the range (inclusive) + #[unstable(feature = "new_range_api", issue = "125687")] + pub last: Idx, +} + +#[unstable(feature = "new_range_api", issue = "125687")] +impl fmt::Debug for RangeToInclusive { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "..=")?; + self.last.fmt(fmt)?; + Ok(()) + } +} + +impl> RangeToInclusive { + /// Returns `true` if `item` is contained in the range. + /// + /// # Examples + /// + /// ``` + /// assert!( (..=5).contains(&-1_000_000_000)); + /// assert!( (..=5).contains(&5)); + /// assert!(!(..=5).contains(&6)); + /// + /// assert!( (..=1.0).contains(&1.0)); + /// assert!(!(..=1.0).contains(&f32::NAN)); + /// assert!(!(..=f32::NAN).contains(&0.5)); + /// ``` + #[inline] + #[unstable(feature = "new_range_api", issue = "125687")] + pub fn contains(&self, item: &U) -> bool + where + Idx: PartialOrd, + U: ?Sized + PartialOrd, + { + >::contains(self, item) + } +} + +// RangeToInclusive cannot impl From> +// because underflow would be possible with (..0).into() + +#[unstable(feature = "new_range_api", issue = "125687")] +impl RangeBounds for RangeToInclusive { + fn start_bound(&self) -> Bound<&T> { + Unbounded + } + fn end_bound(&self) -> Bound<&T> { + Included(&self.last) + } +} + +#[unstable(feature = "range_into_bounds", issue = "136903")] +impl IntoBounds for RangeToInclusive { + fn into_bounds(self) -> (Bound, Bound) { + (Unbounded, Included(self.last)) + } +} diff --git a/library/core/src/range/iter.rs b/library/core/src/range/iter.rs index 1e261d8c1d93..24efd4a204a5 100644 --- a/library/core/src/range/iter.rs +++ b/library/core/src/range/iter.rs @@ -164,7 +164,7 @@ impl IterRangeInclusive { return None; } - Some(RangeInclusive { start: self.0.start, end: self.0.end }) + Some(RangeInclusive { start: self.0.start, last: self.0.end }) } } diff --git a/library/core/src/range/legacy.rs b/library/core/src/range/legacy.rs index 6723c4903f75..aa11331382dd 100644 --- a/library/core/src/range/legacy.rs +++ b/library/core/src/range/legacy.rs @@ -1,10 +1,10 @@ //! # Legacy range types //! //! The types within this module will be replaced by the types -//! [`Range`], [`RangeInclusive`], and [`RangeFrom`] in the parent +//! [`Range`], [`RangeInclusive`], [`RangeToInclusive`], and [`RangeFrom`] in the parent //! module, [`core::range`]. //! //! The types here are equivalent to those in [`core::ops`]. #[doc(inline)] -pub use crate::ops::{Range, RangeFrom, RangeInclusive}; +pub use crate::ops::{Range, RangeFrom, RangeInclusive, RangeToInclusive}; diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index 98091e9fe83f..82a1c66ffec7 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -129,6 +129,8 @@ mod private_slice_index { #[unstable(feature = "new_range_api", issue = "125687")] impl Sealed for range::RangeInclusive {} #[unstable(feature = "new_range_api", issue = "125687")] + impl Sealed for range::RangeToInclusive {} + #[unstable(feature = "new_range_api", issue = "125687")] impl Sealed for range::RangeFrom {} impl Sealed for ops::IndexRange {} @@ -788,6 +790,45 @@ unsafe impl const SliceIndex<[T]> for ops::RangeToInclusive { } } +/// The methods `index` and `index_mut` panic if the end of the range is out of bounds. +#[stable(feature = "inclusive_range", since = "1.26.0")] +#[rustc_const_unstable(feature = "const_index", issue = "143775")] +unsafe impl const SliceIndex<[T]> for range::RangeToInclusive { + type Output = [T]; + + #[inline] + fn get(self, slice: &[T]) -> Option<&[T]> { + (0..=self.last).get(slice) + } + + #[inline] + fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> { + (0..=self.last).get_mut(slice) + } + + #[inline] + unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] { + // SAFETY: the caller has to uphold the safety contract for `get_unchecked`. + unsafe { (0..=self.last).get_unchecked(slice) } + } + + #[inline] + unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] { + // SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`. + unsafe { (0..=self.last).get_unchecked_mut(slice) } + } + + #[inline] + fn index(self, slice: &[T]) -> &[T] { + (0..=self.last).index(slice) + } + + #[inline] + fn index_mut(self, slice: &mut [T]) -> &mut [T] { + (0..=self.last).index_mut(slice) + } +} + /// Performs bounds checking of a range. /// /// This method is similar to [`Index::index`] for slices, but it returns a diff --git a/library/core/src/str/traits.rs b/library/core/src/str/traits.rs index dc88f35eca7e..6a0cdc85dea9 100644 --- a/library/core/src/str/traits.rs +++ b/library/core/src/str/traits.rs @@ -677,11 +677,11 @@ unsafe impl const SliceIndex for range::RangeInclusive { type Output = str; #[inline] fn get(self, slice: &str) -> Option<&Self::Output> { - if self.end == usize::MAX { None } else { self.into_slice_range().get(slice) } + if self.last == usize::MAX { None } else { self.into_slice_range().get(slice) } } #[inline] fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> { - if self.end == usize::MAX { None } else { self.into_slice_range().get_mut(slice) } + if self.last == usize::MAX { None } else { self.into_slice_range().get_mut(slice) } } #[inline] unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output { @@ -695,14 +695,14 @@ unsafe impl const SliceIndex for range::RangeInclusive { } #[inline] fn index(self, slice: &str) -> &Self::Output { - if self.end == usize::MAX { + if self.last == usize::MAX { str_index_overflow_fail(); } self.into_slice_range().index(slice) } #[inline] fn index_mut(self, slice: &mut str) -> &mut Self::Output { - if self.end == usize::MAX { + if self.last == usize::MAX { str_index_overflow_fail(); } self.into_slice_range().index_mut(slice) diff --git a/src/tools/clippy/clippy_utils/src/sym.rs b/src/tools/clippy/clippy_utils/src/sym.rs index 278101ac27f9..8033b74a8d25 100644 --- a/src/tools/clippy/clippy_utils/src/sym.rs +++ b/src/tools/clippy/clippy_utils/src/sym.rs @@ -194,7 +194,6 @@ generate! { itertools, join, kw, - last, lazy_static, lint_vec, ln, diff --git a/tests/ui/const-generics/std/const-generics-range.full.stderr b/tests/ui/const-generics/std/const-generics-range.full.stderr index 2b5c63e6643d..ccede2af9e55 100644 --- a/tests/ui/const-generics/std/const-generics-range.full.stderr +++ b/tests/ui/const-generics/std/const-generics-range.full.stderr @@ -28,7 +28,7 @@ error[E0741]: `RangeTo` must implement `ConstParamTy` to be used as the t LL | struct _RangeTo>; | ^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0741]: `RangeToInclusive` must implement `ConstParamTy` to be used as the type of a const generic parameter +error[E0741]: `std::ops::RangeToInclusive` must implement `ConstParamTy` to be used as the type of a const generic parameter --> $DIR/const-generics-range.rs:34:35 | LL | struct _RangeToInclusive>; diff --git a/tests/ui/const-generics/std/const-generics-range.min.stderr b/tests/ui/const-generics/std/const-generics-range.min.stderr index 04e3fe744534..43a57c880d5d 100644 --- a/tests/ui/const-generics/std/const-generics-range.min.stderr +++ b/tests/ui/const-generics/std/const-generics-range.min.stderr @@ -58,7 +58,7 @@ help: add `#![feature(adt_const_params)]` to the crate attributes to enable more LL + #![feature(adt_const_params)] | -error: `RangeToInclusive` is forbidden as the type of a const generic parameter +error: `std::ops::RangeToInclusive` is forbidden as the type of a const generic parameter --> $DIR/const-generics-range.rs:34:35 | LL | struct _RangeToInclusive>; diff --git a/tests/ui/const-generics/std/const-generics-range.rs b/tests/ui/const-generics/std/const-generics-range.rs index 3a238ed177e0..391366dadba0 100644 --- a/tests/ui/const-generics/std/const-generics-range.rs +++ b/tests/ui/const-generics/std/const-generics-range.rs @@ -7,32 +7,32 @@ // `Range` should be usable within const generics: struct _Range>; //[min]~^ ERROR `std::ops::Range` is forbidden -const RANGE : _Range<{ 0 .. 1000 }> = _Range; +const RANGE: _Range<{ 0..1000 }> = _Range; // `RangeFrom` should be usable within const generics: struct _RangeFrom>; //[min]~^ ERROR `std::ops::RangeFrom` is forbidden -const RANGE_FROM : _RangeFrom<{ 0 .. }> = _RangeFrom; +const RANGE_FROM: _RangeFrom<{ 0.. }> = _RangeFrom; // `RangeFull` should be usable within const generics: struct _RangeFull; //[min]~^ ERROR `RangeFull` is forbidden -const RANGE_FULL : _RangeFull<{ .. }> = _RangeFull; +const RANGE_FULL: _RangeFull<{ .. }> = _RangeFull; // Regression test for #70155 // `RangeInclusive` should be usable within const generics: struct _RangeInclusive>; //[min]~^ ERROR `std::ops::RangeInclusive` is forbidden -const RANGE_INCLUSIVE : _RangeInclusive<{ 0 ..= 999 }> = _RangeInclusive; +const RANGE_INCLUSIVE: _RangeInclusive<{ 0..=999 }> = _RangeInclusive; // `RangeTo` should be usable within const generics: struct _RangeTo>; //[min]~^ ERROR `RangeTo` is forbidden -const RANGE_TO : _RangeTo<{ .. 1000 }> = _RangeTo; +const RANGE_TO: _RangeTo<{ ..1000 }> = _RangeTo; // `RangeToInclusive` should be usable within const generics: struct _RangeToInclusive>; -//[min]~^ ERROR `RangeToInclusive` is forbidden -const RANGE_TO_INCLUSIVE : _RangeToInclusive<{ ..= 999 }> = _RangeToInclusive; +//[min]~^ ERROR `std::ops::RangeToInclusive` is forbidden +const RANGE_TO_INCLUSIVE: _RangeToInclusive<{ ..=999 }> = _RangeToInclusive; pub fn main() {} diff --git a/tests/ui/iterators/ranges.stderr b/tests/ui/iterators/ranges.stderr index b9fbcd5304b1..f85ce644073e 100644 --- a/tests/ui/iterators/ranges.stderr +++ b/tests/ui/iterators/ranges.stderr @@ -8,15 +8,15 @@ LL | for _ in ..10 {} = note: `..end` is a `RangeTo`, which cannot be iterated on; you might have meant to have a bounded `Range`: `0..end` = note: required for `RangeTo<{integer}>` to implement `IntoIterator` -error[E0277]: `RangeToInclusive<{integer}>` is not an iterator +error[E0277]: `std::ops::RangeToInclusive<{integer}>` is not an iterator --> $DIR/ranges.rs:4:14 | LL | for _ in ..=10 {} | ^^^^^ if you meant to iterate until a value (including it), add a starting value | - = help: the trait `Iterator` is not implemented for `RangeToInclusive<{integer}>` + = help: the trait `Iterator` is not implemented for `std::ops::RangeToInclusive<{integer}>` = note: `..=end` is a `RangeToInclusive`, which cannot be iterated on; you might have meant to have a bounded `RangeInclusive`: `0..=end` - = note: required for `RangeToInclusive<{integer}>` to implement `IntoIterator` + = note: required for `std::ops::RangeToInclusive<{integer}>` to implement `IntoIterator` error: aborting due to 2 previous errors diff --git a/tests/ui/new-range/disabled.rs b/tests/ui/new-range/disabled.rs index 1a5fe3f97436..6ba29f5ca9a2 100644 --- a/tests/ui/new-range/disabled.rs +++ b/tests/ui/new-range/disabled.rs @@ -6,20 +6,20 @@ fn main() { // Unchanged let a: core::range::RangeFull = ..; let b: core::range::RangeTo = ..2; - let c: core::range::RangeToInclusive = ..=3; let _: core::ops::RangeFull = a; let _: core::ops::RangeTo = b; - let _: core::ops::RangeToInclusive = c; // Changed let a: core::range::legacy::RangeFrom = 1..; let b: core::range::legacy::Range = 2..3; let c: core::range::legacy::RangeInclusive = 4..=5; + let d: core::range::legacy::RangeToInclusive = ..=3; let a: core::ops::RangeFrom = a; let b: core::ops::Range = b; let c: core::ops::RangeInclusive = c; + let d: core::ops::RangeToInclusive = d; let _: core::ops::RangeFrom = a.into_iter(); let _: core::ops::Range = b.into_iter(); diff --git a/tests/ui/new-range/enabled.rs b/tests/ui/new-range/enabled.rs index a5fb76ad52b7..5ddbba492e76 100644 --- a/tests/ui/new-range/enabled.rs +++ b/tests/ui/new-range/enabled.rs @@ -7,18 +7,18 @@ fn main() { // Unchanged let a: core::range::RangeFull = ..; let b: core::range::RangeTo = ..2; - let c: core::range::RangeToInclusive = ..=3; let _: core::ops::RangeFull = a; let _: core::ops::RangeTo = b; - let _: core::ops::RangeToInclusive = c; // Changed let a: core::range::RangeFrom = 1..; let b: core::range::Range = 2..3; let c: core::range::RangeInclusive = 4..=5; + let d: core::range::RangeToInclusive = ..=3; let _: core::range::IterRangeFrom = a.into_iter(); let _: core::range::IterRange = b.into_iter(); let _: core::range::IterRangeInclusive = c.into_iter(); + // RangeToInclusive has no Iterator implementation } diff --git a/tests/ui/range/issue-54505-no-literals.stderr b/tests/ui/range/issue-54505-no-literals.stderr index c6d4384bcd33..62e2fe4a8389 100644 --- a/tests/ui/range/issue-54505-no-literals.stderr +++ b/tests/ui/range/issue-54505-no-literals.stderr @@ -207,7 +207,7 @@ LL | take_range(std::ops::RangeToInclusive { end: 5 }); | arguments to this function are incorrect | = note: expected reference `&_` - found struct `RangeToInclusive<{integer}>` + found struct `std::ops::RangeToInclusive<{integer}>` note: function defined here --> $DIR/issue-54505-no-literals.rs:12:4 | @@ -227,7 +227,7 @@ LL | take_range(::std::ops::RangeToInclusive { end: 5 }); | arguments to this function are incorrect | = note: expected reference `&_` - found struct `RangeToInclusive<{integer}>` + found struct `std::ops::RangeToInclusive<{integer}>` note: function defined here --> $DIR/issue-54505-no-literals.rs:12:4 | diff --git a/tests/ui/range/issue-54505-no-std.stderr b/tests/ui/range/issue-54505-no-std.stderr index 2aa1d584046d..866a82afb7e2 100644 --- a/tests/ui/range/issue-54505-no-std.stderr +++ b/tests/ui/range/issue-54505-no-std.stderr @@ -112,7 +112,7 @@ LL | take_range(..=42); | arguments to this function are incorrect | = note: expected reference `&_` - found struct `RangeToInclusive<{integer}>` + found struct `core::ops::RangeToInclusive<{integer}>` note: function defined here --> $DIR/issue-54505-no-std.rs:25:4 | diff --git a/tests/ui/range/issue-54505.stderr b/tests/ui/range/issue-54505.stderr index 8b669b2910f6..4d94c6c2d094 100644 --- a/tests/ui/range/issue-54505.stderr +++ b/tests/ui/range/issue-54505.stderr @@ -112,7 +112,7 @@ LL | take_range(..=42); | arguments to this function are incorrect | = note: expected reference `&_` - found struct `RangeToInclusive<{integer}>` + found struct `std::ops::RangeToInclusive<{integer}>` note: function defined here --> $DIR/issue-54505.rs:10:4 | From 20226db7ff086e7ddcb9a46bcd9764e9d36bc301 Mon Sep 17 00:00:00 2001 From: Jeremy Smart Date: Thu, 31 Jul 2025 23:27:02 -0400 Subject: [PATCH 294/682] change end to last --- clippy_utils/src/sym.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index 278101ac27f9..8033b74a8d25 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -194,7 +194,6 @@ generate! { itertools, join, kw, - last, lazy_static, lint_vec, ln, From c63e03490692c2e90e927bae35f51a1f59252868 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 9 Sep 2025 08:31:52 +0200 Subject: [PATCH 295/682] triagebot: warn about #[rustc_intrinsic_const_stable_indirect]; make warnings a bit more noticeable --- triagebot.toml | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/triagebot.toml b/triagebot.toml index bf9bfee4306d..2d58c616bc27 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1308,21 +1308,32 @@ cc = ["@m-ou-se"] [mentions."compiler/rustc_ast_lowering/src/format.rs"] cc = ["@m-ou-se"] +# Content-based mentions + [mentions."#[miri::intrinsic_fallback_is_spec]"] type = "content" message = """ -`#[miri::intrinsic_fallback_is_spec]` must only be used if the function actively checks for all UB cases, +⚠️ `#[miri::intrinsic_fallback_is_spec]` must only be used if the function actively checks for all UB cases, and explores the possible non-determinism of the intrinsic. """ cc = ["@rust-lang/miri"] + [mentions."#[rustc_allow_const_fn_unstable"] type = "content" message = """ -`#[rustc_allow_const_fn_unstable]` needs careful audit to avoid accidentally exposing unstable +⚠️ `#[rustc_allow_const_fn_unstable]` needs careful audit to avoid accidentally exposing unstable implementation details on stable. """ cc = ["@rust-lang/wg-const-eval"] +[mentions."#[rustc_intrinsic_const_stable_indirect]"] +type = "content" +message = """ +⚠️ `#[rustc_intrinsic_const_stable_indirect]` controls whether intrinsics can be exposed to stable const +code; adding it needs t-lang approval. +""" +cc = ["@rust-lang/wg-const-eval"] + # ------------------------------------------------------------------------------ # PR assignments From e8206d0b88cf48d692c059a913d7124375448934 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 9 Sep 2025 08:37:02 +0200 Subject: [PATCH 296/682] Update documentation about how to build the RA book --- src/tools/rust-analyzer/docs/book/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/rust-analyzer/docs/book/README.md b/src/tools/rust-analyzer/docs/book/README.md index 0a3161f3af38..11f7e8f98ca5 100644 --- a/src/tools/rust-analyzer/docs/book/README.md +++ b/src/tools/rust-analyzer/docs/book/README.md @@ -8,6 +8,7 @@ To run the documentation site locally: ```shell cargo install mdbook +cargo install mdbook-toc cargo xtask codegen cd docs/book mdbook serve From a1a1ed25f2749de58c24a618460cb3d4279b1d5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 9 Sep 2025 09:12:09 +0200 Subject: [PATCH 297/682] Add a FAQ entry about RA and Cargo interaction --- src/tools/rust-analyzer/docs/book/src/faq.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/tools/rust-analyzer/docs/book/src/faq.md b/src/tools/rust-analyzer/docs/book/src/faq.md index c87203309011..8c143ab94935 100644 --- a/src/tools/rust-analyzer/docs/book/src/faq.md +++ b/src/tools/rust-analyzer/docs/book/src/faq.md @@ -5,3 +5,12 @@ rust-analyzer fails to resolve `None`, and thinks you are binding to a variable named `None`. That's usually a sign of a corrupted sysroot. Try removing and re-installing it: `rustup component remove rust-src` then `rustup component install rust-src`. + +### Rust Analyzer and Cargo compete over the build lock + +Rust Analyzer invokes Cargo in the background, and it can thus block manually executed +`cargo` commands from making progress (or vice-versa). In some cases, this can also cause +unnecessary recompilations caused by cache thrashing. To avoid this, you can configure +Rust Analyzer to use a [different target directory](./configuration.md#cargo.targetDir). +This will allow both the IDE and Cargo to make progress independently, at the cost of +increased disk space usage caused by the duplicated artifact directories. From 2079d1126bcfb5ed125009f3ccfc67ebd7995d56 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 7 Sep 2025 15:05:11 +0800 Subject: [PATCH 298/682] Fix LifetimeParam::lifetime_bounds invalid implement Lifetime node example: ``` LIFETIME_PARAM@15..21 LIFETIME@15..17 LIFETIME_IDENT@15..17 "'a" COLON@17..18 ":" WHITESPACE@18..19 " " TYPE_BOUND_LIST@19..21 TYPE_BOUND@19..21 LIFETIME@19..21 LIFETIME_IDENT@19..21 "'b" ``` --- .../rust-analyzer/crates/syntax/src/ast/node_ext.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs index 42b0f5cf2da1..af741d100f68 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs @@ -12,8 +12,8 @@ use rowan::{GreenNodeData, GreenTokenData}; use crate::{ NodeOrToken, SmolStr, SyntaxElement, SyntaxToken, T, TokenText, ast::{ - self, AstNode, AstToken, HasAttrs, HasGenericArgs, HasGenericParams, HasName, SyntaxNode, - support, + self, AstNode, AstToken, HasAttrs, HasGenericArgs, HasGenericParams, HasName, + HasTypeBounds, SyntaxNode, support, }, ted, }; @@ -912,11 +912,10 @@ impl ast::Visibility { impl ast::LifetimeParam { pub fn lifetime_bounds(&self) -> impl Iterator { - self.syntax() - .children_with_tokens() - .filter_map(|it| it.into_token()) - .skip_while(|x| x.kind() != T![:]) - .filter(|it| it.kind() == T![lifetime_ident]) + self.type_bound_list() + .into_iter() + .flat_map(|it| it.bounds()) + .filter_map(|it| it.lifetime()?.lifetime_ident_token()) } } From e3dfcb26cd816046451a4dc85a85c5294245ded7 Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Tue, 9 Sep 2025 09:33:49 +0200 Subject: [PATCH 299/682] CI: rfl: move job forward to Linux v6.17-rc5 to remove temporary commits v6.17-rc5 contains the equivalent of the two commits we had here, thus move the Rust for Linux job forward to that so that we don't need the temporary commits anymore. Signed-off-by: Miguel Ojeda --- src/ci/docker/scripts/rfl-build.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/ci/docker/scripts/rfl-build.sh b/src/ci/docker/scripts/rfl-build.sh index 7e0fb5794f47..b7d7151b171d 100755 --- a/src/ci/docker/scripts/rfl-build.sh +++ b/src/ci/docker/scripts/rfl-build.sh @@ -2,9 +2,7 @@ set -euo pipefail -# https://github.com/rust-lang/rust/pull/144443 -# https://github.com/rust-lang/rust/pull/145928 -LINUX_VERSION=8851e27d2cb947ea8bbbe8e812068f7bf5cbd00b +LINUX_VERSION=v6.17-rc5 # Build rustc, rustdoc, cargo, clippy-driver and rustfmt ../x.py build --stage 2 library rustdoc clippy rustfmt From a69ac553992de87fd5192621a09a21a58887b164 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 9 Sep 2025 10:38:20 +0300 Subject: [PATCH 300/682] Resolve paths to snapshot test libraries absolutely That is, resolve them globally, not from the test's location. This *should* result in more robust resolution; for example, previously the code failed to detect the presence of snapshot testing if the snapshot library was renamed in the dependency or was an indirect dependency. --- .../crates/ide/src/hover/tests.rs | 71 +++++++++++++++++++ .../rust-analyzer/crates/ide/src/runnables.rs | 37 ++++------ 2 files changed, 85 insertions(+), 23 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 6f1bbad6ba16..58d8a7edbe04 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -10569,6 +10569,77 @@ macro_rules! str { ); } +#[test] +fn test_runnables_with_snapshot_tests_indirect_dep() { + check_actions( + r#" +//- /lib.rs crate:foo deps:utils +use utils::expect_test::expect; + +#[test] +fn test$0() { + let actual = "new25"; + expect!["new25"].assert_eq(&actual); +} + +//- /expect-test/lib.rs crate:expect_test +struct Expect; + +impl Expect { + fn assert_eq(&self, actual: &str) {} +} + +#[macro_export] +macro_rules! expect { + ($e:expr) => Expect; // dummy +} + +//- /utils/lib.rs crate:utils deps:expect_test +pub use expect_test; + "#, + expect![[r#" + [ + Reference( + FilePositionWrapper { + file_id: FileId( + 0, + ), + offset: 44, + }, + ), + Runnable( + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 33..121, + focus_range: 44..48, + name: "test", + kind: Function, + }, + kind: Test { + test_id: Path( + "test", + ), + attr: TestAttr { + ignore: false, + }, + }, + cfg: None, + update_test: UpdateTest { + expect_test: true, + insta: false, + snapbox: false, + }, + }, + ), + ] + "#]], + ); +} + #[test] fn drop_glue() { check( diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs index 6ad9c7884db9..ec13ba0fde34 100644 --- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs +++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs @@ -4,8 +4,8 @@ use arrayvec::ArrayVec; use ast::HasName; use cfg::{CfgAtom, CfgExpr}; use hir::{ - AsAssocItem, AttrsWithOwner, HasAttrs, HasCrate, HasSource, ModPath, Name, PathKind, Semantics, - Symbol, db::HirDatabase, sym, + AsAssocItem, AttrsWithOwner, HasAttrs, HasCrate, HasSource, Semantics, Symbol, db::HirDatabase, + sym, }; use ide_assists::utils::{has_test_related_attribute, test_related_attribute_syn}; use ide_db::{ @@ -352,8 +352,7 @@ pub(crate) fn runnable_fn( .call_site(); let file_range = fn_source.syntax().original_file_range_with_macro_call_input(sema.db); - let update_test = - UpdateTest::find_snapshot_macro(sema, &fn_source.file_syntax(sema.db), file_range); + let update_test = UpdateTest::find_snapshot_macro(sema, file_range); let cfg = def.attrs(sema.db).cfg(); Some(Runnable { use_name_in_title: false, nav, kind, cfg, update_test }) @@ -388,7 +387,7 @@ pub(crate) fn runnable_mod( file_id: module_source.file_id.original_file(sema.db), range: module_syntax.text_range(), }; - let update_test = UpdateTest::find_snapshot_macro(sema, &module_syntax, file_range); + let update_test = UpdateTest::find_snapshot_macro(sema, file_range); Some(Runnable { use_name_in_title: false, @@ -428,8 +427,7 @@ pub(crate) fn runnable_impl( let impl_source = sema.source(*def)?; let impl_syntax = impl_source.syntax(); let file_range = impl_syntax.original_file_range_with_macro_call_input(sema.db); - let update_test = - UpdateTest::find_snapshot_macro(sema, &impl_syntax.file_syntax(sema.db), file_range); + let update_test = UpdateTest::find_snapshot_macro(sema, file_range); Some(Runnable { use_name_in_title: false, @@ -475,7 +473,7 @@ fn runnable_mod_outline_definition( file_id: mod_source.file_id.original_file(sema.db), range: mod_syntax.text_range(), }; - let update_test = UpdateTest::find_snapshot_macro(sema, &mod_syntax, file_range); + let update_test = UpdateTest::find_snapshot_macro(sema, file_range); Some(Runnable { use_name_in_title: false, @@ -641,7 +639,7 @@ pub struct UpdateTest { pub snapbox: bool, } -static SNAPSHOT_TEST_MACROS: OnceLock>> = OnceLock::new(); +static SNAPSHOT_TEST_MACROS: OnceLock>> = OnceLock::new(); impl UpdateTest { const EXPECT_CRATE: &str = "expect_test"; @@ -665,22 +663,17 @@ impl UpdateTest { const SNAPBOX_CRATE: &str = "snapbox"; const SNAPBOX_MACROS: &[&str] = &["assert_data_eq", "file", "str"]; - fn find_snapshot_macro( - sema: &Semantics<'_, RootDatabase>, - scope: &SyntaxNode, - file_range: hir::FileRange, - ) -> Self { + fn find_snapshot_macro(sema: &Semantics<'_, RootDatabase>, file_range: hir::FileRange) -> Self { fn init<'a>( krate_name: &'a str, paths: &[&str], - map: &mut FxHashMap<&'a str, Vec>, + map: &mut FxHashMap<&'a str, Vec<[Symbol; 2]>>, ) { let mut res = Vec::with_capacity(paths.len()); - let krate = Name::new_symbol_root(Symbol::intern(krate_name)); + let krate = Symbol::intern(krate_name); for path in paths { - let segments = [krate.clone(), Name::new_symbol_root(Symbol::intern(path))]; - let mod_path = ModPath::from_segments(PathKind::Abs, segments); - res.push(mod_path); + let segments = [krate.clone(), Symbol::intern(path)]; + res.push(segments); } map.insert(krate_name, res); } @@ -694,11 +687,9 @@ impl UpdateTest { }); let search_scope = SearchScope::file_range(file_range); - let find_macro = |paths: &[ModPath]| { + let find_macro = |paths: &[[Symbol; 2]]| { for path in paths { - let Some(items) = sema.resolve_mod_path(scope, path) else { - continue; - }; + let items = hir::resolve_absolute_path(sema.db, path.iter().cloned()); for item in items { if let hir::ItemInNs::Macros(makro) = item && Definition::Macro(makro) From c4cbb54f3f01b1e2c1927049d04f98d677734c3a Mon Sep 17 00:00:00 2001 From: LorrensP-2158466 Date: Tue, 9 Sep 2025 09:49:48 +0200 Subject: [PATCH 301/682] add approx_delta to all gamma tests --- library/std/tests/floats/f32.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/library/std/tests/floats/f32.rs b/library/std/tests/floats/f32.rs index c29d803b25e4..3acd06709141 100644 --- a/library/std/tests/floats/f32.rs +++ b/library/std/tests/floats/f32.rs @@ -193,13 +193,13 @@ fn test_atanh() { #[test] fn test_gamma() { // precision can differ between platforms - assert_approx_eq!(1.0f32.gamma(), 1.0f32); - assert_approx_eq!(2.0f32.gamma(), 1.0f32); - assert_approx_eq!(3.0f32.gamma(), 2.0f32); + assert_approx_eq!(1.0f32.gamma(), 1.0f32, APPROX_DELTA); + assert_approx_eq!(2.0f32.gamma(), 1.0f32, APPROX_DELTA); + assert_approx_eq!(3.0f32.gamma(), 2.0f32, APPROX_DELTA); assert_approx_eq!(4.0f32.gamma(), 6.0f32, APPROX_DELTA); assert_approx_eq!(5.0f32.gamma(), 24.0f32, APPROX_DELTA); - assert_approx_eq!(0.5f32.gamma(), consts::PI.sqrt()); - assert_approx_eq!((-0.5f32).gamma(), -2.0 * consts::PI.sqrt()); + assert_approx_eq!(0.5f32.gamma(), consts::PI.sqrt(), APPROX_DELTA); + assert_approx_eq!((-0.5f32).gamma(), -2.0 * consts::PI.sqrt(), APPROX_DELTA); assert_eq!(0.0f32.gamma(), f32::INFINITY); assert_eq!((-0.0f32).gamma(), f32::NEG_INFINITY); assert!((-1.0f32).gamma().is_nan()); From 74a8f7451e75a789a4f656682465b4a2f6b559d2 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 9 Sep 2025 13:31:05 +0300 Subject: [PATCH 302/682] Expand target info to include the architecture And make it easier to expand it more in the future, if needed. --- .../rust-analyzer/crates/base-db/src/input.rs | 4 +- .../rust-analyzer/crates/base-db/src/lib.rs | 7 ++- .../crates/base-db/src/target.rs | 50 +++++++++++++++++++ .../hir-def/src/nameres/tests/incremental.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/db.rs | 3 +- .../crates/hir-ty/src/layout/target.rs | 8 +-- .../crates/hir-ty/src/layout/tests.rs | 11 ++-- .../crates/hir-ty/src/mir/eval.rs | 3 +- src/tools/rust-analyzer/crates/ide/src/lib.rs | 2 +- .../crates/project-model/src/lib.rs | 2 +- .../crates/project-model/src/tests.rs | 7 ++- .../{target_data_layout.rs => target_data.rs} | 41 +++++++++++++-- .../crates/project-model/src/workspace.rs | 38 +++++++------- .../cargo_hello_world_project_model.txt | 10 ++-- ...project_model_with_selective_overrides.txt | 10 ++-- ..._project_model_with_wildcard_overrides.txt | 10 ++-- .../output/rust_project_cfg_groups.txt | 8 +-- ...rust_project_hello_world_project_model.txt | 4 +- .../rust-analyzer/src/cli/rustc_tests.rs | 7 ++- .../rust-analyzer/tests/slow-tests/support.rs | 2 + .../crates/test-fixture/src/lib.rs | 18 +++++-- .../crates/test-utils/src/fixture.rs | 19 ++++++- 22 files changed, 188 insertions(+), 78 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/base-db/src/target.rs rename src/tools/rust-analyzer/crates/project-model/src/toolchain_info/{target_data_layout.rs => target_data.rs} (72%) diff --git a/src/tools/rust-analyzer/crates/base-db/src/input.rs b/src/tools/rust-analyzer/crates/base-db/src/input.rs index 0bf4fbdfbd69..cac74778a26b 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/input.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs @@ -295,8 +295,6 @@ impl CrateDisplayName { } } -pub type TargetLayoutLoadResult = Result, Arc>; - #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum ReleaseChannel { Stable, @@ -929,7 +927,7 @@ mod tests { use super::{CrateGraphBuilder, CrateName, CrateOrigin, Edition::Edition2018, Env, FileId}; fn empty_ws_data() -> Arc { - Arc::new(CrateWorkspaceData { data_layout: Err("".into()), toolchain: None }) + Arc::new(CrateWorkspaceData { target: Err("".into()), toolchain: None }) } #[test] diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs index 14544acc11bd..0e411bcfae60 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs @@ -6,6 +6,7 @@ pub use salsa_macros; // FIXME: Rename this crate, base db is non descriptive mod change; mod input; +pub mod target; use std::{ cell::RefCell, @@ -20,8 +21,7 @@ pub use crate::{ BuiltCrateData, BuiltDependency, Crate, CrateBuilder, CrateBuilderId, CrateDataBuilder, CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin, CratesIdMap, CratesMap, DependencyBuilder, Env, ExtraCrateData, LangCrateOrigin, ProcMacroLoadingError, - ProcMacroPaths, ReleaseChannel, SourceRoot, SourceRootId, TargetLayoutLoadResult, - UniqueCrateData, + ProcMacroPaths, ReleaseChannel, SourceRoot, SourceRootId, UniqueCrateData, }, }; use dashmap::{DashMap, mapref::entry::Entry}; @@ -359,8 +359,7 @@ impl Nonce { /// Crate related data shared by the whole workspace. #[derive(Debug, PartialEq, Eq, Hash, Clone)] pub struct CrateWorkspaceData { - // FIXME: Consider removing this, making HirDatabase::target_data_layout an input query - pub data_layout: TargetLayoutLoadResult, + pub target: Result, /// Toolchain version used to compile the crate. pub toolchain: Option, } diff --git a/src/tools/rust-analyzer/crates/base-db/src/target.rs b/src/tools/rust-analyzer/crates/base-db/src/target.rs new file mode 100644 index 000000000000..19d3407bf3c8 --- /dev/null +++ b/src/tools/rust-analyzer/crates/base-db/src/target.rs @@ -0,0 +1,50 @@ +//! Information about the target. + +use std::fmt; + +use triomphe::Arc; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Arch { + // Only what we need is present here. + Wasm32, + Wasm64, + Other, +} + +#[derive(Debug, PartialEq, Eq, Hash, Clone)] +pub struct TargetData { + pub data_layout: Box, + pub arch: Arch, +} + +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct TargetLoadError(Arc); + +impl fmt::Debug for TargetLoadError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.0, f) + } +} + +impl fmt::Display for TargetLoadError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } +} + +impl std::error::Error for TargetLoadError {} + +impl From for TargetLoadError { + fn from(value: String) -> Self { + Self(value.into()) + } +} + +impl From<&str> for TargetLoadError { + fn from(value: &str) -> Self { + Self(value.into()) + } +} + +pub type TargetLoadResult = Result; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs index 338851b715bf..6afa04bc412a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs @@ -84,7 +84,7 @@ pub const BAZ: u32 = 0; ) .unwrap(), ), - Arc::new(CrateWorkspaceData { data_layout: Err("".into()), toolchain: None }), + Arc::new(CrateWorkspaceData { target: Err("".into()), toolchain: None }), ) }; let a = add_crate("a", 0); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 0b7213d785c9..0aec2b9dec7b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -2,6 +2,7 @@ //! type inference-related queries. use base_db::Crate; +use base_db::target::TargetLoadError; use hir_def::{ AdtId, BlockId, CallableDefId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, StaticId, TraitId, @@ -108,7 +109,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { ) -> Result, LayoutError>; #[salsa::invoke(crate::layout::target_data_layout_query)] - fn target_data_layout(&self, krate: Crate) -> Result, Arc>; + fn target_data_layout(&self, krate: Crate) -> Result, TargetLoadError>; #[salsa::invoke(crate::dyn_compatibility::dyn_compatibility_of_trait_query)] fn dyn_compatibility_of_trait(&self, trait_: TraitId) -> Option; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs index 82d0ed4f1947..8a7d93d50c05 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs @@ -1,6 +1,6 @@ //! Target dependent parameters needed for layouts -use base_db::Crate; +use base_db::{Crate, target::TargetLoadError}; use hir_def::layout::TargetDataLayout; use rustc_abi::{AddressSpace, AlignFromBytesError, TargetDataLayoutErrors}; use triomphe::Arc; @@ -10,9 +10,9 @@ use crate::db::HirDatabase; pub fn target_data_layout_query( db: &dyn HirDatabase, krate: Crate, -) -> Result, Arc> { - match &krate.workspace_data(db).data_layout { - Ok(it) => match TargetDataLayout::parse_from_llvm_datalayout_string(it, AddressSpace::ZERO) { +) -> Result, TargetLoadError> { + match &krate.workspace_data(db).target { + Ok(target) => match TargetDataLayout::parse_from_llvm_datalayout_string(&target.data_layout, AddressSpace::ZERO) { Ok(it) => Ok(Arc::new(it)), Err(e) => { Err(match e { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs index 90de7e5ca633..523ddad94666 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs @@ -1,3 +1,4 @@ +use base_db::target::TargetData; use chalk_ir::{AdtId, TyKind}; use either::Either; use hir_def::db::DefDatabase; @@ -18,8 +19,8 @@ use crate::{ mod closure; -fn current_machine_data_layout() -> String { - project_model::toolchain_info::target_data_layout::get( +fn current_machine_target_data() -> TargetData { + project_model::toolchain_info::target_data::get( QueryConfig::Rustc(&Sysroot::empty(), &std::env::current_dir().unwrap()), None, &FxHashMap::default(), @@ -32,7 +33,8 @@ fn eval_goal( minicore: &str, ) -> Result, LayoutError> { let _tracing = setup_tracing(); - let target_data_layout = current_machine_data_layout(); + let target_data = current_machine_target_data(); + let target_data_layout = target_data.data_layout; let ra_fixture = format!( "//- target_data_layout: {target_data_layout}\n{minicore}//- /main.rs crate:test\n{ra_fixture}", ); @@ -104,7 +106,8 @@ fn eval_expr( minicore: &str, ) -> Result, LayoutError> { let _tracing = setup_tracing(); - let target_data_layout = current_machine_data_layout(); + let target_data = current_machine_target_data(); + let target_data_layout = target_data.data_layout; let ra_fixture = format!( "//- target_data_layout: {target_data_layout}\n{minicore}//- /main.rs crate:test\nfn main(){{let goal = {{{ra_fixture}}};}}", ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index c60ace85be12..94d7c2f5ec5a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -3,6 +3,7 @@ use std::{borrow::Cow, cell::RefCell, fmt::Write, iter, mem, ops::Range}; use base_db::Crate; +use base_db::target::TargetLoadError; use chalk_ir::{Mutability, cast::Cast}; use either::Either; use hir_def::{ @@ -337,7 +338,7 @@ impl Address { pub enum MirEvalError { ConstEvalError(String, Box), LayoutError(LayoutError, Ty), - TargetDataLayoutNotAvailable(Arc), + TargetDataLayoutNotAvailable(TargetLoadError), /// Means that code had undefined behavior. We don't try to actively detect UB, but if it was detected /// then use this type of error. UndefinedBehavior(String), diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 3a6b3247d65c..50cbef581c7d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -263,7 +263,7 @@ impl Analysis { false, proc_macro_cwd, Arc::new(CrateWorkspaceData { - data_layout: Err("fixture has no layout".into()), + target: Err("fixture has no layout".into()), toolchain: None, }), ); diff --git a/src/tools/rust-analyzer/crates/project-model/src/lib.rs b/src/tools/rust-analyzer/crates/project-model/src/lib.rs index d39781b15066..e36b90488151 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/lib.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/lib.rs @@ -18,7 +18,7 @@ pub mod project_json; pub mod toolchain_info { pub mod rustc_cfg; - pub mod target_data_layout; + pub mod target_data; pub mod target_tuple; pub mod version; diff --git a/src/tools/rust-analyzer/crates/project-model/src/tests.rs b/src/tools/rust-analyzer/crates/project-model/src/tests.rs index ed72520f40d4..987d381fac63 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs @@ -9,7 +9,6 @@ use paths::{AbsPath, AbsPathBuf, Utf8Path, Utf8PathBuf}; use rustc_hash::FxHashMap; use serde::de::DeserializeOwned; use span::FileId; -use triomphe::Arc; use crate::{ CargoWorkspace, CfgOverrides, ManifestPath, ProjectJson, ProjectJsonData, ProjectWorkspace, @@ -47,7 +46,7 @@ fn load_workspace_from_metadata(file: &str) -> ProjectWorkspace { sysroot: Sysroot::empty(), rustc_cfg: Vec::new(), toolchain: None, - target_layout: Err("target_data_layout not loaded".into()), + target: Err("target_data_layout not loaded".into()), extra_includes: Vec::new(), set_test: true, } @@ -62,7 +61,7 @@ fn load_rust_project(file: &str) -> (CrateGraphBuilder, ProcMacroPaths) { sysroot, rustc_cfg: Vec::new(), toolchain: None, - target_layout: Err(Arc::from("test has no data layout")), + target: Err("test has no target data".into()), cfg_overrides: Default::default(), extra_includes: Vec::new(), set_test: true, @@ -265,7 +264,7 @@ fn smoke_test_real_sysroot_cargo() { rustc_cfg: Vec::new(), cfg_overrides: Default::default(), toolchain: None, - target_layout: Err("target_data_layout not loaded".into()), + target: Err("target_data_layout not loaded".into()), extra_includes: Vec::new(), set_test: true, }; diff --git a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data_layout.rs b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data.rs similarity index 72% rename from src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data_layout.rs rename to src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data.rs index f1d99cb8b002..b815c0b79718 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data_layout.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data.rs @@ -1,23 +1,54 @@ //! Runs `rustc --print target-spec-json` to get the target_data_layout. use anyhow::Context; +use base_db::target; use rustc_hash::FxHashMap; +use serde_derive::Deserialize; use toolchain::Tool; use crate::{Sysroot, toolchain_info::QueryConfig, utf8_stdout}; +#[derive(Debug, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub enum Arch { + Wasm32, + Wasm64, + #[serde(other)] + Other, +} + +impl From for target::Arch { + fn from(value: Arch) -> Self { + match value { + Arch::Wasm32 => target::Arch::Wasm32, + Arch::Wasm64 => target::Arch::Wasm64, + Arch::Other => target::Arch::Other, + } + } +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub struct TargetSpec { + pub data_layout: String, + pub arch: Arch, +} + /// Uses `rustc --print target-spec-json`. pub fn get( config: QueryConfig<'_>, target: Option<&str>, extra_env: &FxHashMap>, -) -> anyhow::Result { +) -> anyhow::Result { const RUSTC_ARGS: [&str; 2] = ["--print", "target-spec-json"]; let process = |output: String| { - (|| Some(output.split_once(r#""data-layout": ""#)?.1.split_once('"')?.0.to_owned()))() - .ok_or_else(|| { - anyhow::format_err!("could not parse target-spec-json from command output") - }) + let target_spec = serde_json::from_str::(&output).map_err(|_| { + anyhow::format_err!("could not parse target-spec-json from command output") + })?; + Ok(target::TargetData { + arch: target_spec.arch.into(), + data_layout: target_spec.data_layout.into_boxed_str(), + }) }; let (sysroot, current_dir) = match config { QueryConfig::Cargo(sysroot, cargo_toml, _) => { diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index 00032bcf1d2e..e0d2105c8df8 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -8,7 +8,7 @@ use anyhow::Context; use base_db::{ CrateBuilderId, CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin, CrateWorkspaceData, DependencyBuilder, Env, LangCrateOrigin, ProcMacroLoadingError, - ProcMacroPaths, TargetLayoutLoadResult, + ProcMacroPaths, target::TargetLoadResult, }; use cfg::{CfgAtom, CfgDiff, CfgOptions}; use intern::{Symbol, sym}; @@ -30,7 +30,7 @@ use crate::{ env::{cargo_config_env, inject_cargo_env, inject_cargo_package_env, inject_rustc_tool_env}, project_json::{Crate, CrateArrayIdx}, sysroot::RustLibSrcWorkspace, - toolchain_info::{QueryConfig, rustc_cfg, target_data_layout, target_tuple, version}, + toolchain_info::{QueryConfig, rustc_cfg, target_data, target_tuple, version}, utf8_stdout, }; use tracing::{debug, error, info}; @@ -63,7 +63,7 @@ pub struct ProjectWorkspace { /// The toolchain version used by this workspace. pub toolchain: Option, /// The target data layout queried for workspace. - pub target_layout: TargetLayoutLoadResult, + pub target: TargetLoadResult, /// A set of cfg overrides for this workspace. pub cfg_overrides: CfgOverrides, /// Additional includes to add for the VFS. @@ -115,7 +115,7 @@ impl fmt::Debug for ProjectWorkspace { sysroot, rustc_cfg, toolchain, - target_layout, + target: target_layout, cfg_overrides, extra_includes, set_test, @@ -309,8 +309,8 @@ impl ProjectWorkspace { let rustc_cfg = s.spawn(|| { rustc_cfg::get(toolchain_config, targets.first().map(Deref::deref), extra_env) }); - let data_layout = s.spawn(|| { - target_data_layout::get( + let target_data = s.spawn(|| { + target_data::get( toolchain_config, targets.first().map(Deref::deref), extra_env, @@ -392,7 +392,7 @@ impl ProjectWorkspace { s.spawn(move || cargo_config_env(cargo_toml, &config_file)); thread::Result::Ok(( rustc_cfg.join()?, - data_layout.join()?, + target_data.join()?, rustc_dir.join()?, loaded_sysroot.join()?, cargo_metadata.join()?, @@ -442,7 +442,7 @@ impl ProjectWorkspace { rustc_cfg, cfg_overrides: cfg_overrides.clone(), toolchain, - target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())), + target: data_layout.map_err(|it| it.to_string().into()), extra_includes: extra_includes.clone(), set_test: *set_test, }) @@ -480,11 +480,7 @@ impl ProjectWorkspace { rustc_cfg::get(query_config, targets.first().map(Deref::deref), &config.extra_env) }); let data_layout = s.spawn(|| { - target_data_layout::get( - query_config, - targets.first().map(Deref::deref), - &config.extra_env, - ) + target_data::get(query_config, targets.first().map(Deref::deref), &config.extra_env) }); let loaded_sysroot = s.spawn(|| { if let Some(sysroot_project) = sysroot_project { @@ -513,7 +509,7 @@ impl ProjectWorkspace { thread::Result::Ok((rustc_cfg.join()?, data_layout.join()?, loaded_sysroot.join()?)) }); - let (rustc_cfg, target_layout, loaded_sysroot) = match join { + let (rustc_cfg, target_data, loaded_sysroot) = match join { Ok(it) => it, Err(e) => std::panic::resume_unwind(e), }; @@ -527,7 +523,7 @@ impl ProjectWorkspace { sysroot, rustc_cfg, toolchain, - target_layout: target_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())), + target: target_data.map_err(|it| it.to_string().into()), cfg_overrides: config.cfg_overrides.clone(), extra_includes: config.extra_includes.clone(), set_test: config.set_test, @@ -551,7 +547,7 @@ impl ProjectWorkspace { let targets = target_tuple::get(query_config, config.target.as_deref(), &config.extra_env) .unwrap_or_default(); let rustc_cfg = rustc_cfg::get(query_config, None, &config.extra_env); - let data_layout = target_data_layout::get(query_config, None, &config.extra_env); + let target_data = target_data::get(query_config, None, &config.extra_env); let target_dir = config .target_dir .clone() @@ -610,7 +606,7 @@ impl ProjectWorkspace { sysroot, rustc_cfg, toolchain, - target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())), + target: target_data.map_err(|it| it.to_string().into()), cfg_overrides: config.cfg_overrides.clone(), extra_includes: config.extra_includes.clone(), set_test: config.set_test, @@ -942,7 +938,7 @@ impl ProjectWorkspace { let Self { kind, sysroot, cfg_overrides, rustc_cfg, .. } = self; let crate_ws_data = Arc::new(CrateWorkspaceData { toolchain: self.toolchain.clone(), - data_layout: self.target_layout.clone(), + target: self.target.clone(), }); let (crate_graph, proc_macros) = match kind { ProjectWorkspaceKind::Json(project) => project_json_to_crate_graph( @@ -1000,13 +996,15 @@ impl ProjectWorkspace { } pub fn eq_ignore_build_data(&self, other: &Self) -> bool { - let Self { kind, sysroot, rustc_cfg, toolchain, target_layout, cfg_overrides, .. } = self; + let Self { + kind, sysroot, rustc_cfg, toolchain, target: target_layout, cfg_overrides, .. + } = self; let Self { kind: o_kind, sysroot: o_sysroot, rustc_cfg: o_rustc_cfg, toolchain: o_toolchain, - target_layout: o_target_layout, + target: o_target_layout, cfg_overrides: o_cfg_overrides, .. } = other; diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt index 3722e2c72168..4f6ce4dc9537 100644 --- a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt +++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt @@ -70,7 +70,7 @@ }, }, ws_data: CrateWorkspaceData { - data_layout: Err( + target: Err( "target_data_layout not loaded", ), toolchain: None, @@ -155,7 +155,7 @@ }, }, ws_data: CrateWorkspaceData { - data_layout: Err( + target: Err( "target_data_layout not loaded", ), toolchain: None, @@ -240,7 +240,7 @@ }, }, ws_data: CrateWorkspaceData { - data_layout: Err( + target: Err( "target_data_layout not loaded", ), toolchain: None, @@ -325,7 +325,7 @@ }, }, ws_data: CrateWorkspaceData { - data_layout: Err( + target: Err( "target_data_layout not loaded", ), toolchain: None, @@ -406,7 +406,7 @@ }, }, ws_data: CrateWorkspaceData { - data_layout: Err( + target: Err( "target_data_layout not loaded", ), toolchain: None, diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt index 3722e2c72168..4f6ce4dc9537 100644 --- a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt +++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt @@ -70,7 +70,7 @@ }, }, ws_data: CrateWorkspaceData { - data_layout: Err( + target: Err( "target_data_layout not loaded", ), toolchain: None, @@ -155,7 +155,7 @@ }, }, ws_data: CrateWorkspaceData { - data_layout: Err( + target: Err( "target_data_layout not loaded", ), toolchain: None, @@ -240,7 +240,7 @@ }, }, ws_data: CrateWorkspaceData { - data_layout: Err( + target: Err( "target_data_layout not loaded", ), toolchain: None, @@ -325,7 +325,7 @@ }, }, ws_data: CrateWorkspaceData { - data_layout: Err( + target: Err( "target_data_layout not loaded", ), toolchain: None, @@ -406,7 +406,7 @@ }, }, ws_data: CrateWorkspaceData { - data_layout: Err( + target: Err( "target_data_layout not loaded", ), toolchain: None, diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt index 7b156ea63a58..6862918e09ae 100644 --- a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt +++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt @@ -69,7 +69,7 @@ }, }, ws_data: CrateWorkspaceData { - data_layout: Err( + target: Err( "target_data_layout not loaded", ), toolchain: None, @@ -153,7 +153,7 @@ }, }, ws_data: CrateWorkspaceData { - data_layout: Err( + target: Err( "target_data_layout not loaded", ), toolchain: None, @@ -237,7 +237,7 @@ }, }, ws_data: CrateWorkspaceData { - data_layout: Err( + target: Err( "target_data_layout not loaded", ), toolchain: None, @@ -321,7 +321,7 @@ }, }, ws_data: CrateWorkspaceData { - data_layout: Err( + target: Err( "target_data_layout not loaded", ), toolchain: None, @@ -402,7 +402,7 @@ }, }, ws_data: CrateWorkspaceData { - data_layout: Err( + target: Err( "target_data_layout not loaded", ), toolchain: None, diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_cfg_groups.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_cfg_groups.txt index 98fe598eb3a3..28ad3236ae81 100644 --- a/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_cfg_groups.txt +++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_cfg_groups.txt @@ -43,8 +43,8 @@ entries: {}, }, ws_data: CrateWorkspaceData { - data_layout: Err( - "test has no data layout", + target: Err( + "test has no target data", ), toolchain: None, }, @@ -93,8 +93,8 @@ entries: {}, }, ws_data: CrateWorkspaceData { - data_layout: Err( - "test has no data layout", + target: Err( + "test has no target data", ), toolchain: None, }, diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt index 0dc373b5b47e..dabb3aa67441 100644 --- a/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt +++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt @@ -40,8 +40,8 @@ entries: {}, }, ws_data: CrateWorkspaceData { - data_layout: Err( - "test has no data layout", + target: Err( + "test has no target data", ), toolchain: None, }, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs index 36ae98b321b8..609ebf2b514f 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs @@ -11,7 +11,7 @@ use ide_db::base_db; use itertools::Either; use paths::Utf8PathBuf; use profile::StopWatch; -use project_model::toolchain_info::{QueryConfig, target_data_layout}; +use project_model::toolchain_info::{QueryConfig, target_data}; use project_model::{ CargoConfig, ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, RustLibSource, RustSourceWorkspaceConfig, Sysroot, @@ -19,7 +19,6 @@ use project_model::{ use load_cargo::{LoadCargoConfig, ProcMacroServerChoice, load_workspace}; use rustc_hash::FxHashMap; -use triomphe::Arc; use vfs::{AbsPathBuf, FileId}; use walkdir::WalkDir; @@ -87,7 +86,7 @@ impl Tester { sysroot.set_workspace(loaded_sysroot); } - let data_layout = target_data_layout::get( + let target_data = target_data::get( QueryConfig::Rustc(&sysroot, tmp_file.parent().unwrap().as_ref()), None, &cargo_config.extra_env, @@ -101,7 +100,7 @@ impl Tester { sysroot, rustc_cfg: vec![], toolchain: None, - target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())), + target: target_data.map_err(|it| it.to_string().into()), cfg_overrides: Default::default(), extra_includes: vec![], set_test: true, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs index 2bebb0c1b970..3464a9644b19 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs @@ -97,6 +97,7 @@ impl Project<'_> { proc_macro_names, toolchain, target_data_layout: _, + target_arch: _, } = FixtureWithProjectMeta::parse(self.fixture); assert!(proc_macro_names.is_empty()); assert!(mini_core.is_none()); @@ -177,6 +178,7 @@ impl Project<'_> { proc_macro_names, toolchain, target_data_layout: _, + target_arch: _, } = FixtureWithProjectMeta::parse(self.fixture); assert!(proc_macro_names.is_empty()); assert!(mini_core.is_none()); diff --git a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs index 57fca70547bf..7574d12c0cfd 100644 --- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs +++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs @@ -1,6 +1,7 @@ //! A set of high-level utility fixture methods to use in tests. use std::{any::TypeId, mem, str::FromStr, sync}; +use base_db::target::TargetData; use base_db::{ Crate, CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin, CrateWorkspaceData, DependencyBuilder, Env, FileChange, FileSet, FxIndexMap, LangCrateOrigin, SourceDatabase, @@ -136,8 +137,11 @@ impl ChangeFixture { proc_macro_names, toolchain, target_data_layout, + target_arch, } = FixtureWithProjectMeta::parse(ra_fixture); - let target_data_layout = Ok(target_data_layout.into()); + let target_data_layout = target_data_layout.into(); + let target_arch = parse_target_arch(&target_arch); + let target = Ok(TargetData { arch: target_arch, data_layout: target_data_layout }); let toolchain = Some({ let channel = toolchain.as_deref().unwrap_or("stable"); Version::parse(&format!("1.76.0-{channel}")).unwrap() @@ -163,8 +167,7 @@ impl ChangeFixture { let mut file_position = None; - let crate_ws_data = - Arc::new(CrateWorkspaceData { data_layout: target_data_layout, toolchain }); + let crate_ws_data = Arc::new(CrateWorkspaceData { target, toolchain }); // FIXME: This is less than ideal let proc_macro_cwd = Arc::new(AbsPathBuf::assert_utf8(std::env::current_dir().unwrap())); @@ -395,6 +398,15 @@ impl ChangeFixture { } } +fn parse_target_arch(arch: &str) -> base_db::target::Arch { + use base_db::target::Arch::*; + match arch { + "wasm32" => Wasm32, + "wasm64" => Wasm64, + _ => Other, + } +} + fn default_test_proc_macros() -> Box<[(String, ProcMacro)]> { Box::new([ ( diff --git a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs index e830c6a7cf68..c024089a016f 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs @@ -149,6 +149,8 @@ pub struct FixtureWithProjectMeta { /// You probably don't want to manually specify this. See LLVM manual for the /// syntax, if you must: pub target_data_layout: String, + /// Specifies the target architecture. + pub target_arch: String, } impl FixtureWithProjectMeta { @@ -178,6 +180,7 @@ impl FixtureWithProjectMeta { let mut toolchain = None; let mut target_data_layout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128".to_owned(); + let mut target_arch = "x86_64".to_owned(); let mut mini_core = None; let mut res: Vec = Vec::new(); let mut proc_macro_names = vec![]; @@ -194,6 +197,12 @@ impl FixtureWithProjectMeta { fixture = remain; } + if let Some(meta) = fixture.strip_prefix("//- target_arch:") { + let (meta, remain) = meta.split_once('\n').unwrap(); + meta.trim().clone_into(&mut target_arch); + fixture = remain; + } + if let Some(meta) = fixture.strip_prefix("//- proc_macros:") { let (meta, remain) = meta.split_once('\n').unwrap(); proc_macro_names = meta.split(',').map(|it| it.trim().to_owned()).collect(); @@ -232,7 +241,14 @@ impl FixtureWithProjectMeta { } } - Self { fixture: res, mini_core, proc_macro_names, toolchain, target_data_layout } + Self { + fixture: res, + mini_core, + proc_macro_names, + toolchain, + target_data_layout, + target_arch, + } } //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo @@ -511,6 +527,7 @@ fn parse_fixture_gets_full_meta() { proc_macro_names, toolchain, target_data_layout: _, + target_arch: _, } = FixtureWithProjectMeta::parse( r#" //- toolchain: nightly From d6638e9bd2b455cf0d3a0df04afc2bab8f134fc9 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 9 Sep 2025 13:49:11 +0300 Subject: [PATCH 303/682] Make `#[target_feature]` safe always on WASM Even when the feature isn't enabled, as it's not UB to invoke an undefined feature in WASM (just a trap). --- .../hir-ty/src/diagnostics/unsafe_check.rs | 24 +++++++++++++++---- .../rust-analyzer/crates/hir-ty/src/lib.rs | 5 +++- .../rust-analyzer/crates/hir-ty/src/utils.rs | 12 ++++++++-- src/tools/rust-analyzer/crates/hir/src/lib.rs | 23 ++++++++++++++---- .../src/handlers/missing_unsafe.rs | 16 +++++++++++++ 5 files changed, 69 insertions(+), 11 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs index d6d669258cc1..64c4cdeaddf3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs @@ -15,8 +15,9 @@ use hir_def::{ use span::Edition; use crate::{ - InferenceResult, Interner, TargetFeatures, TyExt, TyKind, db::HirDatabase, - utils::is_fn_unsafe_to_call, + InferenceResult, Interner, TargetFeatures, TyExt, TyKind, + db::HirDatabase, + utils::{is_fn_unsafe_to_call, target_feature_is_safe_in_target}, }; #[derive(Debug, Default)] @@ -144,6 +145,9 @@ struct UnsafeVisitor<'db> { def_target_features: TargetFeatures, // FIXME: This needs to be the edition of the span of each call. edition: Edition, + /// On some targets (WASM), calling safe functions with `#[target_feature]` is always safe, even when + /// the target feature is not enabled. This flag encodes that. + target_feature_is_safe: bool, } impl<'db> UnsafeVisitor<'db> { @@ -159,7 +163,12 @@ impl<'db> UnsafeVisitor<'db> { DefWithBodyId::FunctionId(func) => TargetFeatures::from_attrs(&db.attrs(func.into())), _ => TargetFeatures::default(), }; - let edition = resolver.module().krate().data(db).edition; + let krate = resolver.module().krate(); + let edition = krate.data(db).edition; + let target_feature_is_safe = match &krate.workspace_data(db).target { + Ok(target) => target_feature_is_safe_in_target(target), + Err(_) => false, + }; Self { db, infer, @@ -172,6 +181,7 @@ impl<'db> UnsafeVisitor<'db> { callback: unsafe_expr_cb, def_target_features, edition, + target_feature_is_safe, } } @@ -184,7 +194,13 @@ impl<'db> UnsafeVisitor<'db> { } fn check_call(&mut self, node: ExprId, func: FunctionId) { - let unsafety = is_fn_unsafe_to_call(self.db, func, &self.def_target_features, self.edition); + let unsafety = is_fn_unsafe_to_call( + self.db, + func, + &self.def_target_features, + self.edition, + self.target_feature_is_safe, + ); match unsafety { crate::utils::Unsafety::Safe => {} crate::utils::Unsafety::Unsafe => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index c16bbb7b9922..ee352619487c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -129,7 +129,10 @@ pub use mapping::{ pub use method_resolution::check_orphan_rules; pub use target_feature::TargetFeatures; pub use traits::TraitEnvironment; -pub use utils::{Unsafety, all_super_traits, direct_super_traits, is_fn_unsafe_to_call}; +pub use utils::{ + Unsafety, all_super_traits, direct_super_traits, is_fn_unsafe_to_call, + target_feature_is_safe_in_target, +}; pub use variance::Variance; pub use chalk_ir::{ diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs index 881b1c1a9db2..07679d2a119d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs @@ -3,7 +3,10 @@ use std::{cell::LazyCell, iter}; -use base_db::Crate; +use base_db::{ + Crate, + target::{self, TargetData}, +}; use chalk_ir::{DebruijnIndex, fold::FallibleTypeFolder}; use hir_def::{ EnumId, EnumVariantId, FunctionId, Lookup, TraitId, TypeAliasId, TypeOrConstParamId, @@ -275,18 +278,23 @@ pub enum Unsafety { DeprecatedSafe2024, } +pub fn target_feature_is_safe_in_target(target: &TargetData) -> bool { + matches!(target.arch, target::Arch::Wasm32 | target::Arch::Wasm64) +} + pub fn is_fn_unsafe_to_call( db: &dyn HirDatabase, func: FunctionId, caller_target_features: &TargetFeatures, call_edition: Edition, + target_feature_is_safe: bool, ) -> Unsafety { let data = db.function_signature(func); if data.is_unsafe() { return Unsafety::Unsafe; } - if data.has_target_feature() { + if data.has_target_feature() && !target_feature_is_safe { // RFC 2396 . let callee_target_features = TargetFeatures::from_attrs_no_implications(&db.attrs(func.into())); diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 6eb8a8bf60fd..3f78a04df1d2 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -2542,11 +2542,26 @@ impl Function { caller: Option, call_edition: Edition, ) -> bool { - let target_features = caller - .map(|caller| hir_ty::TargetFeatures::from_attrs(&db.attrs(caller.id.into()))) - .unwrap_or_default(); + let (target_features, target_feature_is_safe_in_target) = caller + .map(|caller| { + let target_features = + hir_ty::TargetFeatures::from_attrs(&db.attrs(caller.id.into())); + let target_feature_is_safe_in_target = + match &caller.krate(db).id.workspace_data(db).target { + Ok(target) => hir_ty::target_feature_is_safe_in_target(target), + Err(_) => false, + }; + (target_features, target_feature_is_safe_in_target) + }) + .unwrap_or_else(|| (hir_ty::TargetFeatures::default(), false)); matches!( - hir_ty::is_fn_unsafe_to_call(db, self.id, &target_features, call_edition), + hir_ty::is_fn_unsafe_to_call( + db, + self.id, + &target_features, + call_edition, + target_feature_is_safe_in_target + ), hir_ty::Unsafety::Unsafe ) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs index 17caf6301825..4bb64747f5bb 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs @@ -998,4 +998,20 @@ extern "C" fn naked() { "#, ); } + + #[test] + fn target_feature_safe_on_wasm() { + check_diagnostics( + r#" +//- target_arch: wasm32 + +#[target_feature(enable = "simd128")] +fn requires_target_feature() {} + +fn main() { + requires_target_feature(); +} + "#, + ); + } } From 4c84f0a1b8740503f98723c66b90a921302098b4 Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Tue, 9 Sep 2025 20:26:45 +0800 Subject: [PATCH 304/682] Update the LoongArch target documentation This patch defines minimum CPU feature requirements, updates toolchain baseline, and streamlines maintainer list: - Specify double-precision floating-point and LSX as mandatory CPU features - Raise the minimum required binutils version to 2.42+, due to relocations introduced by the default medium code model - Remove outdated maintainers to reduce irrelevant notifications --- src/doc/rustc/src/platform-support.md | 4 ++-- .../rustc/src/platform-support/loongarch-linux.md | 15 +++++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 6c5b48d8c8f5..9ad9cb5b5264 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -95,8 +95,8 @@ target | notes `arm-unknown-linux-gnueabihf` | Armv6 Linux, hardfloat (kernel 3.2+, glibc 2.17) `armv7-unknown-linux-gnueabihf` | Armv7-A Linux, hardfloat (kernel 3.2+, glibc 2.17) [`armv7-unknown-linux-ohos`](platform-support/openharmony.md) | Armv7-A OpenHarmony -[`loongarch64-unknown-linux-gnu`](platform-support/loongarch-linux.md) | LoongArch64 Linux, LP64D ABI (kernel 5.19+, glibc 2.36) -[`loongarch64-unknown-linux-musl`](platform-support/loongarch-linux.md) | LoongArch64 Linux, LP64D ABI (kernel 5.19+, musl 1.2.5) +[`loongarch64-unknown-linux-gnu`](platform-support/loongarch-linux.md) | LoongArch64 Linux, LP64D ABI (kernel 5.19+, glibc 2.36), LSX required +[`loongarch64-unknown-linux-musl`](platform-support/loongarch-linux.md) | LoongArch64 Linux, LP64D ABI (kernel 5.19+, musl 1.2.5), LSX required [`i686-pc-windows-gnu`](platform-support/windows-gnu.md) | 32-bit MinGW (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI] [^win32-msvc-alignment] `powerpc-unknown-linux-gnu` | PowerPC Linux (kernel 3.2+, glibc 2.17) `powerpc64-unknown-linux-gnu` | PPC64 Linux (kernel 3.2+, glibc 2.17) diff --git a/src/doc/rustc/src/platform-support/loongarch-linux.md b/src/doc/rustc/src/platform-support/loongarch-linux.md index 817d3a892303..a923218282c1 100644 --- a/src/doc/rustc/src/platform-support/loongarch-linux.md +++ b/src/doc/rustc/src/platform-support/loongarch-linux.md @@ -7,8 +7,8 @@ LoongArch is a RISC ISA developed by Loongson Technology Corporation Limited. | Target | Description | |--------|-------------| -| `loongarch64-unknown-linux-gnu` | LoongArch64 Linux, LP64D ABI (kernel 5.19, glibc 2.36) | -| `loongarch64-unknown-linux-musl` | LoongArch64 Linux, LP64D ABI (kernel 5.19, musl 1.2.5) | +| `loongarch64-unknown-linux-gnu` | LoongArch64 Linux, LP64D ABI (kernel 5.19, glibc 2.36), LSX required | +| `loongarch64-unknown-linux-musl` | LoongArch64 Linux, LP64D ABI (kernel 5.19, musl 1.2.5), LSX required | These support both native and cross builds, and have full support for `std`. @@ -23,8 +23,6 @@ Reference material: ## Target maintainers [@heiher](https://github.com/heiher) -[@xiangzhai](https://github.com/xiangzhai) -[@zhaixiaojuan](https://github.com/zhaixiaojuan) [@xen0n](https://github.com/xen0n) ## Requirements @@ -46,8 +44,8 @@ The targets require a reasonably up-to-date LoongArch toolchain on the host. Currently the following components are used by the Rust CI to build the target, and the versions can be seen as the minimum requirement: -* GNU Binutils 2.40 -* GCC 13.x +* GNU Binutils 2.42 +* GCC 14.x * glibc 2.36 * linux-headers 5.19 @@ -59,6 +57,11 @@ for newer LoongArch ELF relocation types, among other features. Recent LLVM/Clang toolchains may be able to build the targets, but are not currently being actively tested. +### CPU features + +These targets require the double-precision floating-point and LSX (LoongArch +SIMD Extension) features. + ## Building These targets are distributed through `rustup`, and otherwise require no From e379c7758667f900aaf5551c4553c7d4c121e3e1 Mon Sep 17 00:00:00 2001 From: Boxy Date: Thu, 21 Aug 2025 16:50:54 +0100 Subject: [PATCH 305/682] erase_regions to erase_and_anonymize_regions --- .../rustc_borrowck/src/region_infer/mod.rs | 2 +- .../src/region_infer/opaque_types/mod.rs | 2 +- compiler/rustc_borrowck/src/type_check/mod.rs | 8 ++-- compiler/rustc_codegen_gcc/src/type_of.rs | 2 +- .../src/debuginfo/metadata.rs | 4 +- compiler/rustc_codegen_llvm/src/type_of.rs | 2 +- .../rustc_const_eval/src/interpret/traits.rs | 7 ++-- .../rustc_hir_analysis/src/check/wfcheck.rs | 2 +- .../src/coherence/orphan.rs | 2 +- .../src/hir_ty_lowering/errors.rs | 2 +- compiler/rustc_hir_typeck/src/cast.rs | 4 +- .../src/fn_ctxt/suggestions.rs | 2 +- compiler/rustc_hir_typeck/src/inline_asm.rs | 2 +- compiler/rustc_hir_typeck/src/writeback.rs | 4 +- .../src/infer/outlives/test_type_match.rs | 2 +- .../rustc_infer/src/infer/outlives/verify.rs | 4 +- .../src/deref_into_dyn_supertrait.rs | 2 +- .../src/for_loops_over_fallibles.rs | 2 +- compiler/rustc_lint/src/types.rs | 2 +- .../rustc_middle/src/mir/interpret/queries.rs | 12 +++--- compiler/rustc_middle/src/query/mod.rs | 4 +- .../rustc_middle/src/ty/abstract_const.rs | 2 +- compiler/rustc_middle/src/ty/erase_regions.rs | 28 ++++++------- compiler/rustc_middle/src/ty/instance.rs | 4 +- compiler/rustc_middle/src/ty/layout.rs | 5 ++- .../src/ty/normalize_erasing_regions.rs | 4 +- compiler/rustc_middle/src/ty/print/pretty.rs | 4 +- compiler/rustc_middle/src/ty/util.rs | 10 ++--- compiler/rustc_middle/src/ty/vtable.rs | 2 +- .../rustc_mir_build/src/check_tail_calls.rs | 4 +- compiler/rustc_mir_build/src/thir/cx/expr.rs | 13 ++++--- .../src/thir/pattern/const_to_pat.rs | 8 ++-- .../rustc_mir_dataflow/src/value_analysis.rs | 2 +- .../src/add_subtyping_projections.rs | 4 +- .../rustc_mir_transform/src/promote_consts.rs | 3 +- compiler/rustc_monomorphize/src/collector.rs | 2 +- compiler/rustc_symbol_mangling/src/legacy.rs | 7 +++- compiler/rustc_symbol_mangling/src/test.rs | 2 +- compiler/rustc_symbol_mangling/src/v0.rs | 5 ++- .../src/error_reporting/infer/mod.rs | 4 +- .../error_reporting/infer/note_and_explain.rs | 6 ++- .../traits/fulfillment_errors.rs | 7 ++-- .../src/error_reporting/traits/suggestions.rs | 6 +-- .../src/solve/delegate.rs | 2 +- .../src/traits/dyn_compatibility.rs | 2 +- .../rustc_trait_selection/src/traits/mod.rs | 8 ++-- .../src/traits/query/dropck_outlives.rs | 2 +- compiler/rustc_traits/src/codegen.rs | 2 +- .../src/normalize_erasing_regions.rs | 2 +- compiler/rustc_transmute/src/layout/mod.rs | 2 +- compiler/rustc_transmute/src/layout/tree.rs | 39 ++++++++++--------- compiler/rustc_ty_utils/src/instance.rs | 8 ++-- compiler/rustc_type_ir/src/visit.rs | 2 +- .../clippy/clippy_lints/src/dereference.rs | 2 +- .../src/loops/explicit_iter_loop.rs | 2 +- .../src/only_used_in_recursion.rs | 2 +- .../src/transmute/transmute_ref_to_ref.rs | 2 +- .../src/transmute/transmute_undefined_repr.rs | 4 +- .../clippy_lints/src/useless_conversion.rs | 2 +- src/tools/clippy/clippy_utils/src/ty/mod.rs | 2 +- 60 files changed, 158 insertions(+), 132 deletions(-) diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 9990f4cb3f2a..f57456949bb5 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -615,7 +615,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } // Type-test failed. Report the error. - let erased_generic_kind = infcx.tcx.erase_regions(type_test.generic_kind); + let erased_generic_kind = infcx.tcx.erase_and_anonymize_regions(type_test.generic_kind); // Skip duplicate-ish errors. if deduplicate_errors.insert(( diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs index 95c5366a6c8e..0af636aa734c 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs @@ -341,7 +341,7 @@ fn compute_concrete_types_from_defining_uses<'tcx>( // // FIXME(-Znext-solver): This isn't necessary after all. We can remove this check again. if let Some((prev_decl_key, prev_span)) = decls_modulo_regions.insert( - rcx.infcx.tcx.erase_regions(opaque_type_key), + rcx.infcx.tcx.erase_and_anonymize_regions(opaque_type_key), (opaque_type_key, hidden_type.span), ) && let Some((arg1, arg2)) = std::iter::zip( prev_decl_key.iter_captured_args(infcx.tcx).map(|(_, arg)| arg), diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 02be78f90b0d..8c5447fe1e9e 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1892,7 +1892,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { if is_diverging { // The signature in this call can reference region variables, // so erase them before calling a query. - let output_ty = self.tcx().erase_regions(sig.output()); + let output_ty = self.tcx().erase_and_anonymize_regions(sig.output()); if !output_ty .is_privately_uninhabited(self.tcx(), self.infcx.typing_env(self.infcx.param_env)) { @@ -1986,7 +1986,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let op_arg_ty = self.normalize(op_arg_ty, term_location); let category = if call_source.from_hir_call() { - ConstraintCategory::CallArgument(Some(self.infcx.tcx.erase_regions(func_ty))) + ConstraintCategory::CallArgument(Some( + self.infcx.tcx.erase_and_anonymize_regions(func_ty), + )) } else { ConstraintCategory::Boring }; @@ -2120,7 +2122,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // Erase the regions from `ty` to get a global type. The // `Sized` bound in no way depends on precise regions, so this // shouldn't affect `is_sized`. - let erased_ty = tcx.erase_regions(ty); + let erased_ty = tcx.erase_and_anonymize_regions(ty); // FIXME(#132279): Using `Ty::is_sized` causes us to incorrectly handle opaques here. if !erased_ty.is_sized(tcx, self.infcx.typing_env(self.infcx.param_env)) { // in current MIR construction, all non-control-flow rvalue diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs index 093f902bc3d8..93202483eed8 100644 --- a/compiler/rustc_codegen_gcc/src/type_of.rs +++ b/compiler/rustc_codegen_gcc/src/type_of.rs @@ -240,7 +240,7 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> { // Make sure lifetimes are erased, to avoid generating distinct LLVM // types for Rust types that only differ in the choice of lifetimes. - let normal_ty = cx.tcx.erase_regions(self.ty); + let normal_ty = cx.tcx.erase_and_anonymize_regions(self.ty); let mut defer = None; let ty = if self.ty != normal_ty { diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index caa3369f413a..0d71de21fef0 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -1435,7 +1435,7 @@ fn build_vtable_type_di_node<'ll, 'tcx>( let vtable_entries = if let Some(poly_trait_ref) = poly_trait_ref { let trait_ref = poly_trait_ref.with_self_ty(tcx, ty); - let trait_ref = tcx.erase_regions(trait_ref); + let trait_ref = tcx.erase_and_anonymize_regions(trait_ref); tcx.vtable_entries(trait_ref) } else { @@ -1562,7 +1562,7 @@ pub(crate) fn apply_vcall_visibility_metadata<'ll, 'tcx>( // Unwrap potential addrspacecast let vtable = find_vtable_behind_cast(vtable); let trait_ref_self = trait_ref.with_self_ty(cx.tcx, ty); - let trait_ref_self = cx.tcx.erase_regions(trait_ref_self); + let trait_ref_self = cx.tcx.erase_and_anonymize_regions(trait_ref_self); let trait_def_id = trait_ref_self.def_id; let trait_vis = cx.tcx.visibility(trait_def_id); diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs index 4e7096da502d..d28ffc4c96e7 100644 --- a/compiler/rustc_codegen_llvm/src/type_of.rs +++ b/compiler/rustc_codegen_llvm/src/type_of.rs @@ -226,7 +226,7 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> { // Make sure lifetimes are erased, to avoid generating distinct LLVM // types for Rust types that only differ in the choice of lifetimes. - let normal_ty = cx.tcx.erase_regions(self.ty); + let normal_ty = cx.tcx.erase_and_anonymize_regions(self.ty); let mut defer = None; let llty = if self.ty != normal_ty { diff --git a/compiler/rustc_const_eval/src/interpret/traits.rs b/compiler/rustc_const_eval/src/interpret/traits.rs index e4b5c82853a2..870f9a396ae2 100644 --- a/compiler/rustc_const_eval/src/interpret/traits.rs +++ b/compiler/rustc_const_eval/src/interpret/traits.rs @@ -23,7 +23,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> InterpResult<'tcx, Pointer>> { trace!("get_vtable(ty={ty:?}, dyn_ty={dyn_ty:?})"); - let (ty, dyn_ty) = self.tcx.erase_regions((ty, dyn_ty)); + let (ty, dyn_ty) = self.tcx.erase_and_anonymize_regions((ty, dyn_ty)); // All vtables must be monomorphic, bail out otherwise. ensure_monomorphic_enough(*self.tcx, ty)?; @@ -53,8 +53,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) -> &'tcx [VtblEntry<'tcx>] { if let Some(trait_) = trait_ { let trait_ref = trait_.with_self_ty(*self.tcx, dyn_ty); - let trait_ref = - self.tcx.erase_regions(self.tcx.instantiate_bound_regions_with_erased(trait_ref)); + let trait_ref = self.tcx.erase_and_anonymize_regions( + self.tcx.instantiate_bound_regions_with_erased(trait_ref), + ); self.tcx.vtable_entries(trait_ref) } else { TyCtxt::COMMON_VTABLE_ENTRIES diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index e6a1f6d8d8bb..22a9446fd4cf 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1047,7 +1047,7 @@ fn check_type_defn<'tcx>( let needs_drop_copy = || { packed && { let ty = tcx.type_of(variant.tail().did).instantiate_identity(); - let ty = tcx.erase_regions(ty); + let ty = tcx.erase_and_anonymize_regions(ty); assert!(!ty.has_infer()); ty.needs_drop(tcx, wfcx.infcx.typing_env(wfcx.param_env)) } diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index f707196c8163..621431ae2343 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -404,7 +404,7 @@ fn emit_orphan_check_error<'tcx>( of_trait.trait_ref.path.span }; - ty = tcx.erase_regions(ty); + ty = tcx.erase_and_anonymize_regions(ty); let is_foreign = !trait_ref.def_id.is_local() && matches!(is_target_ty, IsFirstInputType::No); diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 93b82acf6212..0cf9cb7193f7 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -482,7 +482,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .map(|header| header.trait_ref.instantiate_identity().self_ty()) // We don't care about blanket impls. .filter(|self_ty| !self_ty.has_non_region_param()) - .map(|self_ty| tcx.erase_regions(self_ty).to_string()) + .map(|self_ty| tcx.erase_and_anonymize_regions(self_ty).to_string()) .collect() }; // FIXME: also look at `tcx.generics_of(self.item_def_id()).params` any that diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 3eeb0eac023a..27540fd1a438 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -851,8 +851,8 @@ impl<'a, 'tcx> CastCheck<'tcx> { debug!("check_ptr_ptr_cast m_src={m_src:?} m_dst={m_dst:?}"); // ptr-ptr cast. metadata must match. - let src_kind = fcx.tcx.erase_regions(fcx.pointer_kind(m_src.ty, self.span)?); - let dst_kind = fcx.tcx.erase_regions(fcx.pointer_kind(m_dst.ty, self.span)?); + let src_kind = fcx.tcx.erase_and_anonymize_regions(fcx.pointer_kind(m_src.ty, self.span)?); + let dst_kind = fcx.tcx.erase_and_anonymize_regions(fcx.pointer_kind(m_dst.ty, self.span)?); // We can't cast if target pointer kind is unknown let Some(dst_kind) = dst_kind else { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 84ea2ec0f8a4..d7ddbcc8b53b 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1358,7 +1358,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .infcx .type_implements_trait( clone_trait_def, - [self.tcx.erase_regions(expected_ty)], + [self.tcx.erase_and_anonymize_regions(expected_ty)], self.param_env, ) .must_apply_modulo_regions() diff --git a/compiler/rustc_hir_typeck/src/inline_asm.rs b/compiler/rustc_hir_typeck/src/inline_asm.rs index b59c1752c25a..c0cd23be6909 100644 --- a/compiler/rustc_hir_typeck/src/inline_asm.rs +++ b/compiler/rustc_hir_typeck/src/inline_asm.rs @@ -44,7 +44,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { if ty.has_non_region_infer() { Ty::new_misc_error(self.tcx()) } else { - self.tcx().erase_regions(ty) + self.tcx().erase_and_anonymize_regions(ty) } } diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index d75bc9edab20..8e0180426ef6 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -802,7 +802,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { format!("unexpected inference variable after writeback: {predicate:?}"), ); } else { - let predicate = self.tcx().erase_regions(predicate); + let predicate = self.tcx().erase_and_anonymize_regions(predicate); if cause.has_infer() || cause.has_placeholders() { // We can't use the the obligation cause as it references // information local to this query. @@ -984,7 +984,7 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { // borrowck, and specifically region constraints will be populated during // MIR typeck which is run on the new body. // - // We're not using `tcx.erase_regions` as that also anonymizes bound variables, + // We're not using `tcx.erase_and_anonymize_regions` as that also anonymizes bound variables, // regressing borrowck diagnostics. value = fold_regions(tcx, value, |_, _| tcx.lifetimes.re_erased); diff --git a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs index bfdd282d7e11..f06eb58a371c 100644 --- a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs +++ b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs @@ -76,7 +76,7 @@ pub(super) fn can_match_erased_ty<'tcx>( erased_ty: Ty<'tcx>, ) -> bool { assert!(!outlives_predicate.has_escaping_bound_vars()); - let erased_outlives_predicate = tcx.erase_regions(outlives_predicate); + let erased_outlives_predicate = tcx.erase_and_anonymize_regions(outlives_predicate); let outlives_ty = erased_outlives_predicate.skip_binder().0; if outlives_ty == erased_ty { // pointless micro-optimization diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index 69feecfe30a4..f67b99cb3f84 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -96,7 +96,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { &self, alias_ty: ty::AliasTy<'tcx>, ) -> Vec> { - let erased_alias_ty = self.tcx.erase_regions(alias_ty.to_ty(self.tcx)); + let erased_alias_ty = self.tcx.erase_and_anonymize_regions(alias_ty.to_ty(self.tcx)); self.declared_generic_bounds_from_env_for_erased_ty(erased_alias_ty) } @@ -241,7 +241,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { } let p_ty = p.to_ty(tcx); - let erased_p_ty = self.tcx.erase_regions(p_ty); + let erased_p_ty = self.tcx.erase_and_anonymize_regions(p_ty); (erased_p_ty == erased_ty).then_some(ty::Binder::dummy(ty::OutlivesPredicate(p_ty, r))) })); diff --git a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs index 943fcc0801b1..e1c51ff82990 100644 --- a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs +++ b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs @@ -78,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for DerefIntoDynSupertrait { { // erase regions in self type for better diagnostic presentation let (self_ty, target_principal, supertrait_principal) = - tcx.erase_regions((self_ty, target_principal, supertrait_principal)); + tcx.erase_and_anonymize_regions((self_ty, target_principal, supertrait_principal)); let label2 = tcx .associated_items(item.owner_id) .find_by_ident_and_kind( diff --git a/compiler/rustc_lint/src/for_loops_over_fallibles.rs b/compiler/rustc_lint/src/for_loops_over_fallibles.rs index a56b753bda72..9e1fc5981711 100644 --- a/compiler/rustc_lint/src/for_loops_over_fallibles.rs +++ b/compiler/rustc_lint/src/for_loops_over_fallibles.rs @@ -176,7 +176,7 @@ fn suggest_question_mark<'tcx>( cause, param_env, // Erase any region vids from the type, which may not be resolved - infcx.tcx.erase_regions(ty), + infcx.tcx.erase_and_anonymize_regions(ty), into_iterator_did, ); diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index a72b802eb5d1..88527fa2e6e7 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -935,7 +935,7 @@ impl<'tcx> LateLintPass<'tcx> for VariantSizeDifferences { fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { if let hir::ItemKind::Enum(_, _, ref enum_definition) = it.kind { let t = cx.tcx.type_of(it.owner_id).instantiate_identity(); - let ty = cx.tcx.erase_regions(t); + let ty = cx.tcx.erase_and_anonymize_regions(t); let Ok(layout) = cx.layout_of(ty) else { return }; let Variants::Multiple { tag_encoding: TagEncoding::Direct, tag, variants, .. } = &layout.variants diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index e25f35c59c28..ecf35d9dd6d7 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -41,7 +41,7 @@ impl<'tcx> TyCtxt<'tcx> { let instance = ty::Instance::new_raw(def_id, args); let cid = GlobalId { instance, promoted: None }; let typing_env = ty::TypingEnv::post_analysis(self, def_id); - let inputs = self.erase_regions(typing_env.as_query_input(cid)); + let inputs = self.erase_and_anonymize_regions(typing_env.as_query_input(cid)); self.eval_to_allocation_raw(inputs) } @@ -172,8 +172,9 @@ impl<'tcx> TyCtxt<'tcx> { ) -> EvalToConstValueResult<'tcx> { // Const-eval shouldn't depend on lifetimes at all, so we can erase them, which should // improve caching of queries. - let inputs = - self.erase_regions(typing_env.with_post_analysis_normalized(self).as_query_input(cid)); + let inputs = self.erase_and_anonymize_regions( + typing_env.with_post_analysis_normalized(self).as_query_input(cid), + ); if !span.is_dummy() { // The query doesn't know where it is being invoked, so we need to fix the span. self.at(span).eval_to_const_value_raw(inputs).map_err(|e| e.with_span(span)) @@ -192,8 +193,9 @@ impl<'tcx> TyCtxt<'tcx> { ) -> ConstToValTreeResult<'tcx> { // Const-eval shouldn't depend on lifetimes at all, so we can erase them, which should // improve caching of queries. - let inputs = - self.erase_regions(typing_env.with_post_analysis_normalized(self).as_query_input(cid)); + let inputs = self.erase_and_anonymize_regions( + typing_env.with_post_analysis_normalized(self).as_query_input(cid), + ); debug!(?inputs); let res = if !span.is_dummy() { // The query doesn't know where it is being invoked, so we need to fix the span. diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 8fba218cc71c..2aa32dfa0d82 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -761,9 +761,9 @@ rustc_queries! { } /// Erases regions from `ty` to yield a new type. - /// Normally you would just use `tcx.erase_regions(value)`, + /// Normally you would just use `tcx.erase_and_anonymize_regions(value)`, /// however, which uses this query as a kind of cache. - query erase_regions_ty(ty: Ty<'tcx>) -> Ty<'tcx> { + query erase_and_anonymize_regions_ty(ty: Ty<'tcx>) -> Ty<'tcx> { // This query is not expected to have input -- as a result, it // is not a good candidates for "replay" because it is essentially a // pure function of its input (and hence the expectation is that diff --git a/compiler/rustc_middle/src/ty/abstract_const.rs b/compiler/rustc_middle/src/ty/abstract_const.rs index c9b9ec771b32..463e4c588055 100644 --- a/compiler/rustc_middle/src/ty/abstract_const.rs +++ b/compiler/rustc_middle/src/ty/abstract_const.rs @@ -55,7 +55,7 @@ impl<'tcx> TyCtxt<'tcx> { ty::ConstKind::Unevaluated(uv) => match self.tcx.thir_abstract_const(uv.def) { Err(e) => ty::Const::new_error(self.tcx, e), Ok(Some(bac)) => { - let args = self.tcx.erase_regions(uv.args); + let args = self.tcx.erase_and_anonymize_regions(uv.args); let bac = bac.instantiate(self.tcx, args); return bac.fold_with(self); } diff --git a/compiler/rustc_middle/src/ty/erase_regions.rs b/compiler/rustc_middle/src/ty/erase_regions.rs index f4fead7e9526..74b4adda7fdd 100644 --- a/compiler/rustc_middle/src/ty/erase_regions.rs +++ b/compiler/rustc_middle/src/ty/erase_regions.rs @@ -6,20 +6,20 @@ use crate::ty::{ }; pub(super) fn provide(providers: &mut Providers) { - *providers = Providers { erase_regions_ty, ..*providers }; + *providers = Providers { erase_and_anonymize_regions_ty, ..*providers }; } -fn erase_regions_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { +fn erase_and_anonymize_regions_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { // N.B., use `super_fold_with` here. If we used `fold_with`, it - // could invoke the `erase_regions_ty` query recursively. - ty.super_fold_with(&mut RegionEraserVisitor { tcx }) + // could invoke the `erase_and_anonymize_regions_ty` query recursively. + ty.super_fold_with(&mut RegionEraserAndAnonymizerVisitor { tcx }) } impl<'tcx> TyCtxt<'tcx> { - /// Returns an equivalent value with all free regions removed (note - /// that late-bound regions remain, because they are important for - /// subtyping, but they are anonymized and normalized as well).. - pub fn erase_regions(self, value: T) -> T + /// Returns an equivalent value with all free regions removed and + /// bound regions anonymized. (note that bound regions are important + /// for subtyping and generally type equality so *cannot* be removed) + pub fn erase_and_anonymize_regions(self, value: T) -> T where T: TypeFoldable>, { @@ -27,18 +27,18 @@ impl<'tcx> TyCtxt<'tcx> { if !value.has_type_flags(TypeFlags::HAS_BINDER_VARS | TypeFlags::HAS_FREE_REGIONS) { return value; } - debug!("erase_regions({:?})", value); - let value1 = value.fold_with(&mut RegionEraserVisitor { tcx: self }); - debug!("erase_regions = {:?}", value1); + debug!("erase_and_anonymize_regions({:?})", value); + let value1 = value.fold_with(&mut RegionEraserAndAnonymizerVisitor { tcx: self }); + debug!("erase_and_anonymize_regions = {:?}", value1); value1 } } -struct RegionEraserVisitor<'tcx> { +struct RegionEraserAndAnonymizerVisitor<'tcx> { tcx: TyCtxt<'tcx>, } -impl<'tcx> TypeFolder> for RegionEraserVisitor<'tcx> { +impl<'tcx> TypeFolder> for RegionEraserAndAnonymizerVisitor<'tcx> { fn cx(&self) -> TyCtxt<'tcx> { self.tcx } @@ -49,7 +49,7 @@ impl<'tcx> TypeFolder> for RegionEraserVisitor<'tcx> { } else if ty.has_infer() { ty.super_fold_with(self) } else { - self.tcx.erase_regions_ty(ty) + self.tcx.erase_and_anonymize_regions_ty(ty) } } diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 3a51f79f1216..e76993e0542b 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -544,7 +544,9 @@ impl<'tcx> Instance<'tcx> { // All regions in the result of this query are erased, so it's // fine to erase all of the input regions. - tcx.resolve_instance_raw(tcx.erase_regions(typing_env.as_query_input((def_id, args)))) + tcx.resolve_instance_raw( + tcx.erase_and_anonymize_regions(typing_env.as_query_input((def_id, args))), + ) } pub fn expect_resolve( diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index aed94f9aa04d..2114d080dfa4 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -401,7 +401,10 @@ impl<'tcx> SizeSkeleton<'tcx> { match tail.kind() { ty::Param(_) | ty::Alias(ty::Projection | ty::Inherent, _) => { debug_assert!(tail.has_non_region_param()); - Ok(SizeSkeleton::Pointer { non_zero, tail: tcx.erase_regions(tail) }) + Ok(SizeSkeleton::Pointer { + non_zero, + tail: tcx.erase_and_anonymize_regions(tail), + }) } ty::Error(guar) => { // Fixes ICE #124031 diff --git a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs index f2a4a5a4ecf7..69c1eb9b3454 100644 --- a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs +++ b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs @@ -51,7 +51,7 @@ impl<'tcx> TyCtxt<'tcx> { // Erase first before we do the real query -- this keeps the // cache from being too polluted. - let value = self.erase_regions(value); + let value = self.erase_and_anonymize_regions(value); debug!(?value); if !value.has_aliases() { @@ -83,7 +83,7 @@ impl<'tcx> TyCtxt<'tcx> { // Erase first before we do the real query -- this keeps the // cache from being too polluted. - let value = self.erase_regions(value); + let value = self.erase_and_anonymize_regions(value); debug!(?value); if !value.has_aliases() { diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 74caee7336a5..05c812b6f905 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1436,8 +1436,8 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { // anonymized regions, but the super projections can still // contain named regions. So we erase and anonymize everything // here to compare the types modulo regions below. - let proj = p.tcx().erase_regions(proj); - let super_proj = p.tcx().erase_regions(super_proj); + let proj = p.tcx().erase_and_anonymize_regions(proj); + let super_proj = p.tcx().erase_and_anonymize_regions(super_proj); proj == super_proj }); diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index a7d07adf78f0..5a9e1918e4c6 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -132,9 +132,9 @@ impl<'tcx> TyCtxt<'tcx> { /// context it's calculated within. This is used by the `type_id` intrinsic. pub fn type_id_hash(self, ty: Ty<'tcx>) -> Hash128 { // We want the type_id be independent of the types free regions, so we - // erase them. The erase_regions() call will also anonymize bound - // regions, which is desirable too. - let ty = self.erase_regions(ty); + // erase them. We also want type_id to be independnt of the names of bound + // regions so we anonymize them. + let ty = self.erase_and_anonymize_regions(ty); self.with_stable_hashing_context(|mut hcx| { let mut hasher = StableHasher::new(); @@ -1309,7 +1309,7 @@ impl<'tcx> Ty<'tcx> { debug_assert!(!typing_env.param_env.has_infer()); let query_ty = tcx .try_normalize_erasing_regions(typing_env, query_ty) - .unwrap_or_else(|_| tcx.erase_regions(query_ty)); + .unwrap_or_else(|_| tcx.erase_and_anonymize_regions(query_ty)); tcx.needs_drop_raw(typing_env.as_query_input(query_ty)) } @@ -1346,7 +1346,7 @@ impl<'tcx> Ty<'tcx> { debug_assert!(!typing_env.has_infer()); let query_ty = tcx .try_normalize_erasing_regions(typing_env, query_ty) - .unwrap_or_else(|_| tcx.erase_regions(query_ty)); + .unwrap_or_else(|_| tcx.erase_and_anonymize_regions(query_ty)); tcx.needs_async_drop_raw(typing_env.as_query_input(query_ty)) } diff --git a/compiler/rustc_middle/src/ty/vtable.rs b/compiler/rustc_middle/src/ty/vtable.rs index 6fc19c82342f..e2f09fdcb4b4 100644 --- a/compiler/rustc_middle/src/ty/vtable.rs +++ b/compiler/rustc_middle/src/ty/vtable.rs @@ -89,7 +89,7 @@ pub(super) fn vtable_allocation_provider<'tcx>( let vtable_entries = if let Some(poly_trait_ref) = poly_trait_ref { let trait_ref = poly_trait_ref.with_self_ty(tcx, ty); - let trait_ref = tcx.erase_regions(trait_ref); + let trait_ref = tcx.erase_and_anonymize_regions(trait_ref); tcx.vtable_entries(trait_ref) } else { diff --git a/compiler/rustc_mir_build/src/check_tail_calls.rs b/compiler/rustc_mir_build/src/check_tail_calls.rs index e0cbe8519edd..43a4106297a3 100644 --- a/compiler/rustc_mir_build/src/check_tail_calls.rs +++ b/compiler/rustc_mir_build/src/check_tail_calls.rs @@ -65,9 +65,9 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> { ) }; // While the `caller_sig` does have its regions erased, it does not have its - // binders anonymized. We call `erase_regions` once again to anonymize any binders + // binders anonymized. We call `erase_and_anonymize_regions` once again to anonymize any binders // within the signature, such as in function pointer or `dyn Trait` args. - let caller_sig = self.tcx.erase_regions(caller_sig); + let caller_sig = self.tcx.erase_and_anonymize_regions(caller_sig); let ExprKind::Scope { value, .. } = call.kind else { span_bug!(call.span, "expected scope, found: {call:?}") diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 81b0e21a5f55..0b9bc018a09b 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -792,10 +792,9 @@ impl<'tcx> ThirBuildCx<'tcx> { let ty = self.typeck_results.node_type(anon_const.hir_id); let did = anon_const.def_id.to_def_id(); let typeck_root_def_id = tcx.typeck_root_def_id(did); - let parent_args = tcx.erase_regions(GenericArgs::identity_for_item( - tcx, - typeck_root_def_id, - )); + let parent_args = tcx.erase_and_anonymize_regions( + GenericArgs::identity_for_item(tcx, typeck_root_def_id), + ); let args = InlineConstArgs::new(tcx, InlineConstArgsParts { parent_args, ty }) .args; @@ -831,8 +830,10 @@ impl<'tcx> ThirBuildCx<'tcx> { let ty = self.typeck_results.node_type(anon_const.hir_id); let did = anon_const.def_id.to_def_id(); let typeck_root_def_id = tcx.typeck_root_def_id(did); - let parent_args = - tcx.erase_regions(GenericArgs::identity_for_item(tcx, typeck_root_def_id)); + let parent_args = tcx.erase_and_anonymize_regions(GenericArgs::identity_for_item( + tcx, + typeck_root_def_id, + )); let args = InlineConstArgs::new(tcx, InlineConstArgsParts { parent_args, ty }).args; ExprKind::ConstBlock { did, args } diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index d46c4678bcf7..6316ccf1b8c5 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -105,9 +105,11 @@ impl<'tcx> ConstToPat<'tcx> { // // FIXME: `const_eval_resolve_for_typeck` should probably just modify the env itself // instead of having this logic here - let typing_env = - self.tcx.erase_regions(self.typing_env).with_post_analysis_normalized(self.tcx); - let uv = self.tcx.erase_regions(uv); + let typing_env = self + .tcx + .erase_and_anonymize_regions(self.typing_env) + .with_post_analysis_normalized(self.tcx); + let uv = self.tcx.erase_and_anonymize_regions(uv); // try to resolve e.g. associated constants to their definition on an impl, and then // evaluate the const. diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index 9a00831dc012..bf5ec8f459e6 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -891,7 +891,7 @@ pub fn iter_fields<'tcx>( let field_ty = f_def.ty(tcx, args); let field_ty = tcx .try_normalize_erasing_regions(typing_env, field_ty) - .unwrap_or_else(|_| tcx.erase_regions(field_ty)); + .unwrap_or_else(|_| tcx.erase_and_anonymize_regions(field_ty)); f(variant, f_index.into(), field_ty); } } diff --git a/compiler/rustc_mir_transform/src/add_subtyping_projections.rs b/compiler/rustc_mir_transform/src/add_subtyping_projections.rs index 92ee80eaa353..be4f84d64d03 100644 --- a/compiler/rustc_mir_transform/src/add_subtyping_projections.rs +++ b/compiler/rustc_mir_transform/src/add_subtyping_projections.rs @@ -32,8 +32,8 @@ impl<'a, 'tcx> MutVisitor<'tcx> for SubTypeChecker<'a, 'tcx> { let mut rval_ty = rvalue.ty(self.local_decls, self.tcx); // Not erasing this causes `Free Regions` errors in validator, // when rval is `ReStatic`. - rval_ty = self.tcx.erase_regions(rval_ty); - place_ty = self.tcx.erase_regions(place_ty); + rval_ty = self.tcx.erase_and_anonymize_regions(rval_ty); + place_ty = self.tcx.erase_and_anonymize_regions(place_ty); if place_ty != rval_ty { let temp = self .patcher diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 462ddfa3dd3c..9ea2eb4f25d9 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -875,7 +875,8 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { let mut promoted_operand = |ty, span| { promoted.span = span; promoted.local_decls[RETURN_PLACE] = LocalDecl::new(ty, span); - let args = tcx.erase_regions(GenericArgs::identity_for_item(tcx, def)); + let args = + tcx.erase_and_anonymize_regions(GenericArgs::identity_for_item(tcx, def)); let uneval = mir::UnevaluatedConst { def, args, promoted: Some(next_promoted_index) }; diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index cffeb6f98075..77a3b12d19cb 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1558,7 +1558,7 @@ impl<'v> RootCollector<'_, 'v> { ty::Closure(def_id, args) | ty::Coroutine(def_id, args) | ty::CoroutineClosure(def_id, args) => { - Instance::new_raw(def_id, self.tcx.erase_regions(args)) + Instance::new_raw(def_id, self.tcx.erase_and_anonymize_regions(args)) } _ => unreachable!(), }; diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index 4d9741f1d11c..95a7ec61868d 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -54,7 +54,7 @@ pub(super) fn mangle<'tcx>( // Erase regions because they may not be deterministic when hashed // and should not matter anyhow. - let instance_ty = tcx.erase_regions(instance_ty); + let instance_ty = tcx.erase_and_anonymize_regions(instance_ty); let hash = get_symbol_hash(tcx, instance, instance_ty, instantiating_crate); @@ -422,7 +422,10 @@ impl<'tcx> Printer<'tcx> for LegacySymbolMangler<'tcx> { || &args[..generics.count()] == self .tcx - .erase_regions(ty::GenericArgs::identity_for_item(self.tcx, impl_def_id)) + .erase_and_anonymize_regions(ty::GenericArgs::identity_for_item( + self.tcx, + impl_def_id, + )) .as_slice() { ( diff --git a/compiler/rustc_symbol_mangling/src/test.rs b/compiler/rustc_symbol_mangling/src/test.rs index 0c6d1495e39c..50935e7caf33 100644 --- a/compiler/rustc_symbol_mangling/src/test.rs +++ b/compiler/rustc_symbol_mangling/src/test.rs @@ -58,7 +58,7 @@ impl SymbolNamesTest<'_> { let def_id = def_id.to_def_id(); let instance = Instance::new_raw( def_id, - tcx.erase_regions(GenericArgs::identity_for_item(tcx, def_id)), + tcx.erase_and_anonymize_regions(GenericArgs::identity_for_item(tcx, def_id)), ); let mangled = tcx.symbol_name(instance); tcx.dcx().emit_err(TestOutput { diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 0655c2d5e814..121d3b6b1055 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -329,7 +329,10 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { || &args[..generics.count()] == self .tcx - .erase_regions(ty::GenericArgs::identity_for_item(self.tcx, impl_def_id)) + .erase_and_anonymize_regions(ty::GenericArgs::identity_for_item( + self.tcx, + impl_def_id, + )) .as_slice() { ( diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index d3a17232a40d..d71110521ffa 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -1616,8 +1616,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { && let Some((e, f)) = values.ty() && let TypeError::ArgumentSorts(..) | TypeError::Sorts(_) = terr { - let e = self.tcx.erase_regions(e); - let f = self.tcx.erase_regions(f); + let e = self.tcx.erase_and_anonymize_regions(e); + let f = self.tcx.erase_and_anonymize_regions(f); let mut expected = with_forced_trimmed_paths!(e.sort_string(self.tcx)); let mut found = with_forced_trimmed_paths!(f.sort_string(self.tcx)); if let ObligationCauseCode::Pattern { span, .. } = cause.code() diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs index 40285e5d0e96..e042ce84955e 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs @@ -629,7 +629,11 @@ impl Trait for X { let tcx = self.tcx; // Don't suggest constraining a projection to something containing itself - if self.tcx.erase_regions(values.found).contains(self.tcx.erase_regions(values.expected)) { + if self + .tcx + .erase_and_anonymize_regions(values.found) + .contains(self.tcx.erase_and_anonymize_regions(values.expected)) + { return; } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index bc984f30472d..c82043f02223 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -75,7 +75,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { { if let Some(cause) = self .tcx - .diagnostic_hir_wf_check((tcx.erase_regions(obligation.predicate), *wf_loc)) + .diagnostic_hir_wf_check((tcx.erase_and_anonymize_regions(obligation.predicate), *wf_loc)) { obligation.cause = cause.clone(); span = obligation.cause.span; @@ -2612,8 +2612,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } // Erase regions because layout code doesn't particularly care about regions. - let trait_pred = - self.tcx.erase_regions(self.tcx.instantiate_bound_regions_with_erased(trait_pred)); + let trait_pred = self.tcx.erase_and_anonymize_regions( + self.tcx.instantiate_bound_regions_with_erased(trait_pred), + ); let src_and_dst = rustc_transmute::Types { dst: trait_pred.trait_ref.args.type_at(0), diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index f6dbbeb51cab..cb84d583e6e9 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -2442,7 +2442,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // Look for a type inside the coroutine interior that matches the target type to get // a span. - let target_ty_erased = self.tcx.erase_regions(target_ty); + let target_ty_erased = self.tcx.erase_and_anonymize_regions(target_ty); let ty_matches = |ty| -> bool { // Careful: the regions for types that appear in the // coroutine interior are not generally known, so we @@ -2454,10 +2454,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // interior generally contain "bound regions" to // represent regions that are part of the suspended // coroutine frame. Bound regions are preserved by - // `erase_regions` and so we must also call + // `erase_and_anonymize_regions` and so we must also call // `instantiate_bound_regions_with_erased`. let ty_erased = self.tcx.instantiate_bound_regions_with_erased(ty); - let ty_erased = self.tcx.erase_regions(ty_erased); + let ty_erased = self.tcx.erase_and_anonymize_regions(ty_erased); let eq = ty_erased == target_ty_erased; debug!(?ty_erased, ?target_ty_erased, ?eq); eq diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index 16f444486a64..b6bdf1067a35 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -300,7 +300,7 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< ) -> Result { // Erase regions because we compute layouts in `rustc_transmute`, // which will ICE for region vars. - let (dst, src) = self.tcx.erase_regions((dst, src)); + let (dst, src) = self.tcx.erase_and_anonymize_regions((dst, src)); let Some(assume) = rustc_transmute::Assume::from_const(self.tcx, assume) else { return Err(NoSolution); diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index 4b493c95d593..bcd11d6918d9 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -759,7 +759,7 @@ impl<'tcx> TypeVisitor> for IllegalSelfTypeVisitor<'tcx> { )), ) .map(|trait_ref| { - self.tcx.erase_regions( + self.tcx.erase_and_anonymize_regions( self.tcx.instantiate_bound_regions_with_erased(trait_ref), ) }) diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index a9fb16b8000a..6fefac436994 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -576,7 +576,7 @@ pub fn try_evaluate_const<'tcx>( let args = replace_param_and_infer_args_with_placeholder(tcx, uv.args); let typing_env = infcx - .typing_env(tcx.erase_regions(param_env)) + .typing_env(tcx.erase_and_anonymize_regions(param_env)) .with_post_analysis_normalized(tcx); (args, typing_env) } @@ -589,7 +589,7 @@ pub fn try_evaluate_const<'tcx>( } } else { let typing_env = infcx - .typing_env(tcx.erase_regions(param_env)) + .typing_env(tcx.erase_and_anonymize_regions(param_env)) .with_post_analysis_normalized(tcx); (uv.args, typing_env) } @@ -634,14 +634,14 @@ pub fn try_evaluate_const<'tcx>( } let typing_env = infcx - .typing_env(tcx.erase_regions(param_env)) + .typing_env(tcx.erase_and_anonymize_regions(param_env)) .with_post_analysis_normalized(tcx); (uv.args, typing_env) } }; let uv = ty::UnevaluatedConst::new(uv.def, args); - let erased_uv = tcx.erase_regions(uv); + let erased_uv = tcx.erase_and_anonymize_regions(uv); use rustc_middle::mir::interpret::ErrorHandled; // FIXME: `def_span` will point at the definition of this const; ideally, we'd point at diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index de4045328997..f917b62e461d 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -350,7 +350,7 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>( // Note that we don't care about whether the resume type has any drops since this is // redundant; there is no storage for the resume type, so if it is actually stored // in the interior, we'll already detect the need for a drop by checking the interior. - let typing_env = tcx.erase_regions(typing_env); + let typing_env = tcx.erase_and_anonymize_regions(typing_env); let needs_drop = tcx.mir_coroutine_witnesses(def_id).is_some_and(|witness| { witness.field_tys.iter().any(|field| field.ty.needs_drop(tcx, typing_env)) }); diff --git a/compiler/rustc_traits/src/codegen.rs b/compiler/rustc_traits/src/codegen.rs index 7dd3c59edd05..4b05e2cc3811 100644 --- a/compiler/rustc_traits/src/codegen.rs +++ b/compiler/rustc_traits/src/codegen.rs @@ -73,7 +73,7 @@ pub(crate) fn codegen_select_candidate<'tcx>( } let impl_source = infcx.resolve_vars_if_possible(impl_source); - let impl_source = tcx.erase_regions(impl_source); + let impl_source = tcx.erase_and_anonymize_regions(impl_source); if impl_source.has_non_region_infer() { // Unused generic types or consts on an impl get replaced with inference vars, // but never resolved, causing the return value of a query to contain inference diff --git a/compiler/rustc_traits/src/normalize_erasing_regions.rs b/compiler/rustc_traits/src/normalize_erasing_regions.rs index c1b848a2e79d..c98b56abe9c1 100644 --- a/compiler/rustc_traits/src/normalize_erasing_regions.rs +++ b/compiler/rustc_traits/src/normalize_erasing_regions.rs @@ -41,7 +41,7 @@ fn try_normalize_after_erasing_regions<'tcx, T: TypeFoldable> + Par // fresh `InferCtxt`. If this assert does trigger, it will give // us a test case. debug_assert_eq!(normalized_value, resolved_value); - let erased = infcx.tcx.erase_regions(resolved_value); + let erased = infcx.tcx.erase_and_anonymize_regions(resolved_value); debug_assert!(!erased.has_infer(), "{erased:?}"); Ok(erased) } diff --git a/compiler/rustc_transmute/src/layout/mod.rs b/compiler/rustc_transmute/src/layout/mod.rs index acbce258f39b..4e548ff8fe20 100644 --- a/compiler/rustc_transmute/src/layout/mod.rs +++ b/compiler/rustc_transmute/src/layout/mod.rs @@ -163,7 +163,7 @@ pub mod rustc { ty: Ty<'tcx>, ) -> Result, &'tcx LayoutError<'tcx>> { use rustc_middle::ty::layout::LayoutOf; - let ty = cx.tcx().erase_regions(ty); + let ty = cx.tcx().erase_and_anonymize_regions(ty); cx.layout_of(ty).map(|tl| tl.layout) } } diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index 3f83b4d50aad..406a1b5746f9 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -426,24 +426,25 @@ pub(crate) mod rustc { assert!(def.is_enum()); // Computes the layout of a variant. - let layout_of_variant = - |index, encoding: Option>| -> Result { - let variant_layout = ty_variant(cx, (ty, layout), index); - if variant_layout.is_uninhabited() { - return Ok(Self::uninhabited()); - } - let tag = cx.tcx().tag_for_variant( - cx.typing_env.as_query_input((cx.tcx().erase_regions(ty), index)), - ); - let variant_def = Def::Variant(def.variant(index)); - Self::from_variant( - variant_def, - tag.map(|tag| (tag, index, encoding.unwrap())), - (ty, variant_layout), - layout.size, - cx, - ) - }; + let layout_of_variant = |index, + encoding: Option>| + -> Result { + let variant_layout = ty_variant(cx, (ty, layout), index); + if variant_layout.is_uninhabited() { + return Ok(Self::uninhabited()); + } + let tag = cx.tcx().tag_for_variant( + cx.typing_env.as_query_input((cx.tcx().erase_and_anonymize_regions(ty), index)), + ); + let variant_def = Def::Variant(def.variant(index)); + Self::from_variant( + variant_def, + tag.map(|tag| (tag, index, encoding.unwrap())), + (ty, variant_layout), + layout.size, + cx, + ) + }; match layout.variants() { Variants::Empty => Ok(Self::uninhabited()), @@ -634,7 +635,7 @@ pub(crate) mod rustc { (ty, layout): (Ty<'tcx>, Layout<'tcx>), i: VariantIdx, ) -> Layout<'tcx> { - let ty = cx.tcx().erase_regions(ty); + let ty = cx.tcx().erase_and_anonymize_regions(ty); TyAndLayout { ty, layout }.for_variant(&cx, i).layout } } diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 5c3f7d491a57..e28ebaabc0a1 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -176,7 +176,7 @@ fn resolve_associated_item<'tcx>( args, leaf_def.defining_node, ); - let args = infcx.tcx.erase_regions(args); + let args = infcx.tcx.erase_and_anonymize_regions(args); // HACK: We may have overlapping `dyn Trait` built-in impls and // user-provided blanket impls. Detect that case here, and return @@ -222,7 +222,7 @@ fn resolve_associated_item<'tcx>( return Err(guar); } - let args = tcx.erase_regions(args); + let args = tcx.erase_and_anonymize_regions(args); // We check that the impl item is compatible with the trait item // because otherwise we may ICE in const eval due to type mismatches, @@ -279,7 +279,7 @@ fn resolve_associated_item<'tcx>( assert_eq!(name, sym::clone_from); // Use the default `fn clone_from` from `trait Clone`. - let args = tcx.erase_regions(rcvr_args); + let args = tcx.erase_and_anonymize_regions(rcvr_args); Some(ty::Instance::new_raw(trait_item_id, args)) } } else if tcx.is_lang_item(trait_ref.def_id, LangItem::FnPtrTrait) { @@ -380,7 +380,7 @@ fn resolve_associated_item<'tcx>( } else if tcx.is_lang_item(trait_ref.def_id, LangItem::TransmuteTrait) { let name = tcx.item_name(trait_item_id); assert_eq!(name, sym::transmute); - let args = tcx.erase_regions(rcvr_args); + let args = tcx.erase_and_anonymize_regions(rcvr_args); Some(ty::Instance::new_raw(trait_item_id, args)) } else { Instance::try_resolve_item_for_coroutine(tcx, trait_item_id, trait_id, rcvr_args) diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs index 5104484e9c43..a30aa42a0a49 100644 --- a/compiler/rustc_type_ir/src/visit.rs +++ b/compiler/rustc_type_ir/src/visit.rs @@ -400,7 +400,7 @@ impl std::fmt::Debug for HasTypeFlagsVisitor { // N.B. The only case where this isn't totally true is binders, which also // add `HAS_{RE,TY,CT}_LATE_BOUND` flag depending on the *bound variables* that // are present, regardless of whether those bound variables are used. This -// is important for anonymization of binders in `TyCtxt::erase_regions`. We +// is important for anonymization of binders in `TyCtxt::erase_and_anonymize_regions`. We // specifically detect this case in `visit_binder`. impl TypeVisitor for HasTypeFlagsVisitor { type Result = ControlFlow; diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index 9aa2f3cf0a5b..9645a26a68ae 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -364,7 +364,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { // priority. if let Some(fn_id) = typeck.type_dependent_def_id(hir_id) && let Some(trait_id) = cx.tcx.trait_of_assoc(fn_id) - && let arg_ty = cx.tcx.erase_regions(adjusted_ty) + && let arg_ty = cx.tcx.erase_and_anonymize_regions(adjusted_ty) && let ty::Ref(_, sub_ty, _) = *arg_ty.kind() && let args = typeck.node_args_opt(hir_id).map(|args| &args[1..]).unwrap_or_default() diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs index 010652e1cb90..6ce7f0b1f0bc 100644 --- a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs @@ -140,7 +140,7 @@ fn is_ref_iterable<'tcx>( let res_ty = cx .tcx - .erase_regions(EarlyBinder::bind(req_res_ty).instantiate(cx.tcx, typeck.node_args(call_expr.hir_id))); + .erase_and_anonymize_regions(EarlyBinder::bind(req_res_ty).instantiate(cx.tcx, typeck.node_args(call_expr.hir_id))); let mutbl = if let ty::Ref(_, _, mutbl) = *req_self_ty.kind() { Some(mutbl) } else { diff --git a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs index a42763172f56..809a6728e128 100644 --- a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs +++ b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs @@ -252,7 +252,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { { ( trait_item_id, - FnKind::ImplTraitFn(std::ptr::from_ref(cx.tcx.erase_regions(trait_ref.args)) as usize), + FnKind::ImplTraitFn(std::ptr::from_ref(cx.tcx.erase_and_anonymize_regions(trait_ref.args)) as usize), usize::from(sig.decl.implicit_self.has_implicit_self()), ) } else { diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs index 3842c4eb60e8..6aeb22d41a7d 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs @@ -45,7 +45,7 @@ pub(super) fn check<'tcx>( Applicability::MaybeIncorrect, ); triggered = true; - } else if (cx.tcx.erase_regions(from_ty) != cx.tcx.erase_regions(to_ty)) && !const_context { + } else if (cx.tcx.erase_and_anonymize_regions(from_ty) != cx.tcx.erase_and_anonymize_regions(to_ty)) && !const_context { span_lint_and_then( cx, TRANSMUTE_PTR_TO_PTR, diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs index 26323af31228..3e6aae475ecc 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -12,8 +12,8 @@ pub(super) fn check<'tcx>( from_ty_orig: Ty<'tcx>, to_ty_orig: Ty<'tcx>, ) -> bool { - let mut from_ty = cx.tcx.erase_regions(from_ty_orig); - let mut to_ty = cx.tcx.erase_regions(to_ty_orig); + let mut from_ty = cx.tcx.erase_and_anonymize_regions(from_ty_orig); + let mut to_ty = cx.tcx.erase_and_anonymize_regions(to_ty_orig); while from_ty != to_ty { let reduced_tys = reduce_refs(cx, from_ty, to_ty); diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs index 70ae982a4458..e45f884cfcbc 100644 --- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs +++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs @@ -98,7 +98,7 @@ fn into_iter_bound<'tcx>( if tr.def_id() == into_iter_did { into_iter_span = Some(*span); } else { - let tr = cx.tcx.erase_regions(tr); + let tr = cx.tcx.erase_and_anonymize_regions(tr); if tr.has_escaping_bound_vars() { return None; } diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs index 8e302f9d2ad1..9f77a1c4d9b6 100644 --- a/src/tools/clippy/clippy_utils/src/ty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs @@ -285,7 +285,7 @@ pub fn implements_trait_with_env_from_iter<'tcx>( let _ = tcx.hir_body_owner_kind(callee_id); } - let ty = tcx.erase_regions(ty); + let ty = tcx.erase_and_anonymize_regions(ty); if ty.has_escaping_bound_vars() { return false; } From 332d8d62356a1be77bfe6412ab5ed3fa33ae9af6 Mon Sep 17 00:00:00 2001 From: Boxy Date: Fri, 22 Aug 2025 12:31:34 +0100 Subject: [PATCH 306/682] Driveby fixes --- compiler/rustc_hir_typeck/src/writeback.rs | 4 ++-- compiler/rustc_middle/src/ty/util.rs | 5 ++--- compiler/rustc_mir_build/src/check_tail_calls.rs | 2 +- .../src/traits/query/dropck_outlives.rs | 2 ++ compiler/rustc_transmute/src/layout/tree.rs | 4 +--- compiler/rustc_type_ir/src/visit.rs | 2 +- 6 files changed, 9 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 8e0180426ef6..90301d1b391a 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -984,8 +984,8 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { // borrowck, and specifically region constraints will be populated during // MIR typeck which is run on the new body. // - // We're not using `tcx.erase_and_anonymize_regions` as that also anonymizes bound variables, - // regressing borrowck diagnostics. + // We're not using `tcx.erase_and_anonymize_regions` as that also + // anonymizes bound variables, regressing borrowck diagnostics. value = fold_regions(tcx, value, |_, _| tcx.lifetimes.re_erased); // Normalize consts in writeback, because GCE doesn't normalize eagerly. diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 5a9e1918e4c6..3b14e0256db2 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -131,9 +131,8 @@ impl<'tcx> TyCtxt<'tcx> { /// Creates a hash of the type `Ty` which will be the same no matter what crate /// context it's calculated within. This is used by the `type_id` intrinsic. pub fn type_id_hash(self, ty: Ty<'tcx>) -> Hash128 { - // We want the type_id be independent of the types free regions, so we - // erase them. We also want type_id to be independnt of the names of bound - // regions so we anonymize them. + // We don't have region information, so we erase all free regions. Equal types + // must have the same `TypeId`, so we must anonymize all bound regions as well. let ty = self.erase_and_anonymize_regions(ty); self.with_stable_hashing_context(|mut hcx| { diff --git a/compiler/rustc_mir_build/src/check_tail_calls.rs b/compiler/rustc_mir_build/src/check_tail_calls.rs index 43a4106297a3..d40c77d145f4 100644 --- a/compiler/rustc_mir_build/src/check_tail_calls.rs +++ b/compiler/rustc_mir_build/src/check_tail_calls.rs @@ -64,7 +64,7 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> { "`become` outside of functions should have been disallowed by hir_typeck" ) }; - // While the `caller_sig` does have its regions erased, it does not have its + // While the `caller_sig` does have its free regions erased, it does not have its // binders anonymized. We call `erase_and_anonymize_regions` once again to anonymize any binders // within the signature, such as in function pointer or `dyn Trait` args. let caller_sig = self.tcx.erase_and_anonymize_regions(caller_sig); diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index f917b62e461d..945ca7c37758 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -350,6 +350,8 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>( // Note that we don't care about whether the resume type has any drops since this is // redundant; there is no storage for the resume type, so if it is actually stored // in the interior, we'll already detect the need for a drop by checking the interior. + // + // FIXME(@lcnr): Why do we erase regions in the env here? Seems odd let typing_env = tcx.erase_and_anonymize_regions(typing_env); let needs_drop = tcx.mir_coroutine_witnesses(def_id).is_some_and(|witness| { witness.field_tys.iter().any(|field| field.ty.needs_drop(tcx, typing_env)) diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index 406a1b5746f9..d7ea26a2ab1d 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -426,9 +426,7 @@ pub(crate) mod rustc { assert!(def.is_enum()); // Computes the layout of a variant. - let layout_of_variant = |index, - encoding: Option>| - -> Result { + let layout_of_variant = |index, encoding: Option<_>| -> Result { let variant_layout = ty_variant(cx, (ty, layout), index); if variant_layout.is_uninhabited() { return Ok(Self::uninhabited()); diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs index a30aa42a0a49..6e62e1031a96 100644 --- a/compiler/rustc_type_ir/src/visit.rs +++ b/compiler/rustc_type_ir/src/visit.rs @@ -398,7 +398,7 @@ impl std::fmt::Debug for HasTypeFlagsVisitor { // looks, particular for `Ty`/`Predicate` where it's just a field access. // // N.B. The only case where this isn't totally true is binders, which also -// add `HAS_{RE,TY,CT}_LATE_BOUND` flag depending on the *bound variables* that +// add `HAS_BINDER_VARS` flag depending on the *bound variables* that // are present, regardless of whether those bound variables are used. This // is important for anonymization of binders in `TyCtxt::erase_and_anonymize_regions`. We // specifically detect this case in `visit_binder`. From 8cde544b737892e12a757b1c0399781f44080730 Mon Sep 17 00:00:00 2001 From: Boxy Date: Sun, 24 Aug 2025 17:50:13 +0100 Subject: [PATCH 307/682] Add assertion --- compiler/rustc_symbol_mangling/src/v0.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 121d3b6b1055..1605b4958ba3 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -342,7 +342,7 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { ) } else { assert!( - !args.has_non_region_param(), + !args.has_non_region_param() && !args.has_free_regions(), "should not be mangling partially substituted \ polymorphic instance: {impl_def_id:?} {args:?}" ); From 0df12d7b0a4c8f9e05319cc761858841351d7db4 Mon Sep 17 00:00:00 2001 From: Boxy Date: Thu, 21 Aug 2025 16:50:54 +0100 Subject: [PATCH 308/682] erase_regions to erase_and_anonymize_regions --- clippy_lints/src/dereference.rs | 2 +- clippy_lints/src/loops/explicit_iter_loop.rs | 2 +- clippy_lints/src/only_used_in_recursion.rs | 2 +- clippy_lints/src/transmute/transmute_ref_to_ref.rs | 2 +- clippy_lints/src/transmute/transmute_undefined_repr.rs | 4 ++-- clippy_lints/src/useless_conversion.rs | 2 +- clippy_utils/src/ty/mod.rs | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 9aa2f3cf0a5b..9645a26a68ae 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -364,7 +364,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { // priority. if let Some(fn_id) = typeck.type_dependent_def_id(hir_id) && let Some(trait_id) = cx.tcx.trait_of_assoc(fn_id) - && let arg_ty = cx.tcx.erase_regions(adjusted_ty) + && let arg_ty = cx.tcx.erase_and_anonymize_regions(adjusted_ty) && let ty::Ref(_, sub_ty, _) = *arg_ty.kind() && let args = typeck.node_args_opt(hir_id).map(|args| &args[1..]).unwrap_or_default() diff --git a/clippy_lints/src/loops/explicit_iter_loop.rs b/clippy_lints/src/loops/explicit_iter_loop.rs index 010652e1cb90..6ce7f0b1f0bc 100644 --- a/clippy_lints/src/loops/explicit_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_iter_loop.rs @@ -140,7 +140,7 @@ fn is_ref_iterable<'tcx>( let res_ty = cx .tcx - .erase_regions(EarlyBinder::bind(req_res_ty).instantiate(cx.tcx, typeck.node_args(call_expr.hir_id))); + .erase_and_anonymize_regions(EarlyBinder::bind(req_res_ty).instantiate(cx.tcx, typeck.node_args(call_expr.hir_id))); let mutbl = if let ty::Ref(_, _, mutbl) = *req_self_ty.kind() { Some(mutbl) } else { diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index a42763172f56..809a6728e128 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -252,7 +252,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { { ( trait_item_id, - FnKind::ImplTraitFn(std::ptr::from_ref(cx.tcx.erase_regions(trait_ref.args)) as usize), + FnKind::ImplTraitFn(std::ptr::from_ref(cx.tcx.erase_and_anonymize_regions(trait_ref.args)) as usize), usize::from(sig.decl.implicit_self.has_implicit_self()), ) } else { diff --git a/clippy_lints/src/transmute/transmute_ref_to_ref.rs b/clippy_lints/src/transmute/transmute_ref_to_ref.rs index 3842c4eb60e8..6aeb22d41a7d 100644 --- a/clippy_lints/src/transmute/transmute_ref_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ref_to_ref.rs @@ -45,7 +45,7 @@ pub(super) fn check<'tcx>( Applicability::MaybeIncorrect, ); triggered = true; - } else if (cx.tcx.erase_regions(from_ty) != cx.tcx.erase_regions(to_ty)) && !const_context { + } else if (cx.tcx.erase_and_anonymize_regions(from_ty) != cx.tcx.erase_and_anonymize_regions(to_ty)) && !const_context { span_lint_and_then( cx, TRANSMUTE_PTR_TO_PTR, diff --git a/clippy_lints/src/transmute/transmute_undefined_repr.rs b/clippy_lints/src/transmute/transmute_undefined_repr.rs index 26323af31228..3e6aae475ecc 100644 --- a/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -12,8 +12,8 @@ pub(super) fn check<'tcx>( from_ty_orig: Ty<'tcx>, to_ty_orig: Ty<'tcx>, ) -> bool { - let mut from_ty = cx.tcx.erase_regions(from_ty_orig); - let mut to_ty = cx.tcx.erase_regions(to_ty_orig); + let mut from_ty = cx.tcx.erase_and_anonymize_regions(from_ty_orig); + let mut to_ty = cx.tcx.erase_and_anonymize_regions(to_ty_orig); while from_ty != to_ty { let reduced_tys = reduce_refs(cx, from_ty, to_ty); diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 70ae982a4458..e45f884cfcbc 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -98,7 +98,7 @@ fn into_iter_bound<'tcx>( if tr.def_id() == into_iter_did { into_iter_span = Some(*span); } else { - let tr = cx.tcx.erase_regions(tr); + let tr = cx.tcx.erase_and_anonymize_regions(tr); if tr.has_escaping_bound_vars() { return None; } diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 8e302f9d2ad1..9f77a1c4d9b6 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -285,7 +285,7 @@ pub fn implements_trait_with_env_from_iter<'tcx>( let _ = tcx.hir_body_owner_kind(callee_id); } - let ty = tcx.erase_regions(ty); + let ty = tcx.erase_and_anonymize_regions(ty); if ty.has_escaping_bound_vars() { return false; } From 0fad52401e39eba86e8246834a5317b9c8d77652 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Wed, 13 Aug 2025 22:54:10 +0000 Subject: [PATCH 309/682] WIP switch inference table to next-solver --- .../crates/hir-ty/src/consteval_nextsolver.rs | 6 +- .../crates/hir-ty/src/infer/closure.rs | 2 +- .../crates/hir-ty/src/infer/coerce.rs | 62 +- .../crates/hir-ty/src/infer/expr.rs | 32 +- .../crates/hir-ty/src/infer/path.rs | 12 +- .../crates/hir-ty/src/infer/unify.rs | 591 ++++++----- .../rust-analyzer/crates/hir-ty/src/lib.rs | 16 +- .../crates/hir-ty/src/method_resolution.rs | 201 ++-- .../infer/canonical/canonicalizer.rs | 785 ++++++++++++++ .../src/next_solver/infer/canonical/mod.rs | 1 + .../hir-ty/src/next_solver/infer/mod.rs | 54 +- .../src/next_solver/infer/snapshot/mod.rs | 4 +- .../crates/hir-ty/src/next_solver/mapping.rs | 965 +++++++++++++++++- .../crates/hir-ty/src/next_solver/solver.rs | 4 +- .../rust-analyzer/crates/hir-ty/src/traits.rs | 67 +- .../rust-analyzer/crates/hir/src/attrs.rs | 4 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 12 +- 17 files changed, 2292 insertions(+), 526 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs index 4f95c9a13acd..470033593154 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs @@ -27,7 +27,7 @@ use crate::{ next_solver::{ Const, ConstBytes, ConstKind, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, ParamConst, SolverDefId, Ty, ValueConst, - mapping::{ChalkToNextSolver, convert_args_for_result, convert_binder_to_early_binder}, + mapping::{ChalkToNextSolver, convert_binder_to_early_binder}, }, }; @@ -145,7 +145,7 @@ pub fn try_const_usize<'db>(db: &'db dyn HirDatabase, c: Const<'db>) -> Option GeneralConstId::StaticId(id), _ => unreachable!(), }; - let subst = convert_args_for_result(interner, unevaluated_const.args.as_slice()); + let subst = ChalkToNextSolver::from_nextsolver(unevaluated_const.args, interner); let ec = db.const_eval(c, subst, None).ok()?.to_nextsolver(interner); try_const_usize(db, ec) } @@ -168,7 +168,7 @@ pub fn try_const_isize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option< SolverDefId::StaticId(id) => GeneralConstId::StaticId(id), _ => unreachable!(), }; - let subst = convert_args_for_result(interner, unevaluated_const.args.as_slice()); + let subst = ChalkToNextSolver::from_nextsolver(unevaluated_const.args, interner); let ec = db.const_eval(c, subst, None).ok()?.to_nextsolver(interner); try_const_isize(db, &ec) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 9ef046afdb3d..493652b76401 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -568,7 +568,7 @@ impl InferenceContext<'_> { let supplied_sig = self.supplied_sig_of_closure(body, ret_type, arg_types, closure_kind); let snapshot = self.table.snapshot(); - if !self.table.unify(&expected_sig.substitution, &supplied_sig.expected_sig.substitution) { + if !self.table.unify::<_, crate::next_solver::GenericArgs<'_>>(&expected_sig.substitution.0, &supplied_sig.expected_sig.substitution.0) { self.table.rollback_to(snapshot); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index 631a91bac481..b47834f0c749 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -7,22 +7,20 @@ use std::iter; -use chalk_ir::{BoundVar, Goal, Mutability, TyKind, TyVariableKind, cast::Cast}; -use hir_def::{hir::ExprId, lang_item::LangItem}; +use chalk_ir::{BoundVar, Mutability, TyKind, TyVariableKind, cast::Cast}; +use hir_def::{ + hir::ExprId, + lang_item::LangItem, +}; +use rustc_type_ir::solve::Certainty; use stdx::always; use triomphe::Arc; use crate::{ - Canonical, DomainGoal, FnAbi, FnPointer, FnSig, InEnvironment, Interner, Lifetime, - Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, - autoderef::{Autoderef, AutoderefKind}, - db::HirDatabase, - infer::{ + autoderef::{Autoderef, AutoderefKind}, db::HirDatabase, infer::{ Adjust, Adjustment, AutoBorrow, InferOk, InferenceContext, OverloadedDeref, PointerCast, TypeError, TypeMismatch, - }, - traits::NextTraitSolveResult, - utils::ClosureSubst, + }, utils::ClosureSubst, Canonical, FnAbi, FnPointer, FnSig, Goal, InEnvironment, Interner, Lifetime, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt }; use super::unify::InferenceTable; @@ -42,7 +40,7 @@ fn simple(kind: Adjust) -> impl FnOnce(Ty) -> Vec { fn success( adj: Vec, target: Ty, - goals: Vec>>, + goals: Vec>, ) -> CoerceResult { Ok(InferOk { goals, value: (adj, target) }) } @@ -304,7 +302,7 @@ impl InferenceTable<'_> { fn coerce_inner(&mut self, from_ty: Ty, to_ty: &Ty, coerce_never: CoerceNever) -> CoerceResult { if from_ty.is_never() { if let TyKind::InferenceVar(tv, TyVariableKind::General) = to_ty.kind(Interner) { - self.set_diverging(*tv, true); + self.set_diverging(*tv, TyVariableKind::General, true); } if coerce_never == CoerceNever::Yes { // Subtle: If we are coercing from `!` to `?T`, where `?T` is an unbound @@ -707,41 +705,15 @@ impl InferenceTable<'_> { b.push(coerce_from).push(to_ty.clone()).build() }; - let goal: InEnvironment = - InEnvironment::new(&self.trait_env.env, coerce_unsized_tref.cast(Interner)); + let goal: Goal = coerce_unsized_tref.cast(Interner); - let canonicalized = self.canonicalize_with_free_vars(goal); - - // FIXME: rustc's coerce_unsized is more specialized -- it only tries to - // solve `CoerceUnsized` and `Unsize` goals at this point and leaves the - // rest for later. Also, there's some logic about sized type variables. - // Need to find out in what cases this is necessary - let solution = self.db.trait_solve( - krate, - self.trait_env.block, - canonicalized.value.clone().cast(Interner), - ); - - match solution { - // FIXME: this is a weaker guarantee than Chalk's `Guidance::Unique` - // was. Chalk's unique guidance at least guarantees that the real solution - // is some "subset" of the solutions matching the guidance, but the - // substs for `Certainty::No` don't have that same guarantee (I think). - NextTraitSolveResult::Certain(v) => { - canonicalized.apply_solution( - self, - Canonical { - binders: v.binders, - // FIXME handle constraints - value: v.value.subst, - }, - ); + self.commit_if_ok(|table| { + match table.solve_obligation(goal) { + Ok(Certainty::Yes) => Ok(()), + Ok(Certainty::Maybe(_)) => Ok(()), + Err(_) => Err(TypeError), } - // ...so, should think about how to get some actually get some guidance here - NextTraitSolveResult::Uncertain(..) | NextTraitSolveResult::NoSolution => { - return Err(TypeError); - } - } + })?; let unsize = Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target: to_ty.clone() }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 261c02386822..e39f3ab75f16 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -23,28 +23,9 @@ use stdx::always; use syntax::ast::RangeOp; use crate::{ - Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, DeclContext, - DeclOrigin, IncorrectGenericsLenKind, Interner, Rawness, Scalar, Substitution, - TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, - autoderef::{Autoderef, builtin_deref, deref_by_trait}, - consteval, - generics::generics, - infer::{ - BreakableKind, - coerce::{CoerceMany, CoerceNever, CoercionCause}, - find_continuable, - pat::contains_explicit_ref_binding, - }, - lang_items::lang_items_for_bin_op, - lower::{ - LifetimeElisionKind, ParamLoweringMode, lower_to_chalk_mutability, - path::{GenericArgsLowerer, TypeLikeConst, substs_from_args_and_bindings}, - }, - mapping::{ToChalk, from_chalk}, - method_resolution::{self, VisibleFromModule}, - primitive::{self, UintTy}, - static_lifetime, to_chalk_trait_id, - traits::FnTrait, + autoderef::{builtin_deref, deref_by_trait, Autoderef}, consteval, generics::generics, infer::{ + coerce::{CoerceMany, CoerceNever, CoercionCause}, find_continuable, pat::contains_explicit_ref_binding, BreakableKind + }, lang_items::lang_items_for_bin_op, lower::{lower_to_chalk_mutability, path::{substs_from_args_and_bindings, GenericArgsLowerer, TypeLikeConst}, ParamLoweringMode}, mapping::{from_chalk, ToChalk}, method_resolution::{self, VisibleFromModule}, next_solver::mapping::ChalkToNextSolver, primitive::{self, UintTy}, static_lifetime, to_chalk_trait_id, traits::FnTrait, Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, DeclContext, DeclOrigin, IncorrectGenericsLenKind, Interner, LifetimeElisionKind, Rawness, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind }; use super::{ @@ -826,7 +807,7 @@ impl InferenceContext<'_> { let index_ty = self.infer_expr(*index, &Expectation::none(), ExprIsRead::Yes); if let Some(index_trait) = self.resolve_lang_trait(LangItem::Index) { - let canonicalized = self.canonicalize(base_ty.clone()); + let canonicalized = ChalkToNextSolver::from_nextsolver(self.canonicalize(base_ty.clone().to_nextsolver(self.table.interner)), self.table.interner); let receiver_adjustments = method_resolution::resolve_indexing_op( self.db, self.table.trait_env.clone(), @@ -932,6 +913,7 @@ impl InferenceContext<'_> { } None => { let expected_ty = expected.to_option(&mut self.table); + tracing::debug!(?expected_ty); let opt_ty = match expected_ty.as_ref().map(|it| it.kind(Interner)) { Some(TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_))) => expected_ty, Some(TyKind::Scalar(Scalar::Char)) => { @@ -1678,7 +1660,7 @@ impl InferenceContext<'_> { None => { // no field found, lets attempt to resolve it like a function so that IDE things // work out while people are typing - let canonicalized_receiver = self.canonicalize(receiver_ty.clone()); + let canonicalized_receiver = self.canonicalize(receiver_ty.clone().to_nextsolver(self.table.interner)); let resolved = method_resolution::lookup_method( self.db, &canonicalized_receiver, @@ -1824,7 +1806,7 @@ impl InferenceContext<'_> { expected: &Expectation, ) -> Ty { let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none(), ExprIsRead::Yes); - let canonicalized_receiver = self.canonicalize(receiver_ty.clone()); + let canonicalized_receiver = self.canonicalize(receiver_ty.clone().to_nextsolver(self.table.interner)); let resolved = method_resolution::lookup_method( self.db, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index bc8648ecdd94..9f3cd2b8fb00 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -10,15 +10,7 @@ use hir_expand::name::Name; use stdx::never; use crate::{ - InferenceDiagnostic, Interner, Substitution, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, - TyKind, ValueTyDefId, - builder::ParamKind, - consteval, error_lifetime, - generics::generics, - infer::diagnostics::InferenceTyLoweringContext as TyLoweringContext, - lower::LifetimeElisionKind, - method_resolution::{self, VisibleFromModule}, - to_chalk_trait_id, + builder::ParamKind, consteval, error_lifetime, generics::generics, infer::diagnostics::InferenceTyLoweringContext as TyLoweringContext, method_resolution::{self, VisibleFromModule}, next_solver::mapping::ChalkToNextSolver, to_chalk_trait_id, InferenceDiagnostic, Interner, LifetimeElisionKind, Substitution, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, ValueTyDefId }; use super::{ExprOrPatId, InferenceContext, InferenceTyDiagnosticSource}; @@ -322,7 +314,7 @@ impl InferenceContext<'_> { return Some(result); } - let canonical_ty = self.canonicalize(ty.clone()); + let canonical_ty = self.canonicalize(ty.clone().to_nextsolver(self.table.interner)); let mut not_visible = None; let res = method_resolution::iterate_method_candidates( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index bb4782bd4194..3745d2c98fd9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -3,35 +3,27 @@ use std::{fmt, mem}; use chalk_ir::{ - CanonicalVarKind, FloatTy, IntTy, TyVariableKind, UniverseIndex, cast::Cast, - fold::TypeFoldable, interner::HasInterner, zip::Zip, + cast::Cast, fold::TypeFoldable, interner::HasInterner, CanonicalVarKind, FloatTy, IntTy, TyVariableKind, }; -use chalk_solve::infer::ParameterEnaVariableExt; use either::Either; -use ena::unify::UnifyKey; use hir_def::{AdtId, lang_item::LangItem}; use hir_expand::name::Name; use intern::sym; -use rustc_hash::FxHashMap; +use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_next_trait_solver::solve::HasChanged; +use rustc_type_ir::{inherent::Span, relate::{solver_relating::RelateExt, Relate}, solve::{Certainty, NoSolution}, FloatVid, IntVid, TyVid}; use smallvec::SmallVec; use triomphe::Arc; use super::{InferOk, InferResult, InferenceContext, TypeError}; use crate::{ - AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue, DebruijnIndex, DomainGoal, - GenericArg, GenericArgData, Goal, GoalData, InEnvironment, InferenceVar, Interner, Lifetime, - OpaqueTyId, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Substitution, TraitEnvironment, - TraitRef, Ty, TyBuilder, TyExt, TyKind, VariableKind, WhereClause, - consteval::unknown_const, - db::HirDatabase, - fold_generic_args, fold_tys_and_consts, to_chalk_trait_id, - traits::{FnTrait, NextTraitSolveResult}, + consteval::unknown_const, db::HirDatabase, fold_generic_args, fold_tys_and_consts, next_solver::{infer::{canonical::canonicalizer::OriginalQueryValues, snapshot::CombinedSnapshot, DbInternerInferExt, InferCtxt}, mapping::{ChalkToNextSolver, InferenceVarExt}, DbInterner, ParamEnvAnd, SolverDefIds}, to_chalk_trait_id, traits::{next_trait_solve, next_trait_solve_canonical, next_trait_solve_in_ctxt, FnTrait, NextTraitSolveResult}, AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue, DebruijnIndex, DomainGoal, GenericArg, GenericArgData, Goal, GoalData, InEnvironment, InferenceVar, Interner, Lifetime, OpaqueTyId, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, VariableKind, WhereClause }; -impl InferenceContext<'_> { - pub(super) fn canonicalize(&mut self, t: T) -> Canonical +impl<'db> InferenceContext<'db> { + pub(super) fn canonicalize(&mut self, t: T) -> rustc_type_ir::Canonical, T> where - T: TypeFoldable + HasInterner, + T: rustc_type_ir::TypeFoldable>, { self.table.canonicalize(t) } @@ -42,11 +34,11 @@ impl InferenceContext<'_> { ) -> SmallVec<[WhereClause; 4]> { self.table.resolve_obligations_as_possible(); - let root = self.table.var_unification_table.inference_var_root(self_ty); + let root = InferenceVar::from_vid(self.table.infer_ctxt.root_var(self_ty.to_vid())); let pending_obligations = mem::take(&mut self.table.pending_obligations); let obligations = pending_obligations .iter() - .filter_map(|obligation| match obligation.value.value.goal.data(Interner) { + .filter_map(|obligation| match obligation.goal.data(Interner) { GoalData::DomainGoal(DomainGoal::Holds(clause)) => { let ty = match clause { WhereClause::AliasEq(AliasEq { @@ -59,18 +51,9 @@ impl InferenceContext<'_> { WhereClause::TypeOutlives(to) => to.ty.clone(), _ => return None, }; - - let uncanonical = - chalk_ir::Substitute::apply(&obligation.free_vars, ty, Interner); - if matches!( - self.resolve_ty_shallow(&uncanonical).kind(Interner), - TyKind::InferenceVar(iv, TyVariableKind::General) if *iv == root, - ) { - Some(chalk_ir::Substitute::apply( - &obligation.free_vars, - clause.clone(), - Interner, - )) + let ty = self.resolve_ty_shallow(&ty); + if matches!(ty.kind(Interner), TyKind::InferenceVar(iv, TyVariableKind::General) if *iv == root) { + Some(clause.clone()) } else { None } @@ -229,32 +212,31 @@ type ChalkInferenceTable = chalk_solve::infer::InferenceTable; #[derive(Clone)] pub(crate) struct InferenceTable<'a> { pub(crate) db: &'a dyn HirDatabase, + pub(crate) interner: DbInterner<'a>, pub(crate) trait_env: Arc, pub(crate) tait_coercion_table: Option>, - var_unification_table: ChalkInferenceTable, - type_variable_table: SmallVec<[TypeVariableFlags; 16]>, - pending_obligations: Vec>>, - /// Double buffer used in [`Self::resolve_obligations_as_possible`] to cut down on - /// temporary allocations. - resolve_obligations_buffer: Vec>>, + infer_ctxt: InferCtxt<'a>, + diverging_tys: FxHashSet, + pending_obligations: Vec>, } pub(crate) struct InferenceTableSnapshot { - var_table_snapshot: chalk_solve::infer::InferenceSnapshot, - type_variable_table: SmallVec<[TypeVariableFlags; 16]>, - pending_obligations: Vec>>, + ctxt_snapshot: CombinedSnapshot, + diverging_tys: FxHashSet, + pending_obligations: Vec>, } impl<'a> InferenceTable<'a> { pub(crate) fn new(db: &'a dyn HirDatabase, trait_env: Arc) -> Self { + let interner = DbInterner::new_with(db, Some(trait_env.krate), trait_env.block); InferenceTable { db, + interner, trait_env, tait_coercion_table: None, - var_unification_table: ChalkInferenceTable::new(), - type_variable_table: SmallVec::new(), + infer_ctxt: interner.infer_ctxt().build(rustc_type_ir::TypingMode::Analysis { defining_opaque_types_and_generators: SolverDefIds::new_from_iter(interner, []) }), + diverging_tys: FxHashSet::default(), pending_obligations: Vec::new(), - resolve_obligations_buffer: Vec::new(), } } @@ -265,29 +247,43 @@ impl<'a> InferenceTable<'a> { /// marked as diverging if necessary, so that resolving them gives the right /// result. pub(super) fn propagate_diverging_flag(&mut self) { - for i in 0..self.type_variable_table.len() { - if !self.type_variable_table[i].contains(TypeVariableFlags::DIVERGING) { - continue; + let mut new_tys = FxHashSet::default(); + for ty in self.diverging_tys.iter() { + match ty.kind(Interner) { + TyKind::InferenceVar(var, kind) => { + match kind { + TyVariableKind::General => { + let root = InferenceVar::from(self.infer_ctxt.root_var(TyVid::from_u32(var.index())).as_u32()); + if root.index() != var.index() { + new_tys.insert(TyKind::InferenceVar(root, *kind).intern(Interner)); + } + } + TyVariableKind::Integer => { + let root = InferenceVar::from(self.infer_ctxt.inner.borrow_mut().int_unification_table().find(IntVid::from_usize(var.index() as usize)).as_u32()); + if root.index() != var.index() { + new_tys.insert(TyKind::InferenceVar(root, *kind).intern(Interner)); + } + } + TyVariableKind::Float => { + let root = InferenceVar::from(self.infer_ctxt.inner.borrow_mut().float_unification_table().find(FloatVid::from_usize(var.index() as usize)).as_u32()); + if root.index() != var.index() { + new_tys.insert(TyKind::InferenceVar(root, *kind).intern(Interner)); + } + } + } + } + _ => {} } - let v = InferenceVar::from(i as u32); - let root = self.var_unification_table.inference_var_root(v); - self.modify_type_variable_flag(root, |f| { - *f |= TypeVariableFlags::DIVERGING; - }); } + self.diverging_tys.extend(new_tys.into_iter()); } - pub(super) fn set_diverging(&mut self, iv: InferenceVar, diverging: bool) { - self.modify_type_variable_flag(iv, |f| { - f.set(TypeVariableFlags::DIVERGING, diverging); - }); + pub(super) fn set_diverging(&mut self, iv: InferenceVar, kind: TyVariableKind, diverging: bool) { + self.diverging_tys.insert(TyKind::InferenceVar(iv, kind).intern(Interner)); } fn fallback_value(&self, iv: InferenceVar, kind: TyVariableKind) -> Ty { - let is_diverging = self - .type_variable_table - .get(iv.index() as usize) - .is_some_and(|data| data.contains(TypeVariableFlags::DIVERGING)); + let is_diverging = self.diverging_tys.contains(&TyKind::InferenceVar(iv, kind).intern(Interner)); if is_diverging { return TyKind::Never.intern(Interner); } @@ -299,30 +295,27 @@ impl<'a> InferenceTable<'a> { .intern(Interner) } - pub(crate) fn canonicalize_with_free_vars(&mut self, t: T) -> Canonicalized + pub(crate) fn canonicalize_with_free_vars(&mut self, t: ParamEnvAnd<'a, T>) -> (rustc_type_ir::Canonical, ParamEnvAnd<'a, T>>, OriginalQueryValues<'a>) where - T: TypeFoldable + HasInterner, + T: rustc_type_ir::TypeFoldable>, { // try to resolve obligations before canonicalizing, since this might // result in new knowledge about variables self.resolve_obligations_as_possible(); - let result = self.var_unification_table.canonicalize(Interner, t); - let free_vars = result - .free_vars - .into_iter() - .map(|free_var| free_var.to_generic_arg(Interner)) - .collect(); - Canonicalized { value: result.quantified, free_vars } + + let mut orig_values = OriginalQueryValues::default(); + let result = self.infer_ctxt.canonicalize_query(t, &mut orig_values); + (result.canonical, orig_values) } - pub(crate) fn canonicalize(&mut self, t: T) -> Canonical + pub(crate) fn canonicalize(&mut self, t: T) -> rustc_type_ir::Canonical, T> where - T: TypeFoldable + HasInterner, + T: rustc_type_ir::TypeFoldable>, { // try to resolve obligations before canonicalizing, since this might // result in new knowledge about variables self.resolve_obligations_as_possible(); - self.var_unification_table.canonicalize(Interner, t).quantified + self.infer_ctxt.canonicalize_response(t) } /// Recurses through the given type, normalizing associated types mentioned @@ -348,6 +341,7 @@ impl<'a> InferenceTable<'a> { self.resolve_ty_shallow(&ty) } TyKind::AssociatedType(id, subst) => { + return Either::Left(self.resolve_ty_shallow(&ty)); if ty.data(Interner).flags.intersects( chalk_ir::TypeFlags::HAS_TY_INFER | chalk_ir::TypeFlags::HAS_CT_INFER, @@ -370,21 +364,24 @@ impl<'a> InferenceTable<'a> { )), ); let in_env = InEnvironment::new(&self.trait_env.env, goal); + let goal = in_env.to_nextsolver(self.interner); + let goal = ParamEnvAnd { param_env: goal.param_env, value: goal.predicate }; - let canonicalized = { - let result = - self.var_unification_table.canonicalize(Interner, in_env); - let free_vars = result - .free_vars - .into_iter() - .map(|free_var| free_var.to_generic_arg(Interner)) - .collect(); - Canonicalized { value: result.quantified, free_vars } + let (canonical_goal, _orig_values) = { + let mut orig_values = OriginalQueryValues::default(); + let result = self.infer_ctxt.canonicalize_query(goal, &mut orig_values); + (result.canonical, orig_values) }; - let solution = self.db.trait_solve( + let canonical_goal = rustc_type_ir::Canonical { + max_universe: canonical_goal.max_universe, + variables: canonical_goal.variables, + value: crate::next_solver::Goal { param_env: canonical_goal.value.param_env, predicate: canonical_goal.value.value }, + }; + let solution = next_trait_solve_canonical( + self.db, self.trait_env.krate, self.trait_env.block, - canonicalized.value.clone(), + canonical_goal.clone(), ); if let NextTraitSolveResult::Certain(canonical_subst) = solution { // This is not great :) But let's just assert this for now and come back to it later. @@ -512,38 +509,27 @@ impl<'a> InferenceTable<'a> { var } - fn modify_type_variable_flag(&mut self, var: InferenceVar, cb: F) - where - F: FnOnce(&mut TypeVariableFlags), - { - let idx = var.index() as usize; - if self.type_variable_table.len() <= idx { - self.extend_type_variable_table(idx); - } - if let Some(f) = self.type_variable_table.get_mut(idx) { - cb(f); - } - } - fn extend_type_variable_table(&mut self, to_index: usize) { - let count = to_index - self.type_variable_table.len() + 1; - self.type_variable_table.extend(std::iter::repeat_n(TypeVariableFlags::default(), count)); - } - fn new_var(&mut self, kind: TyVariableKind, diverging: bool) -> Ty { - let var = self.var_unification_table.new_variable(UniverseIndex::ROOT); - // Chalk might have created some type variables for its own purposes that we don't know about... - self.extend_type_variable_table(var.index() as usize); - assert_eq!(var.index() as usize, self.type_variable_table.len() - 1); - let flags = self.type_variable_table.get_mut(var.index() as usize).unwrap(); + let var = match kind { + TyVariableKind::General => { + let var = self.infer_ctxt.next_ty_vid(); + InferenceVar::from(var.as_u32()) + } + TyVariableKind::Integer => { + let var = self.infer_ctxt.next_int_vid(); + InferenceVar::from(var.as_u32()) + } + TyVariableKind::Float => { + let var = self.infer_ctxt.next_float_vid(); + InferenceVar::from(var.as_u32()) + } + }; + + let ty = var.to_ty(Interner, kind); if diverging { - *flags |= TypeVariableFlags::DIVERGING; + self.diverging_tys.insert(ty.clone()); } - if matches!(kind, TyVariableKind::Integer) { - *flags |= TypeVariableFlags::INTEGER; - } else if matches!(kind, TyVariableKind::Float) { - *flags |= TypeVariableFlags::FLOAT; - } - var.to_ty_with_kind(Interner, kind) + ty } pub(crate) fn new_type_var(&mut self) -> Ty { @@ -563,12 +549,14 @@ impl<'a> InferenceTable<'a> { } pub(crate) fn new_const_var(&mut self, ty: Ty) -> Const { - let var = self.var_unification_table.new_variable(UniverseIndex::ROOT); + let var = self.infer_ctxt.next_const_vid(); + let var = InferenceVar::from(var.as_u32()); var.to_const(Interner, ty) } pub(crate) fn new_lifetime_var(&mut self) -> Lifetime { - let var = self.var_unification_table.new_variable(UniverseIndex::ROOT); + let var = self.infer_ctxt.next_region_vid(); + let var = InferenceVar::from(var.as_u32()); var.to_lifetime(Interner) } @@ -580,16 +568,16 @@ impl<'a> InferenceTable<'a> { where T: HasInterner + TypeFoldable, { - self.resolve_with_fallback_inner(&mut Vec::new(), t, &fallback) + self.resolve_with_fallback_inner(t, &fallback) } pub(crate) fn fresh_subst(&mut self, binders: &[CanonicalVarKind]) -> Substitution { Substitution::from_iter( Interner, - binders.iter().map(|kind| { - let param_infer_var = - kind.map_ref(|&ui| self.var_unification_table.new_variable(ui)); - param_infer_var.to_generic_arg(Interner) + binders.iter().map(|kind| match &kind.kind { + chalk_ir::VariableKind::Ty(ty_variable_kind) => self.new_var(*ty_variable_kind, false).cast(Interner), + chalk_ir::VariableKind::Lifetime => self.new_lifetime_var().cast(Interner), + chalk_ir::VariableKind::Const(ty) => self.new_const_var(ty.clone()).cast(Interner), }), ) } @@ -601,16 +589,23 @@ impl<'a> InferenceTable<'a> { let subst = self.fresh_subst(canonical.binders.as_slice(Interner)); subst.apply(canonical.value, Interner) } + + pub(crate) fn instantiate_canonical_ns(&mut self, canonical: rustc_type_ir::Canonical, T>) -> T + where + T: rustc_type_ir::TypeFoldable>, + { + self.infer_ctxt.instantiate_canonical(&canonical).0 + } fn resolve_with_fallback_inner( &mut self, - var_stack: &mut Vec, t: T, fallback: &dyn Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg, ) -> T where T: HasInterner + TypeFoldable, { + let mut var_stack = &mut vec![]; t.fold_with( &mut resolve::Resolver { table: self, var_stack, fallback }, DebruijnIndex::INNERMOST, @@ -639,29 +634,26 @@ impl<'a> InferenceTable<'a> { let int_fallback = TyKind::Scalar(Scalar::Int(IntTy::I32)).intern(Interner); let float_fallback = TyKind::Scalar(Scalar::Float(FloatTy::F64)).intern(Interner); - let scalar_vars: Vec<_> = self - .type_variable_table - .iter() - .enumerate() - .filter_map(|(index, flags)| { - let kind = if flags.contains(TypeVariableFlags::INTEGER) { - TyVariableKind::Integer - } else if flags.contains(TypeVariableFlags::FLOAT) { - TyVariableKind::Float - } else { - return None; - }; - - // FIXME: This is not really the nicest way to get `InferenceVar`s. Can we get them - // without directly constructing them from `index`? - let var = InferenceVar::from(index as u32).to_ty(Interner, kind); - Some(var) - }) - .collect(); - - for var in scalar_vars { + let int_vars = self.infer_ctxt.inner.borrow_mut().int_unification_table().len(); + for v in 0..int_vars { + let var = InferenceVar::from(v as u32).to_ty(Interner, TyVariableKind::Integer); let maybe_resolved = self.resolve_ty_shallow(&var); if let TyKind::InferenceVar(_, kind) = maybe_resolved.kind(Interner) { + // I don't think we can ever unify these vars with float vars, but keep this here for now + let fallback = match kind { + TyVariableKind::Integer => &int_fallback, + TyVariableKind::Float => &float_fallback, + TyVariableKind::General => unreachable!(), + }; + self.unify(&var, fallback); + } + } + let float_vars = self.infer_ctxt.inner.borrow_mut().float_unification_table().len(); + for v in 0..float_vars { + let var = InferenceVar::from(v as u32).to_ty(Interner, TyVariableKind::Integer); + let maybe_resolved = self.resolve_ty_shallow(&var); + if let TyKind::InferenceVar(_, kind) = maybe_resolved.kind(Interner) { + // I don't think we can ever unify these vars with float vars, but keep this here for now let fallback = match kind { TyVariableKind::Integer => &int_fallback, TyVariableKind::Float => &float_fallback, @@ -673,7 +665,7 @@ impl<'a> InferenceTable<'a> { } /// Unify two relatable values (e.g. `Ty`) and register new trait goals that arise from that. - pub(crate) fn unify>(&mut self, ty1: &T, ty2: &T) -> bool { + pub(crate) fn unify, U: Relate>>(&mut self, ty1: &T, ty2: &T) -> bool { let result = match self.try_unify(ty1, ty2) { Ok(r) => r, Err(_) => return false, @@ -683,58 +675,65 @@ impl<'a> InferenceTable<'a> { } /// Unify two relatable values (e.g. `Ty`) and check whether trait goals which arise from that could be fulfilled - pub(crate) fn unify_deeply>(&mut self, ty1: &T, ty2: &T) -> bool { + pub(crate) fn unify_deeply, U: Relate>>(&mut self, ty1: &T, ty2: &T) -> bool { let result = match self.try_unify(ty1, ty2) { Ok(r) => r, Err(_) => return false, }; result.goals.iter().all(|goal| { - let canonicalized = self.canonicalize_with_free_vars(goal.clone()); - self.try_resolve_obligation(&canonicalized).certain() + let goal = goal.to_nextsolver(self.interner); + match next_trait_solve_in_ctxt(&self.infer_ctxt, goal) { + Ok((_, Certainty::Yes)) => true, + _ => false, + } }) } /// Unify two relatable values (e.g. `Ty`) and return new trait goals arising from it, so the /// caller needs to deal with them. - pub(crate) fn try_unify>( + pub(crate) fn try_unify, U: Relate>>( &mut self, t1: &T, t2: &T, ) -> InferResult<()> { - match self.var_unification_table.relate( - Interner, - &self.db, - &self.trait_env.env, - chalk_ir::Variance::Invariant, - t1, - t2, - ) { - Ok(result) => Ok(InferOk { goals: result.goals, value: () }), - Err(chalk_ir::NoSolution) => Err(TypeError), + let param_env = self.trait_env.env.to_nextsolver(self.interner); + let lhs = t1.to_nextsolver(self.interner); + let rhs = t2.to_nextsolver(self.interner); + let variance = rustc_type_ir::Variance::Invariant; + let span = crate::next_solver::Span::dummy(); + match self.infer_ctxt.relate(param_env, lhs, variance, rhs, span) { + Ok(res) => { + let goals = res.into_iter().map(|g| ChalkToNextSolver::from_nextsolver(g, self.interner)).collect(); + Ok(InferOk { goals, value: () }) + } + Err(_) => { + Err(TypeError) + } } } /// If `ty` is a type variable with known type, returns that type; /// otherwise, return ty. + #[tracing::instrument(skip(self))] pub(crate) fn resolve_ty_shallow(&mut self, ty: &Ty) -> Ty { if !ty.data(Interner).flags.intersects(chalk_ir::TypeFlags::HAS_FREE_LOCAL_NAMES) { return ty.clone(); } self.resolve_obligations_as_possible(); - self.var_unification_table.normalize_ty_shallow(Interner, ty).unwrap_or_else(|| ty.clone()) + ChalkToNextSolver::from_nextsolver(self.infer_ctxt.resolve_vars_if_possible(ty.to_nextsolver(self.interner)), self.interner) } pub(crate) fn snapshot(&mut self) -> InferenceTableSnapshot { - let var_table_snapshot = self.var_unification_table.snapshot(); - let type_variable_table = self.type_variable_table.clone(); + let ctxt_snapshot = self.infer_ctxt.start_snapshot(); + let diverging_tys = self.diverging_tys.clone(); let pending_obligations = self.pending_obligations.clone(); - InferenceTableSnapshot { var_table_snapshot, pending_obligations, type_variable_table } + InferenceTableSnapshot {ctxt_snapshot, pending_obligations, diverging_tys } } #[tracing::instrument(skip_all)] pub(crate) fn rollback_to(&mut self, snapshot: InferenceTableSnapshot) { - self.var_unification_table.rollback_to(snapshot.var_table_snapshot); - self.type_variable_table = snapshot.type_variable_table; + self.infer_ctxt.rollback_to(snapshot.ctxt_snapshot); + self.diverging_tys = snapshot.diverging_tys; self.pending_obligations = snapshot.pending_obligations; } @@ -746,15 +745,39 @@ impl<'a> InferenceTable<'a> { result } + pub(crate) fn commit_if_ok(&mut self, f: impl FnOnce(&mut InferenceTable<'_>) -> Result) -> Result { + let snapshot = self.snapshot(); + let result = f(self); + match result { + Ok(_) => {} + Err(_) => { + self.rollback_to(snapshot); + } + } + result + } + /// Checks an obligation without registering it. Useful mostly to check /// whether a trait *might* be implemented before deciding to 'lock in' the /// choice (during e.g. method resolution or deref). #[tracing::instrument(level = "debug", skip(self))] pub(crate) fn try_obligation(&mut self, goal: Goal) -> NextTraitSolveResult { let in_env = InEnvironment::new(&self.trait_env.env, goal); - let canonicalized = self.canonicalize(in_env); + let canonicalized = self.canonicalize(in_env.to_nextsolver(self.interner)); - self.db.trait_solve(self.trait_env.krate, self.trait_env.block, canonicalized) + next_trait_solve_canonical(self.db, self.trait_env.krate, self.trait_env.block, canonicalized) + } + + #[tracing::instrument(level = "debug", skip(self))] + pub(crate) fn solve_obligation(&mut self, goal: Goal) -> Result { + let goal = InEnvironment::new(&self.trait_env.env, goal); + let Some(goal) = self.unify_opaque_instead_of_solve(goal) else { + return Ok(Certainty::Yes); + }; + + let goal = goal.to_nextsolver(self.interner); + let result = next_trait_solve_in_ctxt(&self.infer_ctxt, goal); + result.map(|m| m.1) } pub(crate) fn register_obligation(&mut self, goal: Goal) { @@ -762,8 +785,8 @@ impl<'a> InferenceTable<'a> { self.register_obligation_in_env(in_env) } - #[tracing::instrument(level = "debug", skip(self))] - fn register_obligation_in_env(&mut self, goal: InEnvironment) { + // If this goal is an `AliasEq` for an opaque type, just unify instead of trying to solve (since the next-solver is lazy) + fn unify_opaque_instead_of_solve(&mut self, goal: InEnvironment) -> Option> { match goal.goal.data(Interner) { chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds( chalk_ir::WhereClause::AliasEq(chalk_ir::AliasEq { alias, ty }), @@ -779,7 +802,7 @@ impl<'a> InferenceTable<'a> { .intern(Interner), ty, ) { - return; + return None; } } _ => {} @@ -788,20 +811,20 @@ impl<'a> InferenceTable<'a> { } _ => {} } - let canonicalized = { - let result = self.var_unification_table.canonicalize(Interner, goal); - let free_vars = result - .free_vars - .into_iter() - .map(|free_var| free_var.to_generic_arg(Interner)) - .collect(); - Canonicalized { value: result.quantified, free_vars } - }; - tracing::debug!(?canonicalized); - let solution = self.try_resolve_obligation(&canonicalized); - tracing::debug!(?solution); - if solution.uncertain() { - self.pending_obligations.push(canonicalized); + Some(goal) + } + + #[tracing::instrument(level = "debug", skip(self))] + fn register_obligation_in_env(&mut self, goal: InEnvironment) { + let Some(goal) = self.unify_opaque_instead_of_solve(goal) else { return }; + let result = next_trait_solve_in_ctxt(&self.infer_ctxt, goal.to_nextsolver(self.interner)); + tracing::debug!(?result); + match result { + Ok((_, Certainty::Yes)) => {} + Err(rustc_type_ir::solve::NoSolution) => {} + Ok((_, Certainty::Maybe(_))) => { + self.pending_obligations.push(goal); + } } } @@ -812,28 +835,35 @@ impl<'a> InferenceTable<'a> { pub(crate) fn resolve_obligations_as_possible(&mut self) { let _span = tracing::info_span!("resolve_obligations_as_possible").entered(); let mut changed = true; - let mut obligations = mem::take(&mut self.resolve_obligations_buffer); while mem::take(&mut changed) { - mem::swap(&mut self.pending_obligations, &mut obligations); + let mut obligations = mem::take(&mut self.pending_obligations); - for canonicalized in obligations.drain(..) { - tracing::debug!(obligation = ?canonicalized); - if !self.check_changed(&canonicalized) { - tracing::debug!("not changed"); - self.pending_obligations.push(canonicalized); + for goal in obligations.drain(..) { + tracing::debug!(obligation = ?goal); + + let Some(goal) = self.unify_opaque_instead_of_solve(goal) else { + changed = true; continue; + }; + + let result = next_trait_solve_in_ctxt(&self.infer_ctxt, goal.to_nextsolver(self.interner)); + let (has_changed, certainty) = match result { + Ok(result) => result, + Err(_) => { + continue; + } + }; + + if matches!(has_changed, HasChanged::Yes) { + changed = true; + } + + match certainty { + Certainty::Yes => {} + Certainty::Maybe(_) => self.pending_obligations.push(goal), } - changed = true; - let uncanonical = chalk_ir::Substitute::apply( - &canonicalized.free_vars, - canonicalized.value.value, - Interner, - ); - self.register_obligation_in_env(uncanonical); } } - self.resolve_obligations_buffer = obligations; - self.resolve_obligations_buffer.clear(); } pub(crate) fn fudge_inference>( @@ -904,32 +934,13 @@ impl<'a> InferenceTable<'a> { .fold_with(&mut VarFudger { table: self, highest_known_var }, DebruijnIndex::INNERMOST) } - /// This checks whether any of the free variables in the `canonicalized` - /// have changed (either been unified with another variable, or with a - /// value). If this is not the case, we don't need to try to solve the goal - /// again -- it'll give the same result as last time. - fn check_changed(&mut self, canonicalized: &Canonicalized>) -> bool { - canonicalized.free_vars.iter().any(|var| { - let iv = match var.data(Interner) { - GenericArgData::Ty(ty) => ty.inference_var(Interner), - GenericArgData::Lifetime(lt) => lt.inference_var(Interner), - GenericArgData::Const(c) => c.inference_var(Interner), - } - .expect("free var is not inference var"); - if self.var_unification_table.probe_var(iv).is_some() { - return true; - } - let root = self.var_unification_table.inference_var_root(iv); - iv != root - }) - } - #[tracing::instrument(level = "debug", skip(self))] fn try_resolve_obligation( &mut self, canonicalized: &Canonicalized>, ) -> NextTraitSolveResult { - let solution = self.db.trait_solve( + let solution = next_trait_solve( + self.db, self.trait_env.krate, self.trait_env.block, canonicalized.value.clone(), @@ -1014,33 +1025,15 @@ impl<'a> InferenceTable<'a> { .fill_with_unknown() .build(); - let trait_env = self.trait_env.env.clone(); - let obligation = InEnvironment { - goal: trait_ref.clone().cast(Interner), - environment: trait_env.clone(), - }; - let canonical = self.canonicalize(obligation.clone()); - if !self - .db - .trait_solve(krate, self.trait_env.block, canonical.cast(Interner)) - .no_solution() - { - self.register_obligation(obligation.goal); + let goal: Goal = trait_ref.clone().cast(Interner); + if !self.try_obligation(goal.clone()).no_solution() { + self.register_obligation(goal); let return_ty = self.normalize_projection_ty(projection); for &fn_x in subtraits { let fn_x_trait = fn_x.get_id(self.db, krate)?; trait_ref.trait_id = to_chalk_trait_id(fn_x_trait); - let obligation: chalk_ir::InEnvironment> = - InEnvironment { - goal: trait_ref.clone().cast(Interner), - environment: trait_env.clone(), - }; - let canonical = self.canonicalize(obligation.clone()); - if !self - .db - .trait_solve(krate, self.trait_env.block, canonical.cast(Interner)) - .no_solution() - { + let goal = trait_ref.clone().cast(Interner); + if !self.try_obligation(goal).no_solution() { return Some((fn_x, arg_tys, return_ty)); } } @@ -1165,20 +1158,26 @@ impl<'a> InferenceTable<'a> { impl fmt::Debug for InferenceTable<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("InferenceTable").field("num_vars", &self.type_variable_table.len()).finish() + f.debug_struct("InferenceTable").finish() } } mod resolve { use super::InferenceTable; use crate::{ - ConcreteConst, Const, ConstData, ConstScalar, ConstValue, DebruijnIndex, GenericArg, - InferenceVar, Interner, Lifetime, Ty, TyVariableKind, VariableKind, + next_solver::mapping::ChalkToNextSolver, ConcreteConst, Const, ConstData, ConstScalar, ConstValue, DebruijnIndex, GenericArg, InferenceVar, Interner, Lifetime, Ty, TyVariableKind, VariableKind }; use chalk_ir::{ cast::Cast, fold::{TypeFoldable, TypeFolder}, }; + use rustc_type_ir::{FloatVid, IntVid, TyVid}; + + #[derive(Copy, Clone, PartialEq, Eq)] + pub(super) enum VarKind { + Ty(TyVariableKind), + Const, + } #[derive(chalk_derive::FallibleTypeFolder)] #[has_interner(Interner)] @@ -1188,7 +1187,7 @@ mod resolve { F: Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg, > { pub(super) table: &'a mut InferenceTable<'b>, - pub(super) var_stack: &'a mut Vec, + pub(super) var_stack: &'a mut Vec<(InferenceVar, VarKind)>, pub(super) fallback: F, } impl TypeFolder for Resolver<'_, '_, F> @@ -1209,25 +1208,79 @@ mod resolve { kind: TyVariableKind, outer_binder: DebruijnIndex, ) -> Ty { - let var = self.table.var_unification_table.inference_var_root(var); - if self.var_stack.contains(&var) { - // recursive type - let default = self.table.fallback_value(var, kind).cast(Interner); - return (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder) - .assert_ty_ref(Interner) - .clone(); - } - if let Some(known_ty) = self.table.var_unification_table.probe_var(var) { - // known_ty may contain other variables that are known by now - self.var_stack.push(var); - let result = known_ty.fold_with(self, outer_binder); - self.var_stack.pop(); - result.assert_ty_ref(Interner).clone() - } else { - let default = self.table.fallback_value(var, kind).cast(Interner); - (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder) - .assert_ty_ref(Interner) - .clone() + match kind { + TyVariableKind::General => { + let vid = self.table.infer_ctxt.root_var(TyVid::from(var.index())); + let var = InferenceVar::from(vid.as_u32()); + if self.var_stack.contains(&(var, VarKind::Ty(kind))) { + // recursive type + let default = self.table.fallback_value(var, kind).cast(Interner); + return (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder) + .assert_ty_ref(Interner) + .clone(); + } + if let Ok(known_ty) = self.table.infer_ctxt.probe_ty_var(vid) { + let known_ty: Ty = ChalkToNextSolver::from_nextsolver(known_ty, self.table.interner); + // known_ty may contain other variables that are known by now + self.var_stack.push((var, VarKind::Ty(kind))); + let result = known_ty.fold_with(self, outer_binder); + self.var_stack.pop(); + result + } else { + let default = self.table.fallback_value(var, kind).cast(Interner); + (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder) + .assert_ty_ref(Interner) + .clone() + } + } + TyVariableKind::Integer => { + let vid = self.table.infer_ctxt.inner.borrow_mut().int_unification_table().find(IntVid::from(var.index())); + let var = InferenceVar::from(vid.as_u32()); + if self.var_stack.contains(&(var, VarKind::Ty(kind))) { + // recursive type + let default = self.table.fallback_value(var, kind).cast(Interner); + return (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder) + .assert_ty_ref(Interner) + .clone(); + } + if let Some(known_ty) = self.table.infer_ctxt.resolve_int_var(vid) { + let known_ty: Ty = ChalkToNextSolver::from_nextsolver(known_ty, self.table.interner); + // known_ty may contain other variables that are known by now + self.var_stack.push((var, VarKind::Ty(kind))); + let result = known_ty.fold_with(self, outer_binder); + self.var_stack.pop(); + result + } else { + let default = self.table.fallback_value(var, kind).cast(Interner); + (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder) + .assert_ty_ref(Interner) + .clone() + } + } + TyVariableKind::Float => { + let vid = self.table.infer_ctxt.inner.borrow_mut().float_unification_table().find(FloatVid::from(var.index())); + let var = InferenceVar::from(vid.as_u32()); + if self.var_stack.contains(&(var, VarKind::Ty(kind))) { + // recursive type + let default = self.table.fallback_value(var, kind).cast(Interner); + return (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder) + .assert_ty_ref(Interner) + .clone(); + } + if let Some(known_ty) = self.table.infer_ctxt.resolve_float_var(vid) { + let known_ty: Ty = ChalkToNextSolver::from_nextsolver(known_ty, self.table.interner); + // known_ty may contain other variables that are known by now + self.var_stack.push((var, VarKind::Ty(kind))); + let result = known_ty.fold_with(self, outer_binder); + self.var_stack.pop(); + result + } else { + let default = self.table.fallback_value(var, kind).cast(Interner); + (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder) + .assert_ty_ref(Interner) + .clone() + } + } } } @@ -1237,25 +1290,27 @@ mod resolve { var: InferenceVar, outer_binder: DebruijnIndex, ) -> Const { - let var = self.table.var_unification_table.inference_var_root(var); + let vid = self.table.infer_ctxt.root_const_var(rustc_type_ir::ConstVid::from_u32(var.index())); + let var = InferenceVar::from(vid.as_u32()); let default = ConstData { ty: ty.clone(), value: ConstValue::Concrete(ConcreteConst { interned: ConstScalar::Unknown }), } .intern(Interner) .cast(Interner); - if self.var_stack.contains(&var) { + if self.var_stack.contains(&(var, VarKind::Const)) { // recursive return (self.fallback)(var, VariableKind::Const(ty), default, outer_binder) .assert_const_ref(Interner) .clone(); } - if let Some(known_ty) = self.table.var_unification_table.probe_var(var) { + if let Ok(known_const) = self.table.infer_ctxt.probe_const_var(vid) { + let known_const: Const = ChalkToNextSolver::from_nextsolver(known_const, self.table.interner); // known_ty may contain other variables that are known by now - self.var_stack.push(var); - let result = known_ty.fold_with(self, outer_binder); + self.var_stack.push((var, VarKind::Const)); + let result = known_const.fold_with(self, outer_binder); self.var_stack.pop(); - result.assert_const_ref(Interner).clone() + result } else { (self.fallback)(var, VariableKind::Const(ty), default, outer_binder) .assert_const_ref(Interner) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index c16bbb7b9922..323ea951a914 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -957,23 +957,13 @@ pub fn callable_sig_from_fn_trait( ) .build(); - let block = trait_env.block; - let trait_env = trait_env.env.clone(); - let obligation = - InEnvironment { goal: trait_ref.clone().cast(Interner), environment: trait_env.clone() }; - let canonical = table.canonicalize(obligation.clone()); - if !db.trait_solve(krate, block, canonical.cast(Interner)).no_solution() { - table.register_obligation(obligation.goal); + if !table.try_obligation(trait_ref.clone().cast(Interner)).no_solution() { + table.register_obligation(trait_ref.clone().cast(Interner)); let return_ty = table.normalize_projection_ty(projection); for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] { let fn_x_trait = fn_x.get_id(db, krate)?; trait_ref.trait_id = to_chalk_trait_id(fn_x_trait); - let obligation: chalk_ir::InEnvironment> = InEnvironment { - goal: trait_ref.clone().cast(Interner), - environment: trait_env.clone(), - }; - let canonical = table.canonicalize(obligation.clone()); - if !db.trait_solve(krate, block, canonical.cast(Interner)).no_solution() { + if !table.try_obligation(trait_ref.clone().cast(Interner)).no_solution() { let ret_ty = table.resolve_completely(return_ty); let args_ty = table.resolve_completely(args_ty); let params = args_ty diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index f0be352fdd15..ee223059d1e5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -16,25 +16,13 @@ use hir_def::{ use hir_expand::name::Name; use intern::sym; use rustc_hash::{FxHashMap, FxHashSet}; -use rustc_type_ir::inherent::{IntoKind, SliceLike}; +use rustc_type_ir::inherent::{IntoKind, SliceLike, Ty as _}; use smallvec::{SmallVec, smallvec}; use stdx::never; use triomphe::Arc; use crate::{ - AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, GenericArgData, - Goal, InEnvironment, Interner, Mutability, Scalar, Substitution, TraitEnvironment, TraitRef, - TraitRefExt, Ty, TyBuilder, TyExt, TyKind, TyVariableKind, VariableKind, WhereClause, - autoderef::{self, AutoderefKind}, - db::HirDatabase, - error_lifetime, from_chalk_trait_id, from_foreign_def_id, - infer::{Adjust, Adjustment, OverloadedDeref, PointerCast, unify::InferenceTable}, - lang_items::is_box, - next_solver::SolverDefId, - primitive::{FloatTy, IntTy, UintTy}, - to_chalk_trait_id, - traits::NextTraitSolveResult, - utils::all_super_traits, + autoderef::{self, AutoderefKind}, db::HirDatabase, from_chalk_trait_id, from_foreign_def_id, infer::{unify::InferenceTable, Adjust, Adjustment, OverloadedDeref, PointerCast}, lang_items::is_box, next_solver::{mapping::ChalkToNextSolver, SolverDefId}, primitive::{FloatTy, IntTy, UintTy}, to_chalk_trait_id, traits::{next_trait_solve_canonical}, utils::all_super_traits, AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, GenericArgData, Goal, InEnvironment, Interner, Mutability, Scalar, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, TyVariableKind, VariableKind, WhereClause }; /// This is used as a key for indexing impls. @@ -533,9 +521,9 @@ pub fn def_crates(db: &dyn HirDatabase, ty: &Ty, cur_crate: Crate) -> Option, +pub(crate) fn lookup_method<'db>( + db: &'db dyn HirDatabase, + ty: &crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, env: Arc, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, @@ -697,9 +685,9 @@ impl ReceiverAdjustments { // This would be nicer if it just returned an iterator, but that runs into // lifetime problems, because we need to borrow temp `CrateImplDefs`. // FIXME add a context type here? -pub(crate) fn iterate_method_candidates( - ty: &Canonical, - db: &dyn HirDatabase, +pub(crate) fn iterate_method_candidates<'db, T>( + ty: &crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, + db: &'db dyn HirDatabase, env: Arc, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, @@ -1046,9 +1034,9 @@ pub fn check_orphan_rules(db: &dyn HirDatabase, impl_: ImplId) -> bool { is_not_orphan } -pub fn iterate_path_candidates( - ty: &Canonical, - db: &dyn HirDatabase, +pub fn iterate_path_candidates<'db>( + ty: &crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, + db: &'db dyn HirDatabase, env: Arc, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, @@ -1068,9 +1056,9 @@ pub fn iterate_path_candidates( ) } -pub fn iterate_method_candidates_dyn( - ty: &Canonical, - db: &dyn HirDatabase, +pub fn iterate_method_candidates_dyn<'db>( + ty: &crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, + db: &'db dyn HirDatabase, env: Arc, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, @@ -1108,7 +1096,7 @@ pub fn iterate_method_candidates_dyn( // types*. let mut table = InferenceTable::new(db, env); - let ty = table.instantiate_canonical(ty.clone()); + let ty = table.instantiate_canonical_ns(ty.clone()); let deref_chain = autoderef_method_receiver(&mut table, ty); deref_chain.into_iter().try_for_each(|(receiver_ty, adj)| { @@ -1139,20 +1127,22 @@ pub fn iterate_method_candidates_dyn( } #[tracing::instrument(skip_all, fields(name = ?name))] -fn iterate_method_candidates_with_autoref( - table: &mut InferenceTable<'_>, - receiver_ty: Canonical, +fn iterate_method_candidates_with_autoref<'db>( + table: &mut InferenceTable<'db>, + receiver_ty: crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, first_adjustment: ReceiverAdjustments, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, name: Option<&Name>, callback: &mut dyn MethodCandidateCallback, ) -> ControlFlow<()> { - if receiver_ty.value.is_general_var(Interner, &receiver_ty.binders) { + if matches!(receiver_ty.value.kind(), rustc_type_ir::TyKind::Bound(..)) { // don't try to resolve methods on unknown types return ControlFlow::Continue(()); } + let interner = table.interner; + let mut iterate_method_candidates_by_receiver = move |receiver_ty, first_adjustment| { iterate_method_candidates_by_receiver( table, @@ -1166,7 +1156,11 @@ fn iterate_method_candidates_with_autoref( }; let mut maybe_reborrowed = first_adjustment.clone(); - if let Some((_, _, m)) = receiver_ty.value.as_reference() { + if let rustc_type_ir::TyKind::Ref(_, _, m) = receiver_ty.value.kind() { + let m = match m { + rustc_ast_ir::Mutability::Mut => chalk_ir::Mutability::Mut, + rustc_ast_ir::Mutability::Not => chalk_ir::Mutability::Not, + }; // Prefer reborrow of references to move maybe_reborrowed.autoref = Some(AutorefOrPtrAdjustment::Autoref(m)); maybe_reborrowed.autoderefs += 1; @@ -1174,10 +1168,10 @@ fn iterate_method_candidates_with_autoref( iterate_method_candidates_by_receiver(receiver_ty.clone(), maybe_reborrowed)?; - let refed = Canonical { - value: TyKind::Ref(Mutability::Not, error_lifetime(), receiver_ty.value.clone()) - .intern(Interner), - binders: receiver_ty.binders.clone(), + let refed = crate::next_solver::Canonical { + max_universe: receiver_ty.max_universe, + variables: receiver_ty.variables, + value: crate::next_solver::Ty::new_ref(interner, crate::next_solver::Region::error(interner), receiver_ty.value, rustc_ast_ir::Mutability::Not), }; iterate_method_candidates_by_receiver( @@ -1185,10 +1179,10 @@ fn iterate_method_candidates_with_autoref( first_adjustment.with_autoref(AutorefOrPtrAdjustment::Autoref(Mutability::Not)), )?; - let ref_muted = Canonical { - value: TyKind::Ref(Mutability::Mut, error_lifetime(), receiver_ty.value.clone()) - .intern(Interner), - binders: receiver_ty.binders.clone(), + let ref_muted = crate::next_solver::Canonical { + max_universe: receiver_ty.max_universe, + variables: receiver_ty.variables, + value: crate::next_solver::Ty::new_ref(interner, crate::next_solver::Region::error(interner), receiver_ty.value, rustc_ast_ir::Mutability::Mut), }; iterate_method_candidates_by_receiver( @@ -1196,10 +1190,11 @@ fn iterate_method_candidates_with_autoref( first_adjustment.with_autoref(AutorefOrPtrAdjustment::Autoref(Mutability::Mut)), )?; - if let Some((ty, Mutability::Mut)) = receiver_ty.value.as_raw_ptr() { - let const_ptr_ty = Canonical { - value: TyKind::Raw(Mutability::Not, ty.clone()).intern(Interner), - binders: receiver_ty.binders, + if let rustc_type_ir::TyKind::RawPtr(ty, rustc_ast_ir::Mutability::Mut) = receiver_ty.value.kind() { + let const_ptr_ty = rustc_type_ir::Canonical { + max_universe: rustc_type_ir::UniverseIndex::ZERO, + value: crate::next_solver::Ty::new_ptr(interner, ty, rustc_ast_ir::Mutability::Not), + variables: receiver_ty.variables, }; iterate_method_candidates_by_receiver( const_ptr_ty, @@ -1250,16 +1245,17 @@ where } #[tracing::instrument(skip_all, fields(name = ?name))] -fn iterate_method_candidates_by_receiver( - table: &mut InferenceTable<'_>, - receiver_ty: Canonical, +fn iterate_method_candidates_by_receiver<'db>( + table: &mut InferenceTable<'db>, + receiver_ty: crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, receiver_adjustments: ReceiverAdjustments, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, name: Option<&Name>, callback: &mut dyn MethodCandidateCallback, ) -> ControlFlow<()> { - let receiver_ty = table.instantiate_canonical(receiver_ty); + let receiver_ty = table.instantiate_canonical_ns(receiver_ty); + let receiver_ty: crate::Ty = ChalkToNextSolver::from_nextsolver(receiver_ty, table.interner); // We're looking for methods with *receiver* type receiver_ty. These could // be found in any of the derefs of receiver_ty, so we have to go through // that, including raw derefs. @@ -1307,9 +1303,9 @@ fn iterate_method_candidates_by_receiver( } #[tracing::instrument(skip_all, fields(name = ?name))] -fn iterate_method_candidates_for_self_ty( - self_ty: &Canonical, - db: &dyn HirDatabase, +fn iterate_method_candidates_for_self_ty<'db>( + self_ty: &crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, + db: &'db dyn HirDatabase, env: Arc, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, @@ -1317,7 +1313,7 @@ fn iterate_method_candidates_for_self_ty( callback: &mut dyn MethodCandidateCallback, ) -> ControlFlow<()> { let mut table = InferenceTable::new(db, env); - let self_ty = table.instantiate_canonical(self_ty.clone()); + let self_ty = ChalkToNextSolver::from_nextsolver(table.instantiate_canonical_ns(self_ty.clone()), table.interner); iterate_inherent_methods( &self_ty, &mut table, @@ -1354,7 +1350,7 @@ fn iterate_trait_method_candidates( ) -> ControlFlow<()> { let db = table.db; - let canonical_self_ty = table.canonicalize(self_ty.clone()); + let canonical_self_ty = ChalkToNextSolver::from_nextsolver(table.canonicalize(self_ty.clone().to_nextsolver(table.interner)), table.interner); let TraitEnvironment { krate, block, .. } = *table.trait_env; 'traits: for &t in traits_in_scope { @@ -1583,13 +1579,14 @@ pub(crate) fn resolve_indexing_op( ) -> Option { let mut table = InferenceTable::new(db, env); let ty = table.instantiate_canonical(ty); - let deref_chain = autoderef_method_receiver(&mut table, ty); + let interner = table.interner; + let deref_chain = autoderef_method_receiver(&mut table, ty.to_nextsolver(interner)); for (ty, adj) in deref_chain { - let goal = generic_implements_goal(db, &table.trait_env, index_trait, &ty); - if !db - .trait_solve(table.trait_env.krate, table.trait_env.block, goal.cast(Interner)) - .no_solution() - { + //let goal = generic_implements_goal_ns(db, &table.trait_env, index_trait, &ty); + let goal = generic_implements_goal(db, &table.trait_env, index_trait, &ChalkToNextSolver::from_nextsolver(ty, interner)); + let goal: chalk_ir::Canonical>> = goal.cast(Interner); + let goal = goal.to_nextsolver(interner); + if !next_trait_solve_canonical(db, table.trait_env.krate, table.trait_env.block, goal).no_solution() { return Some(adj); } } @@ -1773,26 +1770,11 @@ fn is_valid_impl_fn_candidate( }); for goal in goals.clone() { - let in_env = InEnvironment::new(&table.trait_env.env, goal); - let canonicalized = table.canonicalize_with_free_vars(in_env); - let solution = table.db.trait_solve( - table.trait_env.krate, - table.trait_env.block, - canonicalized.value.clone(), - ); - - match solution { - NextTraitSolveResult::Certain(canonical_subst) => { - canonicalized.apply_solution( - table, - Canonical { - binders: canonical_subst.binders, - value: canonical_subst.value.subst, - }, - ); + match table.solve_obligation(goal) { + Ok(_) => {} + Err(_) => { + return IsValidCandidate::No; } - NextTraitSolveResult::Uncertain(..) => {} - NextTraitSolveResult::NoSolution => return IsValidCandidate::No, } } @@ -1857,25 +1839,66 @@ fn generic_implements_goal( Canonical { binders, value } } -fn autoderef_method_receiver( - table: &mut InferenceTable<'_>, - ty: Ty, -) -> Vec<(Canonical, ReceiverAdjustments)> { - let mut deref_chain: Vec<_> = Vec::new(); - let mut autoderef = autoderef::Autoderef::new_no_tracking(table, ty, false, true); +/* +/// This creates Substs for a trait with the given Self type and type variables +/// for all other parameters, to query the trait solver with it. +#[tracing::instrument(skip_all)] +fn generic_implements_goal_ns<'db>( + db: &'db dyn HirDatabase, + interner: DbInterner<'db>, + env: &TraitEnvironment, + trait_: TraitId, + self_ty: &crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, +) -> crate::next_solver::Canonical<'db, crate::next_solver::Goal<'db, crate::next_solver::Predicate<'db>>> { + let variables = self_ty.variables; + let trait_ref = TyBuilder::trait_ref(db, trait_) + .push(ChalkToNextSolver::from_nextsolver(self_ty.value, interner)) + .fill_with_bound_vars(DebruijnIndex::INNERMOST, variables.len()) + .build(); + + let infer_ctxt = interner.infer_ctxt().build(TypingMode::non_body_analysis()); + let args = infer_ctxt.fresh_args_for_item(SolverDefId::TraitId(trait_)); + + rustc_type_ir::TraitRef::new(interner, SolverDefId::TraitId(trait_)).with_self_ty(interner, self_ty.value); + + + let kinds = + binders.iter().cloned().chain(trait_ref.substitution.iter(Interner).skip(1).map(|it| { + let vk = match it.data(Interner) { + GenericArgData::Ty(_) => VariableKind::Ty(chalk_ir::TyVariableKind::General), + GenericArgData::Lifetime(_) => VariableKind::Lifetime, + GenericArgData::Const(c) => VariableKind::Const(c.data(Interner).ty.clone()), + }; + WithKind::new(vk, UniverseIndex::ROOT) + })); + let binders = CanonicalVarKinds::from_iter(Interner, kinds); + + let obligation = trait_ref.cast(Interner); + let value = InEnvironment::new(&env.env, obligation); + crate::next_solver::Canonical { max_universe, value, variables } +} +*/ + +fn autoderef_method_receiver<'db>( + table: &mut InferenceTable<'db>, + ty: crate::next_solver::Ty<'db>, +) -> Vec<(crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, ReceiverAdjustments)> { + let interner = table.interner; + let mut deref_chain = Vec::new(); + let mut autoderef = autoderef::Autoderef::new_no_tracking(table, ChalkToNextSolver::from_nextsolver(ty, interner), false, true); while let Some((ty, derefs)) = autoderef.next() { deref_chain.push(( - autoderef.table.canonicalize(ty), + autoderef.table.canonicalize(ty.to_nextsolver(interner)), ReceiverAdjustments { autoref: None, autoderefs: derefs, unsize_array: false }, )); } // As a last step, we can do array unsizing (that's the only unsizing that rustc does for method receivers!) - if let Some((TyKind::Array(parameters, _), binders, adj)) = - deref_chain.last().map(|(ty, adj)| (ty.value.kind(Interner), ty.binders.clone(), adj)) + if let Some((rustc_type_ir::Array(parameters, _), variables, max_universe, adj)) = + deref_chain.last().map(|d| (d.0.value.kind(), d.0.variables.clone(), d.0.max_universe, d.1.clone())) { - let unsized_ty = TyKind::Slice(parameters.clone()).intern(Interner); + let unsized_ty = crate::next_solver::Ty::new_slice(interner, parameters); deref_chain.push(( - Canonical { value: unsized_ty, binders }, + crate::next_solver::Canonical { max_universe, value: unsized_ty, variables, }, ReceiverAdjustments { unsize_array: true, ..adj.clone() }, )); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs new file mode 100644 index 000000000000..0820006e5dfb --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs @@ -0,0 +1,785 @@ + +use rustc_hash::FxHashMap; +use rustc_index::Idx; +use rustc_type_ir::inherent::{Const as _, IntoKind as _, Region as _, SliceLike, Ty as _}; +use rustc_type_ir::InferTy::{self, FloatVar, IntVar, TyVar}; +use rustc_type_ir::{BoundVar, CanonicalQueryInput, CanonicalTyVarKind, DebruijnIndex, Flags, InferConst, RegionKind, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, UniverseIndex}; +use smallvec::SmallVec; +use tracing::debug; + +use crate::next_solver::infer::InferCtxt; +use crate::next_solver::{Binder, BoundRegion, BoundRegionKind, BoundTy, Canonical, CanonicalVarKind, CanonicalVars, Const, ConstKind, DbInterner, GenericArg, ParamEnvAnd, Placeholder, Region, Ty, TyKind}; + +/// When we canonicalize a value to form a query, we wind up replacing +/// various parts of it with canonical variables. This struct stores +/// those replaced bits to remember for when we process the query +/// result. +#[derive(Clone, Debug)] +pub struct OriginalQueryValues<'db> { + /// Map from the universes that appear in the query to the universes in the + /// caller context. For all queries except `evaluate_goal` (used by Chalk), + /// we only ever put ROOT values into the query, so this map is very + /// simple. + pub universe_map: SmallVec<[UniverseIndex; 4]>, + + /// This is equivalent to `CanonicalVarValues`, but using a + /// `SmallVec` yields a significant performance win. + pub var_values: SmallVec<[GenericArg<'db>; 8]>, +} + +impl<'db> Default for OriginalQueryValues<'db> { + fn default() -> Self { + let mut universe_map = SmallVec::default(); + universe_map.push(UniverseIndex::ROOT); + + Self { universe_map, var_values: SmallVec::default() } + } +} + +impl<'db> InferCtxt<'db> { + /// Canonicalizes a query value `V`. When we canonicalize a query, + /// we not only canonicalize unbound inference variables, but we + /// *also* replace all free regions whatsoever. So for example a + /// query like `T: Trait<'static>` would be canonicalized to + /// + /// ```text + /// T: Trait<'?0> + /// ``` + /// + /// with a mapping M that maps `'?0` to `'static`. + /// + /// To get a good understanding of what is happening here, check + /// out the [chapter in the rustc dev guide][c]. + /// + /// [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html#canonicalizing-the-query + pub fn canonicalize_query( + &self, + value: ParamEnvAnd<'db, V>, + query_state: &mut OriginalQueryValues<'db>, + ) -> CanonicalQueryInput, ParamEnvAnd<'db, V>> + where + V: TypeFoldable>, + { + let (param_env, value) = value.into_parts(); + // FIXME(#118965): We don't canonicalize the static lifetimes that appear in the + // `param_env` because they are treated differently by trait selection. + let canonical_param_env = Canonicalizer::canonicalize( + param_env, + None, + self.interner, + &CanonicalizeFreeRegionsOtherThanStatic, + query_state, + ); + + let canonical = Canonicalizer::canonicalize_with_base( + canonical_param_env, + value, + Some(self), + self.interner, + &CanonicalizeAllFreeRegions, + query_state, + ) + .unchecked_map(|(param_env, value)| ParamEnvAnd { param_env, value }); + CanonicalQueryInput { canonical, typing_mode: self.typing_mode() } + } + + /// Canonicalizes a query *response* `V`. When we canonicalize a + /// query response, we only canonicalize unbound inference + /// variables, and we leave other free regions alone. So, + /// continuing with the example from `canonicalize_query`, if + /// there was an input query `T: Trait<'static>`, it would have + /// been canonicalized to + /// + /// ```text + /// T: Trait<'?0> + /// ``` + /// + /// with a mapping M that maps `'?0` to `'static`. But if we found that there + /// exists only one possible impl of `Trait`, and it looks like + /// ```ignore (illustrative) + /// impl Trait<'static> for T { .. } + /// ``` + /// then we would prepare a query result R that (among other + /// things) includes a mapping to `'?0 := 'static`. When + /// canonicalizing this query result R, we would leave this + /// reference to `'static` alone. + /// + /// To get a good understanding of what is happening here, check + /// out the [chapter in the rustc dev guide][c]. + /// + /// [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html#canonicalizing-the-query-result + pub fn canonicalize_response(&self, value: V) -> Canonical<'db, V> + where + V: TypeFoldable>, + { + let mut query_state = OriginalQueryValues::default(); + Canonicalizer::canonicalize( + value, + Some(self), + self.interner, + &CanonicalizeQueryResponse, + &mut query_state, + ) + } + + pub fn canonicalize_user_type_annotation(&self, value: V) -> Canonical<'db, V> + where + V: TypeFoldable>, + { + let mut query_state = OriginalQueryValues::default(); + Canonicalizer::canonicalize( + value, + Some(self), + self.interner, + &CanonicalizeUserTypeAnnotation, + &mut query_state, + ) + } +} + +/// Controls how we canonicalize "free regions" that are not inference +/// variables. This depends on what we are canonicalizing *for* -- +/// e.g., if we are canonicalizing to create a query, we want to +/// replace those with inference variables, since we want to make a +/// maximally general query. But if we are canonicalizing a *query +/// response*, then we don't typically replace free regions, as they +/// must have been introduced from other parts of the system. +trait CanonicalizeMode { + fn canonicalize_free_region<'db>( + &self, + canonicalizer: &mut Canonicalizer<'_, 'db>, + r: Region<'db>, + ) -> Region<'db>; + + fn any(&self) -> bool; + + // Do we preserve universe of variables. + fn preserve_universes(&self) -> bool; +} + +struct CanonicalizeQueryResponse; + +impl CanonicalizeMode for CanonicalizeQueryResponse { + fn canonicalize_free_region<'db>( + &self, + canonicalizer: &mut Canonicalizer<'_, 'db>, + mut r: Region<'db>, + ) -> Region<'db> { + let infcx = canonicalizer.infcx.unwrap(); + + if let RegionKind::ReVar(vid) = r.kind() { + r = infcx + .inner + .borrow_mut() + .unwrap_region_constraints() + .opportunistic_resolve_var(canonicalizer.tcx, vid); + debug!( + "canonical: region var found with vid {vid:?}, \ + opportunistically resolved to {r:?}", + ); + }; + + match r.kind() { + RegionKind::ReLateParam(_) | RegionKind::ReErased | RegionKind::ReStatic | RegionKind::ReEarlyParam(..) | RegionKind::ReError(..) => r, + + RegionKind::RePlaceholder(placeholder) => canonicalizer.canonical_var_for_region( + CanonicalVarKind::PlaceholderRegion(placeholder), + r, + ), + + RegionKind::ReVar(vid) => { + let universe = infcx + .inner + .borrow_mut() + .unwrap_region_constraints() + .probe_value(vid) + .unwrap_err(); + canonicalizer.canonical_var_for_region( + CanonicalVarKind::Region(universe), + r, + ) + } + + _ => { + // Other than `'static` or `'empty`, the query + // response should be executing in a fully + // canonicalized environment, so there shouldn't be + // any other region names it can come up. + // + // rust-lang/rust#57464: `impl Trait` can leak local + // scopes (in manner violating typeck). Therefore, use + // `delayed_bug` to allow type error over an ICE. + panic!("unexpected region in query response: `{r:?}`"); + } + } + } + + fn any(&self) -> bool { + false + } + + fn preserve_universes(&self) -> bool { + true + } +} + +struct CanonicalizeUserTypeAnnotation; + +impl CanonicalizeMode for CanonicalizeUserTypeAnnotation { + fn canonicalize_free_region<'db>( + &self, + canonicalizer: &mut Canonicalizer<'_, 'db>, + r: Region<'db>, + ) -> Region<'db> { + match r.kind() { + RegionKind::ReEarlyParam(_) + | RegionKind::ReLateParam(_) + | RegionKind::ReErased + | RegionKind::ReStatic + | RegionKind::ReError(_) => r, + RegionKind::ReVar(_) => canonicalizer.canonical_var_for_region_in_root_universe(r), + RegionKind::RePlaceholder(..) | RegionKind::ReBound(..) => { + // We only expect region names that the user can type. + panic!("unexpected region in query response: `{:?}`", r) + } + } + } + + fn any(&self) -> bool { + false + } + + fn preserve_universes(&self) -> bool { + false + } +} + +struct CanonicalizeAllFreeRegions; + +impl CanonicalizeMode for CanonicalizeAllFreeRegions { + fn canonicalize_free_region<'db>( + &self, + canonicalizer: &mut Canonicalizer<'_, 'db>, + r: Region<'db>, + ) -> Region<'db> { + canonicalizer.canonical_var_for_region_in_root_universe(r) + } + + fn any(&self) -> bool { + true + } + + fn preserve_universes(&self) -> bool { + false + } +} + +struct CanonicalizeFreeRegionsOtherThanStatic; + +impl CanonicalizeMode for CanonicalizeFreeRegionsOtherThanStatic { + fn canonicalize_free_region<'db>( + &self, + canonicalizer: &mut Canonicalizer<'_, 'db>, + r: Region<'db>, + ) -> Region<'db> { + if r.is_static() { r } else { canonicalizer.canonical_var_for_region_in_root_universe(r) } + } + + fn any(&self) -> bool { + true + } + + fn preserve_universes(&self) -> bool { + false + } +} + +struct Canonicalizer<'cx, 'db> { + /// Set to `None` to disable the resolution of inference variables. + infcx: Option<&'cx InferCtxt<'db>>, + tcx: DbInterner<'db>, + variables: SmallVec<[CanonicalVarKind<'db>; 8]>, + query_state: &'cx mut OriginalQueryValues<'db>, + // Note that indices is only used once `var_values` is big enough to be + // heap-allocated. + indices: FxHashMap, BoundVar>, + canonicalize_mode: &'cx dyn CanonicalizeMode, + needs_canonical_flags: TypeFlags, + + binder_index: DebruijnIndex, +} + +impl<'cx, 'db> TypeFolder> for Canonicalizer<'cx, 'db> { + fn cx(&self) -> DbInterner<'db> { + self.tcx + } + + fn fold_binder(&mut self, t: Binder<'db, T>) -> Binder<'db, T> + where + T: TypeFoldable>, + { + self.binder_index.shift_in(1); + let t = t.super_fold_with(self); + self.binder_index.shift_out(1); + t + } + + fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { + match r.kind() { + RegionKind::ReBound(index, ..) => { + if index >= self.binder_index { + panic!("escaping late-bound region during canonicalization"); + } else { + r + } + } + + RegionKind::ReStatic + | RegionKind::ReEarlyParam(..) + | RegionKind::ReError(_) + | RegionKind::ReLateParam(_) + | RegionKind::RePlaceholder(..) + | RegionKind::ReVar(_) + | RegionKind::ReErased => self.canonicalize_mode.canonicalize_free_region(self, r), + } + } + + fn fold_ty(&mut self, mut t: Ty<'db>) -> Ty<'db> { + match t.kind() { + TyKind::Infer(TyVar(mut vid)) => { + // We need to canonicalize the *root* of our ty var. + // This is so that our canonical response correctly reflects + // any equated inference vars correctly! + let root_vid = self.infcx.unwrap().root_var(vid); + if root_vid != vid { + t = Ty::new_var(self.tcx, root_vid); + vid = root_vid; + } + + debug!("canonical: type var found with vid {:?}", vid); + match self.infcx.unwrap().probe_ty_var(vid) { + // `t` could be a float / int variable; canonicalize that instead. + Ok(t) => { + debug!("(resolved to {:?})", t); + self.fold_ty(t) + } + + // `TyVar(vid)` is unresolved, track its universe index in the canonicalized + // result. + Err(mut ui) => { + if !self.canonicalize_mode.preserve_universes() { + // FIXME: perf problem described in #55921. + ui = UniverseIndex::ROOT; + } + self.canonicalize_ty_var( + CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)), + t, + ) + } + } + } + + TyKind::Infer(IntVar(vid)) => { + let nt = self.infcx.unwrap().opportunistic_resolve_int_var(vid); + if nt != t { + return self.fold_ty(nt); + } else { + self.canonicalize_ty_var( + CanonicalVarKind::Ty(CanonicalTyVarKind::Int), + t, + ) + } + } + TyKind::Infer(FloatVar(vid)) => { + let nt = self.infcx.unwrap().opportunistic_resolve_float_var(vid); + if nt != t { + return self.fold_ty(nt); + } else { + self.canonicalize_ty_var( + CanonicalVarKind::Ty(CanonicalTyVarKind::Float), + t, + ) + } + } + + TyKind::Infer(InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_)) => { + panic!("encountered a fresh type during canonicalization") + } + + TyKind::Placeholder(mut placeholder) => { + if !self.canonicalize_mode.preserve_universes() { + placeholder.universe = UniverseIndex::ROOT; + } + self.canonicalize_ty_var( + CanonicalVarKind::PlaceholderTy(placeholder), + t, + ) + } + + TyKind::Bound(debruijn, _) => { + if debruijn >= self.binder_index { + panic!("escaping bound type during canonicalization") + } else { + t + } + } + + TyKind::Closure(..) + | TyKind::CoroutineClosure(..) + | TyKind::Coroutine(..) + | TyKind::CoroutineWitness(..) + | TyKind::Bool + | TyKind::Char + | TyKind::Int(..) + | TyKind::Uint(..) + | TyKind::Float(..) + | TyKind::Adt(..) + | TyKind::Str + | TyKind::Error(_) + | TyKind::Array(..) + | TyKind::Slice(..) + | TyKind::RawPtr(..) + | TyKind::Ref(..) + | TyKind::FnDef(..) + | TyKind::FnPtr(..) + | TyKind::Dynamic(..) + | TyKind::UnsafeBinder(_) + | TyKind::Never + | TyKind::Tuple(..) + | TyKind::Alias(..) + | TyKind::Foreign(..) + | TyKind::Pat(..) + | TyKind::Param(..) => { + if t.flags().intersects(self.needs_canonical_flags) { + t.super_fold_with(self) + } else { + t + } + } + } + } + + fn fold_const(&mut self, mut ct: Const<'db>) -> Const<'db> { + match ct.kind() { + ConstKind::Infer(InferConst::Var(mut vid)) => { + // We need to canonicalize the *root* of our const var. + // This is so that our canonical response correctly reflects + // any equated inference vars correctly! + let root_vid = self.infcx.unwrap().root_const_var(vid); + if root_vid != vid { + ct = Const::new_var(self.tcx, root_vid); + vid = root_vid; + } + + debug!("canonical: const var found with vid {:?}", vid); + match self.infcx.unwrap().probe_const_var(vid) { + Ok(c) => { + debug!("(resolved to {:?})", c); + return self.fold_const(c); + } + + // `ConstVar(vid)` is unresolved, track its universe index in the + // canonicalized result + Err(mut ui) => { + if !self.canonicalize_mode.preserve_universes() { + // FIXME: perf problem described in #55921. + ui = UniverseIndex::ROOT; + } + return self.canonicalize_const_var( + CanonicalVarKind::Const(ui), + ct, + ); + } + } + } + ConstKind::Infer(InferConst::Fresh(_)) => { + panic!("encountered a fresh const during canonicalization") + } + ConstKind::Bound(debruijn, _) => { + if debruijn >= self.binder_index { + panic!("escaping bound const during canonicalization") + } else { + return ct; + } + } + ConstKind::Placeholder(placeholder) => { + return self.canonicalize_const_var( + CanonicalVarKind::PlaceholderConst(placeholder), + ct, + ); + } + _ => {} + } + + if ct.flags().intersects(self.needs_canonical_flags) { + ct.super_fold_with(self) + } else { + ct + } + } +} + +impl<'cx, 'db> Canonicalizer<'cx, 'db> { + /// The main `canonicalize` method, shared impl of + /// `canonicalize_query` and `canonicalize_response`. + fn canonicalize( + value: V, + infcx: Option<&InferCtxt<'db>>, + tcx: DbInterner<'db>, + canonicalize_region_mode: &dyn CanonicalizeMode, + query_state: &mut OriginalQueryValues<'db>, + ) -> Canonical<'db, V> + where + V: TypeFoldable>, + { + let base = Canonical { + max_universe: UniverseIndex::ROOT, + variables: CanonicalVars::new_from_iter(tcx, []), + value: (), + }; + Canonicalizer::canonicalize_with_base( + base, + value, + infcx, + tcx, + canonicalize_region_mode, + query_state, + ) + .unchecked_map(|((), val)| val) + } + + fn canonicalize_with_base( + base: Canonical<'db, U>, + value: V, + infcx: Option<&InferCtxt<'db>>, + tcx: DbInterner<'db>, + canonicalize_region_mode: &dyn CanonicalizeMode, + query_state: &mut OriginalQueryValues<'db>, + ) -> Canonical<'db, (U, V)> + where + V: TypeFoldable>, + { + let needs_canonical_flags = if canonicalize_region_mode.any() { + TypeFlags::HAS_INFER | TypeFlags::HAS_PLACEHOLDER | TypeFlags::HAS_FREE_REGIONS + } else { + TypeFlags::HAS_INFER | TypeFlags::HAS_PLACEHOLDER + }; + + // Fast path: nothing that needs to be canonicalized. + if !value.has_type_flags(needs_canonical_flags) { + return base.unchecked_map(|b| (b, value)); + } + + let mut canonicalizer = Canonicalizer { + infcx, + tcx, + canonicalize_mode: canonicalize_region_mode, + needs_canonical_flags, + variables: SmallVec::from_slice(base.variables.as_slice()), + query_state, + indices: FxHashMap::default(), + binder_index: DebruijnIndex::ZERO, + }; + if canonicalizer.query_state.var_values.spilled() { + canonicalizer.indices = canonicalizer + .query_state + .var_values + .iter() + .enumerate() + .map(|(i, &kind)| (kind, BoundVar::from(i))) + .collect(); + } + let out_value = value.fold_with(&mut canonicalizer); + + // Once we have canonicalized `out_value`, it should not + // contain anything that ties it to this inference context + // anymore. + debug_assert!(!out_value.has_infer() && !out_value.has_placeholders()); + + let canonical_variables = CanonicalVars::new_from_iter(tcx, canonicalizer.universe_canonicalized_variables()); + + let max_universe = canonical_variables + .iter() + .map(|cvar| cvar.universe()) + .max() + .unwrap_or(UniverseIndex::ROOT); + + Canonical { max_universe, variables: canonical_variables, value: (base.value, out_value) } + } + + /// Creates a canonical variable replacing `kind` from the input, + /// or returns an existing variable if `kind` has already been + /// seen. `kind` is expected to be an unbound variable (or + /// potentially a free region). + fn canonical_var(&mut self, info: CanonicalVarKind<'db>, kind: GenericArg<'db>) -> BoundVar { + let Canonicalizer { variables, query_state, indices, .. } = self; + + let var_values = &mut query_state.var_values; + + let universe = info.universe(); + if universe != UniverseIndex::ROOT { + assert!(self.canonicalize_mode.preserve_universes()); + + // Insert universe into the universe map. To preserve the order of the + // universes in the value being canonicalized, we don't update the + // universe in `info` until we have finished canonicalizing. + match query_state.universe_map.binary_search(&universe) { + Err(idx) => query_state.universe_map.insert(idx, universe), + Ok(_) => {} + } + } + + // This code is hot. `variables` and `var_values` are usually small + // (fewer than 8 elements ~95% of the time). They are SmallVec's to + // avoid allocations in those cases. We also don't use `indices` to + // determine if a kind has been seen before until the limit of 8 has + // been exceeded, to also avoid allocations for `indices`. + if !var_values.spilled() { + // `var_values` is stack-allocated. `indices` isn't used yet. Do a + // direct linear search of `var_values`. + if let Some(idx) = var_values.iter().position(|&k| k == kind) { + // `kind` is already present in `var_values`. + BoundVar::new(idx) + } else { + // `kind` isn't present in `var_values`. Append it. Likewise + // for `info` and `variables`. + variables.push(info); + var_values.push(kind); + assert_eq!(variables.len(), var_values.len()); + + // If `var_values` has become big enough to be heap-allocated, + // fill up `indices` to facilitate subsequent lookups. + if var_values.spilled() { + assert!(indices.is_empty()); + *indices = var_values + .iter() + .enumerate() + .map(|(i, &kind)| (kind, BoundVar::new(i))) + .collect(); + } + // The cv is the index of the appended element. + BoundVar::new(var_values.len() - 1) + } + } else { + // `var_values` is large. Do a hashmap search via `indices`. + *indices.entry(kind).or_insert_with(|| { + variables.push(info); + var_values.push(kind); + assert_eq!(variables.len(), var_values.len()); + BoundVar::new(variables.len() - 1) + }) + } + } + + /// Replaces the universe indexes used in `var_values` with their index in + /// `query_state.universe_map`. This minimizes the maximum universe used in + /// the canonicalized value. + fn universe_canonicalized_variables(self) -> SmallVec<[CanonicalVarKind<'db>; 8]> { + if self.query_state.universe_map.len() == 1 { + return self.variables; + } + + let reverse_universe_map: FxHashMap = self + .query_state + .universe_map + .iter() + .enumerate() + .map(|(idx, universe)| (*universe, UniverseIndex::from_usize(idx))) + .collect(); + + self.variables + .iter() + .map(|v| match *v { + CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) => { + return *v; + } + CanonicalVarKind::Ty(CanonicalTyVarKind::General(u)) => { + CanonicalVarKind::Ty(CanonicalTyVarKind::General(reverse_universe_map[&u])) + } + CanonicalVarKind::Region(u) => { + CanonicalVarKind::Region(reverse_universe_map[&u]) + } + CanonicalVarKind::Const(u) => CanonicalVarKind::Const(reverse_universe_map[&u]), + CanonicalVarKind::PlaceholderTy(placeholder) => { + CanonicalVarKind::PlaceholderTy(Placeholder { + universe: reverse_universe_map[&placeholder.universe], + ..placeholder + }) + } + CanonicalVarKind::PlaceholderRegion(placeholder) => { + CanonicalVarKind::PlaceholderRegion(Placeholder { + universe: reverse_universe_map[&placeholder.universe], + ..placeholder + }) + } + CanonicalVarKind::PlaceholderConst(placeholder) => { + CanonicalVarKind::PlaceholderConst(Placeholder { + universe: reverse_universe_map[&placeholder.universe], + ..placeholder + }) + } + }) + .collect() + } + + /// Shorthand helper that creates a canonical region variable for + /// `r` (always in the root universe). The reason that we always + /// put these variables into the root universe is because this + /// method is used during **query construction:** in that case, we + /// are taking all the regions and just putting them into the most + /// generic context we can. This may generate solutions that don't + /// fit (e.g., that equate some region variable with a placeholder + /// it can't name) on the caller side, but that's ok, the caller + /// can figure that out. In the meantime, it maximizes our + /// caching. + /// + /// (This works because unification never fails -- and hence trait + /// selection is never affected -- due to a universe mismatch.) + fn canonical_var_for_region_in_root_universe( + &mut self, + r: Region<'db>, + ) -> Region<'db> { + self.canonical_var_for_region( + CanonicalVarKind::Region(UniverseIndex::ROOT), + r, + ) + } + + /// Creates a canonical variable (with the given `info`) + /// representing the region `r`; return a region referencing it. + fn canonical_var_for_region( + &mut self, + info: CanonicalVarKind<'db>, + r: Region<'db>, + ) -> Region<'db> { + let var = self.canonical_var(info, r.into()); + let br = BoundRegion { var, kind: BoundRegionKind::Anon }; + Region::new_bound(self.cx(), self.binder_index, br) + } + + /// Given a type variable `ty_var` of the given kind, first check + /// if `ty_var` is bound to anything; if so, canonicalize + /// *that*. Otherwise, create a new canonical variable for + /// `ty_var`. + fn canonicalize_ty_var(&mut self, info: CanonicalVarKind<'db>, ty_var: Ty<'db>) -> Ty<'db> { + debug_assert!(!self.infcx.is_some_and(|infcx| ty_var != infcx.shallow_resolve(ty_var))); + let var = self.canonical_var(info, ty_var.into()); + Ty::new_bound(self.tcx, self.binder_index, BoundTy { kind: crate::next_solver::BoundTyKind::Anon, var }) + } + + /// Given a type variable `const_var` of the given kind, first check + /// if `const_var` is bound to anything; if so, canonicalize + /// *that*. Otherwise, create a new canonical variable for + /// `const_var`. + fn canonicalize_const_var( + &mut self, + info: CanonicalVarKind<'db>, + const_var: Const<'db>, + ) -> Const<'db> { + debug_assert!( + !self.infcx.is_some_and(|infcx| const_var != infcx.shallow_resolve_const(const_var)) + ); + let var = self.canonical_var(info, const_var.into()); + Const::new_bound(self.tcx, self.binder_index, var) + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs index 4457e1340d58..ec41111ed815 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs @@ -42,6 +42,7 @@ use rustc_type_ir::{ }, }; +pub mod canonicalizer; pub mod instantiate; impl<'db> InferCtxt<'db> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs index 585719144ef4..894c91e0f49d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs @@ -190,12 +190,13 @@ impl<'db> InferCtxtInner<'db> { } #[inline] - fn int_unification_table(&mut self) -> UnificationTable<'_, 'db, IntVid> { + pub(crate) fn int_unification_table(&mut self) -> UnificationTable<'_, 'db, IntVid> { + tracing::debug!(?self.int_unification_storage); self.int_unification_storage.with_log(&mut self.undo_log) } #[inline] - fn float_unification_table(&mut self) -> UnificationTable<'_, 'db, FloatVid> { + pub(crate) fn float_unification_table(&mut self) -> UnificationTable<'_, 'db, FloatVid> { self.float_unification_storage.with_log(&mut self.undo_log) } @@ -213,6 +214,7 @@ impl<'db> InferCtxtInner<'db> { } } +#[derive(Clone)] pub struct InferCtxt<'db> { pub interner: DbInterner<'db>, @@ -500,6 +502,10 @@ impl<'db> InferCtxt<'db> { self.next_ty_var_with_origin(TypeVariableOrigin { param_def_id: None }) } + pub fn next_ty_vid(&self) -> TyVid { + self.inner.borrow_mut().type_variables().new_var(self.universe(), TypeVariableOrigin { param_def_id: None }) + } + pub fn next_ty_var_with_origin(&self, origin: TypeVariableOrigin) -> Ty<'db> { let vid = self.inner.borrow_mut().type_variables().new_var(self.universe(), origin); Ty::new_var(self.interner, vid) @@ -519,6 +525,15 @@ impl<'db> InferCtxt<'db> { self.next_const_var_with_origin(ConstVariableOrigin { param_def_id: None }) } + pub fn next_const_vid(&self) -> ConstVid { + self + .inner + .borrow_mut() + .const_unification_table() + .new_key(ConstVariableValue::Unknown { origin: ConstVariableOrigin { param_def_id: None }, universe: self.universe() }) + .vid + } + pub fn next_const_var_with_origin(&self, origin: ConstVariableOrigin) -> Const<'db> { let vid = self .inner @@ -546,12 +561,20 @@ impl<'db> InferCtxt<'db> { Ty::new_int_var(self.interner, next_int_var_id) } + pub fn next_int_vid(&self) -> IntVid { + self.inner.borrow_mut().int_unification_table().new_key(IntVarValue::Unknown) + } + pub fn next_float_var(&self) -> Ty<'db> { let next_float_var_id = self.inner.borrow_mut().float_unification_table().new_key(FloatVarValue::Unknown); Ty::new_float_var(self.interner, next_float_var_id) } + pub fn next_float_vid(&self) -> FloatVid { + self.inner.borrow_mut().float_unification_table().new_key(FloatVarValue::Unknown) + } + /// Creates a fresh region variable with the next available index. /// The variable will be created in the maximum universe created /// thus far, allowing it to name any region created thus far. @@ -559,6 +582,10 @@ impl<'db> InferCtxt<'db> { self.next_region_var_in_universe(self.universe()) } + pub fn next_region_vid(&self) -> RegionVid { + self.inner.borrow_mut().unwrap_region_constraints().new_region_var(self.universe()) + } + /// Creates a fresh region variable with the next available index /// in the given universe; typically, you can use /// `next_region_var` and just use the maximal universe. @@ -782,6 +809,18 @@ impl<'db> InferCtxt<'db> { } } + pub fn resolve_int_var(&self, vid: IntVid) -> Option> { + let mut inner = self.inner.borrow_mut(); + let value = inner.int_unification_table().probe_value(vid); + match value { + IntVarValue::IntType(ty) => Some(Ty::new_int(self.interner, ty)), + IntVarValue::UintType(ty) => Some(Ty::new_uint(self.interner, ty)), + IntVarValue::Unknown => { + None + } + } + } + /// Resolves a float var to a rigid int type, if it was constrained to one, /// or else the root float var in the unification table. pub fn opportunistic_resolve_float_var(&self, vid: FloatVid) -> Ty<'db> { @@ -795,6 +834,17 @@ impl<'db> InferCtxt<'db> { } } + pub fn resolve_float_var(&self, vid: FloatVid) -> Option> { + let mut inner = self.inner.borrow_mut(); + let value = inner.float_unification_table().probe_value(vid); + match value { + FloatVarValue::Known(ty) => Some(Ty::new_float(self.interner, ty)), + FloatVarValue::Unknown => { + None + } + } + } + /// Where possible, replaces type/const variables in /// `value` with their final value. Note that region variables /// are unaffected. If a type/const variable has not been unified, it diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs index eb426205575a..8c7dfb25e07f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs @@ -46,7 +46,7 @@ impl<'db> InferCtxt<'db> { UndoLogs::>::num_open_snapshots(&self.inner.borrow_mut().undo_log) } - fn start_snapshot(&self) -> CombinedSnapshot { + pub(crate) fn start_snapshot(&self) -> CombinedSnapshot { debug!("start_snapshot()"); let mut inner = self.inner.borrow_mut(); @@ -59,7 +59,7 @@ impl<'db> InferCtxt<'db> { } #[instrument(skip(self, snapshot), level = "debug")] - fn rollback_to(&self, snapshot: CombinedSnapshot) { + pub(crate) fn rollback_to(&self, snapshot: CombinedSnapshot) { let CombinedSnapshot { undo_snapshot, region_constraints_snapshot, universe } = snapshot; self.universe.set(universe); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index dc54ee7d5878..79c402ff29c3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -2,8 +2,8 @@ use base_db::Crate; use chalk_ir::{ - CanonicalVarKind, CanonicalVarKinds, ForeignDefId, InferenceVar, Substitution, TyVariableKind, - WellFormed, fold::Shift, interner::HasInterner, + CanonicalVarKind, CanonicalVarKinds, FnPointer, InferenceVar, Substitution, TyVariableKind, + WellFormed, cast::Cast, fold::Shift, interner::HasInterner, }; use hir_def::{ CallableDefId, ConstParamId, FunctionId, GeneralConstId, LifetimeParamId, TypeAliasId, @@ -24,12 +24,12 @@ use salsa::{Id, plumbing::AsId}; use crate::next_solver::BoundConst; use crate::{ - ConcreteConst, ConstScalar, ImplTraitId, Interner, MemoryMap, + ConstScalar, ImplTraitId, Interner, MemoryMap, db::{ HirDatabase, InternedClosureId, InternedCoroutineId, InternedLifetimeParamId, InternedOpaqueTyId, InternedTypeOrConstParamId, }, - from_assoc_type_id, from_chalk_trait_id, + from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, mapping::ToChalk, next_solver::{ Binder, ClauseKind, ConstBytes, TraitPredicate, UnevaluatedConst, @@ -141,6 +141,7 @@ impl<'db> rustc_type_ir::TypeFolder> for BinderToEarlyBinder<'db pub trait ChalkToNextSolver<'db, Out> { fn to_nextsolver(&self, interner: DbInterner<'db>) -> Out; + fn from_nextsolver(out: Out, interner: DbInterner<'db>) -> Self; } impl<'db> ChalkToNextSolver<'db, Ty<'db>> for chalk_ir::Ty { @@ -427,6 +428,343 @@ impl<'db> ChalkToNextSolver<'db, Ty<'db>> for chalk_ir::Ty { }, ) } + + fn from_nextsolver(out: Ty<'db>, interner: DbInterner<'db>) -> Self { + use crate::{Scalar, TyKind}; + use chalk_ir::{FloatTy, IntTy, UintTy}; + match out.kind() { + rustc_type_ir::TyKind::Bool => TyKind::Scalar(Scalar::Bool), + rustc_type_ir::TyKind::Char => TyKind::Scalar(Scalar::Char), + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I8) => { + TyKind::Scalar(Scalar::Int(IntTy::I8)) + } + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I16) => { + TyKind::Scalar(Scalar::Int(IntTy::I16)) + } + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I32) => { + TyKind::Scalar(Scalar::Int(IntTy::I32)) + } + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I64) => { + TyKind::Scalar(Scalar::Int(IntTy::I64)) + } + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I128) => { + TyKind::Scalar(Scalar::Int(IntTy::I128)) + } + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::Isize) => { + TyKind::Scalar(Scalar::Int(IntTy::Isize)) + } + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U8) => { + TyKind::Scalar(Scalar::Uint(UintTy::U8)) + } + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U16) => { + TyKind::Scalar(Scalar::Uint(UintTy::U16)) + } + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U32) => { + TyKind::Scalar(Scalar::Uint(UintTy::U32)) + } + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U64) => { + TyKind::Scalar(Scalar::Uint(UintTy::U64)) + } + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U128) => { + TyKind::Scalar(Scalar::Uint(UintTy::U128)) + } + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::Usize) => { + TyKind::Scalar(Scalar::Uint(UintTy::Usize)) + } + rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F16) => { + TyKind::Scalar(Scalar::Float(FloatTy::F16)) + } + rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F32) => { + TyKind::Scalar(Scalar::Float(FloatTy::F32)) + } + rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F64) => { + TyKind::Scalar(Scalar::Float(FloatTy::F64)) + } + rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F128) => { + TyKind::Scalar(Scalar::Float(FloatTy::F128)) + } + rustc_type_ir::TyKind::Str => TyKind::Str, + rustc_type_ir::TyKind::Error(_) => TyKind::Error, + rustc_type_ir::TyKind::Never => TyKind::Never, + + rustc_type_ir::TyKind::Adt(def, args) => { + let adt_id = def.inner().id; + let subst = ChalkToNextSolver::from_nextsolver(args, interner); + TyKind::Adt(chalk_ir::AdtId(adt_id), subst) + } + + rustc_type_ir::TyKind::Infer(infer_ty) => { + let (var, kind) = match infer_ty { + rustc_type_ir::InferTy::TyVar(var) => { + (InferenceVar::from(var.as_u32()), TyVariableKind::General) + } + rustc_type_ir::InferTy::IntVar(var) => { + (InferenceVar::from(var.as_u32()), TyVariableKind::Integer) + } + rustc_type_ir::InferTy::FloatVar(var) => { + (InferenceVar::from(var.as_u32()), TyVariableKind::Float) + } + _ => todo!(), + }; + TyKind::InferenceVar(var, kind) + } + + rustc_type_ir::TyKind::Ref(r, ty, mutability) => { + let mutability = match mutability { + rustc_ast_ir::Mutability::Mut => chalk_ir::Mutability::Mut, + rustc_ast_ir::Mutability::Not => chalk_ir::Mutability::Not, + }; + let r = ChalkToNextSolver::from_nextsolver(r, interner); + let ty = ChalkToNextSolver::from_nextsolver(ty, interner); + TyKind::Ref(mutability, r, ty) + } + + rustc_type_ir::TyKind::Tuple(tys) => { + let size = tys.len(); + let subst = Substitution::from_iter( + Interner, + tys.iter().map(|ty| { + chalk_ir::GenericArgData::Ty(ChalkToNextSolver::from_nextsolver( + ty, interner, + )) + .intern(Interner) + }), + ); + TyKind::Tuple(size, subst) + } + + rustc_type_ir::TyKind::Array(ty, const_) => { + let ty = ChalkToNextSolver::from_nextsolver(ty, interner); + let const_ = ChalkToNextSolver::from_nextsolver(const_, interner); + TyKind::Array(ty, const_) + } + + rustc_type_ir::TyKind::Alias(alias_ty_kind, alias_ty) => match alias_ty_kind { + rustc_type_ir::AliasTyKind::Projection => { + let assoc_ty_id = match alias_ty.def_id { + SolverDefId::TypeAliasId(id) => id, + _ => unreachable!(), + }; + let associated_ty_id = to_assoc_type_id(assoc_ty_id); + let substitution = ChalkToNextSolver::from_nextsolver(alias_ty.args, interner); + TyKind::AssociatedType(associated_ty_id, substitution) + } + rustc_type_ir::AliasTyKind::Opaque => { + let opaque_ty_id = match alias_ty.def_id { + SolverDefId::InternedOpaqueTyId(id) => id, + _ => unreachable!(), + }; + let substitution = ChalkToNextSolver::from_nextsolver(alias_ty.args, interner); + TyKind::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy { + opaque_ty_id: opaque_ty_id.into(), + substitution, + })) + } + rustc_type_ir::AliasTyKind::Inherent => todo!(), + rustc_type_ir::AliasTyKind::Free => todo!(), + }, + + rustc_type_ir::TyKind::Placeholder(placeholder) => { + let ui = chalk_ir::UniverseIndex { counter: placeholder.universe.as_usize() }; + let placeholder_index = + chalk_ir::PlaceholderIndex { idx: placeholder.bound.var.as_usize(), ui }; + TyKind::Placeholder(placeholder_index) + } + + rustc_type_ir::TyKind::Bound(debruijn_index, ty) => { + TyKind::BoundVar(chalk_ir::BoundVar { + debruijn: chalk_ir::DebruijnIndex::new(debruijn_index.as_u32()), + index: ty.var.as_usize(), + }) + } + + rustc_type_ir::TyKind::FnPtr(bound_sig, fn_header) => { + let num_binders = bound_sig.bound_vars().len(); + let sig = chalk_ir::FnSig { + abi: fn_header.abi, + safety: match fn_header.safety { + crate::next_solver::abi::Safety::Safe => chalk_ir::Safety::Safe, + crate::next_solver::abi::Safety::Unsafe => chalk_ir::Safety::Unsafe, + }, + variadic: fn_header.c_variadic, + }; + let args = GenericArgs::new_from_iter( + interner, + bound_sig.skip_binder().inputs_and_output.iter().map(|a| a.into()), + ); + let substitution = ChalkToNextSolver::from_nextsolver(args, interner); + let substitution = chalk_ir::FnSubst(substitution); + let fnptr = chalk_ir::FnPointer { num_binders, sig, substitution }; + TyKind::Function(fnptr) + } + + rustc_type_ir::TyKind::Dynamic(preds, region, dyn_kind) => { + assert!(matches!(dyn_kind, rustc_type_ir::DynKind::Dyn)); + let self_ty = Ty::new_bound( + interner, + DebruijnIndex::from_u32(1), + BoundTy { kind: BoundTyKind::Anon, var: BoundVar::from_u32(0) }, + ); + let bounds = chalk_ir::QuantifiedWhereClauses::from_iter( + Interner, + preds.iter().map(|p| { + let binders = chalk_ir::VariableKinds::from_iter( + Interner, + p.bound_vars().iter().map(|b| match b { + BoundVarKind::Ty(kind) => { + chalk_ir::VariableKind::Ty(TyVariableKind::General) + } + BoundVarKind::Region(kind) => chalk_ir::VariableKind::Lifetime, + BoundVarKind::Const => chalk_ir::VariableKind::Const( + crate::TyKind::Error.intern(Interner), + ), + }), + ); + let where_clause = match p.skip_binder() { + rustc_type_ir::ExistentialPredicate::Trait(trait_ref) => { + let trait_ref = TraitRef::new( + interner, + trait_ref.def_id, + [self_ty.clone().into()] + .into_iter() + .chain(trait_ref.args.iter()), + ); + let trait_id = match trait_ref.def_id { + SolverDefId::TraitId(id) => to_chalk_trait_id(id), + _ => unreachable!(), + }; + let substitution = + ChalkToNextSolver::from_nextsolver(trait_ref.args, interner); + let trait_ref = chalk_ir::TraitRef { trait_id, substitution }; + chalk_ir::WhereClause::Implemented(trait_ref) + } + rustc_type_ir::ExistentialPredicate::AutoTrait(trait_) => { + let trait_id = match trait_ { + SolverDefId::TraitId(id) => to_chalk_trait_id(id), + _ => unreachable!(), + }; + let substitution = chalk_ir::Substitution::empty(Interner); + let trait_ref = chalk_ir::TraitRef { trait_id, substitution }; + chalk_ir::WhereClause::Implemented(trait_ref) + } + rustc_type_ir::ExistentialPredicate::Projection( + existential_projection, + ) => { + let projection = ProjectionPredicate { + projection_term: AliasTerm::new( + interner, + existential_projection.def_id, + [self_ty.clone().into()] + .iter() + .chain(existential_projection.args.clone().iter()), + ), + term: existential_projection.term.clone(), + }; + let associated_ty_id = match projection.projection_term.def_id { + SolverDefId::TypeAliasId(id) => to_assoc_type_id(id), + _ => unreachable!(), + }; + let substitution = ChalkToNextSolver::from_nextsolver( + projection.projection_term.args, + interner, + ); + let alias = chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { + associated_ty_id, + substitution, + }); + let ty = match projection.term { + Term::Ty(ty) => ty, + _ => unreachable!(), + }; + let ty = ChalkToNextSolver::from_nextsolver(ty, interner); + let alias_eq = chalk_ir::AliasEq { alias, ty }; + chalk_ir::WhereClause::AliasEq(alias_eq) + } + }; + chalk_ir::Binders::new(binders, where_clause) + }), + ); + let binders = chalk_ir::VariableKinds::from1( + Interner, + chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General), + ); + let bounds = chalk_ir::Binders::new(binders, bounds); + let dyn_ty = chalk_ir::DynTy { + bounds, + lifetime: ChalkToNextSolver::from_nextsolver(region, interner), + }; + TyKind::Dyn(dyn_ty) + } + + rustc_type_ir::TyKind::Slice(ty) => { + let ty = ChalkToNextSolver::from_nextsolver(ty, interner); + TyKind::Slice(ty) + } + + rustc_type_ir::TyKind::Foreign(def_id) => { + let id = match def_id { + SolverDefId::TypeAliasId(id) => to_foreign_def_id(id), + _ => unreachable!(), + }; + TyKind::Foreign(id) + } + rustc_type_ir::TyKind::Pat(_, _) => todo!(), + rustc_type_ir::TyKind::RawPtr(ty, mutability) => { + let mutability = match mutability { + rustc_ast_ir::Mutability::Mut => chalk_ir::Mutability::Mut, + rustc_ast_ir::Mutability::Not => chalk_ir::Mutability::Not, + }; + let ty = ChalkToNextSolver::from_nextsolver(ty, interner); + TyKind::Raw(mutability, ty) + } + rustc_type_ir::TyKind::FnDef(def_id, args) => { + let subst = ChalkToNextSolver::from_nextsolver(args, interner); + match def_id { + SolverDefId::FunctionId(id) => { + TyKind::FnDef(CallableDefId::FunctionId(id).to_chalk(interner.db()), subst) + } + SolverDefId::Ctor(Ctor::Enum(e)) => TyKind::FnDef( + CallableDefId::EnumVariantId(e).to_chalk(interner.db()), + subst, + ), + SolverDefId::Ctor(Ctor::Struct(s)) => { + TyKind::FnDef(CallableDefId::StructId(s).to_chalk(interner.db()), subst) + } + _ => unreachable!("Unexpected def id {:?}", def_id), + } + } + + rustc_type_ir::TyKind::Closure(def_id, args) => { + let id = match def_id { + SolverDefId::InternedClosureId(id) => id, + _ => unreachable!(), + }; + let subst = ChalkToNextSolver::from_nextsolver(args, interner); + TyKind::Closure(id.into(), subst) + } + rustc_type_ir::TyKind::CoroutineClosure(_, _) => todo!(), + rustc_type_ir::TyKind::Coroutine(def_id, args) => { + let id = match def_id { + SolverDefId::InternedCoroutineId(id) => id, + _ => unreachable!(), + }; + let subst = ChalkToNextSolver::from_nextsolver(args, interner); + TyKind::Coroutine(id.into(), subst) + } + rustc_type_ir::TyKind::CoroutineWitness(def_id, args) => { + let id = match def_id { + SolverDefId::InternedCoroutineId(id) => id, + _ => unreachable!(), + }; + let subst = ChalkToNextSolver::from_nextsolver(args, interner); + TyKind::CoroutineWitness(id.into(), subst) + } + + rustc_type_ir::TyKind::Param(_) => todo!(), + rustc_type_ir::TyKind::UnsafeBinder(_) => todo!(), + } + .intern(Interner) + } } impl<'db> ChalkToNextSolver<'db, Region<'db>> for chalk_ir::Lifetime { @@ -461,6 +799,40 @@ impl<'db> ChalkToNextSolver<'db, Region<'db>> for chalk_ir::Lifetime { }, ) } + + fn from_nextsolver(out: Region<'db>, interner: DbInterner<'db>) -> Self { + match out.kind() { + rustc_type_ir::RegionKind::ReEarlyParam(early) => todo!(), + rustc_type_ir::RegionKind::ReBound(db, bound) => chalk_ir::Lifetime::new( + Interner, + chalk_ir::LifetimeData::BoundVar(chalk_ir::BoundVar::new( + chalk_ir::DebruijnIndex::new(db.as_u32()), + bound.var.as_usize(), + )), + ), + rustc_type_ir::RegionKind::ReLateParam(_) => todo!(), + rustc_type_ir::RegionKind::ReStatic => { + chalk_ir::Lifetime::new(Interner, chalk_ir::LifetimeData::Static) + } + rustc_type_ir::RegionKind::ReVar(vid) => chalk_ir::Lifetime::new( + Interner, + chalk_ir::LifetimeData::InferenceVar(chalk_ir::InferenceVar::from(vid.as_u32())), + ), + rustc_type_ir::RegionKind::RePlaceholder(placeholder) => chalk_ir::Lifetime::new( + Interner, + chalk_ir::LifetimeData::Placeholder(chalk_ir::PlaceholderIndex { + idx: placeholder.bound.var.as_usize(), + ui: chalk_ir::UniverseIndex { counter: placeholder.universe.as_usize() }, + }), + ), + rustc_type_ir::RegionKind::ReErased => { + chalk_ir::Lifetime::new(Interner, chalk_ir::LifetimeData::Erased) + } + rustc_type_ir::RegionKind::ReError(_) => { + chalk_ir::Lifetime::new(Interner, chalk_ir::LifetimeData::Error) + } + } + } } impl<'db> ChalkToNextSolver<'db, Const<'db>> for chalk_ir::Const { @@ -505,6 +877,62 @@ impl<'db> ChalkToNextSolver<'db, Const<'db>> for chalk_ir::Const { }, ) } + + fn from_nextsolver(out: Const<'db>, interner: DbInterner<'db>) -> Self { + let value: chalk_ir::ConstValue = match out.kind() { + rustc_type_ir::ConstKind::Param(_) => unimplemented!(), + rustc_type_ir::ConstKind::Infer(rustc_type_ir::InferConst::Var(var)) => { + chalk_ir::ConstValue::InferenceVar(chalk_ir::InferenceVar::from(var.as_u32())) + } + rustc_type_ir::ConstKind::Infer(rustc_type_ir::InferConst::Fresh(fresh)) => { + panic!("Vars should not be freshened.") + } + rustc_type_ir::ConstKind::Bound(debruijn_index, var) => { + chalk_ir::ConstValue::BoundVar(chalk_ir::BoundVar::new( + chalk_ir::DebruijnIndex::new(debruijn_index.as_u32()), + var.index(), + )) + } + rustc_type_ir::ConstKind::Placeholder(placeholder_const) => { + chalk_ir::ConstValue::Placeholder(chalk_ir::PlaceholderIndex { + ui: chalk_ir::UniverseIndex { counter: placeholder_const.universe.as_usize() }, + idx: placeholder_const.bound.as_usize(), + }) + } + rustc_type_ir::ConstKind::Unevaluated(unevaluated_const) => { + let id = match unevaluated_const.def { + SolverDefId::ConstId(id) => GeneralConstId::ConstId(id), + SolverDefId::StaticId(id) => GeneralConstId::StaticId(id), + _ => unreachable!(), + }; + let subst = ChalkToNextSolver::from_nextsolver(unevaluated_const.args, interner); + chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { + interned: ConstScalar::UnevaluatedConst(id, subst), + }) + } + rustc_type_ir::ConstKind::Value(value_const) => { + let bytes = value_const.value.inner(); + let value = chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { + // SAFETY: will never use this without a db + interned: ConstScalar::Bytes(bytes.0.clone(), unsafe { + std::mem::transmute::, MemoryMap<'static>>(bytes.1.clone()) + }), + }); + return chalk_ir::ConstData { + ty: ChalkToNextSolver::from_nextsolver(value_const.ty, interner), + value, + } + .intern(Interner); + } + rustc_type_ir::ConstKind::Error(_) => { + chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { + interned: ConstScalar::Unknown, + }) + } + rustc_type_ir::ConstKind::Expr(_) => unimplemented!(), + }; + chalk_ir::ConstData { ty: crate::TyKind::Error.intern(Interner), value }.intern(Interner) + } } impl<'db> ChalkToNextSolver<'db, rustc_type_ir::FnSigTys>> @@ -518,6 +946,13 @@ impl<'db> ChalkToNextSolver<'db, rustc_type_ir::FnSigTys>> ), } } + + fn from_nextsolver( + out: rustc_type_ir::FnSigTys>, + interner: DbInterner<'db>, + ) -> Self { + todo!() + } } impl< @@ -536,6 +971,13 @@ impl< binders.to_nextsolver(interner), ) } + + fn from_nextsolver( + out: rustc_type_ir::Binder, U>, + interner: DbInterner<'db>, + ) -> Self { + todo!() + } } impl<'db> ChalkToNextSolver<'db, BoundVarKinds> for chalk_ir::VariableKinds { @@ -545,6 +987,10 @@ impl<'db> ChalkToNextSolver<'db, BoundVarKinds> for chalk_ir::VariableKinds) -> Self { + todo!() + } } impl<'db> ChalkToNextSolver<'db, BoundVarKind> for chalk_ir::VariableKind { @@ -555,6 +1001,10 @@ impl<'db> ChalkToNextSolver<'db, BoundVarKind> for chalk_ir::VariableKind BoundVarKind::Const, } } + + fn from_nextsolver(out: BoundVarKind, interner: DbInterner<'db>) -> Self { + todo!() + } } impl<'db> ChalkToNextSolver<'db, GenericArg<'db>> for chalk_ir::GenericArg { @@ -565,6 +1015,10 @@ impl<'db> ChalkToNextSolver<'db, GenericArg<'db>> for chalk_ir::GenericArg const_.to_nextsolver(interner).into(), } } + + fn from_nextsolver(out: GenericArg<'db>, interner: DbInterner<'db>) -> Self { + todo!() + } } impl<'db> ChalkToNextSolver<'db, GenericArgs<'db>> for chalk_ir::Substitution { fn to_nextsolver(&self, interner: DbInterner<'db>) -> GenericArgs<'db> { @@ -573,6 +1027,31 @@ impl<'db> ChalkToNextSolver<'db, GenericArgs<'db>> for chalk_ir::Substitution GenericArg<'db> { arg.to_nextsolver(interner) }), ) } + + fn from_nextsolver(out: GenericArgs<'db>, interner: DbInterner<'db>) -> Self { + let mut substs = Vec::with_capacity(out.len()); + for arg in out.iter() { + match arg.clone().kind() { + rustc_type_ir::GenericArgKind::Type(ty) => { + let ty = ChalkToNextSolver::from_nextsolver(ty, interner); + substs.push(chalk_ir::GenericArgData::Ty(ty).intern(Interner)); + } + rustc_type_ir::GenericArgKind::Lifetime(region) => { + let lifetime = ChalkToNextSolver::from_nextsolver(region, interner); + substs.push(chalk_ir::GenericArgData::Lifetime(lifetime).intern(Interner)); + } + rustc_type_ir::GenericArgKind::Const(const_) => { + substs.push( + chalk_ir::GenericArgData::Const(ChalkToNextSolver::from_nextsolver( + const_, interner, + )) + .intern(Interner), + ); + } + } + } + Substitution::from_iter(Interner, substs) + } } impl<'db> ChalkToNextSolver<'db, Tys<'db>> for chalk_ir::Substitution { @@ -588,18 +1067,30 @@ impl<'db> ChalkToNextSolver<'db, Tys<'db>> for chalk_ir::Substitution }), ) } + + fn from_nextsolver(out: Tys<'db>, interner: DbInterner<'db>) -> Self { + todo!() + } } impl<'db> ChalkToNextSolver<'db, rustc_type_ir::DebruijnIndex> for chalk_ir::DebruijnIndex { fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_type_ir::DebruijnIndex { rustc_type_ir::DebruijnIndex::from_u32(self.depth()) } + + fn from_nextsolver(out: rustc_type_ir::DebruijnIndex, interner: DbInterner<'db>) -> Self { + todo!() + } } impl<'db> ChalkToNextSolver<'db, rustc_type_ir::UniverseIndex> for chalk_ir::UniverseIndex { fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_type_ir::UniverseIndex { rustc_type_ir::UniverseIndex::from_u32(self.counter as u32) } + + fn from_nextsolver(out: rustc_type_ir::UniverseIndex, interner: DbInterner<'db>) -> Self { + todo!() + } } impl<'db> ChalkToNextSolver<'db, rustc_type_ir::InferTy> @@ -618,6 +1109,10 @@ impl<'db> ChalkToNextSolver<'db, rustc_type_ir::InferTy> } } } + + fn from_nextsolver(out: rustc_type_ir::InferTy, interner: DbInterner<'db>) -> Self { + todo!() + } } impl<'db> ChalkToNextSolver<'db, rustc_ast_ir::Mutability> for chalk_ir::Mutability { @@ -627,6 +1122,10 @@ impl<'db> ChalkToNextSolver<'db, rustc_ast_ir::Mutability> for chalk_ir::Mutabil chalk_ir::Mutability::Not => rustc_ast_ir::Mutability::Not, } } + + fn from_nextsolver(out: rustc_ast_ir::Mutability, interner: DbInterner<'db>) -> Self { + todo!() + } } impl<'db> ChalkToNextSolver<'db, rustc_type_ir::Variance> for crate::Variance { @@ -638,16 +1137,65 @@ impl<'db> ChalkToNextSolver<'db, rustc_type_ir::Variance> for crate::Variance { crate::Variance::Bivariant => rustc_type_ir::Variance::Bivariant, } } + + fn from_nextsolver(out: rustc_type_ir::Variance, interner: DbInterner<'db>) -> Self { + todo!() + } } -impl<'db> ChalkToNextSolver<'db, Canonical<'db, Goal, Predicate<'db>>>> - for chalk_ir::Canonical>> +impl<'db> ChalkToNextSolver<'db, rustc_type_ir::Variance> for chalk_ir::Variance { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_type_ir::Variance { + match self { + chalk_ir::Variance::Covariant => rustc_type_ir::Variance::Covariant, + chalk_ir::Variance::Invariant => rustc_type_ir::Variance::Invariant, + chalk_ir::Variance::Contravariant => rustc_type_ir::Variance::Contravariant, + } + } + + fn from_nextsolver(out: rustc_type_ir::Variance, interner: DbInterner<'db>) -> Self { + todo!() + } +} + +impl<'db> ChalkToNextSolver<'db, VariancesOf> for chalk_ir::Variances { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> VariancesOf { + VariancesOf::new_from_iter( + interner, + self.as_slice(Interner).iter().map(|v| v.to_nextsolver(interner)), + ) + } + + fn from_nextsolver(out: VariancesOf, interner: DbInterner<'db>) -> Self { + todo!() + } +} + +impl<'db> ChalkToNextSolver<'db, Goal, Predicate<'db>>> + for chalk_ir::InEnvironment> { - fn to_nextsolver( - &self, + fn to_nextsolver(&self, interner: DbInterner<'db>) -> Goal, Predicate<'db>> { + Goal::new( + interner, + self.environment.to_nextsolver(interner), + self.goal.to_nextsolver(interner), + ) + } + + fn from_nextsolver( + out: Goal, Predicate<'db>>, interner: DbInterner<'db>, - ) -> Canonical<'db, Goal, Predicate<'db>>> { - let param_env = self.value.environment.to_nextsolver(interner); + ) -> Self { + chalk_ir::InEnvironment { + environment: ChalkToNextSolver::from_nextsolver(out.param_env, interner), + goal: ChalkToNextSolver::from_nextsolver(out.predicate, interner), + } + } +} + +impl<'db, T: HasInterner + ChalkToNextSolver<'db, U>, U> + ChalkToNextSolver<'db, Canonical<'db, U>> for chalk_ir::Canonical +{ + fn to_nextsolver(&self, interner: DbInterner<'db>) -> Canonical<'db, U> { let variables = CanonicalVars::new_from_iter( interner, self.binders.iter(Interner).map(|k| match &k.kind { @@ -672,10 +1220,49 @@ impl<'db> ChalkToNextSolver<'db, Canonical<'db, Goal, Predicate< ); Canonical { max_universe: UniverseIndex::ROOT, - value: Goal::new(interner, param_env, self.value.goal.to_nextsolver(interner)), + value: self.value.to_nextsolver(interner), variables, } } + + fn from_nextsolver(out: Canonical<'db, U>, interner: DbInterner<'db>) -> Self { + let binders = chalk_ir::CanonicalVarKinds::from_iter( + Interner, + out.variables.iter().map(|v| match v { + rustc_type_ir::CanonicalVarKind::Ty( + rustc_type_ir::CanonicalTyVarKind::General(ui), + ) => chalk_ir::CanonicalVarKind::new( + chalk_ir::VariableKind::Ty(TyVariableKind::General), + chalk_ir::UniverseIndex { counter: ui.as_usize() }, + ), + rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::Int) => { + chalk_ir::CanonicalVarKind::new( + chalk_ir::VariableKind::Ty(TyVariableKind::Integer), + chalk_ir::UniverseIndex::root(), + ) + } + rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::Float) => { + chalk_ir::CanonicalVarKind::new( + chalk_ir::VariableKind::Ty(TyVariableKind::Float), + chalk_ir::UniverseIndex::root(), + ) + } + rustc_type_ir::CanonicalVarKind::PlaceholderTy(_) => todo!(), + rustc_type_ir::CanonicalVarKind::Region(ui) => chalk_ir::CanonicalVarKind::new( + chalk_ir::VariableKind::Lifetime, + chalk_ir::UniverseIndex { counter: ui.as_usize() }, + ), + rustc_type_ir::CanonicalVarKind::PlaceholderRegion(_) => todo!(), + rustc_type_ir::CanonicalVarKind::Const(ui) => chalk_ir::CanonicalVarKind::new( + chalk_ir::VariableKind::Const(chalk_ir::TyKind::Error.intern(Interner)), + chalk_ir::UniverseIndex { counter: ui.as_usize() }, + ), + rustc_type_ir::CanonicalVarKind::PlaceholderConst(_) => todo!(), + }), + ); + let value = ChalkToNextSolver::from_nextsolver(out.value, interner); + chalk_ir::Canonical { binders, value } + } } impl<'db> ChalkToNextSolver<'db, Predicate<'db>> for chalk_ir::Goal { @@ -693,7 +1280,16 @@ impl<'db> ChalkToNextSolver<'db, Predicate<'db>> for chalk_ir::Goal { } chalk_ir::GoalData::All(goals) => panic!("Should not be constructed."), chalk_ir::GoalData::Not(goal) => panic!("Should not be constructed."), - chalk_ir::GoalData::EqGoal(eq_goal) => panic!("Should not be constructed."), + chalk_ir::GoalData::EqGoal(eq_goal) => { + let pred_kind = PredicateKind::AliasRelate( + Term::Ty(eq_goal.a.assert_ty_ref(Interner).clone().to_nextsolver(interner)), + Term::Ty(eq_goal.b.assert_ty_ref(Interner).clone().to_nextsolver(interner)), + rustc_type_ir::AliasRelationDirection::Equate, + ); + let pred_kind = + Binder::bind_with_vars(pred_kind, BoundVarKinds::new_from_iter(interner, [])); + Predicate::new(interner, pred_kind) + } chalk_ir::GoalData::SubtypeGoal(subtype_goal) => { let subtype_predicate = SubtypePredicate { a: subtype_goal.a.to_nextsolver(interner), @@ -718,6 +1314,124 @@ impl<'db> ChalkToNextSolver<'db, Predicate<'db>> for chalk_ir::Goal { chalk_ir::GoalData::CannotProve => panic!("Should not be constructed."), } } + + fn from_nextsolver(out: Predicate<'db>, interner: DbInterner<'db>) -> Self { + chalk_ir::Goal::new( + Interner, + match out.kind().skip_binder() { + rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::Trait( + trait_pred, + )) => { + let trait_ref = + ChalkToNextSolver::from_nextsolver(trait_pred.trait_ref, interner); + let where_clause = chalk_ir::WhereClause::Implemented(trait_ref); + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(where_clause)) + } + rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::Projection( + proj_predicate, + )) => { + let associated_ty_id = match proj_predicate.def_id() { + SolverDefId::TypeAliasId(id) => to_assoc_type_id(id), + _ => unreachable!(), + }; + let substitution = ChalkToNextSolver::from_nextsolver( + proj_predicate.projection_term.args, + interner, + ); + let alias = chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { + associated_ty_id, + substitution, + }); + let ty = match proj_predicate.term.kind() { + rustc_type_ir::TermKind::Ty(ty) => ty, + rustc_type_ir::TermKind::Const(_) => todo!(), + }; + let ty = ChalkToNextSolver::from_nextsolver(ty, interner); + let alias_eq = chalk_ir::AliasEq { alias, ty }; + let where_clause = chalk_ir::WhereClause::AliasEq(alias_eq); + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(where_clause)) + } + rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::TypeOutlives( + outlives, + )) => { + let lifetime = ChalkToNextSolver::from_nextsolver(outlives.1, interner); + let ty = ChalkToNextSolver::from_nextsolver(outlives.0, interner); + let where_clause = + chalk_ir::WhereClause::TypeOutlives(chalk_ir::TypeOutlives { + lifetime, + ty, + }); + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(where_clause)) + } + rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::RegionOutlives(outlives), + ) => { + let a = ChalkToNextSolver::from_nextsolver(outlives.0, interner); + let b = ChalkToNextSolver::from_nextsolver(outlives.1, interner); + let where_clause = + chalk_ir::WhereClause::LifetimeOutlives(chalk_ir::LifetimeOutlives { + a, + b, + }); + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(where_clause)) + } + rustc_type_ir::PredicateKind::Clause(_) => todo!(), + rustc_type_ir::PredicateKind::DynCompatible(_) => todo!(), + rustc_type_ir::PredicateKind::Subtype(subtype_predicate) => todo!(), + rustc_type_ir::PredicateKind::Coerce(coerce_predicate) => todo!(), + rustc_type_ir::PredicateKind::ConstEquate(_, _) => todo!(), + rustc_type_ir::PredicateKind::Ambiguous => todo!(), + rustc_type_ir::PredicateKind::NormalizesTo(normalizes_to) => todo!(), + rustc_type_ir::PredicateKind::AliasRelate( + alias_term, + ty_term, + alias_relation_direction, + ) => { + let ty = match ty_term { + Term::Ty(ty) => ChalkToNextSolver::from_nextsolver(ty, interner), + Term::Const(..) => todo!(), + }; + match alias_term { + Term::Ty(alias_ty) => match alias_ty.kind() { + rustc_type_ir::TyKind::Alias(kind, alias) => match kind { + rustc_type_ir::AliasTyKind::Projection => { + let associated_ty_id = match alias.def_id { + SolverDefId::TypeAliasId(id) => to_assoc_type_id(id), + _ => todo!(), + }; + let substitution = + ChalkToNextSolver::from_nextsolver(alias.args, interner); + let proj_ty = + chalk_ir::ProjectionTy { associated_ty_id, substitution }; + let alias = chalk_ir::AliasTy::Projection(proj_ty); + let alias_eq = chalk_ir::AliasEq { alias, ty }; + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds( + chalk_ir::WhereClause::AliasEq(alias_eq), + )) + } + _ => todo!(), + }, + rustc_type_ir::TyKind::Infer(var) => { + assert!(matches!( + alias_relation_direction, + rustc_type_ir::AliasRelationDirection::Equate + )); + let a: chalk_ir::Ty<_> = + ChalkToNextSolver::from_nextsolver(alias_ty, interner); + let eq_goal = chalk_ir::EqGoal { + a: chalk_ir::GenericArgData::Ty(a).intern(Interner), + b: chalk_ir::GenericArgData::Ty(ty).intern(Interner), + }; + chalk_ir::GoalData::EqGoal(eq_goal) + } + _ => todo!("Unexpected alias term {:?}", alias_term), + }, + Term::Const(..) => todo!(), + } + } + }, + ) + } } impl<'db> ChalkToNextSolver<'db, ParamEnv<'db>> for chalk_ir::Environment { @@ -730,12 +1444,32 @@ impl<'db> ChalkToNextSolver<'db, ParamEnv<'db>> for chalk_ir::Environment, interner: DbInterner<'db>) -> Self { + let clauses = chalk_ir::ProgramClauses::from_iter( + Interner, + out.clauses.iter().map(|c| -> chalk_ir::ProgramClause { + ChalkToNextSolver::from_nextsolver(c, interner) + }), + ); + chalk_ir::Environment { clauses } + } } impl<'db> ChalkToNextSolver<'db, Clause<'db>> for chalk_ir::ProgramClause { fn to_nextsolver(&self, interner: DbInterner<'db>) -> Clause<'db> { Clause(Predicate::new(interner, self.data(Interner).0.to_nextsolver(interner))) } + + fn from_nextsolver(out: Clause<'db>, interner: DbInterner<'db>) -> Self { + chalk_ir::ProgramClause::new( + Interner, + chalk_ir::ProgramClauseData(chalk_ir::Binders::empty( + Interner, + ChalkToNextSolver::from_nextsolver(out.0.kind().skip_binder(), interner), + )), + ) + } } impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> @@ -746,6 +1480,14 @@ impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> assert!(self.constraints.is_empty(Interner)); self.consequence.to_nextsolver(interner) } + fn from_nextsolver(out: PredicateKind<'db>, interner: DbInterner<'db>) -> Self { + chalk_ir::ProgramClauseImplication { + consequence: ChalkToNextSolver::from_nextsolver(out, interner), + conditions: chalk_ir::Goals::empty(Interner), + constraints: chalk_ir::Constraints::empty(Interner), + priority: chalk_ir::ClausePriority::High, + } + } } impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> for chalk_ir::DomainGoal { @@ -864,6 +1606,98 @@ impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> for chalk_ir::DomainGoal panic!("Should not be constructed."), } } + + fn from_nextsolver(out: PredicateKind<'db>, interner: DbInterner<'db>) -> Self { + let domain_goal = match out { + rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::Trait(trait_pred)) => { + let trait_ref = ChalkToNextSolver::from_nextsolver(trait_pred.trait_ref, interner); + let where_clause = chalk_ir::WhereClause::Implemented(trait_ref); + chalk_ir::DomainGoal::Holds(where_clause) + } + rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::Projection( + proj_predicate, + )) => { + let associated_ty_id = match proj_predicate.def_id() { + SolverDefId::TypeAliasId(id) => to_assoc_type_id(id), + _ => unreachable!(), + }; + let substitution = ChalkToNextSolver::from_nextsolver( + proj_predicate.projection_term.args, + interner, + ); + let alias = chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { + associated_ty_id, + substitution, + }); + let ty = match proj_predicate.term.kind() { + rustc_type_ir::TermKind::Ty(ty) => ty, + rustc_type_ir::TermKind::Const(_) => todo!(), + }; + let ty = ChalkToNextSolver::from_nextsolver(ty, interner); + let alias_eq = chalk_ir::AliasEq { alias, ty }; + let where_clause = chalk_ir::WhereClause::AliasEq(alias_eq); + chalk_ir::DomainGoal::Holds(where_clause) + } + rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::TypeOutlives( + outlives, + )) => { + let lifetime = ChalkToNextSolver::from_nextsolver(outlives.1, interner); + let ty = ChalkToNextSolver::from_nextsolver(outlives.0, interner); + let where_clause = + chalk_ir::WhereClause::TypeOutlives(chalk_ir::TypeOutlives { lifetime, ty }); + chalk_ir::DomainGoal::Holds(where_clause) + } + rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::RegionOutlives( + outlives, + )) => { + let a = ChalkToNextSolver::from_nextsolver(outlives.0, interner); + let b = ChalkToNextSolver::from_nextsolver(outlives.1, interner); + let where_clause = + chalk_ir::WhereClause::LifetimeOutlives(chalk_ir::LifetimeOutlives { a, b }); + chalk_ir::DomainGoal::Holds(where_clause) + } + rustc_type_ir::PredicateKind::Clause(_) => todo!(), + rustc_type_ir::PredicateKind::DynCompatible(_) => todo!(), + rustc_type_ir::PredicateKind::Subtype(subtype_predicate) => todo!(), + rustc_type_ir::PredicateKind::Coerce(coerce_predicate) => todo!(), + rustc_type_ir::PredicateKind::ConstEquate(_, _) => todo!(), + rustc_type_ir::PredicateKind::Ambiguous => todo!(), + rustc_type_ir::PredicateKind::NormalizesTo(normalizes_to) => todo!(), + rustc_type_ir::PredicateKind::AliasRelate( + alias_term, + ty_term, + alias_relation_direction, + ) => { + let alias = match alias_term { + Term::Ty(alias_ty) => match alias_ty.kind() { + rustc_type_ir::TyKind::Alias(kind, alias) => match kind { + rustc_type_ir::AliasTyKind::Projection => { + let associated_ty_id = match alias.def_id { + SolverDefId::TypeAliasId(id) => to_assoc_type_id(id), + _ => todo!(), + }; + let substitution = + ChalkToNextSolver::from_nextsolver(alias.args, interner); + let proj_ty = + chalk_ir::ProjectionTy { associated_ty_id, substitution }; + chalk_ir::AliasTy::Projection(proj_ty) + } + _ => todo!(), + }, + _ => todo!(), + }, + Term::Const(..) => todo!(), + }; + let ty = match ty_term { + Term::Ty(ty) => ChalkToNextSolver::from_nextsolver(ty, interner), + Term::Const(..) => todo!(), + }; + let alias_eq = chalk_ir::AliasEq { alias, ty }; + chalk_ir::DomainGoal::Holds(chalk_ir::WhereClause::AliasEq(alias_eq)) + } + }; + domain_goal + } } impl<'db> ChalkToNextSolver<'db, TraitRef<'db>> for chalk_ir::TraitRef { @@ -871,6 +1705,15 @@ impl<'db> ChalkToNextSolver<'db, TraitRef<'db>> for chalk_ir::TraitRef let args = self.substitution.to_nextsolver(interner); TraitRef::new_from_args(interner, from_chalk_trait_id(self.trait_id).into(), args) } + + fn from_nextsolver(out: TraitRef<'db>, interner: DbInterner<'db>) -> Self { + let trait_id = match out.def_id { + SolverDefId::TraitId(id) => to_chalk_trait_id(id), + _ => unreachable!(), + }; + let substitution = ChalkToNextSolver::from_nextsolver(out.args, interner); + chalk_ir::TraitRef { trait_id, substitution } + } } impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> for chalk_ir::WhereClause { @@ -911,6 +1754,10 @@ impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> for chalk_ir::WhereClause, interner: DbInterner<'db>) -> Self { + todo!() + } } pub fn convert_canonical_args_for_result<'db>( @@ -956,7 +1803,10 @@ pub fn convert_canonical_args_for_result<'db>( binders, value: chalk_ir::ConstrainedSubst { constraints: chalk_ir::Constraints::empty(Interner), - subst: convert_args_for_result(interner, &value), + subst: ChalkToNextSolver::from_nextsolver( + GenericArgs::new_from_iter(interner, value), + interner, + ), }, } } @@ -1047,7 +1897,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) rustc_type_ir::TyKind::Adt(def, args) => { let adt_id = def.inner().id; - let subst = convert_args_for_result(interner, args.as_slice()); + let subst = ChalkToNextSolver::from_nextsolver(args, interner); TyKind::Adt(chalk_ir::AdtId(adt_id), subst) } @@ -1062,11 +1912,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) rustc_type_ir::InferTy::FloatVar(var) => { (InferenceVar::from(var.as_u32()), TyVariableKind::Float) } - rustc_type_ir::InferTy::FreshFloatTy(..) - | rustc_type_ir::InferTy::FreshIntTy(..) - | rustc_type_ir::InferTy::FreshTy(..) => { - panic!("Freshening shouldn't happen.") - } + _ => todo!(), }; TyKind::InferenceVar(var, kind) } @@ -1086,7 +1932,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) let subst = Substitution::from_iter( Interner, tys.iter().map(|ty| { - chalk_ir::GenericArgData::Ty(convert_ty_for_result(interner, ty)) + chalk_ir::GenericArgData::Ty(ChalkToNextSolver::from_nextsolver(ty, interner)) .intern(Interner) }), ); @@ -1094,8 +1940,8 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) } rustc_type_ir::TyKind::Array(ty, const_) => { - let ty = convert_ty_for_result(interner, ty); - let const_ = convert_const_for_result(interner, const_); + let ty = ChalkToNextSolver::from_nextsolver(ty, interner); + let const_ = ChalkToNextSolver::from_nextsolver(const_, interner); TyKind::Array(ty, const_) } @@ -1106,7 +1952,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) _ => unreachable!(), }; let associated_ty_id = to_assoc_type_id(assoc_ty_id); - let substitution = convert_args_for_result(interner, alias_ty.args.as_slice()); + let substitution = ChalkToNextSolver::from_nextsolver(alias_ty.args, interner); TyKind::AssociatedType(associated_ty_id, substitution) } rustc_type_ir::AliasTyKind::Opaque => { @@ -1114,14 +1960,14 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) SolverDefId::InternedOpaqueTyId(id) => id, _ => unreachable!(), }; - let substitution = convert_args_for_result(interner, alias_ty.args.as_slice()); + let substitution = ChalkToNextSolver::from_nextsolver(alias_ty.args, interner); TyKind::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy { opaque_ty_id: opaque_ty_id.into(), substitution, })) } - rustc_type_ir::AliasTyKind::Inherent => unimplemented!(), - rustc_type_ir::AliasTyKind::Free => unimplemented!(), + rustc_type_ir::AliasTyKind::Inherent => todo!(), + rustc_type_ir::AliasTyKind::Free => todo!(), }, // For `Placeholder`, `Bound` and `Param`, see the comment on the reverse conversion. @@ -1156,7 +2002,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) interner, bound_sig.skip_binder().inputs_and_output.iter().map(|a| a.into()), ); - let substitution = convert_args_for_result(interner, args.as_slice()); + let substitution = ChalkToNextSolver::from_nextsolver(args, interner); let substitution = chalk_ir::FnSubst(substitution); let fnptr = chalk_ir::FnPointer { num_binders, sig, substitution }; TyKind::Function(fnptr) @@ -1199,11 +2045,11 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) let trait_ref = TraitRef::new( interner, trait_ref.def_id, - [self_ty.into()].into_iter().chain(trait_ref.args.iter()), + [self_ty.clone().into()].into_iter().chain(trait_ref.args.iter()), ); let trait_id = to_chalk_trait_id(trait_ref.def_id.0); let substitution = - convert_args_for_result(interner, trait_ref.args.as_slice()); + ChalkToNextSolver::from_nextsolver(trait_ref.args, interner); let trait_ref = chalk_ir::TraitRef { trait_id, substitution }; chalk_ir::WhereClause::Implemented(trait_ref) } @@ -1221,19 +2067,19 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) projection_term: AliasTerm::new( interner, existential_projection.def_id, - [self_ty.into()] + [self_ty.clone().into()] .iter() - .chain(existential_projection.args.iter()), + .chain(existential_projection.args.clone().iter()), ), - term: existential_projection.term, + term: existential_projection.term.clone(), }; let associated_ty_id = match projection.projection_term.def_id { SolverDefId::TypeAliasId(id) => to_assoc_type_id(id), _ => unreachable!(), }; - let substitution = convert_args_for_result( + let substitution = ChalkToNextSolver::from_nextsolver( + projection.projection_term.args, interner, - projection.projection_term.args.as_slice(), ); let alias = chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { associated_ty_id, @@ -1243,7 +2089,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) Term::Ty(ty) => ty, _ => unreachable!(), }; - let ty = convert_ty_for_result(interner, ty); + let ty = ChalkToNextSolver::from_nextsolver(ty, interner); let alias_eq = chalk_ir::AliasEq { alias, ty }; chalk_ir::WhereClause::AliasEq(alias_eq) } @@ -1262,7 +2108,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) } rustc_type_ir::TyKind::Slice(ty) => { - let ty = convert_ty_for_result(interner, ty); + let ty = ChalkToNextSolver::from_nextsolver(ty, interner); TyKind::Slice(ty) } @@ -1273,24 +2119,29 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) }; TyKind::Foreign(to_foreign_def_id(def_id)) } - rustc_type_ir::TyKind::Pat(_, _) => unimplemented!(), + rustc_type_ir::TyKind::Pat(_, _) => todo!(), rustc_type_ir::TyKind::RawPtr(ty, mutability) => { let mutability = match mutability { rustc_ast_ir::Mutability::Mut => chalk_ir::Mutability::Mut, rustc_ast_ir::Mutability::Not => chalk_ir::Mutability::Not, }; - let ty = convert_ty_for_result(interner, ty); + let ty = ChalkToNextSolver::from_nextsolver(ty, interner); TyKind::Raw(mutability, ty) } rustc_type_ir::TyKind::FnDef(def_id, args) => { - let id = match def_id { - SolverDefId::FunctionId(id) => CallableDefId::FunctionId(id), - SolverDefId::Ctor(Ctor::Struct(id)) => CallableDefId::StructId(id), - SolverDefId::Ctor(Ctor::Enum(id)) => CallableDefId::EnumVariantId(id), - _ => unreachable!(), - }; - let subst = convert_args_for_result(interner, args.as_slice()); - TyKind::FnDef(id.to_chalk(interner.db()), subst) + let subst = ChalkToNextSolver::from_nextsolver(args, interner); + match def_id { + SolverDefId::FunctionId(id) => { + TyKind::FnDef(CallableDefId::FunctionId(id).to_chalk(interner.db()), subst) + } + SolverDefId::Ctor(Ctor::Enum(e)) => { + TyKind::FnDef(CallableDefId::EnumVariantId(e).to_chalk(interner.db()), subst) + } + SolverDefId::Ctor(Ctor::Struct(s)) => { + TyKind::FnDef(CallableDefId::StructId(s).to_chalk(interner.db()), subst) + } + _ => unreachable!("Unexpected def id {:?}", def_id), + } } rustc_type_ir::TyKind::Closure(def_id, args) => { @@ -1298,16 +2149,16 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) SolverDefId::InternedClosureId(id) => id, _ => unreachable!(), }; - let subst = convert_args_for_result(interner, args.as_slice()); + let subst = ChalkToNextSolver::from_nextsolver(args, interner); TyKind::Closure(id.into(), subst) } - rustc_type_ir::TyKind::CoroutineClosure(_, _) => unimplemented!(), + rustc_type_ir::TyKind::CoroutineClosure(_, _) => todo!(), rustc_type_ir::TyKind::Coroutine(def_id, args) => { let id = match def_id { SolverDefId::InternedCoroutineId(id) => id, _ => unreachable!(), }; - let subst = convert_args_for_result(interner, args.as_slice()); + let subst = ChalkToNextSolver::from_nextsolver(args, interner); TyKind::Coroutine(id.into(), subst) } rustc_type_ir::TyKind::CoroutineWitness(def_id, args) => { @@ -1315,7 +2166,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) SolverDefId::InternedCoroutineId(id) => id, _ => unreachable!(), }; - let subst = convert_args_for_result(interner, args.as_slice()); + let subst = ChalkToNextSolver::from_nextsolver(args, interner); TyKind::CoroutineWitness(id.into(), subst) } @@ -1419,3 +2270,17 @@ pub fn convert_region_for_result<'db>( }; chalk_ir::Lifetime::new(Interner, lifetime) } + +pub trait InferenceVarExt { + fn to_vid(self) -> rustc_type_ir::TyVid; + fn from_vid(vid: rustc_type_ir::TyVid) -> InferenceVar; +} + +impl InferenceVarExt for InferenceVar { + fn to_vid(self) -> rustc_type_ir::TyVid { + rustc_type_ir::TyVid::from_u32(self.index()) + } + fn from_vid(vid: rustc_type_ir::TyVid) -> InferenceVar { + InferenceVar::from(vid.as_u32()) + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs index 1d0ee0e488dc..b9b2950a94a3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs @@ -14,7 +14,7 @@ use crate::{ db::HirDatabase, next_solver::{ ClauseKind, CoercePredicate, PredicateKind, SubtypePredicate, - mapping::{ChalkToNextSolver, convert_args_for_result}, + mapping::ChalkToNextSolver, util::sizedness_fast_path, }, }; @@ -200,7 +200,7 @@ impl<'db> SolverDelegate for SolverContext<'db> { SolverDefId::StaticId(c) => GeneralConstId::StaticId(c), _ => unreachable!(), }; - let subst = convert_args_for_result(self.interner, uv.args.as_slice()); + let subst = ChalkToNextSolver::from_nextsolver(uv.args, self.interner); let ec = self.cx().db.const_eval(c, subst, None).ok()?; Some(ec.to_nextsolver(self.interner)) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs index 51cf5be1372c..0c58e031c3a0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -1,6 +1,7 @@ //! Trait solving using Chalk. use core::fmt; +use std::hash::Hash; use chalk_ir::{DebruijnIndex, GoalData, fold::TypeFoldable}; use chalk_solve::rust_ir; @@ -20,17 +21,9 @@ use stdx::never; use triomphe::Arc; use crate::{ - AliasEq, AliasTy, Canonical, DomainGoal, Goal, InEnvironment, Interner, ProjectionTy, - ProjectionTyExt, TraitRefExt, Ty, TyKind, TypeFlags, WhereClause, - db::HirDatabase, - infer::unify::InferenceTable, - next_solver::{ - DbInterner, GenericArg, SolverContext, Span, - infer::{DbInternerInferExt, InferCtxt}, - mapping::{ChalkToNextSolver, convert_canonical_args_for_result}, - util::mini_canonicalize, - }, - utils::UnevaluatedConstEvaluatorFolder, + db::HirDatabase, infer::unify::InferenceTable, next_solver::{ + infer::{DbInternerInferExt, InferCtxt}, mapping::{convert_canonical_args_for_result, ChalkToNextSolver}, util::mini_canonicalize, DbInterner, GenericArg, Predicate, SolverContext, Span + }, utils::UnevaluatedConstEvaluatorFolder, AliasEq, AliasTy, Canonical, DomainGoal, Goal, InEnvironment, Interner, ProjectionTy, ProjectionTyExt, TraitRefExt, Ty, TyKind, TypeFlags, WhereClause }; /// A set of clauses that we assume to be true. E.g. if we are inside this function: @@ -231,7 +224,6 @@ impl NextTraitSolveResult { } } -/// Solve a trait goal using Chalk. pub fn next_trait_solve( db: &dyn HirDatabase, krate: Crate, @@ -290,6 +282,57 @@ pub fn next_trait_solve( } } +pub fn next_trait_solve_canonical<'db>( + db: &'db dyn HirDatabase, + krate: Crate, + block: Option, + goal: crate::next_solver::Canonical<'db, crate::next_solver::Goal<'db, Predicate<'db>>>, +) -> NextTraitSolveResult { + // FIXME: should use analysis_in_body, but that needs GenericDefId::Block + let context = SolverContext( + DbInterner::new_with(db, Some(krate), block) + .infer_ctxt() + .build(TypingMode::non_body_analysis()), + ); + + tracing::info!(?goal); + + let (goal, var_values) = + context.instantiate_canonical(&goal); + tracing::info!(?var_values); + + let res = context.evaluate_root_goal( + goal.clone(), + Span::dummy(), + None + ); + + let vars = + var_values.var_values.iter().map(|g| context.0.resolve_vars_if_possible(g)).collect(); + let canonical_var_values = mini_canonicalize(context, vars); + + let res = res.map(|r| (r.has_changed, r.certainty, canonical_var_values)); + + tracing::debug!("solve_nextsolver({:?}) => {:?}", goal, res); + + match res { + Err(_) => NextTraitSolveResult::NoSolution, + Ok((_, Certainty::Yes, args)) => NextTraitSolveResult::Certain( + convert_canonical_args_for_result(DbInterner::new_with(db, Some(krate), block), args) + ), + Ok((_, Certainty::Maybe(_), args)) => { + let subst = convert_canonical_args_for_result( + DbInterner::new_with(db, Some(krate), block), + args, + ); + NextTraitSolveResult::Uncertain(chalk_ir::Canonical { + binders: subst.binders, + value: subst.value.subst, + }) + } + } +} + /// Solve a trait goal using Chalk. pub fn next_trait_solve_in_ctxt<'db, 'a>( infer_ctxt: &'a InferCtxt<'db>, diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs index 928753ef0ede..79a154a5d8e6 100644 --- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs @@ -14,7 +14,7 @@ use hir_expand::{ mod_path::{ModPath, PathKind}, name::Name, }; -use hir_ty::{db::HirDatabase, method_resolution}; +use hir_ty::{db::HirDatabase, method_resolution, next_solver::{mapping::ChalkToNextSolver, DbInterner}}; use crate::{ Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, DocLinkDef, Enum, ExternCrateDecl, @@ -271,7 +271,7 @@ fn resolve_impl_trait_item<'db>( // // FIXME: resolve type aliases (which are not yielded by iterate_path_candidates) _ = method_resolution::iterate_path_candidates( - &canonical, + &canonical.to_nextsolver(DbInterner::new_with(db, Some(environment.krate), environment.block)), db, environment, &traits_in_scope, diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 6eb8a8bf60fd..52ab808d2284 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -5611,7 +5611,11 @@ impl<'db> Type<'db> { .map_or_else(|| TraitEnvironment::empty(krate.id), |d| db.trait_environment(d)); _ = method_resolution::iterate_method_candidates_dyn( - &canonical, + &canonical.to_nextsolver(DbInterner::new_with( + db, + Some(environment.krate), + environment.block, + )), db, environment, traits_in_scope, @@ -5698,7 +5702,11 @@ impl<'db> Type<'db> { .map_or_else(|| TraitEnvironment::empty(krate.id), |d| db.trait_environment(d)); _ = method_resolution::iterate_path_candidates( - &canonical, + &canonical.to_nextsolver(DbInterner::new_with( + db, + Some(environment.krate), + environment.block, + )), db, environment, traits_in_scope, From 7c20f9c5888ff8ebe66997c2eca504a521d49dd8 Mon Sep 17 00:00:00 2001 From: calvinhirsch Date: Tue, 9 Sep 2025 10:09:09 -0400 Subject: [PATCH 310/682] fix comments about trait solver cycle heads --- compiler/rustc_type_ir/src/search_graph/mod.rs | 4 ++-- compiler/rustc_type_ir/src/search_graph/stack.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index dbbc0c217d7d..8f8f019510fe 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -1262,7 +1262,7 @@ impl, X: Cx> SearchGraph { encountered_overflow |= stack_entry.encountered_overflow; debug_assert_eq!(stack_entry.input, input); - // If the current goal is not the root of a cycle, we are done. + // If the current goal is not a cycle head, we are done. // // There are no provisional cache entries which depend on this goal. let Some(usages) = stack_entry.usages else { @@ -1278,7 +1278,7 @@ impl, X: Cx> SearchGraph { // // Check whether we reached a fixpoint, either because the final result // is equal to the provisional result of the previous iteration, or because - // this was only the root of either coinductive or inductive cycles, and the + // this was only the head of either coinductive or inductive cycles, and the // final result is equal to the initial response for that case. if self.reached_fixpoint(cx, &stack_entry, usages, result) { self.rebase_provisional_cache_entries(&stack_entry, |_, result| result); diff --git a/compiler/rustc_type_ir/src/search_graph/stack.rs b/compiler/rustc_type_ir/src/search_graph/stack.rs index 3fd8e2bd16e4..8348666be412 100644 --- a/compiler/rustc_type_ir/src/search_graph/stack.rs +++ b/compiler/rustc_type_ir/src/search_graph/stack.rs @@ -13,7 +13,7 @@ rustc_index::newtype_index! { pub(super) struct StackDepth {} } -/// Stack entries of the evaluation stack. Its fields tend to be lazily +/// Stack entries of the evaluation stack. Its fields tend to be lazily updated /// when popping a child goal or completely immutable. #[derive_where(Debug; X: Cx)] pub(super) struct StackEntry { @@ -42,7 +42,7 @@ pub(super) struct StackEntry { /// Whether evaluating this goal encountered overflow. Lazily updated. pub encountered_overflow: bool, - /// Whether and how this goal has been used as the root of a cycle. Lazily updated. + /// Whether and how this goal has been used as a cycle head. Lazily updated. pub usages: Option, /// We want to be able to ignore head usages if they happen inside of candidates From 20d02258fc24c5ab7572cfa5064a733021e6fbcd Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Mon, 8 Sep 2025 08:17:06 +0200 Subject: [PATCH 311/682] Make Barrier RefUnwindSafe again This commit manually implements `RefUnwindSafe` for `std::sync::Barrier` to fix 146087. This is a fix for a regression indroduced by https://github.com/rust-lang/rust/commit/e95db591a4550e28ad92660b753ad85b89271882 --- library/std/src/sync/barrier.rs | 5 +++++ library/std/tests/sync/barrier.rs | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/library/std/src/sync/barrier.rs b/library/std/src/sync/barrier.rs index 712ce03f90b0..47d63c57ff1e 100644 --- a/library/std/src/sync/barrier.rs +++ b/library/std/src/sync/barrier.rs @@ -1,3 +1,5 @@ +use core::panic::RefUnwindSafe; + use crate::fmt; use crate::sync::nonpoison::{Condvar, Mutex}; @@ -31,6 +33,9 @@ pub struct Barrier { num_threads: usize, } +#[stable(feature = "rust1", since = "1.0.0")] +impl RefUnwindSafe for Barrier {} + // The inner state of a double barrier struct BarrierState { count: usize, diff --git a/library/std/tests/sync/barrier.rs b/library/std/tests/sync/barrier.rs index 8aefff9d5071..cc9ee322c6c2 100644 --- a/library/std/tests/sync/barrier.rs +++ b/library/std/tests/sync/barrier.rs @@ -1,3 +1,4 @@ +use std::panic::RefUnwindSafe; use std::sync::mpsc::{TryRecvError, channel}; use std::sync::{Arc, Barrier}; use std::thread; @@ -33,3 +34,12 @@ fn test_barrier() { } assert!(leader_found); } + +#[expect(dead_code, reason = "this is essentially a compile pass test")] +fn check_barrier_is_ref_unwind_safe() { + let barrier = Arc::new(Barrier::new(10)); + + fn check(_: T) {} + + check(barrier); +} From 30fd32eca506a96e228abdd6ab18f6f1054b4db3 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Tue, 9 Sep 2025 17:14:07 +0200 Subject: [PATCH 312/682] Make the compile test use a const instead --- library/std/tests/sync/barrier.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/library/std/tests/sync/barrier.rs b/library/std/tests/sync/barrier.rs index cc9ee322c6c2..a66bd6296999 100644 --- a/library/std/tests/sync/barrier.rs +++ b/library/std/tests/sync/barrier.rs @@ -35,11 +35,10 @@ fn test_barrier() { assert!(leader_found); } -#[expect(dead_code, reason = "this is essentially a compile pass test")] -fn check_barrier_is_ref_unwind_safe() { - let barrier = Arc::new(Barrier::new(10)); - - fn check(_: T) {} - - check(barrier); -} +/// Asserts that `Barrier` is ref unwind safe. +/// +/// See . +const _: () = { + const fn check_ref_unwind_safe() {} + check_ref_unwind_safe::(); +}; From ec8725010120bfbe0430ea54ab820db1d9ed84eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Mon, 8 Sep 2025 15:49:51 +0200 Subject: [PATCH 313/682] Improve docs of certain built-in macro expanders --- .../rustc_builtin_macros/src/source_util.rs | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index 37bab5be5421..f95c8f38229b 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -1,3 +1,5 @@ +//! The implementation of built-in macros which relate to the file system. + use std::path::{Path, PathBuf}; use std::rc::Rc; use std::sync::Arc; @@ -23,11 +25,7 @@ use crate::util::{ check_zero_tts, get_single_str_from_tts, get_single_str_spanned_from_tts, parse_expr, }; -// These macros all relate to the file system; they either return -// the column/row/filename of the expression, or they include -// a given file into the current one. - -/// line!(): expands to the current line number +/// Expand `line!()` to the current line number. pub(crate) fn expand_line( cx: &mut ExtCtxt<'_>, sp: Span, @@ -42,7 +40,7 @@ pub(crate) fn expand_line( ExpandResult::Ready(MacEager::expr(cx.expr_u32(topmost, loc.line as u32))) } -/* column!(): expands to the current column number */ +/// Expand `column!()` to the current column number. pub(crate) fn expand_column( cx: &mut ExtCtxt<'_>, sp: Span, @@ -57,9 +55,7 @@ pub(crate) fn expand_column( ExpandResult::Ready(MacEager::expr(cx.expr_u32(topmost, loc.col.to_usize() as u32 + 1))) } -/// file!(): expands to the current filename */ -/// The source_file (`loc.file`) contains a bunch more information we could spit -/// out if we wanted. +/// Expand `file!()` to the current filename. pub(crate) fn expand_file( cx: &mut ExtCtxt<'_>, sp: Span, @@ -81,6 +77,7 @@ pub(crate) fn expand_file( ))) } +/// Expand `stringify!($input)`. pub(crate) fn expand_stringify( cx: &mut ExtCtxt<'_>, sp: Span, @@ -91,6 +88,7 @@ pub(crate) fn expand_stringify( ExpandResult::Ready(MacEager::expr(cx.expr_str(sp, Symbol::intern(&s)))) } +/// Expand `module_path!()` to (a textual representation of) the current module path. pub(crate) fn expand_mod( cx: &mut ExtCtxt<'_>, sp: Span, @@ -104,9 +102,9 @@ pub(crate) fn expand_mod( ExpandResult::Ready(MacEager::expr(cx.expr_str(sp, Symbol::intern(&string)))) } -/// include! : parse the given file as an expr -/// This is generally a bad idea because it's going to behave -/// unhygienically. +/// Expand `include!($input)`. +/// +/// This works in item and expression position. Notably, it doesn't work in pattern position. pub(crate) fn expand_include<'cx>( cx: &'cx mut ExtCtxt<'_>, sp: Span, @@ -187,7 +185,9 @@ pub(crate) fn expand_include<'cx>( ExpandResult::Ready(Box::new(ExpandInclude { p, node_id: cx.current_expansion.lint_node_id })) } -/// `include_str!`: read the given file, insert it as a literal string expr +/// Expand `include_str!($input)` to the content of the UTF-8-encoded file given by path `$input` as a string literal. +/// +/// This works in expression, pattern and statement position. pub(crate) fn expand_include_str( cx: &mut ExtCtxt<'_>, sp: Span, @@ -206,6 +206,7 @@ pub(crate) fn expand_include_str( Ok((bytes, bsp)) => match std::str::from_utf8(&bytes) { Ok(src) => { let interned_src = Symbol::intern(src); + // MacEager converts the expr into a pat if need be. MacEager::expr(cx.expr_str(cx.with_def_site_ctxt(bsp), interned_src)) } Err(utf8err) => { @@ -218,6 +219,9 @@ pub(crate) fn expand_include_str( }) } +/// Expand `include_bytes!($input)` to the content of the file given by path `$input`. +/// +/// This works in expression, pattern and statement position. pub(crate) fn expand_include_bytes( cx: &mut ExtCtxt<'_>, sp: Span, @@ -237,6 +241,7 @@ pub(crate) fn expand_include_bytes( // Don't care about getting the span for the raw bytes, // because the console can't really show them anyway. let expr = cx.expr(sp, ast::ExprKind::IncludedBytes(ByteSymbol::intern(&bytes))); + // MacEager converts the expr into a pat if need be. MacEager::expr(expr) } Err(dummy) => dummy, From 1a13522b56be87ed9d30e4727472da57cdc9ab3c Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 9 Sep 2025 17:19:36 +0200 Subject: [PATCH 314/682] Update `browser-ui-test` version to `0.22.2` --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index def0cfa86a58..d0297bf70b63 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,7 +5,7 @@ "packages": { "": { "dependencies": { - "browser-ui-test": "^0.22.0", + "browser-ui-test": "^0.22.2", "es-check": "^6.2.1", "eslint": "^8.57.1", "eslint-js": "github:eslint/js", @@ -485,9 +485,9 @@ } }, "node_modules/browser-ui-test": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/browser-ui-test/-/browser-ui-test-0.22.0.tgz", - "integrity": "sha512-p/C02TMybTDKsAjpGOdnyNC0Q25KDae/fKMnvHaqcJ0tXRqNKwndW2Ltq7HTmin5xqg8GGOmysEgWTZkXu6pfA==", + "version": "0.22.2", + "resolved": "https://registry.npmjs.org/browser-ui-test/-/browser-ui-test-0.22.2.tgz", + "integrity": "sha512-eNB/PN2yDGe5n5IwE3ld/N6A39jM1oRzJmT5nOVQqrvoZEtcd9JSggDQPNVUnMEyuGcD4OEOWMsEa4oJppAmDQ==", "license": "MIT", "dependencies": { "css-unit-converter": "^1.1.2", diff --git a/package.json b/package.json index 976d63036341..04e0f6af19a0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "dependencies": { - "browser-ui-test": "^0.22.0", + "browser-ui-test": "^0.22.2", "es-check": "^6.2.1", "eslint": "^8.57.1", "eslint-js": "github:eslint/js", From 7e7ce10e5b0474e6833c22062c4973f447ca6463 Mon Sep 17 00:00:00 2001 From: yanglsh Date: Wed, 10 Sep 2025 00:10:05 +0800 Subject: [PATCH 315/682] fix: `useless_attribute` FP on `deprecated_in_future` --- clippy_lints/src/attrs/useless_attribute.rs | 1 + clippy_utils/src/sym.rs | 1 + tests/ui/useless_attribute.fixed | 10 ++++++++++ tests/ui/useless_attribute.rs | 10 ++++++++++ 4 files changed, 22 insertions(+) diff --git a/clippy_lints/src/attrs/useless_attribute.rs b/clippy_lints/src/attrs/useless_attribute.rs index b9b5cedb5aa7..1cebc18edc90 100644 --- a/clippy_lints/src/attrs/useless_attribute.rs +++ b/clippy_lints/src/attrs/useless_attribute.rs @@ -30,6 +30,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) { sym::ambiguous_glob_reexports | sym::dead_code | sym::deprecated + | sym::deprecated_in_future | sym::hidden_glob_reexports | sym::unreachable_pub | sym::unused diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index 278101ac27f9..e2570a151584 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -125,6 +125,7 @@ generate! { cycle, cyclomatic_complexity, de, + deprecated_in_future, diagnostics, disallowed_types, drain, diff --git a/tests/ui/useless_attribute.fixed b/tests/ui/useless_attribute.fixed index be4fb55ddfb8..15070dd9c2ca 100644 --- a/tests/ui/useless_attribute.fixed +++ b/tests/ui/useless_attribute.fixed @@ -158,3 +158,13 @@ pub mod redundant_imports_issue { empty!(); } + +pub mod issue15636 { + pub mod f { + #[deprecated(since = "TBD")] + pub mod deprec {} + } + + #[allow(deprecated_in_future)] + pub use f::deprec; +} diff --git a/tests/ui/useless_attribute.rs b/tests/ui/useless_attribute.rs index 5a1bcf97a5b4..3f530de7fd8e 100644 --- a/tests/ui/useless_attribute.rs +++ b/tests/ui/useless_attribute.rs @@ -158,3 +158,13 @@ pub mod redundant_imports_issue { empty!(); } + +pub mod issue15636 { + pub mod f { + #[deprecated(since = "TBD")] + pub mod deprec {} + } + + #[allow(deprecated_in_future)] + pub use f::deprec; +} From 69140ec44c2f57497ae2375a6f75f0bfe76b8003 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 29 Aug 2025 21:47:10 +0900 Subject: [PATCH 316/682] Fix failing tests and fill-in missing details --- .../crates/hir-ty/src/autoderef.rs | 14 +- .../crates/hir-ty/src/chalk_ext.rs | 41 +- .../crates/hir-ty/src/consteval_nextsolver.rs | 6 +- .../rust-analyzer/crates/hir-ty/src/infer.rs | 28 +- .../crates/hir-ty/src/infer/closure.rs | 12 +- .../crates/hir-ty/src/infer/coerce.rs | 67 +- .../crates/hir-ty/src/infer/expr.rs | 57 +- .../crates/hir-ty/src/infer/pat.rs | 12 +- .../crates/hir-ty/src/infer/path.rs | 12 +- .../crates/hir-ty/src/infer/unify.rs | 478 +++++---- .../rust-analyzer/crates/hir-ty/src/lib.rs | 11 +- .../crates/hir-ty/src/method_resolution.rs | 223 ++-- .../rust-analyzer/crates/hir-ty/src/mir.rs | 3 +- .../crates/hir-ty/src/mir/monomorphization.rs | 26 +- .../crates/hir-ty/src/next_solver.rs | 4 +- .../infer/canonical/canonicalizer.rs | 136 ++- .../hir-ty/src/next_solver/infer/mod.rs | 25 +- .../crates/hir-ty/src/next_solver/mapping.rs | 974 ++++-------------- .../crates/hir-ty/src/next_solver/solver.rs | 6 +- .../crates/hir-ty/src/tests/coercion.rs | 14 +- .../crates/hir-ty/src/tests/incremental.rs | 6 +- .../crates/hir-ty/src/tests/macros.rs | 4 +- .../hir-ty/src/tests/method_resolution.rs | 10 +- .../crates/hir-ty/src/tests/never_type.rs | 24 +- .../crates/hir-ty/src/tests/opaque_types.rs | 4 +- .../crates/hir-ty/src/tests/patterns.rs | 26 +- .../crates/hir-ty/src/tests/regression.rs | 12 +- .../hir-ty/src/tests/regression/new_solver.rs | 2 +- .../crates/hir-ty/src/tests/simple.rs | 4 +- .../crates/hir-ty/src/tests/traits.rs | 28 +- .../rust-analyzer/crates/hir-ty/src/traits.rs | 43 +- .../rust-analyzer/crates/hir/src/attrs.rs | 12 +- .../ide-completion/src/context/tests.rs | 3 +- .../src/handlers/type_mismatch.rs | 3 +- .../rust-analyzer/crates/ide-ssr/src/tests.rs | 15 +- .../crates/ide/src/doc_links/tests.rs | 3 +- 36 files changed, 917 insertions(+), 1431 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs index 4ea0156e1248..82696a5c94da 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs @@ -13,7 +13,7 @@ use triomphe::Arc; use crate::{ Canonical, Goal, Interner, ProjectionTyExt, TraitEnvironment, Ty, TyBuilder, TyKind, - db::HirDatabase, infer::unify::InferenceTable, + db::HirDatabase, infer::unify::InferenceTable, next_solver::mapping::ChalkToNextSolver, }; const AUTODEREF_RECURSION_LIMIT: usize = 20; @@ -98,7 +98,7 @@ impl<'table, 'db> Autoderef<'table, 'db> { explicit: bool, use_receiver_trait: bool, ) -> Self { - let ty = table.resolve_ty_shallow(&ty); + let ty = table.structurally_resolve_type(&ty); Autoderef { table, ty, at_start: true, steps: Vec::new(), explicit, use_receiver_trait } } @@ -114,7 +114,7 @@ impl<'table, 'db> Autoderef<'table, 'db, usize> { explicit: bool, use_receiver_trait: bool, ) -> Self { - let ty = table.resolve_ty_shallow(&ty); + let ty = table.structurally_resolve_type(&ty); Autoderef { table, ty, at_start: true, steps: 0, explicit, use_receiver_trait } } } @@ -160,7 +160,7 @@ pub(crate) fn autoderef_step( use_receiver_trait: bool, ) -> Option<(AutoderefKind, Ty)> { if let Some(derefed) = builtin_deref(table.db, &ty, explicit) { - Some((AutoderefKind::Builtin, table.resolve_ty_shallow(derefed))) + Some((AutoderefKind::Builtin, table.structurally_resolve_type(derefed))) } else { Some((AutoderefKind::Overloaded, deref_by_trait(table, ty, use_receiver_trait)?)) } @@ -187,7 +187,7 @@ pub(crate) fn deref_by_trait( use_receiver_trait: bool, ) -> Option { let _p = tracing::info_span!("deref_by_trait").entered(); - if table.resolve_ty_shallow(&ty).inference_var(Interner).is_some() { + if table.structurally_resolve_type(&ty).inference_var(Interner).is_some() { // don't try to deref unknown variables return None; } @@ -229,8 +229,8 @@ pub(crate) fn deref_by_trait( return None; } - table.register_obligation(implements_goal); + table.register_obligation(implements_goal.to_nextsolver(table.interner)); let result = table.normalize_projection_ty(projection); - Some(table.resolve_ty_shallow(&result)) + Some(table.structurally_resolve_type(&result)) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs index 33ae099c690e..9f0ea14a8063 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs @@ -245,26 +245,30 @@ impl TyExt for Ty { } fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option> { + let handle_async_block_type_impl_trait = |def: DefWithBodyId| { + let krate = def.module(db).krate(); + if let Some(future_trait) = LangItem::Future.resolve_trait(db, krate) { + // This is only used by type walking. + // Parameters will be walked outside, and projection predicate is not used. + // So just provide the Future trait. + let impl_bound = Binders::empty( + Interner, + WhereClause::Implemented(TraitRef { + trait_id: to_chalk_trait_id(future_trait), + substitution: Substitution::empty(Interner), + }), + ); + Some(vec![impl_bound]) + } else { + None + } + }; + match self.kind(Interner) { TyKind::OpaqueType(opaque_ty_id, subst) => { match db.lookup_intern_impl_trait_id((*opaque_ty_id).into()) { ImplTraitId::AsyncBlockTypeImplTrait(def, _expr) => { - let krate = def.module(db).krate(); - if let Some(future_trait) = LangItem::Future.resolve_trait(db, krate) { - // This is only used by type walking. - // Parameters will be walked outside, and projection predicate is not used. - // So just provide the Future trait. - let impl_bound = Binders::empty( - Interner, - WhereClause::Implemented(TraitRef { - trait_id: to_chalk_trait_id(future_trait), - substitution: Substitution::empty(Interner), - }), - ); - Some(vec![impl_bound]) - } else { - None - } + handle_async_block_type_impl_trait(def) } ImplTraitId::ReturnTypeImplTrait(func, idx) => { db.return_type_impl_traits(func).map(|it| { @@ -299,8 +303,9 @@ impl TyExt for Ty { data.substitute(Interner, &opaque_ty.substitution) }) } - // It always has an parameter for Future::Output type. - ImplTraitId::AsyncBlockTypeImplTrait(..) => unreachable!(), + ImplTraitId::AsyncBlockTypeImplTrait(def, _) => { + return handle_async_block_type_impl_trait(def); + } }; predicates.map(|it| it.into_value_and_skipped_binders().0) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs index 470033593154..6e07d3afe552 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs @@ -27,7 +27,7 @@ use crate::{ next_solver::{ Const, ConstBytes, ConstKind, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, ParamConst, SolverDefId, Ty, ValueConst, - mapping::{ChalkToNextSolver, convert_binder_to_early_binder}, + mapping::{ChalkToNextSolver, NextSolverToChalk, convert_binder_to_early_binder}, }, }; @@ -145,7 +145,7 @@ pub fn try_const_usize<'db>(db: &'db dyn HirDatabase, c: Const<'db>) -> Option GeneralConstId::StaticId(id), _ => unreachable!(), }; - let subst = ChalkToNextSolver::from_nextsolver(unevaluated_const.args, interner); + let subst = unevaluated_const.args.to_chalk(interner); let ec = db.const_eval(c, subst, None).ok()?.to_nextsolver(interner); try_const_usize(db, ec) } @@ -168,7 +168,7 @@ pub fn try_const_isize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option< SolverDefId::StaticId(id) => GeneralConstId::StaticId(id), _ => unreachable!(), }; - let subst = ChalkToNextSolver::from_nextsolver(unevaluated_const.args, interner); + let subst = unevaluated_const.args.to_chalk(interner); let ec = db.const_eval(c, subst, None).ok()?.to_nextsolver(interner); try_const_isize(db, &ec) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 71b33a0e9035..265d1f8541cc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -55,10 +55,9 @@ use stdx::{always, never}; use triomphe::Arc; use crate::{ - AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, Goal, ImplTraitId, - ImplTraitIdx, InEnvironment, IncorrectGenericsLenKind, Interner, Lifetime, OpaqueTyId, - ParamLoweringMode, PathLoweringDiagnostic, ProjectionTy, Substitution, TraitEnvironment, Ty, - TyBuilder, TyExt, + AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, ImplTraitId, ImplTraitIdx, + IncorrectGenericsLenKind, Interner, Lifetime, OpaqueTyId, ParamLoweringMode, + PathLoweringDiagnostic, ProjectionTy, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, db::HirDatabase, fold_tys, generics::Generics, @@ -70,6 +69,7 @@ use crate::{ }, lower::{ImplTraitLoweringMode, LifetimeElisionKind, diagnostics::TyLoweringDiagnostic}, mir::MirSpan, + next_solver::{self, mapping::ChalkToNextSolver}, static_lifetime, to_assoc_type_id, traits::FnTrait, utils::UnevaluatedConstEvaluatorFolder, @@ -182,13 +182,13 @@ impl BindingMode { } #[derive(Debug)] -pub(crate) struct InferOk { +pub(crate) struct InferOk<'db, T> { value: T, - goals: Vec>, + goals: Vec>>, } -impl InferOk { - fn map(self, f: impl FnOnce(T) -> U) -> InferOk { +impl<'db, T> InferOk<'db, T> { + fn map(self, f: impl FnOnce(T) -> U) -> InferOk<'db, U> { InferOk { value: f(self.value), goals: self.goals } } } @@ -203,7 +203,7 @@ pub enum InferenceTyDiagnosticSource { #[derive(Debug)] pub(crate) struct TypeError; -pub(crate) type InferResult = Result, TypeError>; +pub(crate) type InferResult<'db, T> = Result, TypeError>; #[derive(Debug, PartialEq, Eq, Clone)] pub enum InferenceDiagnostic { @@ -832,6 +832,7 @@ impl<'db> InferenceContext<'db> { coercion_casts, diagnostics: _, } = &mut result; + table.resolve_obligations_as_possible(); table.fallback_if_possible(); // Comment from rustc: @@ -1480,7 +1481,8 @@ impl<'db> InferenceContext<'db> { } fn push_obligation(&mut self, o: DomainGoal) { - self.table.register_obligation(o.cast(Interner)); + let goal: crate::Goal = o.cast(Interner); + self.table.register_obligation(goal.to_nextsolver(self.table.interner)); } fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { @@ -1746,7 +1748,7 @@ impl<'db> InferenceContext<'db> { ty = self.table.insert_type_vars(ty); ty = self.table.normalize_associated_types_in(ty); - ty = self.table.resolve_ty_shallow(&ty); + ty = self.table.structurally_resolve_type(&ty); if ty.is_unknown() { return (self.err_ty(), None); } @@ -1817,7 +1819,7 @@ impl<'db> InferenceContext<'db> { let ty = match ty.kind(Interner) { TyKind::Alias(AliasTy::Projection(proj_ty)) => { let ty = self.table.normalize_projection_ty(proj_ty.clone()); - self.table.resolve_ty_shallow(&ty) + self.table.structurally_resolve_type(&ty) } _ => ty, }; @@ -2047,7 +2049,7 @@ impl Expectation { fn adjust_for_branches(&self, table: &mut unify::InferenceTable<'_>) -> Expectation { match self { Expectation::HasType(ety) => { - let ety = table.resolve_ty_shallow(ety); + let ety = table.structurally_resolve_type(ety); if ety.is_ty_var() { Expectation::None } else { Expectation::HasType(ety) } } Expectation::RValueLikeUnsized(ety) => Expectation::RValueLikeUnsized(ety.clone()), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 493652b76401..5a69ad68b58e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -39,6 +39,7 @@ use crate::{ infer::{BreakableKind, CoerceMany, Diverges, coerce::CoerceNever}, make_binders, mir::{BorrowKind, MirSpan, MutBorrowKind, ProjectionElem}, + next_solver::mapping::ChalkToNextSolver, to_assoc_type_id, traits::FnTrait, utils::{self, elaborate_clause_supertraits}, @@ -437,10 +438,10 @@ impl InferenceContext<'_> { associated_ty_id: to_assoc_type_id(future_output), substitution: Substitution::from1(Interner, ret_param_future.clone()), }); - self.table.register_obligation( + let goal: crate::Goal = crate::AliasEq { alias: future_projection, ty: ret_param_future_output.clone() } - .cast(Interner), - ); + .cast(Interner); + self.table.register_obligation(goal.to_nextsolver(self.table.interner)); Some(FnSubst(Substitution::from_iter( Interner, @@ -568,7 +569,10 @@ impl InferenceContext<'_> { let supplied_sig = self.supplied_sig_of_closure(body, ret_type, arg_types, closure_kind); let snapshot = self.table.snapshot(); - if !self.table.unify::<_, crate::next_solver::GenericArgs<'_>>(&expected_sig.substitution.0, &supplied_sig.expected_sig.substitution.0) { + if !self.table.unify::<_, crate::next_solver::GenericArgs<'_>>( + &expected_sig.substitution.0, + &supplied_sig.expected_sig.substitution.0, + ) { self.table.rollback_to(snapshot); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index b47834f0c749..df516662bfd3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -8,24 +8,27 @@ use std::iter; use chalk_ir::{BoundVar, Mutability, TyKind, TyVariableKind, cast::Cast}; -use hir_def::{ - hir::ExprId, - lang_item::LangItem, -}; +use hir_def::{hir::ExprId, lang_item::LangItem}; use rustc_type_ir::solve::Certainty; use stdx::always; use triomphe::Arc; use crate::{ - autoderef::{Autoderef, AutoderefKind}, db::HirDatabase, infer::{ + Canonical, FnAbi, FnPointer, FnSig, Goal, Interner, Lifetime, Substitution, TraitEnvironment, + Ty, TyBuilder, TyExt, + autoderef::{Autoderef, AutoderefKind}, + db::HirDatabase, + infer::{ Adjust, Adjustment, AutoBorrow, InferOk, InferenceContext, OverloadedDeref, PointerCast, TypeError, TypeMismatch, - }, utils::ClosureSubst, Canonical, FnAbi, FnPointer, FnSig, Goal, InEnvironment, Interner, Lifetime, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt + }, + next_solver, + utils::ClosureSubst, }; use super::unify::InferenceTable; -pub(crate) type CoerceResult = Result, Ty)>, TypeError>; +pub(crate) type CoerceResult<'db> = Result, Ty)>, TypeError>; /// Do not require any adjustments, i.e. coerce `x -> x`. fn identity(_: Ty) -> Vec { @@ -37,11 +40,11 @@ fn simple(kind: Adjust) -> impl FnOnce(Ty) -> Vec { } /// This always returns `Ok(...)`. -fn success( +fn success<'db>( adj: Vec, target: Ty, - goals: Vec>, -) -> CoerceResult { + goals: Vec>>, +) -> CoerceResult<'db> { Ok(InferOk { goals, value: (adj, target) }) } @@ -107,9 +110,9 @@ impl CoerceMany { /// coerce both to function pointers; /// - if we were concerned with lifetime subtyping, we'd need to look for a /// least upper bound. - pub(super) fn coerce( + pub(super) fn coerce<'db>( &mut self, - ctx: &mut InferenceContext<'_>, + ctx: &mut InferenceContext<'db>, expr: Option, expr_ty: &Ty, cause: CoercionCause, @@ -276,7 +279,7 @@ impl InferenceContext<'_> { } } -impl InferenceTable<'_> { +impl<'db> InferenceTable<'db> { /// Unify two types, but may coerce the first one to the second one /// using "implicit coercion rules" if needed. pub(crate) fn coerce( @@ -285,8 +288,8 @@ impl InferenceTable<'_> { to_ty: &Ty, coerce_never: CoerceNever, ) -> Result<(Vec, Ty), TypeError> { - let from_ty = self.resolve_ty_shallow(from_ty); - let to_ty = self.resolve_ty_shallow(to_ty); + let from_ty = self.structurally_resolve_type(from_ty); + let to_ty = self.structurally_resolve_type(to_ty); match self.coerce_inner(from_ty, &to_ty, coerce_never) { Ok(InferOk { value: (adjustments, ty), goals }) => { self.register_infer_ok(InferOk { value: (), goals }); @@ -299,10 +302,15 @@ impl InferenceTable<'_> { } } - fn coerce_inner(&mut self, from_ty: Ty, to_ty: &Ty, coerce_never: CoerceNever) -> CoerceResult { + fn coerce_inner( + &mut self, + from_ty: Ty, + to_ty: &Ty, + coerce_never: CoerceNever, + ) -> CoerceResult<'db> { if from_ty.is_never() { if let TyKind::InferenceVar(tv, TyVariableKind::General) = to_ty.kind(Interner) { - self.set_diverging(*tv, TyVariableKind::General, true); + self.set_diverging(*tv, TyVariableKind::General); } if coerce_never == CoerceNever::Yes { // Subtle: If we are coercing from `!` to `?T`, where `?T` is an unbound @@ -370,7 +378,7 @@ impl InferenceTable<'_> { } /// Unify two types (using sub or lub) and produce a specific coercion. - fn unify_and(&mut self, t1: &Ty, t2: &Ty, f: F) -> CoerceResult + fn unify_and(&mut self, t1: &Ty, t2: &Ty, f: F) -> CoerceResult<'db> where F: FnOnce(Ty) -> Vec, { @@ -378,7 +386,7 @@ impl InferenceTable<'_> { .and_then(|InferOk { goals, .. }| success(f(t1.clone()), t1.clone(), goals)) } - fn coerce_ptr(&mut self, from_ty: Ty, to_ty: &Ty, to_mt: Mutability) -> CoerceResult { + fn coerce_ptr(&mut self, from_ty: Ty, to_ty: &Ty, to_mt: Mutability) -> CoerceResult<'db> { let (is_ref, from_mt, from_inner) = match from_ty.kind(Interner) { TyKind::Ref(mt, _, ty) => (true, mt, ty), TyKind::Raw(mt, ty) => (false, mt, ty), @@ -420,7 +428,7 @@ impl InferenceTable<'_> { to_ty: &Ty, to_mt: Mutability, to_lt: &Lifetime, - ) -> CoerceResult { + ) -> CoerceResult<'db> { let (_from_lt, from_mt) = match from_ty.kind(Interner) { TyKind::Ref(mt, lt, _) => { coerce_mutabilities(*mt, to_mt)?; @@ -524,7 +532,7 @@ impl InferenceTable<'_> { } /// Attempts to coerce from the type of a Rust function item into a function pointer. - fn coerce_from_fn_item(&mut self, from_ty: Ty, to_ty: &Ty) -> CoerceResult { + fn coerce_from_fn_item(&mut self, from_ty: Ty, to_ty: &Ty) -> CoerceResult<'db> { match to_ty.kind(Interner) { TyKind::Function(_) => { let from_sig = from_ty.callable_sig(self.db).expect("FnDef had no sig"); @@ -566,7 +574,7 @@ impl InferenceTable<'_> { from_ty: Ty, from_f: &FnPointer, to_ty: &Ty, - ) -> CoerceResult { + ) -> CoerceResult<'db> { self.coerce_from_safe_fn( from_ty, from_f, @@ -583,7 +591,7 @@ impl InferenceTable<'_> { to_ty: &Ty, to_unsafe: F, normal: G, - ) -> CoerceResult + ) -> CoerceResult<'db> where F: FnOnce(Ty) -> Vec, G: FnOnce(Ty) -> Vec, @@ -606,7 +614,7 @@ impl InferenceTable<'_> { from_ty: Ty, from_substs: &Substitution, to_ty: &Ty, - ) -> CoerceResult { + ) -> CoerceResult<'db> { match to_ty.kind(Interner) { // if from_substs is non-capturing (FIXME) TyKind::Function(fn_ty) => { @@ -631,7 +639,7 @@ impl InferenceTable<'_> { /// Coerce a type using `from_ty: CoerceUnsized` /// /// See: - fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty) -> CoerceResult { + fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty) -> CoerceResult<'db> { // These 'if' statements require some explanation. // The `CoerceUnsized` trait is special - it is only // possible to write `impl CoerceUnsized for A` where @@ -707,12 +715,9 @@ impl InferenceTable<'_> { let goal: Goal = coerce_unsized_tref.cast(Interner); - self.commit_if_ok(|table| { - match table.solve_obligation(goal) { - Ok(Certainty::Yes) => Ok(()), - Ok(Certainty::Maybe(_)) => Ok(()), - Err(_) => Err(TypeError), - } + self.commit_if_ok(|table| match table.solve_obligation(goal) { + Ok(Certainty::Yes) => Ok(()), + _ => Err(TypeError), })?; let unsize = diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index e39f3ab75f16..bfeb5bae851a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -23,9 +23,29 @@ use stdx::always; use syntax::ast::RangeOp; use crate::{ - autoderef::{builtin_deref, deref_by_trait, Autoderef}, consteval, generics::generics, infer::{ - coerce::{CoerceMany, CoerceNever, CoercionCause}, find_continuable, pat::contains_explicit_ref_binding, BreakableKind - }, lang_items::lang_items_for_bin_op, lower::{lower_to_chalk_mutability, path::{substs_from_args_and_bindings, GenericArgsLowerer, TypeLikeConst}, ParamLoweringMode}, mapping::{from_chalk, ToChalk}, method_resolution::{self, VisibleFromModule}, next_solver::mapping::ChalkToNextSolver, primitive::{self, UintTy}, static_lifetime, to_chalk_trait_id, traits::FnTrait, Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, DeclContext, DeclOrigin, IncorrectGenericsLenKind, Interner, LifetimeElisionKind, Rawness, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind + Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, DeclContext, + DeclOrigin, IncorrectGenericsLenKind, Interner, LifetimeElisionKind, Rawness, Scalar, + Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, + autoderef::{Autoderef, builtin_deref, deref_by_trait}, + consteval, + generics::generics, + infer::{ + BreakableKind, + coerce::{CoerceMany, CoerceNever, CoercionCause}, + find_continuable, + pat::contains_explicit_ref_binding, + }, + lang_items::lang_items_for_bin_op, + lower::{ + ParamLoweringMode, lower_to_chalk_mutability, + path::{GenericArgsLowerer, TypeLikeConst, substs_from_args_and_bindings}, + }, + mapping::{ToChalk, from_chalk}, + method_resolution::{self, VisibleFromModule}, + next_solver::mapping::ChalkToNextSolver, + primitive::{self, UintTy}, + static_lifetime, to_chalk_trait_id, + traits::FnTrait, }; use super::{ @@ -383,7 +403,7 @@ impl InferenceContext<'_> { let matchee_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); let mut all_arms_diverge = Diverges::Always; for arm in arms.iter() { - let input_ty = self.resolve_ty_shallow(&input_ty); + let input_ty = self.table.structurally_resolve_type(&input_ty); self.infer_top_pat(arm.pat, &input_ty, None); } @@ -633,7 +653,7 @@ impl InferenceContext<'_> { &Expr::Box { expr } => self.infer_expr_box(expr, expected), Expr::UnaryOp { expr, op } => { let inner_ty = self.infer_expr_inner(*expr, &Expectation::none(), ExprIsRead::Yes); - let inner_ty = self.resolve_ty_shallow(&inner_ty); + let inner_ty = self.table.structurally_resolve_type(&inner_ty); // FIXME: Note down method resolution her match op { UnaryOp::Deref => { @@ -651,7 +671,7 @@ impl InferenceContext<'_> { ); } if let Some(derefed) = builtin_deref(self.table.db, &inner_ty, true) { - self.resolve_ty_shallow(derefed) + self.table.structurally_resolve_type(derefed) } else { deref_by_trait(&mut self.table, inner_ty, false) .unwrap_or_else(|| self.err_ty()) @@ -807,10 +827,10 @@ impl InferenceContext<'_> { let index_ty = self.infer_expr(*index, &Expectation::none(), ExprIsRead::Yes); if let Some(index_trait) = self.resolve_lang_trait(LangItem::Index) { - let canonicalized = ChalkToNextSolver::from_nextsolver(self.canonicalize(base_ty.clone().to_nextsolver(self.table.interner)), self.table.interner); + let canonicalized = + self.canonicalize(base_ty.clone().to_nextsolver(self.table.interner)); let receiver_adjustments = method_resolution::resolve_indexing_op( - self.db, - self.table.trait_env.clone(), + &mut self.table, canonicalized, index_trait, ); @@ -983,7 +1003,7 @@ impl InferenceContext<'_> { // allows them to be inferred based on how they are used later in the // function. if is_input { - let ty = this.resolve_ty_shallow(&ty); + let ty = this.table.structurally_resolve_type(&ty); match ty.kind(Interner) { TyKind::FnDef(def, parameters) => { let fnptr_ty = TyKind::Function( @@ -1405,9 +1425,10 @@ impl InferenceContext<'_> { // use knowledge of built-in binary ops, which can sometimes help inference let builtin_ret = self.enforce_builtin_binop_types(&lhs_ty, &rhs_ty, op); self.unify(&builtin_ret, &ret_ty); + builtin_ret + } else { + ret_ty } - - ret_ty } fn infer_block( @@ -1660,7 +1681,8 @@ impl InferenceContext<'_> { None => { // no field found, lets attempt to resolve it like a function so that IDE things // work out while people are typing - let canonicalized_receiver = self.canonicalize(receiver_ty.clone().to_nextsolver(self.table.interner)); + let canonicalized_receiver = + self.canonicalize(receiver_ty.clone().to_nextsolver(self.table.interner)); let resolved = method_resolution::lookup_method( self.db, &canonicalized_receiver, @@ -1806,7 +1828,8 @@ impl InferenceContext<'_> { expected: &Expectation, ) -> Ty { let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none(), ExprIsRead::Yes); - let canonicalized_receiver = self.canonicalize(receiver_ty.clone().to_nextsolver(self.table.interner)); + let canonicalized_receiver = + self.canonicalize(receiver_ty.clone().to_nextsolver(self.table.interner)); let resolved = method_resolution::lookup_method( self.db, @@ -2216,7 +2239,7 @@ impl InferenceContext<'_> { } fn register_obligations_for_call(&mut self, callable_ty: &Ty) { - let callable_ty = self.resolve_ty_shallow(callable_ty); + let callable_ty = self.table.structurally_resolve_type(callable_ty); if let TyKind::FnDef(fn_def, parameters) = callable_ty.kind(Interner) { let def: CallableDefId = from_chalk(self.db, *fn_def); let generic_predicates = @@ -2305,9 +2328,9 @@ impl InferenceContext<'_> { /// Dereferences a single level of immutable referencing. fn deref_ty_if_possible(&mut self, ty: &Ty) -> Ty { - let ty = self.resolve_ty_shallow(ty); + let ty = self.table.structurally_resolve_type(ty); match ty.kind(Interner) { - TyKind::Ref(Mutability::Not, _, inner) => self.resolve_ty_shallow(inner), + TyKind::Ref(Mutability::Not, _, inner) => self.table.structurally_resolve_type(inner), _ => ty, } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index 707bec0fce4c..16489e3068f3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -190,7 +190,7 @@ impl InferenceContext<'_> { subs: &[PatId], decl: Option, ) -> Ty { - let expected = self.resolve_ty_shallow(expected); + let expected = self.table.structurally_resolve_type(expected); let expectations = match expected.as_tuple() { Some(parameters) => parameters.as_slice(Interner), _ => &[], @@ -238,7 +238,7 @@ impl InferenceContext<'_> { mut default_bm: BindingMode, decl: Option, ) -> Ty { - let mut expected = self.resolve_ty_shallow(expected); + let mut expected = self.table.structurally_resolve_type(expected); if matches!(&self.body[pat], Pat::Ref { .. }) || self.inside_assignment { cov_mark::hit!(match_ergonomics_ref); @@ -251,7 +251,7 @@ impl InferenceContext<'_> { let mut pat_adjustments = Vec::new(); while let Some((inner, _lifetime, mutability)) = expected.as_reference() { pat_adjustments.push(expected.clone()); - expected = self.resolve_ty_shallow(inner); + expected = self.table.structurally_resolve_type(inner); default_bm = match default_bm { BindingMode::Move => BindingMode::Ref(mutability), BindingMode::Ref(Mutability::Not) => BindingMode::Ref(Mutability::Not), @@ -494,7 +494,7 @@ impl InferenceContext<'_> { default_bm: BindingMode, decl: Option, ) -> Ty { - let expected = self.resolve_ty_shallow(expected); + let expected = self.table.structurally_resolve_type(expected); // If `expected` is an infer ty, we try to equate it to an array if the given pattern // allows it. See issue #16609 @@ -506,7 +506,7 @@ impl InferenceContext<'_> { self.unify(&expected, &resolved_array_ty); } - let expected = self.resolve_ty_shallow(&expected); + let expected = self.table.structurally_resolve_type(&expected); let elem_ty = match expected.kind(Interner) { TyKind::Array(st, _) | TyKind::Slice(st) => st.clone(), _ => self.err_ty(), @@ -542,7 +542,7 @@ impl InferenceContext<'_> { if let Expr::Literal(Literal::ByteString(_)) = self.body[expr] && let Some((inner, ..)) = expected.as_reference() { - let inner = self.resolve_ty_shallow(inner); + let inner = self.table.structurally_resolve_type(inner); if matches!(inner.kind(Interner), TyKind::Slice(_)) { let elem_ty = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(Interner); let slice_ty = TyKind::Slice(elem_ty).intern(Interner); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index 9f3cd2b8fb00..1f62f9c4aa11 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -10,7 +10,15 @@ use hir_expand::name::Name; use stdx::never; use crate::{ - builder::ParamKind, consteval, error_lifetime, generics::generics, infer::diagnostics::InferenceTyLoweringContext as TyLoweringContext, method_resolution::{self, VisibleFromModule}, next_solver::mapping::ChalkToNextSolver, to_chalk_trait_id, InferenceDiagnostic, Interner, LifetimeElisionKind, Substitution, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, ValueTyDefId + InferenceDiagnostic, Interner, LifetimeElisionKind, Substitution, TraitRef, TraitRefExt, Ty, + TyBuilder, TyExt, TyKind, ValueTyDefId, + builder::ParamKind, + consteval, error_lifetime, + generics::generics, + infer::diagnostics::InferenceTyLoweringContext as TyLoweringContext, + method_resolution::{self, VisibleFromModule}, + next_solver::mapping::ChalkToNextSolver, + to_chalk_trait_id, }; use super::{ExprOrPatId, InferenceContext, InferenceTyDiagnosticSource}; @@ -384,7 +392,7 @@ impl InferenceContext<'_> { name: &Name, id: ExprOrPatId, ) -> Option<(ValueNs, Substitution)> { - let ty = self.resolve_ty_shallow(ty); + let ty = self.table.structurally_resolve_type(ty); let (enum_id, subst) = match ty.as_adt() { Some((AdtId::EnumId(e), subst)) => (e, subst), _ => return None, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index 3745d2c98fd9..ec4b7ee85dc6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -3,7 +3,8 @@ use std::{fmt, mem}; use chalk_ir::{ - cast::Cast, fold::TypeFoldable, interner::HasInterner, CanonicalVarKind, FloatTy, IntTy, TyVariableKind, + CanonicalVarKind, FloatTy, IntTy, TyVariableKind, cast::Cast, fold::TypeFoldable, + interner::HasInterner, }; use either::Either; use hir_def::{AdtId, lang_item::LangItem}; @@ -11,13 +12,36 @@ use hir_expand::name::Name; use intern::sym; use rustc_hash::{FxHashMap, FxHashSet}; use rustc_next_trait_solver::solve::HasChanged; -use rustc_type_ir::{inherent::Span, relate::{solver_relating::RelateExt, Relate}, solve::{Certainty, NoSolution}, FloatVid, IntVid, TyVid}; +use rustc_type_ir::{ + AliasRelationDirection, FloatVid, IntVid, TyVid, + inherent::{Span, Term as _}, + relate::{Relate, solver_relating::RelateExt}, + solve::{Certainty, NoSolution}, +}; use smallvec::SmallVec; use triomphe::Arc; use super::{InferOk, InferResult, InferenceContext, TypeError}; use crate::{ - consteval::unknown_const, db::HirDatabase, fold_generic_args, fold_tys_and_consts, next_solver::{infer::{canonical::canonicalizer::OriginalQueryValues, snapshot::CombinedSnapshot, DbInternerInferExt, InferCtxt}, mapping::{ChalkToNextSolver, InferenceVarExt}, DbInterner, ParamEnvAnd, SolverDefIds}, to_chalk_trait_id, traits::{next_trait_solve, next_trait_solve_canonical, next_trait_solve_in_ctxt, FnTrait, NextTraitSolveResult}, AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue, DebruijnIndex, DomainGoal, GenericArg, GenericArgData, Goal, GoalData, InEnvironment, InferenceVar, Interner, Lifetime, OpaqueTyId, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, VariableKind, WhereClause + AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue, DebruijnIndex, DomainGoal, + GenericArg, GenericArgData, Goal, GoalData, InEnvironment, InferenceVar, Interner, Lifetime, + OpaqueTyId, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Substitution, TraitEnvironment, + TraitRef, Ty, TyBuilder, TyExt, TyKind, VariableKind, WhereClause, + consteval::unknown_const, + db::HirDatabase, + fold_generic_args, fold_tys_and_consts, + next_solver::{ + self, Binder, DbInterner, ParamEnvAnd, Predicate, PredicateKind, SolverDefIds, Term, + infer::{ + DbInternerInferExt, InferCtxt, canonical::canonicalizer::OriginalQueryValues, + snapshot::CombinedSnapshot, + }, + mapping::{ChalkToNextSolver, InferenceVarExt, NextSolverToChalk}, + }, + to_chalk_trait_id, + traits::{ + FnTrait, NextTraitSolveResult, next_trait_solve_canonical_in_ctxt, next_trait_solve_in_ctxt, + }, }; impl<'db> InferenceContext<'db> { @@ -38,7 +62,7 @@ impl<'db> InferenceContext<'db> { let pending_obligations = mem::take(&mut self.table.pending_obligations); let obligations = pending_obligations .iter() - .filter_map(|obligation| match obligation.goal.data(Interner) { + .filter_map(|obligation| match obligation.to_chalk(self.table.interner).goal.data(Interner) { GoalData::DomainGoal(DomainGoal::Holds(clause)) => { let ty = match clause { WhereClause::AliasEq(AliasEq { @@ -67,51 +91,6 @@ impl<'db> InferenceContext<'db> { } } -#[derive(Debug, Clone)] -pub(crate) struct Canonicalized -where - T: HasInterner, -{ - pub(crate) value: Canonical, - free_vars: Vec, -} - -impl> Canonicalized { - pub(crate) fn apply_solution( - &self, - ctx: &mut InferenceTable<'_>, - solution: Canonical, - ) { - // the solution may contain new variables, which we need to convert to new inference vars - let new_vars = Substitution::from_iter( - Interner, - solution.binders.iter(Interner).map(|k| match &k.kind { - VariableKind::Ty(TyVariableKind::General) => ctx.new_type_var().cast(Interner), - VariableKind::Ty(TyVariableKind::Integer) => ctx.new_integer_var().cast(Interner), - VariableKind::Ty(TyVariableKind::Float) => ctx.new_float_var().cast(Interner), - // Chalk can sometimes return new lifetime variables. We just replace them by errors - // for now. - VariableKind::Lifetime => ctx.new_lifetime_var().cast(Interner), - VariableKind::Const(ty) => ctx.new_const_var(ty.clone()).cast(Interner), - }), - ); - for (i, v) in solution.value.iter(Interner).enumerate() { - let var = &self.free_vars[i]; - if let Some(ty) = v.ty(Interner) { - // eagerly replace projections in the type; we may be getting types - // e.g. from where clauses where this hasn't happened yet - let ty = ctx.normalize_associated_types_in(new_vars.apply(ty.clone(), Interner)); - tracing::debug!("unifying {:?} {:?}", var, ty); - ctx.unify(var.assert_ty_ref(Interner), &ty); - } else { - let v = new_vars.apply(v.clone(), Interner); - tracing::debug!("try_unifying {:?} {:?}", var, v); - let _ = ctx.try_unify(var, &v); - } - } - } -} - /// Check if types unify. /// /// Note that we consider placeholder types to unify with everything. @@ -207,23 +186,21 @@ bitflags::bitflags! { } } -type ChalkInferenceTable = chalk_solve::infer::InferenceTable; - #[derive(Clone)] pub(crate) struct InferenceTable<'a> { pub(crate) db: &'a dyn HirDatabase, pub(crate) interner: DbInterner<'a>, pub(crate) trait_env: Arc, pub(crate) tait_coercion_table: Option>, - infer_ctxt: InferCtxt<'a>, + pub(crate) infer_ctxt: InferCtxt<'a>, diverging_tys: FxHashSet, - pending_obligations: Vec>, + pending_obligations: Vec>>, } -pub(crate) struct InferenceTableSnapshot { +pub(crate) struct InferenceTableSnapshot<'a> { ctxt_snapshot: CombinedSnapshot, diverging_tys: FxHashSet, - pending_obligations: Vec>, + pending_obligations: Vec>>, } impl<'a> InferenceTable<'a> { @@ -234,7 +211,9 @@ impl<'a> InferenceTable<'a> { interner, trait_env, tait_coercion_table: None, - infer_ctxt: interner.infer_ctxt().build(rustc_type_ir::TypingMode::Analysis { defining_opaque_types_and_generators: SolverDefIds::new_from_iter(interner, []) }), + infer_ctxt: interner.infer_ctxt().build(rustc_type_ir::TypingMode::Analysis { + defining_opaque_types_and_generators: SolverDefIds::new_from_iter(interner, []), + }), diverging_tys: FxHashSet::default(), pending_obligations: Vec::new(), } @@ -250,40 +229,55 @@ impl<'a> InferenceTable<'a> { let mut new_tys = FxHashSet::default(); for ty in self.diverging_tys.iter() { match ty.kind(Interner) { - TyKind::InferenceVar(var, kind) => { - match kind { - TyVariableKind::General => { - let root = InferenceVar::from(self.infer_ctxt.root_var(TyVid::from_u32(var.index())).as_u32()); - if root.index() != var.index() { - new_tys.insert(TyKind::InferenceVar(root, *kind).intern(Interner)); - } - } - TyVariableKind::Integer => { - let root = InferenceVar::from(self.infer_ctxt.inner.borrow_mut().int_unification_table().find(IntVid::from_usize(var.index() as usize)).as_u32()); - if root.index() != var.index() { - new_tys.insert(TyKind::InferenceVar(root, *kind).intern(Interner)); - } - } - TyVariableKind::Float => { - let root = InferenceVar::from(self.infer_ctxt.inner.borrow_mut().float_unification_table().find(FloatVid::from_usize(var.index() as usize)).as_u32()); - if root.index() != var.index() { - new_tys.insert(TyKind::InferenceVar(root, *kind).intern(Interner)); - } + TyKind::InferenceVar(var, kind) => match kind { + TyVariableKind::General => { + let root = InferenceVar::from( + self.infer_ctxt.root_var(TyVid::from_u32(var.index())).as_u32(), + ); + if root.index() != var.index() { + new_tys.insert(TyKind::InferenceVar(root, *kind).intern(Interner)); } } - } + TyVariableKind::Integer => { + let root = InferenceVar::from( + self.infer_ctxt + .inner + .borrow_mut() + .int_unification_table() + .find(IntVid::from_usize(var.index() as usize)) + .as_u32(), + ); + if root.index() != var.index() { + new_tys.insert(TyKind::InferenceVar(root, *kind).intern(Interner)); + } + } + TyVariableKind::Float => { + let root = InferenceVar::from( + self.infer_ctxt + .inner + .borrow_mut() + .float_unification_table() + .find(FloatVid::from_usize(var.index() as usize)) + .as_u32(), + ); + if root.index() != var.index() { + new_tys.insert(TyKind::InferenceVar(root, *kind).intern(Interner)); + } + } + }, _ => {} } } - self.diverging_tys.extend(new_tys.into_iter()); + self.diverging_tys.extend(new_tys); } - pub(super) fn set_diverging(&mut self, iv: InferenceVar, kind: TyVariableKind, diverging: bool) { + pub(super) fn set_diverging(&mut self, iv: InferenceVar, kind: TyVariableKind) { self.diverging_tys.insert(TyKind::InferenceVar(iv, kind).intern(Interner)); } fn fallback_value(&self, iv: InferenceVar, kind: TyVariableKind) -> Ty { - let is_diverging = self.diverging_tys.contains(&TyKind::InferenceVar(iv, kind).intern(Interner)); + let is_diverging = + self.diverging_tys.contains(&TyKind::InferenceVar(iv, kind).intern(Interner)); if is_diverging { return TyKind::Never.intern(Interner); } @@ -295,19 +289,6 @@ impl<'a> InferenceTable<'a> { .intern(Interner) } - pub(crate) fn canonicalize_with_free_vars(&mut self, t: ParamEnvAnd<'a, T>) -> (rustc_type_ir::Canonical, ParamEnvAnd<'a, T>>, OriginalQueryValues<'a>) - where - T: rustc_type_ir::TypeFoldable>, - { - // try to resolve obligations before canonicalizing, since this might - // result in new knowledge about variables - self.resolve_obligations_as_possible(); - - let mut orig_values = OriginalQueryValues::default(); - let result = self.infer_ctxt.canonicalize_query(t, &mut orig_values); - (result.canonical, orig_values) - } - pub(crate) fn canonicalize(&mut self, t: T) -> rustc_type_ir::Canonical, T> where T: rustc_type_ir::TypeFoldable>, @@ -341,7 +322,7 @@ impl<'a> InferenceTable<'a> { self.resolve_ty_shallow(&ty) } TyKind::AssociatedType(id, subst) => { - return Either::Left(self.resolve_ty_shallow(&ty)); + // return Either::Left(self.resolve_ty_shallow(&ty)); if ty.data(Interner).flags.intersects( chalk_ir::TypeFlags::HAS_TY_INFER | chalk_ir::TypeFlags::HAS_CT_INFER, @@ -365,51 +346,44 @@ impl<'a> InferenceTable<'a> { ); let in_env = InEnvironment::new(&self.trait_env.env, goal); let goal = in_env.to_nextsolver(self.interner); - let goal = ParamEnvAnd { param_env: goal.param_env, value: goal.predicate }; + let goal = + ParamEnvAnd { param_env: goal.param_env, value: goal.predicate }; - let (canonical_goal, _orig_values) = { + let (canonical_goal, orig_values) = { let mut orig_values = OriginalQueryValues::default(); - let result = self.infer_ctxt.canonicalize_query(goal, &mut orig_values); + let result = + self.infer_ctxt.canonicalize_query(goal, &mut orig_values); (result.canonical, orig_values) }; let canonical_goal = rustc_type_ir::Canonical { max_universe: canonical_goal.max_universe, variables: canonical_goal.variables, - value: crate::next_solver::Goal { param_env: canonical_goal.value.param_env, predicate: canonical_goal.value.value }, + value: crate::next_solver::Goal { + param_env: canonical_goal.value.param_env, + predicate: canonical_goal.value.value, + }, }; - let solution = next_trait_solve_canonical( - self.db, - self.trait_env.krate, - self.trait_env.block, - canonical_goal.clone(), + let solution = next_trait_solve_canonical_in_ctxt( + &self.infer_ctxt, + canonical_goal, ); if let NextTraitSolveResult::Certain(canonical_subst) = solution { - // This is not great :) But let's just assert this for now and come back to it later. - if canonical_subst.value.subst.len(Interner) != 1 { + let subst = self.instantiate_canonical(canonical_subst).subst; + if subst.len(Interner) != orig_values.var_values.len() { ty } else { - let normalized = canonical_subst.value.subst.as_slice(Interner) - [0] - .assert_ty_ref(Interner); - match normalized.kind(Interner) { - TyKind::Alias(AliasTy::Projection(proj_ty)) => { - if id == &proj_ty.associated_ty_id - && subst == &proj_ty.substitution - { - ty + let target_ty = var.to_nextsolver(self.interner); + subst + .iter(Interner) + .zip(orig_values.var_values.iter()) + .find_map(|(new, orig)| { + if orig.ty() == Some(target_ty) { + Some(new.assert_ty_ref(Interner).clone()) } else { - normalized.clone() + None } - } - TyKind::AssociatedType(new_id, new_subst) => { - if new_id == id && new_subst == subst { - ty - } else { - normalized.clone() - } - } - _ => normalized.clone(), - } + }) + .unwrap_or(ty) } } else { ty @@ -504,8 +478,8 @@ impl<'a> InferenceTable<'a> { pub(crate) fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty { let var = self.new_type_var(); let alias_eq = AliasEq { alias: AliasTy::Projection(proj_ty), ty: var.clone() }; - let obligation = alias_eq.cast(Interner); - self.register_obligation(obligation); + let obligation: Goal = alias_eq.cast(Interner); + self.register_obligation(obligation.to_nextsolver(self.interner)); var } @@ -575,7 +549,9 @@ impl<'a> InferenceTable<'a> { Substitution::from_iter( Interner, binders.iter().map(|kind| match &kind.kind { - chalk_ir::VariableKind::Ty(ty_variable_kind) => self.new_var(*ty_variable_kind, false).cast(Interner), + chalk_ir::VariableKind::Ty(ty_variable_kind) => { + self.new_var(*ty_variable_kind, false).cast(Interner) + } chalk_ir::VariableKind::Lifetime => self.new_lifetime_var().cast(Interner), chalk_ir::VariableKind::Const(ty) => self.new_const_var(ty.clone()).cast(Interner), }), @@ -589,8 +565,11 @@ impl<'a> InferenceTable<'a> { let subst = self.fresh_subst(canonical.binders.as_slice(Interner)); subst.apply(canonical.value, Interner) } - - pub(crate) fn instantiate_canonical_ns(&mut self, canonical: rustc_type_ir::Canonical, T>) -> T + + pub(crate) fn instantiate_canonical_ns( + &mut self, + canonical: rustc_type_ir::Canonical, T>, + ) -> T where T: rustc_type_ir::TypeFoldable>, { @@ -605,7 +584,7 @@ impl<'a> InferenceTable<'a> { where T: HasInterner + TypeFoldable, { - let mut var_stack = &mut vec![]; + let var_stack = &mut vec![]; t.fold_with( &mut resolve::Resolver { table: self, var_stack, fallback }, DebruijnIndex::INNERMOST, @@ -618,6 +597,7 @@ impl<'a> InferenceTable<'a> { { let t = self.resolve_with_fallback(t, &|_, _, d, _| d); let t = self.normalize_associated_types_in(t); + // let t = self.resolve_opaque_tys_in(t); // Resolve again, because maybe normalization inserted infer vars. self.resolve_with_fallback(t, &|_, _, d, _| d) } @@ -650,7 +630,7 @@ impl<'a> InferenceTable<'a> { } let float_vars = self.infer_ctxt.inner.borrow_mut().float_unification_table().len(); for v in 0..float_vars { - let var = InferenceVar::from(v as u32).to_ty(Interner, TyVariableKind::Integer); + let var = InferenceVar::from(v as u32).to_ty(Interner, TyVariableKind::Float); let maybe_resolved = self.resolve_ty_shallow(&var); if let TyKind::InferenceVar(_, kind) = maybe_resolved.kind(Interner) { // I don't think we can ever unify these vars with float vars, but keep this here for now @@ -665,7 +645,11 @@ impl<'a> InferenceTable<'a> { } /// Unify two relatable values (e.g. `Ty`) and register new trait goals that arise from that. - pub(crate) fn unify, U: Relate>>(&mut self, ty1: &T, ty2: &T) -> bool { + pub(crate) fn unify, U: Relate>>( + &mut self, + ty1: &T, + ty2: &T, + ) -> bool { let result = match self.try_unify(ty1, ty2) { Ok(r) => r, Err(_) => return false, @@ -675,17 +659,17 @@ impl<'a> InferenceTable<'a> { } /// Unify two relatable values (e.g. `Ty`) and check whether trait goals which arise from that could be fulfilled - pub(crate) fn unify_deeply, U: Relate>>(&mut self, ty1: &T, ty2: &T) -> bool { + pub(crate) fn unify_deeply, U: Relate>>( + &mut self, + ty1: &T, + ty2: &T, + ) -> bool { let result = match self.try_unify(ty1, ty2) { Ok(r) => r, Err(_) => return false, }; - result.goals.iter().all(|goal| { - let goal = goal.to_nextsolver(self.interner); - match next_trait_solve_in_ctxt(&self.infer_ctxt, goal) { - Ok((_, Certainty::Yes)) => true, - _ => false, - } + result.goals.into_iter().all(|goal| { + matches!(next_trait_solve_in_ctxt(&self.infer_ctxt, goal), Ok((_, Certainty::Yes))) }) } @@ -695,20 +679,15 @@ impl<'a> InferenceTable<'a> { &mut self, t1: &T, t2: &T, - ) -> InferResult<()> { + ) -> InferResult<'a, ()> { let param_env = self.trait_env.env.to_nextsolver(self.interner); let lhs = t1.to_nextsolver(self.interner); let rhs = t2.to_nextsolver(self.interner); let variance = rustc_type_ir::Variance::Invariant; let span = crate::next_solver::Span::dummy(); match self.infer_ctxt.relate(param_env, lhs, variance, rhs, span) { - Ok(res) => { - let goals = res.into_iter().map(|g| ChalkToNextSolver::from_nextsolver(g, self.interner)).collect(); - Ok(InferOk { goals, value: () }) - } - Err(_) => { - Err(TypeError) - } + Ok(goals) => Ok(InferOk { goals, value: () }), + Err(_) => Err(TypeError), } } @@ -719,19 +698,75 @@ impl<'a> InferenceTable<'a> { if !ty.data(Interner).flags.intersects(chalk_ir::TypeFlags::HAS_FREE_LOCAL_NAMES) { return ty.clone(); } - self.resolve_obligations_as_possible(); - ChalkToNextSolver::from_nextsolver(self.infer_ctxt.resolve_vars_if_possible(ty.to_nextsolver(self.interner)), self.interner) + self.infer_ctxt + .resolve_vars_if_possible(ty.to_nextsolver(self.interner)) + .to_chalk(self.interner) } - pub(crate) fn snapshot(&mut self) -> InferenceTableSnapshot { + pub(crate) fn resolve_vars_with_obligations(&mut self, t: T) -> T + where + T: rustc_type_ir::TypeFoldable>, + { + use rustc_type_ir::TypeVisitableExt; + + if !t.has_non_region_infer() { + return t; + } + + let t = self.infer_ctxt.resolve_vars_if_possible(t); + + if !t.has_non_region_infer() { + return t; + } + + self.resolve_obligations_as_possible(); + self.infer_ctxt.resolve_vars_if_possible(t) + } + + pub(crate) fn structurally_resolve_type(&mut self, ty: &Ty) -> Ty { + if let TyKind::Alias(..) = ty.kind(Interner) { + self.structurally_normalize_ty(ty) + } else { + self.resolve_vars_with_obligations(ty.to_nextsolver(self.interner)) + .to_chalk(self.interner) + } + } + + fn structurally_normalize_ty(&mut self, ty: &Ty) -> Ty { + self.structurally_normalize_term(ty.to_nextsolver(self.interner).into()) + .expect_ty() + .to_chalk(self.interner) + } + + fn structurally_normalize_term(&mut self, term: Term<'a>) -> Term<'a> { + if term.to_alias_term().is_none() { + return term; + } + + let new_infer = self.infer_ctxt.next_term_var_of_kind(term); + + self.register_obligation(Predicate::new( + self.interner, + Binder::dummy(PredicateKind::AliasRelate( + term, + new_infer, + AliasRelationDirection::Equate, + )), + )); + self.resolve_obligations_as_possible(); + let res = self.infer_ctxt.resolve_vars_if_possible(new_infer); + if res == new_infer { term } else { res } + } + + pub(crate) fn snapshot(&mut self) -> InferenceTableSnapshot<'a> { let ctxt_snapshot = self.infer_ctxt.start_snapshot(); let diverging_tys = self.diverging_tys.clone(); let pending_obligations = self.pending_obligations.clone(); - InferenceTableSnapshot {ctxt_snapshot, pending_obligations, diverging_tys } + InferenceTableSnapshot { ctxt_snapshot, pending_obligations, diverging_tys } } #[tracing::instrument(skip_all)] - pub(crate) fn rollback_to(&mut self, snapshot: InferenceTableSnapshot) { + pub(crate) fn rollback_to(&mut self, snapshot: InferenceTableSnapshot<'a>) { self.infer_ctxt.rollback_to(snapshot.ctxt_snapshot); self.diverging_tys = snapshot.diverging_tys; self.pending_obligations = snapshot.pending_obligations; @@ -745,7 +780,10 @@ impl<'a> InferenceTable<'a> { result } - pub(crate) fn commit_if_ok(&mut self, f: impl FnOnce(&mut InferenceTable<'_>) -> Result) -> Result { + pub(crate) fn commit_if_ok( + &mut self, + f: impl FnOnce(&mut InferenceTable<'_>) -> Result, + ) -> Result { let snapshot = self.snapshot(); let result = f(self); match result { @@ -765,59 +803,31 @@ impl<'a> InferenceTable<'a> { let in_env = InEnvironment::new(&self.trait_env.env, goal); let canonicalized = self.canonicalize(in_env.to_nextsolver(self.interner)); - next_trait_solve_canonical(self.db, self.trait_env.krate, self.trait_env.block, canonicalized) + next_trait_solve_canonical_in_ctxt(&self.infer_ctxt, canonicalized) } - + #[tracing::instrument(level = "debug", skip(self))] pub(crate) fn solve_obligation(&mut self, goal: Goal) -> Result { let goal = InEnvironment::new(&self.trait_env.env, goal); - let Some(goal) = self.unify_opaque_instead_of_solve(goal) else { - return Ok(Certainty::Yes); - }; - let goal = goal.to_nextsolver(self.interner); let result = next_trait_solve_in_ctxt(&self.infer_ctxt, goal); result.map(|m| m.1) } - pub(crate) fn register_obligation(&mut self, goal: Goal) { - let in_env = InEnvironment::new(&self.trait_env.env, goal); - self.register_obligation_in_env(in_env) - } - - // If this goal is an `AliasEq` for an opaque type, just unify instead of trying to solve (since the next-solver is lazy) - fn unify_opaque_instead_of_solve(&mut self, goal: InEnvironment) -> Option> { - match goal.goal.data(Interner) { - chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds( - chalk_ir::WhereClause::AliasEq(chalk_ir::AliasEq { alias, ty }), - )) => { - if ty.inference_var(Interner).is_some() { - match alias { - chalk_ir::AliasTy::Opaque(opaque) => { - if self.unify( - &chalk_ir::TyKind::OpaqueType( - opaque.opaque_ty_id, - opaque.substitution.clone(), - ) - .intern(Interner), - ty, - ) { - return None; - } - } - _ => {} - } - } - } - _ => {} - } - Some(goal) + pub(crate) fn register_obligation(&mut self, predicate: Predicate<'a>) { + let goal = next_solver::Goal { + param_env: self.trait_env.env.to_nextsolver(self.interner), + predicate, + }; + self.register_obligation_in_env(goal) } #[tracing::instrument(level = "debug", skip(self))] - fn register_obligation_in_env(&mut self, goal: InEnvironment) { - let Some(goal) = self.unify_opaque_instead_of_solve(goal) else { return }; - let result = next_trait_solve_in_ctxt(&self.infer_ctxt, goal.to_nextsolver(self.interner)); + fn register_obligation_in_env( + &mut self, + goal: next_solver::Goal<'a, next_solver::Predicate<'a>>, + ) { + let result = next_trait_solve_in_ctxt(&self.infer_ctxt, goal); tracing::debug!(?result); match result { Ok((_, Certainty::Yes)) => {} @@ -828,7 +838,7 @@ impl<'a> InferenceTable<'a> { } } - pub(crate) fn register_infer_ok(&mut self, infer_ok: InferOk) { + pub(crate) fn register_infer_ok(&mut self, infer_ok: InferOk<'a, T>) { infer_ok.goals.into_iter().for_each(|goal| self.register_obligation_in_env(goal)); } @@ -841,12 +851,7 @@ impl<'a> InferenceTable<'a> { for goal in obligations.drain(..) { tracing::debug!(obligation = ?goal); - let Some(goal) = self.unify_opaque_instead_of_solve(goal) else { - changed = true; - continue; - }; - - let result = next_trait_solve_in_ctxt(&self.infer_ctxt, goal.to_nextsolver(self.interner)); + let result = next_trait_solve_in_ctxt(&self.infer_ctxt, goal); let (has_changed, certainty) = match result { Ok(result) => result, Err(_) => { @@ -934,40 +939,6 @@ impl<'a> InferenceTable<'a> { .fold_with(&mut VarFudger { table: self, highest_known_var }, DebruijnIndex::INNERMOST) } - #[tracing::instrument(level = "debug", skip(self))] - fn try_resolve_obligation( - &mut self, - canonicalized: &Canonicalized>, - ) -> NextTraitSolveResult { - let solution = next_trait_solve( - self.db, - self.trait_env.krate, - self.trait_env.block, - canonicalized.value.clone(), - ); - - tracing::debug!(?solution, ?canonicalized); - match &solution { - NextTraitSolveResult::Certain(v) => { - canonicalized.apply_solution( - self, - Canonical { - binders: v.binders.clone(), - // FIXME handle constraints - value: v.value.subst.clone(), - }, - ); - } - // ...so, should think about how to get some actually get some guidance here - NextTraitSolveResult::Uncertain(v) => { - canonicalized.apply_solution(self, v.clone()); - } - NextTraitSolveResult::NoSolution => {} - } - - solution - } - pub(crate) fn callable_sig( &mut self, ty: &Ty, @@ -1027,7 +998,7 @@ impl<'a> InferenceTable<'a> { let goal: Goal = trait_ref.clone().cast(Interner); if !self.try_obligation(goal.clone()).no_solution() { - self.register_obligation(goal); + self.register_obligation(goal.to_nextsolver(self.interner)); let return_ty = self.normalize_projection_ty(projection); for &fn_x in subtraits { let fn_x_trait = fn_x.get_id(self.db, krate)?; @@ -1067,7 +1038,7 @@ impl<'a> InferenceTable<'a> { match ty.kind(Interner) { TyKind::Error => self.new_type_var(), TyKind::InferenceVar(..) => { - let ty_resolved = self.resolve_ty_shallow(&ty); + let ty_resolved = self.structurally_resolve_type(&ty); if ty_resolved.is_unknown() { self.new_type_var() } else { ty } } _ => ty, @@ -1165,7 +1136,9 @@ impl fmt::Debug for InferenceTable<'_> { mod resolve { use super::InferenceTable; use crate::{ - next_solver::mapping::ChalkToNextSolver, ConcreteConst, Const, ConstData, ConstScalar, ConstValue, DebruijnIndex, GenericArg, InferenceVar, Interner, Lifetime, Ty, TyVariableKind, VariableKind + ConcreteConst, Const, ConstData, ConstScalar, ConstValue, DebruijnIndex, GenericArg, + InferenceVar, Interner, Lifetime, Ty, TyVariableKind, VariableKind, + next_solver::mapping::NextSolverToChalk, }; use chalk_ir::{ cast::Cast, @@ -1220,7 +1193,7 @@ mod resolve { .clone(); } if let Ok(known_ty) = self.table.infer_ctxt.probe_ty_var(vid) { - let known_ty: Ty = ChalkToNextSolver::from_nextsolver(known_ty, self.table.interner); + let known_ty: Ty = known_ty.to_chalk(self.table.interner); // known_ty may contain other variables that are known by now self.var_stack.push((var, VarKind::Ty(kind))); let result = known_ty.fold_with(self, outer_binder); @@ -1234,7 +1207,13 @@ mod resolve { } } TyVariableKind::Integer => { - let vid = self.table.infer_ctxt.inner.borrow_mut().int_unification_table().find(IntVid::from(var.index())); + let vid = self + .table + .infer_ctxt + .inner + .borrow_mut() + .int_unification_table() + .find(IntVid::from(var.index())); let var = InferenceVar::from(vid.as_u32()); if self.var_stack.contains(&(var, VarKind::Ty(kind))) { // recursive type @@ -1244,7 +1223,7 @@ mod resolve { .clone(); } if let Some(known_ty) = self.table.infer_ctxt.resolve_int_var(vid) { - let known_ty: Ty = ChalkToNextSolver::from_nextsolver(known_ty, self.table.interner); + let known_ty: Ty = known_ty.to_chalk(self.table.interner); // known_ty may contain other variables that are known by now self.var_stack.push((var, VarKind::Ty(kind))); let result = known_ty.fold_with(self, outer_binder); @@ -1258,7 +1237,13 @@ mod resolve { } } TyVariableKind::Float => { - let vid = self.table.infer_ctxt.inner.borrow_mut().float_unification_table().find(FloatVid::from(var.index())); + let vid = self + .table + .infer_ctxt + .inner + .borrow_mut() + .float_unification_table() + .find(FloatVid::from(var.index())); let var = InferenceVar::from(vid.as_u32()); if self.var_stack.contains(&(var, VarKind::Ty(kind))) { // recursive type @@ -1268,7 +1253,7 @@ mod resolve { .clone(); } if let Some(known_ty) = self.table.infer_ctxt.resolve_float_var(vid) { - let known_ty: Ty = ChalkToNextSolver::from_nextsolver(known_ty, self.table.interner); + let known_ty: Ty = known_ty.to_chalk(self.table.interner); // known_ty may contain other variables that are known by now self.var_stack.push((var, VarKind::Ty(kind))); let result = known_ty.fold_with(self, outer_binder); @@ -1290,7 +1275,10 @@ mod resolve { var: InferenceVar, outer_binder: DebruijnIndex, ) -> Const { - let vid = self.table.infer_ctxt.root_const_var(rustc_type_ir::ConstVid::from_u32(var.index())); + let vid = self + .table + .infer_ctxt + .root_const_var(rustc_type_ir::ConstVid::from_u32(var.index())); let var = InferenceVar::from(vid.as_u32()); let default = ConstData { ty: ty.clone(), @@ -1305,7 +1293,7 @@ mod resolve { .clone(); } if let Ok(known_const) = self.table.infer_ctxt.probe_const_var(vid) { - let known_const: Const = ChalkToNextSolver::from_nextsolver(known_const, self.table.interner); + let known_const: Const = known_const.to_chalk(self.table.interner); // known_ty may contain other variables that are known by now self.var_stack.push((var, VarKind::Const)); let result = known_const.fold_with(self, outer_binder); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 323ea951a914..659bc5ade3c7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -101,7 +101,10 @@ use crate::{ display::{DisplayTarget, HirDisplay}, generics::Generics, infer::unify::InferenceTable, - next_solver::{DbInterner, mapping::convert_ty_for_result}, + next_solver::{ + DbInterner, + mapping::{ChalkToNextSolver, convert_ty_for_result}, + }, }; pub use autoderef::autoderef; @@ -957,8 +960,10 @@ pub fn callable_sig_from_fn_trait( ) .build(); - if !table.try_obligation(trait_ref.clone().cast(Interner)).no_solution() { - table.register_obligation(trait_ref.clone().cast(Interner)); + let goal: Goal = trait_ref.clone().cast(Interner); + let pred = goal.to_nextsolver(table.interner); + if !table.try_obligation(goal).no_solution() { + table.register_obligation(pred); let return_ty = table.normalize_projection_ty(projection); for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] { let fn_x_trait = fn_x.get_id(db, krate)?; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index ee223059d1e5..fa80567b1ecc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -22,7 +22,25 @@ use stdx::never; use triomphe::Arc; use crate::{ - autoderef::{self, AutoderefKind}, db::HirDatabase, from_chalk_trait_id, from_foreign_def_id, infer::{unify::InferenceTable, Adjust, Adjustment, OverloadedDeref, PointerCast}, lang_items::is_box, next_solver::{mapping::ChalkToNextSolver, SolverDefId}, primitive::{FloatTy, IntTy, UintTy}, to_chalk_trait_id, traits::{next_trait_solve_canonical}, utils::all_super_traits, AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, GenericArgData, Goal, InEnvironment, Interner, Mutability, Scalar, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, TyVariableKind, VariableKind, WhereClause + AdtId, AliasTy, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, + GenericArgData, Goal, InEnvironment, Interner, Mutability, Scalar, Substitution, + TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, TyVariableKind, + VariableKind, WhereClause, + autoderef::{self, AutoderefKind}, + db::HirDatabase, + from_chalk_trait_id, from_foreign_def_id, + infer::{Adjust, Adjustment, OverloadedDeref, PointerCast, unify::InferenceTable}, + lang_items::is_box, + next_solver::{ + self, SolverDefId, + fulfill::FulfillmentCtxt, + infer::DefineOpaqueTypes, + mapping::{ChalkToNextSolver, NextSolverToChalk}, + }, + primitive::{FloatTy, IntTy, UintTy}, + to_chalk_trait_id, + traits::next_trait_solve_canonical_in_ctxt, + utils::all_super_traits, }; /// This is used as a key for indexing impls. @@ -89,6 +107,7 @@ impl TyFingerprint { } TyKind::AssociatedType(_, _) | TyKind::OpaqueType(_, _) + | TyKind::Alias(AliasTy::Opaque(_)) | TyKind::FnDef(_, _) | TyKind::Closure(_, _) | TyKind::Coroutine(..) @@ -106,7 +125,7 @@ impl TyFingerprint { } /// Creates a TyFingerprint for looking up a trait impl. - pub fn for_trait_impl_ns<'db>(ty: &crate::next_solver::Ty<'db>) -> Option { + pub fn for_trait_impl_ns<'db>(ty: &next_solver::Ty<'db>) -> Option { use rustc_type_ir::TyKind; let fp = match (*ty).kind() { TyKind::Str => TyFingerprint::Str, @@ -523,7 +542,7 @@ pub fn def_crates(db: &dyn HirDatabase, ty: &Ty, cur_crate: Crate) -> Option( db: &'db dyn HirDatabase, - ty: &crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, + ty: &next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, env: Arc, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, @@ -605,7 +624,7 @@ pub struct ReceiverAdjustments { impl ReceiverAdjustments { pub(crate) fn apply(&self, table: &mut InferenceTable<'_>, ty: Ty) -> (Ty, Vec) { - let mut ty = table.resolve_ty_shallow(&ty); + let mut ty = table.structurally_resolve_type(&ty); let mut adjust = Vec::new(); for _ in 0..self.autoderefs { match autoderef::autoderef_step(table, ty.clone(), true, false) { @@ -686,7 +705,7 @@ impl ReceiverAdjustments { // lifetime problems, because we need to borrow temp `CrateImplDefs`. // FIXME add a context type here? pub(crate) fn iterate_method_candidates<'db, T>( - ty: &crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, + ty: &next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, db: &'db dyn HirDatabase, env: Arc, traits_in_scope: &FxHashSet, @@ -887,7 +906,7 @@ fn find_matching_impl( if table.try_obligation(goal.clone()).no_solution() { return None; } - table.register_obligation(goal); + table.register_obligation(goal.to_nextsolver(table.interner)); } Some((impl_.impl_items(db), table.resolve_completely(impl_substs))) }) @@ -1035,7 +1054,7 @@ pub fn check_orphan_rules(db: &dyn HirDatabase, impl_: ImplId) -> bool { } pub fn iterate_path_candidates<'db>( - ty: &crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, + ty: &next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, db: &'db dyn HirDatabase, env: Arc, traits_in_scope: &FxHashSet, @@ -1057,7 +1076,7 @@ pub fn iterate_path_candidates<'db>( } pub fn iterate_method_candidates_dyn<'db>( - ty: &crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, + ty: &next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, db: &'db dyn HirDatabase, env: Arc, traits_in_scope: &FxHashSet, @@ -1096,7 +1115,7 @@ pub fn iterate_method_candidates_dyn<'db>( // types*. let mut table = InferenceTable::new(db, env); - let ty = table.instantiate_canonical_ns(ty.clone()); + let ty = table.instantiate_canonical_ns(*ty); let deref_chain = autoderef_method_receiver(&mut table, ty); deref_chain.into_iter().try_for_each(|(receiver_ty, adj)| { @@ -1129,18 +1148,13 @@ pub fn iterate_method_candidates_dyn<'db>( #[tracing::instrument(skip_all, fields(name = ?name))] fn iterate_method_candidates_with_autoref<'db>( table: &mut InferenceTable<'db>, - receiver_ty: crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, + receiver_ty: next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, first_adjustment: ReceiverAdjustments, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, name: Option<&Name>, callback: &mut dyn MethodCandidateCallback, ) -> ControlFlow<()> { - if matches!(receiver_ty.value.kind(), rustc_type_ir::TyKind::Bound(..)) { - // don't try to resolve methods on unknown types - return ControlFlow::Continue(()); - } - let interner = table.interner; let mut iterate_method_candidates_by_receiver = move |receiver_ty, first_adjustment| { @@ -1166,12 +1180,17 @@ fn iterate_method_candidates_with_autoref<'db>( maybe_reborrowed.autoderefs += 1; } - iterate_method_candidates_by_receiver(receiver_ty.clone(), maybe_reborrowed)?; + iterate_method_candidates_by_receiver(receiver_ty, maybe_reborrowed)?; - let refed = crate::next_solver::Canonical { + let refed = next_solver::Canonical { max_universe: receiver_ty.max_universe, variables: receiver_ty.variables, - value: crate::next_solver::Ty::new_ref(interner, crate::next_solver::Region::error(interner), receiver_ty.value, rustc_ast_ir::Mutability::Not), + value: next_solver::Ty::new_ref( + interner, + next_solver::Region::error(interner), + receiver_ty.value, + rustc_ast_ir::Mutability::Not, + ), }; iterate_method_candidates_by_receiver( @@ -1179,10 +1198,15 @@ fn iterate_method_candidates_with_autoref<'db>( first_adjustment.with_autoref(AutorefOrPtrAdjustment::Autoref(Mutability::Not)), )?; - let ref_muted = crate::next_solver::Canonical { + let ref_muted = next_solver::Canonical { max_universe: receiver_ty.max_universe, variables: receiver_ty.variables, - value: crate::next_solver::Ty::new_ref(interner, crate::next_solver::Region::error(interner), receiver_ty.value, rustc_ast_ir::Mutability::Mut), + value: next_solver::Ty::new_ref( + interner, + next_solver::Region::error(interner), + receiver_ty.value, + rustc_ast_ir::Mutability::Mut, + ), }; iterate_method_candidates_by_receiver( @@ -1190,10 +1214,12 @@ fn iterate_method_candidates_with_autoref<'db>( first_adjustment.with_autoref(AutorefOrPtrAdjustment::Autoref(Mutability::Mut)), )?; - if let rustc_type_ir::TyKind::RawPtr(ty, rustc_ast_ir::Mutability::Mut) = receiver_ty.value.kind() { + if let rustc_type_ir::TyKind::RawPtr(ty, rustc_ast_ir::Mutability::Mut) = + receiver_ty.value.kind() + { let const_ptr_ty = rustc_type_ir::Canonical { max_universe: rustc_type_ir::UniverseIndex::ZERO, - value: crate::next_solver::Ty::new_ptr(interner, ty, rustc_ast_ir::Mutability::Not), + value: next_solver::Ty::new_ptr(interner, ty, rustc_ast_ir::Mutability::Not), variables: receiver_ty.variables, }; iterate_method_candidates_by_receiver( @@ -1247,7 +1273,7 @@ where #[tracing::instrument(skip_all, fields(name = ?name))] fn iterate_method_candidates_by_receiver<'db>( table: &mut InferenceTable<'db>, - receiver_ty: crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, + receiver_ty: next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, receiver_adjustments: ReceiverAdjustments, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, @@ -1255,7 +1281,7 @@ fn iterate_method_candidates_by_receiver<'db>( callback: &mut dyn MethodCandidateCallback, ) -> ControlFlow<()> { let receiver_ty = table.instantiate_canonical_ns(receiver_ty); - let receiver_ty: crate::Ty = ChalkToNextSolver::from_nextsolver(receiver_ty, table.interner); + let receiver_ty: crate::Ty = receiver_ty.to_chalk(table.interner); // We're looking for methods with *receiver* type receiver_ty. These could // be found in any of the derefs of receiver_ty, so we have to go through // that, including raw derefs. @@ -1270,6 +1296,7 @@ fn iterate_method_candidates_by_receiver<'db>( Some(&receiver_ty), Some(receiver_adjustments.clone()), visible_from_module, + LookupMode::MethodCall, &mut |adjustments, item, is_visible| { callback.on_inherent_method(adjustments, item, is_visible) }, @@ -1293,6 +1320,7 @@ fn iterate_method_candidates_by_receiver<'db>( name, Some(&receiver_ty), Some(receiver_adjustments.clone()), + LookupMode::MethodCall, &mut |adjustments, item, is_visible| { callback.on_trait_method(adjustments, item, is_visible) }, @@ -1304,7 +1332,7 @@ fn iterate_method_candidates_by_receiver<'db>( #[tracing::instrument(skip_all, fields(name = ?name))] fn iterate_method_candidates_for_self_ty<'db>( - self_ty: &crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, + self_ty: &next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, db: &'db dyn HirDatabase, env: Arc, traits_in_scope: &FxHashSet, @@ -1313,7 +1341,7 @@ fn iterate_method_candidates_for_self_ty<'db>( callback: &mut dyn MethodCandidateCallback, ) -> ControlFlow<()> { let mut table = InferenceTable::new(db, env); - let self_ty = ChalkToNextSolver::from_nextsolver(table.instantiate_canonical_ns(self_ty.clone()), table.interner); + let self_ty = table.instantiate_canonical_ns(*self_ty).to_chalk(table.interner); iterate_inherent_methods( &self_ty, &mut table, @@ -1321,6 +1349,7 @@ fn iterate_method_candidates_for_self_ty<'db>( None, None, visible_from_module, + LookupMode::Path, &mut |adjustments, item, is_visible| { callback.on_inherent_method(adjustments, item, is_visible) }, @@ -1332,6 +1361,7 @@ fn iterate_method_candidates_for_self_ty<'db>( name, None, None, + LookupMode::Path, &mut |adjustments, item, is_visible| { callback.on_trait_method(adjustments, item, is_visible) }, @@ -1346,12 +1376,13 @@ fn iterate_trait_method_candidates( name: Option<&Name>, receiver_ty: Option<&Ty>, receiver_adjustments: Option, + mode: LookupMode, callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, ) -> ControlFlow<()> { let db = table.db; - let canonical_self_ty = ChalkToNextSolver::from_nextsolver(table.canonicalize(self_ty.clone().to_nextsolver(table.interner)), table.interner); - let TraitEnvironment { krate, block, .. } = *table.trait_env; + let canonical_self_ty = table.canonicalize(self_ty.clone().to_nextsolver(table.interner)); + let TraitEnvironment { krate, .. } = *table.trait_env; 'traits: for &t in traits_in_scope { let data = db.trait_signature(t); @@ -1391,15 +1422,22 @@ fn iterate_trait_method_candidates( for &(_, item) in t.trait_items(db).items.iter() { // Don't pass a `visible_from_module` down to `is_valid_candidate`, // since only inherent methods should be included into visibility checking. - let visible = - match is_valid_trait_method_candidate(table, t, name, receiver_ty, item, self_ty) { - IsValidCandidate::Yes => true, - IsValidCandidate::NotVisible => false, - IsValidCandidate::No => continue, - }; + let visible = match is_valid_trait_method_candidate( + table, + t, + name, + receiver_ty, + item, + self_ty, + mode, + ) { + IsValidCandidate::Yes => true, + IsValidCandidate::NotVisible => false, + IsValidCandidate::No => continue, + }; if !known_implemented { - let goal = generic_implements_goal(db, &table.trait_env, t, &canonical_self_ty); - if db.trait_solve(krate, block, goal.cast(Interner)).no_solution() { + let goal = generic_implements_goal_ns(table, t, canonical_self_ty); + if next_trait_solve_canonical_in_ctxt(&table.infer_ctxt, goal).no_solution() { continue 'traits; } } @@ -1418,6 +1456,7 @@ fn iterate_inherent_methods( receiver_ty: Option<&Ty>, receiver_adjustments: Option, visible_from_module: VisibleFromModule, + mode: LookupMode, callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, ) -> ControlFlow<()> { let db = table.db; @@ -1441,6 +1480,7 @@ fn iterate_inherent_methods( receiver_adjustments.clone(), callback, traits, + mode, )?; } TyKind::Dyn(_) => { @@ -1454,6 +1494,7 @@ fn iterate_inherent_methods( receiver_adjustments.clone(), callback, traits.into_iter(), + mode, )?; } } @@ -1512,6 +1553,7 @@ fn iterate_inherent_methods( receiver_adjustments: Option, callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, traits: impl Iterator, + mode: LookupMode, ) -> ControlFlow<()> { let db = table.db; for t in traits { @@ -1525,6 +1567,7 @@ fn iterate_inherent_methods( receiver_ty, item, self_ty, + mode, ) { IsValidCandidate::Yes => true, IsValidCandidate::NotVisible => false, @@ -1571,22 +1614,17 @@ fn iterate_inherent_methods( } /// Returns the receiver type for the index trait call. -pub(crate) fn resolve_indexing_op( - db: &dyn HirDatabase, - env: Arc, - ty: Canonical, +pub(crate) fn resolve_indexing_op<'db>( + table: &mut InferenceTable<'db>, + ty: next_solver::Canonical<'db, next_solver::Ty<'db>>, index_trait: TraitId, ) -> Option { - let mut table = InferenceTable::new(db, env); - let ty = table.instantiate_canonical(ty); - let interner = table.interner; - let deref_chain = autoderef_method_receiver(&mut table, ty.to_nextsolver(interner)); + let ty = table.instantiate_canonical_ns(ty); + let deref_chain = autoderef_method_receiver(table, ty); for (ty, adj) in deref_chain { //let goal = generic_implements_goal_ns(db, &table.trait_env, index_trait, &ty); - let goal = generic_implements_goal(db, &table.trait_env, index_trait, &ChalkToNextSolver::from_nextsolver(ty, interner)); - let goal: chalk_ir::Canonical>> = goal.cast(Interner); - let goal = goal.to_nextsolver(interner); - if !next_trait_solve_canonical(db, table.trait_env.krate, table.trait_env.block, goal).no_solution() { + let goal = generic_implements_goal_ns(table, index_trait, ty); + if !next_trait_solve_canonical_in_ctxt(&table.infer_ctxt, goal).no_solution() { return Some(adj); } } @@ -1666,6 +1704,7 @@ fn is_valid_trait_method_candidate( receiver_ty: Option<&Ty>, item: AssocItemId, self_ty: &Ty, + mode: LookupMode, ) -> IsValidCandidate { let db = table.db; match item { @@ -1693,6 +1732,35 @@ fn is_valid_trait_method_candidate( let expected_receiver = sig.map(|s| s.params()[0].clone()).substitute(Interner, &fn_subst); + // FIXME: Clean up this mess with some context struct like rustc's `ProbeContext` + let variance = match mode { + LookupMode::MethodCall => rustc_type_ir::Variance::Covariant, + LookupMode::Path => rustc_type_ir::Variance::Invariant, + }; + let res = table + .infer_ctxt + .at( + &next_solver::infer::traits::ObligationCause::dummy(), + table.trait_env.env.to_nextsolver(table.interner), + ) + .relate( + DefineOpaqueTypes::No, + expected_receiver.to_nextsolver(table.interner), + variance, + receiver_ty.to_nextsolver(table.interner), + ); + let Ok(infer_ok) = res else { + return IsValidCandidate::No; + }; + + if !infer_ok.obligations.is_empty() { + let mut ctxt = FulfillmentCtxt::new(&table.infer_ctxt); + for pred in infer_ok.into_obligations() { + ctxt.register_predicate_obligation(&table.infer_ctxt, pred); + } + check_that!(ctxt.select_all_or_error(&table.infer_ctxt).is_empty()); + } + check_that!(table.unify(receiver_ty, &expected_receiver)); } @@ -1839,53 +1907,36 @@ fn generic_implements_goal( Canonical { binders, value } } -/* /// This creates Substs for a trait with the given Self type and type variables /// for all other parameters, to query the trait solver with it. #[tracing::instrument(skip_all)] fn generic_implements_goal_ns<'db>( - db: &'db dyn HirDatabase, - interner: DbInterner<'db>, - env: &TraitEnvironment, + table: &mut InferenceTable<'db>, trait_: TraitId, - self_ty: &crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, -) -> crate::next_solver::Canonical<'db, crate::next_solver::Goal<'db, crate::next_solver::Predicate<'db>>> { - let variables = self_ty.variables; - let trait_ref = TyBuilder::trait_ref(db, trait_) - .push(ChalkToNextSolver::from_nextsolver(self_ty.value, interner)) - .fill_with_bound_vars(DebruijnIndex::INNERMOST, variables.len()) - .build(); + self_ty: next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, +) -> next_solver::Canonical<'db, next_solver::Goal<'db, crate::next_solver::Predicate<'db>>> { + let args = table.infer_ctxt.fresh_args_for_item(SolverDefId::TraitId(trait_)); + let self_ty = table.instantiate_canonical_ns(self_ty); + let trait_ref = + rustc_type_ir::TraitRef::new_from_args(table.infer_ctxt.interner, trait_.into(), args) + .with_replaced_self_ty(table.infer_ctxt.interner, self_ty); + let goal = next_solver::Goal::new( + table.infer_ctxt.interner, + table.trait_env.env.to_nextsolver(table.infer_ctxt.interner), + trait_ref, + ); - let infer_ctxt = interner.infer_ctxt().build(TypingMode::non_body_analysis()); - let args = infer_ctxt.fresh_args_for_item(SolverDefId::TraitId(trait_)); - - rustc_type_ir::TraitRef::new(interner, SolverDefId::TraitId(trait_)).with_self_ty(interner, self_ty.value); - - - let kinds = - binders.iter().cloned().chain(trait_ref.substitution.iter(Interner).skip(1).map(|it| { - let vk = match it.data(Interner) { - GenericArgData::Ty(_) => VariableKind::Ty(chalk_ir::TyVariableKind::General), - GenericArgData::Lifetime(_) => VariableKind::Lifetime, - GenericArgData::Const(c) => VariableKind::Const(c.data(Interner).ty.clone()), - }; - WithKind::new(vk, UniverseIndex::ROOT) - })); - let binders = CanonicalVarKinds::from_iter(Interner, kinds); - - let obligation = trait_ref.cast(Interner); - let value = InEnvironment::new(&env.env, obligation); - crate::next_solver::Canonical { max_universe, value, variables } + table.canonicalize(goal) } -*/ fn autoderef_method_receiver<'db>( table: &mut InferenceTable<'db>, - ty: crate::next_solver::Ty<'db>, -) -> Vec<(crate::next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, ReceiverAdjustments)> { + ty: next_solver::Ty<'db>, +) -> Vec<(next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, ReceiverAdjustments)> { let interner = table.interner; let mut deref_chain = Vec::new(); - let mut autoderef = autoderef::Autoderef::new_no_tracking(table, ChalkToNextSolver::from_nextsolver(ty, interner), false, true); + let mut autoderef = + autoderef::Autoderef::new_no_tracking(table, ty.to_chalk(interner), false, true); while let Some((ty, derefs)) = autoderef.next() { deref_chain.push(( autoderef.table.canonicalize(ty.to_nextsolver(interner)), @@ -1894,11 +1945,11 @@ fn autoderef_method_receiver<'db>( } // As a last step, we can do array unsizing (that's the only unsizing that rustc does for method receivers!) if let Some((rustc_type_ir::Array(parameters, _), variables, max_universe, adj)) = - deref_chain.last().map(|d| (d.0.value.kind(), d.0.variables.clone(), d.0.max_universe, d.1.clone())) + deref_chain.last().map(|d| (d.0.value.kind(), d.0.variables, d.0.max_universe, d.1.clone())) { - let unsized_ty = crate::next_solver::Ty::new_slice(interner, parameters); + let unsized_ty = next_solver::Ty::new_slice(interner, parameters); deref_chain.push(( - crate::next_solver::Canonical { max_universe, value: unsized_ty, variables, }, + next_solver::Canonical { max_universe, value: unsized_ty, variables }, ReceiverAdjustments { unsize_array: true, ..adj.clone() }, )); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs index 8c48a16537a2..6465099dffd7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs @@ -34,8 +34,7 @@ pub use eval::{ }; pub use lower::{MirLowerError, lower_to_mir, mir_body_for_closure_query, mir_body_query}; pub use monomorphization::{ - monomorphize_mir_body_bad, monomorphized_mir_body_for_closure_query, - monomorphized_mir_body_query, + monomorphized_mir_body_for_closure_query, monomorphized_mir_body_query, }; use rustc_hash::FxHashMap; use smallvec::{SmallVec, smallvec}; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs index b0b922316914..555b87850924 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs @@ -38,7 +38,6 @@ struct Filler<'a> { trait_env: Arc, subst: &'a Substitution, generics: Option, - owner: DefWithBodyId, } impl FallibleTypeFolder for Filler<'_> { type Error = MirLowerError; @@ -66,7 +65,11 @@ impl FallibleTypeFolder for Filler<'_> { })) .intern(Interner)) } - TyKind::OpaqueType(id, subst) => { + TyKind::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy { + opaque_ty_id: id, + substitution: subst, + })) + | TyKind::OpaqueType(id, subst) => { let impl_trait_id = self.db.lookup_intern_impl_trait_id((*id).into()); let subst = subst.clone().try_fold_with(self.as_dyn(), outer_binder)?; match impl_trait_id { @@ -74,7 +77,6 @@ impl FallibleTypeFolder for Filler<'_> { let infer = self.db.infer(func.into()); let filler = &mut Filler { db: self.db, - owner: self.owner, trait_env: self.trait_env.clone(), subst: &subst, generics: Some(generics(self.db, func.into())), @@ -306,7 +308,7 @@ pub fn monomorphized_mir_body_query( trait_env: Arc, ) -> Result, MirLowerError> { let generics = owner.as_generic_def_id(db).map(|g_def| generics(db, g_def)); - let filler = &mut Filler { db, subst: &subst, trait_env, generics, owner }; + let filler = &mut Filler { db, subst: &subst, trait_env, generics }; let body = db.mir_body(owner)?; let mut body = (*body).clone(); filler.fill_body(&mut body)?; @@ -330,23 +332,9 @@ pub fn monomorphized_mir_body_for_closure_query( ) -> Result, MirLowerError> { let InternedClosure(owner, _) = db.lookup_intern_closure(closure); let generics = owner.as_generic_def_id(db).map(|g_def| generics(db, g_def)); - let filler = &mut Filler { db, subst: &subst, trait_env, generics, owner }; + let filler = &mut Filler { db, subst: &subst, trait_env, generics }; let body = db.mir_body_for_closure(closure)?; let mut body = (*body).clone(); filler.fill_body(&mut body)?; Ok(Arc::new(body)) } - -// FIXME: remove this function. Monomorphization is a time consuming job and should always be a query. -pub fn monomorphize_mir_body_bad( - db: &dyn HirDatabase, - mut body: MirBody, - subst: Substitution, - trait_env: Arc, -) -> Result { - let owner = body.owner; - let generics = owner.as_generic_def_id(db).map(|g_def| generics(db, g_def)); - let filler = &mut Filler { db, subst: &subst, trait_env, generics, owner }; - filler.fill_body(&mut body)?; - Ok(body) -} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs index 21762aa66a85..0225deebe4f3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs @@ -5,11 +5,10 @@ pub mod abi; mod consts; mod def_id; pub mod fold; -mod fulfill; +pub mod fulfill; mod generic_arg; pub mod generics; pub mod infer; -//mod infer_new; pub mod interner; mod ir_print; pub mod mapping; @@ -24,7 +23,6 @@ pub mod util; pub use consts::*; pub use def_id::*; pub use generic_arg::*; -//pub use infer_new::*; pub use interner::*; pub use opaques::*; pub use predicate::*; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs index 0820006e5dfb..5d11525cd199 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs @@ -1,14 +1,28 @@ +//! This module contains code to canonicalize values into a `Canonical<'db, T>`. +//! +//! For an overview of what canonicalization is and how it fits into +//! rustc, check out the [chapter in the rustc dev guide][c]. +//! +//! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html use rustc_hash::FxHashMap; use rustc_index::Idx; -use rustc_type_ir::inherent::{Const as _, IntoKind as _, Region as _, SliceLike, Ty as _}; use rustc_type_ir::InferTy::{self, FloatVar, IntVar, TyVar}; -use rustc_type_ir::{BoundVar, CanonicalQueryInput, CanonicalTyVarKind, DebruijnIndex, Flags, InferConst, RegionKind, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, UniverseIndex}; +use rustc_type_ir::inherent::{Const as _, IntoKind as _, Region as _, SliceLike, Ty as _}; +use rustc_type_ir::{ + BoundVar, CanonicalQueryInput, CanonicalTyVarKind, DebruijnIndex, Flags, InferConst, + RegionKind, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, + UniverseIndex, +}; use smallvec::SmallVec; use tracing::debug; use crate::next_solver::infer::InferCtxt; -use crate::next_solver::{Binder, BoundRegion, BoundRegionKind, BoundTy, Canonical, CanonicalVarKind, CanonicalVars, Const, ConstKind, DbInterner, GenericArg, ParamEnvAnd, Placeholder, Region, Ty, TyKind}; +use crate::next_solver::{ + Binder, BoundConst, BoundRegion, BoundRegionKind, BoundTy, Canonical, CanonicalVarKind, + CanonicalVars, Const, ConstKind, DbInterner, GenericArg, ParamEnvAnd, Placeholder, Region, Ty, + TyKind, +}; /// When we canonicalize a value to form a query, we wind up replacing /// various parts of it with canonical variables. This struct stores @@ -65,7 +79,7 @@ impl<'db> InferCtxt<'db> { // `param_env` because they are treated differently by trait selection. let canonical_param_env = Canonicalizer::canonicalize( param_env, - None, + self, self.interner, &CanonicalizeFreeRegionsOtherThanStatic, query_state, @@ -74,7 +88,7 @@ impl<'db> InferCtxt<'db> { let canonical = Canonicalizer::canonicalize_with_base( canonical_param_env, value, - Some(self), + self, self.interner, &CanonicalizeAllFreeRegions, query_state, @@ -115,7 +129,7 @@ impl<'db> InferCtxt<'db> { let mut query_state = OriginalQueryValues::default(); Canonicalizer::canonicalize( value, - Some(self), + self, self.interner, &CanonicalizeQueryResponse, &mut query_state, @@ -129,7 +143,7 @@ impl<'db> InferCtxt<'db> { let mut query_state = OriginalQueryValues::default(); Canonicalizer::canonicalize( value, - Some(self), + self, self.interner, &CanonicalizeUserTypeAnnotation, &mut query_state, @@ -165,7 +179,7 @@ impl CanonicalizeMode for CanonicalizeQueryResponse { canonicalizer: &mut Canonicalizer<'_, 'db>, mut r: Region<'db>, ) -> Region<'db> { - let infcx = canonicalizer.infcx.unwrap(); + let infcx = canonicalizer.infcx; if let RegionKind::ReVar(vid) = r.kind() { r = infcx @@ -180,12 +194,14 @@ impl CanonicalizeMode for CanonicalizeQueryResponse { }; match r.kind() { - RegionKind::ReLateParam(_) | RegionKind::ReErased | RegionKind::ReStatic | RegionKind::ReEarlyParam(..) | RegionKind::ReError(..) => r, + RegionKind::ReLateParam(_) + | RegionKind::ReErased + | RegionKind::ReStatic + | RegionKind::ReEarlyParam(..) + | RegionKind::ReError(..) => r, - RegionKind::RePlaceholder(placeholder) => canonicalizer.canonical_var_for_region( - CanonicalVarKind::PlaceholderRegion(placeholder), - r, - ), + RegionKind::RePlaceholder(placeholder) => canonicalizer + .canonical_var_for_region(CanonicalVarKind::PlaceholderRegion(placeholder), r), RegionKind::ReVar(vid) => { let universe = infcx @@ -194,10 +210,7 @@ impl CanonicalizeMode for CanonicalizeQueryResponse { .unwrap_region_constraints() .probe_value(vid) .unwrap_err(); - canonicalizer.canonical_var_for_region( - CanonicalVarKind::Region(universe), - r, - ) + canonicalizer.canonical_var_for_region(CanonicalVarKind::Region(universe), r) } _ => { @@ -240,7 +253,7 @@ impl CanonicalizeMode for CanonicalizeUserTypeAnnotation { RegionKind::ReVar(_) => canonicalizer.canonical_var_for_region_in_root_universe(r), RegionKind::RePlaceholder(..) | RegionKind::ReBound(..) => { // We only expect region names that the user can type. - panic!("unexpected region in query response: `{:?}`", r) + panic!("unexpected region in query response: `{r:?}`") } } } @@ -296,7 +309,7 @@ impl CanonicalizeMode for CanonicalizeFreeRegionsOtherThanStatic { struct Canonicalizer<'cx, 'db> { /// Set to `None` to disable the resolution of inference variables. - infcx: Option<&'cx InferCtxt<'db>>, + infcx: &'cx InferCtxt<'db>, tcx: DbInterner<'db>, variables: SmallVec<[CanonicalVarKind<'db>; 8]>, query_state: &'cx mut OriginalQueryValues<'db>, @@ -350,14 +363,14 @@ impl<'cx, 'db> TypeFolder> for Canonicalizer<'cx, 'db> { // We need to canonicalize the *root* of our ty var. // This is so that our canonical response correctly reflects // any equated inference vars correctly! - let root_vid = self.infcx.unwrap().root_var(vid); + let root_vid = self.infcx.root_var(vid); if root_vid != vid { t = Ty::new_var(self.tcx, root_vid); vid = root_vid; } debug!("canonical: type var found with vid {:?}", vid); - match self.infcx.unwrap().probe_ty_var(vid) { + match self.infcx.probe_ty_var(vid) { // `t` could be a float / int variable; canonicalize that instead. Ok(t) => { debug!("(resolved to {:?})", t); @@ -380,29 +393,25 @@ impl<'cx, 'db> TypeFolder> for Canonicalizer<'cx, 'db> { } TyKind::Infer(IntVar(vid)) => { - let nt = self.infcx.unwrap().opportunistic_resolve_int_var(vid); + let nt = self.infcx.opportunistic_resolve_int_var(vid); if nt != t { - return self.fold_ty(nt); + self.fold_ty(nt) } else { - self.canonicalize_ty_var( - CanonicalVarKind::Ty(CanonicalTyVarKind::Int), - t, - ) + self.canonicalize_ty_var(CanonicalVarKind::Ty(CanonicalTyVarKind::Int), t) } } TyKind::Infer(FloatVar(vid)) => { - let nt = self.infcx.unwrap().opportunistic_resolve_float_var(vid); + let nt = self.infcx.opportunistic_resolve_float_var(vid); if nt != t { - return self.fold_ty(nt); + self.fold_ty(nt) } else { - self.canonicalize_ty_var( - CanonicalVarKind::Ty(CanonicalTyVarKind::Float), - t, - ) + self.canonicalize_ty_var(CanonicalVarKind::Ty(CanonicalTyVarKind::Float), t) } } - TyKind::Infer(InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_)) => { + TyKind::Infer( + InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_), + ) => { panic!("encountered a fresh type during canonicalization") } @@ -410,10 +419,7 @@ impl<'cx, 'db> TypeFolder> for Canonicalizer<'cx, 'db> { if !self.canonicalize_mode.preserve_universes() { placeholder.universe = UniverseIndex::ROOT; } - self.canonicalize_ty_var( - CanonicalVarKind::PlaceholderTy(placeholder), - t, - ) + self.canonicalize_ty_var(CanonicalVarKind::PlaceholderTy(placeholder), t) } TyKind::Bound(debruijn, _) => { @@ -465,14 +471,14 @@ impl<'cx, 'db> TypeFolder> for Canonicalizer<'cx, 'db> { // We need to canonicalize the *root* of our const var. // This is so that our canonical response correctly reflects // any equated inference vars correctly! - let root_vid = self.infcx.unwrap().root_const_var(vid); + let root_vid = self.infcx.root_const_var(vid); if root_vid != vid { ct = Const::new_var(self.tcx, root_vid); vid = root_vid; } debug!("canonical: const var found with vid {:?}", vid); - match self.infcx.unwrap().probe_const_var(vid) { + match self.infcx.probe_const_var(vid) { Ok(c) => { debug!("(resolved to {:?})", c); return self.fold_const(c); @@ -485,10 +491,7 @@ impl<'cx, 'db> TypeFolder> for Canonicalizer<'cx, 'db> { // FIXME: perf problem described in #55921. ui = UniverseIndex::ROOT; } - return self.canonicalize_const_var( - CanonicalVarKind::Const(ui), - ct, - ); + return self.canonicalize_const_var(CanonicalVarKind::Const(ui), ct); } } } @@ -503,10 +506,8 @@ impl<'cx, 'db> TypeFolder> for Canonicalizer<'cx, 'db> { } } ConstKind::Placeholder(placeholder) => { - return self.canonicalize_const_var( - CanonicalVarKind::PlaceholderConst(placeholder), - ct, - ); + return self + .canonicalize_const_var(CanonicalVarKind::PlaceholderConst(placeholder), ct); } _ => {} } @@ -524,7 +525,7 @@ impl<'cx, 'db> Canonicalizer<'cx, 'db> { /// `canonicalize_query` and `canonicalize_response`. fn canonicalize( value: V, - infcx: Option<&InferCtxt<'db>>, + infcx: &InferCtxt<'db>, tcx: DbInterner<'db>, canonicalize_region_mode: &dyn CanonicalizeMode, query_state: &mut OriginalQueryValues<'db>, @@ -551,7 +552,7 @@ impl<'cx, 'db> Canonicalizer<'cx, 'db> { fn canonicalize_with_base( base: Canonical<'db, U>, value: V, - infcx: Option<&InferCtxt<'db>>, + infcx: &InferCtxt<'db>, tcx: DbInterner<'db>, canonicalize_region_mode: &dyn CanonicalizeMode, query_state: &mut OriginalQueryValues<'db>, @@ -596,7 +597,8 @@ impl<'cx, 'db> Canonicalizer<'cx, 'db> { // anymore. debug_assert!(!out_value.has_infer() && !out_value.has_placeholders()); - let canonical_variables = CanonicalVars::new_from_iter(tcx, canonicalizer.universe_canonicalized_variables()); + let canonical_variables = + CanonicalVars::new_from_iter(tcx, canonicalizer.universe_canonicalized_variables()); let max_universe = canonical_variables .iter() @@ -690,15 +692,11 @@ impl<'cx, 'db> Canonicalizer<'cx, 'db> { self.variables .iter() .map(|v| match *v { - CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) => { - return *v; - } + CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) => *v, CanonicalVarKind::Ty(CanonicalTyVarKind::General(u)) => { CanonicalVarKind::Ty(CanonicalTyVarKind::General(reverse_universe_map[&u])) } - CanonicalVarKind::Region(u) => { - CanonicalVarKind::Region(reverse_universe_map[&u]) - } + CanonicalVarKind::Region(u) => CanonicalVarKind::Region(reverse_universe_map[&u]), CanonicalVarKind::Const(u) => CanonicalVarKind::Const(reverse_universe_map[&u]), CanonicalVarKind::PlaceholderTy(placeholder) => { CanonicalVarKind::PlaceholderTy(Placeholder { @@ -735,14 +733,8 @@ impl<'cx, 'db> Canonicalizer<'cx, 'db> { /// /// (This works because unification never fails -- and hence trait /// selection is never affected -- due to a universe mismatch.) - fn canonical_var_for_region_in_root_universe( - &mut self, - r: Region<'db>, - ) -> Region<'db> { - self.canonical_var_for_region( - CanonicalVarKind::Region(UniverseIndex::ROOT), - r, - ) + fn canonical_var_for_region_in_root_universe(&mut self, r: Region<'db>) -> Region<'db> { + self.canonical_var_for_region(CanonicalVarKind::Region(UniverseIndex::ROOT), r) } /// Creates a canonical variable (with the given `info`) @@ -762,9 +754,13 @@ impl<'cx, 'db> Canonicalizer<'cx, 'db> { /// *that*. Otherwise, create a new canonical variable for /// `ty_var`. fn canonicalize_ty_var(&mut self, info: CanonicalVarKind<'db>, ty_var: Ty<'db>) -> Ty<'db> { - debug_assert!(!self.infcx.is_some_and(|infcx| ty_var != infcx.shallow_resolve(ty_var))); + debug_assert_eq!(ty_var, self.infcx.shallow_resolve(ty_var)); let var = self.canonical_var(info, ty_var.into()); - Ty::new_bound(self.tcx, self.binder_index, BoundTy { kind: crate::next_solver::BoundTyKind::Anon, var }) + Ty::new_bound( + self.tcx, + self.binder_index, + BoundTy { kind: crate::next_solver::BoundTyKind::Anon, var }, + ) } /// Given a type variable `const_var` of the given kind, first check @@ -776,10 +772,8 @@ impl<'cx, 'db> Canonicalizer<'cx, 'db> { info: CanonicalVarKind<'db>, const_var: Const<'db>, ) -> Const<'db> { - debug_assert!( - !self.infcx.is_some_and(|infcx| const_var != infcx.shallow_resolve_const(const_var)) - ); + debug_assert_eq!(const_var, self.infcx.shallow_resolve_const(const_var)); let var = self.canonical_var(info, const_var.into()); - Const::new_bound(self.tcx, self.binder_index, var) + Const::new_bound(self.tcx, self.binder_index, BoundConst { var }) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs index 894c91e0f49d..2630f2a8cc4e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs @@ -503,7 +503,10 @@ impl<'db> InferCtxt<'db> { } pub fn next_ty_vid(&self) -> TyVid { - self.inner.borrow_mut().type_variables().new_var(self.universe(), TypeVariableOrigin { param_def_id: None }) + self.inner + .borrow_mut() + .type_variables() + .new_var(self.universe(), TypeVariableOrigin { param_def_id: None }) } pub fn next_ty_var_with_origin(&self, origin: TypeVariableOrigin) -> Ty<'db> { @@ -526,11 +529,13 @@ impl<'db> InferCtxt<'db> { } pub fn next_const_vid(&self) -> ConstVid { - self - .inner + self.inner .borrow_mut() .const_unification_table() - .new_key(ConstVariableValue::Unknown { origin: ConstVariableOrigin { param_def_id: None }, universe: self.universe() }) + .new_key(ConstVariableValue::Unknown { + origin: ConstVariableOrigin { param_def_id: None }, + universe: self.universe(), + }) .vid } @@ -566,9 +571,7 @@ impl<'db> InferCtxt<'db> { } pub fn next_float_var(&self) -> Ty<'db> { - let next_float_var_id = - self.inner.borrow_mut().float_unification_table().new_key(FloatVarValue::Unknown); - Ty::new_float_var(self.interner, next_float_var_id) + Ty::new_float_var(self.interner, self.next_float_vid()) } pub fn next_float_vid(&self) -> FloatVid { @@ -815,9 +818,7 @@ impl<'db> InferCtxt<'db> { match value { IntVarValue::IntType(ty) => Some(Ty::new_int(self.interner, ty)), IntVarValue::UintType(ty) => Some(Ty::new_uint(self.interner, ty)), - IntVarValue::Unknown => { - None - } + IntVarValue::Unknown => None, } } @@ -839,9 +840,7 @@ impl<'db> InferCtxt<'db> { let value = inner.float_unification_table().probe_value(vid); match value { FloatVarValue::Known(ty) => Some(Ty::new_float(self.interner, ty)), - FloatVarValue::Unknown => { - None - } + FloatVarValue::Unknown => None, } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index 79c402ff29c3..f66b8dace30a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -141,7 +141,10 @@ impl<'db> rustc_type_ir::TypeFolder> for BinderToEarlyBinder<'db pub trait ChalkToNextSolver<'db, Out> { fn to_nextsolver(&self, interner: DbInterner<'db>) -> Out; - fn from_nextsolver(out: Out, interner: DbInterner<'db>) -> Self; +} + +pub trait NextSolverToChalk<'db, Out> { + fn to_chalk(self, interner: DbInterner<'db>) -> Out; } impl<'db> ChalkToNextSolver<'db, Ty<'db>> for chalk_ir::Ty { @@ -428,342 +431,11 @@ impl<'db> ChalkToNextSolver<'db, Ty<'db>> for chalk_ir::Ty { }, ) } +} - fn from_nextsolver(out: Ty<'db>, interner: DbInterner<'db>) -> Self { - use crate::{Scalar, TyKind}; - use chalk_ir::{FloatTy, IntTy, UintTy}; - match out.kind() { - rustc_type_ir::TyKind::Bool => TyKind::Scalar(Scalar::Bool), - rustc_type_ir::TyKind::Char => TyKind::Scalar(Scalar::Char), - rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I8) => { - TyKind::Scalar(Scalar::Int(IntTy::I8)) - } - rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I16) => { - TyKind::Scalar(Scalar::Int(IntTy::I16)) - } - rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I32) => { - TyKind::Scalar(Scalar::Int(IntTy::I32)) - } - rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I64) => { - TyKind::Scalar(Scalar::Int(IntTy::I64)) - } - rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I128) => { - TyKind::Scalar(Scalar::Int(IntTy::I128)) - } - rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::Isize) => { - TyKind::Scalar(Scalar::Int(IntTy::Isize)) - } - rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U8) => { - TyKind::Scalar(Scalar::Uint(UintTy::U8)) - } - rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U16) => { - TyKind::Scalar(Scalar::Uint(UintTy::U16)) - } - rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U32) => { - TyKind::Scalar(Scalar::Uint(UintTy::U32)) - } - rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U64) => { - TyKind::Scalar(Scalar::Uint(UintTy::U64)) - } - rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U128) => { - TyKind::Scalar(Scalar::Uint(UintTy::U128)) - } - rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::Usize) => { - TyKind::Scalar(Scalar::Uint(UintTy::Usize)) - } - rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F16) => { - TyKind::Scalar(Scalar::Float(FloatTy::F16)) - } - rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F32) => { - TyKind::Scalar(Scalar::Float(FloatTy::F32)) - } - rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F64) => { - TyKind::Scalar(Scalar::Float(FloatTy::F64)) - } - rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F128) => { - TyKind::Scalar(Scalar::Float(FloatTy::F128)) - } - rustc_type_ir::TyKind::Str => TyKind::Str, - rustc_type_ir::TyKind::Error(_) => TyKind::Error, - rustc_type_ir::TyKind::Never => TyKind::Never, - - rustc_type_ir::TyKind::Adt(def, args) => { - let adt_id = def.inner().id; - let subst = ChalkToNextSolver::from_nextsolver(args, interner); - TyKind::Adt(chalk_ir::AdtId(adt_id), subst) - } - - rustc_type_ir::TyKind::Infer(infer_ty) => { - let (var, kind) = match infer_ty { - rustc_type_ir::InferTy::TyVar(var) => { - (InferenceVar::from(var.as_u32()), TyVariableKind::General) - } - rustc_type_ir::InferTy::IntVar(var) => { - (InferenceVar::from(var.as_u32()), TyVariableKind::Integer) - } - rustc_type_ir::InferTy::FloatVar(var) => { - (InferenceVar::from(var.as_u32()), TyVariableKind::Float) - } - _ => todo!(), - }; - TyKind::InferenceVar(var, kind) - } - - rustc_type_ir::TyKind::Ref(r, ty, mutability) => { - let mutability = match mutability { - rustc_ast_ir::Mutability::Mut => chalk_ir::Mutability::Mut, - rustc_ast_ir::Mutability::Not => chalk_ir::Mutability::Not, - }; - let r = ChalkToNextSolver::from_nextsolver(r, interner); - let ty = ChalkToNextSolver::from_nextsolver(ty, interner); - TyKind::Ref(mutability, r, ty) - } - - rustc_type_ir::TyKind::Tuple(tys) => { - let size = tys.len(); - let subst = Substitution::from_iter( - Interner, - tys.iter().map(|ty| { - chalk_ir::GenericArgData::Ty(ChalkToNextSolver::from_nextsolver( - ty, interner, - )) - .intern(Interner) - }), - ); - TyKind::Tuple(size, subst) - } - - rustc_type_ir::TyKind::Array(ty, const_) => { - let ty = ChalkToNextSolver::from_nextsolver(ty, interner); - let const_ = ChalkToNextSolver::from_nextsolver(const_, interner); - TyKind::Array(ty, const_) - } - - rustc_type_ir::TyKind::Alias(alias_ty_kind, alias_ty) => match alias_ty_kind { - rustc_type_ir::AliasTyKind::Projection => { - let assoc_ty_id = match alias_ty.def_id { - SolverDefId::TypeAliasId(id) => id, - _ => unreachable!(), - }; - let associated_ty_id = to_assoc_type_id(assoc_ty_id); - let substitution = ChalkToNextSolver::from_nextsolver(alias_ty.args, interner); - TyKind::AssociatedType(associated_ty_id, substitution) - } - rustc_type_ir::AliasTyKind::Opaque => { - let opaque_ty_id = match alias_ty.def_id { - SolverDefId::InternedOpaqueTyId(id) => id, - _ => unreachable!(), - }; - let substitution = ChalkToNextSolver::from_nextsolver(alias_ty.args, interner); - TyKind::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy { - opaque_ty_id: opaque_ty_id.into(), - substitution, - })) - } - rustc_type_ir::AliasTyKind::Inherent => todo!(), - rustc_type_ir::AliasTyKind::Free => todo!(), - }, - - rustc_type_ir::TyKind::Placeholder(placeholder) => { - let ui = chalk_ir::UniverseIndex { counter: placeholder.universe.as_usize() }; - let placeholder_index = - chalk_ir::PlaceholderIndex { idx: placeholder.bound.var.as_usize(), ui }; - TyKind::Placeholder(placeholder_index) - } - - rustc_type_ir::TyKind::Bound(debruijn_index, ty) => { - TyKind::BoundVar(chalk_ir::BoundVar { - debruijn: chalk_ir::DebruijnIndex::new(debruijn_index.as_u32()), - index: ty.var.as_usize(), - }) - } - - rustc_type_ir::TyKind::FnPtr(bound_sig, fn_header) => { - let num_binders = bound_sig.bound_vars().len(); - let sig = chalk_ir::FnSig { - abi: fn_header.abi, - safety: match fn_header.safety { - crate::next_solver::abi::Safety::Safe => chalk_ir::Safety::Safe, - crate::next_solver::abi::Safety::Unsafe => chalk_ir::Safety::Unsafe, - }, - variadic: fn_header.c_variadic, - }; - let args = GenericArgs::new_from_iter( - interner, - bound_sig.skip_binder().inputs_and_output.iter().map(|a| a.into()), - ); - let substitution = ChalkToNextSolver::from_nextsolver(args, interner); - let substitution = chalk_ir::FnSubst(substitution); - let fnptr = chalk_ir::FnPointer { num_binders, sig, substitution }; - TyKind::Function(fnptr) - } - - rustc_type_ir::TyKind::Dynamic(preds, region, dyn_kind) => { - assert!(matches!(dyn_kind, rustc_type_ir::DynKind::Dyn)); - let self_ty = Ty::new_bound( - interner, - DebruijnIndex::from_u32(1), - BoundTy { kind: BoundTyKind::Anon, var: BoundVar::from_u32(0) }, - ); - let bounds = chalk_ir::QuantifiedWhereClauses::from_iter( - Interner, - preds.iter().map(|p| { - let binders = chalk_ir::VariableKinds::from_iter( - Interner, - p.bound_vars().iter().map(|b| match b { - BoundVarKind::Ty(kind) => { - chalk_ir::VariableKind::Ty(TyVariableKind::General) - } - BoundVarKind::Region(kind) => chalk_ir::VariableKind::Lifetime, - BoundVarKind::Const => chalk_ir::VariableKind::Const( - crate::TyKind::Error.intern(Interner), - ), - }), - ); - let where_clause = match p.skip_binder() { - rustc_type_ir::ExistentialPredicate::Trait(trait_ref) => { - let trait_ref = TraitRef::new( - interner, - trait_ref.def_id, - [self_ty.clone().into()] - .into_iter() - .chain(trait_ref.args.iter()), - ); - let trait_id = match trait_ref.def_id { - SolverDefId::TraitId(id) => to_chalk_trait_id(id), - _ => unreachable!(), - }; - let substitution = - ChalkToNextSolver::from_nextsolver(trait_ref.args, interner); - let trait_ref = chalk_ir::TraitRef { trait_id, substitution }; - chalk_ir::WhereClause::Implemented(trait_ref) - } - rustc_type_ir::ExistentialPredicate::AutoTrait(trait_) => { - let trait_id = match trait_ { - SolverDefId::TraitId(id) => to_chalk_trait_id(id), - _ => unreachable!(), - }; - let substitution = chalk_ir::Substitution::empty(Interner); - let trait_ref = chalk_ir::TraitRef { trait_id, substitution }; - chalk_ir::WhereClause::Implemented(trait_ref) - } - rustc_type_ir::ExistentialPredicate::Projection( - existential_projection, - ) => { - let projection = ProjectionPredicate { - projection_term: AliasTerm::new( - interner, - existential_projection.def_id, - [self_ty.clone().into()] - .iter() - .chain(existential_projection.args.clone().iter()), - ), - term: existential_projection.term.clone(), - }; - let associated_ty_id = match projection.projection_term.def_id { - SolverDefId::TypeAliasId(id) => to_assoc_type_id(id), - _ => unreachable!(), - }; - let substitution = ChalkToNextSolver::from_nextsolver( - projection.projection_term.args, - interner, - ); - let alias = chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { - associated_ty_id, - substitution, - }); - let ty = match projection.term { - Term::Ty(ty) => ty, - _ => unreachable!(), - }; - let ty = ChalkToNextSolver::from_nextsolver(ty, interner); - let alias_eq = chalk_ir::AliasEq { alias, ty }; - chalk_ir::WhereClause::AliasEq(alias_eq) - } - }; - chalk_ir::Binders::new(binders, where_clause) - }), - ); - let binders = chalk_ir::VariableKinds::from1( - Interner, - chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General), - ); - let bounds = chalk_ir::Binders::new(binders, bounds); - let dyn_ty = chalk_ir::DynTy { - bounds, - lifetime: ChalkToNextSolver::from_nextsolver(region, interner), - }; - TyKind::Dyn(dyn_ty) - } - - rustc_type_ir::TyKind::Slice(ty) => { - let ty = ChalkToNextSolver::from_nextsolver(ty, interner); - TyKind::Slice(ty) - } - - rustc_type_ir::TyKind::Foreign(def_id) => { - let id = match def_id { - SolverDefId::TypeAliasId(id) => to_foreign_def_id(id), - _ => unreachable!(), - }; - TyKind::Foreign(id) - } - rustc_type_ir::TyKind::Pat(_, _) => todo!(), - rustc_type_ir::TyKind::RawPtr(ty, mutability) => { - let mutability = match mutability { - rustc_ast_ir::Mutability::Mut => chalk_ir::Mutability::Mut, - rustc_ast_ir::Mutability::Not => chalk_ir::Mutability::Not, - }; - let ty = ChalkToNextSolver::from_nextsolver(ty, interner); - TyKind::Raw(mutability, ty) - } - rustc_type_ir::TyKind::FnDef(def_id, args) => { - let subst = ChalkToNextSolver::from_nextsolver(args, interner); - match def_id { - SolverDefId::FunctionId(id) => { - TyKind::FnDef(CallableDefId::FunctionId(id).to_chalk(interner.db()), subst) - } - SolverDefId::Ctor(Ctor::Enum(e)) => TyKind::FnDef( - CallableDefId::EnumVariantId(e).to_chalk(interner.db()), - subst, - ), - SolverDefId::Ctor(Ctor::Struct(s)) => { - TyKind::FnDef(CallableDefId::StructId(s).to_chalk(interner.db()), subst) - } - _ => unreachable!("Unexpected def id {:?}", def_id), - } - } - - rustc_type_ir::TyKind::Closure(def_id, args) => { - let id = match def_id { - SolverDefId::InternedClosureId(id) => id, - _ => unreachable!(), - }; - let subst = ChalkToNextSolver::from_nextsolver(args, interner); - TyKind::Closure(id.into(), subst) - } - rustc_type_ir::TyKind::CoroutineClosure(_, _) => todo!(), - rustc_type_ir::TyKind::Coroutine(def_id, args) => { - let id = match def_id { - SolverDefId::InternedCoroutineId(id) => id, - _ => unreachable!(), - }; - let subst = ChalkToNextSolver::from_nextsolver(args, interner); - TyKind::Coroutine(id.into(), subst) - } - rustc_type_ir::TyKind::CoroutineWitness(def_id, args) => { - let id = match def_id { - SolverDefId::InternedCoroutineId(id) => id, - _ => unreachable!(), - }; - let subst = ChalkToNextSolver::from_nextsolver(args, interner); - TyKind::CoroutineWitness(id.into(), subst) - } - - rustc_type_ir::TyKind::Param(_) => todo!(), - rustc_type_ir::TyKind::UnsafeBinder(_) => todo!(), - } - .intern(Interner) +impl<'db> NextSolverToChalk<'db, chalk_ir::Ty> for Ty<'db> { + fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::Ty { + convert_ty_for_result(interner, self) } } @@ -799,39 +471,11 @@ impl<'db> ChalkToNextSolver<'db, Region<'db>> for chalk_ir::Lifetime { }, ) } +} - fn from_nextsolver(out: Region<'db>, interner: DbInterner<'db>) -> Self { - match out.kind() { - rustc_type_ir::RegionKind::ReEarlyParam(early) => todo!(), - rustc_type_ir::RegionKind::ReBound(db, bound) => chalk_ir::Lifetime::new( - Interner, - chalk_ir::LifetimeData::BoundVar(chalk_ir::BoundVar::new( - chalk_ir::DebruijnIndex::new(db.as_u32()), - bound.var.as_usize(), - )), - ), - rustc_type_ir::RegionKind::ReLateParam(_) => todo!(), - rustc_type_ir::RegionKind::ReStatic => { - chalk_ir::Lifetime::new(Interner, chalk_ir::LifetimeData::Static) - } - rustc_type_ir::RegionKind::ReVar(vid) => chalk_ir::Lifetime::new( - Interner, - chalk_ir::LifetimeData::InferenceVar(chalk_ir::InferenceVar::from(vid.as_u32())), - ), - rustc_type_ir::RegionKind::RePlaceholder(placeholder) => chalk_ir::Lifetime::new( - Interner, - chalk_ir::LifetimeData::Placeholder(chalk_ir::PlaceholderIndex { - idx: placeholder.bound.var.as_usize(), - ui: chalk_ir::UniverseIndex { counter: placeholder.universe.as_usize() }, - }), - ), - rustc_type_ir::RegionKind::ReErased => { - chalk_ir::Lifetime::new(Interner, chalk_ir::LifetimeData::Erased) - } - rustc_type_ir::RegionKind::ReError(_) => { - chalk_ir::Lifetime::new(Interner, chalk_ir::LifetimeData::Error) - } - } +impl<'db> NextSolverToChalk<'db, chalk_ir::Lifetime> for Region<'db> { + fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::Lifetime { + convert_region_for_result(interner, self) } } @@ -877,61 +521,11 @@ impl<'db> ChalkToNextSolver<'db, Const<'db>> for chalk_ir::Const { }, ) } +} - fn from_nextsolver(out: Const<'db>, interner: DbInterner<'db>) -> Self { - let value: chalk_ir::ConstValue = match out.kind() { - rustc_type_ir::ConstKind::Param(_) => unimplemented!(), - rustc_type_ir::ConstKind::Infer(rustc_type_ir::InferConst::Var(var)) => { - chalk_ir::ConstValue::InferenceVar(chalk_ir::InferenceVar::from(var.as_u32())) - } - rustc_type_ir::ConstKind::Infer(rustc_type_ir::InferConst::Fresh(fresh)) => { - panic!("Vars should not be freshened.") - } - rustc_type_ir::ConstKind::Bound(debruijn_index, var) => { - chalk_ir::ConstValue::BoundVar(chalk_ir::BoundVar::new( - chalk_ir::DebruijnIndex::new(debruijn_index.as_u32()), - var.index(), - )) - } - rustc_type_ir::ConstKind::Placeholder(placeholder_const) => { - chalk_ir::ConstValue::Placeholder(chalk_ir::PlaceholderIndex { - ui: chalk_ir::UniverseIndex { counter: placeholder_const.universe.as_usize() }, - idx: placeholder_const.bound.as_usize(), - }) - } - rustc_type_ir::ConstKind::Unevaluated(unevaluated_const) => { - let id = match unevaluated_const.def { - SolverDefId::ConstId(id) => GeneralConstId::ConstId(id), - SolverDefId::StaticId(id) => GeneralConstId::StaticId(id), - _ => unreachable!(), - }; - let subst = ChalkToNextSolver::from_nextsolver(unevaluated_const.args, interner); - chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { - interned: ConstScalar::UnevaluatedConst(id, subst), - }) - } - rustc_type_ir::ConstKind::Value(value_const) => { - let bytes = value_const.value.inner(); - let value = chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { - // SAFETY: will never use this without a db - interned: ConstScalar::Bytes(bytes.0.clone(), unsafe { - std::mem::transmute::, MemoryMap<'static>>(bytes.1.clone()) - }), - }); - return chalk_ir::ConstData { - ty: ChalkToNextSolver::from_nextsolver(value_const.ty, interner), - value, - } - .intern(Interner); - } - rustc_type_ir::ConstKind::Error(_) => { - chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { - interned: ConstScalar::Unknown, - }) - } - rustc_type_ir::ConstKind::Expr(_) => unimplemented!(), - }; - chalk_ir::ConstData { ty: crate::TyKind::Error.intern(Interner), value }.intern(Interner) +impl<'db> NextSolverToChalk<'db, chalk_ir::Const> for Const<'db> { + fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::Const { + convert_const_for_result(interner, self) } } @@ -946,13 +540,6 @@ impl<'db> ChalkToNextSolver<'db, rustc_type_ir::FnSigTys>> ), } } - - fn from_nextsolver( - out: rustc_type_ir::FnSigTys>, - interner: DbInterner<'db>, - ) -> Self { - todo!() - } } impl< @@ -971,13 +558,6 @@ impl< binders.to_nextsolver(interner), ) } - - fn from_nextsolver( - out: rustc_type_ir::Binder, U>, - interner: DbInterner<'db>, - ) -> Self { - todo!() - } } impl<'db> ChalkToNextSolver<'db, BoundVarKinds> for chalk_ir::VariableKinds { @@ -987,10 +567,6 @@ impl<'db> ChalkToNextSolver<'db, BoundVarKinds> for chalk_ir::VariableKinds) -> Self { - todo!() - } } impl<'db> ChalkToNextSolver<'db, BoundVarKind> for chalk_ir::VariableKind { @@ -1001,10 +577,6 @@ impl<'db> ChalkToNextSolver<'db, BoundVarKind> for chalk_ir::VariableKind BoundVarKind::Const, } } - - fn from_nextsolver(out: BoundVarKind, interner: DbInterner<'db>) -> Self { - todo!() - } } impl<'db> ChalkToNextSolver<'db, GenericArg<'db>> for chalk_ir::GenericArg { @@ -1015,11 +587,8 @@ impl<'db> ChalkToNextSolver<'db, GenericArg<'db>> for chalk_ir::GenericArg const_.to_nextsolver(interner).into(), } } - - fn from_nextsolver(out: GenericArg<'db>, interner: DbInterner<'db>) -> Self { - todo!() - } } + impl<'db> ChalkToNextSolver<'db, GenericArgs<'db>> for chalk_ir::Substitution { fn to_nextsolver(&self, interner: DbInterner<'db>) -> GenericArgs<'db> { GenericArgs::new_from_iter( @@ -1027,30 +596,11 @@ impl<'db> ChalkToNextSolver<'db, GenericArgs<'db>> for chalk_ir::Substitution GenericArg<'db> { arg.to_nextsolver(interner) }), ) } +} - fn from_nextsolver(out: GenericArgs<'db>, interner: DbInterner<'db>) -> Self { - let mut substs = Vec::with_capacity(out.len()); - for arg in out.iter() { - match arg.clone().kind() { - rustc_type_ir::GenericArgKind::Type(ty) => { - let ty = ChalkToNextSolver::from_nextsolver(ty, interner); - substs.push(chalk_ir::GenericArgData::Ty(ty).intern(Interner)); - } - rustc_type_ir::GenericArgKind::Lifetime(region) => { - let lifetime = ChalkToNextSolver::from_nextsolver(region, interner); - substs.push(chalk_ir::GenericArgData::Lifetime(lifetime).intern(Interner)); - } - rustc_type_ir::GenericArgKind::Const(const_) => { - substs.push( - chalk_ir::GenericArgData::Const(ChalkToNextSolver::from_nextsolver( - const_, interner, - )) - .intern(Interner), - ); - } - } - } - Substitution::from_iter(Interner, substs) +impl<'db> NextSolverToChalk<'db, chalk_ir::Substitution> for GenericArgs<'db> { + fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::Substitution { + convert_args_for_result(interner, self.as_slice()) } } @@ -1067,29 +617,29 @@ impl<'db> ChalkToNextSolver<'db, Tys<'db>> for chalk_ir::Substitution }), ) } - - fn from_nextsolver(out: Tys<'db>, interner: DbInterner<'db>) -> Self { - todo!() - } } impl<'db> ChalkToNextSolver<'db, rustc_type_ir::DebruijnIndex> for chalk_ir::DebruijnIndex { - fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_type_ir::DebruijnIndex { + fn to_nextsolver(&self, _interner: DbInterner<'db>) -> rustc_type_ir::DebruijnIndex { rustc_type_ir::DebruijnIndex::from_u32(self.depth()) } +} - fn from_nextsolver(out: rustc_type_ir::DebruijnIndex, interner: DbInterner<'db>) -> Self { - todo!() +impl<'db> NextSolverToChalk<'db, chalk_ir::DebruijnIndex> for rustc_type_ir::DebruijnIndex { + fn to_chalk(self, _interner: DbInterner<'db>) -> chalk_ir::DebruijnIndex { + chalk_ir::DebruijnIndex::new(self.index() as u32) } } impl<'db> ChalkToNextSolver<'db, rustc_type_ir::UniverseIndex> for chalk_ir::UniverseIndex { - fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_type_ir::UniverseIndex { + fn to_nextsolver(&self, _interner: DbInterner<'db>) -> rustc_type_ir::UniverseIndex { rustc_type_ir::UniverseIndex::from_u32(self.counter as u32) } +} - fn from_nextsolver(out: rustc_type_ir::UniverseIndex, interner: DbInterner<'db>) -> Self { - todo!() +impl<'db> NextSolverToChalk<'db, chalk_ir::UniverseIndex> for rustc_type_ir::UniverseIndex { + fn to_chalk(self, _interner: DbInterner<'db>) -> chalk_ir::UniverseIndex { + chalk_ir::UniverseIndex { counter: self.index() } } } @@ -1109,10 +659,6 @@ impl<'db> ChalkToNextSolver<'db, rustc_type_ir::InferTy> } } } - - fn from_nextsolver(out: rustc_type_ir::InferTy, interner: DbInterner<'db>) -> Self { - todo!() - } } impl<'db> ChalkToNextSolver<'db, rustc_ast_ir::Mutability> for chalk_ir::Mutability { @@ -1122,10 +668,6 @@ impl<'db> ChalkToNextSolver<'db, rustc_ast_ir::Mutability> for chalk_ir::Mutabil chalk_ir::Mutability::Not => rustc_ast_ir::Mutability::Not, } } - - fn from_nextsolver(out: rustc_ast_ir::Mutability, interner: DbInterner<'db>) -> Self { - todo!() - } } impl<'db> ChalkToNextSolver<'db, rustc_type_ir::Variance> for crate::Variance { @@ -1137,10 +679,6 @@ impl<'db> ChalkToNextSolver<'db, rustc_type_ir::Variance> for crate::Variance { crate::Variance::Bivariant => rustc_type_ir::Variance::Bivariant, } } - - fn from_nextsolver(out: rustc_type_ir::Variance, interner: DbInterner<'db>) -> Self { - todo!() - } } impl<'db> ChalkToNextSolver<'db, rustc_type_ir::Variance> for chalk_ir::Variance { @@ -1151,10 +689,6 @@ impl<'db> ChalkToNextSolver<'db, rustc_type_ir::Variance> for chalk_ir::Variance chalk_ir::Variance::Contravariant => rustc_type_ir::Variance::Contravariant, } } - - fn from_nextsolver(out: rustc_type_ir::Variance, interner: DbInterner<'db>) -> Self { - todo!() - } } impl<'db> ChalkToNextSolver<'db, VariancesOf> for chalk_ir::Variances { @@ -1164,10 +698,6 @@ impl<'db> ChalkToNextSolver<'db, VariancesOf> for chalk_ir::Variances self.as_slice(Interner).iter().map(|v| v.to_nextsolver(interner)), ) } - - fn from_nextsolver(out: VariancesOf, interner: DbInterner<'db>) -> Self { - todo!() - } } impl<'db> ChalkToNextSolver<'db, Goal, Predicate<'db>>> @@ -1180,14 +710,18 @@ impl<'db> ChalkToNextSolver<'db, Goal, Predicate<'db>>> self.goal.to_nextsolver(interner), ) } +} - fn from_nextsolver( - out: Goal, Predicate<'db>>, +impl<'db> NextSolverToChalk<'db, chalk_ir::InEnvironment>> + for Goal, Predicate<'db>> +{ + fn to_chalk( + self, interner: DbInterner<'db>, - ) -> Self { + ) -> chalk_ir::InEnvironment> { chalk_ir::InEnvironment { - environment: ChalkToNextSolver::from_nextsolver(out.param_env, interner), - goal: ChalkToNextSolver::from_nextsolver(out.predicate, interner), + environment: self.param_env.to_chalk(interner), + goal: self.predicate.to_chalk(interner), } } } @@ -1224,11 +758,15 @@ impl<'db, T: HasInterner + ChalkToNextSolver<'db, U>, U> variables, } } +} - fn from_nextsolver(out: Canonical<'db, U>, interner: DbInterner<'db>) -> Self { +impl<'db, T: NextSolverToChalk<'db, U>, U: HasInterner> + NextSolverToChalk<'db, chalk_ir::Canonical> for Canonical<'db, T> +{ + fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::Canonical { let binders = chalk_ir::CanonicalVarKinds::from_iter( Interner, - out.variables.iter().map(|v| match v { + self.variables.iter().map(|v| match v { rustc_type_ir::CanonicalVarKind::Ty( rustc_type_ir::CanonicalTyVarKind::General(ui), ) => chalk_ir::CanonicalVarKind::new( @@ -1247,20 +785,20 @@ impl<'db, T: HasInterner + ChalkToNextSolver<'db, U>, U> chalk_ir::UniverseIndex::root(), ) } - rustc_type_ir::CanonicalVarKind::PlaceholderTy(_) => todo!(), rustc_type_ir::CanonicalVarKind::Region(ui) => chalk_ir::CanonicalVarKind::new( chalk_ir::VariableKind::Lifetime, chalk_ir::UniverseIndex { counter: ui.as_usize() }, ), - rustc_type_ir::CanonicalVarKind::PlaceholderRegion(_) => todo!(), rustc_type_ir::CanonicalVarKind::Const(ui) => chalk_ir::CanonicalVarKind::new( chalk_ir::VariableKind::Const(chalk_ir::TyKind::Error.intern(Interner)), chalk_ir::UniverseIndex { counter: ui.as_usize() }, ), - rustc_type_ir::CanonicalVarKind::PlaceholderConst(_) => todo!(), + rustc_type_ir::CanonicalVarKind::PlaceholderTy(_) => unimplemented!(), + rustc_type_ir::CanonicalVarKind::PlaceholderRegion(_) => unimplemented!(), + rustc_type_ir::CanonicalVarKind::PlaceholderConst(_) => unimplemented!(), }), ); - let value = ChalkToNextSolver::from_nextsolver(out.value, interner); + let value = self.value.to_chalk(interner); chalk_ir::Canonical { binders, value } } } @@ -1281,9 +819,16 @@ impl<'db> ChalkToNextSolver<'db, Predicate<'db>> for chalk_ir::Goal { chalk_ir::GoalData::All(goals) => panic!("Should not be constructed."), chalk_ir::GoalData::Not(goal) => panic!("Should not be constructed."), chalk_ir::GoalData::EqGoal(eq_goal) => { + let arg_to_term = |g: &chalk_ir::GenericArg| match g.data(Interner) { + chalk_ir::GenericArgData::Ty(ty) => Term::Ty(ty.to_nextsolver(interner)), + chalk_ir::GenericArgData::Const(const_) => { + Term::Const(const_.to_nextsolver(interner)) + } + chalk_ir::GenericArgData::Lifetime(lifetime) => unreachable!(), + }; let pred_kind = PredicateKind::AliasRelate( - Term::Ty(eq_goal.a.assert_ty_ref(Interner).clone().to_nextsolver(interner)), - Term::Ty(eq_goal.b.assert_ty_ref(Interner).clone().to_nextsolver(interner)), + arg_to_term(&eq_goal.a), + arg_to_term(&eq_goal.b), rustc_type_ir::AliasRelationDirection::Equate, ); let pred_kind = @@ -1314,123 +859,11 @@ impl<'db> ChalkToNextSolver<'db, Predicate<'db>> for chalk_ir::Goal { chalk_ir::GoalData::CannotProve => panic!("Should not be constructed."), } } +} - fn from_nextsolver(out: Predicate<'db>, interner: DbInterner<'db>) -> Self { - chalk_ir::Goal::new( - Interner, - match out.kind().skip_binder() { - rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::Trait( - trait_pred, - )) => { - let trait_ref = - ChalkToNextSolver::from_nextsolver(trait_pred.trait_ref, interner); - let where_clause = chalk_ir::WhereClause::Implemented(trait_ref); - chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(where_clause)) - } - rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::Projection( - proj_predicate, - )) => { - let associated_ty_id = match proj_predicate.def_id() { - SolverDefId::TypeAliasId(id) => to_assoc_type_id(id), - _ => unreachable!(), - }; - let substitution = ChalkToNextSolver::from_nextsolver( - proj_predicate.projection_term.args, - interner, - ); - let alias = chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { - associated_ty_id, - substitution, - }); - let ty = match proj_predicate.term.kind() { - rustc_type_ir::TermKind::Ty(ty) => ty, - rustc_type_ir::TermKind::Const(_) => todo!(), - }; - let ty = ChalkToNextSolver::from_nextsolver(ty, interner); - let alias_eq = chalk_ir::AliasEq { alias, ty }; - let where_clause = chalk_ir::WhereClause::AliasEq(alias_eq); - chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(where_clause)) - } - rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::TypeOutlives( - outlives, - )) => { - let lifetime = ChalkToNextSolver::from_nextsolver(outlives.1, interner); - let ty = ChalkToNextSolver::from_nextsolver(outlives.0, interner); - let where_clause = - chalk_ir::WhereClause::TypeOutlives(chalk_ir::TypeOutlives { - lifetime, - ty, - }); - chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(where_clause)) - } - rustc_type_ir::PredicateKind::Clause( - rustc_type_ir::ClauseKind::RegionOutlives(outlives), - ) => { - let a = ChalkToNextSolver::from_nextsolver(outlives.0, interner); - let b = ChalkToNextSolver::from_nextsolver(outlives.1, interner); - let where_clause = - chalk_ir::WhereClause::LifetimeOutlives(chalk_ir::LifetimeOutlives { - a, - b, - }); - chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(where_clause)) - } - rustc_type_ir::PredicateKind::Clause(_) => todo!(), - rustc_type_ir::PredicateKind::DynCompatible(_) => todo!(), - rustc_type_ir::PredicateKind::Subtype(subtype_predicate) => todo!(), - rustc_type_ir::PredicateKind::Coerce(coerce_predicate) => todo!(), - rustc_type_ir::PredicateKind::ConstEquate(_, _) => todo!(), - rustc_type_ir::PredicateKind::Ambiguous => todo!(), - rustc_type_ir::PredicateKind::NormalizesTo(normalizes_to) => todo!(), - rustc_type_ir::PredicateKind::AliasRelate( - alias_term, - ty_term, - alias_relation_direction, - ) => { - let ty = match ty_term { - Term::Ty(ty) => ChalkToNextSolver::from_nextsolver(ty, interner), - Term::Const(..) => todo!(), - }; - match alias_term { - Term::Ty(alias_ty) => match alias_ty.kind() { - rustc_type_ir::TyKind::Alias(kind, alias) => match kind { - rustc_type_ir::AliasTyKind::Projection => { - let associated_ty_id = match alias.def_id { - SolverDefId::TypeAliasId(id) => to_assoc_type_id(id), - _ => todo!(), - }; - let substitution = - ChalkToNextSolver::from_nextsolver(alias.args, interner); - let proj_ty = - chalk_ir::ProjectionTy { associated_ty_id, substitution }; - let alias = chalk_ir::AliasTy::Projection(proj_ty); - let alias_eq = chalk_ir::AliasEq { alias, ty }; - chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds( - chalk_ir::WhereClause::AliasEq(alias_eq), - )) - } - _ => todo!(), - }, - rustc_type_ir::TyKind::Infer(var) => { - assert!(matches!( - alias_relation_direction, - rustc_type_ir::AliasRelationDirection::Equate - )); - let a: chalk_ir::Ty<_> = - ChalkToNextSolver::from_nextsolver(alias_ty, interner); - let eq_goal = chalk_ir::EqGoal { - a: chalk_ir::GenericArgData::Ty(a).intern(Interner), - b: chalk_ir::GenericArgData::Ty(ty).intern(Interner), - }; - chalk_ir::GoalData::EqGoal(eq_goal) - } - _ => todo!("Unexpected alias term {:?}", alias_term), - }, - Term::Const(..) => todo!(), - } - } - }, - ) +impl<'db> NextSolverToChalk<'db, chalk_ir::Goal> for Predicate<'db> { + fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::Goal { + chalk_ir::Goal::new(Interner, self.kind().skip_binder().to_chalk(interner)) } } @@ -1444,12 +877,14 @@ impl<'db> ChalkToNextSolver<'db, ParamEnv<'db>> for chalk_ir::Environment, interner: DbInterner<'db>) -> Self { +impl<'db> NextSolverToChalk<'db, chalk_ir::Environment> for ParamEnv<'db> { + fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::Environment { let clauses = chalk_ir::ProgramClauses::from_iter( Interner, - out.clauses.iter().map(|c| -> chalk_ir::ProgramClause { - ChalkToNextSolver::from_nextsolver(c, interner) + self.clauses.iter().filter_map(|c| -> Option> { + c.to_chalk(interner) }), ); chalk_ir::Environment { clauses } @@ -1460,15 +895,19 @@ impl<'db> ChalkToNextSolver<'db, Clause<'db>> for chalk_ir::ProgramClause) -> Clause<'db> { Clause(Predicate::new(interner, self.data(Interner).0.to_nextsolver(interner))) } +} - fn from_nextsolver(out: Clause<'db>, interner: DbInterner<'db>) -> Self { - chalk_ir::ProgramClause::new( +impl<'db> NextSolverToChalk<'db, Option>> for Clause<'db> { + fn to_chalk(self, interner: DbInterner<'db>) -> Option> { + let value: chalk_ir::ProgramClauseImplication = + as NextSolverToChalk< + 'db, + Option>, + >>::to_chalk(self.0.kind().skip_binder(), interner)?; + Some(chalk_ir::ProgramClause::new( Interner, - chalk_ir::ProgramClauseData(chalk_ir::Binders::empty( - Interner, - ChalkToNextSolver::from_nextsolver(out.0.kind().skip_binder(), interner), - )), - ) + chalk_ir::ProgramClauseData(chalk_ir::Binders::empty(Interner, value)), + )) } } @@ -1480,13 +919,25 @@ impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> assert!(self.constraints.is_empty(Interner)); self.consequence.to_nextsolver(interner) } - fn from_nextsolver(out: PredicateKind<'db>, interner: DbInterner<'db>) -> Self { - chalk_ir::ProgramClauseImplication { - consequence: ChalkToNextSolver::from_nextsolver(out, interner), +} + +impl<'db> NextSolverToChalk<'db, Option>> + for PredicateKind<'db> +{ + fn to_chalk( + self, + interner: DbInterner<'db>, + ) -> Option> { + let chalk_ir::GoalData::DomainGoal(consequence) = self.to_chalk(interner) else { + return None; + }; + + Some(chalk_ir::ProgramClauseImplication { + consequence, conditions: chalk_ir::Goals::empty(Interner), constraints: chalk_ir::Constraints::empty(Interner), priority: chalk_ir::ClausePriority::High, - } + }) } } @@ -1606,13 +1057,15 @@ impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> for chalk_ir::DomainGoal panic!("Should not be constructed."), } } +} - fn from_nextsolver(out: PredicateKind<'db>, interner: DbInterner<'db>) -> Self { - let domain_goal = match out { +impl<'db> NextSolverToChalk<'db, chalk_ir::GoalData> for PredicateKind<'db> { + fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::GoalData { + match self { rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::Trait(trait_pred)) => { - let trait_ref = ChalkToNextSolver::from_nextsolver(trait_pred.trait_ref, interner); + let trait_ref = trait_pred.trait_ref.to_chalk(interner); let where_clause = chalk_ir::WhereClause::Implemented(trait_ref); - chalk_ir::DomainGoal::Holds(where_clause) + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(where_clause)) } rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::Projection( proj_predicate, @@ -1621,82 +1074,67 @@ impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> for chalk_ir::DomainGoal to_assoc_type_id(id), _ => unreachable!(), }; - let substitution = ChalkToNextSolver::from_nextsolver( - proj_predicate.projection_term.args, - interner, - ); + let substitution = proj_predicate.projection_term.args.to_chalk(interner); let alias = chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { associated_ty_id, substitution, }); let ty = match proj_predicate.term.kind() { rustc_type_ir::TermKind::Ty(ty) => ty, - rustc_type_ir::TermKind::Const(_) => todo!(), + rustc_type_ir::TermKind::Const(_) => unimplemented!(), }; - let ty = ChalkToNextSolver::from_nextsolver(ty, interner); + let ty = ty.to_chalk(interner); let alias_eq = chalk_ir::AliasEq { alias, ty }; let where_clause = chalk_ir::WhereClause::AliasEq(alias_eq); - chalk_ir::DomainGoal::Holds(where_clause) + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(where_clause)) } rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::TypeOutlives( outlives, )) => { - let lifetime = ChalkToNextSolver::from_nextsolver(outlives.1, interner); - let ty = ChalkToNextSolver::from_nextsolver(outlives.0, interner); + let lifetime = outlives.1.to_chalk(interner); + let ty = outlives.0.to_chalk(interner); let where_clause = chalk_ir::WhereClause::TypeOutlives(chalk_ir::TypeOutlives { lifetime, ty }); - chalk_ir::DomainGoal::Holds(where_clause) + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(where_clause)) } rustc_type_ir::PredicateKind::Clause(rustc_type_ir::ClauseKind::RegionOutlives( outlives, )) => { - let a = ChalkToNextSolver::from_nextsolver(outlives.0, interner); - let b = ChalkToNextSolver::from_nextsolver(outlives.1, interner); + let a = outlives.0.to_chalk(interner); + let b = outlives.1.to_chalk(interner); let where_clause = chalk_ir::WhereClause::LifetimeOutlives(chalk_ir::LifetimeOutlives { a, b }); - chalk_ir::DomainGoal::Holds(where_clause) + chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(where_clause)) } - rustc_type_ir::PredicateKind::Clause(_) => todo!(), - rustc_type_ir::PredicateKind::DynCompatible(_) => todo!(), - rustc_type_ir::PredicateKind::Subtype(subtype_predicate) => todo!(), - rustc_type_ir::PredicateKind::Coerce(coerce_predicate) => todo!(), - rustc_type_ir::PredicateKind::ConstEquate(_, _) => todo!(), - rustc_type_ir::PredicateKind::Ambiguous => todo!(), - rustc_type_ir::PredicateKind::NormalizesTo(normalizes_to) => todo!(), rustc_type_ir::PredicateKind::AliasRelate( alias_term, - ty_term, + target_term, alias_relation_direction, ) => { - let alias = match alias_term { - Term::Ty(alias_ty) => match alias_ty.kind() { - rustc_type_ir::TyKind::Alias(kind, alias) => match kind { - rustc_type_ir::AliasTyKind::Projection => { - let associated_ty_id = match alias.def_id { - SolverDefId::TypeAliasId(id) => to_assoc_type_id(id), - _ => todo!(), - }; - let substitution = - ChalkToNextSolver::from_nextsolver(alias.args, interner); - let proj_ty = - chalk_ir::ProjectionTy { associated_ty_id, substitution }; - chalk_ir::AliasTy::Projection(proj_ty) - } - _ => todo!(), - }, - _ => todo!(), - }, - Term::Const(..) => todo!(), + let term_to_generic_arg = |term: Term<'db>| match term { + Term::Ty(ty) => chalk_ir::GenericArg::new( + Interner, + chalk_ir::GenericArgData::Ty(ty.to_chalk(interner)), + ), + Term::Const(const_) => chalk_ir::GenericArg::new( + Interner, + chalk_ir::GenericArgData::Const(const_.to_chalk(interner)), + ), }; - let ty = match ty_term { - Term::Ty(ty) => ChalkToNextSolver::from_nextsolver(ty, interner), - Term::Const(..) => todo!(), - }; - let alias_eq = chalk_ir::AliasEq { alias, ty }; - chalk_ir::DomainGoal::Holds(chalk_ir::WhereClause::AliasEq(alias_eq)) + + chalk_ir::GoalData::EqGoal(chalk_ir::EqGoal { + a: term_to_generic_arg(alias_term), + b: term_to_generic_arg(target_term), + }) } - }; - domain_goal + rustc_type_ir::PredicateKind::Clause(_) => unimplemented!(), + rustc_type_ir::PredicateKind::DynCompatible(_) => unimplemented!(), + rustc_type_ir::PredicateKind::Subtype(_) => unimplemented!(), + rustc_type_ir::PredicateKind::Coerce(_) => unimplemented!(), + rustc_type_ir::PredicateKind::ConstEquate(_, _) => unimplemented!(), + rustc_type_ir::PredicateKind::Ambiguous => unimplemented!(), + rustc_type_ir::PredicateKind::NormalizesTo(_) => unimplemented!(), + } } } @@ -1705,13 +1143,12 @@ impl<'db> ChalkToNextSolver<'db, TraitRef<'db>> for chalk_ir::TraitRef let args = self.substitution.to_nextsolver(interner); TraitRef::new_from_args(interner, from_chalk_trait_id(self.trait_id).into(), args) } +} - fn from_nextsolver(out: TraitRef<'db>, interner: DbInterner<'db>) -> Self { - let trait_id = match out.def_id { - SolverDefId::TraitId(id) => to_chalk_trait_id(id), - _ => unreachable!(), - }; - let substitution = ChalkToNextSolver::from_nextsolver(out.args, interner); +impl<'db> NextSolverToChalk<'db, chalk_ir::TraitRef> for TraitRef<'db> { + fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::TraitRef { + let trait_id = to_chalk_trait_id(self.def_id.0); + let substitution = self.args.to_chalk(interner); chalk_ir::TraitRef { trait_id, substitution } } } @@ -1754,9 +1191,17 @@ impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> for chalk_ir::WhereClause, interner: DbInterner<'db>) -> Self { - todo!() +impl<'db, I> NextSolverToChalk<'db, chalk_ir::ConstrainedSubst> for I +where + I: IntoIterator>, +{ + fn to_chalk(self, interner: DbInterner<'db>) -> chalk_ir::ConstrainedSubst { + chalk_ir::ConstrainedSubst { + constraints: chalk_ir::Constraints::empty(Interner), + subst: GenericArgs::new_from_iter(interner, self).to_chalk(interner), + } } } @@ -1764,51 +1209,7 @@ pub fn convert_canonical_args_for_result<'db>( interner: DbInterner<'db>, args: Canonical<'db, Vec>>, ) -> chalk_ir::Canonical> { - let Canonical { value, variables, max_universe } = args; - let binders = CanonicalVarKinds::from_iter( - Interner, - variables.iter().map(|v| match v { - rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::General(_)) => { - CanonicalVarKind::new( - chalk_ir::VariableKind::Ty(TyVariableKind::General), - chalk_ir::UniverseIndex::ROOT, - ) - } - rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::Int) => { - CanonicalVarKind::new( - chalk_ir::VariableKind::Ty(TyVariableKind::Integer), - chalk_ir::UniverseIndex::ROOT, - ) - } - rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::Float) => { - CanonicalVarKind::new( - chalk_ir::VariableKind::Ty(TyVariableKind::Float), - chalk_ir::UniverseIndex::ROOT, - ) - } - rustc_type_ir::CanonicalVarKind::Region(universe_index) => CanonicalVarKind::new( - chalk_ir::VariableKind::Lifetime, - chalk_ir::UniverseIndex::ROOT, - ), - rustc_type_ir::CanonicalVarKind::Const(universe_index) => CanonicalVarKind::new( - chalk_ir::VariableKind::Const(crate::TyKind::Error.intern(Interner)), - chalk_ir::UniverseIndex::ROOT, - ), - rustc_type_ir::CanonicalVarKind::PlaceholderTy(_) => unimplemented!(), - rustc_type_ir::CanonicalVarKind::PlaceholderRegion(_) => unimplemented!(), - rustc_type_ir::CanonicalVarKind::PlaceholderConst(_) => unimplemented!(), - }), - ); - chalk_ir::Canonical { - binders, - value: chalk_ir::ConstrainedSubst { - constraints: chalk_ir::Constraints::empty(Interner), - subst: ChalkToNextSolver::from_nextsolver( - GenericArgs::new_from_iter(interner, value), - interner, - ), - }, - } + args.to_chalk(interner) } pub fn convert_args_for_result<'db>( @@ -1897,7 +1298,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) rustc_type_ir::TyKind::Adt(def, args) => { let adt_id = def.inner().id; - let subst = ChalkToNextSolver::from_nextsolver(args, interner); + let subst = convert_args_for_result(interner, args.as_slice()); TyKind::Adt(chalk_ir::AdtId(adt_id), subst) } @@ -1912,7 +1313,11 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) rustc_type_ir::InferTy::FloatVar(var) => { (InferenceVar::from(var.as_u32()), TyVariableKind::Float) } - _ => todo!(), + rustc_type_ir::InferTy::FreshFloatTy(..) + | rustc_type_ir::InferTy::FreshIntTy(..) + | rustc_type_ir::InferTy::FreshTy(..) => { + panic!("Freshening shouldn't happen.") + } }; TyKind::InferenceVar(var, kind) } @@ -1932,7 +1337,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) let subst = Substitution::from_iter( Interner, tys.iter().map(|ty| { - chalk_ir::GenericArgData::Ty(ChalkToNextSolver::from_nextsolver(ty, interner)) + chalk_ir::GenericArgData::Ty(convert_ty_for_result(interner, ty)) .intern(Interner) }), ); @@ -1940,8 +1345,8 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) } rustc_type_ir::TyKind::Array(ty, const_) => { - let ty = ChalkToNextSolver::from_nextsolver(ty, interner); - let const_ = ChalkToNextSolver::from_nextsolver(const_, interner); + let ty = convert_ty_for_result(interner, ty); + let const_ = convert_const_for_result(interner, const_); TyKind::Array(ty, const_) } @@ -1952,7 +1357,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) _ => unreachable!(), }; let associated_ty_id = to_assoc_type_id(assoc_ty_id); - let substitution = ChalkToNextSolver::from_nextsolver(alias_ty.args, interner); + let substitution = convert_args_for_result(interner, alias_ty.args.as_slice()); TyKind::AssociatedType(associated_ty_id, substitution) } rustc_type_ir::AliasTyKind::Opaque => { @@ -1960,14 +1365,14 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) SolverDefId::InternedOpaqueTyId(id) => id, _ => unreachable!(), }; - let substitution = ChalkToNextSolver::from_nextsolver(alias_ty.args, interner); + let substitution = convert_args_for_result(interner, alias_ty.args.as_slice()); TyKind::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy { opaque_ty_id: opaque_ty_id.into(), substitution, })) } - rustc_type_ir::AliasTyKind::Inherent => todo!(), - rustc_type_ir::AliasTyKind::Free => todo!(), + rustc_type_ir::AliasTyKind::Inherent => unimplemented!(), + rustc_type_ir::AliasTyKind::Free => unimplemented!(), }, // For `Placeholder`, `Bound` and `Param`, see the comment on the reverse conversion. @@ -2002,7 +1407,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) interner, bound_sig.skip_binder().inputs_and_output.iter().map(|a| a.into()), ); - let substitution = ChalkToNextSolver::from_nextsolver(args, interner); + let substitution = convert_args_for_result(interner, args.as_slice()); let substitution = chalk_ir::FnSubst(substitution); let fnptr = chalk_ir::FnPointer { num_binders, sig, substitution }; TyKind::Function(fnptr) @@ -2045,11 +1450,11 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) let trait_ref = TraitRef::new( interner, trait_ref.def_id, - [self_ty.clone().into()].into_iter().chain(trait_ref.args.iter()), + [self_ty.into()].into_iter().chain(trait_ref.args.iter()), ); let trait_id = to_chalk_trait_id(trait_ref.def_id.0); let substitution = - ChalkToNextSolver::from_nextsolver(trait_ref.args, interner); + convert_args_for_result(interner, trait_ref.args.as_slice()); let trait_ref = chalk_ir::TraitRef { trait_id, substitution }; chalk_ir::WhereClause::Implemented(trait_ref) } @@ -2067,19 +1472,19 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) projection_term: AliasTerm::new( interner, existential_projection.def_id, - [self_ty.clone().into()] + [self_ty.into()] .iter() - .chain(existential_projection.args.clone().iter()), + .chain(existential_projection.args.iter()), ), - term: existential_projection.term.clone(), + term: existential_projection.term, }; let associated_ty_id = match projection.projection_term.def_id { SolverDefId::TypeAliasId(id) => to_assoc_type_id(id), _ => unreachable!(), }; - let substitution = ChalkToNextSolver::from_nextsolver( - projection.projection_term.args, + let substitution = convert_args_for_result( interner, + projection.projection_term.args.as_slice(), ); let alias = chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { associated_ty_id, @@ -2089,7 +1494,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) Term::Ty(ty) => ty, _ => unreachable!(), }; - let ty = ChalkToNextSolver::from_nextsolver(ty, interner); + let ty = convert_ty_for_result(interner, ty); let alias_eq = chalk_ir::AliasEq { alias, ty }; chalk_ir::WhereClause::AliasEq(alias_eq) } @@ -2108,7 +1513,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) } rustc_type_ir::TyKind::Slice(ty) => { - let ty = ChalkToNextSolver::from_nextsolver(ty, interner); + let ty = convert_ty_for_result(interner, ty); TyKind::Slice(ty) } @@ -2119,29 +1524,24 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) }; TyKind::Foreign(to_foreign_def_id(def_id)) } - rustc_type_ir::TyKind::Pat(_, _) => todo!(), + rustc_type_ir::TyKind::Pat(_, _) => unimplemented!(), rustc_type_ir::TyKind::RawPtr(ty, mutability) => { let mutability = match mutability { rustc_ast_ir::Mutability::Mut => chalk_ir::Mutability::Mut, rustc_ast_ir::Mutability::Not => chalk_ir::Mutability::Not, }; - let ty = ChalkToNextSolver::from_nextsolver(ty, interner); + let ty = convert_ty_for_result(interner, ty); TyKind::Raw(mutability, ty) } rustc_type_ir::TyKind::FnDef(def_id, args) => { - let subst = ChalkToNextSolver::from_nextsolver(args, interner); - match def_id { - SolverDefId::FunctionId(id) => { - TyKind::FnDef(CallableDefId::FunctionId(id).to_chalk(interner.db()), subst) - } - SolverDefId::Ctor(Ctor::Enum(e)) => { - TyKind::FnDef(CallableDefId::EnumVariantId(e).to_chalk(interner.db()), subst) - } - SolverDefId::Ctor(Ctor::Struct(s)) => { - TyKind::FnDef(CallableDefId::StructId(s).to_chalk(interner.db()), subst) - } - _ => unreachable!("Unexpected def id {:?}", def_id), - } + let id = match def_id { + SolverDefId::FunctionId(id) => CallableDefId::FunctionId(id), + SolverDefId::Ctor(Ctor::Struct(id)) => CallableDefId::StructId(id), + SolverDefId::Ctor(Ctor::Enum(id)) => CallableDefId::EnumVariantId(id), + _ => unreachable!(), + }; + let subst = convert_args_for_result(interner, args.as_slice()); + TyKind::FnDef(id.to_chalk(interner.db()), subst) } rustc_type_ir::TyKind::Closure(def_id, args) => { @@ -2149,16 +1549,16 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) SolverDefId::InternedClosureId(id) => id, _ => unreachable!(), }; - let subst = ChalkToNextSolver::from_nextsolver(args, interner); + let subst = convert_args_for_result(interner, args.as_slice()); TyKind::Closure(id.into(), subst) } - rustc_type_ir::TyKind::CoroutineClosure(_, _) => todo!(), + rustc_type_ir::TyKind::CoroutineClosure(_, _) => unimplemented!(), rustc_type_ir::TyKind::Coroutine(def_id, args) => { let id = match def_id { SolverDefId::InternedCoroutineId(id) => id, _ => unreachable!(), }; - let subst = ChalkToNextSolver::from_nextsolver(args, interner); + let subst = convert_args_for_result(interner, args.as_slice()); TyKind::Coroutine(id.into(), subst) } rustc_type_ir::TyKind::CoroutineWitness(def_id, args) => { @@ -2166,7 +1566,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) SolverDefId::InternedCoroutineId(id) => id, _ => unreachable!(), }; - let subst = ChalkToNextSolver::from_nextsolver(args, interner); + let subst = convert_args_for_result(interner, args.as_slice()); TyKind::CoroutineWitness(id.into(), subst) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs index b9b2950a94a3..385149d78432 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs @@ -9,12 +9,12 @@ use rustc_type_ir::{ solve::{Certainty, NoSolution}, }; +use crate::next_solver::mapping::NextSolverToChalk; use crate::{ TraitRefExt, db::HirDatabase, next_solver::{ - ClauseKind, CoercePredicate, PredicateKind, SubtypePredicate, - mapping::ChalkToNextSolver, + ClauseKind, CoercePredicate, PredicateKind, SubtypePredicate, mapping::ChalkToNextSolver, util::sizedness_fast_path, }, }; @@ -200,7 +200,7 @@ impl<'db> SolverDelegate for SolverContext<'db> { SolverDefId::StaticId(c) => GeneralConstId::StaticId(c), _ => unreachable!(), }; - let subst = ChalkToNextSolver::from_nextsolver(uv.args, self.interner); + let subst = uv.args.to_chalk(self.interner); let ec = self.cx().db.const_eval(c, subst, None).ok()?; Some(ec.to_nextsolver(self.interner)) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs index 3b7d4d2184a5..7ec5231a7341 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs @@ -49,7 +49,7 @@ fn let_stmt_coerce() { //- minicore: coerce_unsized fn test() { let x: &[isize] = &[1]; - // ^^^^ adjustments: Deref(None), Borrow(Ref('?2, Not)), Pointer(Unsize) + // ^^^^ adjustments: Deref(None), Borrow(Ref('?1, Not)), Pointer(Unsize) let x: *const [isize] = &[1]; // ^^^^ adjustments: Deref(None), Borrow(RawPtr(Not)), Pointer(Unsize) } @@ -96,7 +96,7 @@ fn foo(x: &[T]) -> &[T] { x } fn test() { let x = if true { foo(&[1]) - // ^^^^ adjustments: Deref(None), Borrow(Ref('?11, Not)), Pointer(Unsize) + // ^^^^ adjustments: Deref(None), Borrow(Ref('?1, Not)), Pointer(Unsize) } else { &[1] }; @@ -148,7 +148,7 @@ fn foo(x: &[T]) -> &[T] { x } fn test(i: i32) { let x = match i { 2 => foo(&[2]), - // ^^^^ adjustments: Deref(None), Borrow(Ref('?11, Not)), Pointer(Unsize) + // ^^^^ adjustments: Deref(None), Borrow(Ref('?1, Not)), Pointer(Unsize) 1 => &[1], _ => &[3], }; @@ -881,7 +881,7 @@ fn adjust_index() { fn test() { let x = [1, 2, 3]; x[2] = 6; - // ^ adjustments: Borrow(Ref('?8, Mut)) + // ^ adjustments: Borrow(Ref('?0, Mut)) } ", ); @@ -906,11 +906,11 @@ impl core::ops::IndexMut for StructMut { } fn test() { Struct[0]; - // ^^^^^^ adjustments: Borrow(Ref('?2, Not)) + // ^^^^^^ adjustments: Borrow(Ref('?0, Not)) StructMut[0]; - // ^^^^^^^^^ adjustments: Borrow(Ref('?5, Not)) + // ^^^^^^^^^ adjustments: Borrow(Ref('?1, Not)) &mut StructMut[0]; - // ^^^^^^^^^ adjustments: Borrow(Ref('?8, Mut)) + // ^^^^^^^^^ adjustments: Borrow(Ref('?2, Mut)) }", ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs index df9061d23bf5..d79639b93727 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs @@ -589,6 +589,7 @@ fn main() { "attrs_shim", "attrs_shim", "return_type_impl_traits_shim", + "generic_predicates_ns_shim", "infer_shim", "function_signature_shim", "function_signature_with_source_map_shim", @@ -605,8 +606,6 @@ fn main() { "impl_signature_shim", "impl_signature_with_source_map_shim", "callable_item_signature_shim", - "adt_variance_shim", - "variances_of_shim", "trait_impls_in_deps_shim", "trait_impls_in_crate_shim", "impl_trait_with_diagnostics_shim", @@ -615,7 +614,6 @@ fn main() { "impl_trait_with_diagnostics_ns_shim", "impl_self_ty_with_diagnostics_ns_shim", "generic_predicates_ns_shim", - "generic_predicates_ns_shim", "value_ty_shim", "generic_predicates_shim", "lang_item", @@ -691,6 +689,7 @@ fn main() { "attrs_shim", "attrs_shim", "return_type_impl_traits_shim", + "generic_predicates_ns_shim", "infer_shim", "function_signature_with_source_map_shim", "expr_scopes_shim", @@ -706,7 +705,6 @@ fn main() { "impl_trait_with_diagnostics_ns_shim", "impl_self_ty_with_diagnostics_ns_shim", "generic_predicates_ns_shim", - "generic_predicates_ns_shim", "generic_predicates_shim", ] "#]], diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs index 5d088e40cded..6490554b22b7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs @@ -202,7 +202,7 @@ fn expr_macro_def_expanded_in_various_places() { 100..119 'for _ ...!() {}': {unknown} 100..119 'for _ ...!() {}': &'? mut {unknown} 100..119 'for _ ...!() {}': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 100..119 'for _ ...!() {}': Option<{unknown}> + 100..119 'for _ ...!() {}': Option<<{unknown} as Iterator>::Item> 100..119 'for _ ...!() {}': () 100..119 'for _ ...!() {}': () 100..119 'for _ ...!() {}': () @@ -296,7 +296,7 @@ fn expr_macro_rules_expanded_in_various_places() { 114..133 'for _ ...!() {}': {unknown} 114..133 'for _ ...!() {}': &'? mut {unknown} 114..133 'for _ ...!() {}': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 114..133 'for _ ...!() {}': Option<{unknown}> + 114..133 'for _ ...!() {}': Option<<{unknown} as Iterator>::Item> 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs index f09b3ef6bfd0..d50daf0f0d5b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs @@ -1876,9 +1876,9 @@ impl Foo { } fn test() { Foo.foo(); - //^^^ adjustments: Borrow(Ref('?1, Not)) + //^^^ adjustments: Borrow(Ref('?0, Not)) (&Foo).foo(); - // ^^^^ adjustments: Deref(None), Borrow(Ref('?3, Not)) + // ^^^^ adjustments: Deref(None), Borrow(Ref('?2, Not)) } "#, ); @@ -1892,7 +1892,7 @@ fn receiver_adjustment_unsize_array() { fn test() { let a = [1, 2, 3]; a.len(); -} //^ adjustments: Borrow(Ref('?7, Not)), Pointer(Unsize) +} //^ adjustments: Borrow(Ref('?0, Not)), Pointer(Unsize) "#, ); } @@ -2105,7 +2105,7 @@ impl Foo { } fn test() { Box::new(Foo).foo(); - //^^^^^^^^^^^^^ adjustments: Deref(None), Borrow(Ref('?5, Not)) + //^^^^^^^^^^^^^ adjustments: Deref(None), Borrow(Ref('?0, Not)) } "#, ); @@ -2123,7 +2123,7 @@ impl Foo { use core::mem::ManuallyDrop; fn test() { ManuallyDrop::new(Foo).foo(); - //^^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Some(Not)))), Borrow(Ref('?6, Not)) + //^^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Some(Not)))), Borrow(Ref('?0, Not)) } "#, ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs index 6a9135622deb..baca7f2318e2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs @@ -362,12 +362,12 @@ fn diverging_expression_3_break() { 140..141 'x': u32 149..175 '{ for ...; }; }': u32 151..172 'for a ...eak; }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 151..172 'for a ...eak; }': {unknown} + 151..172 'for a ...eak; }': <{unknown} as IntoIterator>::IntoIter 151..172 'for a ...eak; }': ! - 151..172 'for a ...eak; }': {unknown} - 151..172 'for a ...eak; }': &'? mut {unknown} + 151..172 'for a ...eak; }': <{unknown} as IntoIterator>::IntoIter + 151..172 'for a ...eak; }': &'? mut <{unknown} as IntoIterator>::IntoIter 151..172 'for a ...eak; }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 151..172 'for a ...eak; }': Option<{unknown}> + 151..172 'for a ...eak; }': Option<<{unknown} as Iterator>::Item> 151..172 'for a ...eak; }': () 151..172 'for a ...eak; }': () 151..172 'for a ...eak; }': () @@ -379,12 +379,12 @@ fn diverging_expression_3_break() { 226..227 'x': u32 235..253 '{ for ... {}; }': u32 237..250 'for a in b {}': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 237..250 'for a in b {}': {unknown} + 237..250 'for a in b {}': <{unknown} as IntoIterator>::IntoIter 237..250 'for a in b {}': ! - 237..250 'for a in b {}': {unknown} - 237..250 'for a in b {}': &'? mut {unknown} + 237..250 'for a in b {}': <{unknown} as IntoIterator>::IntoIter + 237..250 'for a in b {}': &'? mut <{unknown} as IntoIterator>::IntoIter 237..250 'for a in b {}': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 237..250 'for a in b {}': Option<{unknown}> + 237..250 'for a in b {}': Option<<{unknown} as Iterator>::Item> 237..250 'for a in b {}': () 237..250 'for a in b {}': () 237..250 'for a in b {}': () @@ -395,12 +395,12 @@ fn diverging_expression_3_break() { 304..305 'x': u32 313..340 '{ for ...; }; }': u32 315..337 'for a ...urn; }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 315..337 'for a ...urn; }': {unknown} + 315..337 'for a ...urn; }': <{unknown} as IntoIterator>::IntoIter 315..337 'for a ...urn; }': ! - 315..337 'for a ...urn; }': {unknown} - 315..337 'for a ...urn; }': &'? mut {unknown} + 315..337 'for a ...urn; }': <{unknown} as IntoIterator>::IntoIter + 315..337 'for a ...urn; }': &'? mut <{unknown} as IntoIterator>::IntoIter 315..337 'for a ...urn; }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 315..337 'for a ...urn; }': Option<{unknown}> + 315..337 'for a ...urn; }': Option<<{unknown} as Iterator>::Item> 315..337 'for a ...urn; }': () 315..337 'for a ...urn; }': () 315..337 'for a ...urn; }': () diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs index 11d1a58e5367..256ca7defb78 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs @@ -134,6 +134,9 @@ static ALIAS: AliasTy = { "#, ); + // FIXME(next-solver): This should emit type mismatch error but leaving it for now + // as we should fully migrate into next-solver without chalk-ir and TAIT should be + // reworked on r-a to handle `#[define_opaque(T)]` check_infer_with_mismatches( r#" trait Trait {} @@ -155,7 +158,6 @@ static ALIAS: i32 = { 191..193 '_a': impl Trait + ?Sized 205..211 'Struct': Struct 217..218 '5': i32 - 205..211: expected impl Trait + ?Sized, got Struct "#]], ) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs index 4949d4016bf1..60a2641e1a20 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs @@ -48,12 +48,12 @@ fn infer_pattern() { 83..84 '1': i32 86..93 '"hello"': &'static str 101..151 'for (e... }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 101..151 'for (e... }': {unknown} + 101..151 'for (e... }': <{unknown} as IntoIterator>::IntoIter 101..151 'for (e... }': ! - 101..151 'for (e... }': {unknown} - 101..151 'for (e... }': &'? mut {unknown} + 101..151 'for (e... }': <{unknown} as IntoIterator>::IntoIter + 101..151 'for (e... }': &'? mut <{unknown} as IntoIterator>::IntoIter 101..151 'for (e... }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 101..151 'for (e... }': Option<({unknown}, {unknown})> + 101..151 'for (e... }': Option<<{unknown} as Iterator>::Item> 101..151 'for (e... }': () 101..151 'for (e... }': () 101..151 'for (e... }': () @@ -719,28 +719,28 @@ fn test() { 51..58 'loop {}': ! 56..58 '{}': () 72..171 '{ ... x); }': () - 78..81 'foo': fn foo<&'? (i32, &'? str), i32, impl FnOnce(&'? (i32, &'? str)) -> i32>(&'? (i32, &'? str), impl FnOnce(&'? (i32, &'? str)) -> i32) -> i32 + 78..81 'foo': fn foo<&'? (i32, &'static str), i32, impl FnOnce(&'? (i32, &'static str)) -> i32>(&'? (i32, &'static str), impl FnOnce(&'? (i32, &'static str)) -> i32) -> i32 78..105 'foo(&(...y)| x)': i32 82..91 '&(1, "a")': &'? (i32, &'static str) 83..91 '(1, "a")': (i32, &'static str) 84..85 '1': i32 87..90 '"a"': &'static str - 93..104 '|&(x, y)| x': impl FnOnce(&'? (i32, &'? str)) -> i32 - 94..101 '&(x, y)': &'? (i32, &'? str) - 95..101 '(x, y)': (i32, &'? str) + 93..104 '|&(x, y)| x': impl FnOnce(&'? (i32, &'static str)) -> i32 + 94..101 '&(x, y)': &'? (i32, &'static str) + 95..101 '(x, y)': (i32, &'static str) 96..97 'x': i32 - 99..100 'y': &'? str + 99..100 'y': &'static str 103..104 'x': i32 - 142..145 'foo': fn foo<&'? (i32, &'? str), &'? i32, impl FnOnce(&'? (i32, &'? str)) -> &'? i32>(&'? (i32, &'? str), impl FnOnce(&'? (i32, &'? str)) -> &'? i32) -> &'? i32 + 142..145 'foo': fn foo<&'? (i32, &'static str), &'? i32, impl FnOnce(&'? (i32, &'static str)) -> &'? i32>(&'? (i32, &'static str), impl FnOnce(&'? (i32, &'static str)) -> &'? i32) -> &'? i32 142..168 'foo(&(...y)| x)': &'? i32 146..155 '&(1, "a")': &'? (i32, &'static str) 147..155 '(1, "a")': (i32, &'static str) 148..149 '1': i32 151..154 '"a"': &'static str - 157..167 '|(x, y)| x': impl FnOnce(&'? (i32, &'? str)) -> &'? i32 - 158..164 '(x, y)': (i32, &'? str) + 157..167 '|(x, y)| x': impl FnOnce(&'? (i32, &'static str)) -> &'? i32 + 158..164 '(x, y)': (i32, &'static str) 159..160 'x': &'? i32 - 162..163 'y': &'? &'? str + 162..163 'y': &'? &'static str 166..167 'x': &'? i32 "#]], ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index 6a3f2286215f..eacc4603ea1a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -268,12 +268,12 @@ fn infer_std_crash_5() { expect![[r#" 26..322 '{ ... } }': () 32..320 'for co... }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 32..320 'for co... }': {unknown} + 32..320 'for co... }': <{unknown} as IntoIterator>::IntoIter 32..320 'for co... }': ! - 32..320 'for co... }': {unknown} - 32..320 'for co... }': &'? mut {unknown} + 32..320 'for co... }': <{unknown} as IntoIterator>::IntoIter + 32..320 'for co... }': &'? mut <{unknown} as IntoIterator>::IntoIter 32..320 'for co... }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 32..320 'for co... }': Option<{unknown}> + 32..320 'for co... }': Option<<{unknown} as Iterator>::Item> 32..320 'for co... }': () 32..320 'for co... }': () 32..320 'for co... }': () @@ -628,7 +628,7 @@ fn issue_4053_diesel_where_clauses() { 65..69 'self': Self 267..271 'self': Self 466..470 'self': SelectStatement - 488..522 '{ ... }': () + 488..522 '{ ... }': as BoxedDsl>::Output 498..502 'self': SelectStatement 498..508 'self.order': O 498..515 'self.o...into()': dyn QueryFragment + '? @@ -1248,7 +1248,7 @@ fn test() { 16..66 'for _ ... }': {unknown} 16..66 'for _ ... }': &'? mut {unknown} 16..66 'for _ ... }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 16..66 'for _ ... }': Option<{unknown}> + 16..66 'for _ ... }': Option<<{unknown} as Iterator>::Item> 16..66 'for _ ... }': () 16..66 'for _ ... }': () 16..66 'for _ ... }': () diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs index e4ee52f45eb7..97473bbabba0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -20,7 +20,7 @@ impl<'a> IntoIterator for &'a Grid { "#, expect![[r#" 150..154 'self': &'a Grid - 174..181 '{ }': impl Iterator + 174..181 '{ }': impl Iterator "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index a995a45f6e75..e7357ed5aa78 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -1954,6 +1954,8 @@ fn closure_return_inferred() { ); } +// FIXME(next-solver): `&'? str` in 231..262 seems suspicious. +// Should revisit this once we fully migrated into next-solver without chalk-ir. #[test] fn coroutine_types_inferred() { check_infer( @@ -1998,7 +2000,7 @@ fn test() { 225..360 'match ... }': () 231..239 'Pin::new': fn new<&'? mut |usize| yields i64 -> &'static str>(&'? mut |usize| yields i64 -> &'static str) -> Pin<&'? mut |usize| yields i64 -> &'static str> 231..247 'Pin::n...mut g)': Pin<&'? mut |usize| yields i64 -> &'static str> - 231..262 'Pin::n...usize)': CoroutineState + 231..262 'Pin::n...usize)': CoroutineState 240..246 '&mut g': &'? mut |usize| yields i64 -> &'static str 245..246 'g': |usize| yields i64 -> &'static str 255..261 '0usize': usize diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index a311e579744d..7a946f7ec7ce 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -1,6 +1,8 @@ use cov_mark::check; use expect_test::expect; +use crate::tests::infer_with_mismatches; + use super::{check, check_infer, check_infer_with_mismatches, check_no_mismatches, check_types}; #[test] @@ -2460,7 +2462,7 @@ use core::ops::Index; type Key = ::Key; -pub trait UnificationStoreBase: Index> { +pub trait UnificationStoreBase: Index> { type Key; fn len(&self) -> usize; @@ -3634,8 +3636,7 @@ fn minimized() { #[test] fn no_builtin_binop_expectation_for_general_ty_var() { - // FIXME: Ideally type mismatch should be reported on `take_u32(42 - p)`. - check_types( + infer_with_mismatches( r#" //- minicore: add use core::ops::Add; @@ -3659,6 +3660,7 @@ fn minimized() { take_u32(42 + p); } "#, + true, ); } @@ -4188,6 +4190,8 @@ fn g(p:

::Pointer) { ); } +// FIXME(next-solver): Was `&'a T` but now getting error lifetime. +// This might be fixed once we migrate into next-solver fully without chalk-ir in lowering. #[test] fn gats_with_impl_trait() { // FIXME: the last function (`fn i()`) is not valid Rust as of this writing because you cannot @@ -4211,21 +4215,21 @@ fn f(v: impl Trait) { } fn g<'a, T: 'a>(v: impl Trait = &'a T>) { let a = v.get::(); - //^ &'a T + //^ &'? T let a = v.get::<()>(); //^ = &'a T> as Trait>::Assoc<()> } fn h<'a>(v: impl Trait = &'a i32> + Trait = &'a i64>) { let a = v.get::(); - //^ &'a i32 + //^ &'? i32 let a = v.get::(); - //^ &'a i64 + //^ &'? i64 } fn i<'a>(v: impl Trait = &'a i32, Assoc = &'a i64>) { let a = v.get::(); - //^ &'a i32 + //^ &'? i32 let a = v.get::(); - //^ &'a i64 + //^ &'? i64 } "#, ); @@ -4255,8 +4259,8 @@ fn f<'a>(v: &dyn Trait = &'a i32>) { 127..128 'v': &'? (dyn Trait = &'a i32> + '?) 164..195 '{ ...f(); }': () 170..171 'v': &'? (dyn Trait = &'a i32> + '?) - 170..184 'v.get::()': {unknown} - 170..192 'v.get:...eref()': &'? {unknown} + 170..184 'v.get::()': = &'a i32> + '? as Trait>::Assoc + 170..192 'v.get:...eref()': {unknown} "#]], ); } @@ -4931,6 +4935,8 @@ fn main() { ); } +// FIXME(next-solver): Was `>::Error` but now getting error lifetime. +// This might be fixed once we migrate into next-solver fully without chalk-ir in lowering. #[test] fn new_solver_crash_1() { check_infer( @@ -4947,7 +4953,7 @@ where "#, expect![[r#" 84..86 'de': D - 135..138 '{ }': >::Error + 135..138 '{ }': >::Error "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs index 0c58e031c3a0..a6377243ed98 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -21,9 +21,17 @@ use stdx::never; use triomphe::Arc; use crate::{ - db::HirDatabase, infer::unify::InferenceTable, next_solver::{ - infer::{DbInternerInferExt, InferCtxt}, mapping::{convert_canonical_args_for_result, ChalkToNextSolver}, util::mini_canonicalize, DbInterner, GenericArg, Predicate, SolverContext, Span - }, utils::UnevaluatedConstEvaluatorFolder, AliasEq, AliasTy, Canonical, DomainGoal, Goal, InEnvironment, Interner, ProjectionTy, ProjectionTyExt, TraitRefExt, Ty, TyKind, TypeFlags, WhereClause + AliasEq, AliasTy, Canonical, DomainGoal, Goal, InEnvironment, Interner, ProjectionTy, + ProjectionTyExt, TraitRefExt, Ty, TyKind, TypeFlags, WhereClause, + db::HirDatabase, + infer::unify::InferenceTable, + next_solver::{ + DbInterner, GenericArg, Predicate, SolverContext, Span, + infer::{DbInternerInferExt, InferCtxt}, + mapping::{ChalkToNextSolver, convert_canonical_args_for_result}, + util::mini_canonicalize, + }, + utils::UnevaluatedConstEvaluatorFolder, }; /// A set of clauses that we assume to be true. E.g. if we are inside this function: @@ -282,30 +290,18 @@ pub fn next_trait_solve( } } -pub fn next_trait_solve_canonical<'db>( - db: &'db dyn HirDatabase, - krate: Crate, - block: Option, +pub fn next_trait_solve_canonical_in_ctxt<'db>( + infer_ctxt: &InferCtxt<'db>, goal: crate::next_solver::Canonical<'db, crate::next_solver::Goal<'db, Predicate<'db>>>, ) -> NextTraitSolveResult { - // FIXME: should use analysis_in_body, but that needs GenericDefId::Block - let context = SolverContext( - DbInterner::new_with(db, Some(krate), block) - .infer_ctxt() - .build(TypingMode::non_body_analysis()), - ); + let context = SolverContext(infer_ctxt.clone()); tracing::info!(?goal); - let (goal, var_values) = - context.instantiate_canonical(&goal); + let (goal, var_values) = context.instantiate_canonical(&goal); tracing::info!(?var_values); - let res = context.evaluate_root_goal( - goal.clone(), - Span::dummy(), - None - ); + let res = context.evaluate_root_goal(goal, Span::dummy(), None); let vars = var_values.var_values.iter().map(|g| context.0.resolve_vars_if_possible(g)).collect(); @@ -318,13 +314,10 @@ pub fn next_trait_solve_canonical<'db>( match res { Err(_) => NextTraitSolveResult::NoSolution, Ok((_, Certainty::Yes, args)) => NextTraitSolveResult::Certain( - convert_canonical_args_for_result(DbInterner::new_with(db, Some(krate), block), args) + convert_canonical_args_for_result(infer_ctxt.interner, args), ), Ok((_, Certainty::Maybe(_), args)) => { - let subst = convert_canonical_args_for_result( - DbInterner::new_with(db, Some(krate), block), - args, - ); + let subst = convert_canonical_args_for_result(infer_ctxt.interner, args); NextTraitSolveResult::Uncertain(chalk_ir::Canonical { binders: subst.binders, value: subst.value.subst, diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs index 79a154a5d8e6..c230bbad0bc4 100644 --- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs @@ -14,7 +14,11 @@ use hir_expand::{ mod_path::{ModPath, PathKind}, name::Name, }; -use hir_ty::{db::HirDatabase, method_resolution, next_solver::{mapping::ChalkToNextSolver, DbInterner}}; +use hir_ty::{ + db::HirDatabase, + method_resolution, + next_solver::{DbInterner, mapping::ChalkToNextSolver}, +}; use crate::{ Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, DocLinkDef, Enum, ExternCrateDecl, @@ -271,7 +275,11 @@ fn resolve_impl_trait_item<'db>( // // FIXME: resolve type aliases (which are not yielded by iterate_path_candidates) _ = method_resolution::iterate_path_candidates( - &canonical.to_nextsolver(DbInterner::new_with(db, Some(environment.krate), environment.block)), + &canonical.to_nextsolver(DbInterner::new_with( + db, + Some(environment.krate), + environment.block, + )), db, environment, &traits_in_scope, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs index 6121c61ea3f4..445afa75f3f4 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs @@ -10,7 +10,8 @@ use crate::{ fn check_expected_type_and_name(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { let (db, pos) = position(ra_fixture); let config = TEST_CONFIG; - let (completion_context, _analysis) = CompletionContext::new(&db, pos, &config).unwrap(); + let (completion_context, _analysis) = + salsa::attach(&db, || CompletionContext::new(&db, pos, &config).unwrap()); let ty = completion_context .expected_type diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs index ac54ac0950f3..8613581292f7 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -526,8 +526,7 @@ fn main() { fn run(_t: Rate<5>) { } fn main() { - run(f()) // FIXME: remove this error - //^^^ error: expected Rate<5>, found Rate<_> + run(f()) } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs index 46b633b8a325..875b4d9b06ce 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs @@ -2,7 +2,10 @@ use expect_test::{Expect, expect}; use hir::{FilePosition, FileRange}; use ide_db::{ EditionedFileId, FxHashSet, - base_db::{SourceDatabase, salsa::Durability}, + base_db::{ + SourceDatabase, + salsa::{self, Durability}, + }, }; use test_utils::RangeOrOffset; use triomphe::Arc; @@ -116,7 +119,7 @@ fn assert_ssr_transforms(rules: &[&str], input: &str, expected: Expect) { let rule: SsrRule = rule.parse().unwrap(); match_finder.add_rule(rule).unwrap(); } - let edits = match_finder.edits(); + let edits = salsa::attach(&db, || match_finder.edits()); if edits.is_empty() { panic!("No edits were made"); } @@ -155,8 +158,12 @@ fn assert_matches(pattern: &str, code: &str, expected: &[&str]) { ) .unwrap(); match_finder.add_search_pattern(pattern.parse().unwrap()).unwrap(); - let matched_strings: Vec = - match_finder.matches().flattened().matches.iter().map(|m| m.matched_text()).collect(); + let matched_strings: Vec = salsa::attach(&db, || match_finder.matches()) + .flattened() + .matches + .iter() + .map(|m| m.matched_text()) + .collect(); if matched_strings != expected && !expected.is_empty() { print_match_debug_info(&match_finder, position.file_id, expected[0]); } diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs index 11518d1fbf30..72436307d2ce 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs @@ -47,7 +47,8 @@ fn check_rewrite(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect let (analysis, position) = fixture::position(ra_fixture); let sema = &Semantics::new(&analysis.db); let (cursor_def, docs, range) = def_under_cursor(sema, &position); - let res = rewrite_links(sema.db, docs.as_str(), cursor_def, Some(range)); + let res = + salsa::attach(sema.db, || rewrite_links(sema.db, docs.as_str(), cursor_def, Some(range))); expect.assert_eq(&res) } From 6e36750c617c656432aafaffe170691b91ecc985 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 9 Sep 2025 10:30:03 -0700 Subject: [PATCH 317/682] Update wasm-component-ld to 0.5.17 Keeping this up-to-date as the project itself, and its dependencies, are updated. --- Cargo.lock | 50 +++++++++++++------------- src/tools/wasm-component-ld/Cargo.toml | 2 +- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c29c3158d4a1..cff5864dbbe0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5984,9 +5984,9 @@ dependencies = [ [[package]] name = "wasm-component-ld" -version = "0.5.16" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14cd35d6cae91109a0ffd207b573cf3c741cab7e921dd376ea7aaf2c52a3408c" +checksum = "1c9208f87cac2332fd80dcf36d54e9163d3446e28301e0c6e424984425738984" dependencies = [ "anyhow", "clap", @@ -5994,9 +5994,9 @@ dependencies = [ "libc", "tempfile", "wasi-preview1-component-adapter-provider", - "wasmparser 0.237.0", + "wasmparser 0.239.0", "wat", - "windows-sys 0.59.0", + "windows-sys 0.60.2", "winsplit", "wit-component", "wit-parser", @@ -6021,24 +6021,24 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.237.0" +version = "0.239.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efe92d1321afa53ffc88a57c497bb7330c3cf84c98ffdba4a4caf6a0684fad3c" +checksum = "5be00faa2b4950c76fe618c409d2c3ea5a3c9422013e079482d78544bb2d184c" dependencies = [ "leb128fmt", - "wasmparser 0.237.0", + "wasmparser 0.239.0", ] [[package]] name = "wasm-metadata" -version = "0.237.0" +version = "0.239.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cc0b0a0c4f35ca6efa7a797671372915d4e9659dba2d59edc6fafc931d19997" +checksum = "20b3ec880a9ac69ccd92fbdbcf46ee833071cf09f82bb005b2327c7ae6025ae2" dependencies = [ "anyhow", "indexmap", - "wasm-encoder 0.237.0", - "wasmparser 0.237.0", + "wasm-encoder 0.239.0", + "wasmparser 0.239.0", ] [[package]] @@ -6063,9 +6063,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.237.0" +version = "0.239.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d2a40ca0d2bdf4b0bf36c13a737d0b2c58e4c8aaefe1c57f336dd75369ca250" +checksum = "8c9d90bb93e764f6beabf1d02028c70a2156a6583e63ac4218dd07ef733368b0" dependencies = [ "bitflags", "hashbrown", @@ -6076,22 +6076,22 @@ dependencies = [ [[package]] name = "wast" -version = "237.0.0" +version = "239.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf66f545acbd55082485cb9a6daab54579cb8628a027162253e8e9f5963c767" +checksum = "9139176fe8a2590e0fb174cdcaf373b224cb93c3dde08e4297c1361d2ba1ea5d" dependencies = [ "bumpalo", "leb128fmt", "memchr", "unicode-width 0.2.1", - "wasm-encoder 0.237.0", + "wasm-encoder 0.239.0", ] [[package]] name = "wat" -version = "1.237.0" +version = "1.239.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27975186f549e4b8d6878b627be732863883c72f7bf4dcf8f96e5f8242f73da9" +checksum = "3e1c941927d34709f255558166f8901a2005f8ab4a9650432e9281b7cc6f3b75" dependencies = [ "wast", ] @@ -6580,9 +6580,9 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.237.0" +version = "0.239.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb7674f76c10e82fe00b256a9d4ffb2b8d037d42ab8e9a83ebb3be35c9d0bf6" +checksum = "88a866b19dba2c94d706ec58c92a4c62ab63e482b4c935d2a085ac94caecb136" dependencies = [ "anyhow", "bitflags", @@ -6591,17 +6591,17 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.237.0", + "wasm-encoder 0.239.0", "wasm-metadata", - "wasmparser 0.237.0", + "wasmparser 0.239.0", "wit-parser", ] [[package]] name = "wit-parser" -version = "0.237.0" +version = "0.239.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce2596a5bc7c24cc965b56ad6ff9e32394c4e401764f89620a888519c6e849ab" +checksum = "55c92c939d667b7bf0c6bf2d1f67196529758f99a2a45a3355cc56964fd5315d" dependencies = [ "anyhow", "id-arena", @@ -6612,7 +6612,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.237.0", + "wasmparser 0.239.0", ] [[package]] diff --git a/src/tools/wasm-component-ld/Cargo.toml b/src/tools/wasm-component-ld/Cargo.toml index 23dc86998e88..3def2391a13f 100644 --- a/src/tools/wasm-component-ld/Cargo.toml +++ b/src/tools/wasm-component-ld/Cargo.toml @@ -10,4 +10,4 @@ name = "wasm-component-ld" path = "src/main.rs" [dependencies] -wasm-component-ld = "0.5.16" +wasm-component-ld = "0.5.17" From 23edc4dd4266836980dc3e71a1f5ddd151a3e92a Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Tue, 9 Sep 2025 19:43:50 +0200 Subject: [PATCH 318/682] Fix compare_against_sw_vers test when a version part is 0 --- library/std/src/sys/platform_version/darwin/tests.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/std/src/sys/platform_version/darwin/tests.rs b/library/std/src/sys/platform_version/darwin/tests.rs index eecd58ec79e3..17b2cc18ec09 100644 --- a/library/std/src/sys/platform_version/darwin/tests.rs +++ b/library/std/src/sys/platform_version/darwin/tests.rs @@ -35,9 +35,9 @@ fn compare_against_sw_vers() { assert_eq!(__isOSVersionAtLeast(major, minor, subminor), 1); // One lower is available - assert_eq!(__isOSVersionAtLeast(major, minor, subminor.saturating_sub(1)), 1); - assert_eq!(__isOSVersionAtLeast(major, minor.saturating_sub(1), subminor), 1); - assert_eq!(__isOSVersionAtLeast(major.saturating_sub(1), minor, subminor), 1); + assert_eq!(__isOSVersionAtLeast(major, minor, (subminor as u32).saturating_sub(1) as i32), 1); + assert_eq!(__isOSVersionAtLeast(major, (minor as u32).saturating_sub(1) as i32, subminor), 1); + assert_eq!(__isOSVersionAtLeast((major as u32).saturating_sub(1) as i32, minor, subminor), 1); // One higher isn't available assert_eq!(__isOSVersionAtLeast(major, minor, subminor + 1), 0); From 7a66925a8138df105f77d646f9206024baf7ea4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Mon, 8 Sep 2025 19:25:22 +0200 Subject: [PATCH 319/682] Strip frontmatter in fewer places --- .../rustc_builtin_macros/src/source_util.rs | 69 ++++++++++++------- compiler/rustc_driver_impl/src/lib.rs | 12 +++- compiler/rustc_expand/src/module.rs | 9 ++- .../rustc_expand/src/proc_macro_server.rs | 11 ++- compiler/rustc_interface/src/interface.rs | 22 +++--- compiler/rustc_interface/src/passes.rs | 17 +++-- compiler/rustc_parse/src/lexer/mod.rs | 2 +- compiler/rustc_parse/src/lib.rs | 40 +++++------ compiler/rustc_parse/src/parser/tests.rs | 7 +- src/librustdoc/clean/render_macro_matchers.rs | 21 +++--- src/librustdoc/doctest/make.rs | 19 ++--- .../src/doc/needless_doctest_main.rs | 16 +++-- src/tools/rustfmt/src/parse/parser.rs | 14 +++- tests/ui-fulldeps/auxiliary/parser.rs | 3 +- .../ui-fulldeps/mod_dir_path_canonicalized.rs | 9 ++- tests/ui/frontmatter/auxiliary/expr.rs | 4 ++ tests/ui/frontmatter/auxiliary/makro.rs | 14 +++- tests/ui/frontmatter/include-in-expr-ctxt.rs | 9 +++ tests/ui/frontmatter/include-in-item-ctxt.rs | 10 +++ tests/ui/frontmatter/included-frontmatter.rs | 12 ---- tests/ui/frontmatter/proc-macro-observer.rs | 7 +- 21 files changed, 210 insertions(+), 117 deletions(-) create mode 100644 tests/ui/frontmatter/auxiliary/expr.rs create mode 100644 tests/ui/frontmatter/include-in-expr-ctxt.rs create mode 100644 tests/ui/frontmatter/include-in-item-ctxt.rs delete mode 100644 tests/ui/frontmatter/included-frontmatter.rs diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index f95c8f38229b..11b868f81a97 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -13,9 +13,11 @@ use rustc_expand::base::{ }; use rustc_expand::module::DirOwnership; use rustc_lint_defs::BuiltinLintDiag; -use rustc_parse::parser::{ForceCollect, Parser}; +use rustc_parse::lexer::StripTokens; +use rustc_parse::parser::ForceCollect; use rustc_parse::{new_parser_from_file, unwrap_or_emit_fatal, utf8_error}; use rustc_session::lint::builtin::INCOMPLETE_INCLUDE; +use rustc_session::parse::ParseSess; use rustc_span::source_map::SourceMap; use rustc_span::{ByteSymbol, Pos, Span, Symbol}; use smallvec::SmallVec; @@ -114,39 +116,48 @@ pub(crate) fn expand_include<'cx>( let ExpandResult::Ready(mac) = get_single_str_from_tts(cx, sp, tts, "include!") else { return ExpandResult::Retry(()); }; - let file = match mac { - Ok(file) => file, + let path = match mac { + Ok(path) => path, Err(guar) => return ExpandResult::Ready(DummyResult::any(sp, guar)), }; // The file will be added to the code map by the parser - let file = match resolve_path(&cx.sess, file.as_str(), sp) { - Ok(f) => f, + let path = match resolve_path(&cx.sess, path.as_str(), sp) { + Ok(path) => path, Err(err) => { let guar = err.emit(); return ExpandResult::Ready(DummyResult::any(sp, guar)); } }; - let p = unwrap_or_emit_fatal(new_parser_from_file(cx.psess(), &file, Some(sp))); // If in the included file we have e.g., `mod bar;`, - // then the path of `bar.rs` should be relative to the directory of `file`. + // then the path of `bar.rs` should be relative to the directory of `path`. // See https://github.com/rust-lang/rust/pull/69838/files#r395217057 for a discussion. // `MacroExpander::fully_expand_fragment` later restores, so "stack discipline" is maintained. - let dir_path = file.parent().unwrap_or(&file).to_owned(); + let dir_path = path.parent().unwrap_or(&path).to_owned(); cx.current_expansion.module = Rc::new(cx.current_expansion.module.with_dir_path(dir_path)); cx.current_expansion.dir_ownership = DirOwnership::Owned { relative: None }; struct ExpandInclude<'a> { - p: Parser<'a>, + psess: &'a ParseSess, + path: PathBuf, node_id: ast::NodeId, + span: Span, } impl<'a> MacResult for ExpandInclude<'a> { - fn make_expr(mut self: Box>) -> Option> { - let expr = parse_expr(&mut self.p).ok()?; - if self.p.token != token::Eof { - self.p.psess.buffer_lint( + fn make_expr(self: Box>) -> Option> { + let mut p = unwrap_or_emit_fatal(new_parser_from_file( + self.psess, + &self.path, + // Don't strip frontmatter for backward compatibility, `---` may be the start of a + // manifold negation. FIXME: Ideally, we wouldn't strip shebangs here either. + StripTokens::Shebang, + Some(self.span), + )); + let expr = parse_expr(&mut p).ok()?; + if p.token != token::Eof { + p.psess.buffer_lint( INCOMPLETE_INCLUDE, - self.p.token.span, + p.token.span, self.node_id, BuiltinLintDiag::IncompleteInclude, ); @@ -154,24 +165,27 @@ pub(crate) fn expand_include<'cx>( Some(expr) } - fn make_items(mut self: Box>) -> Option; 1]>> { + fn make_items(self: Box>) -> Option; 1]>> { + let mut p = unwrap_or_emit_fatal(new_parser_from_file( + self.psess, + &self.path, + StripTokens::ShebangAndFrontmatter, + Some(self.span), + )); let mut ret = SmallVec::new(); loop { - match self.p.parse_item(ForceCollect::No) { + match p.parse_item(ForceCollect::No) { Err(err) => { err.emit(); break; } Ok(Some(item)) => ret.push(item), Ok(None) => { - if self.p.token != token::Eof { - self.p - .dcx() - .create_err(errors::ExpectedItem { - span: self.p.token.span, - token: &pprust::token_to_string(&self.p.token), - }) - .emit(); + if p.token != token::Eof { + p.dcx().emit_err(errors::ExpectedItem { + span: p.token.span, + token: &pprust::token_to_string(&p.token), + }); } break; @@ -182,7 +196,12 @@ pub(crate) fn expand_include<'cx>( } } - ExpandResult::Ready(Box::new(ExpandInclude { p, node_id: cx.current_expansion.lint_node_id })) + ExpandResult::Ready(Box::new(ExpandInclude { + psess: cx.psess(), + path, + node_id: cx.current_expansion.lint_node_id, + span: sp, + })) } /// Expand `include_str!($input)` to the content of the UTF-8-encoded file given by path `$input` as a string literal. diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index f3ed60421056..d00a4c358340 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -51,6 +51,7 @@ use rustc_lint::unerased_lint_store; use rustc_metadata::creader::MetadataLoader; use rustc_metadata::locator; use rustc_middle::ty::TyCtxt; +use rustc_parse::lexer::StripTokens; use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal}; use rustc_session::config::{ CG_OPTIONS, CrateType, ErrorOutputType, Input, OptionDesc, OutFileName, OutputType, Sysroot, @@ -1288,10 +1289,15 @@ fn warn_on_confusing_output_filename_flag( fn parse_crate_attrs<'a>(sess: &'a Session) -> PResult<'a, ast::AttrVec> { let mut parser = unwrap_or_emit_fatal(match &sess.io.input { - Input::File(file) => new_parser_from_file(&sess.psess, file, None), - Input::Str { name, input } => { - new_parser_from_source_str(&sess.psess, name.clone(), input.clone()) + Input::File(file) => { + new_parser_from_file(&sess.psess, file, StripTokens::ShebangAndFrontmatter, None) } + Input::Str { name, input } => new_parser_from_source_str( + &sess.psess, + name.clone(), + input.clone(), + StripTokens::ShebangAndFrontmatter, + ), }); parser.parse_inner_attributes() } diff --git a/compiler/rustc_expand/src/module.rs b/compiler/rustc_expand/src/module.rs index 19f3cdbc5491..79ab3cab22ce 100644 --- a/compiler/rustc_expand/src/module.rs +++ b/compiler/rustc_expand/src/module.rs @@ -4,6 +4,7 @@ use std::path::{self, Path, PathBuf}; use rustc_ast::{AttrVec, Attribute, Inline, Item, ModSpans}; use rustc_attr_parsing::validate_attr; use rustc_errors::{Diag, ErrorGuaranteed}; +use rustc_parse::lexer::StripTokens; use rustc_parse::{exp, new_parser_from_file, unwrap_or_emit_fatal}; use rustc_session::Session; use rustc_session::parse::ParseSess; @@ -67,8 +68,12 @@ pub(crate) fn parse_external_mod( } // Actually parse the external file as a module. - let mut parser = - unwrap_or_emit_fatal(new_parser_from_file(&sess.psess, &mp.file_path, Some(span))); + let mut parser = unwrap_or_emit_fatal(new_parser_from_file( + &sess.psess, + &mp.file_path, + StripTokens::ShebangAndFrontmatter, + Some(span), + )); let (inner_attrs, items, inner_span) = parser.parse_mod(exp!(Eof)).map_err(|err| ModError::ParserError(err))?; attrs.extend(inner_attrs); diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 5b1d3d6d35b6..295573f44926 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -8,7 +8,7 @@ use rustc_ast::util::literal::escape_byte_str_symbol; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Diag, ErrorGuaranteed, MultiSpan, PResult}; -use rustc_parse::lexer::nfc_normalize; +use rustc_parse::lexer::{StripTokens, nfc_normalize}; use rustc_parse::parser::Parser; use rustc_parse::{exp, new_parser_from_source_str, source_str_to_stream, unwrap_or_emit_fatal}; use rustc_proc_macro::bridge::{ @@ -485,8 +485,13 @@ impl server::FreeFunctions for Rustc<'_, '_> { fn literal_from_str(&mut self, s: &str) -> Result, ()> { let name = FileName::proc_macro_source_code(s); - let mut parser = - unwrap_or_emit_fatal(new_parser_from_source_str(self.psess(), name, s.to_owned())); + + let mut parser = unwrap_or_emit_fatal(new_parser_from_source_str( + self.psess(), + name, + s.to_owned(), + StripTokens::Nothing, + )); let first_span = parser.token.span.data(); let minus_present = parser.eat(exp!(Minus)); diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 4c820b8877b7..b52c5b4cd663 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -13,7 +13,8 @@ use rustc_lint::LintStore; use rustc_middle::ty; use rustc_middle::ty::CurrentGcx; use rustc_middle::util::Providers; -use rustc_parse::new_parser_from_simple_source_str; +use rustc_parse::lexer::StripTokens; +use rustc_parse::new_parser_from_source_str; use rustc_parse::parser::attr::AllowLeadingUnsafe; use rustc_query_impl::QueryCtxt; use rustc_query_system::query::print_query_stack; @@ -68,7 +69,8 @@ pub(crate) fn parse_cfg(dcx: DiagCtxtHandle<'_>, cfgs: Vec) -> Cfg { }; } - match new_parser_from_simple_source_str(&psess, filename, s.to_string()) { + match new_parser_from_source_str(&psess, filename, s.to_string(), StripTokens::Nothing) + { Ok(mut parser) => match parser.parse_meta_item(AllowLeadingUnsafe::No) { Ok(meta_item) if parser.token == token::Eof => { if meta_item.path.segments.len() != 1 { @@ -166,13 +168,15 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec) -> Ch error!("expected `cfg(name, values(\"value1\", \"value2\", ... \"valueN\"))`") }; - let mut parser = match new_parser_from_simple_source_str(&psess, filename, s.to_string()) { - Ok(parser) => parser, - Err(errs) => { - errs.into_iter().for_each(|err| err.cancel()); - expected_error(); - } - }; + let mut parser = + match new_parser_from_source_str(&psess, filename, s.to_string(), StripTokens::Nothing) + { + Ok(parser) => parser, + Err(errs) => { + errs.into_iter().for_each(|err| err.cancel()); + expected_error(); + } + }; let meta_item = match parser.parse_meta_item(AllowLeadingUnsafe::No) { Ok(meta_item) if parser.token == token::Eof => meta_item, diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index ca8c10311fb8..cf81a1257070 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -27,6 +27,7 @@ use rustc_middle::arena::Arena; use rustc_middle::dep_graph::DepsType; use rustc_middle::ty::{self, CurrentGcx, GlobalCtxt, RegisteredTools, TyCtxt}; use rustc_middle::util::Providers; +use rustc_parse::lexer::StripTokens; use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal}; use rustc_passes::{abi_test, input_stats, layout_test}; use rustc_resolve::{Resolver, ResolverOutputs}; @@ -51,10 +52,18 @@ pub fn parse<'a>(sess: &'a Session) -> ast::Crate { let mut krate = sess .time("parse_crate", || { let mut parser = unwrap_or_emit_fatal(match &sess.io.input { - Input::File(file) => new_parser_from_file(&sess.psess, file, None), - Input::Str { input, name } => { - new_parser_from_source_str(&sess.psess, name.clone(), input.clone()) - } + Input::File(file) => new_parser_from_file( + &sess.psess, + file, + StripTokens::ShebangAndFrontmatter, + None, + ), + Input::Str { input, name } => new_parser_from_source_str( + &sess.psess, + name.clone(), + input.clone(), + StripTokens::ShebangAndFrontmatter, + ), }); parser.parse_crate_mod() }) diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index f5f081efc495..51019db7c00e 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -45,7 +45,7 @@ pub(crate) struct UnmatchedDelim { } /// Which tokens should be stripped before lexing the tokens. -pub(crate) enum StripTokens { +pub enum StripTokens { /// Strip both shebang and frontmatter. ShebangAndFrontmatter, /// Strip the shebang but not frontmatter. diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index d8792d7af4c3..88b67d792deb 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -54,29 +54,18 @@ pub fn unwrap_or_emit_fatal(expr: Result>>) -> T { } } -/// Creates a new parser from a source string. On failure, the errors must be consumed via -/// `unwrap_or_emit_fatal`, `emit`, `cancel`, etc., otherwise a panic will occur when they are -/// dropped. +/// Creates a new parser from a source string. +/// +/// On failure, the errors must be consumed via `unwrap_or_emit_fatal`, `emit`, `cancel`, +/// etc., otherwise a panic will occur when they are dropped. pub fn new_parser_from_source_str( psess: &ParseSess, name: FileName, source: String, + strip_tokens: StripTokens, ) -> Result, Vec>> { let source_file = psess.source_map().new_source_file(name, source); - new_parser_from_source_file(psess, source_file, StripTokens::ShebangAndFrontmatter) -} - -/// Creates a new parser from a simple (no shebang, no frontmatter) source string. -/// -/// On failure, the errors must be consumed via `unwrap_or_emit_fatal`, `emit`, `cancel`, -/// etc., otherwise a panic will occur when they are dropped. -pub fn new_parser_from_simple_source_str( - psess: &ParseSess, - name: FileName, - source: String, -) -> Result, Vec>> { - let source_file = psess.source_map().new_source_file(name, source); - new_parser_from_source_file(psess, source_file, StripTokens::Nothing) + new_parser_from_source_file(psess, source_file, strip_tokens) } /// Creates a new parser from a filename. On failure, the errors must be consumed via @@ -87,6 +76,7 @@ pub fn new_parser_from_simple_source_str( pub fn new_parser_from_file<'a>( psess: &'a ParseSess, path: &Path, + strip_tokens: StripTokens, sp: Option, ) -> Result, Vec>> { let sm = psess.source_map(); @@ -110,7 +100,7 @@ pub fn new_parser_from_file<'a>( } err.emit(); }); - new_parser_from_source_file(psess, source_file, StripTokens::ShebangAndFrontmatter) + new_parser_from_source_file(psess, source_file, strip_tokens) } pub fn utf8_error( @@ -172,6 +162,9 @@ fn new_parser_from_source_file( Ok(parser) } +/// Given a source string, produces a sequence of token trees. +/// +/// NOTE: This only strips shebangs, not frontmatter! pub fn source_str_to_stream( psess: &ParseSess, name: FileName, @@ -179,13 +172,16 @@ pub fn source_str_to_stream( override_span: Option, ) -> Result>> { let source_file = psess.source_map().new_source_file(name, source); - // used mainly for `proc_macro` and the likes, not for our parsing purposes, so don't parse - // frontmatters as frontmatters, but for compatibility reason still strip the shebang + // FIXME(frontmatter): Consider stripping frontmatter in a future edition. We can't strip them + // in the current edition since that would be breaking. + // See also . + // Alternatively, stop stripping shebangs here, too, if T-lang and crater approve. source_file_to_stream(psess, source_file, override_span, StripTokens::Shebang) } -/// Given a source file, produces a sequence of token trees. Returns any buffered errors from -/// parsing the token stream. +/// Given a source file, produces a sequence of token trees. +/// +/// Returns any buffered errors from parsing the token stream. fn source_file_to_stream<'psess>( psess: &'psess ParseSess, source_file: Arc, diff --git a/compiler/rustc_parse/src/parser/tests.rs b/compiler/rustc_parse/src/parser/tests.rs index a6e7266e71b4..e645fb47b9ec 100644 --- a/compiler/rustc_parse/src/parser/tests.rs +++ b/compiler/rustc_parse/src/parser/tests.rs @@ -22,6 +22,7 @@ use rustc_span::{ }; use termcolor::WriteColor; +use crate::lexer::StripTokens; use crate::parser::{ForceCollect, Parser}; use crate::{new_parser_from_source_str, source_str_to_stream, unwrap_or_emit_fatal}; @@ -35,6 +36,7 @@ fn string_to_parser(psess: &ParseSess, source_str: String) -> Parser<'_> { psess, PathBuf::from("bogofile").into(), source_str, + StripTokens::Nothing, )) } @@ -2240,7 +2242,7 @@ fn parse_item_from_source_str( source: String, psess: &ParseSess, ) -> PResult<'_, Option>> { - unwrap_or_emit_fatal(new_parser_from_source_str(psess, name, source)) + unwrap_or_emit_fatal(new_parser_from_source_str(psess, name, source, StripTokens::Nothing)) .parse_item(ForceCollect::No) } @@ -2520,7 +2522,8 @@ fn ttdelim_span() { source: String, psess: &ParseSess, ) -> PResult<'_, Box> { - unwrap_or_emit_fatal(new_parser_from_source_str(psess, name, source)).parse_expr() + unwrap_or_emit_fatal(new_parser_from_source_str(psess, name, source, StripTokens::Nothing)) + .parse_expr() } create_default_session_globals_then(|| { diff --git a/src/librustdoc/clean/render_macro_matchers.rs b/src/librustdoc/clean/render_macro_matchers.rs index d684e6f8650f..b5a8d64ff4f5 100644 --- a/src/librustdoc/clean/render_macro_matchers.rs +++ b/src/librustdoc/clean/render_macro_matchers.rs @@ -3,6 +3,7 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_ast_pretty::pprust::PrintState; use rustc_ast_pretty::pprust::state::State as Printer; use rustc_middle::ty::TyCtxt; +use rustc_parse::lexer::StripTokens; use rustc_session::parse::ParseSess; use rustc_span::symbol::{Ident, Symbol, kw}; use rustc_span::{FileName, Span}; @@ -64,14 +65,18 @@ fn snippet_equal_to_token(tcx: TyCtxt<'_>, matcher: &TokenTree) -> Option parser, - Err(errs) => { - errs.into_iter().for_each(|err| err.cancel()); - return None; - } - }; + let mut parser = match rustc_parse::new_parser_from_source_str( + &psess, + file_name, + snippet.clone(), + StripTokens::Nothing, + ) { + Ok(parser) => parser, + Err(errs) => { + errs.into_iter().for_each(|err| err.cancel()); + return None; + } + }; // Reparse a single token tree. if parser.token == token::Eof { diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs index f229f77c9784..5eaadc9eb451 100644 --- a/src/librustdoc/doctest/make.rs +++ b/src/librustdoc/doctest/make.rs @@ -10,6 +10,7 @@ use rustc_ast::tokenstream::TokenTree; use rustc_ast::{self as ast, AttrStyle, HasAttrs, StmtKind}; use rustc_errors::emitter::stderr_destination; use rustc_errors::{ColorConfig, DiagCtxtHandle}; +use rustc_parse::lexer::StripTokens; use rustc_parse::new_parser_from_source_str; use rustc_session::parse::ParseSess; use rustc_span::edition::{DEFAULT_EDITION, Edition}; @@ -468,14 +469,16 @@ fn parse_source( let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings(); let psess = ParseSess::with_dcx(dcx, sm); - let mut parser = match new_parser_from_source_str(&psess, filename, wrapped_source) { - Ok(p) => p, - Err(errs) => { - errs.into_iter().for_each(|err| err.cancel()); - reset_error_count(&psess); - return Err(()); - } - }; + // Don't strip any tokens; it wouldn't matter anyway because the source is wrapped in a function. + let mut parser = + match new_parser_from_source_str(&psess, filename, wrapped_source, StripTokens::Nothing) { + Ok(p) => p, + Err(errs) => { + errs.into_iter().for_each(|err| err.cancel()); + reset_error_count(&psess); + return Err(()); + } + }; fn push_to_s(s: &mut String, source: &str, span: rustc_span::Span, prev_span_hi: &mut usize) { let extra_len = DOCTEST_CODE_WRAPPER.len(); diff --git a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs index 74283d7ba863..43bb97235550 100644 --- a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs +++ b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs @@ -8,6 +8,7 @@ use rustc_ast::{CoroutineKind, Fn, FnRetTy, Item, ItemKind}; use rustc_errors::emitter::HumanEmitter; use rustc_errors::{Diag, DiagCtxt}; use rustc_lint::LateContext; +use rustc_parse::lexer::StripTokens; use rustc_parse::new_parser_from_source_str; use rustc_parse::parser::ForceCollect; use rustc_session::parse::ParseSess; @@ -49,13 +50,14 @@ pub fn check( let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); let psess = ParseSess::with_dcx(dcx, sm); - let mut parser = match new_parser_from_source_str(&psess, filename, code) { - Ok(p) => p, - Err(errs) => { - errs.into_iter().for_each(Diag::cancel); - return (false, test_attr_spans); - }, - }; + let mut parser = + match new_parser_from_source_str(&psess, filename, code, StripTokens::ShebangAndFrontmatter) { + Ok(p) => p, + Err(errs) => { + errs.into_iter().for_each(Diag::cancel); + return (false, test_attr_spans); + }, + }; let mut relevant_main_found = false; let mut eligible = true; diff --git a/src/tools/rustfmt/src/parse/parser.rs b/src/tools/rustfmt/src/parse/parser.rs index 2ec8769c45f2..63c6c8c99d0d 100644 --- a/src/tools/rustfmt/src/parse/parser.rs +++ b/src/tools/rustfmt/src/parse/parser.rs @@ -3,6 +3,7 @@ use std::path::{Path, PathBuf}; use rustc_ast::{ast, attr}; use rustc_errors::Diag; +use rustc_parse::lexer::StripTokens; use rustc_parse::parser::Parser as RawParser; use rustc_parse::{exp, new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal}; use rustc_span::{Span, sym}; @@ -64,11 +65,14 @@ impl<'a> ParserBuilder<'a> { input: Input, ) -> Result, Vec>> { match input { - Input::File(ref file) => new_parser_from_file(psess, file, None), + Input::File(ref file) => { + new_parser_from_file(psess, file, StripTokens::ShebangAndFrontmatter, None) + } Input::Text(text) => new_parser_from_source_str( psess, rustc_span::FileName::Custom("stdin".to_owned()), text, + StripTokens::ShebangAndFrontmatter, ), } } @@ -104,8 +108,12 @@ impl<'a> Parser<'a> { span: Span, ) -> Result<(ast::AttrVec, ThinVec>, Span), ParserError> { let result = catch_unwind(AssertUnwindSafe(|| { - let mut parser = - unwrap_or_emit_fatal(new_parser_from_file(psess.inner(), path, Some(span))); + let mut parser = unwrap_or_emit_fatal(new_parser_from_file( + psess.inner(), + path, + StripTokens::ShebangAndFrontmatter, + Some(span), + )); match parser.parse_mod(exp!(Eof)) { Ok((a, i, spans)) => Some((a, i, spans.inner_span)), Err(e) => { diff --git a/tests/ui-fulldeps/auxiliary/parser.rs b/tests/ui-fulldeps/auxiliary/parser.rs index 6726969350dd..6ee39e5130f6 100644 --- a/tests/ui-fulldeps/auxiliary/parser.rs +++ b/tests/ui-fulldeps/auxiliary/parser.rs @@ -10,7 +10,7 @@ extern crate rustc_span; use rustc_ast::ast::{AttrKind, Attribute, DUMMY_NODE_ID, Expr}; use rustc_ast::mut_visit::{self, MutVisitor}; use rustc_ast::node_id::NodeId; -use rustc_ast::token::{self, Token}; +use rustc_ast::token; use rustc_ast::tokenstream::{AttrTokenStream, AttrTokenTree, LazyAttrTokenStream}; use rustc_errors::Diag; use rustc_parse::parser::Recovery; @@ -23,6 +23,7 @@ pub fn parse_expr(psess: &ParseSess, source_code: &str) -> Option> { psess, FileName::anon_source_code(source_code), source_code.to_owned(), + rustc_parse::lexer::StripTokens::Nothing, )); let mut parser = parser.recovery(Recovery::Forbidden); diff --git a/tests/ui-fulldeps/mod_dir_path_canonicalized.rs b/tests/ui-fulldeps/mod_dir_path_canonicalized.rs index 99cb5fc5aa1c..df5f29e35fe6 100644 --- a/tests/ui-fulldeps/mod_dir_path_canonicalized.rs +++ b/tests/ui-fulldeps/mod_dir_path_canonicalized.rs @@ -16,7 +16,7 @@ extern crate rustc_span; #[allow(unused_extern_crates)] extern crate rustc_driver; -use rustc_parse::{new_parser_from_file, unwrap_or_emit_fatal}; +use rustc_parse::{lexer::StripTokens, new_parser_from_file, unwrap_or_emit_fatal}; use rustc_session::parse::ParseSess; use std::path::Path; @@ -34,6 +34,11 @@ fn parse() { let path = Path::new(file!()); let path = path.canonicalize().unwrap(); - let mut parser = unwrap_or_emit_fatal(new_parser_from_file(&psess, &path, None)); + let mut parser = unwrap_or_emit_fatal(new_parser_from_file( + &psess, + &path, + StripTokens::ShebangAndFrontmatter, + None, + )); let _ = parser.parse_crate_mod(); } diff --git a/tests/ui/frontmatter/auxiliary/expr.rs b/tests/ui/frontmatter/auxiliary/expr.rs new file mode 100644 index 000000000000..5f6941106664 --- /dev/null +++ b/tests/ui/frontmatter/auxiliary/expr.rs @@ -0,0 +1,4 @@ +--- +- +--- +1 diff --git a/tests/ui/frontmatter/auxiliary/makro.rs b/tests/ui/frontmatter/auxiliary/makro.rs index 70707b27bff9..1d64fa44bd3e 100644 --- a/tests/ui/frontmatter/auxiliary/makro.rs +++ b/tests/ui/frontmatter/auxiliary/makro.rs @@ -1,8 +1,20 @@ extern crate proc_macro; -use proc_macro::TokenStream; +use proc_macro::{Literal, TokenStream}; #[proc_macro] pub fn check(_: TokenStream) -> TokenStream { + // In the following test cases, the `---` may look like the start of frontmatter but it is not! + // That's because it would be backward incompatible to interpret them as such in the latest + // stable edition. That's not only the case due to the feature gate error but also due to the + // fact that we "eagerly" emit errors on malformed frontmatter. + + // issue: + _ = "---".parse::(); + // Just a sequence of regular Rust punctuation tokens. assert_eq!(6, "---\n---".parse::().unwrap().into_iter().count()); + + // issue: + assert!("---".parse::().is_err()); + Default::default() } diff --git a/tests/ui/frontmatter/include-in-expr-ctxt.rs b/tests/ui/frontmatter/include-in-expr-ctxt.rs new file mode 100644 index 000000000000..7b02c9cb8a54 --- /dev/null +++ b/tests/ui/frontmatter/include-in-expr-ctxt.rs @@ -0,0 +1,9 @@ +// Check that an expr-ctxt `include` doesn't try to parse frontmatter and instead +// treats it as a regular Rust token sequence. +//@ check-pass +#![expect(double_negations)] + +fn main() { + // issue: + const _: () = assert!(-1 == include!("auxiliary/expr.rs")); +} diff --git a/tests/ui/frontmatter/include-in-item-ctxt.rs b/tests/ui/frontmatter/include-in-item-ctxt.rs new file mode 100644 index 000000000000..c8455bc49abd --- /dev/null +++ b/tests/ui/frontmatter/include-in-item-ctxt.rs @@ -0,0 +1,10 @@ +// Ensure that in item ctxts we can `include` files that contain frontmatter. +//@ check-pass + +#![feature(frontmatter)] + +include!("auxiliary/lib.rs"); + +fn main() { + foo(1); +} diff --git a/tests/ui/frontmatter/included-frontmatter.rs b/tests/ui/frontmatter/included-frontmatter.rs deleted file mode 100644 index 57616cd1228c..000000000000 --- a/tests/ui/frontmatter/included-frontmatter.rs +++ /dev/null @@ -1,12 +0,0 @@ -#![feature(frontmatter)] - -//@ check-pass - -include!("auxiliary/lib.rs"); - -// auxiliary/lib.rs contains a frontmatter. Ensure that we can use them in an -// `include!` macro. - -fn main() { - foo(1); -} diff --git a/tests/ui/frontmatter/proc-macro-observer.rs b/tests/ui/frontmatter/proc-macro-observer.rs index b1cc1460933f..6c4c8c572897 100644 --- a/tests/ui/frontmatter/proc-macro-observer.rs +++ b/tests/ui/frontmatter/proc-macro-observer.rs @@ -2,10 +2,9 @@ //@ proc-macro: makro.rs //@ edition: 2021 +// Check that a proc-macro doesn't try to parse frontmatter and instead treats +// it as a regular Rust token sequence. See `auxiliary/makro.rs` for details. + makro::check!(); -// checks that a proc-macro doesn't know or parse frontmatters at all and instead treats -// it as normal Rust code. -// see auxiliary/makro.rs for how it is tested. - fn main() {} From c9ed8a1ced9601bfd96a63be4fa0cdc4c6cb41a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Mon, 8 Sep 2025 19:25:22 +0200 Subject: [PATCH 320/682] Strip frontmatter in fewer places --- clippy_lints/src/doc/needless_doctest_main.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/doc/needless_doctest_main.rs b/clippy_lints/src/doc/needless_doctest_main.rs index 74283d7ba863..43bb97235550 100644 --- a/clippy_lints/src/doc/needless_doctest_main.rs +++ b/clippy_lints/src/doc/needless_doctest_main.rs @@ -8,6 +8,7 @@ use rustc_ast::{CoroutineKind, Fn, FnRetTy, Item, ItemKind}; use rustc_errors::emitter::HumanEmitter; use rustc_errors::{Diag, DiagCtxt}; use rustc_lint::LateContext; +use rustc_parse::lexer::StripTokens; use rustc_parse::new_parser_from_source_str; use rustc_parse::parser::ForceCollect; use rustc_session::parse::ParseSess; @@ -49,13 +50,14 @@ pub fn check( let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); let psess = ParseSess::with_dcx(dcx, sm); - let mut parser = match new_parser_from_source_str(&psess, filename, code) { - Ok(p) => p, - Err(errs) => { - errs.into_iter().for_each(Diag::cancel); - return (false, test_attr_spans); - }, - }; + let mut parser = + match new_parser_from_source_str(&psess, filename, code, StripTokens::ShebangAndFrontmatter) { + Ok(p) => p, + Err(errs) => { + errs.into_iter().for_each(Diag::cancel); + return (false, test_attr_spans); + }, + }; let mut relevant_main_found = false; let mut eligible = true; From 0c96200f268df10d4f3ac102f3161c67e5e67221 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Mon, 8 Sep 2025 18:45:41 +0200 Subject: [PATCH 321/682] c-variadic: reject non-unsafe functions --- compiler/rustc_ast/src/ast.rs | 23 ++++++++++ compiler/rustc_ast_passes/messages.ftl | 4 ++ .../rustc_ast_passes/src/ast_validation.rs | 23 ++++++---- compiler/rustc_ast_passes/src/errors.rs | 15 +++++++ .../variadic-ffi-semantic-restrictions.rs | 12 +++--- .../variadic-ffi-semantic-restrictions.stderr | 42 ++++++++++++++++--- 6 files changed, 98 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 2733a2603d6d..915bae9e5f5a 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2309,6 +2309,22 @@ impl FnSig { self.span.shrink_to_lo() } + + /// The span of the header's safety, or where to insert it if empty. + pub fn safety_span(&self) -> Span { + match self.header.safety { + Safety::Unsafe(span) | Safety::Safe(span) => span, + Safety::Default => { + // Insert after the `coroutine_kind` if available. + if let Some(extern_span) = self.header.ext.span() { + return extern_span.shrink_to_lo(); + } + + // Insert right at the front of the signature. + self.header_span().shrink_to_hi() + } + } + } } /// A constraint on an associated item. @@ -3553,6 +3569,13 @@ impl Extern { None => Extern::Implicit(span), } } + + pub fn span(self) -> Option { + match self { + Extern::None => None, + Extern::Implicit(span) | Extern::Explicit(_, span) => Some(span), + } + } } /// A function header. diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 958797a60894..1aa38a1beda3 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -68,6 +68,10 @@ ast_passes_bound_in_context = bounds on `type`s in {$ctx} have no effect ast_passes_c_variadic_associated_function = associated functions cannot have a C variable argument list +ast_passes_c_variadic_must_be_unsafe = + functions with a C variable argument list must be unsafe + .suggestion = add the `unsafe` keyword to this definition + ast_passes_c_variadic_no_extern = `...` is not supported for non-extern functions ast_passes_const_and_c_variadic = functions cannot be both `const` and C-variadic diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index f425acda3583..7b32778ddf04 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -698,20 +698,25 @@ impl<'a> AstValidator<'a> { FnCtxt::Foreign => return, FnCtxt::Free => match sig.header.ext { Extern::Implicit(_) => { - if matches!(sig.header.safety, Safety::Unsafe(_)) { - return; + // Implicitly defaults to C. + if !matches!(sig.header.safety, Safety::Unsafe(_)) { + self.dcx().emit_err(errors::CVariadicMustBeUnsafe { + span: variadic_param.span, + unsafe_span: sig.safety_span(), + }); } - - self.dcx().emit_err(errors::BadCVariadic { span: variadic_param.span }); } Extern::Explicit(StrLit { symbol_unescaped, .. }, _) => { - if matches!(symbol_unescaped, sym::C | sym::C_dash_unwind) { - if matches!(sig.header.safety, Safety::Unsafe(_)) { - return; - } + if !matches!(symbol_unescaped, sym::C | sym::C_dash_unwind) { + self.dcx().emit_err(errors::BadCVariadic { span: variadic_param.span }); } - self.dcx().emit_err(errors::BadCVariadic { span: variadic_param.span }); + if !matches!(sig.header.safety, Safety::Unsafe(_)) { + self.dcx().emit_err(errors::CVariadicMustBeUnsafe { + span: variadic_param.span, + unsafe_span: sig.safety_span(), + }); + } } Extern::None => { let err = errors::CVariadicNoExtern { span: variadic_param.span }; diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 6b57e5ef3761..946a22d2e759 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -332,6 +332,21 @@ pub(crate) struct CVariadicNoExtern { pub span: Span, } +#[derive(Diagnostic)] +#[diag(ast_passes_c_variadic_must_be_unsafe)] +pub(crate) struct CVariadicMustBeUnsafe { + #[primary_span] + pub span: Span, + + #[suggestion( + ast_passes_suggestion, + applicability = "maybe-incorrect", + code = "unsafe ", + style = "verbose" + )] + pub unsafe_span: Span, +} + #[derive(Diagnostic)] #[diag(ast_passes_bad_c_variadic)] pub(crate) struct BadCVariadic { diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs index fe5421ab2c0a..ff491322efc5 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs @@ -10,19 +10,19 @@ fn f1_2(...) {} //~^ ERROR `...` is not supported for non-extern functions extern "C" fn f2_1(x: isize, ...) {} -//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +//~^ ERROR functions with a C variable argument list must be unsafe extern "C" fn f2_2(...) {} -//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +//~^ ERROR functions with a C variable argument list must be unsafe extern "C" fn f2_3(..., x: isize) {} //~^ ERROR `...` must be the last argument of a C-variadic function extern "C" fn f3_1(x: isize, ...) {} -//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +//~^ ERROR functions with a C variable argument list must be unsafe extern "C" fn f3_2(...) {} -//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +//~^ ERROR functions with a C variable argument list must be unsafe extern "C" fn f3_3(..., x: isize) {} //~^ ERROR `...` must be the last argument of a C-variadic function @@ -33,12 +33,12 @@ const unsafe extern "C" fn f4_1(x: isize, ...) {} const extern "C" fn f4_2(x: isize, ...) {} //~^ ERROR functions cannot be both `const` and C-variadic -//~| ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +//~| ERROR functions with a C variable argument list must be unsafe //~| ERROR destructor of `VaListImpl<'_>` cannot be evaluated at compile-time const extern "C" fn f4_3(..., x: isize, ...) {} //~^ ERROR functions cannot be both `const` and C-variadic -//~| ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +//~| ERROR functions with a C variable argument list must be unsafe //~| ERROR `...` must be the last argument of a C-variadic function extern "C" { diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr index 0abe15264053..98f1d79f8f4e 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr @@ -10,17 +10,27 @@ error: `...` is not supported for non-extern functions LL | fn f1_2(...) {} | ^^^ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +error: functions with a C variable argument list must be unsafe --> $DIR/variadic-ffi-semantic-restrictions.rs:12:30 | LL | extern "C" fn f2_1(x: isize, ...) {} | ^^^ + | +help: add the `unsafe` keyword to this definition + | +LL | unsafe extern "C" fn f2_1(x: isize, ...) {} + | ++++++ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +error: functions with a C variable argument list must be unsafe --> $DIR/variadic-ffi-semantic-restrictions.rs:15:20 | LL | extern "C" fn f2_2(...) {} | ^^^ + | +help: add the `unsafe` keyword to this definition + | +LL | unsafe extern "C" fn f2_2(...) {} + | ++++++ error: `...` must be the last argument of a C-variadic function --> $DIR/variadic-ffi-semantic-restrictions.rs:18:20 @@ -28,17 +38,27 @@ error: `...` must be the last argument of a C-variadic function LL | extern "C" fn f2_3(..., x: isize) {} | ^^^ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +error: functions with a C variable argument list must be unsafe --> $DIR/variadic-ffi-semantic-restrictions.rs:21:30 | LL | extern "C" fn f3_1(x: isize, ...) {} | ^^^ + | +help: add the `unsafe` keyword to this definition + | +LL | unsafe extern "C" fn f3_1(x: isize, ...) {} + | ++++++ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +error: functions with a C variable argument list must be unsafe --> $DIR/variadic-ffi-semantic-restrictions.rs:24:20 | LL | extern "C" fn f3_2(...) {} | ^^^ + | +help: add the `unsafe` keyword to this definition + | +LL | unsafe extern "C" fn f3_2(...) {} + | ++++++ error: `...` must be the last argument of a C-variadic function --> $DIR/variadic-ffi-semantic-restrictions.rs:27:20 @@ -58,11 +78,16 @@ error: functions cannot be both `const` and C-variadic LL | const extern "C" fn f4_2(x: isize, ...) {} | ^^^^^ `const` because of this ^^^ C-variadic because of this -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +error: functions with a C variable argument list must be unsafe --> $DIR/variadic-ffi-semantic-restrictions.rs:34:36 | LL | const extern "C" fn f4_2(x: isize, ...) {} | ^^^ + | +help: add the `unsafe` keyword to this definition + | +LL | const unsafe extern "C" fn f4_2(x: isize, ...) {} + | ++++++ error: `...` must be the last argument of a C-variadic function --> $DIR/variadic-ffi-semantic-restrictions.rs:39:26 @@ -76,11 +101,16 @@ error: functions cannot be both `const` and C-variadic LL | const extern "C" fn f4_3(..., x: isize, ...) {} | ^^^^^ `const` because of this ^^^ C-variadic because of this -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +error: functions with a C variable argument list must be unsafe --> $DIR/variadic-ffi-semantic-restrictions.rs:39:41 | LL | const extern "C" fn f4_3(..., x: isize, ...) {} | ^^^ + | +help: add the `unsafe` keyword to this definition + | +LL | const unsafe extern "C" fn f4_3(..., x: isize, ...) {} + | ++++++ error: `...` must be the last argument of a C-variadic function --> $DIR/variadic-ffi-semantic-restrictions.rs:45:13 From 9196844f0d86242ee3d6a446fe681b6b2d2d674c Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Mon, 8 Sep 2025 19:27:07 +0200 Subject: [PATCH 322/682] c-variadic: reject functions with unsupported extern ABI --- compiler/rustc_ast/src/ast.rs | 5 + compiler/rustc_ast_passes/messages.ftl | 7 +- .../rustc_ast_passes/src/ast_validation.rs | 7 +- compiler/rustc_ast_passes/src/errors.rs | 9 +- tests/ui/c-variadic/issue-86053-1.stderr | 2 + .../cmse-nonsecure-entry/generics.rs | 2 +- .../cmse-nonsecure-entry/generics.stderr | 8 +- .../variadic-ffi-semantic-restrictions.rs | 3 + .../variadic-ffi-semantic-restrictions.stderr | 94 +++++++++++-------- 9 files changed, 88 insertions(+), 49 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 915bae9e5f5a..3e8fddd9954e 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2325,6 +2325,11 @@ impl FnSig { } } } + + /// The span of the header's extern, or where to insert it if empty. + pub fn extern_span(&self) -> Span { + self.header.ext.span().unwrap_or(self.safety_span().shrink_to_hi()) + } } /// A constraint on an associated item. diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 1aa38a1beda3..8dcf3e3aa388 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -57,8 +57,6 @@ ast_passes_auto_super_lifetime = auto traits cannot have super traits or lifetim .label = {ast_passes_auto_super_lifetime} .suggestion = remove the super traits or lifetime bounds -ast_passes_bad_c_variadic = defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - ast_passes_body_in_extern = incorrect `{$kind}` inside `extern` block .cannot_have = cannot have a body .invalid = the invalid body @@ -68,11 +66,16 @@ ast_passes_bound_in_context = bounds on `type`s in {$ctx} have no effect ast_passes_c_variadic_associated_function = associated functions cannot have a C variable argument list +ast_passes_c_variadic_bad_extern = `...` is not supported for `extern "{$abi}"` functions + .label = `extern "{$abi}"` because of this + .help = only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + ast_passes_c_variadic_must_be_unsafe = functions with a C variable argument list must be unsafe .suggestion = add the `unsafe` keyword to this definition ast_passes_c_variadic_no_extern = `...` is not supported for non-extern functions + .help = only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list ast_passes_const_and_c_variadic = functions cannot be both `const` and C-variadic .const = `const` because of this diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 7b32778ddf04..a6ef89b553db 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -698,7 +698,6 @@ impl<'a> AstValidator<'a> { FnCtxt::Foreign => return, FnCtxt::Free => match sig.header.ext { Extern::Implicit(_) => { - // Implicitly defaults to C. if !matches!(sig.header.safety, Safety::Unsafe(_)) { self.dcx().emit_err(errors::CVariadicMustBeUnsafe { span: variadic_param.span, @@ -708,7 +707,11 @@ impl<'a> AstValidator<'a> { } Extern::Explicit(StrLit { symbol_unescaped, .. }, _) => { if !matches!(symbol_unescaped, sym::C | sym::C_dash_unwind) { - self.dcx().emit_err(errors::BadCVariadic { span: variadic_param.span }); + self.dcx().emit_err(errors::CVariadicBadExtern { + span: variadic_param.span, + abi: symbol_unescaped, + extern_span: sig.extern_span(), + }); } if !matches!(sig.header.safety, Safety::Unsafe(_)) { diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 946a22d2e759..ae805042c549 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -327,6 +327,7 @@ pub(crate) struct CVariadicAssociatedFunction { #[derive(Diagnostic)] #[diag(ast_passes_c_variadic_no_extern)] +#[help] pub(crate) struct CVariadicNoExtern { #[primary_span] pub span: Span, @@ -348,10 +349,14 @@ pub(crate) struct CVariadicMustBeUnsafe { } #[derive(Diagnostic)] -#[diag(ast_passes_bad_c_variadic)] -pub(crate) struct BadCVariadic { +#[diag(ast_passes_c_variadic_bad_extern)] +#[help] +pub(crate) struct CVariadicBadExtern { #[primary_span] pub span: Span, + pub abi: Symbol, + #[label] + pub extern_span: Span, } #[derive(Diagnostic)] diff --git a/tests/ui/c-variadic/issue-86053-1.stderr b/tests/ui/c-variadic/issue-86053-1.stderr index eb08370b4f80..adbc04d3a65a 100644 --- a/tests/ui/c-variadic/issue-86053-1.stderr +++ b/tests/ui/c-variadic/issue-86053-1.stderr @@ -51,6 +51,8 @@ error: `...` is not supported for non-extern functions | LL | self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize ) { | ^^^ + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error[E0412]: cannot find type `F` in this scope --> $DIR/issue-86053-1.rs:11:48 diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs index 4ebc2bbf7612..eb6f4a323655 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs @@ -67,6 +67,6 @@ extern "cmse-nonsecure-entry" fn wrapped_trait_object(x: WrapperTransparent) -> } unsafe extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { - //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention + //~^ ERROR `...` is not supported for `extern "cmse-nonsecure-entry"` functions //~| ERROR requires `va_list` lang_item } diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr index 55921e764ba2..8937def9428c 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr @@ -1,8 +1,12 @@ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +error: `...` is not supported for `extern "cmse-nonsecure-entry"` functions --> $DIR/generics.rs:69:60 | LL | unsafe extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { - | ^^^^^^ + | ----------------------------- ^^^^^^ + | | + | `extern "cmse-nonsecure-entry"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type --> $DIR/generics.rs:30:1 diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs index ff491322efc5..4db056f15a51 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs @@ -9,6 +9,9 @@ fn f1_1(x: isize, ...) {} fn f1_2(...) {} //~^ ERROR `...` is not supported for non-extern functions +unsafe extern "Rust" fn f1_3(...) {} +//~^ ERROR `...` is not supported for `extern "Rust"` functions + extern "C" fn f2_1(x: isize, ...) {} //~^ ERROR functions with a C variable argument list must be unsafe diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr index 98f1d79f8f4e..0cd78318de6a 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr @@ -3,15 +3,29 @@ error: `...` is not supported for non-extern functions | LL | fn f1_1(x: isize, ...) {} | ^^^ + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error: `...` is not supported for non-extern functions --> $DIR/variadic-ffi-semantic-restrictions.rs:9:9 | LL | fn f1_2(...) {} | ^^^ + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "Rust"` functions + --> $DIR/variadic-ffi-semantic-restrictions.rs:12:30 + | +LL | unsafe extern "Rust" fn f1_3(...) {} + | ------------- ^^^ + | | + | `extern "Rust"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error: functions with a C variable argument list must be unsafe - --> $DIR/variadic-ffi-semantic-restrictions.rs:12:30 + --> $DIR/variadic-ffi-semantic-restrictions.rs:15:30 | LL | extern "C" fn f2_1(x: isize, ...) {} | ^^^ @@ -22,7 +36,7 @@ LL | unsafe extern "C" fn f2_1(x: isize, ...) {} | ++++++ error: functions with a C variable argument list must be unsafe - --> $DIR/variadic-ffi-semantic-restrictions.rs:15:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:18:20 | LL | extern "C" fn f2_2(...) {} | ^^^ @@ -33,13 +47,13 @@ LL | unsafe extern "C" fn f2_2(...) {} | ++++++ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:18:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:21:20 | LL | extern "C" fn f2_3(..., x: isize) {} | ^^^ error: functions with a C variable argument list must be unsafe - --> $DIR/variadic-ffi-semantic-restrictions.rs:21:30 + --> $DIR/variadic-ffi-semantic-restrictions.rs:24:30 | LL | extern "C" fn f3_1(x: isize, ...) {} | ^^^ @@ -50,7 +64,7 @@ LL | unsafe extern "C" fn f3_1(x: isize, ...) {} | ++++++ error: functions with a C variable argument list must be unsafe - --> $DIR/variadic-ffi-semantic-restrictions.rs:24:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:27:20 | LL | extern "C" fn f3_2(...) {} | ^^^ @@ -61,25 +75,25 @@ LL | unsafe extern "C" fn f3_2(...) {} | ++++++ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:27:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:30:20 | LL | extern "C" fn f3_3(..., x: isize) {} | ^^^ error: functions cannot be both `const` and C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:30:1 + --> $DIR/variadic-ffi-semantic-restrictions.rs:33:1 | LL | const unsafe extern "C" fn f4_1(x: isize, ...) {} | ^^^^^ `const` because of this ^^^ C-variadic because of this error: functions cannot be both `const` and C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:34:1 + --> $DIR/variadic-ffi-semantic-restrictions.rs:37:1 | LL | const extern "C" fn f4_2(x: isize, ...) {} | ^^^^^ `const` because of this ^^^ C-variadic because of this error: functions with a C variable argument list must be unsafe - --> $DIR/variadic-ffi-semantic-restrictions.rs:34:36 + --> $DIR/variadic-ffi-semantic-restrictions.rs:37:36 | LL | const extern "C" fn f4_2(x: isize, ...) {} | ^^^ @@ -90,19 +104,19 @@ LL | const unsafe extern "C" fn f4_2(x: isize, ...) {} | ++++++ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:39:26 + --> $DIR/variadic-ffi-semantic-restrictions.rs:42:26 | LL | const extern "C" fn f4_3(..., x: isize, ...) {} | ^^^ error: functions cannot be both `const` and C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:39:1 + --> $DIR/variadic-ffi-semantic-restrictions.rs:42:1 | LL | const extern "C" fn f4_3(..., x: isize, ...) {} | ^^^^^ `const` because of this ^^^ C-variadic because of this error: functions with a C variable argument list must be unsafe - --> $DIR/variadic-ffi-semantic-restrictions.rs:39:41 + --> $DIR/variadic-ffi-semantic-restrictions.rs:42:41 | LL | const extern "C" fn f4_3(..., x: isize, ...) {} | ^^^ @@ -113,49 +127,49 @@ LL | const unsafe extern "C" fn f4_3(..., x: isize, ...) {} | ++++++ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:45:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:48:13 | LL | fn e_f2(..., x: isize); | ^^^ error: associated functions cannot have a C variable argument list - --> $DIR/variadic-ffi-semantic-restrictions.rs:52:23 + --> $DIR/variadic-ffi-semantic-restrictions.rs:55:23 | LL | fn i_f1(x: isize, ...) {} | ^^^ error: associated functions cannot have a C variable argument list - --> $DIR/variadic-ffi-semantic-restrictions.rs:54:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:57:13 | LL | fn i_f2(...) {} | ^^^ -error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:56:13 - | -LL | fn i_f3(..., x: isize, ...) {} - | ^^^ - -error: associated functions cannot have a C variable argument list - --> $DIR/variadic-ffi-semantic-restrictions.rs:56:28 - | -LL | fn i_f3(..., x: isize, ...) {} - | ^^^ - error: `...` must be the last argument of a C-variadic function --> $DIR/variadic-ffi-semantic-restrictions.rs:59:13 | -LL | fn i_f4(..., x: isize, ...) {} +LL | fn i_f3(..., x: isize, ...) {} | ^^^ error: associated functions cannot have a C variable argument list --> $DIR/variadic-ffi-semantic-restrictions.rs:59:28 | +LL | fn i_f3(..., x: isize, ...) {} + | ^^^ + +error: `...` must be the last argument of a C-variadic function + --> $DIR/variadic-ffi-semantic-restrictions.rs:62:13 + | +LL | fn i_f4(..., x: isize, ...) {} + | ^^^ + +error: associated functions cannot have a C variable argument list + --> $DIR/variadic-ffi-semantic-restrictions.rs:62:28 + | LL | fn i_f4(..., x: isize, ...) {} | ^^^ error: functions cannot be both `const` and C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:62:5 + --> $DIR/variadic-ffi-semantic-restrictions.rs:65:5 | LL | const fn i_f5(x: isize, ...) {} | ^^^^^ ^^^ C-variadic because of this @@ -163,49 +177,49 @@ LL | const fn i_f5(x: isize, ...) {} | `const` because of this error: associated functions cannot have a C variable argument list - --> $DIR/variadic-ffi-semantic-restrictions.rs:62:29 + --> $DIR/variadic-ffi-semantic-restrictions.rs:65:29 | LL | const fn i_f5(x: isize, ...) {} | ^^^ error: associated functions cannot have a C variable argument list - --> $DIR/variadic-ffi-semantic-restrictions.rs:69:23 + --> $DIR/variadic-ffi-semantic-restrictions.rs:72:23 | LL | fn t_f1(x: isize, ...) {} | ^^^ error: associated functions cannot have a C variable argument list - --> $DIR/variadic-ffi-semantic-restrictions.rs:71:23 + --> $DIR/variadic-ffi-semantic-restrictions.rs:74:23 | LL | fn t_f2(x: isize, ...); | ^^^ error: associated functions cannot have a C variable argument list - --> $DIR/variadic-ffi-semantic-restrictions.rs:73:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:76:13 | LL | fn t_f3(...) {} | ^^^ error: associated functions cannot have a C variable argument list - --> $DIR/variadic-ffi-semantic-restrictions.rs:75:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:78:13 | LL | fn t_f4(...); | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:77:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:80:13 | LL | fn t_f5(..., x: isize) {} | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:79:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:82:13 | LL | fn t_f6(..., x: isize); | ^^^ error[E0493]: destructor of `VaListImpl<'_>` cannot be evaluated at compile-time - --> $DIR/variadic-ffi-semantic-restrictions.rs:30:43 + --> $DIR/variadic-ffi-semantic-restrictions.rs:33:43 | LL | const unsafe extern "C" fn f4_1(x: isize, ...) {} | ^^^ - value is dropped here @@ -213,7 +227,7 @@ LL | const unsafe extern "C" fn f4_1(x: isize, ...) {} | the destructor for this type cannot be evaluated in constant functions error[E0493]: destructor of `VaListImpl<'_>` cannot be evaluated at compile-time - --> $DIR/variadic-ffi-semantic-restrictions.rs:34:36 + --> $DIR/variadic-ffi-semantic-restrictions.rs:37:36 | LL | const extern "C" fn f4_2(x: isize, ...) {} | ^^^ - value is dropped here @@ -221,13 +235,13 @@ LL | const extern "C" fn f4_2(x: isize, ...) {} | the destructor for this type cannot be evaluated in constant functions error[E0493]: destructor of `VaListImpl<'_>` cannot be evaluated at compile-time - --> $DIR/variadic-ffi-semantic-restrictions.rs:62:29 + --> $DIR/variadic-ffi-semantic-restrictions.rs:65:29 | LL | const fn i_f5(x: isize, ...) {} | ^^^ - value is dropped here | | | the destructor for this type cannot be evaluated in constant functions -error: aborting due to 32 previous errors +error: aborting due to 33 previous errors For more information about this error, try `rustc --explain E0493`. From cbacd00f106778830803ad2e2364518f06ff9078 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 7 Jun 2025 22:56:22 +0200 Subject: [PATCH 323/682] allow `#[rustc_align_static(N)]` on `static`s We need a different attribute than `rustc_align` because unstable attributes are tied to their feature (we can't have two unstable features use the same unstable attribute). Otherwise this uses all of the same infrastructure as `#[rustc_align]`. --- .../src/attributes/codegen_attrs.rs | 1 + .../rustc_attr_parsing/src/attributes/repr.rs | 27 +++++++++++ compiler/rustc_attr_parsing/src/context.rs | 3 +- compiler/rustc_codegen_gcc/src/consts.rs | 2 + compiler/rustc_codegen_llvm/src/consts.rs | 2 + .../rustc_const_eval/src/interpret/memory.rs | 1 + .../rustc_const_eval/src/interpret/util.rs | 11 ++++- compiler/rustc_feature/src/builtin_attrs.rs | 1 + compiler/rustc_feature/src/unstable.rs | 2 + .../rustc_middle/src/mir/interpret/mod.rs | 11 ++++- compiler/rustc_passes/messages.ftl | 4 ++ compiler/rustc_passes/src/check_attr.rs | 8 +++- compiler/rustc_passes/src/errors.rs | 9 ++++ compiler/rustc_span/src/symbol.rs | 2 + src/tools/miri/tests/pass/static_align.rs | 14 ++++++ tests/codegen-llvm/align-static.rs | 31 +++++++++++++ tests/ui/attributes/malformed-static-align.rs | 17 +++++++ .../attributes/malformed-static-align.stderr | 45 +++++++++++++++++++ .../feature-gate-static_align.rs | 11 +++++ .../feature-gate-static_align.stderr | 23 ++++++++++ tests/ui/static/static-align.rs | 26 +++++++++++ 21 files changed, 246 insertions(+), 5 deletions(-) create mode 100644 src/tools/miri/tests/pass/static_align.rs create mode 100644 tests/codegen-llvm/align-static.rs create mode 100644 tests/ui/attributes/malformed-static-align.rs create mode 100644 tests/ui/attributes/malformed-static-align.stderr create mode 100644 tests/ui/feature-gates/feature-gate-static_align.rs create mode 100644 tests/ui/feature-gates/feature-gate-static_align.stderr create mode 100644 tests/ui/static/static-align.rs diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index ffdacff71521..d5d51f2e79ab 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -218,6 +218,7 @@ impl AttributeParser for NakedParser { sym::rustc_std_internal_symbol, // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity sym::rustc_align, + sym::rustc_align_static, // obviously compatible with self sym::naked, // documentation diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs index 23aabd155976..0330e2515c7d 100644 --- a/compiler/rustc_attr_parsing/src/attributes/repr.rs +++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs @@ -331,3 +331,30 @@ impl AttributeParser for AlignParser { Some(AttributeKind::Align { align, span }) } } + +#[derive(Default)] +pub(crate) struct AlignStaticParser(AlignParser); + +impl AlignStaticParser { + const PATH: &'static [Symbol] = &[sym::rustc_align_static]; + const TEMPLATE: AttributeTemplate = AlignParser::TEMPLATE; + + fn parse<'c, S: Stage>( + &mut self, + cx: &'c mut AcceptContext<'_, '_, S>, + args: &'c ArgParser<'_>, + ) { + self.0.parse(cx, args) + } +} + +impl AttributeParser for AlignStaticParser { + const ATTRIBUTES: AcceptMapping = &[(Self::PATH, Self::TEMPLATE, Self::parse)]; + const ALLOWED_TARGETS: AllowedTargets = + AllowedTargets::AllowList(&[Allow(Target::Static), Allow(Target::ForeignStatic)]); + + fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option { + let (align, span) = self.0.0?; + Some(AttributeKind::Align { align, span }) + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 7f5b810f244f..1fb0660f5349 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -47,7 +47,7 @@ use crate::attributes::proc_macro_attrs::{ ProcMacroAttributeParser, ProcMacroDeriveParser, ProcMacroParser, RustcBuiltinMacroParser, }; use crate::attributes::prototype::CustomMirParser; -use crate::attributes::repr::{AlignParser, ReprParser}; +use crate::attributes::repr::{AlignParser, AlignStaticParser, ReprParser}; use crate::attributes::rustc_internal::{ RustcLayoutScalarValidRangeEnd, RustcLayoutScalarValidRangeStart, RustcObjectLifetimeDefaultParser, @@ -149,6 +149,7 @@ attribute_parsers!( pub(crate) static ATTRIBUTE_PARSERS = [ // tidy-alphabetical-start AlignParser, + AlignStaticParser, BodyStabilityParser, ConfusablesParser, ConstStabilityParser, diff --git a/compiler/rustc_codegen_gcc/src/consts.rs b/compiler/rustc_codegen_gcc/src/consts.rs index 619277eba8b8..7fe8fc122b3c 100644 --- a/compiler/rustc_codegen_gcc/src/consts.rs +++ b/compiler/rustc_codegen_gcc/src/consts.rs @@ -81,6 +81,8 @@ impl<'gcc, 'tcx> StaticCodegenMethods for CodegenCx<'gcc, 'tcx> { if global.to_rvalue().get_type() != val_llty { global.to_rvalue().set_type(val_llty); } + + // NOTE: Alignment from attributes has already been applied to the allocation. set_global_alignment(self, global, alloc.align); global.global_set_initializer_rvalue(value); diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 9ec7b0f80aee..dc9bb743560d 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -452,6 +452,8 @@ impl<'ll> CodegenCx<'ll, '_> { self.statics_to_rauw.borrow_mut().push((g, new_g)); new_g }; + + // NOTE: Alignment from attributes has already been applied to the allocation. set_global_alignment(self, g, alloc.align); llvm::set_initializer(g, v); diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 2c1e5087e1c6..67e1f6b2fbd2 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -953,6 +953,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // # Global allocations if let Some(global_alloc) = self.tcx.try_get_global_alloc(id) { + // NOTE: `static` alignment from attributes has already been applied to the allocation. let (size, align) = global_alloc.size_and_align(*self.tcx, self.typing_env); let mutbl = global_alloc.mutability(*self.tcx, self.typing_env); let kind = match global_alloc { diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index 72bee3454065..f667823723c0 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -1,6 +1,6 @@ use rustc_hir::def_id::LocalDefId; use rustc_middle::mir; -use rustc_middle::mir::interpret::{AllocInit, Allocation, InterpResult, Pointer}; +use rustc_middle::mir::interpret::{AllocInit, Allocation, GlobalAlloc, InterpResult, Pointer}; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{TyCtxt, TypeVisitable, TypeVisitableExt}; use tracing::debug; @@ -38,7 +38,14 @@ pub(crate) fn create_static_alloc<'tcx>( static_def_id: LocalDefId, layout: TyAndLayout<'tcx>, ) -> InterpResult<'tcx, MPlaceTy<'tcx>> { - let alloc = Allocation::try_new(layout.size, layout.align.abi, AllocInit::Uninit, ())?; + // Inherit size and align from the `GlobalAlloc::Static` so we can avoid duplicating + // the alignment attribute logic. + let (size, align) = + GlobalAlloc::Static(static_def_id.into()).size_and_align(*ecx.tcx, ecx.typing_env); + assert_eq!(size, layout.size); + assert!(align >= layout.align.abi); + + let alloc = Allocation::try_new(size, align, AllocInit::Uninit, ())?; let alloc_id = ecx.tcx.reserve_and_set_static_alloc(static_def_id.into()); assert_eq!(ecx.machine.static_root_ids, None); ecx.machine.static_root_ids = Some((alloc_id, static_def_id)); diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index e81003b18972..129ab7eccb57 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -621,6 +621,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity gated!(rustc_align, Normal, template!(List: &["alignment"]), DuplicatesOk, EncodeCrossCrate::No, fn_align, experimental!(rustc_align)), + gated!(rustc_align_static, Normal, template!(List: &["alignment"]), DuplicatesOk, EncodeCrossCrate::No, static_align, experimental!(rustc_align_static)), ungated!( unsafe(Edition2024) export_name, Normal, template!(NameValueStr: "name", "https://doc.rust-lang.org/reference/abi.html#the-export_name-attribute"), diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 4f35bf63a1a4..93e5588146e1 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -632,6 +632,8 @@ declare_features! ( (unstable, simd_ffi, "1.0.0", Some(27731)), /// Allows specialization of implementations (RFC 1210). (incomplete, specialization, "1.7.0", Some(31844)), + /// Allows using `#[rustc_align_static(...)]` on static items. + (unstable, static_align, "CURRENT_RUSTC_VERSION", Some(146177)), /// Allows attributes on expressions and non-item statements. (unstable, stmt_expr_attributes, "1.6.0", Some(15701)), /// Allows lints part of the strict provenance effort. diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index bed99a4ff2ab..9762e0f21da9 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -386,7 +386,16 @@ impl<'tcx> GlobalAlloc<'tcx> { .expect("statics should not have generic parameters"); let layout = tcx.layout_of(typing_env.as_query_input(ty)).unwrap(); assert!(layout.is_sized()); - (layout.size, layout.align.abi) + + // Take over-alignment from attributes into account. + let align = match tcx.codegen_fn_attrs(def_id).alignment { + Some(align_from_attribute) => { + Ord::max(align_from_attribute, layout.align.abi) + } + None => layout.align.abi, + }; + + (layout.size, align) } } GlobalAlloc::Memory(alloc) => { diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index afd08319738d..ab7885905a6e 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -501,6 +501,10 @@ passes_repr_align_should_be_align = `#[repr(align(...))]` is not supported on {$item} .help = use `#[rustc_align(...)]` instead +passes_repr_align_should_be_align_static = + `#[repr(align(...))]` is not supported on {$item} + .help = use `#[rustc_align_static(...)]` instead + passes_repr_conflicting = conflicting representation hints diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 2cd4830b5d93..10585e7b9208 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1602,12 +1602,18 @@ impl<'tcx> CheckAttrVisitor<'tcx> { ReprAttr::ReprAlign(align) => { match target { Target::Struct | Target::Union | Target::Enum => {} - Target::Fn | Target::Method(_) => { + Target::Fn | Target::Method(_) if self.tcx.features().fn_align() => { self.dcx().emit_err(errors::ReprAlignShouldBeAlign { span: *repr_span, item: target.plural_name(), }); } + Target::Static if self.tcx.features().static_align() => { + self.dcx().emit_err(errors::ReprAlignShouldBeAlignStatic { + span: *repr_span, + item: target.plural_name(), + }); + } _ => { self.dcx().emit_err(errors::AttrApplication::StructEnumUnion { hint_span: *repr_span, diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 23dcabef1a17..2da4b6f52cf2 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1609,6 +1609,15 @@ pub(crate) struct ReprAlignShouldBeAlign { pub item: &'static str, } +#[derive(Diagnostic)] +#[diag(passes_repr_align_should_be_align_static)] +pub(crate) struct ReprAlignShouldBeAlignStatic { + #[primary_span] + #[help] + pub span: Span, + pub item: &'static str, +} + #[derive(Diagnostic)] #[diag(passes_custom_mir_phase_requires_dialect)] pub(crate) struct CustomMirPhaseRequiresDialect { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index e5108d8b7e92..ac933b206086 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1846,6 +1846,7 @@ symbols! { rustc_abi, // FIXME(#82232, #143834): temporary name to mitigate `#[align]` nameres ambiguity rustc_align, + rustc_align_static, rustc_allocator, rustc_allocator_zeroed, rustc_allocator_zeroed_variant, @@ -2097,6 +2098,7 @@ symbols! { staged_api, start, state, + static_align, static_in_const, static_nobundle, static_recursion, diff --git a/src/tools/miri/tests/pass/static_align.rs b/src/tools/miri/tests/pass/static_align.rs new file mode 100644 index 000000000000..f292f028568b --- /dev/null +++ b/src/tools/miri/tests/pass/static_align.rs @@ -0,0 +1,14 @@ +#![feature(static_align)] + +// When a static uses `align(N)`, its address should be a multiple of `N`. + +#[rustc_align_static(256)] +static FOO: u64 = 0; + +#[rustc_align_static(512)] +static BAR: u64 = 0; + +fn main() { + assert!(core::ptr::from_ref(&FOO).addr().is_multiple_of(256)); + assert!(core::ptr::from_ref(&BAR).addr().is_multiple_of(512)); +} diff --git a/tests/codegen-llvm/align-static.rs b/tests/codegen-llvm/align-static.rs new file mode 100644 index 000000000000..53db998919af --- /dev/null +++ b/tests/codegen-llvm/align-static.rs @@ -0,0 +1,31 @@ +//@ compile-flags: -C no-prepopulate-passes -Z mir-opt-level=0 + +#![crate_type = "lib"] +#![feature(static_align)] + +// CHECK: @STATIC_ALIGN = +// CHECK-SAME: align 16 +#[no_mangle] +#[rustc_align_static(16)] +pub static STATIC_ALIGN: u64 = 0; + +// CHECK: @ALIGN_SPECIFIED_TWICE_1 = +// CHECK-SAME: align 64 +#[no_mangle] +#[rustc_align_static(32)] +#[rustc_align_static(64)] +pub static ALIGN_SPECIFIED_TWICE_1: u64 = 0; + +// CHECK: @ALIGN_SPECIFIED_TWICE_2 = +// CHECK-SAME: align 128 +#[no_mangle] +#[rustc_align_static(128)] +#[rustc_align_static(32)] +pub static ALIGN_SPECIFIED_TWICE_2: u64 = 0; + +// CHECK: @ALIGN_SPECIFIED_TWICE_3 = +// CHECK-SAME: align 256 +#[no_mangle] +#[rustc_align_static(32)] +#[rustc_align_static(256)] +pub static ALIGN_SPECIFIED_TWICE_3: u64 = 0; diff --git a/tests/ui/attributes/malformed-static-align.rs b/tests/ui/attributes/malformed-static-align.rs new file mode 100644 index 000000000000..305d8acf8af2 --- /dev/null +++ b/tests/ui/attributes/malformed-static-align.rs @@ -0,0 +1,17 @@ +#![feature(static_align)] +#![crate_type = "lib"] + +#[rustc_align_static = 16] //~ ERROR malformed `rustc_align_static` attribute input +static S1: () = (); + +#[rustc_align_static("hello")] //~ ERROR invalid alignment value: not an unsuffixed integer +static S2: () = (); + +#[rustc_align_static(0)] //~ ERROR invalid alignment value: not a power of two +static S3: () = (); + +#[repr(align(16))] //~ ERROR `#[repr(align(...))]` is not supported on static +static S4: () = (); + +#[rustc_align_static(16)] //~ ERROR `#[rustc_align_static]` attribute cannot be used on structs +struct Struct1; diff --git a/tests/ui/attributes/malformed-static-align.stderr b/tests/ui/attributes/malformed-static-align.stderr new file mode 100644 index 000000000000..35f654d3990f --- /dev/null +++ b/tests/ui/attributes/malformed-static-align.stderr @@ -0,0 +1,45 @@ +error[E0539]: malformed `rustc_align_static` attribute input + --> $DIR/malformed-static-align.rs:4:1 + | +LL | #[rustc_align_static = 16] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expected this to be a list + | help: must be of the form: `#[rustc_align_static()]` + +error[E0589]: invalid alignment value: not an unsuffixed integer + --> $DIR/malformed-static-align.rs:7:22 + | +LL | #[rustc_align_static("hello")] + | ^^^^^^^ + +error[E0589]: invalid alignment value: not a power of two + --> $DIR/malformed-static-align.rs:10:22 + | +LL | #[rustc_align_static(0)] + | ^ + +error: `#[rustc_align_static]` attribute cannot be used on structs + --> $DIR/malformed-static-align.rs:16:1 + | +LL | #[rustc_align_static(16)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_align_static]` can be applied to statics and foreign statics + +error: `#[repr(align(...))]` is not supported on statics + --> $DIR/malformed-static-align.rs:13:8 + | +LL | #[repr(align(16))] + | ^^^^^^^^^ + | +help: use `#[rustc_align_static(...)]` instead + --> $DIR/malformed-static-align.rs:13:8 + | +LL | #[repr(align(16))] + | ^^^^^^^^^ + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0539, E0589. +For more information about an error, try `rustc --explain E0539`. diff --git a/tests/ui/feature-gates/feature-gate-static_align.rs b/tests/ui/feature-gates/feature-gate-static_align.rs new file mode 100644 index 000000000000..4d8f0e18d94c --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-static_align.rs @@ -0,0 +1,11 @@ +#![crate_type = "lib"] + +#[rustc_align_static(16)] +//~^ ERROR the `#[rustc_align_static]` attribute is an experimental feature +static REQUIRES_ALIGNMENT: u64 = 0; + +extern "C" { + #[rustc_align_static(16)] + //~^ ERROR the `#[rustc_align_static]` attribute is an experimental feature + static FOREIGN_STATIC: u32; +} diff --git a/tests/ui/feature-gates/feature-gate-static_align.stderr b/tests/ui/feature-gates/feature-gate-static_align.stderr new file mode 100644 index 000000000000..b45fcdefc9cd --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-static_align.stderr @@ -0,0 +1,23 @@ +error[E0658]: the `#[rustc_align_static]` attribute is an experimental feature + --> $DIR/feature-gate-static_align.rs:3:1 + | +LL | #[rustc_align_static(16)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #146177 for more information + = help: add `#![feature(static_align)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: the `#[rustc_align_static]` attribute is an experimental feature + --> $DIR/feature-gate-static_align.rs:8:5 + | +LL | #[rustc_align_static(16)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #146177 for more information + = help: add `#![feature(static_align)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/static/static-align.rs b/tests/ui/static/static-align.rs new file mode 100644 index 000000000000..93241db09f94 --- /dev/null +++ b/tests/ui/static/static-align.rs @@ -0,0 +1,26 @@ +//@ run-pass +#![feature(static_align)] + +#[rustc_align_static(64)] +static A: u8 = 0; + +#[rustc_align_static(64)] +static B: u8 = 0; + +#[rustc_align_static(128)] +#[no_mangle] +static EXPORTED: u64 = 0; + +unsafe extern "C" { + #[rustc_align_static(128)] + #[link_name = "EXPORTED"] + static C: u64; +} + +fn main() { + assert!(core::ptr::from_ref(&A).addr().is_multiple_of(64)); + assert!(core::ptr::from_ref(&B).addr().is_multiple_of(64)); + + assert!(core::ptr::from_ref(&EXPORTED).addr().is_multiple_of(128)); + unsafe { assert!(core::ptr::from_ref(&C).addr().is_multiple_of(128)) }; +} From ce2f518b5393c48370f67725a4e15573b49f2c7d Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 9 Sep 2025 21:57:19 +0300 Subject: [PATCH 324/682] Upgrade rustc crates and handle changes to canonicalization They have to do with diagnostics, we could probably not support them but we will also someday want good diagnostics. The code is mostly copied from rustc. --- src/tools/rust-analyzer/Cargo.lock | 45 +++---- src/tools/rust-analyzer/Cargo.toml | 16 +-- .../infer/canonical/canonicalizer.rs | 41 +++--- .../src/next_solver/infer/canonical/mod.rs | 61 +++++---- .../hir-ty/src/next_solver/infer/context.rs | 8 ++ .../hir-ty/src/next_solver/infer/mod.rs | 8 ++ .../next_solver/infer/snapshot/undo_log.rs | 4 +- .../src/next_solver/infer/type_variable.rs | 117 ++++++++++++++++++ .../hir-ty/src/next_solver/infer/unify_key.rs | 3 + .../crates/hir-ty/src/next_solver/mapping.rs | 46 +++---- .../crates/hir-ty/src/next_solver/solver.rs | 12 +- .../crates/hir-ty/src/next_solver/util.rs | 17 ++- 12 files changed, 259 insertions(+), 119 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 344e6d101fe3..b70b89ea543d 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1863,9 +1863,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_abi" -version = "0.126.0" +version = "0.128.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6789d94fb3e6e30d62f55e99a321ba63484a8bb3b4ead338687c9ddc282d28" +checksum = "8da95e732b424802b1f043ab4007c78a0fc515ab249587abbea4634bf5fdce9a" dependencies = [ "bitflags 2.9.1", "ra-ap-rustc_hashes", @@ -1875,24 +1875,24 @@ dependencies = [ [[package]] name = "ra-ap-rustc_ast_ir" -version = "0.126.0" +version = "0.128.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaab80bda0f05e9842e3afb7779b0bad0a4b54e0f7ba6deb5705dcf86482811d" +checksum = "3838d9d7a3a5cdc511cfb6ad78740ce532f75a2366d3fc3b9853ea1b5c872779" [[package]] name = "ra-ap-rustc_hashes" -version = "0.126.0" +version = "0.128.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64bd405e538102b5f699241794b2eefee39d5414c0e4bc72435e91430c51f905" +checksum = "bdc8995d268d3bb4ece910f575ea5a063d6003e193ec155d15703b65882d53fb" dependencies = [ "rustc-stable-hash", ] [[package]] name = "ra-ap-rustc_index" -version = "0.126.0" +version = "0.128.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521621e271aa03b8433dad5981838278d6cfd7d2d8c9f4eb6d427f1d671f90fc" +checksum = "ed0ccdf6e5627c6c3e54e571e52ce0bc8b94d5f0b94b7460269ca68a4706be69" dependencies = [ "ra-ap-rustc_index_macros", "smallvec", @@ -1900,9 +1900,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index_macros" -version = "0.126.0" +version = "0.128.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245e30f2e1fef258913cc548b36f575549c8af31cbc4649929d21deda96ceeb7" +checksum = "bd28f42362b5c9fb9b8766c3189df02a402b13363600c6885e11027889f03ee6" dependencies = [ "proc-macro2", "quote", @@ -1911,9 +1911,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_lexer" -version = "0.126.0" +version = "0.128.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82681f924500e888c860e60ed99e9bf702a219a69374f59116c4261525a2157" +checksum = "f1c31a82f091b910a27ee53a86a9af28a2df10c3484e2f1bbfe70633aa84dee9" dependencies = [ "memchr", "unicode-properties", @@ -1922,9 +1922,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_next_trait_solver" -version = "0.126.0" +version = "0.128.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9ce51f2431fbdc7fabd2d957522b6e27f41f68ec2af74b52a6f4116352ce1a" +checksum = "f8cac6c2b5a8924209d4ca682cbc507252c58a664911e0ef463c112882ba6f72" dependencies = [ "derive-where", "ra-ap-rustc_index", @@ -1935,9 +1935,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_parse_format" -version = "0.126.0" +version = "0.128.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc85ef3fdb6c084bde84857d8948dc66b752129dc8417a8614ce490e99a143f" +checksum = "a085a1cf902dcca8abbc537faaef154bbccbbb51850f779ce5484ae3782b5d8f" dependencies = [ "ra-ap-rustc_lexer", "rustc-literal-escaper 0.0.5", @@ -1945,9 +1945,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_pattern_analysis" -version = "0.126.0" +version = "0.128.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cd81eccf33d9528905d4e5abaa254b3129a6405d6c5f123fed9b73a3d217f35" +checksum = "8ba32e3985367bc34856b41c7604133649d4a367eb5d7bdf50623025731459d8" dependencies = [ "ra-ap-rustc_index", "rustc-hash 2.1.1", @@ -1958,10 +1958,11 @@ dependencies = [ [[package]] name = "ra-ap-rustc_type_ir" -version = "0.126.0" +version = "0.128.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11cb0da02853698d9c89e1d1c01657b9969752befd56365e8899d4310e52b373" +checksum = "9c9911d72f75d85d21fe88374d7bcec94f2200feffb7234108a24cc3da7c3591" dependencies = [ + "arrayvec", "bitflags 2.9.1", "derive-where", "ena", @@ -1977,9 +1978,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_type_ir_macros" -version = "0.126.0" +version = "0.128.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc93adeb52c483ede13bee6680466458218243ab479c04fb71bb53925a6e0ff" +checksum = "22f539b87991683ce17cc52e62600fdf2b4a8af43952db30387edc1a576d3b43" dependencies = [ "proc-macro2", "quote", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index f325027ee587..c5ffad544a6b 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -89,14 +89,14 @@ vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" } edition = { path = "./crates/edition", version = "0.0.0" } -ra-ap-rustc_lexer = { version = "0.126", default-features = false } -ra-ap-rustc_parse_format = { version = "0.126", default-features = false } -ra-ap-rustc_index = { version = "0.126", default-features = false } -ra-ap-rustc_abi = { version = "0.126", default-features = false } -ra-ap-rustc_pattern_analysis = { version = "0.126", default-features = false } -ra-ap-rustc_ast_ir = { version = "0.126", default-features = false } -ra-ap-rustc_type_ir = { version = "0.126", default-features = false } -ra-ap-rustc_next_trait_solver = { version = "0.126", default-features = false } +ra-ap-rustc_lexer = { version = "0.128", default-features = false } +ra-ap-rustc_parse_format = { version = "0.128", default-features = false } +ra-ap-rustc_index = { version = "0.128", default-features = false } +ra-ap-rustc_abi = { version = "0.128", default-features = false } +ra-ap-rustc_pattern_analysis = { version = "0.128", default-features = false } +ra-ap-rustc_ast_ir = { version = "0.128", default-features = false } +ra-ap-rustc_type_ir = { version = "0.128", default-features = false } +ra-ap-rustc_next_trait_solver = { version = "0.128", default-features = false } # local crates that aren't published to crates.io. These should not have versions. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs index 5d11525cd199..ffb9c076fa06 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs @@ -10,9 +10,8 @@ use rustc_index::Idx; use rustc_type_ir::InferTy::{self, FloatVar, IntVar, TyVar}; use rustc_type_ir::inherent::{Const as _, IntoKind as _, Region as _, SliceLike, Ty as _}; use rustc_type_ir::{ - BoundVar, CanonicalQueryInput, CanonicalTyVarKind, DebruijnIndex, Flags, InferConst, - RegionKind, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, - UniverseIndex, + BoundVar, CanonicalQueryInput, DebruijnIndex, Flags, InferConst, RegionKind, TyVid, TypeFlags, + TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, UniverseIndex, }; use smallvec::SmallVec; use tracing::debug; @@ -316,6 +315,13 @@ struct Canonicalizer<'cx, 'db> { // Note that indices is only used once `var_values` is big enough to be // heap-allocated. indices: FxHashMap, BoundVar>, + /// Maps each `sub_unification_table_root_var` to the index of the first + /// variable which used it. + /// + /// This means in case two type variables have the same sub relations root, + /// we set the `sub_root` of the second variable to the position of the first. + /// Otherwise the `sub_root` of each type variable is just its own position. + sub_root_lookup_table: FxHashMap, canonicalize_mode: &'cx dyn CanonicalizeMode, needs_canonical_flags: TypeFlags, @@ -384,10 +390,9 @@ impl<'cx, 'db> TypeFolder> for Canonicalizer<'cx, 'db> { // FIXME: perf problem described in #55921. ui = UniverseIndex::ROOT; } - self.canonicalize_ty_var( - CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)), - t, - ) + + let sub_root = self.get_or_insert_sub_root(vid); + self.canonicalize_ty_var(CanonicalVarKind::Ty { ui, sub_root }, t) } } } @@ -395,17 +400,17 @@ impl<'cx, 'db> TypeFolder> for Canonicalizer<'cx, 'db> { TyKind::Infer(IntVar(vid)) => { let nt = self.infcx.opportunistic_resolve_int_var(vid); if nt != t { - self.fold_ty(nt) + return self.fold_ty(nt); } else { - self.canonicalize_ty_var(CanonicalVarKind::Ty(CanonicalTyVarKind::Int), t) + self.canonicalize_ty_var(CanonicalVarKind::Int, t) } } TyKind::Infer(FloatVar(vid)) => { let nt = self.infcx.opportunistic_resolve_float_var(vid); if nt != t { - self.fold_ty(nt) + return self.fold_ty(nt); } else { - self.canonicalize_ty_var(CanonicalVarKind::Ty(CanonicalTyVarKind::Float), t) + self.canonicalize_ty_var(CanonicalVarKind::Float, t) } } @@ -579,6 +584,7 @@ impl<'cx, 'db> Canonicalizer<'cx, 'db> { variables: SmallVec::from_slice(base.variables.as_slice()), query_state, indices: FxHashMap::default(), + sub_root_lookup_table: Default::default(), binder_index: DebruijnIndex::ZERO, }; if canonicalizer.query_state.var_values.spilled() { @@ -673,6 +679,13 @@ impl<'cx, 'db> Canonicalizer<'cx, 'db> { } } + fn get_or_insert_sub_root(&mut self, vid: TyVid) -> BoundVar { + let root_vid = self.infcx.sub_unification_table_root_var(vid); + let idx = + *self.sub_root_lookup_table.entry(root_vid).or_insert_with(|| self.variables.len()); + BoundVar::from(idx) + } + /// Replaces the universe indexes used in `var_values` with their index in /// `query_state.universe_map`. This minimizes the maximum universe used in /// the canonicalized value. @@ -692,9 +705,9 @@ impl<'cx, 'db> Canonicalizer<'cx, 'db> { self.variables .iter() .map(|v| match *v { - CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) => *v, - CanonicalVarKind::Ty(CanonicalTyVarKind::General(u)) => { - CanonicalVarKind::Ty(CanonicalTyVarKind::General(reverse_universe_map[&u])) + CanonicalVarKind::Int | CanonicalVarKind::Float => *v, + CanonicalVarKind::Ty { ui, sub_root } => { + CanonicalVarKind::Ty { ui: reverse_universe_map[&ui], sub_root } } CanonicalVarKind::Region(u) => CanonicalVarKind::Region(reverse_universe_map[&u]), CanonicalVarKind::Const(u) => CanonicalVarKind::Const(reverse_universe_map[&u]), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs index ec41111ed815..8db4320acc9c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs @@ -32,9 +32,10 @@ use crate::next_solver::{ }; use instantiate::CanonicalExt; use rustc_index::IndexVec; +use rustc_type_ir::inherent::IntoKind; use rustc_type_ir::{ - AliasRelationDirection, AliasTyKind, CanonicalTyVarKind, CanonicalVarKind, InferTy, - TypeFoldable, UniverseIndex, Upcast, Variance, + AliasRelationDirection, AliasTyKind, CanonicalVarKind, InferTy, TypeFoldable, UniverseIndex, + Upcast, Variance, inherent::{SliceLike, Ty as _}, relate::{ Relate, TypeRelation, VarianceDiagInfo, @@ -78,27 +79,15 @@ impl<'db> InferCtxt<'db> { .chain((1..=canonical.max_universe.as_u32()).map(|_| self.create_next_universe())) .collect(); - let canonical_inference_vars = - self.instantiate_canonical_vars(canonical.variables, |ui| universes[ui]); - let result = canonical.instantiate(self.interner, &canonical_inference_vars); - (result, canonical_inference_vars) - } - - /// Given the "infos" about the canonical variables from some - /// canonical, creates fresh variables with the same - /// characteristics (see `instantiate_canonical_var` for - /// details). You can then use `instantiate` to instantiate the - /// canonical variable with these inference variables. - fn instantiate_canonical_vars( - &self, - variables: CanonicalVars<'db>, - universe_map: impl Fn(UniverseIndex) -> UniverseIndex, - ) -> CanonicalVarValues<'db> { - CanonicalVarValues { - var_values: self.interner.mk_args_from_iter( - variables.iter().map(|info| self.instantiate_canonical_var(info, &universe_map)), - ), - } + let var_values = CanonicalVarValues::instantiate( + self.interner, + canonical.variables, + |var_values, info| { + self.instantiate_canonical_var(info, &var_values, |ui| universes[ui]) + }, + ); + let result = canonical.instantiate(self.interner, &var_values); + (result, var_values) } /// Given the "info" about a canonical variable, creates a fresh @@ -112,22 +101,28 @@ impl<'db> InferCtxt<'db> { pub fn instantiate_canonical_var( &self, cv_info: CanonicalVarKind>, + previous_var_values: &[GenericArg<'db>], universe_map: impl Fn(UniverseIndex) -> UniverseIndex, ) -> GenericArg<'db> { match cv_info { - CanonicalVarKind::Ty(ty_kind) => { - let ty = match ty_kind { - CanonicalTyVarKind::General(ui) => { - self.next_ty_var_in_universe(universe_map(ui)) + CanonicalVarKind::Ty { ui, sub_root } => { + let vid = self.next_ty_var_id_in_universe(universe_map(ui)); + // If this inference variable is related to an earlier variable + // via subtyping, we need to add that info to the inference context. + if let Some(prev) = previous_var_values.get(sub_root.as_usize()) { + if let TyKind::Infer(InferTy::TyVar(sub_root)) = prev.expect_ty().kind() { + self.sub_unify_ty_vids_raw(vid, sub_root); + } else { + unreachable!() } - - CanonicalTyVarKind::Int => self.next_int_var(), - - CanonicalTyVarKind::Float => self.next_float_var(), - }; - ty.into() + } + Ty::new_var(self.interner, vid).into() } + CanonicalVarKind::Int => self.next_int_var().into(), + + CanonicalVarKind::Float => self.next_float_var().into(), + CanonicalVarKind::PlaceholderTy(PlaceholderTy { universe, bound }) => { let universe_mapped = universe_map(universe); let placeholder_mapped = PlaceholderTy { universe: universe_mapped, bound }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs index 93fd6eeab34e..45ce7e6f6cc7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs @@ -313,4 +313,12 @@ impl<'db> rustc_type_ir::InferCtxtLike for InferCtxt<'db> { fn reset_opaque_types(&self) { let _ = self.take_opaque_types(); } + + fn sub_unification_table_root_var(&self, var: rustc_type_ir::TyVid) -> rustc_type_ir::TyVid { + self.sub_unification_table_root_var(var) + } + + fn sub_unify_ty_vids_raw(&self, a: rustc_type_ir::TyVid, b: rustc_type_ir::TyVid) { + self.sub_unify_ty_vids_raw(a, b); + } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs index 2630f2a8cc4e..ce6c94128732 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs @@ -1019,6 +1019,14 @@ impl<'db> InferCtxt<'db> { } } } + + fn sub_unification_table_root_var(&self, var: rustc_type_ir::TyVid) -> rustc_type_ir::TyVid { + self.inner.borrow_mut().type_variables().sub_unification_table_root_var(var) + } + + fn sub_unify_ty_vids_raw(&self, a: rustc_type_ir::TyVid, b: rustc_type_ir::TyVid) { + self.inner.borrow_mut().type_variables().sub_unify(a, b); + } } /// Helper for [InferCtxt::ty_or_const_infer_var_changed] (see comment on that), currently diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs index 0fa6421f517d..28ae56f4ee70 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs @@ -25,7 +25,7 @@ pub struct Snapshot { pub(crate) enum UndoLog<'db> { DuplicateOpaqueType, OpaqueTypes(OpaqueTypeKey<'db>, Option>), - TypeVariables(sv::UndoLog>>), + TypeVariables(type_variable::UndoLog<'db>), ConstUnificationTable(sv::UndoLog>>), IntUnificationTable(sv::UndoLog>), FloatUnificationTable(sv::UndoLog>), @@ -51,6 +51,8 @@ impl_from! { RegionConstraintCollector(region_constraints::UndoLog<'db>), TypeVariables(sv::UndoLog>>), + TypeVariables(sv::UndoLog>), + TypeVariables(type_variable::UndoLog<'db>), IntUnificationTable(sv::UndoLog>), FloatUnificationTable(sv::UndoLog>), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/type_variable.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/type_variable.rs index 5217308af473..b640039af625 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/type_variable.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/type_variable.rs @@ -17,12 +17,48 @@ use crate::next_solver::SolverDefId; use crate::next_solver::Ty; use crate::next_solver::infer::InferCtxtUndoLogs; +/// Represents a single undo-able action that affects a type inference variable. +#[derive(Clone)] +pub(crate) enum UndoLog<'tcx> { + EqRelation(sv::UndoLog>>), + SubRelation(sv::UndoLog>), +} + +/// Convert from a specific kind of undo to the more general UndoLog +impl<'db> From>>> for UndoLog<'db> { + fn from(l: sv::UndoLog>>) -> Self { + UndoLog::EqRelation(l) + } +} + +/// Convert from a specific kind of undo to the more general UndoLog +impl<'db> From>> for UndoLog<'db> { + fn from(l: sv::UndoLog>) -> Self { + UndoLog::SubRelation(l) + } +} + impl<'db> Rollback>>> for TypeVariableStorage<'db> { fn reverse(&mut self, undo: sv::UndoLog>>) { self.eq_relations.reverse(undo) } } +impl<'tcx> Rollback>> for TypeVariableStorage<'tcx> { + fn reverse(&mut self, undo: sv::UndoLog>) { + self.sub_unification_table.reverse(undo) + } +} + +impl<'tcx> Rollback> for TypeVariableStorage<'tcx> { + fn reverse(&mut self, undo: UndoLog<'tcx>) { + match undo { + UndoLog::EqRelation(undo) => self.eq_relations.reverse(undo), + UndoLog::SubRelation(undo) => self.sub_unification_table.reverse(undo), + } + } +} + #[derive(Clone, Default)] pub(crate) struct TypeVariableStorage<'db> { /// The origins of each type variable. @@ -31,6 +67,25 @@ pub(crate) struct TypeVariableStorage<'db> { /// constraint `?X == ?Y`. This table also stores, for each key, /// the known value. eq_relations: ut::UnificationTableStorage>, + /// Only used by `-Znext-solver` and for diagnostics. Tracks whether + /// type variables are related via subtyping at all, ignoring which of + /// the two is the subtype. + /// + /// When reporting ambiguity errors, we sometimes want to + /// treat all inference vars which are subtypes of each + /// others as if they are equal. For this case we compute + /// the transitive closure of our subtype obligations here. + /// + /// E.g. when encountering ambiguity errors, we want to suggest + /// specifying some method argument or to add a type annotation + /// to a local variable. Because subtyping cannot change the + /// shape of a type, it's fine if the cause of the ambiguity error + /// is only related to the suggested variable via subtyping. + /// + /// Even for something like `let x = returns_arg(); x.method();` the + /// type of `x` is only a supertype of the argument of `returns_arg`. We + /// still want to suggest specifying the type of the argument. + sub_unification_table: ut::UnificationTableStorage, } pub(crate) struct TypeVariableTable<'a, 'db> { @@ -112,6 +167,17 @@ impl<'db> TypeVariableTable<'_, 'db> { debug_assert!(self.probe(a).is_unknown()); debug_assert!(self.probe(b).is_unknown()); self.eq_relations().union(a, b); + self.sub_unification_table().union(a, b); + } + + /// Records that `a` and `b` are related via subtyping. We don't track + /// which of the two is the subtype. + /// + /// Precondition: neither `a` nor `b` are known. + pub(crate) fn sub_unify(&mut self, a: TyVid, b: TyVid) { + debug_assert!(self.probe(a).is_unknown()); + debug_assert!(self.probe(b).is_unknown()); + self.sub_unification_table().union(a, b); } /// Instantiates `vid` with the type `ty`. @@ -141,6 +207,10 @@ impl<'db> TypeVariableTable<'_, 'db> { /// for improving error messages. pub(crate) fn new_var(&mut self, universe: UniverseIndex, origin: TypeVariableOrigin) -> TyVid { let eq_key = self.eq_relations().new_key(TypeVariableValue::Unknown { universe }); + + let sub_key = self.sub_unification_table().new_key(()); + debug_assert_eq!(eq_key.vid, sub_key.vid); + let index = self.storage.values.push(TypeVariableData { origin }); debug_assert_eq!(eq_key.vid, index); @@ -163,6 +233,18 @@ impl<'db> TypeVariableTable<'_, 'db> { self.eq_relations().find(vid).vid } + /// Returns the "root" variable of `vid` in the `sub_unification_table` + /// equivalence table. All type variables that have been are related via + /// equality or subtyping will yield the same root variable (per the + /// union-find algorithm), so `sub_unification_table_root_var(a) + /// == sub_unification_table_root_var(b)` implies that: + /// ```text + /// exists X. (a <: X || X <: a) && (b <: X || X <: b) + /// ``` + pub(crate) fn sub_unification_table_root_var(&mut self, vid: TyVid) -> TyVid { + self.sub_unification_table().find(vid).vid + } + /// Retrieves the type to which `vid` has been instantiated, if /// any. pub(crate) fn probe(&mut self, vid: TyVid) -> TypeVariableValue<'db> { @@ -180,6 +262,11 @@ impl<'db> TypeVariableTable<'_, 'db> { self.storage.eq_relations.with_log(self.undo_log) } + #[inline] + fn sub_unification_table(&mut self) -> super::UnificationTable<'_, 'db, TyVidSubKey> { + self.storage.sub_unification_table.with_log(self.undo_log) + } + /// Returns indices of all variables that are not yet /// instantiated. pub(crate) fn unresolved_variables(&mut self) -> Vec { @@ -228,6 +315,36 @@ impl<'db> ut::UnifyKey for TyVidEqKey<'db> { fn tag() -> &'static str { "TyVidEqKey" } + fn order_roots(a: Self, _: &Self::Value, b: Self, _: &Self::Value) -> Option<(Self, Self)> { + if a.vid.as_u32() < b.vid.as_u32() { Some((a, b)) } else { Some((b, a)) } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub(crate) struct TyVidSubKey { + vid: TyVid, +} + +impl From for TyVidSubKey { + #[inline] // make this function eligible for inlining - it is quite hot. + fn from(vid: TyVid) -> Self { + TyVidSubKey { vid } + } +} + +impl ut::UnifyKey for TyVidSubKey { + type Value = (); + #[inline] + fn index(&self) -> u32 { + self.vid.as_u32() + } + #[inline] + fn from_index(i: u32) -> TyVidSubKey { + TyVidSubKey { vid: TyVid::from_u32(i) } + } + fn tag() -> &'static str { + "TyVidSubKey" + } } impl<'db> ut::UnifyValue for TypeVariableValue<'db> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/unify_key.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/unify_key.rs index b9afb45ba89d..dc913b262a7c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/unify_key.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/unify_key.rs @@ -139,6 +139,9 @@ impl<'db> UnifyKey for ConstVidKey<'db> { fn tag() -> &'static str { "ConstVidKey" } + fn order_roots(a: Self, _: &Self::Value, b: Self, _: &Self::Value) -> Option<(Self, Self)> { + if a.vid.as_u32() < b.vid.as_u32() { Some((a, b)) } else { Some((b, a)) } + } } impl<'db> UnifyValue for ConstVariableValue<'db> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index f66b8dace30a..11bc6e6abe94 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -734,15 +734,13 @@ impl<'db, T: HasInterner + ChalkToNextSolver<'db, U>, U> interner, self.binders.iter(Interner).map(|k| match &k.kind { chalk_ir::VariableKind::Ty(ty_variable_kind) => match ty_variable_kind { - TyVariableKind::General => rustc_type_ir::CanonicalVarKind::Ty( - rustc_type_ir::CanonicalTyVarKind::General(UniverseIndex::ROOT), - ), - TyVariableKind::Integer => { - rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::Int) - } - TyVariableKind::Float => rustc_type_ir::CanonicalVarKind::Ty( - rustc_type_ir::CanonicalTyVarKind::Float, - ), + // FIXME(next-solver): the info is incorrect, but we have no way to store the information in Chalk. + TyVariableKind::General => rustc_type_ir::CanonicalVarKind::Ty { + ui: UniverseIndex::ROOT, + sub_root: BoundVar::from_u32(0), + }, + TyVariableKind::Integer => rustc_type_ir::CanonicalVarKind::Int, + TyVariableKind::Float => rustc_type_ir::CanonicalVarKind::Float, }, chalk_ir::VariableKind::Lifetime => { rustc_type_ir::CanonicalVarKind::Region(UniverseIndex::ROOT) @@ -767,24 +765,20 @@ impl<'db, T: NextSolverToChalk<'db, U>, U: HasInterner> let binders = chalk_ir::CanonicalVarKinds::from_iter( Interner, self.variables.iter().map(|v| match v { - rustc_type_ir::CanonicalVarKind::Ty( - rustc_type_ir::CanonicalTyVarKind::General(ui), - ) => chalk_ir::CanonicalVarKind::new( - chalk_ir::VariableKind::Ty(TyVariableKind::General), - chalk_ir::UniverseIndex { counter: ui.as_usize() }, + rustc_type_ir::CanonicalVarKind::Ty { ui, sub_root: _ } => { + chalk_ir::CanonicalVarKind::new( + chalk_ir::VariableKind::Ty(TyVariableKind::General), + chalk_ir::UniverseIndex { counter: ui.as_usize() }, + ) + } + rustc_type_ir::CanonicalVarKind::Int => chalk_ir::CanonicalVarKind::new( + chalk_ir::VariableKind::Ty(TyVariableKind::Integer), + chalk_ir::UniverseIndex::root(), + ), + rustc_type_ir::CanonicalVarKind::Float => chalk_ir::CanonicalVarKind::new( + chalk_ir::VariableKind::Ty(TyVariableKind::Float), + chalk_ir::UniverseIndex::root(), ), - rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::Int) => { - chalk_ir::CanonicalVarKind::new( - chalk_ir::VariableKind::Ty(TyVariableKind::Integer), - chalk_ir::UniverseIndex::root(), - ) - } - rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::Float) => { - chalk_ir::CanonicalVarKind::new( - chalk_ir::VariableKind::Ty(TyVariableKind::Float), - chalk_ir::UniverseIndex::root(), - ) - } rustc_type_ir::CanonicalVarKind::Region(ui) => chalk_ir::CanonicalVarKind::new( chalk_ir::VariableKind::Lifetime, chalk_ir::UniverseIndex { counter: ui.as_usize() }, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs index 385149d78432..dc5073305c8c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs @@ -9,6 +9,7 @@ use rustc_type_ir::{ solve::{Certainty, NoSolution}, }; +use crate::next_solver::CanonicalVarKind; use crate::next_solver::mapping::NextSolverToChalk; use crate::{ TraitRefExt, @@ -117,13 +118,14 @@ impl<'db> SolverDelegate for SolverContext<'db> { canonical.instantiate(self.cx(), &values) } - fn instantiate_canonical_var_with_infer( + fn instantiate_canonical_var( &self, - cv_info: rustc_type_ir::CanonicalVarKind, - _span: ::Span, + kind: CanonicalVarKind<'db>, + span: ::Span, + var_values: &[GenericArg<'db>], universe_map: impl Fn(rustc_type_ir::UniverseIndex) -> rustc_type_ir::UniverseIndex, - ) -> ::GenericArg { - self.0.instantiate_canonical_var(cv_info, universe_map) + ) -> GenericArg<'db> { + self.0.instantiate_canonical_var(kind, var_values, universe_map) } fn add_item_bounds_for_hidden_type( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs index 50b96a160ed1..97d3ea72c93c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs @@ -532,17 +532,14 @@ pub(crate) fn mini_canonicalize<'db, T: TypeFoldable>>( max_universe: UniverseIndex::from_u32(1), variables: CanonicalVars::new_from_iter( context.cx(), - vars.iter().map(|(k, v)| match (*k).kind() { + vars.iter().enumerate().map(|(idx, (k, v))| match (*k).kind() { GenericArgKind::Type(ty) => match ty.kind() { - TyKind::Int(..) | TyKind::Uint(..) => { - rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::Int) - } - TyKind::Float(..) => rustc_type_ir::CanonicalVarKind::Ty( - rustc_type_ir::CanonicalTyVarKind::Float, - ), - _ => rustc_type_ir::CanonicalVarKind::Ty( - rustc_type_ir::CanonicalTyVarKind::General(UniverseIndex::ZERO), - ), + TyKind::Int(..) | TyKind::Uint(..) => rustc_type_ir::CanonicalVarKind::Int, + TyKind::Float(..) => rustc_type_ir::CanonicalVarKind::Float, + _ => rustc_type_ir::CanonicalVarKind::Ty { + ui: UniverseIndex::ZERO, + sub_root: BoundVar::from_usize(idx), + }, }, GenericArgKind::Lifetime(_) => { rustc_type_ir::CanonicalVarKind::Region(UniverseIndex::ZERO) From 8407376006546c484bcd72d2743f0e4e1f66ac94 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 9 Sep 2025 22:32:07 +0300 Subject: [PATCH 325/682] Adopt even more custom types in the new solver A lot of simplification and fun. --- .../crates/hir-def/src/lang_item.rs | 13 ++ .../crates/hir-ty/src/display.rs | 43 ++--- .../rust-analyzer/crates/hir-ty/src/layout.rs | 12 +- .../crates/hir-ty/src/method_resolution.rs | 5 +- .../crates/hir-ty/src/mir/eval.rs | 61 +++---- .../crates/hir-ty/src/next_solver/def_id.rs | 63 ++++++- .../infer/canonical/canonicalizer.rs | 4 +- .../src/next_solver/infer/canonical/mod.rs | 4 +- .../crates/hir-ty/src/next_solver/interner.rs | 160 +++++++++--------- .../crates/hir-ty/src/next_solver/mapping.rs | 46 ++--- .../crates/hir-ty/src/next_solver/solver.rs | 12 +- .../crates/hir-ty/src/next_solver/ty.rs | 21 ++- 12 files changed, 221 insertions(+), 223 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs index 600f20677001..df0705bf90cb 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs @@ -84,6 +84,15 @@ impl LangItemTarget { _ => None, } } + + pub fn as_adt(self) -> Option { + match self { + LangItemTarget::Union(it) => Some(it.into()), + LangItemTarget::EnumId(it) => Some(it.into()), + LangItemTarget::Struct(it) => Some(it.into()), + _ => None, + } + } } /// Salsa query. This will look for lang items in a specific crate. @@ -289,6 +298,10 @@ impl LangItem { lang_item(db, start_crate, self).and_then(|t| t.as_trait()) } + pub fn resolve_adt(self, db: &dyn DefDatabase, start_crate: Crate) -> Option { + lang_item(db, start_crate, self).and_then(|t| t.as_adt()) + } + pub fn resolve_enum(self, db: &dyn DefDatabase, start_crate: Crate) -> Option { lang_item(db, start_crate, self).and_then(|t| t.as_enum()) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index fcb79e9ffb57..ea8bdf8bcbae 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -59,7 +59,7 @@ use crate::{ lt_from_placeholder_idx, mir::pad16, next_solver::{ - BoundExistentialPredicate, Ctor, DbInterner, GenericArgs, SolverDefId, + BoundExistentialPredicate, DbInterner, GenericArgs, SolverDefId, mapping::{ ChalkToNextSolver, convert_args_for_result, convert_const_for_result, convert_region_for_result, convert_ty_for_result, @@ -911,14 +911,13 @@ fn render_const_scalar_inner( f.write_str("&")?; render_const_scalar_ns(f, bytes, memory_map, t) } - TyKind::Adt(adt, _) if b.len() == 2 * size_of::() => match adt.def_id() { - SolverDefId::AdtId(hir_def::AdtId::StructId(s)) => { + TyKind::Adt(adt, _) if b.len() == 2 * size_of::() => match adt.def_id().0 { + hir_def::AdtId::StructId(s) => { let data = f.db.struct_signature(s); write!(f, "&{}", data.name.display(f.db, f.edition()))?; Ok(()) } - SolverDefId::AdtId(_) => f.write_str(""), - _ => unreachable!(), + _ => f.write_str(""), }, _ => { let addr = usize::from_le_bytes(match b.try_into() { @@ -966,10 +965,7 @@ fn render_const_scalar_inner( f.write_str(")") } TyKind::Adt(def, args) => { - let def = match def.def_id() { - SolverDefId::AdtId(def) => def, - _ => unreachable!(), - }; + let def = def.def_id().0; let Ok(layout) = f.db.layout_of_adt(def, args, trait_env.clone()) else { return f.write_str(""); }; @@ -1300,12 +1296,7 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { sig.hir_fmt(f)?; } TyKind::FnDef(def, args) => { - let def = match def { - SolverDefId::FunctionId(id) => CallableDefId::FunctionId(id), - SolverDefId::Ctor(Ctor::Enum(e)) => CallableDefId::EnumVariantId(e), - SolverDefId::Ctor(Ctor::Struct(s)) => CallableDefId::StructId(s), - _ => unreachable!(), - }; + let def = def.0; let sig = db .callable_item_signature(def) .substitute(Interner, &convert_args_for_result(interner, args.as_slice())); @@ -1406,10 +1397,7 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { } } TyKind::Adt(def, parameters) => { - let def_id = match def.def_id() { - SolverDefId::AdtId(id) => id, - _ => unreachable!(), - }; + let def_id = def.def_id().0; f.start_location_link(def_id.into()); match f.display_kind { DisplayKind::Diagnostics | DisplayKind::Test => { @@ -1448,7 +1436,7 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { hir_fmt_generics( f, convert_args_for_result(interner, parameters.as_slice()).as_slice(Interner), - def.def_id().try_into().ok(), + Some(def.def_id().0.into()), None, )?; } @@ -1466,13 +1454,9 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { projection_ty.hir_fmt(f)?; } - TyKind::Foreign(type_alias) => { - let alias = match type_alias { - SolverDefId::TypeAliasId(id) => id, - _ => unreachable!(), - }; - let type_alias = db.type_alias_signature(alias); - f.start_location_link(alias.into()); + TyKind::Foreign(alias) => { + let type_alias = db.type_alias_signature(alias.0); + f.start_location_link(alias.0.into()); write!(f, "{}", type_alias.name.display(f.db, f.edition()))?; f.end_location_link(); } @@ -1549,10 +1533,7 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { } } TyKind::Closure(id, substs) => { - let id = match id { - SolverDefId::InternedClosureId(id) => id, - _ => unreachable!(), - }; + let id = id.0; let substs = convert_args_for_result(interner, substs.as_slice()); if f.display_kind.is_source_code() { if !f.display_kind.allows_opaque() { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index f8abb3b7f6fe..2020a8b34b4f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -25,7 +25,7 @@ use crate::{ consteval_nextsolver::try_const_usize, db::HirDatabase, next_solver::{ - DbInterner, GenericArgs, ParamEnv, SolverDefId, Ty, TyKind, TypingMode, + DbInterner, GenericArgs, ParamEnv, Ty, TyKind, TypingMode, infer::{DbInternerInferExt, traits::ObligationCause}, mapping::{ChalkToNextSolver, convert_args_for_result}, project::solve_normalize::deeply_normalize, @@ -323,14 +323,10 @@ pub fn layout_of_ty_query<'db>( ptr.valid_range_mut().start = 1; Layout::scalar(dl, ptr) } - TyKind::Closure(c, args) => { - let id = match c { - SolverDefId::InternedClosureId(id) => id, - _ => unreachable!(), - }; - let def = db.lookup_intern_closure(id); + TyKind::Closure(id, args) => { + let def = db.lookup_intern_closure(id.0); let infer = db.infer(def.0); - let (captures, _) = infer.closure_info(&id.into()); + let (captures, _) = infer.closure_info(&id.0.into()); let fields = captures .iter() .map(|it| { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index fa80567b1ecc..3f966694d504 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -161,10 +161,7 @@ impl TyFingerprint { rustc_ast_ir::Mutability::Mut => TyFingerprint::RawPtr(Mutability::Mut), rustc_ast_ir::Mutability::Not => TyFingerprint::RawPtr(Mutability::Not), }, - TyKind::Foreign(def) => { - let SolverDefId::TypeAliasId(def) = def else { unreachable!() }; - TyFingerprint::ForeignType(crate::to_foreign_def_id(def)) - } + TyKind::Foreign(def) => TyFingerprint::ForeignType(crate::to_foreign_def_id(def.0)), TyKind::Dynamic(bounds, _, _) => { let trait_ref = bounds .as_slice() diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index c60ace85be12..ca7467d3855f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -42,7 +42,7 @@ use crate::{ layout::{Layout, LayoutError, RustcEnumVariantIdx}, method_resolution::{is_dyn_method, lookup_impl_const}, next_solver::{ - Ctor, DbInterner, SolverDefId, + DbInterner, mapping::{ChalkToNextSolver, convert_args_for_result, convert_ty_for_result}, }, static_lifetime, @@ -2366,8 +2366,8 @@ impl<'db> Evaluator<'db> { let new_id = self.vtable_map.id(ty); self.write_memory(addr, &new_id.to_le_bytes())?; } - TyKind::Adt(id, args) => match id.def_id() { - SolverDefId::AdtId(AdtId::StructId(s)) => { + TyKind::Adt(id, args) => match id.def_id().0 { + AdtId::StructId(s) => { for (i, (_, ty)) in self.db.field_types_ns(s.into()).iter().enumerate() { let offset = layout.fields.offset(i).bytes_usize(); let ty = ty.instantiate(interner, args); @@ -2380,8 +2380,8 @@ impl<'db> Evaluator<'db> { )?; } } - SolverDefId::AdtId(AdtId::UnionId(_)) => (), - SolverDefId::AdtId(AdtId::EnumId(e)) => { + AdtId::UnionId(_) => (), + AdtId::EnumId(e) => { if let Some((ev, layout)) = detect_variant_from_bytes( &layout, self.db, @@ -2402,7 +2402,6 @@ impl<'db> Evaluator<'db> { } } } - _ => unreachable!(), }, TyKind::Tuple(tys) => { for (id, ty) in tys.iter().enumerate() { @@ -2472,38 +2471,24 @@ impl<'db> Evaluator<'db> { let interner = DbInterner::new_with(self.db, None, None); use rustc_type_ir::TyKind; match next_ty.kind() { - TyKind::FnDef(def, generic_args) => { - let def = match def { - SolverDefId::FunctionId(id) => CallableDefId::FunctionId(id), - SolverDefId::Ctor(Ctor::Struct(s)) => CallableDefId::StructId(s), - SolverDefId::Ctor(Ctor::Enum(e)) => CallableDefId::EnumVariantId(e), - _ => unreachable!(), - }; - self.exec_fn_def( - def, - &convert_args_for_result(interner, generic_args.as_slice()), - destination, - args, - locals, - target_bb, - span, - ) - } - TyKind::Closure(id, generic_args) => { - let id = match id { - SolverDefId::InternedClosureId(id) => id, - _ => unreachable!(), - }; - self.exec_closure( - id.into(), - bytes.slice(0..0), - &convert_args_for_result(interner, generic_args.as_slice()), - destination, - args, - locals, - span, - ) - } + TyKind::FnDef(def, generic_args) => self.exec_fn_def( + def.0, + &convert_args_for_result(interner, generic_args.as_slice()), + destination, + args, + locals, + target_bb, + span, + ), + TyKind::Closure(id, generic_args) => self.exec_closure( + id.0.into(), + bytes.slice(0..0), + &convert_args_for_result(interner, generic_args.as_slice()), + destination, + args, + locals, + span, + ), _ => Err(MirEvalError::InternalError("function pointer to non function".into())), } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs index 8bbc6e337039..a9c572d3f34e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs @@ -1,8 +1,8 @@ //! Definition of `SolverDefId` use hir_def::{ - AdtId, ConstId, EnumId, EnumVariantId, FunctionId, GenericDefId, ImplId, StaticId, StructId, - TraitId, TypeAliasId, UnionId, + AdtId, CallableDefId, ConstId, EnumId, EnumVariantId, FunctionId, GenericDefId, ImplId, + StaticId, StructId, TraitId, TypeAliasId, UnionId, }; use rustc_type_ir::inherent; use stdx::impl_from; @@ -42,7 +42,8 @@ impl_from!( TypeAliasId, InternedClosureId, InternedCoroutineId, - InternedOpaqueTyId + InternedOpaqueTyId, + Ctor for SolverDefId ); @@ -145,3 +146,59 @@ macro_rules! declare_id_wrapper { } declare_id_wrapper!(TraitIdWrapper, TraitId); +declare_id_wrapper!(TypeAliasIdWrapper, TypeAliasId); +declare_id_wrapper!(ClosureIdWrapper, InternedClosureId); +declare_id_wrapper!(CoroutineIdWrapper, InternedCoroutineId); +declare_id_wrapper!(AdtIdWrapper, AdtId); +declare_id_wrapper!(ImplIdWrapper, ImplId); + +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct CallableIdWrapper(pub CallableDefId); + +impl std::fmt::Debug for CallableIdWrapper { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(&self.0, f) + } +} +impl From for CallableDefId { + #[inline] + fn from(value: CallableIdWrapper) -> CallableDefId { + value.0 + } +} +impl From for CallableIdWrapper { + #[inline] + fn from(value: CallableDefId) -> CallableIdWrapper { + Self(value) + } +} +impl From for SolverDefId { + #[inline] + fn from(value: CallableIdWrapper) -> SolverDefId { + match value.0 { + CallableDefId::FunctionId(it) => it.into(), + CallableDefId::StructId(it) => Ctor::Struct(it).into(), + CallableDefId::EnumVariantId(it) => Ctor::Enum(it).into(), + } + } +} +impl TryFrom for CallableIdWrapper { + type Error = (); + #[inline] + fn try_from(value: SolverDefId) -> Result { + match value { + SolverDefId::FunctionId(it) => Ok(Self(it.into())), + SolverDefId::Ctor(Ctor::Struct(it)) => Ok(Self(it.into())), + SolverDefId::Ctor(Ctor::Enum(it)) => Ok(Self(it.into())), + _ => Err(()), + } + } +} +impl<'db> inherent::DefId> for CallableIdWrapper { + fn as_local(self) -> Option { + Some(self.into()) + } + fn is_local(self) -> bool { + true + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs index ffb9c076fa06..beaac11a2de4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs @@ -400,7 +400,7 @@ impl<'cx, 'db> TypeFolder> for Canonicalizer<'cx, 'db> { TyKind::Infer(IntVar(vid)) => { let nt = self.infcx.opportunistic_resolve_int_var(vid); if nt != t { - return self.fold_ty(nt); + self.fold_ty(nt) } else { self.canonicalize_ty_var(CanonicalVarKind::Int, t) } @@ -408,7 +408,7 @@ impl<'cx, 'db> TypeFolder> for Canonicalizer<'cx, 'db> { TyKind::Infer(FloatVar(vid)) => { let nt = self.infcx.opportunistic_resolve_float_var(vid); if nt != t { - return self.fold_ty(nt); + self.fold_ty(nt) } else { self.canonicalize_ty_var(CanonicalVarKind::Float, t) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs index 8db4320acc9c..d0669f5c3bcc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/mod.rs @@ -82,9 +82,7 @@ impl<'db> InferCtxt<'db> { let var_values = CanonicalVarValues::instantiate( self.interner, canonical.variables, - |var_values, info| { - self.instantiate_canonical_var(info, &var_values, |ui| universes[ui]) - }, + |var_values, info| self.instantiate_canonical_var(info, var_values, |ui| universes[ui]), ); let result = canonical.instantiate(self.interner, &var_values); (result, var_values) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 5709beaefc29..0f512fdaf868 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -19,7 +19,7 @@ use rustc_type_ir::error::TypeError; use rustc_type_ir::inherent::{ AdtDef as _, GenericArgs as _, GenericsOf, IntoKind, SliceLike as _, Span as _, }; -use rustc_type_ir::lang_items::{SolverLangItem, SolverTraitLangItem}; +use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem}; use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::{ AliasTerm, AliasTermKind, AliasTy, AliasTyKind, EarlyBinder, FlagComputation, Flags, @@ -47,8 +47,9 @@ use crate::method_resolution::{ALL_FLOAT_FPS, ALL_INT_FPS, TyFingerprint}; use crate::next_solver::infer::InferCtxt; use crate::next_solver::util::{ContainsTypeErrors, explicit_item_bounds, for_trait_impls}; use crate::next_solver::{ - BoundConst, CanonicalVarKind, FxIndexMap, InternedWrapperNoDebug, RegionAssumptions, - SolverContext, SolverDefIds, TraitIdWrapper, + AdtIdWrapper, BoundConst, CallableIdWrapper, CanonicalVarKind, ClosureIdWrapper, + CoroutineIdWrapper, FxIndexMap, ImplIdWrapper, InternedWrapperNoDebug, RegionAssumptions, + SolverContext, SolverDefIds, TraitIdWrapper, TypeAliasIdWrapper, }; use crate::{ConstScalar, FnAbi, Interner, db::HirDatabase}; @@ -609,8 +610,8 @@ impl AdtDef { } impl<'db> inherent::AdtDef> for AdtDef { - fn def_id(self) -> as rustc_type_ir::Interner>::DefId { - SolverDefId::AdtId(self.inner().id) + fn def_id(self) -> AdtIdWrapper { + self.inner().id.into() } fn is_struct(self) -> bool { @@ -889,6 +890,13 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { type LocalDefId = SolverDefId; type LocalDefIds = SolverDefIds; type TraitId = TraitIdWrapper; + type ForeignId = TypeAliasIdWrapper; + type FunctionId = CallableIdWrapper; + type ClosureId = ClosureIdWrapper; + type CoroutineClosureId = CoroutineIdWrapper; + type CoroutineId = CoroutineIdWrapper; + type AdtId = AdtIdWrapper; + type ImplId = ImplIdWrapper; type Span = Span; type GenericArgs = GenericArgs<'db>; @@ -1103,12 +1111,8 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { self.db().ty_ns(def_id) } - fn adt_def(self, adt_def_id: Self::DefId) -> Self::AdtDef { - let def_id = match adt_def_id { - SolverDefId::AdtId(adt_id) => adt_id, - _ => panic!("Invalid DefId passed to adt_def"), - }; - AdtDef::new(def_id, self) + fn adt_def(self, def_id: Self::AdtId) -> Self::AdtDef { + AdtDef::new(def_id.0, self) } fn alias_ty_kind(self, alias: rustc_type_ir::AliasTy) -> AliasTyKind { @@ -1219,24 +1223,16 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { fn fn_sig( self, - def_id: Self::DefId, + def_id: Self::FunctionId, ) -> EarlyBinder>> { - let id = match def_id { - SolverDefId::FunctionId(id) => CallableDefId::FunctionId(id), - SolverDefId::Ctor(ctor) => match ctor { - super::Ctor::Struct(struct_id) => CallableDefId::StructId(struct_id), - super::Ctor::Enum(enum_variant_id) => CallableDefId::EnumVariantId(enum_variant_id), - }, - def => unreachable!("{:?}", def), - }; - self.db().callable_item_signature_ns(id) + self.db().callable_item_signature_ns(def_id.0) } - fn coroutine_movability(self, def_id: Self::DefId) -> rustc_ast_ir::Movability { + fn coroutine_movability(self, def_id: Self::CoroutineId) -> rustc_ast_ir::Movability { unimplemented!() } - fn coroutine_for_closure(self, def_id: Self::DefId) -> Self::DefId { + fn coroutine_for_closure(self, def_id: Self::CoroutineId) -> Self::CoroutineId { unimplemented!() } @@ -1361,13 +1357,9 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { fn impl_super_outlives( self, - impl_def_id: Self::DefId, + impl_id: Self::ImplId, ) -> EarlyBinder> { - let impl_id = match impl_def_id { - SolverDefId::ImplId(id) => id, - _ => unreachable!(), - }; - let trait_ref = self.db().impl_trait_ns(impl_id).expect("expected an impl of trait"); + let trait_ref = self.db().impl_trait_ns(impl_id.0).expect("expected an impl of trait"); trait_ref.map_bound(|trait_ref| { let clause: Clause<'_> = trait_ref.upcast(self); Clauses::new_from_iter( @@ -1392,7 +1384,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { EarlyBinder::bind([unimplemented!()]) } - fn has_target_features(self, def_id: Self::DefId) -> bool { + fn has_target_features(self, def_id: Self::FunctionId) -> bool { false } @@ -1407,8 +1399,6 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { SolverLangItem::DynMetadata => LangItem::DynMetadata, SolverLangItem::FutureOutput => LangItem::FutureOutput, SolverLangItem::Metadata => LangItem::Metadata, - SolverLangItem::Option => LangItem::Option, - SolverLangItem::Poll => LangItem::Poll, }; let target = hir_def::lang_item::lang_item( self.db(), @@ -1468,24 +1458,33 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { .into() } - #[allow(clippy::match_like_matches_macro)] - fn is_lang_item(self, def_id: Self::DefId, lang_item: SolverLangItem) -> bool { - use SolverLangItem::*; + fn require_adt_lang_item(self, lang_item: SolverAdtLangItem) -> AdtIdWrapper { + let lang_item = match lang_item { + SolverAdtLangItem::Option => LangItem::Option, + SolverAdtLangItem::Poll => LangItem::Poll, + }; + lang_item + .resolve_adt(self.db(), self.krate.expect("Must have self.krate")) + .unwrap_or_else(|| panic!("Lang item {lang_item:?} required but not found.")) + .into() + } - // FIXME: derive PartialEq on SolverLangItem + fn is_lang_item(self, def_id: Self::DefId, lang_item: SolverLangItem) -> bool { self.as_lang_item(def_id) .map_or(false, |l| std::mem::discriminant(&l) == std::mem::discriminant(&lang_item)) } - #[allow(clippy::match_like_matches_macro)] fn is_trait_lang_item(self, def_id: Self::TraitId, lang_item: SolverTraitLangItem) -> bool { - use SolverTraitLangItem::*; - - // FIXME: derive PartialEq on SolverTraitLangItem self.as_trait_lang_item(def_id) .map_or(false, |l| std::mem::discriminant(&l) == std::mem::discriminant(&lang_item)) } + fn is_adt_lang_item(self, def_id: Self::AdtId, lang_item: SolverAdtLangItem) -> bool { + // FIXME: derive PartialEq on SolverTraitLangItem + self.as_adt_lang_item(def_id) + .map_or(false, |l| std::mem::discriminant(&l) == std::mem::discriminant(&lang_item)) + } + fn as_lang_item(self, def_id: Self::DefId) -> Option { let def_id: AttrDefId = match def_id { SolverDefId::TraitId(id) => id.into(), @@ -1505,9 +1504,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { DynMetadata, CoroutineReturn, CoroutineYield, - Poll, FutureOutput, - Option, AsyncFnOnceOutput, CallRefFuture, CallOnceFuture, @@ -1556,6 +1553,19 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { ) } + fn as_adt_lang_item(self, def_id: Self::AdtId) -> Option { + let def_id: AttrDefId = def_id.0.into(); + let lang_item = self.db().lang_attr(def_id)?; + as_lang_item!( + SolverAdtLangItem, lang_item; + + ignore = {} + + Option, + Poll, + ) + } + fn associated_type_def_ids(self, def_id: Self::DefId) -> impl IntoIterator { let trait_ = match def_id { SolverDefId::TraitId(id) => id, @@ -1568,7 +1578,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { self, trait_: Self::TraitId, self_ty: Self::Ty, - mut f: impl FnMut(Self::DefId), + mut f: impl FnMut(Self::ImplId), ) { let trait_ = trait_.0; let self_ty_fp = TyFingerprint::for_trait_impl_ns(&self_ty); @@ -1595,7 +1605,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { continue; } - f(SolverDefId::ImplId(i)); + f(i.into()); } ControlFlow::Continue(()) }, @@ -1618,7 +1628,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { continue; } - f(SolverDefId::ImplId(i)); + f(i.into()); } } ControlFlow::Continue(()) @@ -1632,7 +1642,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { true } - fn impl_is_default(self, impl_def_id: Self::DefId) -> bool { + fn impl_is_default(self, impl_def_id: Self::ImplId) -> bool { // FIXME false } @@ -1640,26 +1650,16 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { #[tracing::instrument(skip(self), ret)] fn impl_trait_ref( self, - impl_def_id: Self::DefId, + impl_id: Self::ImplId, ) -> EarlyBinder> { - let impl_id = match impl_def_id { - SolverDefId::ImplId(id) => id, - _ => panic!("Unexpected SolverDefId in impl_trait_ref"), - }; - let db = self.db(); - - db.impl_trait_ns(impl_id) + db.impl_trait_ns(impl_id.0) // ImplIds for impls where the trait ref can't be resolved should never reach trait solving .expect("invalid impl passed to trait solver") } - fn impl_polarity(self, impl_def_id: Self::DefId) -> rustc_type_ir::ImplPolarity { - let impl_id = match impl_def_id { - SolverDefId::ImplId(id) => id, - _ => unreachable!(), - }; - let impl_data = self.db().impl_signature(impl_id); + fn impl_polarity(self, impl_id: Self::ImplId) -> rustc_type_ir::ImplPolarity { + let impl_data = self.db().impl_signature(impl_id.0); if impl_data.flags.contains(ImplFlags::NEGATIVE) { ImplPolarity::Negative } else { @@ -1701,33 +1701,29 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { panic!("Bug encountered in next-trait-solver.") } - fn is_general_coroutine(self, coroutine_def_id: Self::DefId) -> bool { + fn is_general_coroutine(self, coroutine_def_id: Self::CoroutineId) -> bool { // FIXME(next-solver) true } - fn coroutine_is_async(self, coroutine_def_id: Self::DefId) -> bool { + fn coroutine_is_async(self, coroutine_def_id: Self::CoroutineId) -> bool { // FIXME(next-solver) true } - fn coroutine_is_gen(self, coroutine_def_id: Self::DefId) -> bool { + fn coroutine_is_gen(self, coroutine_def_id: Self::CoroutineId) -> bool { // FIXME(next-solver) false } - fn coroutine_is_async_gen(self, coroutine_def_id: Self::DefId) -> bool { + fn coroutine_is_async_gen(self, coroutine_def_id: Self::CoroutineId) -> bool { // FIXME(next-solver) false } - fn unsizing_params_for_adt(self, adt_def_id: Self::DefId) -> Self::UnsizingParams { - let id = match adt_def_id { - SolverDefId::AdtId(id) => id, - _ => unreachable!(), - }; - let def = AdtDef::new(id, self); - let num_params = self.generics_of(adt_def_id).count(); + fn unsizing_params_for_adt(self, id: Self::AdtId) -> Self::UnsizingParams { + let def = AdtDef::new(id.0, self); + let num_params = self.generics_of(id.into()).count(); let maybe_unsizing_param_idx = |arg: GenericArg<'db>| match arg.kind() { GenericArgKind::Type(ty) => match ty.kind() { @@ -1835,15 +1831,15 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { EarlyBinder::bind([]) } - fn fn_is_const(self, def_id: Self::DefId) -> bool { - let id = match def_id { - SolverDefId::FunctionId(id) => id, - _ => unreachable!(), + fn fn_is_const(self, id: Self::FunctionId) -> bool { + let id = match id.0 { + CallableDefId::FunctionId(id) => id, + _ => return false, }; self.db().function_signature(id).flags.contains(FnFlags::CONST) } - fn impl_is_const(self, def_id: Self::DefId) -> bool { + fn impl_is_const(self, def_id: Self::ImplId) -> bool { false } @@ -1877,7 +1873,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { fn coroutine_hidden_types( self, - def_id: Self::DefId, + def_id: Self::CoroutineId, ) -> EarlyBinder>> { // FIXME(next-solver) @@ -1896,11 +1892,11 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { self.db().trait_signature(trait_.0).flags.contains(TraitFlags::UNSAFE) } - fn impl_self_is_guaranteed_unsized(self, def_id: Self::DefId) -> bool { + fn impl_self_is_guaranteed_unsized(self, def_id: Self::ImplId) -> bool { false } - fn impl_specializes(self, impl_def_id: Self::DefId, victim_def_id: Self::DefId) -> bool { + fn impl_specializes(self, impl_def_id: Self::ImplId, victim_def_id: Self::ImplId) -> bool { false } @@ -2020,6 +2016,12 @@ macro_rules! TrivialTypeTraversalImpls { TrivialTypeTraversalImpls! { SolverDefId, TraitIdWrapper, + TypeAliasIdWrapper, + CallableIdWrapper, + ClosureIdWrapper, + CoroutineIdWrapper, + AdtIdWrapper, + ImplIdWrapper, Pattern<'db>, Safety, FnAbi, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index 11bc6e6abe94..ce56282a00b2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -243,12 +243,10 @@ impl<'db> ChalkToNextSolver<'db, Ty<'db>> for chalk_ir::Ty { } chalk_ir::TyKind::FnDef(fn_def_id, substitution) => { let def_id = CallableDefId::from_chalk(interner.db(), *fn_def_id); - let id: SolverDefId = match def_id { - CallableDefId::FunctionId(id) => id.into(), - CallableDefId::StructId(id) => SolverDefId::Ctor(Ctor::Struct(id)), - CallableDefId::EnumVariantId(id) => SolverDefId::Ctor(Ctor::Enum(id)), - }; - rustc_type_ir::TyKind::FnDef(id, substitution.to_nextsolver(interner)) + rustc_type_ir::TyKind::FnDef( + def_id.into(), + substitution.to_nextsolver(interner), + ) } chalk_ir::TyKind::Str => rustc_type_ir::TyKind::Str, chalk_ir::TyKind::Never => rustc_type_ir::TyKind::Never, @@ -271,7 +269,7 @@ impl<'db> ChalkToNextSolver<'db, Ty<'db>> for chalk_ir::Ty { ) } chalk_ir::TyKind::Foreign(foreign_def_id) => rustc_type_ir::TyKind::Foreign( - SolverDefId::TypeAliasId(crate::from_foreign_def_id(*foreign_def_id)), + crate::from_foreign_def_id(*foreign_def_id).into(), ), chalk_ir::TyKind::Error => rustc_type_ir::TyKind::Error(ErrorGuaranteed), chalk_ir::TyKind::Dyn(dyn_ty) => { @@ -1511,13 +1509,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) TyKind::Slice(ty) } - rustc_type_ir::TyKind::Foreign(foreign) => { - let def_id = match foreign { - SolverDefId::TypeAliasId(id) => id, - _ => unreachable!(), - }; - TyKind::Foreign(to_foreign_def_id(def_id)) - } + rustc_type_ir::TyKind::Foreign(foreign) => TyKind::Foreign(to_foreign_def_id(foreign.0)), rustc_type_ir::TyKind::Pat(_, _) => unimplemented!(), rustc_type_ir::TyKind::RawPtr(ty, mutability) => { let mutability = match mutability { @@ -1528,40 +1520,22 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) TyKind::Raw(mutability, ty) } rustc_type_ir::TyKind::FnDef(def_id, args) => { - let id = match def_id { - SolverDefId::FunctionId(id) => CallableDefId::FunctionId(id), - SolverDefId::Ctor(Ctor::Struct(id)) => CallableDefId::StructId(id), - SolverDefId::Ctor(Ctor::Enum(id)) => CallableDefId::EnumVariantId(id), - _ => unreachable!(), - }; let subst = convert_args_for_result(interner, args.as_slice()); - TyKind::FnDef(id.to_chalk(interner.db()), subst) + TyKind::FnDef(def_id.0.to_chalk(interner.db()), subst) } rustc_type_ir::TyKind::Closure(def_id, args) => { - let id = match def_id { - SolverDefId::InternedClosureId(id) => id, - _ => unreachable!(), - }; let subst = convert_args_for_result(interner, args.as_slice()); - TyKind::Closure(id.into(), subst) + TyKind::Closure(def_id.0.into(), subst) } rustc_type_ir::TyKind::CoroutineClosure(_, _) => unimplemented!(), rustc_type_ir::TyKind::Coroutine(def_id, args) => { - let id = match def_id { - SolverDefId::InternedCoroutineId(id) => id, - _ => unreachable!(), - }; let subst = convert_args_for_result(interner, args.as_slice()); - TyKind::Coroutine(id.into(), subst) + TyKind::Coroutine(def_id.0.into(), subst) } rustc_type_ir::TyKind::CoroutineWitness(def_id, args) => { - let id = match def_id { - SolverDefId::InternedCoroutineId(id) => id, - _ => unreachable!(), - }; let subst = convert_args_for_result(interner, args.as_slice()); - TyKind::CoroutineWitness(id.into(), subst) + TyKind::CoroutineWitness(def_id.0.into(), subst) } rustc_type_ir::TyKind::UnsafeBinder(_) => unimplemented!(), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs index dc5073305c8c..c7591c0c7796 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs @@ -9,8 +9,8 @@ use rustc_type_ir::{ solve::{Certainty, NoSolution}, }; -use crate::next_solver::CanonicalVarKind; use crate::next_solver::mapping::NextSolverToChalk; +use crate::next_solver::{CanonicalVarKind, ImplIdWrapper}; use crate::{ TraitRefExt, db::HirDatabase, @@ -148,12 +148,8 @@ impl<'db> SolverDelegate for SolverContext<'db> { &self, goal_trait_ref: rustc_type_ir::TraitRef, trait_assoc_def_id: ::DefId, - impl_def_id: ::DefId, + impl_id: ImplIdWrapper, ) -> Result::DefId>, ErrorGuaranteed> { - let impl_id = match impl_def_id { - SolverDefId::ImplId(id) => id, - _ => panic!("Unexpected SolverDefId"), - }; let trait_assoc_id = match trait_assoc_def_id { SolverDefId::TypeAliasId(id) => id, _ => panic!("Unexpected SolverDefId"), @@ -162,7 +158,7 @@ impl<'db> SolverDelegate for SolverContext<'db> { .0 .interner .db() - .impl_trait(impl_id) + .impl_trait(impl_id.0) // ImplIds for impls where the trait ref can't be resolved should never reach solver .expect("invalid impl passed to next-solver") .into_value_and_skipped_binders() @@ -170,7 +166,7 @@ impl<'db> SolverDelegate for SolverContext<'db> { let trait_ = trait_ref.hir_trait_id(); let trait_data = trait_.trait_items(self.0.interner.db()); let id = - impl_id.impl_items(self.0.interner.db()).items.iter().find_map(|item| -> Option<_> { + impl_id.0.impl_items(self.0.interner.db()).items.iter().find_map(|item| -> Option<_> { match item { (_, AssocItemId::TypeAliasId(type_alias)) => { let name = &self.0.interner.db().type_alias_signature(*type_alias).name; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index 4794e2d60450..e1c29160ad1a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -20,7 +20,9 @@ use rustc_type_ir::{ use salsa::plumbing::{AsId, FromId}; use smallvec::SmallVec; -use crate::next_solver::GenericArg; +use crate::next_solver::{ + CallableIdWrapper, ClosureIdWrapper, CoroutineIdWrapper, GenericArg, TypeAliasIdWrapper, +}; use crate::{ db::HirDatabase, interner::InternedWrapperNoDebug, @@ -599,10 +601,7 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> { Ty::new(interner, TyKind::Adt(adt_def, args)) } - fn new_foreign( - interner: DbInterner<'db>, - def_id: as rustc_type_ir::Interner>::DefId, - ) -> Self { + fn new_foreign(interner: DbInterner<'db>, def_id: TypeAliasIdWrapper) -> Self { Ty::new(interner, TyKind::Foreign(def_id)) } @@ -617,7 +616,7 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> { fn new_coroutine( interner: DbInterner<'db>, - def_id: as rustc_type_ir::Interner>::DefId, + def_id: CoroutineIdWrapper, args: as rustc_type_ir::Interner>::GenericArgs, ) -> Self { Ty::new(interner, TyKind::Coroutine(def_id, args)) @@ -625,7 +624,7 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> { fn new_coroutine_closure( interner: DbInterner<'db>, - def_id: as rustc_type_ir::Interner>::DefId, + def_id: CoroutineIdWrapper, args: as rustc_type_ir::Interner>::GenericArgs, ) -> Self { Ty::new(interner, TyKind::CoroutineClosure(def_id, args)) @@ -633,7 +632,7 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> { fn new_closure( interner: DbInterner<'db>, - def_id: as rustc_type_ir::Interner>::DefId, + def_id: ClosureIdWrapper, args: as rustc_type_ir::Interner>::GenericArgs, ) -> Self { Ty::new(interner, TyKind::Closure(def_id, args)) @@ -641,7 +640,7 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> { fn new_coroutine_witness( interner: DbInterner<'db>, - def_id: as rustc_type_ir::Interner>::DefId, + def_id: CoroutineIdWrapper, args: as rustc_type_ir::Interner>::GenericArgs, ) -> Self { Ty::new(interner, TyKind::CoroutineWitness(def_id, args)) @@ -649,7 +648,7 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> { fn new_coroutine_witness_for_coroutine( interner: DbInterner<'db>, - def_id: as rustc_type_ir::Interner>::DefId, + def_id: CoroutineIdWrapper, coroutine_args: as rustc_type_ir::Interner>::GenericArgs, ) -> Self { // HACK: Coroutine witness types are lifetime erased, so they @@ -714,7 +713,7 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> { fn new_fn_def( interner: DbInterner<'db>, - def_id: as rustc_type_ir::Interner>::DefId, + def_id: CallableIdWrapper, args: as rustc_type_ir::Interner>::GenericArgs, ) -> Self { Ty::new(interner, TyKind::FnDef(def_id, args)) From 1acb4c2c1621578ea787afb698ed8718cbdf3d4f Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 28 Aug 2025 03:35:22 +0300 Subject: [PATCH 326/682] An associated type is not a projection! More correctly, a `TyKind::AssociatedType` is not the same as `TyKind::Projection`. We used to map next-solver `TyKind::Alias` to Chalk's `TyKind::AssociatedType`. This is very incorrect, as `AssociatedType` is assumed to be fully normalized, and caused a lot of type mismatches. Unfortunately fixing this causes a lot of stack overflows, because the next solver doesn't have something akin to `AssociatedType` so we normalize again and again. The reason is that is the lazy-normalization nature of the next solver, which means we need to stop normalizing everything. This will be fixed in the next commit. --- .../crates/hir-ty/src/next_solver/mapping.rs | 5 +++- .../hir-ty/src/tests/regression/new_solver.rs | 27 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index f66b8dace30a..57a56b3f0c66 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -1358,7 +1358,10 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) }; let associated_ty_id = to_assoc_type_id(assoc_ty_id); let substitution = convert_args_for_result(interner, alias_ty.args.as_slice()); - TyKind::AssociatedType(associated_ty_id, substitution) + TyKind::Alias(crate::AliasTy::Projection(crate::ProjectionTy { + associated_ty_id, + substitution, + })) } rustc_type_ir::AliasTyKind::Opaque => { let opaque_ty_id = match alias_ty.def_id { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs index 97473bbabba0..471108d964f6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -71,3 +71,30 @@ fn main() { }"#, ); } + +#[test] +fn projection_is_not_associated_type() { + check_no_mismatches( + r#" +//- minicore: fn +trait Iterator { + type Item; + + fn partition(self, f: F) + where + F: FnMut(&Self::Item) -> bool, + { + } +} + +struct Iter; +impl Iterator for Iter { + type Item = i32; +} + +fn main() { + Iter.partition(|n| true); +} + "#, + ); +} From b262cae8575589997d4f2a9bc166d99cc48e5eb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Sun, 24 Aug 2025 11:14:26 +0200 Subject: [PATCH 327/682] port `#[no_core]` to the new attribute parsing infrastructure --- .../rustc_attr_parsing/src/attributes/crate_level.rs | 12 ++++++++++++ compiler/rustc_attr_parsing/src/context.rs | 5 +++-- compiler/rustc_hir/src/attrs/data_structures.rs | 3 +++ compiler/rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_passes/src/check_attr.rs | 1 + 5 files changed, 20 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index d23a7ae72f8c..d2e28ed97aac 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -176,3 +176,15 @@ impl SingleAttributeParser for PatternComplexityLimitParser { }) } } + +pub(crate) struct NoCoreParser; + +impl NoArgsAttributeParser for NoCoreParser { + const PATH: &[Symbol] = &[sym::no_core]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; + // FIXME: recursion limit is allowed on all targets and ignored, + // even though it should only be valid on crates of course + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoCore; + const TYPE: AttributeType = AttributeType::CrateLevel; +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 98e86838a3a1..3d532d432b5c 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -25,8 +25,8 @@ use crate::attributes::codegen_attrs::{ }; use crate::attributes::confusables::ConfusablesParser; use crate::attributes::crate_level::{ - CrateNameParser, MoveSizeLimitParser, PatternComplexityLimitParser, RecursionLimitParser, - TypeLengthLimitParser, + CrateNameParser, MoveSizeLimitParser, NoCoreParser, PatternComplexityLimitParser, + RecursionLimitParser, TypeLengthLimitParser, }; use crate::attributes::deprecation::DeprecationParser; use crate::attributes::dummy::DummyParser; @@ -222,6 +222,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index b4c1b61b9b58..c2a3844af022 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -579,6 +579,9 @@ pub enum AttributeKind { /// Represents `#[naked]` Naked(Span), + /// Represents `#[no_core]` + NoCore(Span), + /// Represents `#[no_implicit_prelude]` NoImplicitPrelude(Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 0e208be3497a..d46e8abf9378 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -64,6 +64,7 @@ impl AttributeKind { MoveSizeLimit { .. } => No, MustUse { .. } => Yes, Naked(..) => No, + NoCore(..) => No, NoImplicitPrelude(..) => No, NoMangle(..) => Yes, // Needed for rustdoc NonExhaustive(..) => Yes, // Needed for rustdoc diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 487bdd2a888e..cecc8b7fa6ba 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -274,6 +274,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::MoveSizeLimit { .. } | AttributeKind::TypeLengthLimit { .. } | AttributeKind::PatternComplexityLimit { .. } + | AttributeKind::NoCore { .. } ) => { /* do nothing */ } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); From 0db2eb7734633fe55dacbb05dbc677a079301cab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Sun, 24 Aug 2025 11:15:09 +0200 Subject: [PATCH 328/682] port `#[no_std]` to the new attribute parsing infrastructure --- .../rustc_attr_parsing/src/attributes/crate_level.rs | 11 +++++++++++ compiler/rustc_attr_parsing/src/context.rs | 3 ++- compiler/rustc_hir/src/attrs/data_structures.rs | 3 +++ compiler/rustc_hir/src/attrs/encode_cross_crate.rs | 3 ++- compiler/rustc_passes/src/check_attr.rs | 1 + 5 files changed, 19 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index d2e28ed97aac..3ec847bcaae2 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -188,3 +188,14 @@ impl NoArgsAttributeParser for NoCoreParser { const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoCore; const TYPE: AttributeType = AttributeType::CrateLevel; } + +pub(crate) struct NoStdParser; +impl NoArgsAttributeParser for NoStdParser { + const PATH: &[Symbol] = &[sym::no_std]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; + // FIXME: recursion limit is allowed on all targets and ignored, + // even though it should only be valid on crates of course + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoStd; + const TYPE: AttributeType = AttributeType::CrateLevel; +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 3d532d432b5c..2e6d840ab517 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -25,7 +25,7 @@ use crate::attributes::codegen_attrs::{ }; use crate::attributes::confusables::ConfusablesParser; use crate::attributes::crate_level::{ - CrateNameParser, MoveSizeLimitParser, NoCoreParser, PatternComplexityLimitParser, + CrateNameParser, MoveSizeLimitParser, NoCoreParser, NoStdParser, PatternComplexityLimitParser, RecursionLimitParser, TypeLengthLimitParser, }; use crate::attributes::deprecation::DeprecationParser; @@ -225,6 +225,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index c2a3844af022..ea11a99efbc3 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -588,6 +588,9 @@ pub enum AttributeKind { /// Represents `#[no_mangle]` NoMangle(Span), + /// Represents `#[no_std]` + NoStd(Span), + /// Represents `#[non_exhaustive]` NonExhaustive(Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index d46e8abf9378..55521c158540 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -66,7 +66,8 @@ impl AttributeKind { Naked(..) => No, NoCore(..) => No, NoImplicitPrelude(..) => No, - NoMangle(..) => Yes, // Needed for rustdoc + NoMangle(..) => Yes, // Needed for rustdoc + NoStd(..) => No, NonExhaustive(..) => Yes, // Needed for rustdoc Optimize(..) => No, ParenSugar(..) => No, diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index cecc8b7fa6ba..3b744acbca6b 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -275,6 +275,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::TypeLengthLimit { .. } | AttributeKind::PatternComplexityLimit { .. } | AttributeKind::NoCore { .. } + | AttributeKind::NoStd { .. } ) => { /* do nothing */ } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); From 90e74de473a1c46f34437c4337fe2f3065800b92 Mon Sep 17 00:00:00 2001 From: beepster4096 <19316085+beepster4096@users.noreply.github.com> Date: Tue, 9 Sep 2025 16:23:14 -0700 Subject: [PATCH 329/682] don't trim paths in mir dumping when filtering and at the top of the file --- compiler/rustc_middle/src/mir/pretty.rs | 12 +++++++----- ....Test.SimplifyCfg-make_shim.after.panic-abort.mir | 2 +- ...Test.SimplifyCfg-make_shim.after.panic-unwind.mir | 2 +- ...ace.[String;42].AddMovesForPackedDrops.before.mir | 2 +- ..._place.[String].AddMovesForPackedDrops.before.mir | 2 +- ..._place.Vec_i32_.AddMovesForPackedDrops.before.mir | 2 +- 6 files changed, 12 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index a7d99f513a12..f9d0a5f0a3b6 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -78,8 +78,9 @@ impl<'dis, 'de, 'tcx> MirDumper<'dis, 'de, 'tcx> { pub fn new(tcx: TyCtxt<'tcx>, pass_name: &'static str, body: &Body<'tcx>) -> Option { let dump_enabled = if let Some(ref filters) = tcx.sess.opts.unstable_opts.dump_mir { // see notes on #41697 below - let node_path = - ty::print::with_forced_impl_filename_line!(tcx.def_path_str(body.source.def_id())); + let node_path = ty::print::with_no_trimmed_paths!( + ty::print::with_forced_impl_filename_line!(tcx.def_path_str(body.source.def_id())) + ); filters.split('|').any(|or_filter| { or_filter.split('&').all(|and_filter| { let and_filter_trimmed = and_filter.trim(); @@ -173,9 +174,10 @@ impl<'dis, 'de, 'tcx> MirDumper<'dis, 'de, 'tcx> { // trigger `type_of`, and this can run while we are already attempting to evaluate `type_of`. pub fn dump_mir_to_writer(&self, body: &Body<'tcx>, w: &mut dyn io::Write) -> io::Result<()> { // see notes on #41697 above - let def_path = ty::print::with_forced_impl_filename_line!( - self.tcx().def_path_str(body.source.def_id()) - ); + let def_path = + ty::print::with_no_trimmed_paths!(ty::print::with_forced_impl_filename_line!( + self.tcx().def_path_str(body.source.def_id()) + )); // ignore-tidy-odd-backticks the literal below is fine write!(w, "// MIR for `{def_path}")?; match body.source.promoted { diff --git a/tests/mir-opt/retag.core.ptr-drop_in_place.Test.SimplifyCfg-make_shim.after.panic-abort.mir b/tests/mir-opt/retag.core.ptr-drop_in_place.Test.SimplifyCfg-make_shim.after.panic-abort.mir index 7be3ab8cbae9..8e47aabb9b9f 100644 --- a/tests/mir-opt/retag.core.ptr-drop_in_place.Test.SimplifyCfg-make_shim.after.panic-abort.mir +++ b/tests/mir-opt/retag.core.ptr-drop_in_place.Test.SimplifyCfg-make_shim.after.panic-abort.mir @@ -1,4 +1,4 @@ -// MIR for `drop_in_place` after SimplifyCfg-make_shim +// MIR for `std::ptr::drop_in_place` after SimplifyCfg-make_shim fn drop_in_place(_1: *mut Test) -> () { let mut _0: (); diff --git a/tests/mir-opt/retag.core.ptr-drop_in_place.Test.SimplifyCfg-make_shim.after.panic-unwind.mir b/tests/mir-opt/retag.core.ptr-drop_in_place.Test.SimplifyCfg-make_shim.after.panic-unwind.mir index 6c3c1aaa2bd2..2457405d9969 100644 --- a/tests/mir-opt/retag.core.ptr-drop_in_place.Test.SimplifyCfg-make_shim.after.panic-unwind.mir +++ b/tests/mir-opt/retag.core.ptr-drop_in_place.Test.SimplifyCfg-make_shim.after.panic-unwind.mir @@ -1,4 +1,4 @@ -// MIR for `drop_in_place` after SimplifyCfg-make_shim +// MIR for `std::ptr::drop_in_place` after SimplifyCfg-make_shim fn drop_in_place(_1: *mut Test) -> () { let mut _0: (); diff --git a/tests/mir-opt/slice_drop_shim.core.ptr-drop_in_place.[String;42].AddMovesForPackedDrops.before.mir b/tests/mir-opt/slice_drop_shim.core.ptr-drop_in_place.[String;42].AddMovesForPackedDrops.before.mir index 9d5af8e84e4f..ed3f4788cea7 100644 --- a/tests/mir-opt/slice_drop_shim.core.ptr-drop_in_place.[String;42].AddMovesForPackedDrops.before.mir +++ b/tests/mir-opt/slice_drop_shim.core.ptr-drop_in_place.[String;42].AddMovesForPackedDrops.before.mir @@ -1,4 +1,4 @@ -// MIR for `drop_in_place` before AddMovesForPackedDrops +// MIR for `std::ptr::drop_in_place` before AddMovesForPackedDrops fn drop_in_place(_1: *mut [String; 42]) -> () { let mut _0: (); diff --git a/tests/mir-opt/slice_drop_shim.core.ptr-drop_in_place.[String].AddMovesForPackedDrops.before.mir b/tests/mir-opt/slice_drop_shim.core.ptr-drop_in_place.[String].AddMovesForPackedDrops.before.mir index 144880d15989..bee671af6dfe 100644 --- a/tests/mir-opt/slice_drop_shim.core.ptr-drop_in_place.[String].AddMovesForPackedDrops.before.mir +++ b/tests/mir-opt/slice_drop_shim.core.ptr-drop_in_place.[String].AddMovesForPackedDrops.before.mir @@ -1,4 +1,4 @@ -// MIR for `drop_in_place` before AddMovesForPackedDrops +// MIR for `std::ptr::drop_in_place` before AddMovesForPackedDrops fn drop_in_place(_1: *mut [String]) -> () { let mut _0: (); diff --git a/tests/mir-opt/unusual_item_types.core.ptr-drop_in_place.Vec_i32_.AddMovesForPackedDrops.before.mir b/tests/mir-opt/unusual_item_types.core.ptr-drop_in_place.Vec_i32_.AddMovesForPackedDrops.before.mir index 51ef9f7c068e..1bdb1c1debdb 100644 --- a/tests/mir-opt/unusual_item_types.core.ptr-drop_in_place.Vec_i32_.AddMovesForPackedDrops.before.mir +++ b/tests/mir-opt/unusual_item_types.core.ptr-drop_in_place.Vec_i32_.AddMovesForPackedDrops.before.mir @@ -1,4 +1,4 @@ -// MIR for `drop_in_place` before AddMovesForPackedDrops +// MIR for `std::ptr::drop_in_place` before AddMovesForPackedDrops fn drop_in_place(_1: *mut Vec) -> () { let mut _0: (); From 4a4b77f1c75e48944507fd69e75db36a1658f800 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 10 Sep 2025 03:32:06 +0300 Subject: [PATCH 330/682] Properly handle normalization Previously normalization was broken, which caused a lot of fake errors. This fix most type mismatches of the new solver, and it also reverts many test regressions. The downside is that now `chalk_ir::TyKind::AssociatedType`/`chalk_ir::TyKind::Alias` cannot be trusted anymore with their roles, namely: `AssociatedType` is always fully normalized and `Alias` only if it can possibly be normalized further. That seems okay as the new solver does not have this distinction at all (due to it being a lazy normalizer), so this will only hold for the migration time. This does mean we have to change some APIs, notably `hir::Type::walk()` and `TyFingerprint`, to treat `Alias` the same as `AssociatedType`. Another small thing this commit does is to isolate processing of user-written types (currently involving replacing error types and normalizing, but in the future validation will also be needed) to separate functions. --- .../rust-analyzer/crates/hir-ty/src/infer.rs | 50 +++- .../crates/hir-ty/src/infer/closure.rs | 10 +- .../crates/hir-ty/src/infer/expr.rs | 8 +- .../crates/hir-ty/src/infer/pat.rs | 4 +- .../crates/hir-ty/src/infer/path.rs | 17 +- .../crates/hir-ty/src/infer/unify.rs | 242 +++++++++--------- .../crates/hir-ty/src/method_resolution.rs | 21 +- .../crates/hir-ty/src/tests/macros.rs | 4 +- .../crates/hir-ty/src/tests/never_type.rs | 24 +- .../crates/hir-ty/src/tests/patterns.rs | 8 +- .../crates/hir-ty/src/tests/regression.rs | 12 +- .../hir-ty/src/tests/regression/new_solver.rs | 2 +- .../crates/hir-ty/src/tests/simple.rs | 2 +- .../crates/hir-ty/src/tests/traits.rs | 18 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 9 +- 15 files changed, 234 insertions(+), 197 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 265d1f8541cc..3d91a2558f0b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -54,6 +54,8 @@ use rustc_hash::{FxHashMap, FxHashSet}; use stdx::{always, never}; use triomphe::Arc; +use crate::next_solver::DbInterner; +use crate::next_solver::mapping::NextSolverToChalk; use crate::{ AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, ImplTraitId, ImplTraitIdx, IncorrectGenericsLenKind, Interner, Lifetime, OpaqueTyId, ParamLoweringMode, @@ -922,13 +924,15 @@ impl<'db> InferenceContext<'db> { }); diagnostics.shrink_to_fit(); for (_, subst) in method_resolutions.values_mut() { - *subst = table.resolve_completely(subst.clone()); + *subst = + table.resolve_completely::<_, crate::next_solver::GenericArgs<'db>>(subst.clone()); *has_errors = *has_errors || subst.type_parameters(Interner).any(|ty| ty.contains_unknown()); } method_resolutions.shrink_to_fit(); for (_, subst) in assoc_resolutions.values_mut() { - *subst = table.resolve_completely(subst.clone()); + *subst = + table.resolve_completely::<_, crate::next_solver::GenericArgs<'db>>(subst.clone()); *has_errors = *has_errors || subst.type_parameters(Interner).any(|ty| ty.contains_unknown()); } @@ -946,7 +950,12 @@ impl<'db> InferenceContext<'db> { result.tuple_field_access_types = tuple_field_accesses_rev .into_iter() .enumerate() - .map(|(idx, subst)| (TupleId(idx as u32), table.resolve_completely(subst))) + .map(|(idx, subst)| { + ( + TupleId(idx as u32), + table.resolve_completely::<_, crate::next_solver::GenericArgs<'db>>(subst), + ) + }) .inspect(|(_, subst)| { *has_errors = *has_errors || subst.type_parameters(Interner).any(|ty| ty.contains_unknown()); @@ -1015,14 +1024,12 @@ impl<'db> InferenceContext<'db> { if let Some(self_param) = self.body.self_param && let Some(ty) = param_tys.next() { - let ty = self.insert_type_vars(ty); - let ty = self.normalize_associated_types_in(ty); + let ty = self.process_user_written_ty(ty); self.write_binding_ty(self_param, ty); } let mut tait_candidates = FxHashSet::default(); for (ty, pat) in param_tys.zip(&*self.body.params) { - let ty = self.insert_type_vars(ty); - let ty = self.normalize_associated_types_in(ty); + let ty = self.process_user_written_ty(ty); self.infer_top_pat(*pat, &ty, None); if ty @@ -1073,7 +1080,7 @@ impl<'db> InferenceContext<'db> { None => self.result.standard_types.unit.clone(), }; - self.return_ty = self.normalize_associated_types_in(return_ty); + self.return_ty = self.process_user_written_ty(return_ty); self.return_coercion = Some(CoerceMany::new(self.return_ty.clone())); // Functions might be defining usage sites of TAITs. @@ -1415,8 +1422,7 @@ impl<'db> InferenceContext<'db> { ) -> Ty { let ty = self .with_ty_lowering(store, type_source, lifetime_elision, |ctx| ctx.lower_ty(type_ref)); - let ty = self.insert_type_vars(ty); - self.normalize_associated_types_in(ty) + self.process_user_written_ty(ty) } fn make_body_ty(&mut self, type_ref: TypeRefId) -> Ty { @@ -1562,15 +1568,35 @@ impl<'db> InferenceContext<'db> { ty } + /// Whenever you lower a user-written type, you should call this. + fn process_user_written_ty(&mut self, ty: T) -> T + where + T: HasInterner + TypeFoldable + ChalkToNextSolver<'db, U>, + U: NextSolverToChalk<'db, T> + rustc_type_ir::TypeFoldable>, + { + self.table.process_user_written_ty(ty) + } + + /// The difference of this method from `process_user_written_ty()` is that this method doesn't register a well-formed obligation, + /// while `process_user_written_ty()` should (but doesn't currently). + fn process_remote_user_written_ty(&mut self, ty: T) -> T + where + T: HasInterner + TypeFoldable + ChalkToNextSolver<'db, U>, + U: NextSolverToChalk<'db, T> + rustc_type_ir::TypeFoldable>, + { + self.table.process_remote_user_written_ty(ty) + } + /// Recurses through the given type, normalizing associated types mentioned /// in it by replacing them by type variables and registering obligations to /// resolve later. This should be done once for every type we get from some /// type annotation (e.g. from a let type annotation, field type or function /// call). `make_ty` handles this already, but e.g. for field types we need /// to do it as well. - fn normalize_associated_types_in(&mut self, ty: T) -> T + fn normalize_associated_types_in(&mut self, ty: T) -> T where - T: HasInterner + TypeFoldable, + T: HasInterner + TypeFoldable + ChalkToNextSolver<'db, U>, + U: NextSolverToChalk<'db, T> + rustc_type_ir::TypeFoldable>, { self.table.normalize_associated_types_in(ty) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 5a69ad68b58e..fd7e5a6a0e12 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -53,7 +53,7 @@ pub(super) struct ClosureSignature { pub(super) expected_sig: FnPointer, } -impl InferenceContext<'_> { +impl<'db> InferenceContext<'db> { pub(super) fn infer_closure( &mut self, body: &ExprId, @@ -71,9 +71,13 @@ impl InferenceContext<'_> { None => (None, None), }; - let ClosureSignature { expected_sig: bound_sig, ret_ty: body_ret_ty } = + let ClosureSignature { expected_sig: mut bound_sig, ret_ty: body_ret_ty } = self.sig_of_closure(body, ret_type, arg_types, closure_kind, expected_sig); - let bound_sig = self.normalize_associated_types_in(bound_sig); + bound_sig.substitution.0 = self + .normalize_associated_types_in::<_, crate::next_solver::GenericArgs<'db>>( + bound_sig.substitution.0, + ); + let bound_sig = bound_sig; let sig_ty = TyKind::Function(bound_sig.clone()).intern(Interner); let (id, ty, resume_yield_tys) = match closure_kind { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index bfeb5bae851a..0a58ea11bb87 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -1419,7 +1419,7 @@ impl InferenceContext<'_> { None => self.err_ty(), }; - let ret_ty = self.normalize_associated_types_in(ret_ty); + let ret_ty = self.process_remote_user_written_ty(ret_ty); if self.is_builtin_binop(&lhs_ty, &rhs_ty, op) { // use knowledge of built-in binary ops, which can sometimes help inference @@ -1630,8 +1630,7 @@ impl InferenceContext<'_> { Some(match res { Some((field_id, ty)) => { let adjustments = auto_deref_adjust_steps(&autoderef); - let ty = self.insert_type_vars(ty); - let ty = self.normalize_associated_types_in(ty); + let ty = self.process_remote_user_written_ty(ty); (ty, field_id, adjustments, true) } @@ -1641,8 +1640,7 @@ impl InferenceContext<'_> { let ty = self.db.field_types(field_id.parent)[field_id.local_id] .clone() .substitute(Interner, &subst); - let ty = self.insert_type_vars(ty); - let ty = self.normalize_associated_types_in(ty); + let ty = self.process_remote_user_written_ty(ty); (ty, Either::Left(field_id), adjustments, false) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index 16489e3068f3..6781bc84d1c1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -88,7 +88,7 @@ impl InferenceContext<'_> { Some(substs) => f.substitute(Interner, substs), None => f.substitute(Interner, &Substitution::empty(Interner)), }; - self.normalize_associated_types_in(expected_ty) + self.process_remote_user_written_ty(expected_ty) } None => self.err_ty(), } @@ -152,7 +152,7 @@ impl InferenceContext<'_> { Some(substs) => f.substitute(Interner, substs), None => f.substitute(Interner, &Substitution::empty(Interner)), }; - self.normalize_associated_types_in(expected_ty) + self.process_remote_user_written_ty(expected_ty) } None => { self.push_diagnostic(InferenceDiagnostic::NoSuchField { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index 1f62f9c4aa11..80f7324e58b2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -23,7 +23,7 @@ use crate::{ use super::{ExprOrPatId, InferenceContext, InferenceTyDiagnosticSource}; -impl InferenceContext<'_> { +impl<'db> InferenceContext<'db> { pub(super) fn infer_path(&mut self, path: &Path, id: ExprOrPatId) -> Option { let (value_def, generic_def, substs) = match self.resolve_value_path(path, id)? { ValuePathResolution::GenericDef(value_def, generic_def, substs) => { @@ -31,13 +31,13 @@ impl InferenceContext<'_> { } ValuePathResolution::NonGeneric(ty) => return Some(ty), }; - let substs = self.insert_type_vars(substs); - let substs = self.normalize_associated_types_in(substs); + let substs = + self.process_remote_user_written_ty::<_, crate::next_solver::GenericArgs<'db>>(substs); self.add_required_obligations_for_value_path(generic_def, &substs); let ty = self.db.value_ty(value_def)?.substitute(Interner, &substs); - let ty = self.normalize_associated_types_in(ty); + let ty = self.process_remote_user_written_ty(ty); Some(ty) } @@ -173,14 +173,12 @@ impl InferenceContext<'_> { let last = path.segments().last()?; let (ty, orig_ns) = path_ctx.ty_ctx().lower_ty_ext(type_ref); - let ty = self.table.insert_type_vars(ty); - let ty = self.table.normalize_associated_types_in(ty); + let ty = self.table.process_user_written_ty(ty); path_ctx.ignore_last_segment(); let (ty, _) = path_ctx.lower_ty_relative_path(ty, orig_ns, true); drop_ctx(ctx, no_diagnostics); - let ty = self.table.insert_type_vars(ty); - let ty = self.table.normalize_associated_types_in(ty); + let ty = self.table.process_user_written_ty(ty); self.resolve_ty_assoc_item(ty, last.name, id).map(|(it, substs)| (it, Some(substs)))? } else { let hygiene = self.body.expr_or_pat_path_hygiene(id); @@ -223,8 +221,7 @@ impl InferenceContext<'_> { return None; } - let ty = self.insert_type_vars(ty); - let ty = self.normalize_associated_types_in(ty); + let ty = self.process_user_written_ty(ty); self.resolve_ty_assoc_item(ty, last_segment.name, id) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index ec4b7ee85dc6..19b83d3c212d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -12,12 +12,14 @@ use hir_expand::name::Name; use intern::sym; use rustc_hash::{FxHashMap, FxHashSet}; use rustc_next_trait_solver::solve::HasChanged; +use rustc_type_ir::inherent::IntoKind; use rustc_type_ir::{ AliasRelationDirection, FloatVid, IntVid, TyVid, inherent::{Span, Term as _}, relate::{Relate, solver_relating::RelateExt}, solve::{Certainty, NoSolution}, }; +use rustc_type_ir::{TypeSuperFoldable, TypeVisitableExt}; use smallvec::SmallVec; use triomphe::Arc; @@ -31,11 +33,8 @@ use crate::{ db::HirDatabase, fold_generic_args, fold_tys_and_consts, next_solver::{ - self, Binder, DbInterner, ParamEnvAnd, Predicate, PredicateKind, SolverDefIds, Term, - infer::{ - DbInternerInferExt, InferCtxt, canonical::canonicalizer::OriginalQueryValues, - snapshot::CombinedSnapshot, - }, + self, Binder, DbInterner, Predicate, PredicateKind, SolverDefIds, Term, + infer::{DbInternerInferExt, InferCtxt, snapshot::CombinedSnapshot}, mapping::{ChalkToNextSolver, InferenceVarExt, NextSolverToChalk}, }, to_chalk_trait_id, @@ -305,116 +304,21 @@ impl<'a> InferenceTable<'a> { /// type annotation (e.g. from a let type annotation, field type or function /// call). `make_ty` handles this already, but e.g. for field types we need /// to do it as well. - #[tracing::instrument(skip(self), ret)] - pub(crate) fn normalize_associated_types_in(&mut self, ty: T) -> T + pub(crate) fn normalize_associated_types_in(&mut self, ty: T) -> T where - T: HasInterner + TypeFoldable, + T: ChalkToNextSolver<'a, U>, + U: NextSolverToChalk<'a, T> + rustc_type_ir::TypeFoldable>, { - fold_tys_and_consts( - ty, - |e, _| match e { - Either::Left(ty) => { - let ty = self.resolve_ty_shallow(&ty); - tracing::debug!(?ty); - Either::Left(match ty.kind(Interner) { - TyKind::Alias(AliasTy::Projection(proj_ty)) => { - let ty = self.normalize_projection_ty(proj_ty.clone()); - self.resolve_ty_shallow(&ty) - } - TyKind::AssociatedType(id, subst) => { - // return Either::Left(self.resolve_ty_shallow(&ty)); - if ty.data(Interner).flags.intersects( - chalk_ir::TypeFlags::HAS_TY_INFER - | chalk_ir::TypeFlags::HAS_CT_INFER, - ) { - return Either::Left(ty); - } - let var = self.new_type_var(); - let proj_ty = chalk_ir::ProjectionTy { - associated_ty_id: *id, - substitution: subst.clone(), - }; - let normalize = chalk_ir::Normalize { - alias: AliasTy::Projection(proj_ty), - ty: var.clone(), - }; - let goal = chalk_ir::Goal::new( - Interner, - chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Normalize( - normalize, - )), - ); - let in_env = InEnvironment::new(&self.trait_env.env, goal); - let goal = in_env.to_nextsolver(self.interner); - let goal = - ParamEnvAnd { param_env: goal.param_env, value: goal.predicate }; + self.normalize_associated_types_in_ns(ty.to_nextsolver(self.interner)) + .to_chalk(self.interner) + } - let (canonical_goal, orig_values) = { - let mut orig_values = OriginalQueryValues::default(); - let result = - self.infer_ctxt.canonicalize_query(goal, &mut orig_values); - (result.canonical, orig_values) - }; - let canonical_goal = rustc_type_ir::Canonical { - max_universe: canonical_goal.max_universe, - variables: canonical_goal.variables, - value: crate::next_solver::Goal { - param_env: canonical_goal.value.param_env, - predicate: canonical_goal.value.value, - }, - }; - let solution = next_trait_solve_canonical_in_ctxt( - &self.infer_ctxt, - canonical_goal, - ); - if let NextTraitSolveResult::Certain(canonical_subst) = solution { - let subst = self.instantiate_canonical(canonical_subst).subst; - if subst.len(Interner) != orig_values.var_values.len() { - ty - } else { - let target_ty = var.to_nextsolver(self.interner); - subst - .iter(Interner) - .zip(orig_values.var_values.iter()) - .find_map(|(new, orig)| { - if orig.ty() == Some(target_ty) { - Some(new.assert_ty_ref(Interner).clone()) - } else { - None - } - }) - .unwrap_or(ty) - } - } else { - ty - } - } - _ => ty, - }) - } - Either::Right(c) => Either::Right(match &c.data(Interner).value { - chalk_ir::ConstValue::Concrete(cc) => match &cc.interned { - crate::ConstScalar::UnevaluatedConst(c_id, subst) => { - // FIXME: Ideally here we should do everything that we do with type alias, i.e. adding a variable - // and registering an obligation. But it needs chalk support, so we handle the most basic - // case (a non associated const without generic parameters) manually. - if subst.len(Interner) == 0 { - if let Ok(eval) = self.db.const_eval(*c_id, subst.clone(), None) { - eval - } else { - unknown_const(c.data(Interner).ty.clone()) - } - } else { - unknown_const(c.data(Interner).ty.clone()) - } - } - _ => c, - }, - _ => c, - }), - }, - DebruijnIndex::INNERMOST, - ) + pub(crate) fn normalize_associated_types_in_ns(&mut self, ty: T) -> T + where + T: rustc_type_ir::TypeFoldable>, + { + let ty = self.resolve_vars_with_obligations(ty); + ty.fold_with(&mut Normalizer { table: self }) } /// Works almost same as [`Self::normalize_associated_types_in`], but this also resolves shallow @@ -476,11 +380,27 @@ impl<'a> InferenceTable<'a> { } pub(crate) fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty { - let var = self.new_type_var(); - let alias_eq = AliasEq { alias: AliasTy::Projection(proj_ty), ty: var.clone() }; - let obligation: Goal = alias_eq.cast(Interner); - self.register_obligation(obligation.to_nextsolver(self.interner)); - var + let ty = TyKind::Alias(chalk_ir::AliasTy::Projection(proj_ty)) + .intern(Interner) + .to_nextsolver(self.interner); + self.normalize_alias_ty(ty).to_chalk(self.interner) + } + + pub(crate) fn normalize_alias_ty( + &mut self, + alias: crate::next_solver::Ty<'a>, + ) -> crate::next_solver::Ty<'a> { + let infer_term = self.infer_ctxt.next_ty_var(); + let obligation = crate::next_solver::Predicate::new( + self.interner, + crate::next_solver::Binder::dummy(crate::next_solver::PredicateKind::AliasRelate( + alias.into(), + infer_term.into(), + rustc_type_ir::AliasRelationDirection::Equate, + )), + ); + self.register_obligation(obligation); + self.resolve_vars_with_obligations(infer_term) } fn new_var(&mut self, kind: TyVariableKind, diverging: bool) -> Ty { @@ -591,9 +511,10 @@ impl<'a> InferenceTable<'a> { ) } - pub(crate) fn resolve_completely(&mut self, t: T) -> T + pub(crate) fn resolve_completely(&mut self, t: T) -> T where - T: HasInterner + TypeFoldable, + T: HasInterner + TypeFoldable + ChalkToNextSolver<'a, U>, + U: NextSolverToChalk<'a, T> + rustc_type_ir::TypeFoldable>, { let t = self.resolve_with_fallback(t, &|_, _, d, _| d); let t = self.normalize_associated_types_in(t); @@ -1045,6 +966,30 @@ impl<'a> InferenceTable<'a> { } } + /// Whenever you lower a user-written type, you should call this. + pub(crate) fn process_user_written_ty(&mut self, ty: T) -> T + where + T: HasInterner + TypeFoldable + ChalkToNextSolver<'a, U>, + U: NextSolverToChalk<'a, T> + rustc_type_ir::TypeFoldable>, + { + self.process_remote_user_written_ty(ty) + // FIXME: Register a well-formed obligation. + } + + /// The difference of this method from `process_user_written_ty()` is that this method doesn't register a well-formed obligation, + /// while `process_user_written_ty()` should (but doesn't currently). + pub(crate) fn process_remote_user_written_ty(&mut self, ty: T) -> T + where + T: HasInterner + TypeFoldable + ChalkToNextSolver<'a, U>, + U: NextSolverToChalk<'a, T> + rustc_type_ir::TypeFoldable>, + { + let ty = self.insert_type_vars(ty); + // See https://github.com/rust-lang/rust/blob/cdb45c87e2cd43495379f7e867e3cc15dcee9f93/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs#L487-L495: + // Even though the new solver only lazily normalizes usually, here we eagerly normalize so that not everything needs + // to normalize before inspecting the `TyKind`. + self.normalize_associated_types_in(ty) + } + /// Replaces ConstScalar::Unknown by a new type var, so we can maybe still infer it. pub(super) fn insert_const_vars_shallow(&mut self, c: Const) -> Const { let data = c.data(Interner); @@ -1319,3 +1264,62 @@ mod resolve { } } } + +/// This expects its input to be resolved. +struct Normalizer<'a, 'b> { + table: &'a mut InferenceTable<'b>, +} + +impl<'db> Normalizer<'_, 'db> { + fn normalize_alias_term( + &mut self, + alias_term: crate::next_solver::Term<'db>, + ) -> crate::next_solver::Term<'db> { + let infer_term = self.table.infer_ctxt.next_term_var_of_kind(alias_term); + let obligation = crate::next_solver::Predicate::new( + self.table.interner, + crate::next_solver::Binder::dummy(crate::next_solver::PredicateKind::AliasRelate( + alias_term, + infer_term, + rustc_type_ir::AliasRelationDirection::Equate, + )), + ); + self.table.register_obligation(obligation); + let term = self.table.resolve_vars_with_obligations(infer_term); + // Now normalize the result, because maybe it contains more aliases. + match term { + Term::Ty(term) => term.super_fold_with(self).into(), + Term::Const(term) => term.super_fold_with(self).into(), + } + } +} + +impl<'db> rustc_type_ir::TypeFolder> for Normalizer<'_, 'db> { + fn cx(&self) -> DbInterner<'db> { + self.table.interner + } + + fn fold_ty(&mut self, ty: crate::next_solver::Ty<'db>) -> crate::next_solver::Ty<'db> { + if !ty.has_aliases() { + return ty; + } + + let crate::next_solver::TyKind::Alias(..) = ty.kind() else { + return ty.super_fold_with(self); + }; + // FIXME: Handle escaping bound vars by replacing them with placeholders (relevant to when we handle HRTB only). + self.normalize_alias_term(ty.into()).expect_type() + } + + fn fold_const(&mut self, ct: crate::next_solver::Const<'db>) -> crate::next_solver::Const<'db> { + if !ct.has_aliases() { + return ct; + } + + let crate::next_solver::ConstKind::Unevaluated(..) = ct.kind() else { + return ct.super_fold_with(self); + }; + // FIXME: Handle escaping bound vars by replacing them with placeholders (relevant to when we handle HRTB only). + self.normalize_alias_term(ct.into()).expect_const() + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index fa80567b1ecc..a234312173a2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -22,10 +22,9 @@ use stdx::never; use triomphe::Arc; use crate::{ - AdtId, AliasTy, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, - GenericArgData, Goal, InEnvironment, Interner, Mutability, Scalar, Substitution, - TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, TyVariableKind, - VariableKind, WhereClause, + AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, GenericArgData, + Goal, InEnvironment, Interner, Mutability, Scalar, Substitution, TraitEnvironment, TraitRef, + TraitRefExt, Ty, TyBuilder, TyExt, TyKind, TyVariableKind, VariableKind, WhereClause, autoderef::{self, AutoderefKind}, db::HirDatabase, from_chalk_trait_id, from_foreign_def_id, @@ -106,8 +105,12 @@ impl TyFingerprint { } } TyKind::AssociatedType(_, _) + // FIXME(next-solver): Putting `Alias` here is *probably* incorrect, AFAIK it should return `None`. But this breaks + // flyimport, which uses an incorrect but fast method resolution algorithm. Therefore we put it here, + // because this function is only called by flyimport, and anyway we should get rid of `TyFingerprint` + // and switch to `rustc_type_ir`'s `SimplifiedType`. + | TyKind::Alias(_) | TyKind::OpaqueType(_, _) - | TyKind::Alias(AliasTy::Opaque(_)) | TyKind::FnDef(_, _) | TyKind::Closure(_, _) | TyKind::Coroutine(..) @@ -115,8 +118,7 @@ impl TyFingerprint { TyKind::Function(fn_ptr) => { TyFingerprint::Function(fn_ptr.substitution.0.len(Interner) as u32) } - TyKind::Alias(_) - | TyKind::Placeholder(_) + TyKind::Placeholder(_) | TyKind::BoundVar(_) | TyKind::InferenceVar(_, _) | TyKind::Error => return None, @@ -908,7 +910,10 @@ fn find_matching_impl( } table.register_obligation(goal.to_nextsolver(table.interner)); } - Some((impl_.impl_items(db), table.resolve_completely(impl_substs))) + Some(( + impl_.impl_items(db), + table.resolve_completely::<_, crate::next_solver::GenericArgs<'_>>(impl_substs), + )) }) }) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs index 6490554b22b7..5d088e40cded 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs @@ -202,7 +202,7 @@ fn expr_macro_def_expanded_in_various_places() { 100..119 'for _ ...!() {}': {unknown} 100..119 'for _ ...!() {}': &'? mut {unknown} 100..119 'for _ ...!() {}': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 100..119 'for _ ...!() {}': Option<<{unknown} as Iterator>::Item> + 100..119 'for _ ...!() {}': Option<{unknown}> 100..119 'for _ ...!() {}': () 100..119 'for _ ...!() {}': () 100..119 'for _ ...!() {}': () @@ -296,7 +296,7 @@ fn expr_macro_rules_expanded_in_various_places() { 114..133 'for _ ...!() {}': {unknown} 114..133 'for _ ...!() {}': &'? mut {unknown} 114..133 'for _ ...!() {}': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 114..133 'for _ ...!() {}': Option<<{unknown} as Iterator>::Item> + 114..133 'for _ ...!() {}': Option<{unknown}> 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs index baca7f2318e2..6a9135622deb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs @@ -362,12 +362,12 @@ fn diverging_expression_3_break() { 140..141 'x': u32 149..175 '{ for ...; }; }': u32 151..172 'for a ...eak; }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 151..172 'for a ...eak; }': <{unknown} as IntoIterator>::IntoIter + 151..172 'for a ...eak; }': {unknown} 151..172 'for a ...eak; }': ! - 151..172 'for a ...eak; }': <{unknown} as IntoIterator>::IntoIter - 151..172 'for a ...eak; }': &'? mut <{unknown} as IntoIterator>::IntoIter + 151..172 'for a ...eak; }': {unknown} + 151..172 'for a ...eak; }': &'? mut {unknown} 151..172 'for a ...eak; }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 151..172 'for a ...eak; }': Option<<{unknown} as Iterator>::Item> + 151..172 'for a ...eak; }': Option<{unknown}> 151..172 'for a ...eak; }': () 151..172 'for a ...eak; }': () 151..172 'for a ...eak; }': () @@ -379,12 +379,12 @@ fn diverging_expression_3_break() { 226..227 'x': u32 235..253 '{ for ... {}; }': u32 237..250 'for a in b {}': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 237..250 'for a in b {}': <{unknown} as IntoIterator>::IntoIter + 237..250 'for a in b {}': {unknown} 237..250 'for a in b {}': ! - 237..250 'for a in b {}': <{unknown} as IntoIterator>::IntoIter - 237..250 'for a in b {}': &'? mut <{unknown} as IntoIterator>::IntoIter + 237..250 'for a in b {}': {unknown} + 237..250 'for a in b {}': &'? mut {unknown} 237..250 'for a in b {}': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 237..250 'for a in b {}': Option<<{unknown} as Iterator>::Item> + 237..250 'for a in b {}': Option<{unknown}> 237..250 'for a in b {}': () 237..250 'for a in b {}': () 237..250 'for a in b {}': () @@ -395,12 +395,12 @@ fn diverging_expression_3_break() { 304..305 'x': u32 313..340 '{ for ...; }; }': u32 315..337 'for a ...urn; }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 315..337 'for a ...urn; }': <{unknown} as IntoIterator>::IntoIter + 315..337 'for a ...urn; }': {unknown} 315..337 'for a ...urn; }': ! - 315..337 'for a ...urn; }': <{unknown} as IntoIterator>::IntoIter - 315..337 'for a ...urn; }': &'? mut <{unknown} as IntoIterator>::IntoIter + 315..337 'for a ...urn; }': {unknown} + 315..337 'for a ...urn; }': &'? mut {unknown} 315..337 'for a ...urn; }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 315..337 'for a ...urn; }': Option<<{unknown} as Iterator>::Item> + 315..337 'for a ...urn; }': Option<{unknown}> 315..337 'for a ...urn; }': () 315..337 'for a ...urn; }': () 315..337 'for a ...urn; }': () diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs index 60a2641e1a20..02cb03706919 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs @@ -48,12 +48,12 @@ fn infer_pattern() { 83..84 '1': i32 86..93 '"hello"': &'static str 101..151 'for (e... }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 101..151 'for (e... }': <{unknown} as IntoIterator>::IntoIter + 101..151 'for (e... }': {unknown} 101..151 'for (e... }': ! - 101..151 'for (e... }': <{unknown} as IntoIterator>::IntoIter - 101..151 'for (e... }': &'? mut <{unknown} as IntoIterator>::IntoIter + 101..151 'for (e... }': {unknown} + 101..151 'for (e... }': &'? mut {unknown} 101..151 'for (e... }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 101..151 'for (e... }': Option<<{unknown} as Iterator>::Item> + 101..151 'for (e... }': Option<({unknown}, {unknown})> 101..151 'for (e... }': () 101..151 'for (e... }': () 101..151 'for (e... }': () diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index eacc4603ea1a..6a3f2286215f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -268,12 +268,12 @@ fn infer_std_crash_5() { expect![[r#" 26..322 '{ ... } }': () 32..320 'for co... }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 32..320 'for co... }': <{unknown} as IntoIterator>::IntoIter + 32..320 'for co... }': {unknown} 32..320 'for co... }': ! - 32..320 'for co... }': <{unknown} as IntoIterator>::IntoIter - 32..320 'for co... }': &'? mut <{unknown} as IntoIterator>::IntoIter + 32..320 'for co... }': {unknown} + 32..320 'for co... }': &'? mut {unknown} 32..320 'for co... }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 32..320 'for co... }': Option<<{unknown} as Iterator>::Item> + 32..320 'for co... }': Option<{unknown}> 32..320 'for co... }': () 32..320 'for co... }': () 32..320 'for co... }': () @@ -628,7 +628,7 @@ fn issue_4053_diesel_where_clauses() { 65..69 'self': Self 267..271 'self': Self 466..470 'self': SelectStatement - 488..522 '{ ... }': as BoxedDsl>::Output + 488..522 '{ ... }': () 498..502 'self': SelectStatement 498..508 'self.order': O 498..515 'self.o...into()': dyn QueryFragment + '? @@ -1248,7 +1248,7 @@ fn test() { 16..66 'for _ ... }': {unknown} 16..66 'for _ ... }': &'? mut {unknown} 16..66 'for _ ... }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 16..66 'for _ ... }': Option<<{unknown} as Iterator>::Item> + 16..66 'for _ ... }': Option<{unknown}> 16..66 'for _ ... }': () 16..66 'for _ ... }': () 16..66 'for _ ... }': () diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs index 471108d964f6..6d6c56696a30 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -20,7 +20,7 @@ impl<'a> IntoIterator for &'a Grid { "#, expect![[r#" 150..154 'self': &'a Grid - 174..181 '{ }': impl Iterator + 174..181 '{ }': impl Iterator "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index e7357ed5aa78..60ad0f49c6ab 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -2000,7 +2000,7 @@ fn test() { 225..360 'match ... }': () 231..239 'Pin::new': fn new<&'? mut |usize| yields i64 -> &'static str>(&'? mut |usize| yields i64 -> &'static str) -> Pin<&'? mut |usize| yields i64 -> &'static str> 231..247 'Pin::n...mut g)': Pin<&'? mut |usize| yields i64 -> &'static str> - 231..262 'Pin::n...usize)': CoroutineState + 231..262 'Pin::n...usize)': CoroutineState 240..246 '&mut g': &'? mut |usize| yields i64 -> &'static str 245..246 'g': |usize| yields i64 -> &'static str 255..261 '0usize': usize diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index 7a946f7ec7ce..22332fdc2b8a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -4190,8 +4190,6 @@ fn g(p:

::Pointer) { ); } -// FIXME(next-solver): Was `&'a T` but now getting error lifetime. -// This might be fixed once we migrate into next-solver fully without chalk-ir in lowering. #[test] fn gats_with_impl_trait() { // FIXME: the last function (`fn i()`) is not valid Rust as of this writing because you cannot @@ -4215,21 +4213,21 @@ fn f(v: impl Trait) { } fn g<'a, T: 'a>(v: impl Trait = &'a T>) { let a = v.get::(); - //^ &'? T + //^ &'a T let a = v.get::<()>(); //^ = &'a T> as Trait>::Assoc<()> } fn h<'a>(v: impl Trait = &'a i32> + Trait = &'a i64>) { let a = v.get::(); - //^ &'? i32 + //^ &'a i32 let a = v.get::(); - //^ &'? i64 + //^ &'a i64 } fn i<'a>(v: impl Trait = &'a i32, Assoc = &'a i64>) { let a = v.get::(); - //^ &'? i32 + //^ &'a i32 let a = v.get::(); - //^ &'? i64 + //^ &'a i64 } "#, ); @@ -4259,8 +4257,8 @@ fn f<'a>(v: &dyn Trait = &'a i32>) { 127..128 'v': &'? (dyn Trait = &'a i32> + '?) 164..195 '{ ...f(); }': () 170..171 'v': &'? (dyn Trait = &'a i32> + '?) - 170..184 'v.get::()': = &'a i32> + '? as Trait>::Assoc - 170..192 'v.get:...eref()': {unknown} + 170..184 'v.get::()': {unknown} + 170..192 'v.get:...eref()': &'? {unknown} "#]], ); } @@ -4953,7 +4951,7 @@ where "#, expect![[r#" 84..86 'de': D - 135..138 '{ }': >::Error + 135..138 '{ }': >::Error "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 52ab808d2284..888392b0ff20 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -76,7 +76,7 @@ use hir_expand::{ }; use hir_ty::{ AliasTy, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId, GenericArg, - GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar, Substitution, + GenericArgData, Interner, ParamKind, ProjectionTy, QuantifiedWhereClause, Scalar, Substitution, TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, TyLoweringDiagnostic, ValueTyDefId, WhereClause, all_super_traits, autoderef, check_orphan_rules, consteval::{ConstExt, try_const_usize, unknown_const_as_generic}, @@ -4973,6 +4973,7 @@ impl<'db> Type<'db> { | TyKind::Tuple(_, substitution) | TyKind::OpaqueType(_, substitution) | TyKind::AssociatedType(_, substitution) + | TyKind::Alias(AliasTy::Projection(ProjectionTy { substitution, .. })) | TyKind::FnDef(_, substitution) => { substitution.iter(Interner).filter_map(|x| x.ty(Interner)).any(|ty| go(db, ty)) } @@ -5819,7 +5820,11 @@ impl<'db> Type<'db> { cb(type_.derived(ty.clone())); walk_substs(db, type_, substs, cb); } - TyKind::AssociatedType(_, substs) => { + TyKind::AssociatedType(_, substs) + | TyKind::Alias(AliasTy::Projection(hir_ty::ProjectionTy { + substitution: substs, + .. + })) => { if ty.associated_type_parent_trait(db).is_some() { cb(type_.derived(ty.clone())); } From b2b33f9faab81507abda08d025c0680eb8d4995d Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 10 Sep 2025 04:51:33 +0300 Subject: [PATCH 331/682] Always coerce in a cast, even when there are unknown types This cause the relationships between inference vars to get recorded. --- .../crates/hir-ty/src/infer/cast.rs | 13 +++++++------ .../hir-ty/src/tests/regression/new_solver.rs | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs index 43364963eb05..bc3ee3c4c54d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs @@ -106,6 +106,13 @@ impl CastCheck { self.expr_ty = table.eagerly_normalize_and_resolve_shallow_in(self.expr_ty.clone()); self.cast_ty = table.eagerly_normalize_and_resolve_shallow_in(self.cast_ty.clone()); + // This should always come first so that we apply the coercion, which impacts infer vars. + if let Ok((adj, _)) = table.coerce(&self.expr_ty, &self.cast_ty, CoerceNever::Yes) { + apply_adjustments(self.source_expr, adj); + set_coercion_cast(self.source_expr); + return Ok(()); + } + if self.expr_ty.contains_unknown() || self.cast_ty.contains_unknown() { return Ok(()); } @@ -126,12 +133,6 @@ impl CastCheck { return Ok(()); } - if let Ok((adj, _)) = table.coerce(&self.expr_ty, &self.cast_ty, CoerceNever::Yes) { - apply_adjustments(self.source_expr, adj); - set_coercion_cast(self.source_expr); - return Ok(()); - } - self.do_check(table, apply_adjustments) .map_err(|e| e.into_diagnostic(self.expr, self.expr_ty.clone(), self.cast_ty.clone())) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs index 6d6c56696a30..4df788638a31 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -98,3 +98,21 @@ fn main() { "#, ); } + +#[test] +fn cast_error_type() { + check_infer( + r#" +fn main() { + let foo: [_; _] = [false] as _; +} + "#, + expect![[r#" + 10..47 '{ le...s _; }': () + 18..21 'foo': [bool; 1] + 32..39 '[false]': [bool; 1] + 32..44 '[false] as _': [bool; 1] + 33..38 'false': bool + "#]], + ); +} From ef7b036458552bb5a2ab1cdef8e39a17d9f68839 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Wed, 10 Sep 2025 07:55:03 +0200 Subject: [PATCH 332/682] Add suggestions --- library/std/src/sync/barrier.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/library/std/src/sync/barrier.rs b/library/std/src/sync/barrier.rs index 47d63c57ff1e..8988126bd90c 100644 --- a/library/std/src/sync/barrier.rs +++ b/library/std/src/sync/barrier.rs @@ -1,6 +1,5 @@ -use core::panic::RefUnwindSafe; - use crate::fmt; +use crate::panic::RefUnwindSafe; use crate::sync::nonpoison::{Condvar, Mutex}; /// A barrier enables multiple threads to synchronize the beginning @@ -33,7 +32,7 @@ pub struct Barrier { num_threads: usize, } -#[stable(feature = "rust1", since = "1.0.0")] +#[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")] impl RefUnwindSafe for Barrier {} // The inner state of a double barrier From 64ea775d27599a33b070dd3aa4353e1e7faee0b4 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 8 Sep 2025 15:56:48 +0200 Subject: [PATCH 333/682] interpret: copy_provenance: avoid large intermediate buffer for large repeat counts --- .../rustc_const_eval/src/interpret/memory.rs | 4 +- compiler/rustc_data_structures/src/lib.rs | 1 + .../rustc_data_structures/src/sorted_map.rs | 33 +++--- .../src/sorted_map/tests.rs | 10 +- .../src/mir/interpret/allocation.rs | 9 +- .../interpret/allocation/provenance_map.rs | 105 ++++++++---------- 6 files changed, 81 insertions(+), 81 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 6ec85648d6d8..43bcd34eb36d 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -1503,7 +1503,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // This will also error if copying partial provenance is not supported. let provenance = src_alloc .provenance() - .prepare_copy(src_range, dest_offset, num_copies, self) + .prepare_copy(src_range, self) .map_err(|e| e.to_interp_error(src_alloc_id))?; // Prepare a copy of the initialization mask. let init = src_alloc.init_mask().prepare_copy(src_range); @@ -1589,7 +1589,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { num_copies, ); // copy the provenance to the destination - dest_alloc.provenance_apply_copy(provenance); + dest_alloc.provenance_apply_copy(provenance, alloc_range(dest_offset, size), num_copies); interp_ok(()) } diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 17da3ea83c81..e4e86bcc41aa 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -34,6 +34,7 @@ #![feature(sized_hierarchy)] #![feature(test)] #![feature(thread_id_value)] +#![feature(trusted_len)] #![feature(type_alias_impl_trait)] #![feature(unwrap_infallible)] // tidy-alphabetical-end diff --git a/compiler/rustc_data_structures/src/sorted_map.rs b/compiler/rustc_data_structures/src/sorted_map.rs index c002d47815b1..c3c173b3fc4d 100644 --- a/compiler/rustc_data_structures/src/sorted_map.rs +++ b/compiler/rustc_data_structures/src/sorted_map.rs @@ -1,6 +1,7 @@ use std::borrow::Borrow; use std::cmp::Ordering; use std::fmt::Debug; +use std::iter::TrustedLen; use std::mem; use std::ops::{Bound, Index, IndexMut, RangeBounds}; @@ -215,32 +216,36 @@ impl SortedMap { /// It is up to the caller to make sure that the elements are sorted by key /// and that there are no duplicates. #[inline] - pub fn insert_presorted(&mut self, elements: Vec<(K, V)>) { - if elements.is_empty() { + pub fn insert_presorted( + &mut self, + // We require `TrustedLen` to ensure that the `splice` below is actually efficient. + mut elements: impl Iterator + DoubleEndedIterator + TrustedLen, + ) { + let Some(first) = elements.next() else { return; - } + }; - debug_assert!(elements.array_windows().all(|[fst, snd]| fst.0 < snd.0)); - - let start_index = self.lookup_index_for(&elements[0].0); + let start_index = self.lookup_index_for(&first.0); let elements = match start_index { Ok(index) => { - let mut elements = elements.into_iter(); - self.data[index] = elements.next().unwrap(); - elements + self.data[index] = first; // overwrite first element + elements.chain(None) // insert the rest below } Err(index) => { - if index == self.data.len() || elements.last().unwrap().0 < self.data[index].0 { + let last = elements.next_back(); + if index == self.data.len() + || last.as_ref().is_none_or(|l| l.0 < self.data[index].0) + { // We can copy the whole range without having to mix with // existing elements. - self.data.splice(index..index, elements); + self.data + .splice(index..index, std::iter::once(first).chain(elements).chain(last)); return; } - let mut elements = elements.into_iter(); - self.data.insert(index, elements.next().unwrap()); - elements + self.data.insert(index, first); + elements.chain(last) // insert the rest below } }; diff --git a/compiler/rustc_data_structures/src/sorted_map/tests.rs b/compiler/rustc_data_structures/src/sorted_map/tests.rs index ea4d2f1feacc..17d0d3cb1701 100644 --- a/compiler/rustc_data_structures/src/sorted_map/tests.rs +++ b/compiler/rustc_data_structures/src/sorted_map/tests.rs @@ -171,7 +171,7 @@ fn test_insert_presorted_non_overlapping() { map.insert(2, 0); map.insert(8, 0); - map.insert_presorted(vec![(3, 0), (7, 0)]); + map.insert_presorted(vec![(3, 0), (7, 0)].into_iter()); let expected = vec![2, 3, 7, 8]; assert_eq!(keys(map), expected); @@ -183,7 +183,7 @@ fn test_insert_presorted_first_elem_equal() { map.insert(2, 2); map.insert(8, 8); - map.insert_presorted(vec![(2, 0), (7, 7)]); + map.insert_presorted(vec![(2, 0), (7, 7)].into_iter()); let expected = vec![(2, 0), (7, 7), (8, 8)]; assert_eq!(elements(map), expected); @@ -195,7 +195,7 @@ fn test_insert_presorted_last_elem_equal() { map.insert(2, 2); map.insert(8, 8); - map.insert_presorted(vec![(3, 3), (8, 0)]); + map.insert_presorted(vec![(3, 3), (8, 0)].into_iter()); let expected = vec![(2, 2), (3, 3), (8, 0)]; assert_eq!(elements(map), expected); @@ -207,7 +207,7 @@ fn test_insert_presorted_shuffle() { map.insert(2, 2); map.insert(7, 7); - map.insert_presorted(vec![(1, 1), (3, 3), (8, 8)]); + map.insert_presorted(vec![(1, 1), (3, 3), (8, 8)].into_iter()); let expected = vec![(1, 1), (2, 2), (3, 3), (7, 7), (8, 8)]; assert_eq!(elements(map), expected); @@ -219,7 +219,7 @@ fn test_insert_presorted_at_end() { map.insert(1, 1); map.insert(2, 2); - map.insert_presorted(vec![(3, 3), (8, 8)]); + map.insert_presorted(vec![(3, 3), (8, 8)].into_iter()); let expected = vec![(1, 1), (2, 2), (3, 3), (8, 8)]; assert_eq!(elements(map), expected); diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 67962813ae43..8e603ce1b911 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -849,8 +849,13 @@ impl Allocation /// /// This is dangerous to use as it can violate internal `Allocation` invariants! /// It only exists to support an efficient implementation of `mem_copy_repeatedly`. - pub fn provenance_apply_copy(&mut self, copy: ProvenanceCopy) { - self.provenance.apply_copy(copy) + pub fn provenance_apply_copy( + &mut self, + copy: ProvenanceCopy, + range: AllocRange, + repeat: u64, + ) { + self.provenance.apply_copy(copy, range, repeat) } /// Applies a previously prepared copy of the init mask. diff --git a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs index 720e58d7aa0e..46f28328aebc 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs @@ -278,90 +278,78 @@ impl ProvenanceMap { /// A partial, owned list of provenance to transfer into another allocation. /// -/// Offsets are already adjusted to the destination allocation. +/// Offsets are relative to the beginning of the copied range. pub struct ProvenanceCopy { - dest_ptrs: Option>, - dest_bytes: Option>, + ptrs: Box<[(Size, Prov)]>, + bytes: Box<[(Size, (Prov, u8))]>, } impl ProvenanceMap { pub fn prepare_copy( &self, - src: AllocRange, - dest: Size, - count: u64, + range: AllocRange, cx: &impl HasDataLayout, ) -> AllocResult> { - let shift_offset = move |idx, offset| { - // compute offset for current repetition - let dest_offset = dest + src.size * idx; // `Size` operations - // shift offsets from source allocation to destination allocation - (offset - src.start) + dest_offset // `Size` operations - }; + let shift_offset = move |offset| offset - range.start; let ptr_size = cx.data_layout().pointer_size(); // # Pointer-sized provenances // Get the provenances that are entirely within this range. // (Different from `range_get_ptrs` which asks if they overlap the range.) // Only makes sense if we are copying at least one pointer worth of bytes. - let mut dest_ptrs_box = None; - if src.size >= ptr_size { - let adjusted_end = Size::from_bytes(src.end().bytes() - (ptr_size.bytes() - 1)); - let ptrs = self.ptrs.range(src.start..adjusted_end); - // If `count` is large, this is rather wasteful -- we are allocating a big array here, which - // is mostly filled with redundant information since it's just N copies of the same `Prov`s - // at slightly adjusted offsets. The reason we do this is so that in `mark_provenance_range` - // we can use `insert_presorted`. That wouldn't work with an `Iterator` that just produces - // the right sequence of provenance for all N copies. - // Basically, this large array would have to be created anyway in the target allocation. - let mut dest_ptrs = Vec::with_capacity(ptrs.len() * (count as usize)); - for i in 0..count { - dest_ptrs - .extend(ptrs.iter().map(|&(offset, reloc)| (shift_offset(i, offset), reloc))); - } - debug_assert_eq!(dest_ptrs.len(), dest_ptrs.capacity()); - dest_ptrs_box = Some(dest_ptrs.into_boxed_slice()); + let mut ptrs_box: Box<[_]> = Box::new([]); + if range.size >= ptr_size { + let adjusted_end = Size::from_bytes(range.end().bytes() - (ptr_size.bytes() - 1)); + let ptrs = self.ptrs.range(range.start..adjusted_end); + ptrs_box = ptrs.iter().map(|&(offset, reloc)| (shift_offset(offset), reloc)).collect(); }; // # Byte-sized provenances // This includes the existing bytewise provenance in the range, and ptr provenance // that overlaps with the begin/end of the range. - let mut dest_bytes_box = None; - let begin_overlap = self.range_ptrs_get(alloc_range(src.start, Size::ZERO), cx).first(); - let end_overlap = self.range_ptrs_get(alloc_range(src.end(), Size::ZERO), cx).first(); + let mut bytes_box: Box<[_]> = Box::new([]); + let begin_overlap = self.range_ptrs_get(alloc_range(range.start, Size::ZERO), cx).first(); + let end_overlap = self.range_ptrs_get(alloc_range(range.end(), Size::ZERO), cx).first(); // We only need to go here if there is some overlap or some bytewise provenance. if begin_overlap.is_some() || end_overlap.is_some() || self.bytes.is_some() { let mut bytes: Vec<(Size, (Prov, u8))> = Vec::new(); // First, if there is a part of a pointer at the start, add that. if let Some(entry) = begin_overlap { trace!("start overlapping entry: {entry:?}"); - // For really small copies, make sure we don't run off the end of the `src` range. - let entry_end = cmp::min(entry.0 + ptr_size, src.end()); - for offset in src.start..entry_end { - bytes.push((offset, (entry.1, (offset - entry.0).bytes() as u8))); + // For really small copies, make sure we don't run off the end of the range. + let entry_end = cmp::min(entry.0 + ptr_size, range.end()); + for offset in range.start..entry_end { + bytes.push((shift_offset(offset), (entry.1, (offset - entry.0).bytes() as u8))); } } else { trace!("no start overlapping entry"); } // Then the main part, bytewise provenance from `self.bytes`. - bytes.extend(self.range_bytes_get(src)); + bytes.extend( + self.range_bytes_get(range) + .iter() + .map(|&(offset, reloc)| (shift_offset(offset), reloc)), + ); // And finally possibly parts of a pointer at the end. if let Some(entry) = end_overlap { trace!("end overlapping entry: {entry:?}"); - // For really small copies, make sure we don't start before `src` does. - let entry_start = cmp::max(entry.0, src.start); - for offset in entry_start..src.end() { + // For really small copies, make sure we don't start before `range` does. + let entry_start = cmp::max(entry.0, range.start); + for offset in entry_start..range.end() { if bytes.last().is_none_or(|bytes_entry| bytes_entry.0 < offset) { // The last entry, if it exists, has a lower offset than us, so we // can add it at the end and remain sorted. - bytes.push((offset, (entry.1, (offset - entry.0).bytes() as u8))); + bytes.push(( + shift_offset(offset), + (entry.1, (offset - entry.0).bytes() as u8), + )); } else { // There already is an entry for this offset in there! This can happen when the // start and end range checks actually end up hitting the same pointer, so we // already added this in the "pointer at the start" part above. - assert!(entry.0 <= src.start); + assert!(entry.0 <= range.start); } } } else { @@ -372,33 +360,34 @@ impl ProvenanceMap { if !bytes.is_empty() && !Prov::OFFSET_IS_ADDR { // FIXME(#146291): We need to ensure that we don't mix different pointers with // the same provenance. - return Err(AllocError::ReadPartialPointer(src.start)); + return Err(AllocError::ReadPartialPointer(range.start)); } // And again a buffer for the new list on the target side. - let mut dest_bytes = Vec::with_capacity(bytes.len() * (count as usize)); - for i in 0..count { - dest_bytes - .extend(bytes.iter().map(|&(offset, reloc)| (shift_offset(i, offset), reloc))); - } - debug_assert_eq!(dest_bytes.len(), dest_bytes.capacity()); - dest_bytes_box = Some(dest_bytes.into_boxed_slice()); + bytes_box = bytes.into_boxed_slice(); } - Ok(ProvenanceCopy { dest_ptrs: dest_ptrs_box, dest_bytes: dest_bytes_box }) + Ok(ProvenanceCopy { ptrs: ptrs_box, bytes: bytes_box }) } /// Applies a provenance copy. /// The affected range, as defined in the parameters to `prepare_copy` is expected /// to be clear of provenance. - pub fn apply_copy(&mut self, copy: ProvenanceCopy) { - if let Some(dest_ptrs) = copy.dest_ptrs { - self.ptrs.insert_presorted(dest_ptrs.into()); + pub fn apply_copy(&mut self, copy: ProvenanceCopy, range: AllocRange, repeat: u64) { + let shift_offset = |idx: u64, offset: Size| offset + range.start + idx * range.size; + if !copy.ptrs.is_empty() { + for i in 0..repeat { + self.ptrs.insert_presorted( + copy.ptrs.iter().map(|&(offset, reloc)| (shift_offset(i, offset), reloc)), + ); + } } - if let Some(dest_bytes) = copy.dest_bytes - && !dest_bytes.is_empty() - { - self.bytes.get_or_insert_with(Box::default).insert_presorted(dest_bytes.into()); + if !copy.bytes.is_empty() { + for i in 0..repeat { + self.bytes.get_or_insert_with(Box::default).insert_presorted( + copy.bytes.iter().map(|&(offset, reloc)| (shift_offset(i, offset), reloc)), + ); + } } } } From 7abbc9c8b2c638752e2a6b0913ed1e93e14d21ba Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 8 Sep 2025 16:10:37 +0200 Subject: [PATCH 334/682] avoid calling insert_presorted more than once --- .../rustc_data_structures/src/sorted_map.rs | 2 +- .../interpret/allocation/provenance_map.rs | 26 ++++++++++++------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_data_structures/src/sorted_map.rs b/compiler/rustc_data_structures/src/sorted_map.rs index c3c173b3fc4d..15e3e6ea4c32 100644 --- a/compiler/rustc_data_structures/src/sorted_map.rs +++ b/compiler/rustc_data_structures/src/sorted_map.rs @@ -249,7 +249,7 @@ impl SortedMap { } }; - // Insert the rest + // Insert the rest. This is super inefficicent since each insertion copies the entire tail. for (k, v) in elements { self.insert(k, v); } diff --git a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs index 46f28328aebc..67baf63bbfad 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs @@ -376,18 +376,24 @@ impl ProvenanceMap { pub fn apply_copy(&mut self, copy: ProvenanceCopy, range: AllocRange, repeat: u64) { let shift_offset = |idx: u64, offset: Size| offset + range.start + idx * range.size; if !copy.ptrs.is_empty() { - for i in 0..repeat { - self.ptrs.insert_presorted( - copy.ptrs.iter().map(|&(offset, reloc)| (shift_offset(i, offset), reloc)), - ); - } + // We want to call `insert_presorted` only once so that, if possible, the entries + // after the range we insert are moved back only once. + let chunk_len = copy.ptrs.len() as u64; + self.ptrs.insert_presorted((0..chunk_len * repeat).map(|i| { + let chunk = i / chunk_len; + let (offset, reloc) = copy.ptrs[(i % chunk_len) as usize]; + (shift_offset(chunk, offset), reloc) + })); } if !copy.bytes.is_empty() { - for i in 0..repeat { - self.bytes.get_or_insert_with(Box::default).insert_presorted( - copy.bytes.iter().map(|&(offset, reloc)| (shift_offset(i, offset), reloc)), - ); - } + let chunk_len = copy.bytes.len() as u64; + self.bytes.get_or_insert_with(Box::default).insert_presorted( + (0..chunk_len * repeat).map(|i| { + let chunk = i / chunk_len; + let (offset, reloc) = copy.bytes[(i % chunk_len) as usize]; + (shift_offset(chunk, offset), reloc) + }), + ); } } } From 092d653882f50bba2eea1158ed57891d1a272542 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 10 Sep 2025 10:24:07 +0200 Subject: [PATCH 335/682] Update to LLVM 21.1.1 --- src/llvm-project | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llvm-project b/src/llvm-project index 19f0a49c5c3f..333793696b0a 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit 19f0a49c5c3f4ba88b5e7ac620b9a0d8574c09cb +Subproject commit 333793696b0a08002acac6af983adc7a5233fc56 From e4f69ea1af40cd43da1aec1d6489ac6e78d3e769 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 10 Sep 2025 09:35:45 +0200 Subject: [PATCH 336/682] fix(needless_closure): don't lint on `AsyncFn*`s --- clippy_lints/src/eta_reduction.rs | 27 +++++++++++++++------------ tests/ui/eta.fixed | 8 ++++++++ tests/ui/eta.rs | 8 ++++++++ 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 2da1c2bad117..752f39b4e6dc 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -197,6 +197,18 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx // in a type which is `'static`. // For now ignore all callee types which reference a type parameter. && !generic_args.types().any(|t| matches!(t.kind(), ty::Param(_))) + // Rule out `AsyncFn*`s, because while they can be called as `|x| f(x)`, + // they can't be passed directly into a place expecting an `Fn*` (#13892) + && let Ok((closure_kind, _)) = cx + .tcx + .infer_ctxt() + .build(cx.typing_mode()) + .err_ctxt() + .type_implements_fn_trait( + cx.param_env, + Binder::bind_with_vars(callee_ty_adjusted, List::empty()), + ty::PredicatePolarity::Positive, + ) { span_lint_hir_and_then( cx, @@ -213,19 +225,10 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx // 'cuz currently nothing changes after deleting this check. local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr) }) { - match cx - .tcx - .infer_ctxt() - .build(cx.typing_mode()) - .err_ctxt() - .type_implements_fn_trait( - cx.param_env, - Binder::bind_with_vars(callee_ty_adjusted, List::empty()), - ty::PredicatePolarity::Positive, - ) { + match closure_kind { // Mutable closure is used after current expr; we cannot consume it. - Ok((ClosureKind::FnMut, _)) => snippet = format!("&mut {snippet}"), - Ok((ClosureKind::Fn, _)) if !callee_ty_raw.is_ref() => { + ClosureKind::FnMut => snippet = format!("&mut {snippet}"), + ClosureKind::Fn if !callee_ty_raw.is_ref() => { snippet = format!("&{snippet}"); }, _ => (), diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed index 6944a979c05e..107318e5323d 100644 --- a/tests/ui/eta.fixed +++ b/tests/ui/eta.fixed @@ -635,3 +635,11 @@ fn issue8817() { //~| HELP: replace the closure with the tuple variant itself .unwrap(); // just for nicer formatting } + +async fn issue13892<'a, T, F>(maybe: Option<&'a T>, visitor: F) +where + F: AsyncFn(&'a T), + T: 'a, +{ + maybe.map(|x| visitor(x)); +} diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs index 5bcc1cb26fd7..b85e8e75153a 100644 --- a/tests/ui/eta.rs +++ b/tests/ui/eta.rs @@ -635,3 +635,11 @@ fn issue8817() { //~| HELP: replace the closure with the tuple variant itself .unwrap(); // just for nicer formatting } + +async fn issue13892<'a, T, F>(maybe: Option<&'a T>, visitor: F) +where + F: AsyncFn(&'a T), + T: 'a, +{ + maybe.map(|x| visitor(x)); +} From bd089e1e6e52256c0535f19f58b4b6fe9609b70c Mon Sep 17 00:00:00 2001 From: Bryanskiy Date: Tue, 19 Aug 2025 21:34:33 +0300 Subject: [PATCH 337/682] Default auto traits: revert to the default supertraits --- .../src/collect/predicates_of.rs | 6 - .../src/hir_ty_lowering/bounds.rs | 144 ++---------------- .../src/multiple_supertrait_upcastable.rs | 3 +- .../backward-compatible-lazy-bounds-pass.rs | 31 ---- .../maybe-bounds-in-dyn-traits.rs | 78 +++++++--- .../maybe-bounds-in-dyn-traits.stderr | 60 ++++---- .../maybe-bounds-in-traits.rs | 78 +++------- .../maybe-bounds-in-traits.stderr | 84 ++++------ 8 files changed, 153 insertions(+), 331 deletions(-) delete mode 100644 tests/ui/traits/default_auto_traits/backward-compatible-lazy-bounds-pass.rs diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index b59dc4bd132f..126ffabd448c 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -174,12 +174,6 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen } }; - if let Node::TraitItem(item) = node { - let mut bounds = Vec::new(); - icx.lowerer().add_default_trait_item_bounds(item, &mut bounds); - predicates.extend(bounds); - } - let generics = tcx.generics_of(def_id); // Below we'll consider the bounds on the type parameters (including `Self`) diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index d14aef8ace46..99dc8e6e5221 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -1,12 +1,13 @@ +use std::assert_matches::assert_matches; use std::ops::ControlFlow; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::struct_span_code_err; use rustc_hir as hir; +use rustc_hir::PolyTraitRef; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId}; -use rustc_hir::{AmbigArg, PolyTraitRef}; use rustc_middle::bug; use rustc_middle::ty::{ self as ty, IsSuggestable, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, @@ -230,122 +231,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - /// Checks whether `Self: DefaultAutoTrait` bounds should be added on trait super bounds - /// or associated items. - /// - /// To keep backward compatibility with existing code, `experimental_default_bounds` bounds - /// should be added everywhere, including super bounds. However this causes a huge performance - /// costs. For optimization purposes instead of adding default supertraits, bounds - /// are added to the associated items: - /// - /// ```ignore(illustrative) - /// // Default bounds are generated in the following way: - /// trait Trait { - /// fn foo(&self) where Self: Leak {} - /// } - /// - /// // instead of this: - /// trait Trait: Leak { - /// fn foo(&self) {} - /// } - /// ``` - /// It is not always possible to do this because of backward compatibility: - /// - /// ```ignore(illustrative) - /// pub trait Trait {} - /// pub trait Trait1 : Trait {} - /// //~^ ERROR: `Rhs` requires `DefaultAutoTrait`, but `Self` is not `DefaultAutoTrait` - /// ``` - /// - /// or: - /// - /// ```ignore(illustrative) - /// trait Trait { - /// type Type where Self: Sized; - /// } - /// trait Trait2 : Trait {} - /// //~^ ERROR: `DefaultAutoTrait` required for `Trait2`, by implicit `Self: DefaultAutoTrait` in `Trait::Type` - /// ``` - /// - /// Therefore, `experimental_default_bounds` are still being added to supertraits if - /// the `SelfTyParam` or `AssocItemConstraint` were found in a trait header. - fn requires_default_supertraits( - &self, - hir_bounds: &'tcx [hir::GenericBound<'tcx>], - hir_generics: &'tcx hir::Generics<'tcx>, - ) -> bool { - struct TraitInfoCollector; - - impl<'tcx> hir::intravisit::Visitor<'tcx> for TraitInfoCollector { - type Result = ControlFlow<()>; - - fn visit_assoc_item_constraint( - &mut self, - _constraint: &'tcx hir::AssocItemConstraint<'tcx>, - ) -> Self::Result { - ControlFlow::Break(()) - } - - fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx, AmbigArg>) -> Self::Result { - if matches!( - &t.kind, - hir::TyKind::Path(hir::QPath::Resolved( - _, - hir::Path { res: hir::def::Res::SelfTyParam { .. }, .. }, - )) - ) { - return ControlFlow::Break(()); - } - hir::intravisit::walk_ty(self, t) - } - } - - let mut found = false; - for bound in hir_bounds { - found |= hir::intravisit::walk_param_bound(&mut TraitInfoCollector, bound).is_break(); - } - found |= hir::intravisit::walk_generics(&mut TraitInfoCollector, hir_generics).is_break(); - found - } - - /// Implicitly add `Self: DefaultAutoTrait` clauses on trait associated items if - /// they are not added as super trait bounds to the trait itself. See - /// `requires_default_supertraits` for more information. - pub(crate) fn add_default_trait_item_bounds( - &self, - trait_item: &hir::TraitItem<'tcx>, - bounds: &mut Vec<(ty::Clause<'tcx>, Span)>, - ) { - let tcx = self.tcx(); - if !tcx.sess.opts.unstable_opts.experimental_default_bounds { - return; - } - - let parent = tcx.local_parent(trait_item.hir_id().owner.def_id); - let hir::Node::Item(parent_trait) = tcx.hir_node_by_def_id(parent) else { - unreachable!(); - }; - - let (trait_generics, trait_bounds) = match parent_trait.kind { - hir::ItemKind::Trait(_, _, _, _, generics, supertraits, _) => (generics, supertraits), - hir::ItemKind::TraitAlias(_, generics, supertraits) => (generics, supertraits), - _ => unreachable!(), - }; - - if !self.requires_default_supertraits(trait_bounds, trait_generics) { - let self_ty_where_predicates = (parent, trait_item.generics.predicates); - self.add_default_traits( - bounds, - tcx.types.self_param, - &[], - Some(self_ty_where_predicates), - trait_item.span, - ); - } - } - - /// Lazily sets `experimental_default_bounds` to true on trait super bounds. - /// See `requires_default_supertraits` for more information. + /// Adds `experimental_default_bounds` bounds to the supertrait bounds. pub(crate) fn add_default_super_traits( &self, trait_def_id: LocalDefId, @@ -354,21 +240,21 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir_generics: &'tcx hir::Generics<'tcx>, span: Span, ) { - if !self.tcx().sess.opts.unstable_opts.experimental_default_bounds { + assert_matches!(self.tcx().def_kind(trait_def_id), DefKind::Trait | DefKind::TraitAlias); + + // Supertraits for auto trait are unsound according to the unstable book: + // https://doc.rust-lang.org/beta/unstable-book/language-features/auto-traits.html#supertraits + if self.tcx().trait_is_auto(trait_def_id.to_def_id()) { return; } - assert!(matches!(self.tcx().def_kind(trait_def_id), DefKind::Trait | DefKind::TraitAlias)); - if self.requires_default_supertraits(hir_bounds, hir_generics) { - let self_ty_where_predicates = (trait_def_id, hir_generics.predicates); - self.add_default_traits( - bounds, - self.tcx().types.self_param, - hir_bounds, - Some(self_ty_where_predicates), - span, - ); - } + self.add_default_traits( + bounds, + self.tcx().types.self_param, + hir_bounds, + Some((trait_def_id, hir_generics.predicates)), + span, + ); } pub(crate) fn add_default_traits( diff --git a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs index 5513c703f1d5..93f067d09833 100644 --- a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs +++ b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs @@ -47,7 +47,8 @@ impl<'tcx> LateLintPass<'tcx> for MultipleSupertraitUpcastable { .explicit_super_predicates_of(def_id) .iter_identity_copied() .filter_map(|(pred, _)| pred.as_trait_clause()) - .filter(|pred| !cx.tcx.is_lang_item(pred.def_id(), hir::LangItem::MetaSized)); + .filter(|pred| !cx.tcx.is_lang_item(pred.def_id(), hir::LangItem::MetaSized)) + .filter(|pred| !cx.tcx.is_default_trait(pred.def_id())); if direct_super_traits_iter.count() > 1 { cx.emit_span_lint( MULTIPLE_SUPERTRAIT_UPCASTABLE, diff --git a/tests/ui/traits/default_auto_traits/backward-compatible-lazy-bounds-pass.rs b/tests/ui/traits/default_auto_traits/backward-compatible-lazy-bounds-pass.rs deleted file mode 100644 index 745b6ee9bc5c..000000000000 --- a/tests/ui/traits/default_auto_traits/backward-compatible-lazy-bounds-pass.rs +++ /dev/null @@ -1,31 +0,0 @@ -//@ check-pass -//@ compile-flags: -Zexperimental-default-bounds - -#![feature(auto_traits, lang_items, no_core, rustc_attrs, trait_alias)] -#![no_std] -#![no_core] - -#[lang = "pointee_sized"] -trait PointeeSized {} - -#[lang = "meta_sized"] -trait MetaSized: PointeeSized {} - -#[lang = "sized"] -trait Sized: MetaSized {} - -#[lang = "default_trait1"] -auto trait DefaultTrait1 {} - -#[lang = "default_trait2"] -auto trait DefaultTrait2 {} - -trait Trait {} -trait Trait1 : Trait {} - -trait Trait2 { - type Type; -} -trait Trait3 = Trait2; - -fn main() {} diff --git a/tests/ui/traits/default_auto_traits/maybe-bounds-in-dyn-traits.rs b/tests/ui/traits/default_auto_traits/maybe-bounds-in-dyn-traits.rs index e7cca41a47ea..2e1a5d2424bf 100644 --- a/tests/ui/traits/default_auto_traits/maybe-bounds-in-dyn-traits.rs +++ b/tests/ui/traits/default_auto_traits/maybe-bounds-in-dyn-traits.rs @@ -13,32 +13,37 @@ #![no_core] #[lang = "pointee_sized"] -trait PointeeSized {} +trait PointeeSized: ?Leak {} #[lang = "meta_sized"] -trait MetaSized: PointeeSized {} +trait MetaSized: PointeeSized + ?Leak {} #[lang = "sized"] -trait Sized: MetaSized {} +trait Sized: MetaSized + ?Leak {} #[lang = "copy"] -pub trait Copy {} +pub trait Copy: ?Leak {} impl<'a, T: ?Sized> Copy for &'a T {} #[lang = "legacy_receiver"] -trait Receiver {} +trait Receiver: ?Leak {} impl Receiver for &T {} +impl Receiver for &mut T {} #[lang = "unsize"] -trait Unsize {} +trait Unsize: ?Leak {} #[lang = "coerce_unsized"] -trait CoerceUnsized {} +trait CoerceUnsized: ?Leak {} impl<'a, 'b: 'a, T: ?Sized + ?Leak + Unsize, U: ?Sized + ?Leak> CoerceUnsized<&'a U> for &'b T {} +// Omit `T: ?Leak` and `U: ?Leak`. +impl<'a, 'b: 'a, T: ?Sized + Unsize, U: ?Sized> CoerceUnsized<&'a mut U> for &'b mut T {} #[lang = "dispatch_from_dyn"] -trait DispatchFromDyn {} +trait DispatchFromDyn: ?Leak {} impl<'a, T: ?Sized + ?Leak + Unsize, U: ?Sized + ?Leak> DispatchFromDyn<&'a U> for &'a T {} +// Omit `T: ?Leak` and `U: ?Leak`. +impl<'a, T: ?Sized + Unsize, U: ?Sized> DispatchFromDyn<&'a mut U> for &'a mut T {} #[lang = "default_trait1"] auto trait Leak {} @@ -47,25 +52,52 @@ struct NonLeakS; impl !Leak for NonLeakS {} struct LeakS; -trait Trait { - fn leak_foo(&self) {} - fn maybe_leak_foo(&self) where Self: ?Leak {} +fn bounds_check() { + trait LeakTr {} + + trait MaybeLeakTr: ?Leak {} + + impl MaybeLeakTr for NonLeakS {} + + impl LeakTr for LeakS {} + impl MaybeLeakTr for LeakS {} + + let _: &dyn LeakTr = &NonLeakS; + //~^ ERROR the trait bound `NonLeakS: bounds_check::LeakTr` is not satisfied + let _: &dyn LeakTr = &LeakS; + + let _: &(dyn LeakTr + ?Leak) = &NonLeakS; + let _: &(dyn LeakTr + ?Leak) = &LeakS; + + let _: &dyn MaybeLeakTr = &NonLeakS; + let _: &dyn MaybeLeakTr = &LeakS; } -impl Trait for NonLeakS {} -impl Trait for LeakS {} +fn dyn_compat_check() { + trait DynCompatCheck1: ?Leak { + fn foo(&self) {} + } + + trait DynCompatCheck2: ?Leak { + fn mut_foo(&mut self) {} + } + + impl DynCompatCheck1 for NonLeakS {} + impl DynCompatCheck2 for NonLeakS {} + + let _: &(dyn DynCompatCheck1 + ?Leak) = &NonLeakS; + // There is no `?Leak` bound on corresponding `DispatchFromDyn` impl. + let _: &dyn DynCompatCheck2 = &NonLeakS; + //~^ ERROR the trait `DynCompatCheck2` is not dyn compatible +} + +fn args_check() { + trait LeakTr {} -fn main() { - let _: &dyn Trait = &NonLeakS; - //~^ ERROR the trait bound `NonLeakS: Leak` is not satisfied - let _: &dyn Trait = &LeakS; - let _: &(dyn Trait + ?Leak) = &LeakS; - let x: &(dyn Trait + ?Leak) = &NonLeakS; - x.leak_foo(); - //~^ ERROR the trait bound `dyn Trait: Leak` is not satisfied - x.maybe_leak_foo(); // Ensure that we validate the generic args of relaxed bounds in trait object types. - let _: dyn Trait + ?Leak<(), Undefined = ()>; + let _: dyn LeakTr + ?Leak<(), Undefined = ()>; //~^ ERROR trait takes 0 generic arguments but 1 generic argument was supplied //~| ERROR associated type `Undefined` not found for `Leak` } + +fn main() {} diff --git a/tests/ui/traits/default_auto_traits/maybe-bounds-in-dyn-traits.stderr b/tests/ui/traits/default_auto_traits/maybe-bounds-in-dyn-traits.stderr index b19c082a1b8f..b96a2915c333 100644 --- a/tests/ui/traits/default_auto_traits/maybe-bounds-in-dyn-traits.stderr +++ b/tests/ui/traits/default_auto_traits/maybe-bounds-in-dyn-traits.stderr @@ -1,49 +1,57 @@ -error[E0277]: the trait bound `NonLeakS: Leak` is not satisfied - --> $DIR/maybe-bounds-in-dyn-traits.rs:59:25 +error[E0277]: the trait bound `NonLeakS: bounds_check::LeakTr` is not satisfied + --> $DIR/maybe-bounds-in-dyn-traits.rs:65:26 | -LL | let _: &dyn Trait = &NonLeakS; - | ^^^^^^^^^ unsatisfied trait bound +LL | let _: &dyn LeakTr = &NonLeakS; + | ^^^^^^^^^ unsatisfied trait bound | -help: the trait `Leak` is not implemented for `NonLeakS` - --> $DIR/maybe-bounds-in-dyn-traits.rs:46:1 +help: the trait `bounds_check::LeakTr` is not implemented for `NonLeakS` + --> $DIR/maybe-bounds-in-dyn-traits.rs:51:1 | LL | struct NonLeakS; | ^^^^^^^^^^^^^^^ - = note: required for the cast from `&NonLeakS` to `&dyn Trait + Leak` + = help: the trait `bounds_check::LeakTr` is implemented for `LeakS` + = note: required for the cast from `&NonLeakS` to `&dyn bounds_check::LeakTr + Leak` -error[E0277]: the trait bound `dyn Trait: Leak` is not satisfied - --> $DIR/maybe-bounds-in-dyn-traits.rs:64:7 +error[E0038]: the trait `DynCompatCheck2` is not dyn compatible + --> $DIR/maybe-bounds-in-dyn-traits.rs:90:17 | -LL | x.leak_foo(); - | ^^^^^^^^ the trait `Leak` is not implemented for `dyn Trait` +LL | fn mut_foo(&mut self) {} + | --------- help: consider changing method `mut_foo`'s `self` parameter to be `&self`: `&Self` +... +LL | let _: &dyn DynCompatCheck2 = &NonLeakS; + | ^^^^^^^^^^^^^^^ `DynCompatCheck2` is not dyn compatible | -note: required by a bound in `Trait::leak_foo` - --> $DIR/maybe-bounds-in-dyn-traits.rs:51:5 +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/maybe-bounds-in-dyn-traits.rs:82:20 | -LL | fn leak_foo(&self) {} - | ^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Trait::leak_foo` +LL | trait DynCompatCheck2: ?Leak { + | --------------- this trait is not dyn compatible... +LL | fn mut_foo(&mut self) {} + | ^^^^^^^^^ ...because method `mut_foo`'s `self` parameter cannot be dispatched on + = help: only type `NonLeakS` implements `DynCompatCheck2`; consider using it directly instead. error[E0107]: trait takes 0 generic arguments but 1 generic argument was supplied - --> $DIR/maybe-bounds-in-dyn-traits.rs:68:25 + --> $DIR/maybe-bounds-in-dyn-traits.rs:98:26 | -LL | let _: dyn Trait + ?Leak<(), Undefined = ()>; - | ^^^^-------------------- help: remove the unnecessary generics - | | - | expected 0 generic arguments +LL | let _: dyn LeakTr + ?Leak<(), Undefined = ()>; + | ^^^^-------------------- help: remove the unnecessary generics + | | + | expected 0 generic arguments | note: trait defined here, with 0 generic parameters - --> $DIR/maybe-bounds-in-dyn-traits.rs:44:12 + --> $DIR/maybe-bounds-in-dyn-traits.rs:49:12 | LL | auto trait Leak {} | ^^^^ error[E0220]: associated type `Undefined` not found for `Leak` - --> $DIR/maybe-bounds-in-dyn-traits.rs:68:34 + --> $DIR/maybe-bounds-in-dyn-traits.rs:98:35 | -LL | let _: dyn Trait + ?Leak<(), Undefined = ()>; - | ^^^^^^^^^ associated type `Undefined` not found +LL | let _: dyn LeakTr + ?Leak<(), Undefined = ()>; + | ^^^^^^^^^ associated type `Undefined` not found error: aborting due to 4 previous errors -Some errors have detailed explanations: E0107, E0220, E0277. -For more information about an error, try `rustc --explain E0107`. +Some errors have detailed explanations: E0038, E0107, E0220, E0277. +For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/traits/default_auto_traits/maybe-bounds-in-traits.rs b/tests/ui/traits/default_auto_traits/maybe-bounds-in-traits.rs index b3801baaf704..820132b4e543 100644 --- a/tests/ui/traits/default_auto_traits/maybe-bounds-in-traits.rs +++ b/tests/ui/traits/default_auto_traits/maybe-bounds-in-traits.rs @@ -14,18 +14,22 @@ #![no_std] #![no_core] +#[lang = "copy"] +pub trait Copy: ?Leak {} + #[lang = "pointee_sized"] -trait PointeeSized {} +trait PointeeSized: ?Leak {} #[lang = "meta_sized"] -trait MetaSized: PointeeSized {} +trait MetaSized: PointeeSized + ?Leak {} #[lang = "sized"] -trait Sized: MetaSized {} +trait Sized: MetaSized + ?Leak {} #[lang = "legacy_receiver"] -trait LegacyReceiver {} +trait LegacyReceiver: ?Leak {} impl LegacyReceiver for &T {} +// Omit `T: ?Leak`. impl LegacyReceiver for &mut T {} #[lang = "default_trait1"] @@ -43,78 +47,38 @@ mod supertraits { impl MaybeLeakT1 for NonLeakS {} impl MaybeLeakT2 for NonLeakS {} + + trait LeakT {} + impl LeakT for NonLeakS {} + //~^ ERROR the trait bound `NonLeakS: Leak` is not satisfied } -mod maybe_self_assoc_type { +mod assoc_type_maybe_bounds { use crate::*; - trait TestBase1 {} - trait TestBase2 {} - - trait Test1 { - type MaybeLeakSelf: TestBase1 where Self: ?Leak; - //~^ ERROR the trait bound `Self: Leak` is not satisfied - type LeakSelf: TestBase1; - } - - trait Test2 { - type MaybeLeakSelf: TestBase2 where Self: ?Leak; - type LeakSelf: TestBase2; - } - - trait Test3 { + trait Test1 { type Leak1 = LeakS; type Leak2 = NonLeakS; //~^ ERROR the trait bound `NonLeakS: Leak` is not satisfied } - trait Test4 { + trait Test2 { type MaybeLeak1: ?Leak = LeakS; type MaybeLeak2: ?Leak = NonLeakS; } - - trait Test5: ?Leak { - // ok, because assoc types have implicit where Self: Leak - type MaybeLeakSelf1: TestBase1; - type MaybeLeakSelf2: TestBase2; - } -} - -mod maybe_self_assoc_const { - use crate::*; - - const fn size_of() -> usize { - 0 - } - - trait Trait { - const CLeak: usize = size_of::(); - const CNonLeak: usize = size_of::() where Self: ?Leak; - //~^ ERROR the trait bound `Self: Leak` is not satisfied - } } mod methods { use crate::*; - trait Trait { - fn leak_foo(&self) {} - fn maybe_leak_foo(&self) where Self: ?Leak {} - fn mut_leak_foo(&mut self) {} - // there is no relax bound on corresponding Receiver impl - fn mut_maybe_leak_foo(&mut self) where Self: ?Leak {} - //~^ ERROR `&mut Self` cannot be used as the type of `self` without the `arbitrary_self_types` + trait ReceiveCheck1: ?Leak { + fn foo(&self) {} } - impl Trait for NonLeakS {} - impl Trait for LeakS {} - - fn foo() { - LeakS.leak_foo(); - LeakS.maybe_leak_foo(); - NonLeakS.leak_foo(); - //~^ ERROR the trait bound `NonLeakS: Leak` is not satisfied - NonLeakS.maybe_leak_foo(); + trait ReceiveCheck2: ?Leak { + // There is no `?Leak` bound on corresponding `LegacyReceiver` impl. + fn mut_foo(&mut self) {} + //~^ ERROR `&mut Self` cannot be used as the type of `self` without the `arbitrary_self_types` } } diff --git a/tests/ui/traits/default_auto_traits/maybe-bounds-in-traits.stderr b/tests/ui/traits/default_auto_traits/maybe-bounds-in-traits.stderr index 372bf8176009..95c77b1756cf 100644 --- a/tests/ui/traits/default_auto_traits/maybe-bounds-in-traits.stderr +++ b/tests/ui/traits/default_auto_traits/maybe-bounds-in-traits.stderr @@ -1,81 +1,49 @@ error[E0277]: the trait bound `NonLeakS: Leak` is not satisfied - --> $DIR/maybe-bounds-in-traits.rs:67:22 + --> $DIR/maybe-bounds-in-traits.rs:52:20 + | +LL | impl LeakT for NonLeakS {} + | ^^^^^^^^ unsatisfied trait bound + | +help: the trait `Leak` is not implemented for `NonLeakS` + --> $DIR/maybe-bounds-in-traits.rs:38:1 + | +LL | struct NonLeakS; + | ^^^^^^^^^^^^^^^ +note: required by a bound in `LeakT` + --> $DIR/maybe-bounds-in-traits.rs:51:5 + | +LL | trait LeakT {} + | ^^^^^^^^^^^^^^ required by this bound in `LeakT` + +error[E0277]: the trait bound `NonLeakS: Leak` is not satisfied + --> $DIR/maybe-bounds-in-traits.rs:61:22 | LL | type Leak2 = NonLeakS; | ^^^^^^^^ unsatisfied trait bound | help: the trait `Leak` is not implemented for `NonLeakS` - --> $DIR/maybe-bounds-in-traits.rs:34:1 + --> $DIR/maybe-bounds-in-traits.rs:38:1 | LL | struct NonLeakS; | ^^^^^^^^^^^^^^^ -note: required by a bound in `Test3::Leak2` - --> $DIR/maybe-bounds-in-traits.rs:67:9 +note: required by a bound in `Test1::Leak2` + --> $DIR/maybe-bounds-in-traits.rs:61:9 | LL | type Leak2 = NonLeakS; - | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Test3::Leak2` - -error[E0277]: the trait bound `Self: Leak` is not satisfied - --> $DIR/maybe-bounds-in-traits.rs:55:29 - | -LL | type MaybeLeakSelf: TestBase1 where Self: ?Leak; - | ^^^^^^^^^^^^^^^ the trait `Leak` is not implemented for `Self` - | -note: required by a bound in `TestBase1` - --> $DIR/maybe-bounds-in-traits.rs:51:21 - | -LL | trait TestBase1 {} - | ^ required by this bound in `TestBase1` -help: consider further restricting `Self` - | -LL | trait Test1: Leak { - | ++++++ + | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Test1::Leak2` error[E0658]: `&mut Self` cannot be used as the type of `self` without the `arbitrary_self_types` feature - --> $DIR/maybe-bounds-in-traits.rs:105:31 + --> $DIR/maybe-bounds-in-traits.rs:80:20 | -LL | fn mut_maybe_leak_foo(&mut self) where Self: ?Leak {} - | ^^^^^^^^^ +LL | fn mut_foo(&mut self) {} + | ^^^^^^^^^ | = note: see issue #44874 for more information = help: add `#![feature(arbitrary_self_types)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date = help: consider changing to `self`, `&self`, `&mut self`, or a type implementing `Receiver` such as `self: Box`, `self: Rc`, or `self: Arc` -error[E0277]: the trait bound `Self: Leak` is not satisfied - --> $DIR/maybe-bounds-in-traits.rs:92:43 - | -LL | const CNonLeak: usize = size_of::() where Self: ?Leak; - | ^^^^ the trait `Leak` is not implemented for `Self` - | -note: required by a bound in `size_of` - --> $DIR/maybe-bounds-in-traits.rs:86:22 - | -LL | const fn size_of() -> usize { - | ^ required by this bound in `size_of` -help: consider further restricting `Self` - | -LL | trait Trait: Leak { - | ++++++ - -error[E0277]: the trait bound `NonLeakS: Leak` is not satisfied - --> $DIR/maybe-bounds-in-traits.rs:115:18 - | -LL | NonLeakS.leak_foo(); - | ^^^^^^^^ unsatisfied trait bound - | -help: the trait `Leak` is not implemented for `NonLeakS` - --> $DIR/maybe-bounds-in-traits.rs:34:1 - | -LL | struct NonLeakS; - | ^^^^^^^^^^^^^^^ -note: required by a bound in `methods::Trait::leak_foo` - --> $DIR/maybe-bounds-in-traits.rs:101:9 - | -LL | fn leak_foo(&self) {} - | ^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Trait::leak_foo` - -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors Some errors have detailed explanations: E0277, E0658. For more information about an error, try `rustc --explain E0277`. From 3ab7b397bbd27d220d1eebc1b21963235dd26711 Mon Sep 17 00:00:00 2001 From: Bryanskiy Date: Sun, 24 Aug 2025 21:29:26 +0300 Subject: [PATCH 338/682] Permit `more_maybe_bounds` in supertraits and trait objects only --- compiler/rustc_ast_lowering/src/lib.rs | 15 +++++---- tests/ui/trait-bounds/more_maybe_bounds.rs | 14 ++++++++ .../ui/trait-bounds/more_maybe_bounds.stderr | 32 ++++++++++++++++++- .../maybe-bounds-in-traits.rs | 7 ++-- .../maybe-bounds-in-traits.stderr | 10 +++--- 5 files changed, 60 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 72f20a95ff02..4e2243e87873 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -2101,17 +2101,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { { return; } - if self.tcx.features().more_maybe_bounds() { - return; - } } RelaxedBoundPolicy::Forbidden(reason) => { - if self.tcx.features().more_maybe_bounds() { - return; - } - match reason { RelaxedBoundForbiddenReason::TraitObjectTy => { + if self.tcx.features().more_maybe_bounds() { + return; + } + self.dcx().span_err( span, "relaxed bounds are not permitted in trait object types", @@ -2119,6 +2116,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { return; } RelaxedBoundForbiddenReason::SuperTrait => { + if self.tcx.features().more_maybe_bounds() { + return; + } + let mut diag = self.dcx().struct_span_err( span, "relaxed bounds are not permitted in supertrait bounds", diff --git a/tests/ui/trait-bounds/more_maybe_bounds.rs b/tests/ui/trait-bounds/more_maybe_bounds.rs index d367dd5b299d..ddd4313bd5e1 100644 --- a/tests/ui/trait-bounds/more_maybe_bounds.rs +++ b/tests/ui/trait-bounds/more_maybe_bounds.rs @@ -21,6 +21,20 @@ fn bar(_: &T) {} // FIXME: `?Trait1` should be rejected, `Trait1` isn't marked `#[lang = "default_traitN"]`. fn baz() where T: Iterator {} +//~^ ERROR this relaxed bound is not permitted here + +struct S1(T); + +impl S1 { + fn f() where T: ?Trait1 {} + //~^ ERROR this relaxed bound is not permitted here +} + +trait Trait5<'a> {} + +struct S2(T) where for<'a> T: ?Trait5<'a>; +//~^ ERROR this relaxed bound is not permitted here +//~| ERROR bound modifier `?` can only be applied to default traits like `Sized` struct S; impl !Trait2 for S {} diff --git a/tests/ui/trait-bounds/more_maybe_bounds.stderr b/tests/ui/trait-bounds/more_maybe_bounds.stderr index 8dd83fc7728a..0d78cfd58204 100644 --- a/tests/ui/trait-bounds/more_maybe_bounds.stderr +++ b/tests/ui/trait-bounds/more_maybe_bounds.stderr @@ -1,3 +1,27 @@ +error: this relaxed bound is not permitted here + --> $DIR/more_maybe_bounds.rs:23:37 + | +LL | fn baz() where T: Iterator {} + | ^^^^^^^ + | + = note: in this context, relaxed bounds are only allowed on type parameters defined by the closest item + +error: this relaxed bound is not permitted here + --> $DIR/more_maybe_bounds.rs:29:21 + | +LL | fn f() where T: ?Trait1 {} + | ^^^^^^^ + | + = note: in this context, relaxed bounds are only allowed on type parameters defined by the closest item + +error: this relaxed bound is not permitted here + --> $DIR/more_maybe_bounds.rs:35:34 + | +LL | struct S2(T) where for<'a> T: ?Trait5<'a>; + | ^^^^^^^^^^^ + | + = note: in this context, relaxed bounds are only allowed on type parameters defined by the closest item + error: bound modifier `?` can only be applied to default traits like `Sized` --> $DIR/more_maybe_bounds.rs:17:20 | @@ -16,5 +40,11 @@ error: bound modifier `?` can only be applied to default traits like `Sized` LL | fn bar(_: &T) {} | ^^^^^^^ -error: aborting due to 3 previous errors +error: bound modifier `?` can only be applied to default traits like `Sized` + --> $DIR/more_maybe_bounds.rs:35:34 + | +LL | struct S2(T) where for<'a> T: ?Trait5<'a>; + | ^^^^^^^^^^^ + +error: aborting due to 7 previous errors diff --git a/tests/ui/traits/default_auto_traits/maybe-bounds-in-traits.rs b/tests/ui/traits/default_auto_traits/maybe-bounds-in-traits.rs index 820132b4e543..ac4c4aca2efe 100644 --- a/tests/ui/traits/default_auto_traits/maybe-bounds-in-traits.rs +++ b/tests/ui/traits/default_auto_traits/maybe-bounds-in-traits.rs @@ -42,11 +42,8 @@ struct LeakS; mod supertraits { use crate::*; - trait MaybeLeakT1: ?Leak {} - trait MaybeLeakT2 where Self: ?Leak {} - - impl MaybeLeakT1 for NonLeakS {} - impl MaybeLeakT2 for NonLeakS {} + trait MaybeLeak: ?Leak {} + impl MaybeLeak for NonLeakS {} trait LeakT {} impl LeakT for NonLeakS {} diff --git a/tests/ui/traits/default_auto_traits/maybe-bounds-in-traits.stderr b/tests/ui/traits/default_auto_traits/maybe-bounds-in-traits.stderr index 95c77b1756cf..ab62ab81b213 100644 --- a/tests/ui/traits/default_auto_traits/maybe-bounds-in-traits.stderr +++ b/tests/ui/traits/default_auto_traits/maybe-bounds-in-traits.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `NonLeakS: Leak` is not satisfied - --> $DIR/maybe-bounds-in-traits.rs:52:20 + --> $DIR/maybe-bounds-in-traits.rs:49:20 | LL | impl LeakT for NonLeakS {} | ^^^^^^^^ unsatisfied trait bound @@ -10,13 +10,13 @@ help: the trait `Leak` is not implemented for `NonLeakS` LL | struct NonLeakS; | ^^^^^^^^^^^^^^^ note: required by a bound in `LeakT` - --> $DIR/maybe-bounds-in-traits.rs:51:5 + --> $DIR/maybe-bounds-in-traits.rs:48:5 | LL | trait LeakT {} | ^^^^^^^^^^^^^^ required by this bound in `LeakT` error[E0277]: the trait bound `NonLeakS: Leak` is not satisfied - --> $DIR/maybe-bounds-in-traits.rs:61:22 + --> $DIR/maybe-bounds-in-traits.rs:58:22 | LL | type Leak2 = NonLeakS; | ^^^^^^^^ unsatisfied trait bound @@ -27,13 +27,13 @@ help: the trait `Leak` is not implemented for `NonLeakS` LL | struct NonLeakS; | ^^^^^^^^^^^^^^^ note: required by a bound in `Test1::Leak2` - --> $DIR/maybe-bounds-in-traits.rs:61:9 + --> $DIR/maybe-bounds-in-traits.rs:58:9 | LL | type Leak2 = NonLeakS; | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Test1::Leak2` error[E0658]: `&mut Self` cannot be used as the type of `self` without the `arbitrary_self_types` feature - --> $DIR/maybe-bounds-in-traits.rs:80:20 + --> $DIR/maybe-bounds-in-traits.rs:77:20 | LL | fn mut_foo(&mut self) {} | ^^^^^^^^^ From b1c4e19e13d7ad2eea0d2afae92791c28d7239fa Mon Sep 17 00:00:00 2001 From: Romain Perier Date: Tue, 9 Sep 2025 19:57:56 +0200 Subject: [PATCH 339/682] Unify and deduplicate bits conv float tests --- library/coretests/tests/floats/f128.rs | 28 ------------ library/coretests/tests/floats/f16.rs | 27 ----------- library/coretests/tests/floats/f32.rs | 28 ------------ library/coretests/tests/floats/f64.rs | 27 ----------- library/coretests/tests/floats/mod.rs | 63 ++++++++++++++++++++++++++ 5 files changed, 63 insertions(+), 110 deletions(-) diff --git a/library/coretests/tests/floats/f128.rs b/library/coretests/tests/floats/f128.rs index 4267fef50f83..d31eba863f5f 100644 --- a/library/coretests/tests/floats/f128.rs +++ b/library/coretests/tests/floats/f128.rs @@ -17,12 +17,6 @@ const TOL: f128 = 1e-12; #[allow(unused)] const TOL_PRECISE: f128 = 1e-28; -/// First pattern over the mantissa -const NAN_MASK1: u128 = 0x0000aaaaaaaaaaaaaaaaaaaaaaaaaaaa; - -/// Second pattern over the mantissa -const NAN_MASK2: u128 = 0x00005555555555555555555555555555; - // FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support // the intrinsics. @@ -54,28 +48,6 @@ fn test_max_recip() { ); } -#[test] -fn test_float_bits_conv() { - assert_eq!((1f128).to_bits(), 0x3fff0000000000000000000000000000); - assert_eq!((12.5f128).to_bits(), 0x40029000000000000000000000000000); - assert_eq!((1337f128).to_bits(), 0x40094e40000000000000000000000000); - assert_eq!((-14.25f128).to_bits(), 0xc002c800000000000000000000000000); - assert_biteq!(f128::from_bits(0x3fff0000000000000000000000000000), 1.0); - assert_biteq!(f128::from_bits(0x40029000000000000000000000000000), 12.5); - assert_biteq!(f128::from_bits(0x40094e40000000000000000000000000), 1337.0); - assert_biteq!(f128::from_bits(0xc002c800000000000000000000000000), -14.25); - - // Check that NaNs roundtrip their bits regardless of signaling-ness - // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits - let masked_nan1 = f128::NAN.to_bits() ^ NAN_MASK1; - let masked_nan2 = f128::NAN.to_bits() ^ NAN_MASK2; - assert!(f128::from_bits(masked_nan1).is_nan()); - assert!(f128::from_bits(masked_nan2).is_nan()); - - assert_eq!(f128::from_bits(masked_nan1).to_bits(), masked_nan1); - assert_eq!(f128::from_bits(masked_nan2).to_bits(), masked_nan2); -} - #[test] fn test_from() { assert_biteq!(f128::from(false), 0.0); diff --git a/library/coretests/tests/floats/f16.rs b/library/coretests/tests/floats/f16.rs index d2818a6d7689..302fd0861d75 100644 --- a/library/coretests/tests/floats/f16.rs +++ b/library/coretests/tests/floats/f16.rs @@ -19,12 +19,6 @@ const TOL_P2: f16 = 0.5; #[allow(unused)] const TOL_P4: f16 = 10.0; -/// First pattern over the mantissa -const NAN_MASK1: u16 = 0x02aa; - -/// Second pattern over the mantissa -const NAN_MASK2: u16 = 0x0155; - // FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support // the intrinsics. @@ -52,27 +46,6 @@ fn test_max_recip() { assert_approx_eq!(f16::MAX.recip(), 1.526624e-5f16, 1e-4); } -#[test] -fn test_float_bits_conv() { - assert_eq!((1f16).to_bits(), 0x3c00); - assert_eq!((12.5f16).to_bits(), 0x4a40); - assert_eq!((1337f16).to_bits(), 0x6539); - assert_eq!((-14.25f16).to_bits(), 0xcb20); - assert_biteq!(f16::from_bits(0x3c00), 1.0); - assert_biteq!(f16::from_bits(0x4a40), 12.5); - assert_biteq!(f16::from_bits(0x6539), 1337.0); - assert_biteq!(f16::from_bits(0xcb20), -14.25); - - // Check that NaNs roundtrip their bits regardless of signaling-ness - let masked_nan1 = f16::NAN.to_bits() ^ NAN_MASK1; - let masked_nan2 = f16::NAN.to_bits() ^ NAN_MASK2; - assert!(f16::from_bits(masked_nan1).is_nan()); - assert!(f16::from_bits(masked_nan2).is_nan()); - - assert_eq!(f16::from_bits(masked_nan1).to_bits(), masked_nan1); - assert_eq!(f16::from_bits(masked_nan2).to_bits(), masked_nan2); -} - #[test] fn test_from() { assert_biteq!(f16::from(false), 0.0); diff --git a/library/coretests/tests/floats/f32.rs b/library/coretests/tests/floats/f32.rs index 7b25f354da42..a1fe8b076501 100644 --- a/library/coretests/tests/floats/f32.rs +++ b/library/coretests/tests/floats/f32.rs @@ -2,12 +2,6 @@ use core::f32; use super::assert_biteq; -/// First pattern over the mantissa -const NAN_MASK1: u32 = 0x002a_aaaa; - -/// Second pattern over the mantissa -const NAN_MASK2: u32 = 0x0055_5555; - // FIXME(#140515): mingw has an incorrect fma https://sourceforge.net/p/mingw-w64/bugs/848/ #[cfg_attr(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")), ignore)] #[test] @@ -25,25 +19,3 @@ fn test_mul_add() { assert_biteq!(f32::math::mul_add(8.9f32, inf, 3.2), inf); assert_biteq!(f32::math::mul_add(-3.2f32, 2.4, neg_inf), neg_inf); } - -#[test] -fn test_float_bits_conv() { - assert_eq!((1f32).to_bits(), 0x3f800000); - assert_eq!((12.5f32).to_bits(), 0x41480000); - assert_eq!((1337f32).to_bits(), 0x44a72000); - assert_eq!((-14.25f32).to_bits(), 0xc1640000); - assert_biteq!(f32::from_bits(0x3f800000), 1.0); - assert_biteq!(f32::from_bits(0x41480000), 12.5); - assert_biteq!(f32::from_bits(0x44a72000), 1337.0); - assert_biteq!(f32::from_bits(0xc1640000), -14.25); - - // Check that NaNs roundtrip their bits regardless of signaling-ness - // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits - let masked_nan1 = f32::NAN.to_bits() ^ NAN_MASK1; - let masked_nan2 = f32::NAN.to_bits() ^ NAN_MASK2; - assert!(f32::from_bits(masked_nan1).is_nan()); - assert!(f32::from_bits(masked_nan2).is_nan()); - - assert_eq!(f32::from_bits(masked_nan1).to_bits(), masked_nan1); - assert_eq!(f32::from_bits(masked_nan2).to_bits(), masked_nan2); -} diff --git a/library/coretests/tests/floats/f64.rs b/library/coretests/tests/floats/f64.rs index 099d85627a34..4c5a3d68d1fd 100644 --- a/library/coretests/tests/floats/f64.rs +++ b/library/coretests/tests/floats/f64.rs @@ -2,12 +2,6 @@ use core::f64; use super::assert_biteq; -/// First pattern over the mantissa -const NAN_MASK1: u64 = 0x000a_aaaa_aaaa_aaaa; - -/// Second pattern over the mantissa -const NAN_MASK2: u64 = 0x0005_5555_5555_5555; - // FIXME(#140515): mingw has an incorrect fma https://sourceforge.net/p/mingw-w64/bugs/848/ #[cfg_attr(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")), ignore)] #[test] @@ -25,24 +19,3 @@ fn test_mul_add() { assert_biteq!(8.9f64.mul_add(inf, 3.2), inf); assert_biteq!((-3.2f64).mul_add(2.4, neg_inf), neg_inf); } - -#[test] -fn test_float_bits_conv() { - assert_eq!((1f64).to_bits(), 0x3ff0000000000000); - assert_eq!((12.5f64).to_bits(), 0x4029000000000000); - assert_eq!((1337f64).to_bits(), 0x4094e40000000000); - assert_eq!((-14.25f64).to_bits(), 0xc02c800000000000); - assert_biteq!(f64::from_bits(0x3ff0000000000000), 1.0); - assert_biteq!(f64::from_bits(0x4029000000000000), 12.5); - assert_biteq!(f64::from_bits(0x4094e40000000000), 1337.0); - assert_biteq!(f64::from_bits(0xc02c800000000000), -14.25); - - // Check that NaNs roundtrip their bits regardless of signaling-ness - let masked_nan1 = f64::NAN.to_bits() ^ NAN_MASK1; - let masked_nan2 = f64::NAN.to_bits() ^ NAN_MASK2; - assert!(f64::from_bits(masked_nan1).is_nan()); - assert!(f64::from_bits(masked_nan2).is_nan()); - - assert_eq!(f64::from_bits(masked_nan1).to_bits(), masked_nan1); - assert_eq!(f64::from_bits(masked_nan2).to_bits(), masked_nan2); -} diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/floats/mod.rs index c0439845a4a4..31515561c637 100644 --- a/library/coretests/tests/floats/mod.rs +++ b/library/coretests/tests/floats/mod.rs @@ -30,6 +30,10 @@ trait TestableFloat: Sized { const EPS_ADD: Self; const EPS_MUL: Self; const EPS_DIV: Self; + const RAW_1: Self; + const RAW_12_DOT_5: Self; + const RAW_1337: Self; + const RAW_MINUS_14_DOT_25: Self; } impl TestableFloat for f16 { @@ -50,6 +54,10 @@ impl TestableFloat for f16 { const EPS_ADD: Self = if cfg!(miri) { 1e1 } else { 0.0 }; const EPS_MUL: Self = if cfg!(miri) { 1e3 } else { 0.0 }; const EPS_DIV: Self = if cfg!(miri) { 1e0 } else { 0.0 }; + const RAW_1: Self = Self::from_bits(0x3c00); + const RAW_12_DOT_5: Self = Self::from_bits(0x4a40); + const RAW_1337: Self = Self::from_bits(0x6539); + const RAW_MINUS_14_DOT_25: Self = Self::from_bits(0xcb20); } impl TestableFloat for f32 { @@ -72,6 +80,10 @@ impl TestableFloat for f32 { const EPS_ADD: Self = if cfg!(miri) { 1e-3 } else { 0.0 }; const EPS_MUL: Self = if cfg!(miri) { 1e-1 } else { 0.0 }; const EPS_DIV: Self = if cfg!(miri) { 1e-4 } else { 0.0 }; + const RAW_1: Self = Self::from_bits(0x3f800000); + const RAW_12_DOT_5: Self = Self::from_bits(0x41480000); + const RAW_1337: Self = Self::from_bits(0x44a72000); + const RAW_MINUS_14_DOT_25: Self = Self::from_bits(0xc1640000); } impl TestableFloat for f64 { @@ -90,6 +102,10 @@ impl TestableFloat for f64 { const EPS_ADD: Self = if cfg!(miri) { 1e-6 } else { 0.0 }; const EPS_MUL: Self = if cfg!(miri) { 1e-6 } else { 0.0 }; const EPS_DIV: Self = if cfg!(miri) { 1e-6 } else { 0.0 }; + const RAW_1: Self = Self::from_bits(0x3ff0000000000000); + const RAW_12_DOT_5: Self = Self::from_bits(0x4029000000000000); + const RAW_1337: Self = Self::from_bits(0x4094e40000000000); + const RAW_MINUS_14_DOT_25: Self = Self::from_bits(0xc02c800000000000); } impl TestableFloat for f128 { @@ -108,6 +124,10 @@ impl TestableFloat for f128 { const EPS_ADD: Self = if cfg!(miri) { 1e-6 } else { 0.0 }; const EPS_MUL: Self = if cfg!(miri) { 1e-6 } else { 0.0 }; const EPS_DIV: Self = if cfg!(miri) { 1e-6 } else { 0.0 }; + const RAW_1: Self = Self::from_bits(0x3fff0000000000000000000000000000); + const RAW_12_DOT_5: Self = Self::from_bits(0x40029000000000000000000000000000); + const RAW_1337: Self = Self::from_bits(0x40094e40000000000000000000000000); + const RAW_MINUS_14_DOT_25: Self = Self::from_bits(0xc002c800000000000000000000000000); } /// Determine the tolerance for values of the argument type. @@ -250,6 +270,8 @@ macro_rules! float_test { $( $( #[$f16_meta] )+ )? fn test_f16() { type $fty = f16; + #[allow(unused)] + const fn flt (x: $fty) -> $fty { x } $test } @@ -257,6 +279,8 @@ macro_rules! float_test { $( $( #[$f32_meta] )+ )? fn test_f32() { type $fty = f32; + #[allow(unused)] + const fn flt (x: $fty) -> $fty { x } $test } @@ -264,6 +288,8 @@ macro_rules! float_test { $( $( #[$f64_meta] )+ )? fn test_f64() { type $fty = f64; + #[allow(unused)] + const fn flt (x: $fty) -> $fty { x } $test } @@ -271,6 +297,8 @@ macro_rules! float_test { $( $( #[$f128_meta] )+ )? fn test_f128() { type $fty = f128; + #[allow(unused)] + const fn flt (x: $fty) -> $fty { x } $test } @@ -293,6 +321,8 @@ macro_rules! float_test { $( $( #[$f16_const_meta] )+ )? fn test_f16() { type $fty = f16; + #[allow(unused)] + const fn flt (x: $fty) -> $fty { x } const { $test } } @@ -300,6 +330,8 @@ macro_rules! float_test { $( $( #[$f32_const_meta] )+ )? fn test_f32() { type $fty = f32; + #[allow(unused)] + const fn flt (x: $fty) -> $fty { x } const { $test } } @@ -307,6 +339,8 @@ macro_rules! float_test { $( $( #[$f64_const_meta] )+ )? fn test_f64() { type $fty = f64; + #[allow(unused)] + const fn flt (x: $fty) -> $fty { x } const { $test } } @@ -314,6 +348,8 @@ macro_rules! float_test { $( $( #[$f128_const_meta] )+ )? fn test_f128() { type $fty = f128; + #[allow(unused)] + const fn flt (x: $fty) -> $fty { x } const { $test } } } @@ -1479,3 +1515,30 @@ float_test! { assert_approx_eq!(a.algebraic_rem(b), a % b, Float::EPS_DIV); } } + +float_test! { + name: to_bits_conv, + attrs: { + f16: #[cfg(target_has_reliable_f16)], + f128: #[cfg(target_has_reliable_f128)], + }, + test { + assert_biteq!(flt(1.0), Float::RAW_1); + assert_biteq!(flt(12.5), Float::RAW_12_DOT_5); + assert_biteq!(flt(1337.0), Float::RAW_1337); + assert_biteq!(flt(-14.25), Float::RAW_MINUS_14_DOT_25); + assert_biteq!(Float::RAW_1, 1.0); + assert_biteq!(Float::RAW_12_DOT_5, 12.5); + assert_biteq!(Float::RAW_1337, 1337.0); + assert_biteq!(Float::RAW_MINUS_14_DOT_25, -14.25); + + // Check that NaNs roundtrip their bits regardless of signaling-ness + let masked_nan1 = Float::NAN.to_bits() ^ Float::NAN_MASK1; + let masked_nan2 = Float::NAN.to_bits() ^ Float::NAN_MASK2; + assert!(Float::from_bits(masked_nan1).is_nan()); + assert!(Float::from_bits(masked_nan2).is_nan()); + + assert_biteq!(Float::from_bits(masked_nan1), Float::from_bits(masked_nan1)); + assert_biteq!(Float::from_bits(masked_nan2), Float::from_bits(masked_nan2)); + } +} From ad08577a503dfd03e308bd272e76d95e31c6d0ef Mon Sep 17 00:00:00 2001 From: joboet Date: Sat, 9 Aug 2025 19:07:08 +0200 Subject: [PATCH 340/682] std: move `thread` into `sys` --- library/std/src/sys/mod.rs | 1 + library/std/src/sys/pal/hermit/mod.rs | 1 - library/std/src/sys/pal/sgx/abi/mod.rs | 2 +- library/std/src/sys/pal/sgx/mod.rs | 1 - library/std/src/sys/pal/solid/mod.rs | 4 +- library/std/src/sys/pal/teeos/mod.rs | 1 - library/std/src/sys/pal/uefi/mod.rs | 1 - library/std/src/sys/pal/unix/mod.rs | 3 +- library/std/src/sys/pal/wasip1/mod.rs | 1 - library/std/src/sys/pal/wasip2/mod.rs | 1 - library/std/src/sys/pal/wasip2/time.rs | 2 +- library/std/src/sys/pal/wasm/mod.rs | 15 +- library/std/src/sys/pal/windows/mod.rs | 5 +- library/std/src/sys/pal/windows/time.rs | 2 +- library/std/src/sys/pal/xous/mod.rs | 1 - library/std/src/sys/thread/hermit.rs | 68 +-- library/std/src/sys/thread/mod.rs | 152 +++++ library/std/src/sys/thread/sgx.rs | 49 +- library/std/src/sys/thread/solid.rs | 42 +- library/std/src/sys/thread/teeos.rs | 71 +-- library/std/src/sys/thread/uefi.rs | 75 +-- library/std/src/sys/thread/unix.rs | 667 +++++++++++----------- library/std/src/sys/thread/unsupported.rs | 39 +- library/std/src/sys/thread/wasip1.rs | 333 +++++------ library/std/src/sys/thread/wasip2.rs | 85 +-- library/std/src/sys/thread/wasm.rs | 90 +-- library/std/src/sys/thread/windows.rs | 112 ++-- library/std/src/sys/thread/xous.rs | 54 +- library/std/src/thread/mod.rs | 8 +- 29 files changed, 843 insertions(+), 1043 deletions(-) create mode 100644 library/std/src/sys/thread/mod.rs diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index 8c1150155806..2dbdc8a4e026 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -31,6 +31,7 @@ pub mod process; pub mod random; pub mod stdio; pub mod sync; +pub mod thread; pub mod thread_local; // FIXME(117276): remove this, move feature implementations into individual diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index fb8d69b73759..3ddf6e5acb02 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -25,7 +25,6 @@ pub mod futex; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -pub mod thread; pub mod time; pub fn unsupported() -> crate::io::Result { diff --git a/library/std/src/sys/pal/sgx/abi/mod.rs b/library/std/src/sys/pal/sgx/abi/mod.rs index 57247cffad3f..b8c4d7740c4e 100644 --- a/library/std/src/sys/pal/sgx/abi/mod.rs +++ b/library/std/src/sys/pal/sgx/abi/mod.rs @@ -67,7 +67,7 @@ extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64 let tls_guard = unsafe { tls.activate() }; if secondary { - let join_notifier = super::thread::Thread::entry(); + let join_notifier = crate::sys::thread::Thread::entry(); drop(tls_guard); drop(join_notifier); diff --git a/library/std/src/sys/pal/sgx/mod.rs b/library/std/src/sys/pal/sgx/mod.rs index 4a297b6823f2..9a33873af581 100644 --- a/library/std/src/sys/pal/sgx/mod.rs +++ b/library/std/src/sys/pal/sgx/mod.rs @@ -13,7 +13,6 @@ mod libunwind_integration; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -pub mod thread; pub mod thread_parking; pub mod time; pub mod waitqueue; diff --git a/library/std/src/sys/pal/solid/mod.rs b/library/std/src/sys/pal/solid/mod.rs index 0011cf256df7..9ca6dc581183 100644 --- a/library/std/src/sys/pal/solid/mod.rs +++ b/library/std/src/sys/pal/solid/mod.rs @@ -10,10 +10,8 @@ pub mod itron { pub mod error; pub mod spin; pub mod task; - pub mod thread; pub mod thread_parking; pub mod time; - use super::unsupported; } // `error` is `pub(crate)` so that it can be accessed by `itron/error.rs` as @@ -22,7 +20,7 @@ pub(crate) mod error; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -pub use self::itron::{thread, thread_parking}; +pub use self::itron::thread_parking; pub mod time; // SAFETY: must be called only once during runtime initialization. diff --git a/library/std/src/sys/pal/teeos/mod.rs b/library/std/src/sys/pal/teeos/mod.rs index c7b177772585..dd0155265da6 100644 --- a/library/std/src/sys/pal/teeos/mod.rs +++ b/library/std/src/sys/pal/teeos/mod.rs @@ -9,7 +9,6 @@ pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -pub mod thread; #[allow(non_upper_case_globals)] #[path = "../unix/time.rs"] pub mod time; diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs index 8911a2ee5194..ebd311db1e12 100644 --- a/library/std/src/sys/pal/uefi/mod.rs +++ b/library/std/src/sys/pal/uefi/mod.rs @@ -17,7 +17,6 @@ pub mod helpers; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -pub mod thread; pub mod time; #[cfg(test)] diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index ac5c823a1bf6..dd1059fe04a2 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -17,7 +17,6 @@ pub mod os; pub mod pipe; pub mod stack_overflow; pub mod sync; -pub mod thread; pub mod thread_parking; pub mod time; @@ -55,7 +54,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { // thread-id for the main thread and so renaming the main thread will rename the // process and we only want to enable this on platforms we've tested. if cfg!(target_vendor = "apple") { - thread::Thread::set_name(&c"main"); + crate::sys::thread::set_name(c"main"); } unsafe fn sanitize_standard_fds() { diff --git a/library/std/src/sys/pal/wasip1/mod.rs b/library/std/src/sys/pal/wasip1/mod.rs index 61dd1c3f98b1..ae5da3c1f77b 100644 --- a/library/std/src/sys/pal/wasip1/mod.rs +++ b/library/std/src/sys/pal/wasip1/mod.rs @@ -20,7 +20,6 @@ pub mod futex; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -pub mod thread; pub mod time; #[path = "../unsupported/common.rs"] diff --git a/library/std/src/sys/pal/wasip2/mod.rs b/library/std/src/sys/pal/wasip2/mod.rs index 5f3fb6d6ddfb..c1d89da2677c 100644 --- a/library/std/src/sys/pal/wasip2/mod.rs +++ b/library/std/src/sys/pal/wasip2/mod.rs @@ -14,7 +14,6 @@ pub mod futex; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -pub mod thread; pub mod time; #[path = "../unsupported/common.rs"] diff --git a/library/std/src/sys/pal/wasip2/time.rs b/library/std/src/sys/pal/wasip2/time.rs index f1f6839774b9..980070e7b851 100644 --- a/library/std/src/sys/pal/wasip2/time.rs +++ b/library/std/src/sys/pal/wasip2/time.rs @@ -25,7 +25,7 @@ impl Instant { Some(Instant(self.0.checked_sub(*other)?)) } - pub(super) fn as_duration(&self) -> &Duration { + pub(crate) fn as_duration(&self) -> &Duration { &self.0 } } diff --git a/library/std/src/sys/pal/wasm/mod.rs b/library/std/src/sys/pal/wasm/mod.rs index 346c9ff88c96..a20cd0e9ac77 100644 --- a/library/std/src/sys/pal/wasm/mod.rs +++ b/library/std/src/sys/pal/wasm/mod.rs @@ -23,18 +23,9 @@ pub mod pipe; #[path = "../unsupported/time.rs"] pub mod time; -cfg_select! { - target_feature = "atomics" => { - #[path = "atomics/futex.rs"] - pub mod futex; - #[path = "atomics/thread.rs"] - pub mod thread; - } - _ => { - #[path = "../unsupported/thread.rs"] - pub mod thread; - } -} +#[cfg(target_feature = "atomics")] +#[path = "atomics/futex.rs"] +pub mod futex; #[path = "../unsupported/common.rs"] #[deny(unsafe_op_in_unsafe_fn)] diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index 3b6a86cbc8f4..3357946b8f71 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -20,7 +20,6 @@ pub mod futex; pub mod handle; pub mod os; pub mod pipe; -pub mod thread; pub mod time; cfg_select! { not(target_vendor = "uwp") => { @@ -48,9 +47,9 @@ pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) { unsafe { stack_overflow::init(); - // Normally, `thread::spawn` will call `Thread::set_name` but since this thread already + // Normally, `thread::spawn` will call `set_name` but since this thread already // exists, we have to call it ourselves. - thread::Thread::set_name_wide(wide_str!("main")); + crate::sys::thread::set_name_wide(wide_str!("main")); } } diff --git a/library/std/src/sys/pal/windows/time.rs b/library/std/src/sys/pal/windows/time.rs index a948c07e0a31..f8f9a9fd8180 100644 --- a/library/std/src/sys/pal/windows/time.rs +++ b/library/std/src/sys/pal/windows/time.rs @@ -232,7 +232,7 @@ mod perf_counter { } /// A timer you can wait on. -pub(super) struct WaitableTimer { +pub(crate) struct WaitableTimer { handle: c::HANDLE, } impl WaitableTimer { diff --git a/library/std/src/sys/pal/xous/mod.rs b/library/std/src/sys/pal/xous/mod.rs index 042c4ff862ff..e673157e0eb5 100644 --- a/library/std/src/sys/pal/xous/mod.rs +++ b/library/std/src/sys/pal/xous/mod.rs @@ -5,7 +5,6 @@ use crate::os::xous::ffi::exit; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -pub mod thread; pub mod time; #[path = "../unsupported/common.rs"] diff --git a/library/std/src/sys/thread/hermit.rs b/library/std/src/sys/thread/hermit.rs index cc4734b68190..4d9f3b114c2a 100644 --- a/library/std/src/sys/thread/hermit.rs +++ b/library/std/src/sys/thread/hermit.rs @@ -1,10 +1,5 @@ -#![allow(dead_code)] - -use super::hermit_abi; -use crate::ffi::CStr; -use crate::mem::ManuallyDrop; use crate::num::NonZero; -use crate::time::{Duration, Instant}; +use crate::time::Duration; use crate::{io, ptr}; pub type Tid = hermit_abi::Tid; @@ -68,57 +63,30 @@ impl Thread { } } - #[inline] - pub fn yield_now() { - unsafe { - hermit_abi::yield_now(); - } - } - - #[inline] - pub fn set_name(_name: &CStr) { - // nope - } - - #[inline] - pub fn sleep(dur: Duration) { - let micros = dur.as_micros() + if dur.subsec_nanos() % 1_000 > 0 { 1 } else { 0 }; - let micros = u64::try_from(micros).unwrap_or(u64::MAX); - - unsafe { - hermit_abi::usleep(micros); - } - } - - pub fn sleep_until(deadline: Instant) { - let now = Instant::now(); - - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); - } - } - pub fn join(self) { unsafe { let _ = hermit_abi::join(self.tid); } } - - #[inline] - pub fn id(&self) -> Tid { - self.tid - } - - #[inline] - pub fn into_id(self) -> Tid { - ManuallyDrop::new(self).tid - } -} - -pub(crate) fn current_os_id() -> Option { - None } pub fn available_parallelism() -> io::Result> { unsafe { Ok(NonZero::new_unchecked(hermit_abi::available_parallelism())) } } + +#[inline] +pub fn sleep(dur: Duration) { + let micros = dur.as_micros() + if dur.subsec_nanos() % 1_000 > 0 { 1 } else { 0 }; + let micros = u64::try_from(micros).unwrap_or(u64::MAX); + + unsafe { + hermit_abi::usleep(micros); + } +} + +#[inline] +pub fn yield_now() { + unsafe { + hermit_abi::yield_now(); + } +} diff --git a/library/std/src/sys/thread/mod.rs b/library/std/src/sys/thread/mod.rs new file mode 100644 index 000000000000..6bb7fc1a20e2 --- /dev/null +++ b/library/std/src/sys/thread/mod.rs @@ -0,0 +1,152 @@ +cfg_select! { + target_os = "hermit" => { + mod hermit; + pub use hermit::{Thread, available_parallelism, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; + #[expect(dead_code)] + mod unsupported; + pub use unsupported::{current_os_id, set_name}; + } + all(target_vendor = "fortanix", target_env = "sgx") => { + mod sgx; + pub use sgx::{Thread, current_os_id, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; + + // SGX should protect in-enclave data from outside attackers, so there + // must not be any data leakage to the OS, particularly no 1-1 mapping + // between SGX thread names and OS thread names. Hence `set_name` is + // intentionally a no-op. + // + // Note that the internally visible SGX thread name is already provided + // by the platform-agnostic Rust thread code. This can be observed in + // the [`std::thread::tests::test_named_thread`] test, which succeeds + // as-is with the SGX target. + #[expect(dead_code)] + mod unsupported; + pub use unsupported::{available_parallelism, set_name}; + } + target_os = "solid_asp3" => { + mod solid; + pub use solid::{Thread, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; + #[expect(dead_code)] + mod unsupported; + pub use unsupported::{available_parallelism, current_os_id, set_name}; + } + target_os = "teeos" => { + mod teeos; + pub use teeos::{Thread, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; + #[expect(dead_code)] + mod unsupported; + pub use unsupported::{available_parallelism, current_os_id, set_name}; + } + target_os = "uefi" => { + mod uefi; + pub use uefi::{available_parallelism, sleep}; + #[expect(dead_code)] + mod unsupported; + pub use unsupported::{Thread, current_os_id, set_name, yield_now, DEFAULT_MIN_STACK_SIZE}; + } + target_family = "unix" => { + mod unix; + pub use unix::{Thread, available_parallelism, current_os_id, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; + #[cfg(not(any( + target_env = "newlib", + target_os = "l4re", + target_os = "emscripten", + target_os = "redox", + target_os = "hurd", + target_os = "aix", + )))] + pub use unix::set_name; + #[cfg(any( + target_os = "freebsd", + target_os = "netbsd", + target_os = "linux", + target_os = "android", + target_os = "solaris", + target_os = "illumos", + target_os = "dragonfly", + target_os = "hurd", + target_os = "fuchsia", + target_os = "vxworks", + ))] + pub use unix::sleep_until; + #[expect(dead_code)] + mod unsupported; + #[cfg(any( + target_env = "newlib", + target_os = "l4re", + target_os = "emscripten", + target_os = "redox", + target_os = "hurd", + target_os = "aix", + ))] + pub use unsupported::set_name; + } + all(target_os = "wasi", target_env = "p1") => { + mod wasip1; + pub use wasip1::{DEFAULT_MIN_STACK_SIZE, sleep, yield_now}; + #[cfg(target_feature = "atomics")] + pub use wasip1::{Thread, available_parallelism}; + #[expect(dead_code)] + mod unsupported; + pub use unsupported::{current_os_id, set_name}; + #[cfg(not(target_feature = "atomics"))] + pub use unsupported::{Thread, available_parallelism}; + } + all(target_os = "wasi", target_env = "p2") => { + mod wasip2; + pub use wasip2::{sleep, sleep_until}; + #[expect(dead_code)] + mod unsupported; + // Note that unlike WASIp1 even if the wasm `atomics` feature is enabled + // there is no support for threads, not even experimentally, not even in + // wasi-libc. Thus this is unconditionally unsupported. + pub use unsupported::{Thread, available_parallelism, current_os_id, set_name, yield_now, DEFAULT_MIN_STACK_SIZE}; + } + all(target_family = "wasm", target_feature = "atomics") => { + mod wasm; + pub use wasm::sleep; + + #[expect(dead_code)] + mod unsupported; + pub use unsupported::{Thread, available_parallelism, current_os_id, set_name, yield_now, DEFAULT_MIN_STACK_SIZE}; + } + target_os = "windows" => { + mod windows; + pub use windows::{Thread, available_parallelism, current_os_id, set_name, set_name_wide, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; + } + target_os = "xous" => { + mod xous; + pub use xous::{Thread, available_parallelism, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; + + #[expect(dead_code)] + mod unsupported; + pub use unsupported::{current_os_id, set_name}; + } + _ => { + mod unsupported; + pub use unsupported::{Thread, available_parallelism, current_os_id, set_name, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; + } +} + +#[cfg(not(any( + target_os = "freebsd", + target_os = "netbsd", + target_os = "linux", + target_os = "android", + target_os = "solaris", + target_os = "illumos", + target_os = "dragonfly", + target_os = "hurd", + target_os = "fuchsia", + target_os = "vxworks", + all(target_os = "wasi", target_env = "p2"), +)))] +pub fn sleep_until(deadline: crate::time::Instant) { + use crate::time::Instant; + + let now = Instant::now(); + + if let Some(delay) = deadline.checked_duration_since(now) { + sleep(delay); + } +} diff --git a/library/std/src/sys/thread/sgx.rs b/library/std/src/sys/thread/sgx.rs index 1f613badcd70..f20ef7d86b9c 100644 --- a/library/std/src/sys/thread/sgx.rs +++ b/library/std/src/sys/thread/sgx.rs @@ -1,11 +1,8 @@ #![cfg_attr(test, allow(dead_code))] // why is this necessary? -use super::abi::{thread, usercalls}; -use super::unsupported; -use crate::ffi::CStr; use crate::io; -use crate::num::NonZero; -use crate::time::{Duration, Instant}; +use crate::sys::pal::abi::{thread, usercalls}; +use crate::time::Duration; pub struct Thread(task_queue::JoinHandle); @@ -108,51 +105,27 @@ impl Thread { Ok(Thread(handle)) } - pub(super) fn entry() -> JoinNotifier { + pub(crate) fn entry() -> JoinNotifier { let mut pending_tasks = task_queue::lock(); let task = rtunwrap!(Some, pending_tasks.pop()); drop(pending_tasks); // make sure to not hold the task queue lock longer than necessary task.run() } - pub fn yield_now() { - let wait_error = rtunwrap!(Err, usercalls::wait(0, usercalls::raw::WAIT_NO)); - rtassert!(wait_error.kind() == io::ErrorKind::WouldBlock); - } - - /// SGX should protect in-enclave data from the outside (attacker), - /// so there should be no data leakage to the OS, - /// and therefore also no 1-1 mapping between SGX thread names and OS thread names. - /// - /// This is why the method is intentionally No-Op. - pub fn set_name(_name: &CStr) { - // Note that the internally visible SGX thread name is already provided - // by the platform-agnostic (target-agnostic) Rust thread code. - // This can be observed in the [`std::thread::tests::test_named_thread`] test, - // which succeeds as-is with the SGX target. - } - - pub fn sleep(dur: Duration) { - usercalls::wait_timeout(0, dur, || true); - } - - pub fn sleep_until(deadline: Instant) { - let now = Instant::now(); - - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); - } - } - pub fn join(self) { self.0.wait(); } } -pub(crate) fn current_os_id() -> Option { +pub fn current_os_id() -> Option { Some(thread::current().addr().get() as u64) } -pub fn available_parallelism() -> io::Result> { - unsupported() +pub fn sleep(dur: Duration) { + usercalls::wait_timeout(0, dur, || true); +} + +pub fn yield_now() { + let wait_error = rtunwrap!(Err, usercalls::wait(0, usercalls::raw::WAIT_NO)); + rtassert!(wait_error.kind() == io::ErrorKind::WouldBlock); } diff --git a/library/std/src/sys/thread/solid.rs b/library/std/src/sys/thread/solid.rs index 4e14cb3cbcaa..46a84faa8022 100644 --- a/library/std/src/sys/thread/solid.rs +++ b/library/std/src/sys/thread/solid.rs @@ -1,16 +1,14 @@ //! Thread implementation backed by μITRON tasks. Assumes `acre_tsk` and //! `exd_tsk` are available. -use super::error::{ItronError, expect_success, expect_success_aborting}; -use super::time::dur2reltims; -use super::{abi, task}; use crate::cell::UnsafeCell; -use crate::ffi::CStr; use crate::mem::ManuallyDrop; -use crate::num::NonZero; use crate::ptr::NonNull; use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; -use crate::time::{Duration, Instant}; +use crate::sys::pal::itron::error::{ItronError, expect_success, expect_success_aborting}; +use crate::sys::pal::itron::time::dur2reltims; +use crate::sys::pal::itron::{abi, task}; +use crate::time::Duration; use crate::{hint, io}; pub struct Thread { @@ -195,28 +193,6 @@ impl Thread { Ok(Self { p_inner, task: new_task }) } - pub fn yield_now() { - expect_success(unsafe { abi::rot_rdq(abi::TPRI_SELF) }, &"rot_rdq"); - } - - pub fn set_name(_name: &CStr) { - // nope - } - - pub fn sleep(dur: Duration) { - for timeout in dur2reltims(dur) { - expect_success(unsafe { abi::dly_tsk(timeout) }, &"dly_tsk"); - } - } - - pub fn sleep_until(deadline: Instant) { - let now = Instant::now(); - - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); - } - } - pub fn join(self) { // Safety: `ThreadInner` is alive at this point let inner = unsafe { self.p_inner.as_ref() }; @@ -361,10 +337,12 @@ unsafe fn terminate_and_delete_current_task() -> ! { unsafe { crate::hint::unreachable_unchecked() }; } -pub(crate) fn current_os_id() -> Option { - None +pub fn yield_now() { + expect_success(unsafe { abi::rot_rdq(abi::TPRI_SELF) }, &"rot_rdq"); } -pub fn available_parallelism() -> io::Result> { - super::unsupported() +pub fn sleep(dur: Duration) { + for timeout in dur2reltims(dur) { + expect_success(unsafe { abi::dly_tsk(timeout) }, &"dly_tsk"); + } } diff --git a/library/std/src/sys/thread/teeos.rs b/library/std/src/sys/thread/teeos.rs index 1812d11e692e..cad100395c9f 100644 --- a/library/std/src/sys/thread/teeos.rs +++ b/library/std/src/sys/thread/teeos.rs @@ -1,12 +1,18 @@ -use crate::ffi::CStr; use crate::mem::{self, ManuallyDrop}; -use crate::num::NonZero; use crate::sys::os; -use crate::time::{Duration, Instant}; +use crate::time::Duration; use crate::{cmp, io, ptr}; pub const DEFAULT_MIN_STACK_SIZE: usize = 8 * 1024; +unsafe extern "C" { + safe fn TEE_Wait(timeout: u32) -> u32; +} + +fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { + libc::PTHREAD_STACK_MIN.try_into().expect("Infallible") +} + pub struct Thread { id: libc::pthread_t, } @@ -16,10 +22,6 @@ pub struct Thread { unsafe impl Send for Thread {} unsafe impl Sync for Thread {} -unsafe extern "C" { - pub fn TEE_Wait(timeout: u32) -> u32; -} - impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements pub unsafe fn new( @@ -74,7 +76,7 @@ impl Thread { } else { // The new thread will start running earliest after the next yield. // We add a yield here, so that the user does not have to. - Thread::yield_now(); + yield_now(); Ok(Thread { id: native }) }; @@ -91,36 +93,6 @@ impl Thread { } } - pub fn yield_now() { - let ret = unsafe { libc::sched_yield() }; - debug_assert_eq!(ret, 0); - } - - /// This does not do anything on teeos - pub fn set_name(_name: &CStr) { - // Both pthread_setname_np and prctl are not available to the TA, - // so we can't implement this currently. If the need arises please - // contact the teeos rustzone team. - } - - /// only main thread could wait for sometime in teeos - pub fn sleep(dur: Duration) { - let sleep_millis = dur.as_millis(); - let final_sleep: u32 = - if sleep_millis >= u32::MAX as u128 { u32::MAX } else { sleep_millis as u32 }; - unsafe { - let _ = TEE_Wait(final_sleep); - } - } - - pub fn sleep_until(deadline: Instant) { - let now = Instant::now(); - - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); - } - } - /// must join, because no pthread_detach supported pub fn join(self) { let id = self.into_id(); @@ -128,10 +100,6 @@ impl Thread { assert!(ret == 0, "failed to join thread: {}", io::Error::from_raw_os_error(ret)); } - pub fn id(&self) -> libc::pthread_t { - self.id - } - pub fn into_id(self) -> libc::pthread_t { ManuallyDrop::new(self).id } @@ -144,16 +112,15 @@ impl Drop for Thread { } } -pub(crate) fn current_os_id() -> Option { - None +pub fn yield_now() { + let ret = unsafe { libc::sched_yield() }; + debug_assert_eq!(ret, 0); } -// Note: Both `sched_getaffinity` and `sysconf` are available but not functional on -// teeos, so this function always returns an Error! -pub fn available_parallelism() -> io::Result> { - Err(io::Error::UNKNOWN_THREAD_COUNT) -} - -fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { - libc::PTHREAD_STACK_MIN.try_into().expect("Infallible") +/// only main thread could wait for sometime in teeos +pub fn sleep(dur: Duration) { + let sleep_millis = dur.as_millis(); + let final_sleep: u32 = + if sleep_millis >= u32::MAX as u128 { u32::MAX } else { sleep_millis as u32 }; + TEE_Wait(final_sleep); } diff --git a/library/std/src/sys/thread/uefi.rs b/library/std/src/sys/thread/uefi.rs index 47a48008c76d..94f67d7ace2b 100644 --- a/library/std/src/sys/thread/uefi.rs +++ b/library/std/src/sys/thread/uefi.rs @@ -1,66 +1,25 @@ -use super::unsupported; -use crate::ffi::CStr; use crate::io; use crate::num::NonZero; use crate::ptr::NonNull; -use crate::time::{Duration, Instant}; - -pub struct Thread(!); - -pub const DEFAULT_MIN_STACK_SIZE: usize = 64 * 1024; - -impl Thread { - // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new( - _stack: usize, - _name: Option<&str>, - _p: Box, - ) -> io::Result { - unsupported() - } - - pub fn yield_now() { - // do nothing - } - - pub fn set_name(_name: &CStr) { - // nope - } - - pub fn sleep(dur: Duration) { - let boot_services: NonNull = - crate::os::uefi::env::boot_services().expect("can't sleep").cast(); - let mut dur_ms = dur.as_micros(); - // ceil up to the nearest microsecond - if dur.subsec_nanos() % 1000 > 0 { - dur_ms += 1; - } - - while dur_ms > 0 { - let ms = crate::cmp::min(dur_ms, usize::MAX as u128); - let _ = unsafe { ((*boot_services.as_ptr()).stall)(ms as usize) }; - dur_ms -= ms; - } - } - - pub fn sleep_until(deadline: Instant) { - let now = Instant::now(); - - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); - } - } - - pub fn join(self) { - self.0 - } -} - -pub(crate) fn current_os_id() -> Option { - None -} +use crate::time::Duration; pub fn available_parallelism() -> io::Result> { // UEFI is single threaded Ok(NonZero::new(1).unwrap()) } + +pub fn sleep(dur: Duration) { + let boot_services: NonNull = + crate::os::uefi::env::boot_services().expect("can't sleep").cast(); + let mut dur_ms = dur.as_micros(); + // ceil up to the nearest microsecond + if dur.subsec_nanos() % 1000 > 0 { + dur_ms += 1; + } + + while dur_ms > 0 { + let ms = crate::cmp::min(dur_ms, usize::MAX as u128); + let _ = unsafe { ((*boot_services.as_ptr()).stall)(ms as usize) }; + dur_ms -= ms; + } +} diff --git a/library/std/src/sys/thread/unix.rs b/library/std/src/sys/thread/unix.rs index 3389b8c0c8a5..2d2c4f902128 100644 --- a/library/std/src/sys/thread/unix.rs +++ b/library/std/src/sys/thread/unix.rs @@ -1,3 +1,11 @@ +#[cfg(not(any( + target_env = "newlib", + target_os = "l4re", + target_os = "emscripten", + target_os = "redox", + target_os = "hurd", + target_os = "aix", +)))] use crate::ffi::CStr; use crate::mem::{self, ManuallyDrop}; use crate::num::NonZero; @@ -6,7 +14,7 @@ use crate::sys::weak::dlsym; #[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "nto",))] use crate::sys::weak::weak; use crate::sys::{os, stack_overflow}; -use crate::time::{Duration, Instant}; +use crate::time::Duration; use crate::{cmp, io, ptr}; #[cfg(not(any( target_os = "l4re", @@ -121,273 +129,6 @@ impl Thread { } } - pub fn yield_now() { - let ret = unsafe { libc::sched_yield() }; - debug_assert_eq!(ret, 0); - } - - #[cfg(target_os = "android")] - pub fn set_name(name: &CStr) { - const PR_SET_NAME: libc::c_int = 15; - unsafe { - let res = libc::prctl( - PR_SET_NAME, - name.as_ptr(), - 0 as libc::c_ulong, - 0 as libc::c_ulong, - 0 as libc::c_ulong, - ); - // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. - debug_assert_eq!(res, 0); - } - } - - #[cfg(any( - target_os = "linux", - target_os = "freebsd", - target_os = "dragonfly", - target_os = "nuttx", - target_os = "cygwin" - ))] - pub fn set_name(name: &CStr) { - unsafe { - cfg_select! { - any(target_os = "linux", target_os = "cygwin") => { - // Linux and Cygwin limits the allowed length of the name. - const TASK_COMM_LEN: usize = 16; - let name = truncate_cstr::<{ TASK_COMM_LEN }>(name); - } - _ => { - // FreeBSD, DragonFly BSD and NuttX do not enforce length limits. - } - }; - // Available since glibc 2.12, musl 1.1.16, and uClibc 1.0.20 for Linux, - // FreeBSD 12.2 and 13.0, and DragonFly BSD 6.0. - let res = libc::pthread_setname_np(libc::pthread_self(), name.as_ptr()); - // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. - debug_assert_eq!(res, 0); - } - } - - #[cfg(target_os = "openbsd")] - pub fn set_name(name: &CStr) { - unsafe { - libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr()); - } - } - - #[cfg(target_vendor = "apple")] - pub fn set_name(name: &CStr) { - unsafe { - let name = truncate_cstr::<{ libc::MAXTHREADNAMESIZE }>(name); - let res = libc::pthread_setname_np(name.as_ptr()); - // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. - debug_assert_eq!(res, 0); - } - } - - #[cfg(target_os = "netbsd")] - pub fn set_name(name: &CStr) { - unsafe { - let res = libc::pthread_setname_np( - libc::pthread_self(), - c"%s".as_ptr(), - name.as_ptr() as *mut libc::c_void, - ); - debug_assert_eq!(res, 0); - } - } - - #[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "nto"))] - pub fn set_name(name: &CStr) { - weak!( - fn pthread_setname_np( - thread: libc::pthread_t, - name: *const libc::c_char, - ) -> libc::c_int; - ); - - if let Some(f) = pthread_setname_np.get() { - #[cfg(target_os = "nto")] - const THREAD_NAME_MAX: usize = libc::_NTO_THREAD_NAME_MAX as usize; - #[cfg(any(target_os = "solaris", target_os = "illumos"))] - const THREAD_NAME_MAX: usize = 32; - - let name = truncate_cstr::<{ THREAD_NAME_MAX }>(name); - let res = unsafe { f(libc::pthread_self(), name.as_ptr()) }; - debug_assert_eq!(res, 0); - } - } - - #[cfg(target_os = "fuchsia")] - pub fn set_name(name: &CStr) { - use super::fuchsia::*; - unsafe { - zx_object_set_property( - zx_thread_self(), - ZX_PROP_NAME, - name.as_ptr() as *const libc::c_void, - name.to_bytes().len(), - ); - } - } - - #[cfg(target_os = "haiku")] - pub fn set_name(name: &CStr) { - unsafe { - let thread_self = libc::find_thread(ptr::null_mut()); - let res = libc::rename_thread(thread_self, name.as_ptr()); - // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. - debug_assert_eq!(res, libc::B_OK); - } - } - - #[cfg(target_os = "vxworks")] - pub fn set_name(name: &CStr) { - let mut name = truncate_cstr::<{ (libc::VX_TASK_RENAME_LENGTH - 1) as usize }>(name); - let res = unsafe { libc::taskNameSet(libc::taskIdSelf(), name.as_mut_ptr()) }; - debug_assert_eq!(res, libc::OK); - } - - #[cfg(any( - target_env = "newlib", - target_os = "l4re", - target_os = "emscripten", - target_os = "redox", - target_os = "hurd", - target_os = "aix", - ))] - pub fn set_name(_name: &CStr) { - // Newlib and Emscripten have no way to set a thread name. - } - - #[cfg(not(target_os = "espidf"))] - pub fn sleep(dur: Duration) { - let mut secs = dur.as_secs(); - let mut nsecs = dur.subsec_nanos() as _; - - // If we're awoken with a signal then the return value will be -1 and - // nanosleep will fill in `ts` with the remaining time. - unsafe { - while secs > 0 || nsecs > 0 { - let mut ts = libc::timespec { - tv_sec: cmp::min(libc::time_t::MAX as u64, secs) as libc::time_t, - tv_nsec: nsecs, - }; - secs -= ts.tv_sec as u64; - let ts_ptr = &raw mut ts; - if libc::nanosleep(ts_ptr, ts_ptr) == -1 { - assert_eq!(os::errno(), libc::EINTR); - secs += ts.tv_sec as u64; - nsecs = ts.tv_nsec; - } else { - nsecs = 0; - } - } - } - } - - #[cfg(target_os = "espidf")] - pub fn sleep(dur: Duration) { - // ESP-IDF does not have `nanosleep`, so we use `usleep` instead. - // As per the documentation of `usleep`, it is expected to support - // sleep times as big as at least up to 1 second. - // - // ESP-IDF does support almost up to `u32::MAX`, but due to a potential integer overflow in its - // `usleep` implementation - // (https://github.com/espressif/esp-idf/blob/d7ca8b94c852052e3bc33292287ef4dd62c9eeb1/components/newlib/time.c#L210), - // we limit the sleep time to the maximum one that would not cause the underlying `usleep` implementation to overflow - // (`portTICK_PERIOD_MS` can be anything between 1 to 1000, and is 10 by default). - const MAX_MICROS: u32 = u32::MAX - 1_000_000 - 1; - - // Add any nanoseconds smaller than a microsecond as an extra microsecond - // so as to comply with the `std::thread::sleep` contract which mandates - // implementations to sleep for _at least_ the provided `dur`. - // We can't overflow `micros` as it is a `u128`, while `Duration` is a pair of - // (`u64` secs, `u32` nanos), where the nanos are strictly smaller than 1 second - // (i.e. < 1_000_000_000) - let mut micros = dur.as_micros() + if dur.subsec_nanos() % 1_000 > 0 { 1 } else { 0 }; - - while micros > 0 { - let st = if micros > MAX_MICROS as u128 { MAX_MICROS } else { micros as u32 }; - unsafe { - libc::usleep(st); - } - - micros -= st as u128; - } - } - - // Any unix that has clock_nanosleep - // If this list changes update the MIRI chock_nanosleep shim - #[cfg(any( - target_os = "freebsd", - target_os = "netbsd", - target_os = "linux", - target_os = "android", - target_os = "solaris", - target_os = "illumos", - target_os = "dragonfly", - target_os = "hurd", - target_os = "fuchsia", - target_os = "vxworks", - ))] - pub fn sleep_until(deadline: Instant) { - let Some(ts) = deadline.into_inner().into_timespec().to_timespec() else { - // The deadline is further in the future then can be passed to - // clock_nanosleep. We have to use Self::sleep instead. This might - // happen on 32 bit platforms, especially closer to 2038. - let now = Instant::now(); - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); - } - return; - }; - - unsafe { - // When we get interrupted (res = EINTR) call clock_nanosleep again - loop { - let res = libc::clock_nanosleep( - super::time::Instant::CLOCK_ID, - libc::TIMER_ABSTIME, - &ts, - core::ptr::null_mut(), // not required with TIMER_ABSTIME - ); - - if res == 0 { - break; - } else { - assert_eq!( - res, - libc::EINTR, - "timespec is in range, - clockid is valid and kernel should support it" - ); - } - } - } - } - - // Any unix that does not have clock_nanosleep - #[cfg(not(any( - target_os = "freebsd", - target_os = "netbsd", - target_os = "linux", - target_os = "android", - target_os = "solaris", - target_os = "illumos", - target_os = "dragonfly", - target_os = "hurd", - target_os = "fuchsia", - target_os = "vxworks", - )))] - pub fn sleep_until(deadline: Instant) { - let now = Instant::now(); - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); - } - } - pub fn join(self) { let id = self.into_id(); let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; @@ -410,84 +151,6 @@ impl Drop for Thread { } } -pub(crate) fn current_os_id() -> Option { - // Most Unix platforms have a way to query an integer ID of the current thread, all with - // slightly different spellings. - // - // The OS thread ID is used rather than `pthread_self` so as to match what will be displayed - // for process inspection (debuggers, trace, `top`, etc.). - cfg_select! { - // Most platforms have a function returning a `pid_t` or int, which is an `i32`. - any(target_os = "android", target_os = "linux") => { - use crate::sys::weak::syscall; - - // `libc::gettid` is only available on glibc 2.30+, but the syscall is available - // since Linux 2.4.11. - syscall!(fn gettid() -> libc::pid_t;); - - // SAFETY: FFI call with no preconditions. - let id: libc::pid_t = unsafe { gettid() }; - Some(id as u64) - } - target_os = "nto" => { - // SAFETY: FFI call with no preconditions. - let id: libc::pid_t = unsafe { libc::gettid() }; - Some(id as u64) - } - target_os = "openbsd" => { - // SAFETY: FFI call with no preconditions. - let id: libc::pid_t = unsafe { libc::getthrid() }; - Some(id as u64) - } - target_os = "freebsd" => { - // SAFETY: FFI call with no preconditions. - let id: libc::c_int = unsafe { libc::pthread_getthreadid_np() }; - Some(id as u64) - } - target_os = "netbsd" => { - // SAFETY: FFI call with no preconditions. - let id: libc::lwpid_t = unsafe { libc::_lwp_self() }; - Some(id as u64) - } - any(target_os = "illumos", target_os = "solaris") => { - // On Illumos and Solaris, the `pthread_t` is the same as the OS thread ID. - // SAFETY: FFI call with no preconditions. - let id: libc::pthread_t = unsafe { libc::pthread_self() }; - Some(id as u64) - } - target_vendor = "apple" => { - // Apple allows querying arbitrary thread IDs, `thread=NULL` queries the current thread. - let mut id = 0u64; - // SAFETY: `thread_id` is a valid pointer, no other preconditions. - let status: libc::c_int = unsafe { libc::pthread_threadid_np(0, &mut id) }; - if status == 0 { - Some(id) - } else { - None - } - } - // Other platforms don't have an OS thread ID or don't have a way to access it. - _ => None, - } -} - -#[cfg(any( - target_os = "linux", - target_os = "nto", - target_os = "solaris", - target_os = "illumos", - target_os = "vxworks", - target_os = "cygwin", - target_vendor = "apple", -))] -fn truncate_cstr(cstr: &CStr) -> [libc::c_char; MAX_WITH_NUL] { - let mut result = [0; MAX_WITH_NUL]; - for (src, dst) in cstr.to_bytes().iter().zip(&mut result[..MAX_WITH_NUL - 1]) { - *dst = *src as libc::c_char; - } - result -} - pub fn available_parallelism() -> io::Result> { cfg_select! { any( @@ -668,6 +331,318 @@ pub fn available_parallelism() -> io::Result> { } } +pub fn current_os_id() -> Option { + // Most Unix platforms have a way to query an integer ID of the current thread, all with + // slightly different spellings. + // + // The OS thread ID is used rather than `pthread_self` so as to match what will be displayed + // for process inspection (debuggers, trace, `top`, etc.). + cfg_select! { + // Most platforms have a function returning a `pid_t` or int, which is an `i32`. + any(target_os = "android", target_os = "linux") => { + use crate::sys::pal::weak::syscall; + + // `libc::gettid` is only available on glibc 2.30+, but the syscall is available + // since Linux 2.4.11. + syscall!(fn gettid() -> libc::pid_t;); + + // SAFETY: FFI call with no preconditions. + let id: libc::pid_t = unsafe { gettid() }; + Some(id as u64) + } + target_os = "nto" => { + // SAFETY: FFI call with no preconditions. + let id: libc::pid_t = unsafe { libc::gettid() }; + Some(id as u64) + } + target_os = "openbsd" => { + // SAFETY: FFI call with no preconditions. + let id: libc::pid_t = unsafe { libc::getthrid() }; + Some(id as u64) + } + target_os = "freebsd" => { + // SAFETY: FFI call with no preconditions. + let id: libc::c_int = unsafe { libc::pthread_getthreadid_np() }; + Some(id as u64) + } + target_os = "netbsd" => { + // SAFETY: FFI call with no preconditions. + let id: libc::lwpid_t = unsafe { libc::_lwp_self() }; + Some(id as u64) + } + any(target_os = "illumos", target_os = "solaris") => { + // On Illumos and Solaris, the `pthread_t` is the same as the OS thread ID. + // SAFETY: FFI call with no preconditions. + let id: libc::pthread_t = unsafe { libc::pthread_self() }; + Some(id as u64) + } + target_vendor = "apple" => { + // Apple allows querying arbitrary thread IDs, `thread=NULL` queries the current thread. + let mut id = 0u64; + // SAFETY: `thread_id` is a valid pointer, no other preconditions. + let status: libc::c_int = unsafe { libc::pthread_threadid_np(0, &mut id) }; + if status == 0 { + Some(id) + } else { + None + } + } + // Other platforms don't have an OS thread ID or don't have a way to access it. + _ => None, + } +} + +#[cfg(any( + target_os = "linux", + target_os = "nto", + target_os = "solaris", + target_os = "illumos", + target_os = "vxworks", + target_os = "cygwin", + target_vendor = "apple", +))] +fn truncate_cstr(cstr: &CStr) -> [libc::c_char; MAX_WITH_NUL] { + let mut result = [0; MAX_WITH_NUL]; + for (src, dst) in cstr.to_bytes().iter().zip(&mut result[..MAX_WITH_NUL - 1]) { + *dst = *src as libc::c_char; + } + result +} + +#[cfg(target_os = "android")] +pub fn set_name(name: &CStr) { + const PR_SET_NAME: libc::c_int = 15; + unsafe { + let res = libc::prctl( + PR_SET_NAME, + name.as_ptr(), + 0 as libc::c_ulong, + 0 as libc::c_ulong, + 0 as libc::c_ulong, + ); + // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. + debug_assert_eq!(res, 0); + } +} + +#[cfg(any( + target_os = "linux", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "nuttx", + target_os = "cygwin" +))] +pub fn set_name(name: &CStr) { + unsafe { + cfg_select! { + any(target_os = "linux", target_os = "cygwin") => { + // Linux and Cygwin limits the allowed length of the name. + const TASK_COMM_LEN: usize = 16; + let name = truncate_cstr::<{ TASK_COMM_LEN }>(name); + } + _ => { + // FreeBSD, DragonFly BSD and NuttX do not enforce length limits. + } + }; + // Available since glibc 2.12, musl 1.1.16, and uClibc 1.0.20 for Linux, + // FreeBSD 12.2 and 13.0, and DragonFly BSD 6.0. + let res = libc::pthread_setname_np(libc::pthread_self(), name.as_ptr()); + // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. + debug_assert_eq!(res, 0); + } +} + +#[cfg(target_os = "openbsd")] +pub fn set_name(name: &CStr) { + unsafe { + libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr()); + } +} + +#[cfg(target_vendor = "apple")] +pub fn set_name(name: &CStr) { + unsafe { + let name = truncate_cstr::<{ libc::MAXTHREADNAMESIZE }>(name); + let res = libc::pthread_setname_np(name.as_ptr()); + // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. + debug_assert_eq!(res, 0); + } +} + +#[cfg(target_os = "netbsd")] +pub fn set_name(name: &CStr) { + unsafe { + let res = libc::pthread_setname_np( + libc::pthread_self(), + c"%s".as_ptr(), + name.as_ptr() as *mut libc::c_void, + ); + debug_assert_eq!(res, 0); + } +} + +#[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "nto"))] +pub fn set_name(name: &CStr) { + weak!( + fn pthread_setname_np(thread: libc::pthread_t, name: *const libc::c_char) -> libc::c_int; + ); + + if let Some(f) = pthread_setname_np.get() { + #[cfg(target_os = "nto")] + const THREAD_NAME_MAX: usize = libc::_NTO_THREAD_NAME_MAX as usize; + #[cfg(any(target_os = "solaris", target_os = "illumos"))] + const THREAD_NAME_MAX: usize = 32; + + let name = truncate_cstr::<{ THREAD_NAME_MAX }>(name); + let res = unsafe { f(libc::pthread_self(), name.as_ptr()) }; + debug_assert_eq!(res, 0); + } +} + +#[cfg(target_os = "fuchsia")] +pub fn set_name(name: &CStr) { + use crate::sys::pal::fuchsia::*; + unsafe { + zx_object_set_property( + zx_thread_self(), + ZX_PROP_NAME, + name.as_ptr() as *const libc::c_void, + name.to_bytes().len(), + ); + } +} + +#[cfg(target_os = "haiku")] +pub fn set_name(name: &CStr) { + unsafe { + let thread_self = libc::find_thread(ptr::null_mut()); + let res = libc::rename_thread(thread_self, name.as_ptr()); + // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. + debug_assert_eq!(res, libc::B_OK); + } +} + +#[cfg(target_os = "vxworks")] +pub fn set_name(name: &CStr) { + let mut name = truncate_cstr::<{ (libc::VX_TASK_RENAME_LENGTH - 1) as usize }>(name); + let res = unsafe { libc::taskNameSet(libc::taskIdSelf(), name.as_mut_ptr()) }; + debug_assert_eq!(res, libc::OK); +} + +#[cfg(not(target_os = "espidf"))] +pub fn sleep(dur: Duration) { + let mut secs = dur.as_secs(); + let mut nsecs = dur.subsec_nanos() as _; + + // If we're awoken with a signal then the return value will be -1 and + // nanosleep will fill in `ts` with the remaining time. + unsafe { + while secs > 0 || nsecs > 0 { + let mut ts = libc::timespec { + tv_sec: cmp::min(libc::time_t::MAX as u64, secs) as libc::time_t, + tv_nsec: nsecs, + }; + secs -= ts.tv_sec as u64; + let ts_ptr = &raw mut ts; + if libc::nanosleep(ts_ptr, ts_ptr) == -1 { + assert_eq!(os::errno(), libc::EINTR); + secs += ts.tv_sec as u64; + nsecs = ts.tv_nsec; + } else { + nsecs = 0; + } + } + } +} + +#[cfg(target_os = "espidf")] +pub fn sleep(dur: Duration) { + // ESP-IDF does not have `nanosleep`, so we use `usleep` instead. + // As per the documentation of `usleep`, it is expected to support + // sleep times as big as at least up to 1 second. + // + // ESP-IDF does support almost up to `u32::MAX`, but due to a potential integer overflow in its + // `usleep` implementation + // (https://github.com/espressif/esp-idf/blob/d7ca8b94c852052e3bc33292287ef4dd62c9eeb1/components/newlib/time.c#L210), + // we limit the sleep time to the maximum one that would not cause the underlying `usleep` implementation to overflow + // (`portTICK_PERIOD_MS` can be anything between 1 to 1000, and is 10 by default). + const MAX_MICROS: u32 = u32::MAX - 1_000_000 - 1; + + // Add any nanoseconds smaller than a microsecond as an extra microsecond + // so as to comply with the `std::thread::sleep` contract which mandates + // implementations to sleep for _at least_ the provided `dur`. + // We can't overflow `micros` as it is a `u128`, while `Duration` is a pair of + // (`u64` secs, `u32` nanos), where the nanos are strictly smaller than 1 second + // (i.e. < 1_000_000_000) + let mut micros = dur.as_micros() + if dur.subsec_nanos() % 1_000 > 0 { 1 } else { 0 }; + + while micros > 0 { + let st = if micros > MAX_MICROS as u128 { MAX_MICROS } else { micros as u32 }; + unsafe { + libc::usleep(st); + } + + micros -= st as u128; + } +} + +// Any unix that has clock_nanosleep +// If this list changes update the MIRI chock_nanosleep shim +#[cfg(any( + target_os = "freebsd", + target_os = "netbsd", + target_os = "linux", + target_os = "android", + target_os = "solaris", + target_os = "illumos", + target_os = "dragonfly", + target_os = "hurd", + target_os = "fuchsia", + target_os = "vxworks", +))] +pub fn sleep_until(deadline: crate::time::Instant) { + use crate::time::Instant; + + let Some(ts) = deadline.into_inner().into_timespec().to_timespec() else { + // The deadline is further in the future then can be passed to + // clock_nanosleep. We have to use Self::sleep instead. This might + // happen on 32 bit platforms, especially closer to 2038. + let now = Instant::now(); + if let Some(delay) = deadline.checked_duration_since(now) { + sleep(delay); + } + return; + }; + + unsafe { + // When we get interrupted (res = EINTR) call clock_nanosleep again + loop { + let res = libc::clock_nanosleep( + crate::sys::time::Instant::CLOCK_ID, + libc::TIMER_ABSTIME, + &ts, + core::ptr::null_mut(), // not required with TIMER_ABSTIME + ); + + if res == 0 { + break; + } else { + assert_eq!( + res, + libc::EINTR, + "timespec is in range, + clockid is valid and kernel should support it" + ); + } + } + } +} + +pub fn yield_now() { + let ret = unsafe { libc::sched_yield() }; + debug_assert_eq!(ret, 0); +} + #[cfg(any(target_os = "android", target_os = "linux"))] mod cgroups { //! Currently not covered diff --git a/library/std/src/sys/thread/unsupported.rs b/library/std/src/sys/thread/unsupported.rs index 34d9b5ec70c6..a5001efa3b40 100644 --- a/library/std/src/sys/thread/unsupported.rs +++ b/library/std/src/sys/thread/unsupported.rs @@ -1,8 +1,7 @@ -use super::unsupported; use crate::ffi::CStr; use crate::io; use crate::num::NonZero; -use crate::time::{Duration, Instant}; +use crate::time::Duration; pub struct Thread(!); @@ -15,23 +14,7 @@ impl Thread { _name: Option<&str>, _p: Box, ) -> io::Result { - unsupported() - } - - pub fn yield_now() { - // do nothing - } - - pub fn set_name(_name: &CStr) { - // nope - } - - pub fn sleep(_dur: Duration) { - panic!("can't sleep"); - } - - pub fn sleep_until(_deadline: Instant) { - panic!("can't sleep"); + Err(io::Error::UNSUPPORTED_PLATFORM) } pub fn join(self) { @@ -39,10 +22,22 @@ impl Thread { } } -pub(crate) fn current_os_id() -> Option { +pub fn available_parallelism() -> io::Result> { + Err(io::Error::UNKNOWN_THREAD_COUNT) +} + +pub fn current_os_id() -> Option { None } -pub fn available_parallelism() -> io::Result> { - unsupported() +pub fn yield_now() { + // do nothing +} + +pub fn set_name(_name: &CStr) { + // nope +} + +pub fn sleep(_dur: Duration) { + panic!("can't sleep"); } diff --git a/library/std/src/sys/thread/wasip1.rs b/library/std/src/sys/thread/wasip1.rs index e062b49bd7a2..83001fad49c8 100644 --- a/library/std/src/sys/thread/wasip1.rs +++ b/library/std/src/sys/thread/wasip1.rs @@ -1,214 +1,185 @@ #![forbid(unsafe_op_in_unsafe_fn)] -use crate::ffi::CStr; +#[cfg(target_feature = "atomics")] +use crate::io; +use crate::mem; +#[cfg(target_feature = "atomics")] use crate::num::NonZero; -use crate::time::{Duration, Instant}; -use crate::{io, mem}; +#[cfg(target_feature = "atomics")] +use crate::sys::os; +use crate::time::Duration; +#[cfg(target_feature = "atomics")] +use crate::{cmp, ptr}; -cfg_select! { - target_feature = "atomics" => { - use crate::cmp; - use crate::ptr; - use crate::sys::os; - // Add a few symbols not in upstream `libc` just yet. - mod libc { - pub use crate::ffi; - pub use libc::*; +// Add a few symbols not in upstream `libc` just yet. +#[cfg(target_feature = "atomics")] +mod libc { + pub use libc::*; - // defined in wasi-libc - // https://github.com/WebAssembly/wasi-libc/blob/a6f871343313220b76009827ed0153586361c0d5/libc-top-half/musl/include/alltypes.h.in#L108 - #[repr(C)] - union pthread_attr_union { - __i: [ffi::c_int; if size_of::() == 8 { 14 } else { 9 }], - __vi: [ffi::c_int; if size_of::() == 8 { 14 } else { 9 }], - __s: [ffi::c_ulong; if size_of::() == 8 { 7 } else { 9 }], - } + pub use crate::ffi; - #[repr(C)] - pub struct pthread_attr_t { - __u: pthread_attr_union, - } - - #[allow(non_camel_case_types)] - pub type pthread_t = *mut ffi::c_void; - - pub const _SC_NPROCESSORS_ONLN: ffi::c_int = 84; - - unsafe extern "C" { - pub fn pthread_create( - native: *mut pthread_t, - attr: *const pthread_attr_t, - f: extern "C" fn(*mut ffi::c_void) -> *mut ffi::c_void, - value: *mut ffi::c_void, - ) -> ffi::c_int; - pub fn pthread_join(native: pthread_t, value: *mut *mut ffi::c_void) -> ffi::c_int; - pub fn pthread_attr_init(attrp: *mut pthread_attr_t) -> ffi::c_int; - pub fn pthread_attr_setstacksize( - attr: *mut pthread_attr_t, - stack_size: libc::size_t, - ) -> ffi::c_int; - pub fn pthread_attr_destroy(attr: *mut pthread_attr_t) -> ffi::c_int; - pub fn pthread_detach(thread: pthread_t) -> ffi::c_int; - } - } - - pub struct Thread { - id: libc::pthread_t, - } - - impl Drop for Thread { - fn drop(&mut self) { - let ret = unsafe { libc::pthread_detach(self.id) }; - debug_assert_eq!(ret, 0); - } - } + // defined in wasi-libc + // https://github.com/WebAssembly/wasi-libc/blob/a6f871343313220b76009827ed0153586361c0d5/libc-top-half/musl/include/alltypes.h.in#L108 + #[repr(C)] + union pthread_attr_union { + __i: [ffi::c_int; if size_of::() == 8 { 14 } else { 9 }], + __vi: [ffi::c_int; if size_of::() == 8 { 14 } else { 9 }], + __s: [ffi::c_ulong; if size_of::() == 8 { 7 } else { 9 }], } - _ => { - pub struct Thread(!); + + #[repr(C)] + pub struct pthread_attr_t { + __u: pthread_attr_union, + } + + #[allow(non_camel_case_types)] + pub type pthread_t = *mut ffi::c_void; + + pub const _SC_NPROCESSORS_ONLN: ffi::c_int = 84; + + unsafe extern "C" { + pub fn pthread_create( + native: *mut pthread_t, + attr: *const pthread_attr_t, + f: extern "C" fn(*mut ffi::c_void) -> *mut ffi::c_void, + value: *mut ffi::c_void, + ) -> ffi::c_int; + pub fn pthread_join(native: pthread_t, value: *mut *mut ffi::c_void) -> ffi::c_int; + pub fn pthread_attr_init(attrp: *mut pthread_attr_t) -> ffi::c_int; + pub fn pthread_attr_setstacksize( + attr: *mut pthread_attr_t, + stack_size: libc::size_t, + ) -> ffi::c_int; + pub fn pthread_attr_destroy(attr: *mut pthread_attr_t) -> ffi::c_int; + pub fn pthread_detach(thread: pthread_t) -> ffi::c_int; + } +} + +#[cfg(target_feature = "atomics")] +pub struct Thread { + id: libc::pthread_t, +} + +#[cfg(target_feature = "atomics")] +impl Drop for Thread { + fn drop(&mut self) { + let ret = unsafe { libc::pthread_detach(self.id) }; + debug_assert_eq!(ret, 0); } } pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024; +#[cfg(target_feature = "atomics")] impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements - cfg_select! { - target_feature = "atomics" => { - pub unsafe fn new(stack: usize, _name: Option<&str>, p: Box) -> io::Result { - let p = Box::into_raw(Box::new(p)); - let mut native: libc::pthread_t = unsafe { mem::zeroed() }; - let mut attr: libc::pthread_attr_t = unsafe { mem::zeroed() }; - assert_eq!(unsafe { libc::pthread_attr_init(&mut attr) }, 0); + pub unsafe fn new( + stack: usize, + _name: Option<&str>, + p: Box, + ) -> io::Result { + let p = Box::into_raw(Box::new(p)); + let mut native: libc::pthread_t = unsafe { mem::zeroed() }; + let mut attr: libc::pthread_attr_t = unsafe { mem::zeroed() }; + assert_eq!(unsafe { libc::pthread_attr_init(&mut attr) }, 0); - let stack_size = cmp::max(stack, DEFAULT_MIN_STACK_SIZE); + let stack_size = cmp::max(stack, DEFAULT_MIN_STACK_SIZE); - match unsafe { libc::pthread_attr_setstacksize(&mut attr, stack_size) } { - 0 => {} - n => { - assert_eq!(n, libc::EINVAL); - // EINVAL means |stack_size| is either too small or not a - // multiple of the system page size. Because it's definitely - // >= PTHREAD_STACK_MIN, it must be an alignment issue. - // Round up to the nearest page and try again. - let page_size = os::page_size(); - let stack_size = - (stack_size + page_size - 1) & (-(page_size as isize - 1) as usize - 1); - assert_eq!(unsafe { libc::pthread_attr_setstacksize(&mut attr, stack_size) }, 0); - } - }; - - let ret = unsafe { libc::pthread_create(&mut native, &attr, thread_start, p as *mut _) }; - // Note: if the thread creation fails and this assert fails, then p will - // be leaked. However, an alternative design could cause double-free - // which is clearly worse. - assert_eq!(unsafe {libc::pthread_attr_destroy(&mut attr) }, 0); - - return if ret != 0 { - // The thread failed to start and as a result p was not consumed. Therefore, it is - // safe to reconstruct the box so that it gets deallocated. - unsafe { drop(Box::from_raw(p)); } - Err(io::Error::from_raw_os_error(ret)) - } else { - Ok(Thread { id: native }) - }; - - extern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void { - unsafe { - // Finally, let's run some code. - Box::from_raw(main as *mut Box)(); - } - ptr::null_mut() - } + match unsafe { libc::pthread_attr_setstacksize(&mut attr, stack_size) } { + 0 => {} + n => { + assert_eq!(n, libc::EINVAL); + // EINVAL means |stack_size| is either too small or not a + // multiple of the system page size. Because it's definitely + // >= PTHREAD_STACK_MIN, it must be an alignment issue. + // Round up to the nearest page and try again. + let page_size = os::page_size(); + let stack_size = + (stack_size + page_size - 1) & (-(page_size as isize - 1) as usize - 1); + assert_eq!(unsafe { libc::pthread_attr_setstacksize(&mut attr, stack_size) }, 0); } - } - _ => { - pub unsafe fn new(_stack: usize, _name: Option<&str>, _p: Box) -> io::Result { - crate::sys::unsupported() - } - } - } + }; - pub fn yield_now() { - let ret = unsafe { wasi::sched_yield() }; - debug_assert_eq!(ret, Ok(())); - } + let ret = unsafe { libc::pthread_create(&mut native, &attr, thread_start, p as *mut _) }; + // Note: if the thread creation fails and this assert fails, then p will + // be leaked. However, an alternative design could cause double-free + // which is clearly worse. + assert_eq!(unsafe { libc::pthread_attr_destroy(&mut attr) }, 0); - pub fn set_name(_name: &CStr) { - // nope - } - - pub fn sleep(dur: Duration) { - let mut nanos = dur.as_nanos(); - while nanos > 0 { - const USERDATA: wasi::Userdata = 0x0123_45678; - - let clock = wasi::SubscriptionClock { - id: wasi::CLOCKID_MONOTONIC, - timeout: u64::try_from(nanos).unwrap_or(u64::MAX), - precision: 0, - flags: 0, - }; - nanos -= u128::from(clock.timeout); - - let in_ = wasi::Subscription { - userdata: USERDATA, - u: wasi::SubscriptionU { tag: 0, u: wasi::SubscriptionUU { clock } }, - }; + return if ret != 0 { + // The thread failed to start and as a result p was not consumed. Therefore, it is + // safe to reconstruct the box so that it gets deallocated. unsafe { - let mut event: wasi::Event = mem::zeroed(); - let res = wasi::poll_oneoff(&in_, &mut event, 1); - match (res, event) { - ( - Ok(1), - wasi::Event { - userdata: USERDATA, - error: wasi::ERRNO_SUCCESS, - type_: wasi::EVENTTYPE_CLOCK, - .. - }, - ) => {} - _ => panic!("thread::sleep(): unexpected result of poll_oneoff"), - } + drop(Box::from_raw(p)); } - } - } + Err(io::Error::from_raw_os_error(ret)) + } else { + Ok(Thread { id: native }) + }; - pub fn sleep_until(deadline: Instant) { - let now = Instant::now(); - - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); + extern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void { + unsafe { + // Finally, let's run some code. + Box::from_raw(main as *mut Box)(); + } + ptr::null_mut() } } pub fn join(self) { - cfg_select! { - target_feature = "atomics" => { - let id = mem::ManuallyDrop::new(self).id; - let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; - if ret != 0 { - rtabort!("failed to join thread: {}", io::Error::from_raw_os_error(ret)); - } - } - _ => { - self.0 - } + let id = mem::ManuallyDrop::new(self).id; + let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; + if ret != 0 { + rtabort!("failed to join thread: {}", io::Error::from_raw_os_error(ret)); } } } -pub(crate) fn current_os_id() -> Option { - None -} - +#[cfg(target_feature = "atomics")] pub fn available_parallelism() -> io::Result> { - cfg_select! { - target_feature = "atomics" => { - match unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) } { - -1 => Err(io::Error::last_os_error()), - cpus => NonZero::new(cpus as usize).ok_or(io::Error::UNKNOWN_THREAD_COUNT), - } - } - _ => crate::sys::unsupported(), + match unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) } { + -1 => Err(io::Error::last_os_error()), + cpus => NonZero::new(cpus as usize).ok_or(io::Error::UNKNOWN_THREAD_COUNT), + } +} + +pub fn yield_now() { + let ret = unsafe { wasi::sched_yield() }; + debug_assert_eq!(ret, Ok(())); +} + +pub fn sleep(dur: Duration) { + let mut nanos = dur.as_nanos(); + while nanos > 0 { + const USERDATA: wasi::Userdata = 0x0123_45678; + + let clock = wasi::SubscriptionClock { + id: wasi::CLOCKID_MONOTONIC, + timeout: u64::try_from(nanos).unwrap_or(u64::MAX), + precision: 0, + flags: 0, + }; + nanos -= u128::from(clock.timeout); + + let in_ = wasi::Subscription { + userdata: USERDATA, + u: wasi::SubscriptionU { tag: 0, u: wasi::SubscriptionUU { clock } }, + }; + unsafe { + let mut event: wasi::Event = mem::zeroed(); + let res = wasi::poll_oneoff(&in_, &mut event, 1); + match (res, event) { + ( + Ok(1), + wasi::Event { + userdata: USERDATA, + error: wasi::ERRNO_SUCCESS, + type_: wasi::EVENTTYPE_CLOCK, + .. + }, + ) => {} + _ => panic!("thread::sleep(): unexpected result of poll_oneoff"), + } + } } } diff --git a/library/std/src/sys/thread/wasip2.rs b/library/std/src/sys/thread/wasip2.rs index ad52918f15a1..420cad2a5e4a 100644 --- a/library/std/src/sys/thread/wasip2.rs +++ b/library/std/src/sys/thread/wasip2.rs @@ -1,73 +1,32 @@ -use crate::ffi::CStr; -use crate::io; -use crate::num::NonZero; use crate::time::{Duration, Instant}; -pub struct Thread(!); - -pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024; - -impl Thread { - pub unsafe fn new( - _stack: usize, - _name: Option<&str>, - _p: Box, - ) -> io::Result { - // Note that unlike WASIp1 even if the wasm `atomics` feature is enabled - // there is no support for threads, not even experimentally, not even in - // wasi-libc. Thus this is unconditionally unsupported. - crate::sys::unsupported() +pub fn sleep(dur: Duration) { + // Sleep in increments of `u64::MAX` nanoseconds until the `dur` is + // entirely drained. + let mut remaining = dur.as_nanos(); + while remaining > 0 { + let amt = u64::try_from(remaining).unwrap_or(u64::MAX); + wasip2::clocks::monotonic_clock::subscribe_duration(amt).block(); + remaining -= u128::from(amt); } +} - pub fn yield_now() { - // no API for this in WASIp2, but there's also no threads, so that's - // sort of expected. - } - - pub fn set_name(_name: &CStr) { - // nope - } - - pub fn sleep(dur: Duration) { - // Sleep in increments of `u64::MAX` nanoseconds until the `dur` is - // entirely drained. - let mut remaining = dur.as_nanos(); - while remaining > 0 { - let amt = u64::try_from(remaining).unwrap_or(u64::MAX); - wasip2::clocks::monotonic_clock::subscribe_duration(amt).block(); - remaining -= u128::from(amt); +pub fn sleep_until(deadline: Instant) { + match u64::try_from(deadline.into_inner().as_duration().as_nanos()) { + // If the point in time we're sleeping to fits within a 64-bit + // number of nanoseconds then directly use `subscribe_instant`. + Ok(deadline) => { + wasip2::clocks::monotonic_clock::subscribe_instant(deadline).block(); } - } + // ... otherwise we're sleeping for 500+ years relative to the + // "start" of what the system is using as a clock so speed/accuracy + // is not so much of a concern. Use `sleep` instead. + Err(_) => { + let now = Instant::now(); - pub fn sleep_until(deadline: Instant) { - match u64::try_from(deadline.into_inner().as_duration().as_nanos()) { - // If the point in time we're sleeping to fits within a 64-bit - // number of nanoseconds then directly use `subscribe_instant`. - Ok(deadline) => { - wasip2::clocks::monotonic_clock::subscribe_instant(deadline).block(); - } - // ... otherwise we're sleeping for 500+ years relative to the - // "start" of what the system is using as a clock so speed/accuracy - // is not so much of a concern. Use `sleep` instead. - Err(_) => { - let now = Instant::now(); - - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); - } + if let Some(delay) = deadline.checked_duration_since(now) { + sleep(delay); } } } - - pub fn join(self) { - self.0 - } -} - -pub(crate) fn current_os_id() -> Option { - None -} - -pub fn available_parallelism() -> io::Result> { - crate::sys::unsupported() } diff --git a/library/std/src/sys/thread/wasm.rs b/library/std/src/sys/thread/wasm.rs index 42a7dbdf8b8b..e843bc992ba1 100644 --- a/library/std/src/sys/thread/wasm.rs +++ b/library/std/src/sys/thread/wasm.rs @@ -1,75 +1,23 @@ -use crate::ffi::CStr; -use crate::io; -use crate::num::NonZero; -use crate::sys::unsupported; -use crate::time::{Duration, Instant}; +use crate::cmp; +use crate::time::Duration; -pub struct Thread(!); +pub fn sleep(dur: Duration) { + #[cfg(target_arch = "wasm32")] + use core::arch::wasm32 as wasm; + #[cfg(target_arch = "wasm64")] + use core::arch::wasm64 as wasm; -pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024; - -impl Thread { - // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new( - _stack: usize, - _name: Option<&str>, - _p: Box, - ) -> io::Result { - unsupported() - } - - pub fn yield_now() {} - - pub fn set_name(_name: &CStr) {} - - pub fn sleep(dur: Duration) { - #[cfg(target_arch = "wasm32")] - use core::arch::wasm32 as wasm; - #[cfg(target_arch = "wasm64")] - use core::arch::wasm64 as wasm; - - use crate::cmp; - - // Use an atomic wait to block the current thread artificially with a - // timeout listed. Note that we should never be notified (return value - // of 0) or our comparison should never fail (return value of 1) so we - // should always only resume execution through a timeout (return value - // 2). - let mut nanos = dur.as_nanos(); - while nanos > 0 { - let amt = cmp::min(i64::MAX as u128, nanos); - let mut x = 0; - let val = unsafe { wasm::memory_atomic_wait32(&mut x, 0, amt as i64) }; - debug_assert_eq!(val, 2); - nanos -= amt; - } - } - - pub fn sleep_until(deadline: Instant) { - let now = Instant::now(); - - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); - } - } - - pub fn join(self) {} -} - -pub(crate) fn current_os_id() -> Option { - None -} - -pub fn available_parallelism() -> io::Result> { - unsupported() -} - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None + // Use an atomic wait to block the current thread artificially with a + // timeout listed. Note that we should never be notified (return value + // of 0) or our comparison should never fail (return value of 1) so we + // should always only resume execution through a timeout (return value + // 2). + let mut nanos = dur.as_nanos(); + while nanos > 0 { + let amt = cmp::min(i64::MAX as u128, nanos); + let mut x = 0; + let val = unsafe { wasm::memory_atomic_wait32(&mut x, 0, amt as i64) }; + debug_assert_eq!(val, 2); + nanos -= amt; } } diff --git a/library/std/src/sys/thread/windows.rs b/library/std/src/sys/thread/windows.rs index b0e38220a2d3..a5640c51c4a5 100644 --- a/library/std/src/sys/thread/windows.rs +++ b/library/std/src/sys/thread/windows.rs @@ -1,14 +1,14 @@ use core::ffi::c_void; -use super::time::WaitableTimer; -use super::to_u16s; use crate::ffi::CStr; use crate::num::NonZero; use crate::os::windows::io::{AsRawHandle, HandleOrNull}; use crate::sys::handle::Handle; +use crate::sys::pal::time::WaitableTimer; +use crate::sys::pal::{dur2timeout, to_u16s}; use crate::sys::{c, stack_overflow}; use crate::sys_common::FromInner; -use crate::time::{Duration, Instant}; +use crate::time::Duration; use crate::{io, ptr}; pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024; @@ -62,24 +62,6 @@ impl Thread { } } - pub fn set_name(name: &CStr) { - if let Ok(utf8) = name.to_str() { - if let Ok(utf16) = to_u16s(utf8) { - unsafe { - // SAFETY: the vec returned by `to_u16s` ends with a zero value - Self::set_name_wide(&utf16) - } - }; - }; - } - - /// # Safety - /// - /// `name` must end with a zero value - pub unsafe fn set_name_wide(name: &[u16]) { - unsafe { c::SetThreadDescription(c::GetCurrentThread(), name.as_ptr()) }; - } - pub fn join(self) { let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) }; if rc == c::WAIT_FAILED { @@ -87,37 +69,6 @@ impl Thread { } } - pub fn yield_now() { - // This function will return 0 if there are no other threads to execute, - // but this also means that the yield was useless so this isn't really a - // case that needs to be worried about. - unsafe { - c::SwitchToThread(); - } - } - - pub fn sleep(dur: Duration) { - fn high_precision_sleep(dur: Duration) -> Result<(), ()> { - let timer = WaitableTimer::high_resolution()?; - timer.set(dur)?; - timer.wait() - } - // Attempt to use high-precision sleep (Windows 10, version 1803+). - // On error fallback to the standard `Sleep` function. - // Also preserves the zero duration behavior of `Sleep`. - if dur.is_zero() || high_precision_sleep(dur).is_err() { - unsafe { c::Sleep(super::dur2timeout(dur)) } - } - } - - pub fn sleep_until(deadline: Instant) { - let now = Instant::now(); - - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); - } - } - pub fn handle(&self) -> &Handle { &self.handle } @@ -127,14 +78,6 @@ impl Thread { } } -pub(crate) fn current_os_id() -> Option { - // SAFETY: FFI call with no preconditions. - let id: u32 = unsafe { c::GetCurrentThreadId() }; - - // A return value of 0 indicates failed lookup. - if id == 0 { None } else { Some(id.into()) } -} - pub fn available_parallelism() -> io::Result> { let res = unsafe { let mut sysinfo: c::SYSTEM_INFO = crate::mem::zeroed(); @@ -146,3 +89,52 @@ pub fn available_parallelism() -> io::Result> { cpus => Ok(unsafe { NonZero::new_unchecked(cpus) }), } } + +pub fn current_os_id() -> Option { + // SAFETY: FFI call with no preconditions. + let id: u32 = unsafe { c::GetCurrentThreadId() }; + + // A return value of 0 indicates failed lookup. + if id == 0 { None } else { Some(id.into()) } +} + +pub fn set_name(name: &CStr) { + if let Ok(utf8) = name.to_str() { + if let Ok(utf16) = to_u16s(utf8) { + unsafe { + // SAFETY: the vec returned by `to_u16s` ends with a zero value + set_name_wide(&utf16) + } + }; + }; +} + +/// # Safety +/// +/// `name` must end with a zero value +pub unsafe fn set_name_wide(name: &[u16]) { + unsafe { c::SetThreadDescription(c::GetCurrentThread(), name.as_ptr()) }; +} + +pub fn sleep(dur: Duration) { + fn high_precision_sleep(dur: Duration) -> Result<(), ()> { + let timer = WaitableTimer::high_resolution()?; + timer.set(dur)?; + timer.wait() + } + // Attempt to use high-precision sleep (Windows 10, version 1803+). + // On error fallback to the standard `Sleep` function. + // Also preserves the zero duration behavior of `Sleep`. + if dur.is_zero() || high_precision_sleep(dur).is_err() { + unsafe { c::Sleep(dur2timeout(dur)) } + } +} + +pub fn yield_now() { + // This function will return 0 if there are no other threads to execute, + // but this also means that the yield was useless so this isn't really a + // case that needs to be worried about. + unsafe { + c::SwitchToThread(); + } +} diff --git a/library/std/src/sys/thread/xous.rs b/library/std/src/sys/thread/xous.rs index 92803c94c6e7..133e15a0928c 100644 --- a/library/std/src/sys/thread/xous.rs +++ b/library/std/src/sys/thread/xous.rs @@ -1,6 +1,5 @@ use core::arch::asm; -use crate::ffi::CStr; use crate::io; use crate::num::NonZero; use crate::os::xous::ffi::{ @@ -8,7 +7,7 @@ use crate::os::xous::ffi::{ map_memory, update_memory_flags, }; use crate::os::xous::services::{TicktimerScalar, ticktimer_server}; -use crate::time::{Duration, Instant}; +use crate::time::Duration; pub struct Thread { tid: ThreadId, @@ -110,46 +109,29 @@ impl Thread { Ok(Thread { tid }) } - pub fn yield_now() { - do_yield(); - } - - pub fn set_name(_name: &CStr) { - // nope - } - - pub fn sleep(dur: Duration) { - // Because the sleep server works on units of `usized milliseconds`, split - // the messages up into these chunks. This means we may run into issues - // if you try to sleep a thread for more than 49 days on a 32-bit system. - let mut millis = dur.as_millis(); - while millis > 0 { - let sleep_duration = - if millis > (usize::MAX as _) { usize::MAX } else { millis as usize }; - blocking_scalar(ticktimer_server(), TicktimerScalar::SleepMs(sleep_duration).into()) - .expect("failed to send message to ticktimer server"); - millis -= sleep_duration as u128; - } - } - - pub fn sleep_until(deadline: Instant) { - let now = Instant::now(); - - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); - } - } - pub fn join(self) { join_thread(self.tid).unwrap(); } } -pub(crate) fn current_os_id() -> Option { - None -} - pub fn available_parallelism() -> io::Result> { // We're unicore right now. Ok(unsafe { NonZero::new_unchecked(1) }) } + +pub fn yield_now() { + do_yield(); +} + +pub fn sleep(dur: Duration) { + // Because the sleep server works on units of `usized milliseconds`, split + // the messages up into these chunks. This means we may run into issues + // if you try to sleep a thread for more than 49 days on a 32-bit system. + let mut millis = dur.as_millis(); + while millis > 0 { + let sleep_duration = if millis > (usize::MAX as _) { usize::MAX } else { millis as usize }; + blocking_scalar(ticktimer_server(), TicktimerScalar::SleepMs(sleep_duration).into()) + .expect("failed to send message to ticktimer server"); + millis -= sleep_duration as u128; + } +} diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index b6059c28cec7..97b506c1b8fe 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -550,7 +550,7 @@ impl Builder { } if let Some(name) = their_thread.cname() { - imp::Thread::set_name(name); + imp::set_name(name); } let f = f.into_inner(); @@ -763,7 +763,7 @@ where /// [`Mutex`]: crate::sync::Mutex #[stable(feature = "rust1", since = "1.0.0")] pub fn yield_now() { - imp::Thread::yield_now() + imp::yield_now() } /// Determines whether the current thread is unwinding because of panic. @@ -884,7 +884,7 @@ pub fn sleep_ms(ms: u32) { /// ``` #[stable(feature = "thread_sleep", since = "1.4.0")] pub fn sleep(dur: Duration) { - imp::Thread::sleep(dur) + imp::sleep(dur) } /// Puts the current thread to sleep until the specified deadline has passed. @@ -983,7 +983,7 @@ pub fn sleep(dur: Duration) { /// ``` #[unstable(feature = "thread_sleep_until", issue = "113752")] pub fn sleep_until(deadline: Instant) { - imp::Thread::sleep_until(deadline) + imp::sleep_until(deadline) } /// Used to ensure that `park` and `park_timeout` do not unwind, as that can From f049b6147c15496d70b69490dbcaef4f824f1cdf Mon Sep 17 00:00:00 2001 From: joboet Date: Sat, 9 Aug 2025 23:40:24 +0200 Subject: [PATCH 341/682] bless miri tests --- .../fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr | 4 ++-- .../libc_pthread_rwlock_write_read_deadlock.stderr | 4 ++-- .../libc_pthread_rwlock_write_write_deadlock.stderr | 4 ++-- .../tests/fail-dep/concurrency/windows_join_detached.stderr | 4 ++-- .../miri/tests/fail-dep/concurrency/windows_join_main.stderr | 4 ++-- .../miri/tests/fail-dep/concurrency/windows_join_self.stderr | 4 ++-- .../miri/tests/fail-dep/libc/eventfd_block_read_twice.stderr | 4 ++-- .../miri/tests/fail-dep/libc/eventfd_block_write_twice.stderr | 4 ++-- .../tests/fail-dep/libc/libc_epoll_block_two_thread.stderr | 4 ++-- .../tests/fail-dep/libc/socketpair-close-while-blocked.stderr | 4 ++-- .../tests/fail-dep/libc/socketpair_block_read_twice.stderr | 4 ++-- .../tests/fail-dep/libc/socketpair_block_write_twice.stderr | 4 ++-- 12 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr index bcec5557a3fa..c7c1a769cdaa 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr @@ -8,13 +8,13 @@ LL | assert_eq!(libc::pthread_mutex_lock(lock_copy.0.get() as *mut _ = note: inside closure at tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.rs:LL:CC error: the evaluated program deadlocked - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | ^ this thread got stuck here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr index 51533cd17a8f..ae8b2b936a71 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr @@ -8,13 +8,13 @@ LL | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mu = note: inside closure at tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.rs:LL:CC error: the evaluated program deadlocked - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | ^ this thread got stuck here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr index 63fc7ce346e0..dfa9a6e25831 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr @@ -8,13 +8,13 @@ LL | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mu = note: inside closure at tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.rs:LL:CC error: the evaluated program deadlocked - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | ^ this thread got stuck here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_detached.stderr b/src/tools/miri/tests/fail-dep/concurrency/windows_join_detached.stderr index d8a3e058da93..47a0ebdcfef8 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_detached.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_detached.stderr @@ -1,5 +1,5 @@ error: Undefined Behavior: trying to join a detached thread - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here @@ -7,7 +7,7 @@ LL | let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle( = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr index 079f01c0e549..3b1181c92fd9 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr @@ -9,13 +9,13 @@ LL | assert_eq!(WaitForSingleObject(MAIN_THREAD, INFINITE), WAIT_OBJ = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error: the evaluated program deadlocked - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) }; | ^ this thread got stuck here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr b/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr index 70de94f56bac..6eefa2da1d81 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr @@ -8,13 +8,13 @@ LL | assert_eq!(WaitForSingleObject(native, INFINITE), WAIT_OBJECT_0 = note: inside closure at tests/fail-dep/concurrency/windows_join_self.rs:LL:CC error: the evaluated program deadlocked - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) }; | ^ this thread got stuck here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.stderr b/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.stderr index 4f3a56fef826..6dd6c36ab656 100644 --- a/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.stderr +++ b/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.stderr @@ -1,11 +1,11 @@ error: the evaluated program deadlocked - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | ^ this thread got stuck here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.stderr b/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.stderr index 5045badaa870..3154197b95ed 100644 --- a/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.stderr +++ b/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.stderr @@ -1,11 +1,11 @@ error: the evaluated program deadlocked - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | ^ this thread got stuck here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr index 3713c8da392b..1af44c107ff8 100644 --- a/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr +++ b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr @@ -5,13 +5,13 @@ error: the evaluated program deadlocked = note: BACKTRACE on thread `unnamed-ID`: error: the evaluated program deadlocked - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | ^ this thread got stuck here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair-close-while-blocked.stderr b/src/tools/miri/tests/fail-dep/libc/socketpair-close-while-blocked.stderr index 47fc889b0010..b85470225c69 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair-close-while-blocked.stderr +++ b/src/tools/miri/tests/fail-dep/libc/socketpair-close-while-blocked.stderr @@ -1,11 +1,11 @@ error: the evaluated program deadlocked - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | ^ this thread got stuck here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr b/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr index 99d242ec7daf..bbd2ab1c4d8d 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr @@ -1,11 +1,11 @@ error: the evaluated program deadlocked - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | ^ this thread got stuck here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr b/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr index f766500d331d..c3cc20681731 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr @@ -1,11 +1,11 @@ error: the evaluated program deadlocked - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | ^ this thread got stuck here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` From 4c992199598b4fddc25d52f40fa3642c9e065e60 Mon Sep 17 00:00:00 2001 From: joboet Date: Tue, 9 Sep 2025 13:17:45 +0200 Subject: [PATCH 342/682] std: only test `dlsym!` on platforms where it is actually used `dlsym` doesn't work for finding libc symbols on platforms like linux-musl, so the test will fail. --- library/std/src/sys/pal/unix/weak.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/library/std/src/sys/pal/unix/weak.rs b/library/std/src/sys/pal/unix/weak.rs index ad649fdbab6c..a3b980a3f3d8 100644 --- a/library/std/src/sys/pal/unix/weak.rs +++ b/library/std/src/sys/pal/unix/weak.rs @@ -27,6 +27,16 @@ use crate::marker::{FnPtr, PhantomData}; use crate::sync::atomic::{Atomic, AtomicPtr, Ordering}; use crate::{mem, ptr}; +// We currently only test `dlsym!`, but that doesn't work on all platforms, so +// we gate the tests to only the platforms where it is actually used. +// +// FIXME(joboet): add more tests, reorganise the whole module and get rid of +// `#[allow(dead_code, unused_macros)]`. +#[cfg(any( + target_vendor = "apple", + all(target_os = "linux", target_env = "gnu"), + target_os = "freebsd", +))] #[cfg(test)] mod tests; From 8ecda4b7f866d77d4a87c5b5bc601d4078841093 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 10 Sep 2025 12:05:53 +0200 Subject: [PATCH 343/682] interpret: fix overlapping aggregate initialization --- .../rustc_const_eval/src/interpret/place.rs | 2 +- .../rustc_const_eval/src/interpret/step.rs | 16 +++++++++++++--- .../fail/both_borrows/issue-miri-1050-1.rs | 2 ++ .../fail/both_borrows/issue-miri-1050-2.rs | 2 ++ .../return_invalid_shr.stack.stderr | 2 +- .../return_invalid_shr.tree.stderr | 2 +- .../return_invalid_shr_option.stack.stderr | 2 +- .../return_invalid_shr_option.tree.stderr | 2 +- .../return_invalid_shr_tuple.stack.stderr | 2 +- .../return_invalid_shr_tuple.tree.stderr | 2 +- .../fail/overlapping_assignment_aggregate.rs | 18 ++++++++++++++++++ .../overlapping_assignment_aggregate.stderr | 15 +++++++++++++++ .../miri/tests/fail/validity/nonzero.stderr | 2 +- ...overlapping_assignment_aggregate_scalar.rs | 19 +++++++++++++++++++ 14 files changed, 77 insertions(+), 11 deletions(-) create mode 100644 src/tools/miri/tests/fail/overlapping_assignment_aggregate.rs create mode 100644 src/tools/miri/tests/fail/overlapping_assignment_aggregate.stderr create mode 100644 src/tools/miri/tests/pass/overlapping_assignment_aggregate_scalar.rs diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index a86fdf80f601..cd34892f029c 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -858,7 +858,7 @@ where /// Also, if you use this you are responsible for validating that things get copied at the /// right type. #[instrument(skip(self), level = "trace")] - fn copy_op_no_validate( + pub(super) fn copy_op_no_validate( &mut self, src: &impl Projectable<'tcx, M::Provenance>, dest: &impl Writeable<'tcx, M::Provenance>, diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 23d362de308f..46950d60f8c7 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -310,7 +310,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { operands: &IndexSlice>, dest: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx> { - self.write_uninit(dest)?; // make sure all the padding ends up as uninit let (variant_index, variant_dest, active_field_index) = match *kind { mir::AggregateKind::Adt(_, variant_index, _, _, active_field_index) => { let variant_dest = self.project_downcast(dest, variant_index)?; @@ -346,9 +345,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let field_index = active_field_index.unwrap_or(field_index); let field_dest = self.project_field(&variant_dest, field_index)?; let op = self.eval_operand(operand, Some(field_dest.layout))?; - self.copy_op(&op, &field_dest)?; + // We validate manually below so we don't have to do it here. + self.copy_op_no_validate(&op, &field_dest, /*allow_transmute*/ false)?; } - self.write_discriminant(variant_index, dest) + self.write_discriminant(variant_index, dest)?; + // Validate that the entire thing is valid, and reset padding that might be in between the + // fields. + if M::enforce_validity(self, dest.layout()) { + self.validate_operand( + dest, + M::enforce_validity_recursively(self, dest.layout()), + /*reset_provenance_and_padding*/ true, + )?; + } + interp_ok(()) } /// Repeats `operand` into the destination. `dest` must have array type, and that type diff --git a/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.rs b/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.rs index dd7dae9cecf9..d907c5de7970 100644 --- a/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.rs +++ b/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.rs @@ -1,4 +1,6 @@ //@revisions: stack tree +// Ensure this even hits the aliasing model +//@compile-flags: -Zmiri-disable-validation //@[tree]compile-flags: -Zmiri-tree-borrows //@error-in-other-file: pointer not dereferenceable diff --git a/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-2.rs b/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-2.rs index 5c947e641425..b54f27bb8b23 100644 --- a/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-2.rs +++ b/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-2.rs @@ -1,4 +1,6 @@ //@revisions: stack tree +// Ensure this even hits the aliasing model +//@compile-flags: -Zmiri-disable-validation //@[tree]compile-flags: -Zmiri-tree-borrows //@error-in-other-file: is a dangling pointer use std::ptr::NonNull; diff --git a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr.stack.stderr b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr.stack.stderr index d52143500c46..7c4fe7487012 100644 --- a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr.stack.stderr +++ b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr.stack.stderr @@ -11,7 +11,7 @@ help: was created by a SharedReadOnly retag at offsets [0x4..0x8] | LL | let ret = unsafe { &(*xraw).1 }; | ^^^^^^^^^^ -help: was later invalidated at offsets [0x0..0x8] by a write access +help: was later invalidated at offsets [0x4..0x8] by a write access --> tests/fail/both_borrows/return_invalid_shr.rs:LL:CC | LL | unsafe { *xraw = (42, 23) }; // unfreeze diff --git a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr.tree.stderr b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr.tree.stderr index ee0f313d980d..a8e3553aae2c 100644 --- a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr.tree.stderr +++ b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr.tree.stderr @@ -12,7 +12,7 @@ help: the accessed tag was created here, in the initial state Frozen | LL | let ret = unsafe { &(*xraw).1 }; | ^^^^^^^^^^ -help: the accessed tag later transitioned to Disabled due to a foreign write access at offsets [0x0..0x8] +help: the accessed tag later transitioned to Disabled due to a foreign write access at offsets [0x4..0x8] --> tests/fail/both_borrows/return_invalid_shr.rs:LL:CC | LL | unsafe { *xraw = (42, 23) }; // unfreeze diff --git a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_option.stack.stderr b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_option.stack.stderr index d66c8eeb1af6..8411437ea4c9 100644 --- a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_option.stack.stderr +++ b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_option.stack.stderr @@ -14,7 +14,7 @@ help: was created by a SharedReadOnly retag at offsets [0x4..0x8] | LL | let ret = Some(unsafe { &(*xraw).1 }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: was later invalidated at offsets [0x0..0x8] by a write access +help: was later invalidated at offsets [0x4..0x8] by a write access --> tests/fail/both_borrows/return_invalid_shr_option.rs:LL:CC | LL | unsafe { *xraw = (42, 23) }; // unfreeze diff --git a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_option.tree.stderr b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_option.tree.stderr index 16110e5b062c..39da45ad6db4 100644 --- a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_option.tree.stderr +++ b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_option.tree.stderr @@ -12,7 +12,7 @@ help: the accessed tag was created here, in the initial state Frozen | LL | let ret = Some(unsafe { &(*xraw).1 }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: the accessed tag later transitioned to Disabled due to a foreign write access at offsets [0x0..0x8] +help: the accessed tag later transitioned to Disabled due to a foreign write access at offsets [0x4..0x8] --> tests/fail/both_borrows/return_invalid_shr_option.rs:LL:CC | LL | unsafe { *xraw = (42, 23) }; // unfreeze diff --git a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_tuple.stack.stderr b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_tuple.stack.stderr index 727b52ec7296..a7c422aa73f2 100644 --- a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_tuple.stack.stderr +++ b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_tuple.stack.stderr @@ -14,7 +14,7 @@ help: was created by a SharedReadOnly retag at offsets [0x4..0x8] | LL | let ret = (unsafe { &(*xraw).1 },); | ^^^^^^^^^^^^^^^^^^^^^^^^ -help: was later invalidated at offsets [0x0..0x8] by a write access +help: was later invalidated at offsets [0x4..0x8] by a write access --> tests/fail/both_borrows/return_invalid_shr_tuple.rs:LL:CC | LL | unsafe { *xraw = (42, 23) }; // unfreeze diff --git a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_tuple.tree.stderr b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_tuple.tree.stderr index f93698f570ee..66b03e57905e 100644 --- a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_tuple.tree.stderr +++ b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_tuple.tree.stderr @@ -12,7 +12,7 @@ help: the accessed tag was created here, in the initial state Frozen | LL | let ret = (unsafe { &(*xraw).1 },); | ^^^^^^^^^^^^^^^^^^^^^^^^ -help: the accessed tag later transitioned to Disabled due to a foreign write access at offsets [0x0..0x8] +help: the accessed tag later transitioned to Disabled due to a foreign write access at offsets [0x4..0x8] --> tests/fail/both_borrows/return_invalid_shr_tuple.rs:LL:CC | LL | unsafe { *xraw = (42, 23) }; // unfreeze diff --git a/src/tools/miri/tests/fail/overlapping_assignment_aggregate.rs b/src/tools/miri/tests/fail/overlapping_assignment_aggregate.rs new file mode 100644 index 000000000000..8d7b1946242c --- /dev/null +++ b/src/tools/miri/tests/fail/overlapping_assignment_aggregate.rs @@ -0,0 +1,18 @@ +//! This is like `pass/overlapping_assignment_aggregate_scalar.rs` but with a non-scalar +//! type, and that makes it definite UB. +#![feature(custom_mir, core_intrinsics)] +#![allow(internal_features)] + +use std::intrinsics::mir::*; + +#[custom_mir(dialect = "runtime")] +fn main() { + mir! { + let _1: ([u8; 1],); + { + _1.0 = [0_u8; 1]; + _1 = (_1.0, ); //~ERROR: overlapping ranges + Return() + } + } +} diff --git a/src/tools/miri/tests/fail/overlapping_assignment_aggregate.stderr b/src/tools/miri/tests/fail/overlapping_assignment_aggregate.stderr new file mode 100644 index 000000000000..f2a6d326b718 --- /dev/null +++ b/src/tools/miri/tests/fail/overlapping_assignment_aggregate.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: `copy_nonoverlapping` called on overlapping ranges + --> tests/fail/overlapping_assignment_aggregate.rs:LL:CC + | +LL | _1 = (_1.0, ); + | ^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at tests/fail/overlapping_assignment_aggregate.rs:LL:CC + +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/validity/nonzero.stderr b/src/tools/miri/tests/fail/validity/nonzero.stderr index 0c3a35d6b9fa..7be3ef46639c 100644 --- a/src/tools/miri/tests/fail/validity/nonzero.stderr +++ b/src/tools/miri/tests/fail/validity/nonzero.stderr @@ -2,7 +2,7 @@ error: Undefined Behavior: constructing invalid value: encountered 0, but expect --> tests/fail/validity/nonzero.rs:LL:CC | LL | let _x = Some(unsafe { NonZero(0) }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | ^^^^^^^^^^ Undefined Behavior occurred here | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/pass/overlapping_assignment_aggregate_scalar.rs b/src/tools/miri/tests/pass/overlapping_assignment_aggregate_scalar.rs new file mode 100644 index 000000000000..0cb2f7642424 --- /dev/null +++ b/src/tools/miri/tests/pass/overlapping_assignment_aggregate_scalar.rs @@ -0,0 +1,19 @@ +#![feature(custom_mir, core_intrinsics)] +#![allow(internal_features)] + +use std::intrinsics::mir::*; + +#[custom_mir(dialect = "runtime")] +fn main() { + mir! { + let _1: (u8,); + { + _1.0 = 0_u8; + // This is a scalar type, so overlap is (for now) not UB. + // However, we used to treat such overlapping assignments incorrectly + // (see ). + _1 = (_1.0, ); + Return() + } + } +} From 72225060edefb6ae4014c671e9953f9822ddff7a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 10 Sep 2025 14:27:43 +0200 Subject: [PATCH 344/682] clarify current MIR semantics re: overlapping assignment and double-check that we match it in codegen --- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 11 ++++++++++- compiler/rustc_middle/src/mir/syntax.rs | 8 +++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index f6f2e3f2a3a3..f8755874014d 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -1,5 +1,5 @@ use itertools::Itertools as _; -use rustc_abi::{self as abi, FIRST_VARIANT}; +use rustc_abi::{self as abi, BackendRepr, FIRST_VARIANT}; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; @@ -25,6 +25,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { match *rvalue { mir::Rvalue::Use(ref operand) => { let cg_operand = self.codegen_operand(bx, operand); + // Crucially, we do *not* use `OperandValue::Ref` for types with + // `BackendRepr::Scalar | BackendRepr::ScalarPair`. This ensures we match the MIR + // semantics regarding when assignment operators allow overlap of LHS and RHS. + if matches!( + cg_operand.layout.backend_repr, + BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..), + ) { + debug_assert!(!matches!(cg_operand.val, OperandValue::Ref(..))); + } // FIXME: consider not copying constants through stack. (Fixable by codegen'ing // constants into `OperandValue::Ref`; why don’t we do that yet if we don’t?) cg_operand.val.store(bx, dest); diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 3b8def67f92d..d402ea4b04f9 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -327,9 +327,11 @@ pub enum StatementKind<'tcx> { /// interesting for optimizations? Do we want to allow such optimizations? /// /// **Needs clarification**: We currently require that the LHS place not overlap with any place - /// read as part of computation of the RHS for some rvalues (generally those not producing - /// primitives). This requirement is under discussion in [#68364]. As a part of this discussion, - /// it is also unclear in what order the components are evaluated. + /// read as part of computation of the RHS for some rvalues. This requirement is under + /// discussion in [#68364]. Specifically, overlap is permitted only for assignments of a type + /// with `BackendRepr::Scalar | BackendRepr::ScalarPair` where all the scalar fields are + /// [`Scalar::Initialized`][rustc_abi::Scalar::Initialized]. As a part of this discussion, it is + /// also unclear in what order the components are evaluated. /// /// [#68364]: https://github.com/rust-lang/rust/issues/68364 /// From edbc0a08fb8bafbb80b4522ae0050244bb21e4f5 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 10 Sep 2025 16:09:28 +0200 Subject: [PATCH 345/682] weak memory tests: add more info on where they come from --- .../tests/pass/0weak_memory_consistency.rs | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/tools/miri/tests/pass/0weak_memory_consistency.rs b/src/tools/miri/tests/pass/0weak_memory_consistency.rs index e4ed9675de82..16969d9649c9 100644 --- a/src/tools/miri/tests/pass/0weak_memory_consistency.rs +++ b/src/tools/miri/tests/pass/0weak_memory_consistency.rs @@ -14,13 +14,6 @@ // To mitigate this, each test is ran enough times such that the chance // of spurious success is very low. These tests never spuriously fail. -// Test cases and their consistent outcomes are from -// http://svr-pes20-cppmem.cl.cam.ac.uk/cppmem/ -// Based on -// M. Batty, S. Owens, S. Sarkar, P. Sewell and T. Weber, -// "Mathematizing C++ concurrency", ACM SIGPLAN Notices, vol. 46, no. 1, pp. 55-66, 2011. -// Available: https://ss265.host.cs.st-andrews.ac.uk/papers/n3132.pdf. - use std::sync::atomic::Ordering::*; use std::sync::atomic::{AtomicBool, AtomicI32, Ordering, fence}; use std::thread::spawn; @@ -56,6 +49,7 @@ fn spin_until_bool(loc: &AtomicBool, ord: Ordering, val: bool) -> bool { val } +/// Test matching https://www.doc.ic.ac.uk/~afd/homepages/papers/pdfs/2017/POPL.pdf, Figure 7 fn test_corr() { let x = static_atomic(0); let y = static_atomic(0); @@ -75,19 +69,25 @@ fn test_corr() { let j3 = spawn(move || { // | | spin_until_i32(&y, Acquire, 1); // <---------+ | x.load(Relaxed) // <----------------------------------------------+ - // The two reads on x are ordered by hb, so they cannot observe values - // differently from the modification order. If the first read observed - // 2, then the second read must observe 2 as well. }); j1.join().unwrap(); let r2 = j2.join().unwrap(); let r3 = j3.join().unwrap(); + // The two reads on x are ordered by hb, so they cannot observe values + // differently from the modification order. If the first read observed + // 2, then the second read must observe 2 as well. if r2 == 2 { assert_eq!(r3, 2); } } +/// This test case is from: +/// http://svr-pes20-cppmem.cl.cam.ac.uk/cppmem/, "WRC" +/// Based on +/// M. Batty, S. Owens, S. Sarkar, P. Sewell and T. Weber, +/// "Mathematizing C++ concurrency", ACM SIGPLAN Notices, vol. 46, no. 1, pp. 55-66, 2011. +/// Available: https://www.cl.cam.ac.uk/~pes20/cpp/popl085ap-sewell.pdf. fn test_wrc() { let x = static_atomic(0); let y = static_atomic(0); @@ -114,6 +114,8 @@ fn test_wrc() { assert_eq!(r3, 1); } +/// Another test from http://svr-pes20-cppmem.cl.cam.ac.uk/cppmem/: +/// MP (na_rel+acq_na) fn test_message_passing() { let mut var = 0u32; let ptr = &mut var as *mut u32; @@ -139,7 +141,8 @@ fn test_message_passing() { assert_eq!(r2, 1); } -// LB+acq_rel+acq_rel +/// Another test from http://svr-pes20-cppmem.cl.cam.ac.uk/cppmem/: +/// LB+acq_rel+acq_rel fn test_load_buffering_acq_rel() { let x = static_atomic(0); let y = static_atomic(0); From 0e0b254df9b6f7abe4aa0efa8617ac97baa2bea7 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 10 Sep 2025 16:16:49 +0200 Subject: [PATCH 346/682] ensure we do not see the inconsistent execution from Figure 8 --- src/tools/miri/tests/pass/0weak_memory_consistency.rs | 3 ++- src/tools/miri/tests/pass/weak_memory/weak.rs | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/tests/pass/0weak_memory_consistency.rs b/src/tools/miri/tests/pass/0weak_memory_consistency.rs index 16969d9649c9..142080c1372d 100644 --- a/src/tools/miri/tests/pass/0weak_memory_consistency.rs +++ b/src/tools/miri/tests/pass/0weak_memory_consistency.rs @@ -49,7 +49,8 @@ fn spin_until_bool(loc: &AtomicBool, ord: Ordering, val: bool) -> bool { val } -/// Test matching https://www.doc.ic.ac.uk/~afd/homepages/papers/pdfs/2017/POPL.pdf, Figure 7 +/// Test matching https://www.doc.ic.ac.uk/~afd/homepages/papers/pdfs/2017/POPL.pdf, Figure 7. +/// (The Figure 8 test is in `weak_memory/weak.rs`.) fn test_corr() { let x = static_atomic(0); let y = static_atomic(0); diff --git a/src/tools/miri/tests/pass/weak_memory/weak.rs b/src/tools/miri/tests/pass/weak_memory/weak.rs index 199f83f05286..6794b43bfb37 100644 --- a/src/tools/miri/tests/pass/weak_memory/weak.rs +++ b/src/tools/miri/tests/pass/weak_memory/weak.rs @@ -69,6 +69,9 @@ fn seq_cst() -> bool { j2.join().unwrap(); let r3 = j3.join().unwrap(); + // Even though we force t3 to run last, it can still see the value 1. + // And it can *never* see the value 2! + assert!(r3 == 1 || r3 == 3); r3 == 1 } @@ -148,4 +151,9 @@ pub fn main() { assert_once(|| initialization_write(false)); assert_once(|| initialization_write(true)); assert_once(faa_replaced_by_load); + + // Run seq_cst a few more times since it has an assertion we want to ensure holds always. + for _ in 0..50 { + seq_cst(); + } } From 21e3111ef90f670b85eebf4a1ed9cc8be2f21061 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 10 Sep 2025 16:31:29 +0200 Subject: [PATCH 347/682] refactor weak-mem test to list all expected executions --- .../tests/pass/0weak_memory_consistency.rs | 3 +- .../tests/pass/0weak_memory_consistency_sc.rs | 3 +- src/tools/miri/tests/pass/weak_memory/weak.rs | 233 +++++++++--------- 3 files changed, 125 insertions(+), 114 deletions(-) diff --git a/src/tools/miri/tests/pass/0weak_memory_consistency.rs b/src/tools/miri/tests/pass/0weak_memory_consistency.rs index 142080c1372d..f1d5a56c2b02 100644 --- a/src/tools/miri/tests/pass/0weak_memory_consistency.rs +++ b/src/tools/miri/tests/pass/0weak_memory_consistency.rs @@ -1,6 +1,7 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-disable-stacked-borrows -Zmiri-disable-validation -Zmiri-provenance-gc=10000 +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-disable-stacked-borrows -Zmiri-disable-validation // This test's runtime explodes if the GC interval is set to 1 (which we do in CI), so we // override it internally back to the default frequency. +//@compile-flags: -Zmiri-provenance-gc=10000 // The following tests check whether our weak memory emulation produces // any inconsistent execution outcomes diff --git a/src/tools/miri/tests/pass/0weak_memory_consistency_sc.rs b/src/tools/miri/tests/pass/0weak_memory_consistency_sc.rs index 937c2a8cf282..43675a8b5e16 100644 --- a/src/tools/miri/tests/pass/0weak_memory_consistency_sc.rs +++ b/src/tools/miri/tests/pass/0weak_memory_consistency_sc.rs @@ -1,6 +1,7 @@ -//@compile-flags: -Zmiri-ignore-leaks -Zmiri-disable-stacked-borrows -Zmiri-disable-validation -Zmiri-provenance-gc=10000 +//@compile-flags: -Zmiri-ignore-leaks -Zmiri-disable-stacked-borrows -Zmiri-disable-validation // This test's runtime explodes if the GC interval is set to 1 (which we do in CI), so we // override it internally back to the default frequency. +//@compile-flags: -Zmiri-provenance-gc=10000 // The following tests check whether our weak memory emulation produces // any inconsistent execution outcomes diff --git a/src/tools/miri/tests/pass/weak_memory/weak.rs b/src/tools/miri/tests/pass/weak_memory/weak.rs index 6794b43bfb37..f39ef0ed98da 100644 --- a/src/tools/miri/tests/pass/weak_memory/weak.rs +++ b/src/tools/miri/tests/pass/weak_memory/weak.rs @@ -1,9 +1,11 @@ //@compile-flags: -Zmiri-ignore-leaks -Zmiri-fixed-schedule +// This test's runtime explodes if the GC interval is set to 1 (which we do in CI), so we +// override it internally back to the default frequency. +//@compile-flags: -Zmiri-provenance-gc=10000 + +// Tests showing weak memory behaviours are exhibited, even with a fixed scheule. +// We run all tests a number of times and then check that we see the desired list of outcomes. -// Tests showing weak memory behaviours are exhibited. All tests -// return true when the desired behaviour is seen. -// This is scheduler and pseudo-RNG dependent, so each test is -// run multiple times until one try returns true. // Spurious failure is possible, if you are really unlucky with // the RNG and always read the latest value from the store buffer. @@ -31,129 +33,136 @@ fn spin_until(loc: &AtomicUsize, val: usize) -> usize { val } -fn relaxed(initial_read: bool) -> bool { - let x = static_atomic(0); - let j1 = spawn(move || { - x.store(1, Relaxed); - // Preemption is disabled, so the store above will never be the - // latest store visible to another thread. - x.store(2, Relaxed); +/// Check that the function produces the intended set of outcomes. +#[track_caller] +fn check_all_outcomes( + expected: impl IntoIterator, + generate: impl Fn() -> T, +) { + use std::collections::HashSet; + + let expected: HashSet = HashSet::from_iter(expected); + let mut seen = HashSet::new(); + // Let's give it N times as many tries as we are expecting values. + let tries = expected.len() * 8; + for _ in 0..tries { + let val = generate(); + assert!(expected.contains(&val), "got an unexpected value: {val}"); + seen.insert(val); + } + // Let's see if we saw them all. + for val in expected { + if !seen.contains(&val) { + panic!("did not get value that should be possible: {val}"); + } + } +} + +fn relaxed() { + check_all_outcomes([0, 1, 2], || { + let x = static_atomic(0); + let j1 = spawn(move || { + x.store(1, Relaxed); + // Preemption is disabled, so the store above will never be the + // latest store visible to another thread. + x.store(2, Relaxed); + }); + + let j2 = spawn(move || x.load(Relaxed)); + + j1.join().unwrap(); + let r2 = j2.join().unwrap(); + + // There are three possible values here: 0 (from the initial read), 1 (from the first relaxed + // read), and 2 (the last read). + r2 }); - - let j2 = spawn(move || x.load(Relaxed)); - - j1.join().unwrap(); - let r2 = j2.join().unwrap(); - - // There are three possible values here: 0 (from the initial read), 1 (from the first relaxed - // read), and 2 (the last read). The last case is boring and we cover the other two. - r2 == if initial_read { 0 } else { 1 } } // https://www.doc.ic.ac.uk/~afd/homepages/papers/pdfs/2017/POPL.pdf Figure 8 -fn seq_cst() -> bool { - let x = static_atomic(0); +fn seq_cst() { + check_all_outcomes([1, 3], || { + let x = static_atomic(0); - let j1 = spawn(move || { - x.store(1, Relaxed); + let j1 = spawn(move || { + x.store(1, Relaxed); + }); + + let j2 = spawn(move || { + x.store(2, SeqCst); + x.store(3, SeqCst); + }); + + let j3 = spawn(move || x.load(SeqCst)); + + j1.join().unwrap(); + j2.join().unwrap(); + let r3 = j3.join().unwrap(); + + // Even though we force t3 to run last, it can still see the value 1. + // And it can *never* see the value 2! + r3 }); - - let j2 = spawn(move || { - x.store(2, SeqCst); - x.store(3, SeqCst); - }); - - let j3 = spawn(move || x.load(SeqCst)); - - j1.join().unwrap(); - j2.join().unwrap(); - let r3 = j3.join().unwrap(); - - // Even though we force t3 to run last, it can still see the value 1. - // And it can *never* see the value 2! - assert!(r3 == 1 || r3 == 3); - r3 == 1 } -fn initialization_write(add_fence: bool) -> bool { - let x = static_atomic(11); +fn initialization_write(add_fence: bool) { + check_all_outcomes([11, 22], || { + let x = static_atomic(11); - let wait = static_atomic(0); + let wait = static_atomic(0); - let j1 = spawn(move || { - x.store(22, Relaxed); - // Relaxed is intentional. We want to test if the thread 2 reads the initialisation write - // after a relaxed write - wait.store(1, Relaxed); + let j1 = spawn(move || { + x.store(22, Relaxed); + // Relaxed is intentional. We want to test if the thread 2 reads the initialisation write + // after a relaxed write + wait.store(1, Relaxed); + }); + + let j2 = spawn(move || { + spin_until(wait, 1); + if add_fence { + fence(AcqRel); + } + x.load(Relaxed) + }); + + j1.join().unwrap(); + let r2 = j2.join().unwrap(); + + r2 }); +} - let j2 = spawn(move || { - spin_until(wait, 1); - if add_fence { - fence(AcqRel); +fn faa_replaced_by_load() { + check_all_outcomes([true, false], || { + // Example from https://github.com/llvm/llvm-project/issues/56450#issuecomment-1183695905 + pub fn rdmw(storing: &AtomicUsize, sync: &AtomicUsize, loading: &AtomicUsize) -> usize { + storing.store(1, Relaxed); + fence(Release); + // sync.fetch_add(0, Relaxed); + sync.load(Relaxed); + fence(Acquire); + loading.load(Relaxed) } - x.load(Relaxed) + + let x = static_atomic(0); + let y = static_atomic(0); + let z = static_atomic(0); + + let t1 = spawn(move || rdmw(y, x, z)); + + let t2 = spawn(move || rdmw(z, x, y)); + + let a = t1.join().unwrap(); + let b = t2.join().unwrap(); + (a, b) == (0, 0) }); - - j1.join().unwrap(); - let r2 = j2.join().unwrap(); - - r2 == 11 -} - -fn faa_replaced_by_load() -> bool { - // Example from https://github.com/llvm/llvm-project/issues/56450#issuecomment-1183695905 - #[no_mangle] - pub fn rdmw(storing: &AtomicUsize, sync: &AtomicUsize, loading: &AtomicUsize) -> usize { - storing.store(1, Relaxed); - fence(Release); - // sync.fetch_add(0, Relaxed); - sync.load(Relaxed); - fence(Acquire); - loading.load(Relaxed) - } - - let x = static_atomic(0); - let y = static_atomic(0); - let z = static_atomic(0); - - // Since each thread is so short, we need to make sure that they truely run at the same time - // Otherwise t1 will finish before t2 even starts - let go = static_atomic(0); - - let t1 = spawn(move || { - spin_until(go, 1); - rdmw(y, x, z) - }); - - let t2 = spawn(move || { - spin_until(go, 1); - rdmw(z, x, y) - }); - - go.store(1, Relaxed); - - let a = t1.join().unwrap(); - let b = t2.join().unwrap(); - (a, b) == (0, 0) -} - -/// Asserts that the function returns true at least once in 100 runs -#[track_caller] -fn assert_once(f: fn() -> bool) { - assert!(std::iter::repeat_with(|| f()).take(100).any(|x| x)); } pub fn main() { - assert_once(|| relaxed(false)); - assert_once(|| relaxed(true)); - assert_once(seq_cst); - assert_once(|| initialization_write(false)); - assert_once(|| initialization_write(true)); - assert_once(faa_replaced_by_load); - - // Run seq_cst a few more times since it has an assertion we want to ensure holds always. - for _ in 0..50 { - seq_cst(); - } + relaxed(); + seq_cst(); + initialization_write(false); + initialization_write(true); + faa_replaced_by_load(); } From 614dab3ed2e61acd32a572c37e5480538b547da8 Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Wed, 10 Sep 2025 23:02:49 +0800 Subject: [PATCH 348/682] loongarch: Align intrinsic signatures with LLVM --- .../crates/core_arch/src/loongarch32/mod.rs | 7 ++++--- .../crates/core_arch/src/loongarch64/mod.rs | 17 ++++++++++------- .../core_arch/src/loongarch_shared/mod.rs | 12 ++++++------ 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/loongarch32/mod.rs b/library/stdarch/crates/core_arch/src/loongarch32/mod.rs index fb05450373c2..4e3f3d27182e 100644 --- a/library/stdarch/crates/core_arch/src/loongarch32/mod.rs +++ b/library/stdarch/crates/core_arch/src/loongarch32/mod.rs @@ -17,9 +17,10 @@ unsafe extern "unadjusted" { /// Generates the cache operation instruction #[inline] #[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub unsafe fn cacop(a: i32, b: i32) { - static_assert_simm_bits!(IMM12, 12); - __cacop(a, b, IMM12); +pub unsafe fn cacop(b: i32) { + static_assert_uimm_bits!(IMM5, 5); + static_assert_simm_bits!(IMM_S12, 12); + __cacop(IMM5, b, IMM_S12); } /// Reads the CSR diff --git a/library/stdarch/crates/core_arch/src/loongarch64/mod.rs b/library/stdarch/crates/core_arch/src/loongarch64/mod.rs index e8249805eae2..ab968aff20bb 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/mod.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/mod.rs @@ -64,9 +64,10 @@ pub fn crcc_w_d_w(a: i64, b: i32) -> i32 { /// Generates the cache operation instruction #[inline] #[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub unsafe fn cacop(a: i64, b: i64) { - static_assert_simm_bits!(IMM12, 12); - __cacop(a, b, IMM12); +pub unsafe fn cacop(b: i64) { + static_assert_uimm_bits!(IMM5, 5); + static_assert_simm_bits!(IMM_S12, 12); + __cacop(IMM5, b, IMM_S12); } /// Reads the CSR @@ -125,14 +126,16 @@ pub unsafe fn asrtgt(a: i64, b: i64) { #[inline] #[rustc_legacy_const_generics(1)] #[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub unsafe fn lddir(a: i64) -> i64 { - __lddir(a, B) +pub unsafe fn lddir(a: i64) -> i64 { + static_assert_uimm_bits!(IMM8, 8); + __lddir(a, IMM8) } /// Loads the page table entry #[inline] #[rustc_legacy_const_generics(1)] #[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub unsafe fn ldpte(a: i64) { - __ldpte(a, B) +pub unsafe fn ldpte(a: i64) { + static_assert_uimm_bits!(IMM8, 8); + __ldpte(a, IMM8) } diff --git a/library/stdarch/crates/core_arch/src/loongarch_shared/mod.rs b/library/stdarch/crates/core_arch/src/loongarch_shared/mod.rs index 710b926f8df4..8991fe857682 100644 --- a/library/stdarch/crates/core_arch/src/loongarch_shared/mod.rs +++ b/library/stdarch/crates/core_arch/src/loongarch_shared/mod.rs @@ -131,17 +131,17 @@ pub fn ibar() { /// Moves data from a GPR to the FCSR #[inline] #[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub unsafe fn movgr2fcsr(a: i32) { - static_assert_uimm_bits!(IMM5, 5); - __movgr2fcsr(IMM5, a); +pub unsafe fn movgr2fcsr(a: i32) { + static_assert_uimm_bits!(IMM2, 2); + __movgr2fcsr(IMM2, a); } /// Moves data from a FCSR to the GPR #[inline] #[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn movfcsr2gr() -> i32 { - static_assert_uimm_bits!(IMM5, 5); - unsafe { __movfcsr2gr(IMM5) } +pub fn movfcsr2gr() -> i32 { + static_assert_uimm_bits!(IMM2, 2); + unsafe { __movfcsr2gr(IMM2) } } /// Reads the 8-bit IO-CSR From 655b8474348a087acbcec498d48fd705160ee080 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 10 Sep 2025 16:35:07 +0200 Subject: [PATCH 349/682] also use nicer check_all_outcomes in float_nan --- src/tools/miri/tests/pass/float_nan.rs | 319 +++++++++++-------------- 1 file changed, 142 insertions(+), 177 deletions(-) diff --git a/src/tools/miri/tests/pass/float_nan.rs b/src/tools/miri/tests/pass/float_nan.rs index 3ffdb6868ac0..f0a2b8e9ee06 100644 --- a/src/tools/miri/tests/pass/float_nan.rs +++ b/src/tools/miri/tests/pass/float_nan.rs @@ -1,7 +1,8 @@ +// This test's runtime explodes if the GC interval is set to 1 (which we do in CI), so we +// override it internally back to the default frequency. +//@compile-flags: -Zmiri-provenance-gc=10000 #![feature(float_gamma, portable_simd, core_intrinsics)] -use std::collections::HashSet; use std::fmt; -use std::hash::Hash; use std::hint::black_box; fn ldexp(a: f64, b: i32) -> f64 { @@ -25,11 +26,18 @@ enum NaNKind { } use NaNKind::*; +/// Check that the function produces the intended set of outcomes. #[track_caller] -fn check_all_outcomes(expected: HashSet, generate: impl Fn() -> T) { +fn check_all_outcomes( + expected: impl IntoIterator, + generate: impl Fn() -> T, +) { + use std::collections::HashSet; + + let expected: HashSet = HashSet::from_iter(expected); let mut seen = HashSet::new(); - // Let's give it sixteen times as many tries as we are expecting values. - let tries = expected.len() * 16; + // Let's give it N times as many tries as we are expecting values. + let tries = expected.len() * 12; for _ in 0..tries { let val = generate(); assert!(expected.contains(&val), "got an unexpected value: {val}"); @@ -193,51 +201,50 @@ impl F64 { fn test_f32() { // Freshly generated NaNs can have either sign. - check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(0.0 / black_box(0.0)), - ); + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(0.0 / black_box(0.0)) + }); // When there are NaN inputs, their payload can be propagated, with any sign. let all1_payload = u32_ones(22); let all1 = F32::nan(Pos, Quiet, all1_payload).as_f32(); check_all_outcomes( - HashSet::from_iter([ + [ F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0), F32::nan(Pos, Quiet, all1_payload), F32::nan(Neg, Quiet, all1_payload), - ]), + ], || F32::from(0.0 + all1), ); // When there are two NaN inputs, the output can be either one, or the preferred NaN. let just1 = F32::nan(Neg, Quiet, 1).as_f32(); check_all_outcomes( - HashSet::from_iter([ + [ F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0), F32::nan(Pos, Quiet, 1), F32::nan(Neg, Quiet, 1), F32::nan(Pos, Quiet, all1_payload), F32::nan(Neg, Quiet, all1_payload), - ]), + ], || F32::from(just1 - all1), ); // When there are *signaling* NaN inputs, they might be quieted or not. let all1_snan = F32::nan(Pos, Signaling, all1_payload).as_f32(); check_all_outcomes( - HashSet::from_iter([ + [ F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0), F32::nan(Pos, Quiet, all1_payload), F32::nan(Neg, Quiet, all1_payload), F32::nan(Pos, Signaling, all1_payload), F32::nan(Neg, Signaling, all1_payload), - ]), + ], || F32::from(0.0 * all1_snan), ); // Mix signaling and non-signaling NaN. check_all_outcomes( - HashSet::from_iter([ + [ F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0), F32::nan(Pos, Quiet, 1), @@ -246,35 +253,26 @@ fn test_f32() { F32::nan(Neg, Quiet, all1_payload), F32::nan(Pos, Signaling, all1_payload), F32::nan(Neg, Signaling, all1_payload), - ]), + ], || F32::from(just1 % all1_snan), ); // Unary `-` must preserve payloads exactly. - check_all_outcomes(HashSet::from_iter([F32::nan(Neg, Quiet, all1_payload)]), || { - F32::from(-all1) - }); - check_all_outcomes(HashSet::from_iter([F32::nan(Neg, Signaling, all1_payload)]), || { - F32::from(-all1_snan) - }); + check_all_outcomes([F32::nan(Neg, Quiet, all1_payload)], || F32::from(-all1)); + check_all_outcomes([F32::nan(Neg, Signaling, all1_payload)], || F32::from(-all1_snan)); // Intrinsics let nan = F32::nan(Neg, Quiet, 0).as_f32(); let snan = F32::nan(Neg, Signaling, 1).as_f32(); + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(f32::min(nan, nan)) + }); + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(nan.floor()) + }); + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || F32::from(nan.sin())); check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(f32::min(nan, nan)), - ); - check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(nan.floor()), - ); - check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(nan.sin()), - ); - check_all_outcomes( - HashSet::from_iter([ + [ F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0), F32::nan(Pos, Quiet, 1), @@ -285,37 +283,32 @@ fn test_f32() { F32::nan(Neg, Quiet, all1_payload), F32::nan(Pos, Signaling, all1_payload), F32::nan(Neg, Signaling, all1_payload), - ]), + ], || F32::from(just1.mul_add(F32::nan(Neg, Quiet, 2).as_f32(), all1_snan)), ); + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(nan.powf(nan)) + }); check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(nan.powf(nan)), - ); - check_all_outcomes( - HashSet::from_iter([1.0f32.into()]), + [1.0f32.into()], || F32::from(1.0f32.powf(nan)), // special `pow` rule ); - check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(nan.powi(1)), - ); + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(nan.powi(1)) + }); // libm functions + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(nan.sinh()) + }); + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(nan.atan2(nan)) + }); + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(nan.ln_gamma().0) + }); check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(nan.sinh()), - ); - check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(nan.atan2(nan)), - ); - check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(nan.ln_gamma().0), - ); - check_all_outcomes( - HashSet::from_iter([ + [ F32::from(1.0), F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0), @@ -323,58 +316,57 @@ fn test_f32() { F32::nan(Neg, Quiet, 1), F32::nan(Pos, Signaling, 1), F32::nan(Neg, Signaling, 1), - ]), + ], || F32::from(snan.powf(0.0)), ); } fn test_f64() { // Freshly generated NaNs can have either sign. - check_all_outcomes( - HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), - || F64::from(0.0 / black_box(0.0)), - ); + check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || { + F64::from(0.0 / black_box(0.0)) + }); // When there are NaN inputs, their payload can be propagated, with any sign. let all1_payload = u64_ones(51); let all1 = F64::nan(Pos, Quiet, all1_payload).as_f64(); check_all_outcomes( - HashSet::from_iter([ + [ F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0), F64::nan(Pos, Quiet, all1_payload), F64::nan(Neg, Quiet, all1_payload), - ]), + ], || F64::from(0.0 + all1), ); // When there are two NaN inputs, the output can be either one, or the preferred NaN. let just1 = F64::nan(Neg, Quiet, 1).as_f64(); check_all_outcomes( - HashSet::from_iter([ + [ F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0), F64::nan(Pos, Quiet, 1), F64::nan(Neg, Quiet, 1), F64::nan(Pos, Quiet, all1_payload), F64::nan(Neg, Quiet, all1_payload), - ]), + ], || F64::from(just1 - all1), ); // When there are *signaling* NaN inputs, they might be quieted or not. let all1_snan = F64::nan(Pos, Signaling, all1_payload).as_f64(); check_all_outcomes( - HashSet::from_iter([ + [ F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0), F64::nan(Pos, Quiet, all1_payload), F64::nan(Neg, Quiet, all1_payload), F64::nan(Pos, Signaling, all1_payload), F64::nan(Neg, Signaling, all1_payload), - ]), + ], || F64::from(0.0 * all1_snan), ); // Mix signaling and non-signaling NaN. check_all_outcomes( - HashSet::from_iter([ + [ F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0), F64::nan(Pos, Quiet, 1), @@ -383,27 +375,22 @@ fn test_f64() { F64::nan(Neg, Quiet, all1_payload), F64::nan(Pos, Signaling, all1_payload), F64::nan(Neg, Signaling, all1_payload), - ]), + ], || F64::from(just1 % all1_snan), ); // Intrinsics let nan = F64::nan(Neg, Quiet, 0).as_f64(); let snan = F64::nan(Neg, Signaling, 1).as_f64(); + check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || { + F64::from(f64::min(nan, nan)) + }); + check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || { + F64::from(nan.floor()) + }); + check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || F64::from(nan.sin())); check_all_outcomes( - HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), - || F64::from(f64::min(nan, nan)), - ); - check_all_outcomes( - HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), - || F64::from(nan.floor()), - ); - check_all_outcomes( - HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), - || F64::from(nan.sin()), - ); - check_all_outcomes( - HashSet::from_iter([ + [ F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0), F64::nan(Pos, Quiet, 1), @@ -414,41 +401,35 @@ fn test_f64() { F64::nan(Neg, Quiet, all1_payload), F64::nan(Pos, Signaling, all1_payload), F64::nan(Neg, Signaling, all1_payload), - ]), + ], || F64::from(just1.mul_add(F64::nan(Neg, Quiet, 2).as_f64(), all1_snan)), ); + check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || { + F64::from(nan.powf(nan)) + }); check_all_outcomes( - HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), - || F64::from(nan.powf(nan)), - ); - check_all_outcomes( - HashSet::from_iter([1.0f64.into()]), + [1.0f64.into()], || F64::from(1.0f64.powf(nan)), // special `pow` rule ); - check_all_outcomes( - HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), - || F64::from(nan.powi(1)), - ); + check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || { + F64::from(nan.powi(1)) + }); // libm functions + check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || { + F64::from(nan.sinh()) + }); + check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || { + F64::from(nan.atan2(nan)) + }); + check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || { + F64::from(ldexp(nan, 1)) + }); + check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || { + F64::from(nan.ln_gamma().0) + }); check_all_outcomes( - HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), - || F64::from(nan.sinh()), - ); - check_all_outcomes( - HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), - || F64::from(nan.atan2(nan)), - ); - check_all_outcomes( - HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), - || F64::from(ldexp(nan, 1)), - ); - check_all_outcomes( - HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), - || F64::from(nan.ln_gamma().0), - ); - check_all_outcomes( - HashSet::from_iter([ + [ F64::from(1.0), F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0), @@ -456,7 +437,7 @@ fn test_f64() { F64::nan(Neg, Quiet, 1), F64::nan(Pos, Signaling, 1), F64::nan(Neg, Signaling, 1), - ]), + ], || F64::from(snan.powf(0.0)), ); } @@ -467,82 +448,79 @@ fn test_casts() { let left1_payload_64 = (all1_payload_32 as u64) << (51 - 22); // 64-to-32 - check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(F64::nan(Pos, Quiet, 0).as_f64() as f32), - ); + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(F64::nan(Pos, Quiet, 0).as_f64() as f32) + }); // The preferred payload is always a possibility. check_all_outcomes( - HashSet::from_iter([ + [ F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0), F32::nan(Pos, Quiet, all1_payload_32), F32::nan(Neg, Quiet, all1_payload_32), - ]), + ], || F32::from(F64::nan(Pos, Quiet, all1_payload_64).as_f64() as f32), ); // If the input is signaling, then the output *may* also be signaling. check_all_outcomes( - HashSet::from_iter([ + [ F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0), F32::nan(Pos, Quiet, all1_payload_32), F32::nan(Neg, Quiet, all1_payload_32), F32::nan(Pos, Signaling, all1_payload_32), F32::nan(Neg, Signaling, all1_payload_32), - ]), + ], || F32::from(F64::nan(Pos, Signaling, all1_payload_64).as_f64() as f32), ); // Check that the low bits are gone (not the high bits). + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(F64::nan(Pos, Quiet, 1).as_f64() as f32) + }); check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(F64::nan(Pos, Quiet, 1).as_f64() as f32), - ); - check_all_outcomes( - HashSet::from_iter([ + [ F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0), F32::nan(Pos, Quiet, 1), F32::nan(Neg, Quiet, 1), - ]), + ], || F32::from(F64::nan(Pos, Quiet, 1 << (51 - 22)).as_f64() as f32), ); check_all_outcomes( - HashSet::from_iter([ + [ F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0), // The `1` payload becomes `0`, and the `0` payload cannot be signaling, // so these are the only options. - ]), + ], || F32::from(F64::nan(Pos, Signaling, 1).as_f64() as f32), ); // 32-to-64 - check_all_outcomes( - HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), - || F64::from(F32::nan(Pos, Quiet, 0).as_f32() as f64), - ); + check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || { + F64::from(F32::nan(Pos, Quiet, 0).as_f32() as f64) + }); // The preferred payload is always a possibility. // Also checks that 0s are added on the right. check_all_outcomes( - HashSet::from_iter([ + [ F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0), F64::nan(Pos, Quiet, left1_payload_64), F64::nan(Neg, Quiet, left1_payload_64), - ]), + ], || F64::from(F32::nan(Pos, Quiet, all1_payload_32).as_f32() as f64), ); // If the input is signaling, then the output *may* also be signaling. check_all_outcomes( - HashSet::from_iter([ + [ F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0), F64::nan(Pos, Quiet, left1_payload_64), F64::nan(Neg, Quiet, left1_payload_64), F64::nan(Pos, Signaling, left1_payload_64), F64::nan(Neg, Signaling, left1_payload_64), - ]), + ], || F64::from(F32::nan(Pos, Signaling, all1_payload_32).as_f32() as f64), ); } @@ -552,48 +530,35 @@ fn test_simd() { use std::simd::*; let nan = F32::nan(Neg, Quiet, 0).as_f32(); - check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(unsafe { simd_div(f32x4::splat(0.0), f32x4::splat(0.0)) }[0]), - ); - check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(unsafe { simd_fmin(f32x4::splat(nan), f32x4::splat(nan)) }[0]), - ); - check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(unsafe { simd_fmax(f32x4::splat(nan), f32x4::splat(nan)) }[0]), - ); - check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || { - F32::from( - unsafe { simd_fma(f32x4::splat(nan), f32x4::splat(nan), f32x4::splat(nan)) }[0], - ) - }, - ); - check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(unsafe { simd_reduce_add_ordered::<_, f32>(f32x4::splat(nan), nan) }), - ); - check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(unsafe { simd_reduce_max::<_, f32>(f32x4::splat(nan)) }), - ); - check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(unsafe { simd_fsqrt(f32x4::splat(nan)) }[0]), - ); - check_all_outcomes( - HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]), - || F32::from(unsafe { simd_ceil(f32x4::splat(nan)) }[0]), - ); + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(unsafe { simd_div(f32x4::splat(0.0), f32x4::splat(0.0)) }[0]) + }); + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(unsafe { simd_fmin(f32x4::splat(nan), f32x4::splat(nan)) }[0]) + }); + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(unsafe { simd_fmax(f32x4::splat(nan), f32x4::splat(nan)) }[0]) + }); + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(unsafe { simd_fma(f32x4::splat(nan), f32x4::splat(nan), f32x4::splat(nan)) }[0]) + }); + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(unsafe { simd_reduce_add_ordered::<_, f32>(f32x4::splat(nan), nan) }) + }); + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(unsafe { simd_reduce_max::<_, f32>(f32x4::splat(nan)) }) + }); + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(unsafe { simd_fsqrt(f32x4::splat(nan)) }[0]) + }); + check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || { + F32::from(unsafe { simd_ceil(f32x4::splat(nan)) }[0]) + }); // Casts - check_all_outcomes( - HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]), - || F64::from(unsafe { simd_cast::(f32x4::splat(nan)) }[0]), - ); + check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || { + F64::from(unsafe { simd_cast::(f32x4::splat(nan)) }[0]) + }); } fn main() { From 827a6541e6a9e02fee35f1cc94f61e3ded4c258f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 10 Sep 2025 16:40:48 +0200 Subject: [PATCH 350/682] move all weak memory tests into their folder --- .../consistency.rs} | 2 +- .../consistency_sc.rs} | 2 +- .../miri/tests/pass/{weak_memory => 0weak_memory}/extra_cpp.rs | 0 src/tools/miri/tests/pass/{weak_memory => 0weak_memory}/weak.rs | 0 4 files changed, 2 insertions(+), 2 deletions(-) rename src/tools/miri/tests/pass/{0weak_memory_consistency.rs => 0weak_memory/consistency.rs} (99%) rename src/tools/miri/tests/pass/{0weak_memory_consistency_sc.rs => 0weak_memory/consistency_sc.rs} (99%) rename src/tools/miri/tests/pass/{weak_memory => 0weak_memory}/extra_cpp.rs (100%) rename src/tools/miri/tests/pass/{weak_memory => 0weak_memory}/weak.rs (100%) diff --git a/src/tools/miri/tests/pass/0weak_memory_consistency.rs b/src/tools/miri/tests/pass/0weak_memory/consistency.rs similarity index 99% rename from src/tools/miri/tests/pass/0weak_memory_consistency.rs rename to src/tools/miri/tests/pass/0weak_memory/consistency.rs index f1d5a56c2b02..16a38ebd9d4d 100644 --- a/src/tools/miri/tests/pass/0weak_memory_consistency.rs +++ b/src/tools/miri/tests/pass/0weak_memory/consistency.rs @@ -51,7 +51,7 @@ fn spin_until_bool(loc: &AtomicBool, ord: Ordering, val: bool) -> bool { } /// Test matching https://www.doc.ic.ac.uk/~afd/homepages/papers/pdfs/2017/POPL.pdf, Figure 7. -/// (The Figure 8 test is in `weak_memory/weak.rs`.) +/// (The Figure 8 test is in `weak.rs`.) fn test_corr() { let x = static_atomic(0); let y = static_atomic(0); diff --git a/src/tools/miri/tests/pass/0weak_memory_consistency_sc.rs b/src/tools/miri/tests/pass/0weak_memory/consistency_sc.rs similarity index 99% rename from src/tools/miri/tests/pass/0weak_memory_consistency_sc.rs rename to src/tools/miri/tests/pass/0weak_memory/consistency_sc.rs index 43675a8b5e16..cb8535b8ad74 100644 --- a/src/tools/miri/tests/pass/0weak_memory_consistency_sc.rs +++ b/src/tools/miri/tests/pass/0weak_memory/consistency_sc.rs @@ -349,7 +349,7 @@ fn test_sc_relaxed() { } pub fn main() { - for _ in 0..50 { + for _ in 0..32 { test_sc_store_buffering(); test_iriw_sc_rlx(); test_cpp20_sc_fence_fix(); diff --git a/src/tools/miri/tests/pass/weak_memory/extra_cpp.rs b/src/tools/miri/tests/pass/0weak_memory/extra_cpp.rs similarity index 100% rename from src/tools/miri/tests/pass/weak_memory/extra_cpp.rs rename to src/tools/miri/tests/pass/0weak_memory/extra_cpp.rs diff --git a/src/tools/miri/tests/pass/weak_memory/weak.rs b/src/tools/miri/tests/pass/0weak_memory/weak.rs similarity index 100% rename from src/tools/miri/tests/pass/weak_memory/weak.rs rename to src/tools/miri/tests/pass/0weak_memory/weak.rs From cb819729d0d2d3f6f74b05168e17c34ca7f626b0 Mon Sep 17 00:00:00 2001 From: clubby789 Date: Wed, 10 Sep 2025 16:56:36 +0100 Subject: [PATCH 351/682] tidy: Introduce `WorkspaceInfo` struct for deps information --- src/tools/tidy/src/deps.rs | 97 ++++++++++++++++++++++++----------- src/tools/tidy/src/extdeps.rs | 12 +++-- 2 files changed, 73 insertions(+), 36 deletions(-) diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 60347b2ea645..08c0d3ca7865 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -48,39 +48,74 @@ const LICENSES: &[&str] = &[ type ExceptionList = &'static [(&'static str, &'static str)]; +pub(crate) struct WorkspaceInfo<'a> { + /// Path to the directory containing the workspace root Cargo.toml file. + pub(crate) path: &'a str, + /// The list of license exceptions. + pub(crate) exceptions: ExceptionList, + /// Optionally: + /// * A list of crates for which dependencies need to be explicitly allowed. + /// * The list of allowed dependencies. + pub(crate) crates_and_deps: Option<(&'a [&'a str], &'a [&'a str])>, + /// Submodules required for the workspace + pub(crate) submodules: &'a [&'a str], +} + +impl<'a> WorkspaceInfo<'a> { + const fn new( + path: &'a str, + exceptions: ExceptionList, + crates_and_deps: Option<(&'a [&str], &'a [&str])>, + submodules: &'a [&str], + ) -> Self { + Self { path, exceptions, crates_and_deps, submodules } + } +} + /// The workspaces to check for licensing and optionally permitted dependencies. -/// -/// Each entry consists of a tuple with the following elements: -/// -/// * The path to the workspace root Cargo.toml file. -/// * The list of license exceptions. -/// * Optionally a tuple of: -/// * A list of crates for which dependencies need to be explicitly allowed. -/// * The list of allowed dependencies. -/// * Submodules required for the workspace. // FIXME auto detect all cargo workspaces -pub(crate) const WORKSPACES: &[(&str, ExceptionList, Option<(&[&str], &[&str])>, &[&str])] = &[ +pub(crate) const WORKSPACES: &[WorkspaceInfo<'static>] = &[ // The root workspace has to be first for check_rustfix to work. - (".", EXCEPTIONS, Some((&["rustc-main"], PERMITTED_RUSTC_DEPENDENCIES)), &[]), - ("library", EXCEPTIONS_STDLIB, Some((&["sysroot"], PERMITTED_STDLIB_DEPENDENCIES)), &[]), + WorkspaceInfo::new(".", EXCEPTIONS, Some((&["rustc-main"], PERMITTED_RUSTC_DEPENDENCIES)), &[]), + WorkspaceInfo::new( + "library", + EXCEPTIONS_STDLIB, + Some((&["sysroot"], PERMITTED_STDLIB_DEPENDENCIES)), + &[], + ), // Outside of the alphabetical section because rustfmt formats it using multiple lines. - ( + WorkspaceInfo::new( "compiler/rustc_codegen_cranelift", EXCEPTIONS_CRANELIFT, Some((&["rustc_codegen_cranelift"], PERMITTED_CRANELIFT_DEPENDENCIES)), &[], ), // tidy-alphabetical-start - ("compiler/rustc_codegen_gcc", EXCEPTIONS_GCC, None, &[]), - ("src/bootstrap", EXCEPTIONS_BOOTSTRAP, None, &[]), - ("src/tools/cargo", EXCEPTIONS_CARGO, None, &["src/tools/cargo"]), - //("src/tools/miri/test-cargo-miri", &[], None), // FIXME uncomment once all deps are vendored - //("src/tools/miri/test_dependencies", &[], None), // FIXME uncomment once all deps are vendored - ("src/tools/rust-analyzer", EXCEPTIONS_RUST_ANALYZER, None, &[]), - ("src/tools/rustbook", EXCEPTIONS_RUSTBOOK, None, &["src/doc/book", "src/doc/reference"]), - ("src/tools/rustc-perf", EXCEPTIONS_RUSTC_PERF, None, &["src/tools/rustc-perf"]), - ("src/tools/test-float-parse", EXCEPTIONS, None, &[]), - ("tests/run-make-cargo/uefi-qemu/uefi_qemu_test", EXCEPTIONS_UEFI_QEMU_TEST, None, &[]), + WorkspaceInfo::new("compiler/rustc_codegen_gcc", EXCEPTIONS_GCC, None, &[]), + WorkspaceInfo::new("src/bootstrap", EXCEPTIONS_BOOTSTRAP, None, &[]), + WorkspaceInfo::new("src/tools/cargo", EXCEPTIONS_CARGO, None, &["src/tools/cargo"]), + //WorkspaceInfo::new("src/tools/miri/test-cargo-miri", &[], None), // FIXME uncomment once all deps are vendored + //WorkspaceInfo::new("src/tools/miri/test_dependencies", &[], None), // FIXME uncomment once all deps are vendored + WorkspaceInfo::new("src/tools/rust-analyzer", EXCEPTIONS_RUST_ANALYZER, None, &[]), + WorkspaceInfo::new( + "src/tools/rustbook", + EXCEPTIONS_RUSTBOOK, + None, + &["src/doc/book", "src/doc/reference"], + ), + WorkspaceInfo::new( + "src/tools/rustc-perf", + EXCEPTIONS_RUSTC_PERF, + None, + &["src/tools/rustc-perf"], + ), + WorkspaceInfo::new("src/tools/test-float-parse", EXCEPTIONS, None, &[]), + WorkspaceInfo::new( + "tests/run-make-cargo/uefi-qemu/uefi_qemu_test", + EXCEPTIONS_UEFI_QEMU_TEST, + None, + &[], + ), // tidy-alphabetical-end ]; @@ -567,29 +602,29 @@ pub fn check(root: &Path, cargo: &Path, bless: bool, bad: &mut bool) { check_proc_macro_dep_list(root, cargo, bless, bad); - for &(workspace, exceptions, permitted_deps, submodules) in WORKSPACES { + for &WorkspaceInfo { path, exceptions, crates_and_deps, submodules } in WORKSPACES { if has_missing_submodule(root, submodules) { continue; } - if !root.join(workspace).join("Cargo.lock").exists() { - tidy_error!(bad, "the `{workspace}` workspace doesn't have a Cargo.lock"); + if !root.join(path).join("Cargo.lock").exists() { + tidy_error!(bad, "the `{path}` workspace doesn't have a Cargo.lock"); continue; } let mut cmd = cargo_metadata::MetadataCommand::new(); cmd.cargo_path(cargo) - .manifest_path(root.join(workspace).join("Cargo.toml")) + .manifest_path(root.join(path).join("Cargo.toml")) .features(cargo_metadata::CargoOpt::AllFeatures) .other_options(vec!["--locked".to_owned()]); let metadata = t!(cmd.exec()); - check_license_exceptions(&metadata, workspace, exceptions, bad); - if let Some((crates, permitted_deps)) = permitted_deps { - check_permitted_dependencies(&metadata, workspace, permitted_deps, crates, bad); + check_license_exceptions(&metadata, path, exceptions, bad); + if let Some((crates, permitted_deps)) = crates_and_deps { + check_permitted_dependencies(&metadata, path, permitted_deps, crates, bad); } - if workspace == "library" { + if path == "library" { check_runtime_license_exceptions(&metadata, bad); check_runtime_no_duplicate_dependencies(&metadata, bad); check_runtime_no_proc_macros(&metadata, bad); diff --git a/src/tools/tidy/src/extdeps.rs b/src/tools/tidy/src/extdeps.rs index bc217a55cc19..2b212cfa67a4 100644 --- a/src/tools/tidy/src/extdeps.rs +++ b/src/tools/tidy/src/extdeps.rs @@ -3,6 +3,8 @@ use std::fs; use std::path::Path; +use crate::deps::WorkspaceInfo; + /// List of allowed sources for packages. const ALLOWED_SOURCES: &[&str] = &[ r#""registry+https://github.com/rust-lang/crates.io-index""#, @@ -13,22 +15,22 @@ const ALLOWED_SOURCES: &[&str] = &[ /// Checks for external package sources. `root` is the path to the directory that contains the /// workspace `Cargo.toml`. pub fn check(root: &Path, bad: &mut bool) { - for &(workspace, _, _, submodules) in crate::deps::WORKSPACES { + for &WorkspaceInfo { path, submodules, .. } in crate::deps::WORKSPACES { if crate::deps::has_missing_submodule(root, submodules) { continue; } // FIXME check other workspaces too // `Cargo.lock` of rust. - let path = root.join(workspace).join("Cargo.lock"); + let lockfile = root.join(path).join("Cargo.lock"); - if !path.exists() { - tidy_error!(bad, "the `{workspace}` workspace doesn't have a Cargo.lock"); + if !lockfile.exists() { + tidy_error!(bad, "the `{path}` workspace doesn't have a Cargo.lock"); continue; } // Open and read the whole file. - let cargo_lock = t!(fs::read_to_string(&path)); + let cargo_lock = t!(fs::read_to_string(&lockfile)); // Process each line. for line in cargo_lock.lines() { From 7d5413bd17ede225bc3c2ac9a7f640885628edc8 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 10 Sep 2025 17:55:44 +0200 Subject: [PATCH 352/682] this apparently needs more test rounds --- src/tools/miri/tests/pass/0weak_memory/weak.rs | 8 ++++++-- src/tools/miri/tests/pass/float_nan.rs | 6 +++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/tools/miri/tests/pass/0weak_memory/weak.rs b/src/tools/miri/tests/pass/0weak_memory/weak.rs index f39ef0ed98da..0dd661d3e9b4 100644 --- a/src/tools/miri/tests/pass/0weak_memory/weak.rs +++ b/src/tools/miri/tests/pass/0weak_memory/weak.rs @@ -44,11 +44,15 @@ fn check_all_outcomes( let expected: HashSet = HashSet::from_iter(expected); let mut seen = HashSet::new(); // Let's give it N times as many tries as we are expecting values. - let tries = expected.len() * 8; - for _ in 0..tries { + let tries = expected.len() * 12; + for i in 0..tries { let val = generate(); assert!(expected.contains(&val), "got an unexpected value: {val}"); seen.insert(val); + if i > tries / 2 && expected.len() == seen.len() { + // We saw everything and we did quite a few tries, let's avoid wasting time. + return; + } } // Let's see if we saw them all. for val in expected { diff --git a/src/tools/miri/tests/pass/float_nan.rs b/src/tools/miri/tests/pass/float_nan.rs index f0a2b8e9ee06..902816307403 100644 --- a/src/tools/miri/tests/pass/float_nan.rs +++ b/src/tools/miri/tests/pass/float_nan.rs @@ -38,10 +38,14 @@ fn check_all_outcomes( let mut seen = HashSet::new(); // Let's give it N times as many tries as we are expecting values. let tries = expected.len() * 12; - for _ in 0..tries { + for i in 0..tries { let val = generate(); assert!(expected.contains(&val), "got an unexpected value: {val}"); seen.insert(val); + if i > tries / 2 && expected.len() == seen.len() { + // We saw everything and we did quite a few tries, let's avoid wasting time. + return; + } } // Let's see if we saw them all. for val in expected { From d50a80e1d0e655524a3c0dff1f55d7c667314184 Mon Sep 17 00:00:00 2001 From: binarycat Date: Tue, 10 Jun 2025 16:35:44 -0500 Subject: [PATCH 353/682] core::ptr: deduplicate docs for as_ref, addr, and as_uninit_ref also add INFO.md file explaining the purpouse of the ptr/docs dir. --- library/core/src/ptr/const_ptr.rs | 78 +++--------------- library/core/src/ptr/docs/INFO.md | 21 +++++ library/core/src/ptr/docs/addr.md | 22 +++++ library/core/src/ptr/docs/as_ref.md | 19 +++++ library/core/src/ptr/docs/as_uninit_ref.md | 15 ++++ library/core/src/ptr/mut_ptr.rs | 94 ++++++---------------- 6 files changed, 113 insertions(+), 136 deletions(-) create mode 100644 library/core/src/ptr/docs/INFO.md create mode 100644 library/core/src/ptr/docs/addr.md create mode 100644 library/core/src/ptr/docs/as_ref.md create mode 100644 library/core/src/ptr/docs/as_uninit_ref.md diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 9366cb36c6ef..7cd2a1d28e33 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -146,28 +146,7 @@ impl *const T { self as _ } - /// Gets the "address" portion of the pointer. - /// - /// This is similar to `self as usize`, except that the [provenance][crate::ptr#provenance] of - /// the pointer is discarded and not [exposed][crate::ptr#exposed-provenance]. This means that - /// casting the returned address back to a pointer yields a [pointer without - /// provenance][without_provenance], which is undefined behavior to dereference. To properly - /// restore the lost information and obtain a dereferenceable pointer, use - /// [`with_addr`][pointer::with_addr] or [`map_addr`][pointer::map_addr]. - /// - /// If using those APIs is not possible because there is no way to preserve a pointer with the - /// required provenance, then Strict Provenance might not be for you. Use pointer-integer casts - /// or [`expose_provenance`][pointer::expose_provenance] and [`with_exposed_provenance`][with_exposed_provenance] - /// instead. However, note that this makes your code less portable and less amenable to tools - /// that check for compliance with the Rust memory model. - /// - /// On most platforms this will produce a value with the same bytes as the original - /// pointer, because all the bytes are dedicated to describing the address. - /// Platforms which need to store additional information in the pointer may - /// perform a change of representation to produce a value containing only the address - /// portion of the pointer. What that means is up to the platform to define. - /// - /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. + #[doc = include_str!("./docs/addr.md")] #[must_use] #[inline(always)] #[stable(feature = "strict_provenance", since = "1.84.0")] @@ -254,23 +233,16 @@ impl *const T { (self.cast(), metadata(self)) } - /// Returns `None` if the pointer is null, or else returns a shared reference to - /// the value wrapped in `Some`. If the value may be uninitialized, [`as_uninit_ref`] - /// must be used instead. + #[doc = include_str!("./docs/as_ref.md")] /// - /// [`as_uninit_ref`]: #method.as_uninit_ref + /// ``` + /// let ptr: *const u8 = &10u8 as *const u8; /// - /// # Safety - /// - /// When calling this method, you have to ensure that *either* the pointer is null *or* - /// the pointer is [convertible to a reference](crate::ptr#pointer-to-reference-conversion). - /// - /// # Panics during const evaluation - /// - /// This method will panic during const evaluation if the pointer cannot be - /// determined to be null or not. See [`is_null`] for more information. - /// - /// [`is_null`]: #method.is_null + /// unsafe { + /// let val_back = &*ptr; + /// assert_eq!(val_back, &10); + /// } + /// ``` /// /// # Examples /// @@ -284,20 +256,9 @@ impl *const T { /// } /// ``` /// - /// # Null-unchecked version /// - /// If you are sure the pointer can never be null and are looking for some kind of - /// `as_ref_unchecked` that returns the `&T` instead of `Option<&T>`, know that you can - /// dereference the pointer directly. - /// - /// ``` - /// let ptr: *const u8 = &10u8 as *const u8; - /// - /// unsafe { - /// let val_back = &*ptr; - /// assert_eq!(val_back, &10); - /// } - /// ``` + /// [`is_null`]: #method.is_null + /// [`as_uninit_ref`]: #method.as_uninit_ref #[stable(feature = "ptr_as_ref", since = "1.9.0")] #[rustc_const_stable(feature = "const_ptr_is_null", since = "1.84.0")] #[inline] @@ -338,23 +299,10 @@ impl *const T { unsafe { &*self } } - /// Returns `None` if the pointer is null, or else returns a shared reference to - /// the value wrapped in `Some`. In contrast to [`as_ref`], this does not require - /// that the value has to be initialized. - /// - /// [`as_ref`]: #method.as_ref - /// - /// # Safety - /// - /// When calling this method, you have to ensure that *either* the pointer is null *or* - /// the pointer is [convertible to a reference](crate::ptr#pointer-to-reference-conversion). - /// - /// # Panics during const evaluation - /// - /// This method will panic during const evaluation if the pointer cannot be - /// determined to be null or not. See [`is_null`] for more information. + #[doc = include_str!("./docs/as_uninit_ref.md")] /// /// [`is_null`]: #method.is_null + /// [`as_ref`]: #method.as_ref /// /// # Examples /// diff --git a/library/core/src/ptr/docs/INFO.md b/library/core/src/ptr/docs/INFO.md new file mode 100644 index 000000000000..28a0da4926a9 --- /dev/null +++ b/library/core/src/ptr/docs/INFO.md @@ -0,0 +1,21 @@ +This directory holds method documentation that otherwise +would be duplicated across mutable and immutable pointers. + +Note that most of the docs here are not the complete docs +for their corresponding method. This is for a few reasons: + +1. Examples need to be different for mutable/immutable + pointers, in order to actually call the correct method. +2. Link reference definitions are frequently different + between mutable/immutable pointers, in order to link to + the correct method. + For example, `<*const T>::as_ref` links to + `<*const T>::is_null`, while `<*mut T>::as_ref` links to + `<*mut T>::is_null`. +3. Many methods on mutable pointers link to an alternate + version that returns a mutable reference instead of + a shared reference. + +Always review the rendered docs manually when making +changes to these files to make sure you're not accidentally +splitting up a section. diff --git a/library/core/src/ptr/docs/addr.md b/library/core/src/ptr/docs/addr.md new file mode 100644 index 000000000000..785b88a99870 --- /dev/null +++ b/library/core/src/ptr/docs/addr.md @@ -0,0 +1,22 @@ +Gets the "address" portion of the pointer. + +This is similar to `self as usize`, except that the [provenance][crate::ptr#provenance] of +the pointer is discarded and not [exposed][crate::ptr#exposed-provenance]. This means that +casting the returned address back to a pointer yields a [pointer without +provenance][without_provenance], which is undefined behavior to dereference. To properly +restore the lost information and obtain a dereferenceable pointer, use +[`with_addr`][pointer::with_addr] or [`map_addr`][pointer::map_addr]. + +If using those APIs is not possible because there is no way to preserve a pointer with the +required provenance, then Strict Provenance might not be for you. Use pointer-integer casts +or [`expose_provenance`][pointer::expose_provenance] and [`with_exposed_provenance`][with_exposed_provenance] +instead. However, note that this makes your code less portable and less amenable to tools +that check for compliance with the Rust memory model. + +On most platforms this will produce a value with the same bytes as the original +pointer, because all the bytes are dedicated to describing the address. +Platforms which need to store additional information in the pointer may +perform a change of representation to produce a value containing only the address +portion of the pointer. What that means is up to the platform to define. + +This is a [Strict Provenance][crate::ptr#strict-provenance] API. diff --git a/library/core/src/ptr/docs/as_ref.md b/library/core/src/ptr/docs/as_ref.md new file mode 100644 index 000000000000..0c0d2768c748 --- /dev/null +++ b/library/core/src/ptr/docs/as_ref.md @@ -0,0 +1,19 @@ +Returns `None` if the pointer is null, or else returns a shared reference to +the value wrapped in `Some`. If the value may be uninitialized, [`as_uninit_ref`] +must be used instead. + +# Safety + +When calling this method, you have to ensure that *either* the pointer is null *or* +the pointer is [convertible to a reference](crate::ptr#pointer-to-reference-conversion). + +# Panics during const evaluation + +This method will panic during const evaluation if the pointer cannot be +determined to be null or not. See [`is_null`] for more information. + +# Null-unchecked version + +If you are sure the pointer can never be null and are looking for some kind of +`as_ref_unchecked` that returns the `&T` instead of `Option<&T>`, know that you can +dereference the pointer directly. diff --git a/library/core/src/ptr/docs/as_uninit_ref.md b/library/core/src/ptr/docs/as_uninit_ref.md new file mode 100644 index 000000000000..5b9a1ecb85b9 --- /dev/null +++ b/library/core/src/ptr/docs/as_uninit_ref.md @@ -0,0 +1,15 @@ +Returns `None` if the pointer is null, or else returns a shared reference to +the value wrapped in `Some`. In contrast to [`as_ref`], this does not require +that the value has to be initialized. + +# Safety + +When calling this method, you have to ensure that *either* the pointer is null *or* +the pointer is [convertible to a reference](crate::ptr#pointer-to-reference-conversion). +Note that because the created reference is to `MaybeUninit`, the +source pointer can point to uninitialized memory. + +# Panics during const evaluation + +This method will panic during const evaluation if the pointer cannot be +determined to be null or not. See [`is_null`] for more information. diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index efe1031b79ca..62cada51259e 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -133,28 +133,9 @@ impl *mut T { self as _ } - /// Gets the "address" portion of the pointer. + #[doc = include_str!("./docs/addr.md")] /// - /// This is similar to `self as usize`, except that the [provenance][crate::ptr#provenance] of - /// the pointer is discarded and not [exposed][crate::ptr#exposed-provenance]. This means that - /// casting the returned address back to a pointer yields a [pointer without - /// provenance][without_provenance_mut], which is undefined behavior to dereference. To properly - /// restore the lost information and obtain a dereferenceable pointer, use - /// [`with_addr`][pointer::with_addr] or [`map_addr`][pointer::map_addr]. - /// - /// If using those APIs is not possible because there is no way to preserve a pointer with the - /// required provenance, then Strict Provenance might not be for you. Use pointer-integer casts - /// or [`expose_provenance`][pointer::expose_provenance] and [`with_exposed_provenance`][with_exposed_provenance] - /// instead. However, note that this makes your code less portable and less amenable to tools - /// that check for compliance with the Rust memory model. - /// - /// On most platforms this will produce a value with the same bytes as the original - /// pointer, because all the bytes are dedicated to describing the address. - /// Platforms which need to store additional information in the pointer may - /// perform a change of representation to produce a value containing only the address - /// portion of the pointer. What that means is up to the platform to define. - /// - /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. + /// [without_provenance]: without_provenance_mut #[must_use] #[inline(always)] #[stable(feature = "strict_provenance", since = "1.84.0")] @@ -241,26 +222,16 @@ impl *mut T { (self.cast(), super::metadata(self)) } - /// Returns `None` if the pointer is null, or else returns a shared reference to - /// the value wrapped in `Some`. If the value may be uninitialized, [`as_uninit_ref`] - /// must be used instead. + #[doc = include_str!("./docs/as_ref.md")] /// - /// For the mutable counterpart see [`as_mut`]. + /// ``` + /// let ptr: *mut u8 = &mut 10u8 as *mut u8; /// - /// [`as_uninit_ref`]: pointer#method.as_uninit_ref-1 - /// [`as_mut`]: #method.as_mut - /// - /// # Safety - /// - /// When calling this method, you have to ensure that *either* the pointer is null *or* - /// the pointer is [convertible to a reference](crate::ptr#pointer-to-reference-conversion). - /// - /// # Panics during const evaluation - /// - /// This method will panic during const evaluation if the pointer cannot be - /// determined to be null or not. See [`is_null`] for more information. - /// - /// [`is_null`]: #method.is_null-1 + /// unsafe { + /// let val_back = &*ptr; + /// println!("We got back the value: {val_back}!"); + /// } + /// ``` /// /// # Examples /// @@ -274,20 +245,14 @@ impl *mut T { /// } /// ``` /// - /// # Null-unchecked version + /// # See Also /// - /// If you are sure the pointer can never be null and are looking for some kind of - /// `as_ref_unchecked` that returns the `&T` instead of `Option<&T>`, know that you can - /// dereference the pointer directly. + /// For the mutable counterpart see [`as_mut`]. /// - /// ``` - /// let ptr: *mut u8 = &mut 10u8 as *mut u8; - /// - /// unsafe { - /// let val_back = &*ptr; - /// println!("We got back the value: {val_back}!"); - /// } - /// ``` + /// [`is_null`]: #method.is_null-1 + /// [`as_uninit_ref`]: pointer#method.as_uninit_ref-1 + /// [`as_mut`]: #method.as_mut + #[stable(feature = "ptr_as_ref", since = "1.9.0")] #[rustc_const_stable(feature = "const_ptr_is_null", since = "1.84.0")] #[inline] @@ -330,28 +295,15 @@ impl *mut T { unsafe { &*self } } - /// Returns `None` if the pointer is null, or else returns a shared reference to - /// the value wrapped in `Some`. In contrast to [`as_ref`], this does not require - /// that the value has to be initialized. - /// - /// For the mutable counterpart see [`as_uninit_mut`]. - /// - /// [`as_ref`]: pointer#method.as_ref-1 - /// [`as_uninit_mut`]: #method.as_uninit_mut - /// - /// # Safety - /// - /// When calling this method, you have to ensure that *either* the pointer is null *or* - /// the pointer is [convertible to a reference](crate::ptr#pointer-to-reference-conversion). - /// Note that because the created reference is to `MaybeUninit`, the - /// source pointer can point to uninitialized memory. - /// - /// # Panics during const evaluation - /// - /// This method will panic during const evaluation if the pointer cannot be - /// determined to be null or not. See [`is_null`] for more information. + #[doc = include_str!("./docs/as_uninit_ref.md")] /// /// [`is_null`]: #method.is_null-1 + /// [`as_ref`]: pointer#method.as_ref-1 + /// + /// # See Also + /// For the mutable counterpart see [`as_uninit_mut`]. + /// + /// [`as_uninit_mut`]: #method.as_uninit_mut /// /// # Examples /// From ba874d38a561ec59d0f8e6fdde3d830e84f6da98 Mon Sep 17 00:00:00 2001 From: clubby789 Date: Wed, 10 Sep 2025 17:07:28 +0100 Subject: [PATCH 354/682] tidy: Print crate name on dependency error --- src/tools/tidy/src/deps.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 08c0d3ca7865..184f52a247f8 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -621,7 +621,8 @@ pub fn check(root: &Path, cargo: &Path, bless: bool, bad: &mut bool) { check_license_exceptions(&metadata, path, exceptions, bad); if let Some((crates, permitted_deps)) = crates_and_deps { - check_permitted_dependencies(&metadata, path, permitted_deps, crates, bad); + let descr = crates.get(0).unwrap_or(&path); + check_permitted_dependencies(&metadata, descr, permitted_deps, crates, bad); } if path == "library" { From adfb6ec089fbb41519acd530cbedd3477e84399c Mon Sep 17 00:00:00 2001 From: clubby789 Date: Wed, 10 Sep 2025 17:13:21 +0100 Subject: [PATCH 355/682] tidy: More accurate permitted dependencies location --- src/tools/tidy/src/deps.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 184f52a247f8..9acd445f3e23 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -261,7 +261,19 @@ const EXCEPTIONS_UEFI_QEMU_TEST: ExceptionList = &[ ("r-efi", "MIT OR Apache-2.0 OR LGPL-2.1-or-later"), // LGPL is not acceptable, but we use it under MIT OR Apache-2.0 ]; -const PERMITTED_DEPS_LOCATION: &str = concat!(file!(), ":", line!()); +struct ListLocation { + path: &'static str, + line: u32, +} + +/// Creates a [`ListLocation`] for the current location (with an additional offset to the actual list start); +macro_rules! location { + (+ $offset:literal) => { + ListLocation { path: file!(), line: line!() + $offset } + }; +} + +const PERMITTED_RUSTC_DEPS_LOCATION: ListLocation = location!(+6); /// Crates rustc is allowed to depend on. Avoid adding to the list if possible. /// @@ -930,7 +942,8 @@ fn check_permitted_dependencies( } if has_permitted_dep_error { - eprintln!("Go to `{PERMITTED_DEPS_LOCATION}` for the list."); + let ListLocation { path, line } = PERMITTED_RUSTC_DEPS_LOCATION; + eprintln!("Go to `{path}:{line}` for the list."); } } From 11a22b30904fb6e0fd41d9004b3a88e985ec8455 Mon Sep 17 00:00:00 2001 From: clubby789 Date: Wed, 10 Sep 2025 17:19:51 +0100 Subject: [PATCH 356/682] tidy: Add specific line info for allowed dependencies --- src/tools/tidy/src/deps.rs | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 9acd445f3e23..290f0ea31341 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -48,6 +48,7 @@ const LICENSES: &[&str] = &[ type ExceptionList = &'static [(&'static str, &'static str)]; +#[derive(Clone, Copy)] pub(crate) struct WorkspaceInfo<'a> { /// Path to the directory containing the workspace root Cargo.toml file. pub(crate) path: &'a str, @@ -56,7 +57,8 @@ pub(crate) struct WorkspaceInfo<'a> { /// Optionally: /// * A list of crates for which dependencies need to be explicitly allowed. /// * The list of allowed dependencies. - pub(crate) crates_and_deps: Option<(&'a [&'a str], &'a [&'a str])>, + /// * The source code location of the allowed dependencies list + crates_and_deps: Option<(&'a [&'a str], &'a [&'a str], ListLocation)>, /// Submodules required for the workspace pub(crate) submodules: &'a [&'a str], } @@ -65,7 +67,7 @@ impl<'a> WorkspaceInfo<'a> { const fn new( path: &'a str, exceptions: ExceptionList, - crates_and_deps: Option<(&'a [&str], &'a [&str])>, + crates_and_deps: Option<(&'a [&str], &'a [&str], ListLocation)>, submodules: &'a [&str], ) -> Self { Self { path, exceptions, crates_and_deps, submodules } @@ -76,18 +78,27 @@ impl<'a> WorkspaceInfo<'a> { // FIXME auto detect all cargo workspaces pub(crate) const WORKSPACES: &[WorkspaceInfo<'static>] = &[ // The root workspace has to be first for check_rustfix to work. - WorkspaceInfo::new(".", EXCEPTIONS, Some((&["rustc-main"], PERMITTED_RUSTC_DEPENDENCIES)), &[]), + WorkspaceInfo::new( + ".", + EXCEPTIONS, + Some((&["rustc-main"], PERMITTED_RUSTC_DEPENDENCIES, PERMITTED_RUSTC_DEPS_LOCATION)), + &[], + ), WorkspaceInfo::new( "library", EXCEPTIONS_STDLIB, - Some((&["sysroot"], PERMITTED_STDLIB_DEPENDENCIES)), + Some((&["sysroot"], PERMITTED_STDLIB_DEPENDENCIES, PERMITTED_STDLIB_DEPS_LOCATION)), &[], ), // Outside of the alphabetical section because rustfmt formats it using multiple lines. WorkspaceInfo::new( "compiler/rustc_codegen_cranelift", EXCEPTIONS_CRANELIFT, - Some((&["rustc_codegen_cranelift"], PERMITTED_CRANELIFT_DEPENDENCIES)), + Some(( + &["rustc_codegen_cranelift"], + PERMITTED_CRANELIFT_DEPENDENCIES, + PERMITTED_CRANELIFT_DEPS_LOCATION, + )), &[], ), // tidy-alphabetical-start @@ -261,6 +272,7 @@ const EXCEPTIONS_UEFI_QEMU_TEST: ExceptionList = &[ ("r-efi", "MIT OR Apache-2.0 OR LGPL-2.1-or-later"), // LGPL is not acceptable, but we use it under MIT OR Apache-2.0 ]; +#[derive(Clone, Copy)] struct ListLocation { path: &'static str, line: u32, @@ -499,6 +511,8 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ // tidy-alphabetical-end ]; +const PERMITTED_STDLIB_DEPS_LOCATION: ListLocation = location!(+2); + const PERMITTED_STDLIB_DEPENDENCIES: &[&str] = &[ // tidy-alphabetical-start "addr2line", @@ -540,6 +554,8 @@ const PERMITTED_STDLIB_DEPENDENCIES: &[&str] = &[ // tidy-alphabetical-end ]; +const PERMITTED_CRANELIFT_DEPS_LOCATION: ListLocation = location!(+2); + const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[ // tidy-alphabetical-start "allocator-api2", @@ -632,9 +648,9 @@ pub fn check(root: &Path, cargo: &Path, bless: bool, bad: &mut bool) { let metadata = t!(cmd.exec()); check_license_exceptions(&metadata, path, exceptions, bad); - if let Some((crates, permitted_deps)) = crates_and_deps { + if let Some((crates, permitted_deps, location)) = crates_and_deps { let descr = crates.get(0).unwrap_or(&path); - check_permitted_dependencies(&metadata, descr, permitted_deps, crates, bad); + check_permitted_dependencies(&metadata, descr, permitted_deps, crates, location, bad); } if path == "library" { @@ -882,6 +898,7 @@ fn check_permitted_dependencies( descr: &str, permitted_dependencies: &[&'static str], restricted_dependency_crates: &[&'static str], + permitted_location: ListLocation, bad: &mut bool, ) { let mut has_permitted_dep_error = false; @@ -942,8 +959,7 @@ fn check_permitted_dependencies( } if has_permitted_dep_error { - let ListLocation { path, line } = PERMITTED_RUSTC_DEPS_LOCATION; - eprintln!("Go to `{path}:{line}` for the list."); + eprintln!("Go to `{}:{}` for the list.", permitted_location.path, permitted_location.line); } } From 5a36fe23879ee8dde1326bcf08f0016af6ccd896 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 10 Sep 2025 15:44:42 +0200 Subject: [PATCH 357/682] Improve suggestion in case a bare URL is surrounded by brackets --- src/librustdoc/passes/lint/bare_urls.rs | 53 +++++++++++++++++++------ 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/src/librustdoc/passes/lint/bare_urls.rs b/src/librustdoc/passes/lint/bare_urls.rs index f70bdf4e4fe3..ffa160beae30 100644 --- a/src/librustdoc/passes/lint/bare_urls.rs +++ b/src/librustdoc/passes/lint/bare_urls.rs @@ -17,7 +17,10 @@ use crate::core::DocContext; use crate::html::markdown::main_body_opts; pub(super) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: &str) { - let report_diag = |cx: &DocContext<'_>, msg: &'static str, range: Range| { + let report_diag = |cx: &DocContext<'_>, + msg: &'static str, + range: Range, + without_brackets: Option<&str>| { let maybe_sp = source_span_for_markdown_range(cx.tcx, dox, &range, &item.attrs.doc_strings) .map(|(sp, _)| sp); let sp = maybe_sp.unwrap_or_else(|| item.attr_span(cx.tcx)); @@ -27,14 +30,22 @@ pub(super) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: & // The fallback of using the attribute span is suitable for // highlighting where the error is, but not for placing the < and > if let Some(sp) = maybe_sp { - lint.multipart_suggestion( - "use an automatic link instead", - vec![ - (sp.shrink_to_lo(), "<".to_string()), - (sp.shrink_to_hi(), ">".to_string()), - ], - Applicability::MachineApplicable, - ); + if let Some(without_brackets) = without_brackets { + lint.multipart_suggestion( + "use an automatic link instead", + vec![(sp, format!("<{without_brackets}>"))], + Applicability::MachineApplicable, + ); + } else { + lint.multipart_suggestion( + "use an automatic link instead", + vec![ + (sp.shrink_to_lo(), "<".to_string()), + (sp.shrink_to_hi(), ">".to_string()), + ], + Applicability::MachineApplicable, + ); + } } }); }; @@ -43,7 +54,7 @@ pub(super) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: & while let Some((event, range)) = p.next() { match event { - Event::Text(s) => find_raw_urls(cx, &s, range, &report_diag), + Event::Text(s) => find_raw_urls(cx, dox, &s, range, &report_diag), // We don't want to check the text inside code blocks or links. Event::Start(tag @ (Tag::CodeBlock(_) | Tag::Link { .. })) => { for (event, _) in p.by_ref() { @@ -67,25 +78,41 @@ static URL_REGEX: LazyLock = LazyLock::new(|| { r"https?://", // url scheme r"([-a-zA-Z0-9@:%._\+~#=]{2,256}\.)+", // one or more subdomains r"[a-zA-Z]{2,63}", // root domain - r"\b([-a-zA-Z0-9@:%_\+.~#?&/=]*)" // optional query or url fragments + r"\b([-a-zA-Z0-9@:%_\+.~#?&/=]*)", // optional query or url fragments )) .expect("failed to build regex") }); fn find_raw_urls( cx: &DocContext<'_>, + whole_text: &str, text: &str, range: Range, - f: &impl Fn(&DocContext<'_>, &'static str, Range), + f: &impl Fn(&DocContext<'_>, &'static str, Range, Option<&str>), ) { trace!("looking for raw urls in {text}"); // For now, we only check "full" URLs (meaning, starting with "http://" or "https://"). for match_ in URL_REGEX.find_iter(text) { let url_range = match_.range(); + let mut without_brackets = None; + let mut extra_range = 0; + // If the whole text is contained inside `[]`, then we need to replace the brackets and + // not just add `<>`. + if whole_text[..range.start + url_range.start].ends_with('[') + && range.start + url_range.end <= whole_text.len() + && whole_text[range.start + url_range.end..].starts_with(']') + { + extra_range = 1; + without_brackets = Some(match_.as_str()); + } f( cx, "this URL is not a hyperlink", - Range { start: range.start + url_range.start, end: range.start + url_range.end }, + Range { + start: range.start + url_range.start - extra_range, + end: range.start + url_range.end + extra_range, + }, + without_brackets, ); } } From 3205e4db0f9278ce06240b9de20d0802afef1de9 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 10 Sep 2025 15:44:59 +0200 Subject: [PATCH 358/682] Add new ui tests for `rustdoc::bare_urls` --- tests/rustdoc-ui/lints/bare-urls.fixed | 14 +++++ tests/rustdoc-ui/lints/bare-urls.rs | 14 +++++ tests/rustdoc-ui/lints/bare-urls.stderr | 70 ++++++++++++++++++++++++- 3 files changed, 97 insertions(+), 1 deletion(-) diff --git a/tests/rustdoc-ui/lints/bare-urls.fixed b/tests/rustdoc-ui/lints/bare-urls.fixed index a91573146b82..ac63e291c5b0 100644 --- a/tests/rustdoc-ui/lints/bare-urls.fixed +++ b/tests/rustdoc-ui/lints/bare-urls.fixed @@ -68,3 +68,17 @@ pub mod foo { /// https://somewhere.com/a?hello=12&bye=11#xyz pub fn bar() {} } + +/// +//~^ ERROR this URL is not a hyperlink +/// [ ] +//~^ ERROR this URL is not a hyperlink +/// [ ] +//~^ ERROR this URL is not a hyperlink +/// [ ] +//~^ ERROR this URL is not a hyperlink +/// [ +//~^ ERROR this URL is not a hyperlink +/// ] +//~^ ERROR this URL is not a hyperlink +pub fn lint_with_brackets() {} diff --git a/tests/rustdoc-ui/lints/bare-urls.rs b/tests/rustdoc-ui/lints/bare-urls.rs index 5b008cdafa23..a70a3ec822e4 100644 --- a/tests/rustdoc-ui/lints/bare-urls.rs +++ b/tests/rustdoc-ui/lints/bare-urls.rs @@ -68,3 +68,17 @@ pub mod foo { /// https://somewhere.com/a?hello=12&bye=11#xyz pub fn bar() {} } + +/// [https://bloob.blob] +//~^ ERROR this URL is not a hyperlink +/// [ https://bloob.blob ] +//~^ ERROR this URL is not a hyperlink +/// [ https://bloob.blob] +//~^ ERROR this URL is not a hyperlink +/// [https://bloob.blob ] +//~^ ERROR this URL is not a hyperlink +/// [https://bloob.blob +//~^ ERROR this URL is not a hyperlink +/// https://bloob.blob] +//~^ ERROR this URL is not a hyperlink +pub fn lint_with_brackets() {} diff --git a/tests/rustdoc-ui/lints/bare-urls.stderr b/tests/rustdoc-ui/lints/bare-urls.stderr index e1108c7e7f84..fc3c642f5aac 100644 --- a/tests/rustdoc-ui/lints/bare-urls.stderr +++ b/tests/rustdoc-ui/lints/bare-urls.stderr @@ -243,5 +243,73 @@ help: use an automatic link instead LL | #[doc = ""] | + + -error: aborting due to 20 previous errors +error: this URL is not a hyperlink + --> $DIR/bare-urls.rs:72:5 + | +LL | /// [https://bloob.blob] + | ^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + | + = note: bare URLs are not automatically turned into clickable links + +error: this URL is not a hyperlink + --> $DIR/bare-urls.rs:74:7 + | +LL | /// [ https://bloob.blob ] + | ^^^^^^^^^^^^^^^^^^ + | + = note: bare URLs are not automatically turned into clickable links +help: use an automatic link instead + | +LL | /// [ ] + | + + + +error: this URL is not a hyperlink + --> $DIR/bare-urls.rs:76:7 + | +LL | /// [ https://bloob.blob] + | ^^^^^^^^^^^^^^^^^^ + | + = note: bare URLs are not automatically turned into clickable links +help: use an automatic link instead + | +LL | /// [ ] + | + + + +error: this URL is not a hyperlink + --> $DIR/bare-urls.rs:78:6 + | +LL | /// [https://bloob.blob ] + | ^^^^^^^^^^^^^^^^^^ + | + = note: bare URLs are not automatically turned into clickable links +help: use an automatic link instead + | +LL | /// [ ] + | + + + +error: this URL is not a hyperlink + --> $DIR/bare-urls.rs:80:6 + | +LL | /// [https://bloob.blob + | ^^^^^^^^^^^^^^^^^^ + | + = note: bare URLs are not automatically turned into clickable links +help: use an automatic link instead + | +LL | /// [ + | + + + +error: this URL is not a hyperlink + --> $DIR/bare-urls.rs:82:5 + | +LL | /// https://bloob.blob] + | ^^^^^^^^^^^^^^^^^^ + | + = note: bare URLs are not automatically turned into clickable links +help: use an automatic link instead + | +LL | /// ] + | + + + +error: aborting due to 26 previous errors From 7966ae0f3352894834a316bea9fd45068f94d937 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 10 Sep 2025 18:54:38 +0200 Subject: [PATCH 359/682] Simplify code for `find_raw_urls` --- src/librustdoc/passes/lint/bare_urls.rs | 28 ++++++++++--------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/src/librustdoc/passes/lint/bare_urls.rs b/src/librustdoc/passes/lint/bare_urls.rs index ffa160beae30..06256d611512 100644 --- a/src/librustdoc/passes/lint/bare_urls.rs +++ b/src/librustdoc/passes/lint/bare_urls.rs @@ -85,7 +85,7 @@ static URL_REGEX: LazyLock = LazyLock::new(|| { fn find_raw_urls( cx: &DocContext<'_>, - whole_text: &str, + dox: &str, text: &str, range: Range, f: &impl Fn(&DocContext<'_>, &'static str, Range, Option<&str>), @@ -93,26 +93,20 @@ fn find_raw_urls( trace!("looking for raw urls in {text}"); // For now, we only check "full" URLs (meaning, starting with "http://" or "https://"). for match_ in URL_REGEX.find_iter(text) { - let url_range = match_.range(); + let mut url_range = match_.range(); + url_range.start += range.start; + url_range.end += range.start; let mut without_brackets = None; - let mut extra_range = 0; - // If the whole text is contained inside `[]`, then we need to replace the brackets and + // If the link is contained inside `[]`, then we need to replace the brackets and // not just add `<>`. - if whole_text[..range.start + url_range.start].ends_with('[') - && range.start + url_range.end <= whole_text.len() - && whole_text[range.start + url_range.end..].starts_with(']') + if dox[..url_range.start].ends_with('[') + && url_range.end <= dox.len() + && dox[url_range.end..].starts_with(']') { - extra_range = 1; + url_range.start -= 1; + url_range.end += 1; without_brackets = Some(match_.as_str()); } - f( - cx, - "this URL is not a hyperlink", - Range { - start: range.start + url_range.start - extra_range, - end: range.start + url_range.end + extra_range, - }, - without_brackets, - ); + f(cx, "this URL is not a hyperlink", url_range, without_brackets); } } From dbd3ef1332574ef074c147f3b9e3e74dd29cd7b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Sun, 24 Aug 2025 14:42:44 +0200 Subject: [PATCH 360/682] fixup no_{core,std} handling code --- .../src/attributes/crate_level.rs | 9 +- src/tools/clippy/clippy_utils/src/lib.rs | 10 +- tests/ui/attributes/malformed-no-std.rs | 25 ++++ tests/ui/attributes/malformed-no-std.stderr | 86 ++++++++++++ .../issue-43106-gating-of-builtin-attrs.rs | 12 +- ...issue-43106-gating-of-builtin-attrs.stderr | 131 ++++++++++-------- .../lint/unused/unused-attr-duplicate.stderr | 24 ++-- tests/ui/unpretty/exhaustive.hir.stdout | 2 +- 8 files changed, 207 insertions(+), 92 deletions(-) create mode 100644 tests/ui/attributes/malformed-no-std.rs create mode 100644 tests/ui/attributes/malformed-no-std.stderr diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index 3ec847bcaae2..0b2c05482bfa 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -182,19 +182,20 @@ pub(crate) struct NoCoreParser; impl NoArgsAttributeParser for NoCoreParser { const PATH: &[Symbol] = &[sym::no_core]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - // FIXME: recursion limit is allowed on all targets and ignored, - // even though it should only be valid on crates of course + // because it's a crate-level attribute, we already warn about it. + // Putting target limitations here would give duplicate warnings const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoCore; const TYPE: AttributeType = AttributeType::CrateLevel; } pub(crate) struct NoStdParser; + impl NoArgsAttributeParser for NoStdParser { const PATH: &[Symbol] = &[sym::no_std]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - // FIXME: recursion limit is allowed on all targets and ignored, - // even though it should only be valid on crates of course + // because it's a crate-level attribute, we already warn about it. + // Putting target limitations here would give duplicate warnings const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoStd; const TYPE: AttributeType = AttributeType::CrateLevel; diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 14b64eb4d54e..e1077bdf4b1a 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -2133,17 +2133,11 @@ pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> { } pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool { - cx.tcx - .hir_attrs(hir::CRATE_HIR_ID) - .iter() - .any(|attr| attr.has_name(sym::no_std)) + find_attr!(cx.tcx.hir_attrs(hir::CRATE_HIR_ID), AttributeKind::NoStd(..)) } pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool { - cx.tcx - .hir_attrs(hir::CRATE_HIR_ID) - .iter() - .any(|attr| attr.has_name(sym::no_core)) + find_attr!(cx.tcx.hir_attrs(hir::CRATE_HIR_ID), AttributeKind::NoCore(..)) } /// Check if parent of a hir node is a trait implementation block. diff --git a/tests/ui/attributes/malformed-no-std.rs b/tests/ui/attributes/malformed-no-std.rs new file mode 100644 index 000000000000..528ecb549b0e --- /dev/null +++ b/tests/ui/attributes/malformed-no-std.rs @@ -0,0 +1,25 @@ +#![feature(no_core)] +// these all still apply no_std and then later error +#![no_std = "foo"] +//~^ ERROR malformed `no_std` attribute input +#![no_std("bar")] +//~^ ERROR malformed `no_std` attribute input +#![no_std(foo = "bar")] +//~^ ERROR malformed `no_std` attribute input +#![no_core = "foo"] +//~^ ERROR malformed `no_core` attribute input +#![no_core("bar")] +//~^ ERROR malformed `no_core` attribute input +#![no_core(foo = "bar")] +//~^ ERROR malformed `no_core` attribute input + +#[deny(unused_attributes)] +#[no_std] +//~^ ERROR crate-level attribute should be an inner attribute: add an exclamation mark: +#[no_core] +//~^ ERROR crate-level attribute should be an inner attribute: add an exclamation mark: +// to fix compilation +extern crate core; +extern crate std; + +fn main() {} diff --git a/tests/ui/attributes/malformed-no-std.stderr b/tests/ui/attributes/malformed-no-std.stderr new file mode 100644 index 000000000000..322d5f03e41c --- /dev/null +++ b/tests/ui/attributes/malformed-no-std.stderr @@ -0,0 +1,86 @@ +error[E0565]: malformed `no_std` attribute input + --> $DIR/malformed-no-std.rs:3:1 + | +LL | #![no_std = "foo"] + | ^^^^^^^^^^-------^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#![no_std]` + +error[E0565]: malformed `no_std` attribute input + --> $DIR/malformed-no-std.rs:5:1 + | +LL | #![no_std("bar")] + | ^^^^^^^^^-------^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#![no_std]` + +error[E0565]: malformed `no_std` attribute input + --> $DIR/malformed-no-std.rs:7:1 + | +LL | #![no_std(foo = "bar")] + | ^^^^^^^^^-------------^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#![no_std]` + +error[E0565]: malformed `no_core` attribute input + --> $DIR/malformed-no-std.rs:9:1 + | +LL | #![no_core = "foo"] + | ^^^^^^^^^^^-------^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#![no_core]` + +error[E0565]: malformed `no_core` attribute input + --> $DIR/malformed-no-std.rs:11:1 + | +LL | #![no_core("bar")] + | ^^^^^^^^^^-------^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#![no_core]` + +error[E0565]: malformed `no_core` attribute input + --> $DIR/malformed-no-std.rs:13:1 + | +LL | #![no_core(foo = "bar")] + | ^^^^^^^^^^-------------^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#![no_core]` + +error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![no_std]` + --> $DIR/malformed-no-std.rs:17:1 + | +LL | #[no_std] + | ^^^^^^^^^ + | +note: This attribute does not have an `!`, which means it is applied to this extern crate + --> $DIR/malformed-no-std.rs:22:1 + | +LL | extern crate core; + | ^^^^^^^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/malformed-no-std.rs:16:8 + | +LL | #[deny(unused_attributes)] + | ^^^^^^^^^^^^^^^^^ + +error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![no_core]` + --> $DIR/malformed-no-std.rs:19:1 + | +LL | #[no_core] + | ^^^^^^^^^^ + | +note: This attribute does not have an `!`, which means it is applied to this extern crate + --> $DIR/malformed-no-std.rs:22:1 + | +LL | extern crate core; + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0565`. diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs index c2653dd82a9f..546aa4052d33 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs @@ -539,26 +539,26 @@ mod macro_escape { #[no_std] //~^ WARN crate-level attribute should be an inner attribute -//~| HELP add a `!` mod no_std { + //~^ NOTE This attribute does not have an `!`, which means it is applied to this module mod inner { #![no_std] } -//~^ WARN crate-level attribute should be in the root module +//~^ WARN the `#![no_std]` attribute can only be used at the crate root #[no_std] fn f() { } //~^ WARN crate-level attribute should be an inner attribute - //~| HELP add a `!` + //~| NOTE This attribute does not have an `!`, which means it is applied to this function #[no_std] struct S; //~^ WARN crate-level attribute should be an inner attribute - //~| HELP add a `!` + //~| NOTE This attribute does not have an `!`, which means it is applied to this struct #[no_std] type T = S; //~^ WARN crate-level attribute should be an inner attribute - //~| HELP add a `!` + //~| NOTE This attribute does not have an `!`, which means it is applied to this type alias #[no_std] impl S { } //~^ WARN crate-level attribute should be an inner attribute - //~| HELP add a `!` + //~| NOTE This attribute does not have an `!`, which means it is applied to this implementation block } // At time of authorship, #[proc_macro_derive = "2500"] signals error diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr index 4a3520972bf5..3c835be5cffa 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr @@ -209,17 +209,6 @@ help: add a `!` LL | #![reexport_test_harness_main = "2900"] | + -warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:540:1 - | -LL | #[no_std] - | ^^^^^^^^^ - | -help: add a `!` - | -LL | #![no_std] - | + - warning: attribute should be applied to an `extern` block with non-Rust ABI --> $DIR/issue-43106-gating-of-builtin-attrs.rs:695:1 | @@ -387,56 +376,6 @@ help: add a `!` LL | #![reexport_test_harness_main = "2900"] impl S { } | + -warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:544:17 - | -LL | mod inner { #![no_std] } - | ^^^^^^^^^^ - -warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:547:5 - | -LL | #[no_std] fn f() { } - | ^^^^^^^^^ - | -help: add a `!` - | -LL | #![no_std] fn f() { } - | + - -warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:551:5 - | -LL | #[no_std] struct S; - | ^^^^^^^^^ - | -help: add a `!` - | -LL | #![no_std] struct S; - | + - -warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:555:5 - | -LL | #[no_std] type T = S; - | ^^^^^^^^^ - | -help: add a `!` - | -LL | #![no_std] type T = S; - | + - -warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:559:5 - | -LL | #[no_std] impl S { } - | ^^^^^^^^^ - | -help: add a `!` - | -LL | #![no_std] impl S { } - | + - warning: attribute should be applied to an `extern` block with non-Rust ABI --> $DIR/issue-43106-gating-of-builtin-attrs.rs:701:17 | @@ -1095,6 +1034,76 @@ LL | #[macro_escape] impl S { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = help: `#[macro_escape]` can be applied to modules, extern crates, and crates +warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![no_std]` + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:540:1 + | +LL | #[no_std] + | ^^^^^^^^^ + | +note: This attribute does not have an `!`, which means it is applied to this module + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:542:1 + | +LL | / mod no_std { +LL | | +LL | | mod inner { #![no_std] } +... | +LL | | } + | |_^ + +warning: the `#![no_std]` attribute can only be used at the crate root + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:544:17 + | +LL | mod inner { #![no_std] } + | ^^^^^^^^^^ + +warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![no_std]` + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:547:5 + | +LL | #[no_std] fn f() { } + | ^^^^^^^^^ + | +note: This attribute does not have an `!`, which means it is applied to this function + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:547:15 + | +LL | #[no_std] fn f() { } + | ^^^^^^^^^^ + +warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![no_std]` + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:551:5 + | +LL | #[no_std] struct S; + | ^^^^^^^^^ + | +note: This attribute does not have an `!`, which means it is applied to this struct + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:551:15 + | +LL | #[no_std] struct S; + | ^^^^^^^^^ + +warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![no_std]` + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:555:5 + | +LL | #[no_std] type T = S; + | ^^^^^^^^^ + | +note: This attribute does not have an `!`, which means it is applied to this type alias + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:555:15 + | +LL | #[no_std] type T = S; + | ^^^^^^^^^^^ + +warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![no_std]` + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:559:5 + | +LL | #[no_std] impl S { } + | ^^^^^^^^^ + | +note: This attribute does not have an `!`, which means it is applied to this implementation block + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:559:15 + | +LL | #[no_std] impl S { } + | ^^^^^^^^^^ + warning: `#[cold]` attribute cannot be used on modules --> $DIR/issue-43106-gating-of-builtin-attrs.rs:581:1 | diff --git a/tests/ui/lint/unused/unused-attr-duplicate.stderr b/tests/ui/lint/unused/unused-attr-duplicate.stderr index 076a08ac6f24..3a3b450f3c55 100644 --- a/tests/ui/lint/unused/unused-attr-duplicate.stderr +++ b/tests/ui/lint/unused/unused-attr-duplicate.stderr @@ -28,18 +28,6 @@ note: attribute also specified here LL | #[no_link] | ^^^^^^^^^^ -error: unused attribute - --> $DIR/unused-attr-duplicate.rs:27:1 - | -LL | #![no_std] - | ^^^^^^^^^^ help: remove this attribute - | -note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:26:1 - | -LL | #![no_std] - | ^^^^^^^^^^ - error: unused attribute --> $DIR/unused-attr-duplicate.rs:31:1 | @@ -304,6 +292,18 @@ LL | #![type_length_limit = "1048576"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +error: unused attribute + --> $DIR/unused-attr-duplicate.rs:27:1 + | +LL | #![no_std] + | ^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/unused-attr-duplicate.rs:26:1 + | +LL | #![no_std] + | ^^^^^^^^^^ + error: unused attribute --> $DIR/unused-attr-duplicate.rs:29:1 | diff --git a/tests/ui/unpretty/exhaustive.hir.stdout b/tests/ui/unpretty/exhaustive.hir.stdout index 924fb98ae18a..96d85d1e7c14 100644 --- a/tests/ui/unpretty/exhaustive.hir.stdout +++ b/tests/ui/unpretty/exhaustive.hir.stdout @@ -55,7 +55,7 @@ use self::prelude::*; */ #[doc = "inner doc attribute"] #[allow(dead_code, unused_variables)] -#[no_std] +#[attr = NoStd] mod attributes { /// outer single-line doc comment From 32722771ec2735038567545bad598d45291759a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Sun, 24 Aug 2025 14:42:44 +0200 Subject: [PATCH 361/682] fixup no_{core,std} handling code --- clippy_utils/src/lib.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 14b64eb4d54e..e1077bdf4b1a 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -2133,17 +2133,11 @@ pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> { } pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool { - cx.tcx - .hir_attrs(hir::CRATE_HIR_ID) - .iter() - .any(|attr| attr.has_name(sym::no_std)) + find_attr!(cx.tcx.hir_attrs(hir::CRATE_HIR_ID), AttributeKind::NoStd(..)) } pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool { - cx.tcx - .hir_attrs(hir::CRATE_HIR_ID) - .iter() - .any(|attr| attr.has_name(sym::no_core)) + find_attr!(cx.tcx.hir_attrs(hir::CRATE_HIR_ID), AttributeKind::NoCore(..)) } /// Check if parent of a hir node is a trait implementation block. From faf0e14eedee063f7861eb55767099b42c6c5d62 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Sun, 7 Sep 2025 18:30:07 +0100 Subject: [PATCH 362/682] Update the arm-* and aarch64-* platform docs. The Rust Embedded Devices Working Group (wg-embedded) Arm Team (t-arm) agreed to listed as maintainers of: * aarch64-unknown-none * aarch64-unknown-none-softfloat * armv7a-none-eabi * armv7r-none-eabi * armv7r-none-eabihf The aarch64-unknown-none* target didn't have a page so I added it. wg-embedded t-arm did not want to take over: * armebv7r-none-eabi * armebv7r-none-eabihf So I gave them their own target page. The current maintainer remains. --- src/doc/rustc/src/SUMMARY.md | 5 +- src/doc/rustc/src/platform-support.md | 6 +- .../platform-support/aarch64-unknown-none.md | 121 ++++++++++++++++++ .../src/platform-support/arm-none-eabi.md | 6 +- .../platform-support/armebv7r-none-eabi.md | 54 ++++++++ .../src/platform-support/armv7a-none-eabi.md | 51 ++++++++ .../src/platform-support/armv7r-none-eabi.md | 20 ++- .../platform-support/armv8r-none-eabihf.md | 18 ++- 8 files changed, 263 insertions(+), 18 deletions(-) create mode 100644 src/doc/rustc/src/platform-support/aarch64-unknown-none.md create mode 100644 src/doc/rustc/src/platform-support/armebv7r-none-eabi.md create mode 100644 src/doc/rustc/src/platform-support/armv7a-none-eabi.md diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 8e378e53e518..06de834a2f64 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -48,6 +48,7 @@ - [\*-apple-visionos](platform-support/apple-visionos.md) - [aarch64-nintendo-switch-freestanding](platform-support/aarch64-nintendo-switch-freestanding.md) - [aarch64-unknown-linux-musl](platform-support/aarch64-unknown-linux-musl.md) + - [aarch64-unknown-none{,-softfloat}](aarch64-unknown-none.md) - [aarch64_be-unknown-none-softfloat](platform-support/aarch64_be-unknown-none-softfloat.md) - [aarch64_be-unknown-linux-musl](platform-support/aarch64_be-unknown-linux-musl.md) - [amdgcn-amd-amdhsa](platform-support/amdgcn-amd-amdhsa.md) @@ -55,7 +56,9 @@ - [arm-none-eabi](platform-support/arm-none-eabi.md) - [armv4t-none-eabi](platform-support/armv4t-none-eabi.md) - [armv5te-none-eabi](platform-support/armv5te-none-eabi.md) - - [armv7r-none-eabi](platform-support/armv7r-none-eabi.md) + - [armv7a-none-eabi{,hf}](platform-support/armv7a-none-eabi.md) + - [armv7r-none-eabi{,hf}](platform-support/armv7r-none-eabi.md) + - [armebv7r-none-eabi{,hf}](platform-support/armebv7r-none-eabi.md) - [armv8r-none-eabihf](platform-support/armv8r-none-eabihf.md) - [thumbv6m-none-eabi](./platform-support/thumbv6m-none-eabi.md) - [thumbv7em-none-eabi\*](./platform-support/thumbv7em-none-eabi.md) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 6c5b48d8c8f5..3c91d1250577 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -156,15 +156,15 @@ target | std | notes `arm-unknown-linux-musleabi` | ✓ | Armv6 Linux with musl 1.2.3 `arm-unknown-linux-musleabihf` | ✓ | Armv6 Linux with musl 1.2.3, hardfloat [`arm64ec-pc-windows-msvc`](platform-support/arm64ec-pc-windows-msvc.md) | ✓ | Arm64EC Windows MSVC -[`armebv7r-none-eabi`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R, Big Endian -[`armebv7r-none-eabihf`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R, Big Endian, hardfloat +[`armebv7r-none-eabi`](platform-support/armebv7r-none-eabi.md) | * | Bare Armv7-R, Big Endian +[`armebv7r-none-eabihf`](platform-support/armebv7r-none-eabi.md) | * | Bare Armv7-R, Big Endian, hardfloat [`armv5te-unknown-linux-gnueabi`](platform-support/armv5te-unknown-linux-gnueabi.md) | ✓ | Armv5TE Linux (kernel 4.4+, glibc 2.23) `armv5te-unknown-linux-musleabi` | ✓ | Armv5TE Linux with musl 1.2.3 [`armv7-linux-androideabi`](platform-support/android.md) | ✓ | Armv7-A Android `armv7-unknown-linux-gnueabi` | ✓ | Armv7-A Linux (kernel 4.15+, glibc 2.27) `armv7-unknown-linux-musleabi` | ✓ | Armv7-A Linux with musl 1.2.3 `armv7-unknown-linux-musleabihf` | ✓ | Armv7-A Linux with musl 1.2.3, hardfloat -[`armv7a-none-eabi`](platform-support/arm-none-eabi.md) | * | Bare Armv7-A +[`armv7a-none-eabi`](platform-support/armv7a-none-eabi.md) | * | Bare Armv7-A [`armv7r-none-eabi`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R [`armv7r-none-eabihf`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R, hardfloat `i586-unknown-linux-gnu` | ✓ | 32-bit Linux (kernel 3.2+, glibc 2.17, original Pentium) [^x86_32-floats-x87] diff --git a/src/doc/rustc/src/platform-support/aarch64-unknown-none.md b/src/doc/rustc/src/platform-support/aarch64-unknown-none.md new file mode 100644 index 000000000000..0c1d47796198 --- /dev/null +++ b/src/doc/rustc/src/platform-support/aarch64-unknown-none.md @@ -0,0 +1,121 @@ +# `aarch64-unknown-none` + +**Tier: 2** + +Bare-metal target for CPUs in the Armv8-A architecture family, running in AArch64 mode. + +For the AArch32 mode carried over from Armv7-A, see +[`armv7a-none-eabi`](armv7a-none-eabi.md) instead. + +Processors in this family include the [Arm Cortex-A35, 53, 76, etc][aarch64-cpus]. + +[aarch64-cpus]: https://en.wikipedia.org/wiki/Comparison_of_ARM_processors#ARMv8-A + +## Target maintainers + +* [Rust Embedded Devices Working Group Arm Team] + +[Rust Embedded Devices Working Group Arm Team]: https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team + +## Target CPU and Target Feature options + +It is possible to tell Rust (or LLVM) that you have a specific model of Arm +processor, using the [`-Ctarget-cpu`][target-cpu] option. You can also control +whether Rust (or LLVM) will include instructions that target optional hardware +features, e.g. hardware floating-point, or Advanced SIMD operations, using +[`-Ctarget-feature`][target-feature]. + +It is important to note that selecting a *target-cpu* will typically enable +*all* the optional features available from Arm on that model of CPU and your +particular implementation of that CPU may not have those features available. +In that case, you can use `-Ctarget-feature=-option` to turn off the specific +CPU features you do not have available, leaving you with the optimized +instruction scheduling and support for the features you do have. More details +are available in the detailed target-specific documentation. + +

+ +Many target-features are currently unstable and subject to change, and +if you use them you should disassemble the compiler output and manually inspect +it to ensure only appropriate instructions for your CPU have been generated. + +
+ +If you wish to use the *target-cpu* and *target-feature* options, you can add +them to your `.cargo/config.toml` file alongside any other flags your project +uses (likely linker related ones): + +```toml +rustflags = [ + # Usual Arm bare-metal linker setup + "-Clink-arg=-Tlink.x", + "-Clink-arg=--nmagic", + # tell Rust we have a Cortex-A72 + "-Ctarget-cpu=cortex-a72", +] + +[build] +target = "aarch64-unknown-none" +``` + +[target-cpu]: https://doc.rust-lang.org/rustc/codegen-options/index.html#target-cpu +[target-feature]: https://doc.rust-lang.org/rustc/codegen-options/index.html#target-feature + +## Requirements + +These targets are cross-compiled and use static linking. + +By default, the `lld` linker included with Rust will be used; however, you may +want to use the GNU linker instead. This can be obtained for Windows/Mac/Linux +from the [Arm Developer Website][arm-gnu-toolchain], or possibly from your OS's +package manager. To use it, add the following to your `.cargo/config.toml`: + +```toml +[target.] +linker = "arm-none-eabi-ld" +``` + +The GNU linker can also be used by specifying `aarch64-none-gcc` as the +linker. This is needed when using GCC's link time optimization. + +These targets don't provide a linker script, so you'll need to bring your own +according to the specific device you are using. Pass +`-Clink-arg=-Tyour_script.ld` as a rustc argument to make the linker use +`your_script.ld` during linking. + +All AArch64 processors include an FPU. The difference between the `-none` and +`-none-softfloat` targets is whether the FPU is used for passing function arguments. +You may prefer the `-softfloat` target when writing a kernel or interfacing with +pre-compiled binaries that use the soft-float ABI. + +When using the hardfloat targets, the minimum floating-point features assumed +are those of the `fp-armv8`, which excludes NEON SIMD support. If your +processor supports a different set of floating-point features than the default +expectations of `fp-armv8`, then these should also be enabled or disabled as +needed with `-C target-feature=(+/-)`. For example, +`-Ctarget-feature=+neon-fp-armv8`. + +[arm-gnu-toolchain]: https://developer.arm.com/Tools%20and%20Software/GNU%20Toolchain + +## Testing + +This is a cross-compiled target that you will need to emulate during testing. + +The exact emulator that you'll need depends on the specific device you want to +run your code on. + +## Start-up and Low-Level Code + +The [Rust Embedded Devices Working Group Arm Team] maintain the +[`aarch64-cpu`], which may be useful for writing bare-metal code using this +target. + +The *TrustedFirmware* group also maintain [Rust crates for this +target](https://github.com/ArmFirmwareCrates). + +[`aarch64-cpu`]: https://docs.rs/aarch64-cpu + +## Cross-compilation toolchains and C code + +This target supports C code compiled with the `aarch64-unknown-none` target +triple and a suitable `-march` or `-mcpu` flag. diff --git a/src/doc/rustc/src/platform-support/arm-none-eabi.md b/src/doc/rustc/src/platform-support/arm-none-eabi.md index 9732df4be7f7..aaa80e429718 100644 --- a/src/doc/rustc/src/platform-support/arm-none-eabi.md +++ b/src/doc/rustc/src/platform-support/arm-none-eabi.md @@ -12,10 +12,10 @@ their own document. ### Tier 2 Target List - Arm A-Profile Architectures - - `armv7a-none-eabi` + - [`armv7a-none-eabi`](armv7a-none-eabi.md) - Arm R-Profile Architectures - [`armv7r-none-eabi` and `armv7r-none-eabihf`](armv7r-none-eabi.md) - - [`armebv7r-none-eabi` and `armebv7r-none-eabihf`](armv7r-none-eabi.md) + - [`armebv7r-none-eabi` and `armebv7r-none-eabihf`](armebv7r-none-eabi.md) - Arm M-Profile Architectures - [`thumbv6m-none-eabi`](thumbv6m-none-eabi.md) - [`thumbv7m-none-eabi`](thumbv7m-none-eabi.md) @@ -28,7 +28,7 @@ their own document. ### Tier 3 Target List - Arm A-Profile Architectures - - `armv7a-none-eabihf` + - [`armv7a-none-eabihf`](armv7a-none-eabi.md) - Arm R-Profile Architectures - [`armv8r-none-eabihf`](armv8r-none-eabihf.md) - Arm M-Profile Architectures diff --git a/src/doc/rustc/src/platform-support/armebv7r-none-eabi.md b/src/doc/rustc/src/platform-support/armebv7r-none-eabi.md new file mode 100644 index 000000000000..a5efd4adb973 --- /dev/null +++ b/src/doc/rustc/src/platform-support/armebv7r-none-eabi.md @@ -0,0 +1,54 @@ +# `armebv7r-none-eabi` and `armebv7r-none-eabihf` + +**Tier: 2** + +Bare-metal target for CPUs in the Armv7-R architecture family running in Big +Endian mode. These processors support dual ARM/Thumb mode, with ARM mode as +the default. + +**NOTE:** You should almost always prefer the [little-endian +versions](armv7r-none-eabi.md) of these target. Big Endian Arm systems are +highly unusual. + +Processors in this family include the [Arm Cortex-R4, 5, 7, and 8][cortex-r]. + +See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all +`arm-none-eabi` targets. + +[cortex-r]: https://en.wikipedia.org/wiki/ARM_Cortex-R + +## Target maintainers + +* [@chrisnc](https://github.com/chrisnc) + +## Requirements + +Note that some variants of the Cortex-R have both big-endian instructions and +data. This configuration is known as BE-32, while data-only big-endianness is +known as BE-8. To build programs for BE-32 processors, the GNU linker must be +used with the `-mbe32` option. See [ARM Cortex-R Series Programmer's Guide: +Endianness][endianness] for more details about different endian modes. + +When using the hardfloat targets, the minimum floating-point features assumed +are those of the `vfpv3-d16`, which includes single- and double-precision, with +16 double-precision registers. This floating-point unit appears in Cortex-R4F +and Cortex-R5F processors. See [VFP in the Cortex-R processors][vfp] +for more details on the possible FPU variants. + +If your processor supports a different set of floating-point features than the +default expectations of `vfpv3-d16`, then these should also be enabled or +disabled as needed with `-C target-feature=(+/-)`. + +[endianness]: https://developer.arm.com/documentation/den0042/a/Coding-for-Cortex-R-Processors/Endianness + +[vfp]: https://developer.arm.com/documentation/den0042/a/Floating-Point/Floating-point-basics-and-the-IEEE-754-standard/VFP-in-the-Cortex-R-processors + +## Start-up and Low-Level Code + +The [Rust Embedded Devices Working Group Arm Team] maintain the [`cortex-ar`] +and [`cortex-r-rt`] crates, which may be useful for writing bare-metal code +using this target. Those crates include several examples which run in QEMU and +build using these targets. + +[`cortex-ar`]: https://docs.rs/cortex-ar +[`cortex-r-rt`]: https://docs.rs/cortex-r-rt diff --git a/src/doc/rustc/src/platform-support/armv7a-none-eabi.md b/src/doc/rustc/src/platform-support/armv7a-none-eabi.md new file mode 100644 index 000000000000..9a1c6316c468 --- /dev/null +++ b/src/doc/rustc/src/platform-support/armv7a-none-eabi.md @@ -0,0 +1,51 @@ +# `armv7a-none-eabi` and `armv7a-none-eabihf` + +**Tier: 2** for `armv7a-none-eabi` +**Tier: 3** for `armv7a-none-eabihf` + +Bare-metal target for CPUs in the Armv7-A architecture family, supporting +dual ARM/Thumb mode, with ARM mode as the default. + +Note, this is for processors running in AArch32 mode. For the AArch64 mode +added in Armv8-A, see [`aarch64-unknown-none`](aarch64-unknown-none.md) instead. + +Processors in this family include the [Arm Cortex-A5, 8, 32, etc][cortex-a]. + +See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all +`arm-none-eabi` targets. + +[cortex-a]: https://en.wikipedia.org/wiki/ARM_Cortex-A + +## Target maintainers + +* [Rust Embedded Devices Working Group Arm Team] + +[Rust Embedded Devices Working Group Arm Team]: https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team + +## Requirements + +All Armv7-A processors include an FPU (a VFPv3 or a VFPv4). The difference +between the `-eabi` and `-eabihf` targets is whether the FPU is used for +passing function arguments. You may prefer the `-eabi` soft-float target when + +When using the hardfloat targets, the minimum floating-point features assumed +are those of the `vfpv3-d16`, which includes single- and double-precision, +with 16 double-precision registers. This floating-point unit appears in +Cortex-A8 and Cortex-A8 processors. See [VFP in the Cortex-A processors][vfp] +for more details on the possible FPU variants. + +If your processor supports a different set of floating-point features than the +default expectations of `vfpv3-d16`, then these should also be enabled or +disabled as needed with `-C target-feature=(+/-)`. + +[vfp]: https://developer.arm.com/documentation/den0013/0400/Floating-Point/Floating-point-basics-and-the-IEEE-754-standard/ARM-VFP + +## Start-up and Low-Level Code + +The [Rust Embedded Devices Working Group Arm Team] maintain the [`cortex-ar`] +and [`cortex-a-rt`] crates, which may be useful for writing bare-metal code +using this target. Those crates include several examples which run in QEMU and +build using these targets. + +[`cortex-ar`]: https://docs.rs/cortex-ar +[`cortex-a-rt`]: https://docs.rs/cortex-a-rt diff --git a/src/doc/rustc/src/platform-support/armv7r-none-eabi.md b/src/doc/rustc/src/platform-support/armv7r-none-eabi.md index 88b2689dcf0f..7d621e5c827d 100644 --- a/src/doc/rustc/src/platform-support/armv7r-none-eabi.md +++ b/src/doc/rustc/src/platform-support/armv7r-none-eabi.md @@ -1,4 +1,4 @@ -# `arm(eb)?v7r-none-eabi(hf)?` +# `armv7r-none-eabi` and `armv7r-none-eabihf` **Tier: 2** @@ -7,8 +7,6 @@ dual ARM/Thumb mode, with ARM mode as the default. Processors in this family include the [Arm Cortex-R4, 5, 7, and 8][cortex-r]. -The `eb` versions of this target generate code for big-endian processors. - See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all `arm-none-eabi` targets. @@ -16,7 +14,10 @@ See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all ## Target maintainers -[@chrisnc](https://github.com/chrisnc) +* [@chrisnc](https://github.com/chrisnc) +* [Rust Embedded Devices Working Group Arm Team] + +[Rust Embedded Devices Working Group Arm Team]: https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team ## Requirements @@ -41,7 +42,12 @@ disabled as needed with `-C target-feature=(+/-)`. [vfp]: https://developer.arm.com/documentation/den0042/a/Floating-Point/Floating-point-basics-and-the-IEEE-754-standard/VFP-in-the-Cortex-R-processors -## Cross-compilation toolchains and C code +## Start-up and Low-Level Code -This target supports C code compiled with the `arm-none-eabi` target triple and -`-march=armv7-r` or a suitable `-mcpu` flag. +The [Rust Embedded Devices Working Group Arm Team] maintain the [`cortex-ar`] +and [`cortex-r-rt`] crates, which may be useful for writing bare-metal code +using this target. Those crates include several examples which run in QEMU and +build using these targets. + +[`cortex-ar`]: https://docs.rs/cortex-ar +[`cortex-r-rt`]: https://docs.rs/cortex-r-rt diff --git a/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md b/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md index 569d8802ebeb..ed0102f48e08 100644 --- a/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md +++ b/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md @@ -16,7 +16,10 @@ See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all ## Target maintainers -[@chrisnc](https://github.com/chrisnc) +* [@chrisnc](https://github.com/chrisnc) +* [Rust Embedded Devices Working Group Arm Team] + +[Rust Embedded Devices Working Group Arm Team]: https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team ## Requirements @@ -34,7 +37,14 @@ Technical Reference Manual for more details. [fpu]: https://developer.arm.com/documentation/100026/0104/Advanced-SIMD-and-floating-point-support/About-the-Advanced-SIMD-and-floating-point-support -## Cross-compilation toolchains and C code +### Table of supported CPUs for `armv8r-none-eabihf` -This target supports C code compiled with the `arm-none-eabi` target triple and -`-march=armv8-r` or a suitable `-mcpu` flag. +| CPU | FPU | Neon | Target CPU | Target Features | +|:----------- | --- |:---- |:---------------- |:------------------ | +| Any | SP | No | None | None | +| Cortex-R52 | SP | No | `cortex-r52` | `-fp64,-d32,-neon` | +| Cortex-R52 | DP | No | `cortex-r52` | `-neon` | +| Cortex-R52 | DP | Yes | `cortex-r52` | None | +| Cortex-R52+ | SP | No | `cortex-r52plus` | `-fp64,-d32,-neon` | +| Cortex-R52+ | DP | No | `cortex-r52plus` | `-neon` | +| Cortex-R52+ | DP | Yes | `cortex-r52plus` | None | From f1abb70836aa2eee2119b0a9a23795883dc99e43 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Wed, 10 Sep 2025 20:30:41 +0100 Subject: [PATCH 363/682] Pick up changes from robamu that I missed. From https://github.com/thejpster/rust/pull/1. --- src/doc/rustc/src/SUMMARY.md | 1 - .../src/platform-support/armv7a-none-eabi.md | 38 ++++++++++++++----- .../src/platform-support/armv7r-none-eabi.md | 7 ---- 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 06de834a2f64..e866f3c0556a 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -48,7 +48,6 @@ - [\*-apple-visionos](platform-support/apple-visionos.md) - [aarch64-nintendo-switch-freestanding](platform-support/aarch64-nintendo-switch-freestanding.md) - [aarch64-unknown-linux-musl](platform-support/aarch64-unknown-linux-musl.md) - - [aarch64-unknown-none{,-softfloat}](aarch64-unknown-none.md) - [aarch64_be-unknown-none-softfloat](platform-support/aarch64_be-unknown-none-softfloat.md) - [aarch64_be-unknown-linux-musl](platform-support/aarch64_be-unknown-linux-musl.md) - [amdgcn-amd-amdhsa](platform-support/amdgcn-amd-amdhsa.md) diff --git a/src/doc/rustc/src/platform-support/armv7a-none-eabi.md b/src/doc/rustc/src/platform-support/armv7a-none-eabi.md index 9a1c6316c468..77c463697bf6 100644 --- a/src/doc/rustc/src/platform-support/armv7a-none-eabi.md +++ b/src/doc/rustc/src/platform-support/armv7a-none-eabi.md @@ -24,28 +24,46 @@ See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all ## Requirements -All Armv7-A processors include an FPU (a VFPv3 or a VFPv4). The difference -between the `-eabi` and `-eabihf` targets is whether the FPU is used for -passing function arguments. You may prefer the `-eabi` soft-float target when +Almost all Armv7-A processors include an FPU (a VFPv3 or a VFPv4). The +difference between the `-eabi` and `-eabihf` targets is whether the FPU is +used for passing function arguments. You may prefer the `-eabi` soft-float +target when the processor does not have a floating point unit or the compiled +code should not use the floating point unit. When using the hardfloat targets, the minimum floating-point features assumed -are those of the `vfpv3-d16`, which includes single- and double-precision, -with 16 double-precision registers. This floating-point unit appears in -Cortex-A8 and Cortex-A8 processors. See [VFP in the Cortex-A processors][vfp] -for more details on the possible FPU variants. +are those of the VFPv3-D16, which includes single- and double-precision, with +16 double-precision registers. This floating-point unit appears in Cortex-A8 +and Cortex-A9 processors. See [VFP in the Cortex-A processors][vfp] for more +details on the possible FPU variants. If your processor supports a different set of floating-point features than the -default expectations of `vfpv3-d16`, then these should also be enabled or +default expectations of VFPv3-D16, then these should also be enabled or disabled as needed with `-C target-feature=(+/-)`. +In general, the following four combinations are possible: + +- VFPv3-D16, target feature `+vfp3` and `-d32` +- VFPv3-D32, target feature `+vfp3` and `+d32` +- VFPv4-D16, target feature `+vfp4` and `-d32` +- VFPv4-D32, target feature `+vfp4` and `+d32` + +An Armv7-A processor may optionally include a NEON hardware unit which +provides Single Instruction Multiple Data (SIMD) operations. The +implementation of this unit implies VFPv3-D32. The target feature `+neon` may +be added to inform the compiler about the availability of NEON. + +You can refer to the [arm-none-eabi](arm-none-eabi.md) documentation for a +generic guide on target feature and target CPU specification and how to enable +and disable them via `.cargo/config.toml` file. + [vfp]: https://developer.arm.com/documentation/den0013/0400/Floating-Point/Floating-point-basics-and-the-IEEE-754-standard/ARM-VFP ## Start-up and Low-Level Code The [Rust Embedded Devices Working Group Arm Team] maintain the [`cortex-ar`] and [`cortex-a-rt`] crates, which may be useful for writing bare-metal code -using this target. Those crates include several examples which run in QEMU and -build using these targets. +using this target. The [`cortex-ar` repository](https://github.com/rust-embedded/cortex-ar) +includes several examples which run in QEMU and build using these targets. [`cortex-ar`]: https://docs.rs/cortex-ar [`cortex-a-rt`]: https://docs.rs/cortex-a-rt diff --git a/src/doc/rustc/src/platform-support/armv7r-none-eabi.md b/src/doc/rustc/src/platform-support/armv7r-none-eabi.md index 7d621e5c827d..36980a36b19a 100644 --- a/src/doc/rustc/src/platform-support/armv7r-none-eabi.md +++ b/src/doc/rustc/src/platform-support/armv7r-none-eabi.md @@ -21,13 +21,6 @@ See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all ## Requirements -When using the big-endian version of this target, note that some variants of -the Cortex-R have both big-endian instructions and data. This configuration is -known as BE-32, while data-only big-endianness is known as BE-8. To build -programs for BE-32 processors, the GNU linker must be used with the `-mbe32` -option. See [ARM Cortex-R Series Programmer's Guide: Endianness][endianness] -for more details about different endian modes. - When using the hardfloat targets, the minimum floating-point features assumed are those of the `vfpv3-d16`, which includes single- and double-precision, with 16 double-precision registers. This floating-point unit appears in Cortex-R4F From b152974301cc24b9003674935e7492fb46d6f299 Mon Sep 17 00:00:00 2001 From: Sasha Pourcelot Date: Wed, 3 Sep 2025 15:23:07 +0200 Subject: [PATCH 364/682] tidy: check that error messages don't start with a capitalized letter --- compiler/rustc_codegen_ssa/messages.ftl | 2 +- compiler/rustc_passes/messages.ftl | 2 +- src/tools/tidy/src/fluent_lowercase.rs | 64 +++++++++++++++++++ src/tools/tidy/src/lib.rs | 1 + src/tools/tidy/src/main.rs | 1 + .../raw-dylib/windows/dlltool-failed.rs | 2 +- .../raw-dylib/windows/dlltool-failed.stderr | 2 +- 7 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 src/tools/tidy/src/fluent_lowercase.rs diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index dd688b8b3454..1dd65d38a2be 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -31,7 +31,7 @@ codegen_ssa_cpu_required = target requires explicitly specifying a cpu with `-C codegen_ssa_create_temp_dir = couldn't create a temp dir: {$error} codegen_ssa_dlltool_fail_import_library = - Dlltool could not create import library with {$dlltool_path} {$dlltool_args}: + dlltool could not create import library with {$dlltool_path} {$dlltool_args}: {$stdout} {$stderr} diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index afd08319738d..1dd96240f0a9 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -75,7 +75,7 @@ passes_const_stable_not_stable = .label = attribute specified here passes_custom_mir_incompatible_dialect_and_phase = - The {$dialect} dialect is not compatible with the {$phase} phase + the {$dialect} dialect is not compatible with the {$phase} phase .dialect_span = this dialect... .phase_span = ... is not compatible with this phase diff --git a/src/tools/tidy/src/fluent_lowercase.rs b/src/tools/tidy/src/fluent_lowercase.rs new file mode 100644 index 000000000000..13f0319909e1 --- /dev/null +++ b/src/tools/tidy/src/fluent_lowercase.rs @@ -0,0 +1,64 @@ +//! Checks that the error messages start with a lowercased letter (except when allowed to). + +use std::path::Path; + +use fluent_syntax::ast::{Entry, Message, PatternElement}; + +use crate::walk::{filter_dirs, walk}; + +#[rustfmt::skip] +const ALLOWED_CAPITALIZED_WORDS: &[&str] = &[ + // tidy-alphabetical-start + "ABI", + "ABIs", + "ADT", + "C", + "CGU", + "Ferris", + "MIR", + "OK", + "Rust", + "VS", // VS Code + // tidy-alphabetical-end +]; + +fn filter_fluent(path: &Path) -> bool { + if let Some(ext) = path.extension() { ext.to_str() != Some("ftl") } else { true } +} + +fn is_allowed_capitalized_word(msg: &str) -> bool { + ALLOWED_CAPITALIZED_WORDS.iter().any(|word| { + msg.strip_prefix(word) + .map(|tail| tail.chars().next().map(|c| c == '-' || c.is_whitespace()).unwrap_or(true)) + .unwrap_or_default() + }) +} + +fn check_lowercase(filename: &str, contents: &str, bad: &mut bool) { + let (Ok(parse) | Err((parse, _))) = fluent_syntax::parser::parse(contents); + + for entry in &parse.body { + if let Entry::Message(msg) = entry + && let Message { value: Some(pattern), .. } = msg + && let [first_pattern, ..] = &pattern.elements[..] + && let PatternElement::TextElement { value } = first_pattern + && value.chars().next().is_some_and(char::is_uppercase) + && !is_allowed_capitalized_word(value) + { + tidy_error!( + bad, + "{filename}: message `{value}` starts with an uppercase letter. Fix it or add it to `ALLOWED_CAPITALIZED_WORDS`" + ); + } + } +} + +pub fn check(path: &Path, bad: &mut bool) { + walk( + path, + |path, is_dir| filter_dirs(path) || (!is_dir && filter_fluent(path)), + &mut |ent, contents| { + check_lowercase(ent.path().to_str().unwrap(), contents, bad); + }, + ); +} diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index 37713cbf75e9..2a9c3963d2d8 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -257,6 +257,7 @@ pub mod extra_checks; pub mod features; pub mod filenames; pub mod fluent_alphabetical; +pub mod fluent_lowercase; pub mod fluent_period; mod fluent_used; pub mod gcc_submodule; diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index bfe30258915e..f9e82341b7aa 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -121,6 +121,7 @@ fn main() { check!(error_codes, &root_path, &[&compiler_path, &librustdoc_path], verbose, &ci_info); check!(fluent_alphabetical, &compiler_path, bless); check!(fluent_period, &compiler_path); + check!(fluent_lowercase, &compiler_path); check!(target_policy, &root_path); check!(gcc_submodule, &root_path, &compiler_path); diff --git a/tests/ui/linkage-attr/raw-dylib/windows/dlltool-failed.rs b/tests/ui/linkage-attr/raw-dylib/windows/dlltool-failed.rs index 92cb60bb16d4..dac878c1cd99 100644 --- a/tests/ui/linkage-attr/raw-dylib/windows/dlltool-failed.rs +++ b/tests/ui/linkage-attr/raw-dylib/windows/dlltool-failed.rs @@ -20,4 +20,4 @@ pub fn lib_main() { unsafe { f(42); } } -//~? ERROR Dlltool could not create import library with +//~? ERROR dlltool could not create import library with diff --git a/tests/ui/linkage-attr/raw-dylib/windows/dlltool-failed.stderr b/tests/ui/linkage-attr/raw-dylib/windows/dlltool-failed.stderr index 90cca83d1c1f..5f3c29c3a212 100644 --- a/tests/ui/linkage-attr/raw-dylib/windows/dlltool-failed.stderr +++ b/tests/ui/linkage-attr/raw-dylib/windows/dlltool-failed.stderr @@ -1,4 +1,4 @@ -error: Dlltool could not create import library with $DLLTOOL -d $DEF_FILE -D foo.dll -l $LIB_FILE $TARGET_MACHINE $ASM_FLAGS --no-leading-underscore $TEMP_PREFIX: +error: dlltool could not create import library with $DLLTOOL -d $DEF_FILE -D foo.dll -l $LIB_FILE $TARGET_MACHINE $ASM_FLAGS --no-leading-underscore $TEMP_PREFIX: $DLLTOOL: Syntax error in def file $DEF_FILE:1␍ From 3b2178336ff01d1db306be9ac48dc6eadc00763a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 10 Sep 2025 18:15:59 +0200 Subject: [PATCH 365/682] s390x: mark soft-float target feature as incompatible --- compiler/rustc_target/src/target_features.rs | 8 ++++++ tests/ui/abi/simd-abi-checks-s390x.rs | 1 + tests/ui/abi/simd-abi-checks-s390x.z10.stderr | 20 +++++++------- ...simd-abi-checks-s390x.z13_no_vector.stderr | 20 +++++++------- ...imd-abi-checks-s390x.z13_soft_float.stderr | 27 +++++++++++-------- 5 files changed, 45 insertions(+), 31 deletions(-) diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 4c1b8c99426b..dc70089c385f 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -849,6 +849,7 @@ const IBMZ_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("miscellaneous-extensions-3", Unstable(sym::s390x_target_feature), &[]), ("miscellaneous-extensions-4", Unstable(sym::s390x_target_feature), &[]), ("nnp-assist", Unstable(sym::s390x_target_feature), &["vector"]), + ("soft-float", Forbidden { reason: "currently unsupported ABI-configuration feature" }, &[]), ("transactional-execution", Unstable(sym::s390x_target_feature), &[]), ("vector", Unstable(sym::s390x_target_feature), &[]), ("vector-enhancements-1", Unstable(sym::s390x_target_feature), &["vector"]), @@ -1177,6 +1178,13 @@ impl Target { _ => unreachable!(), } } + "s390x" => { + // We don't currently support a softfloat target on this architecture. + // As usual, we have to reject swapping the `soft-float` target feature. + // The "vector" target feature does not affect the ABI for floats + // because the vector and float registers overlap. + FeatureConstraints { required: &[], incompatible: &["soft-float"] } + } _ => NOTHING, } } diff --git a/tests/ui/abi/simd-abi-checks-s390x.rs b/tests/ui/abi/simd-abi-checks-s390x.rs index 877a25e8b083..75232a66ab09 100644 --- a/tests/ui/abi/simd-abi-checks-s390x.rs +++ b/tests/ui/abi/simd-abi-checks-s390x.rs @@ -8,6 +8,7 @@ // FIXME: +soft-float itself doesn't set -vector //@[z13_soft_float] compile-flags: --target s390x-unknown-linux-gnu -C target-cpu=z13 -C target-feature=-vector,+soft-float //@[z13_soft_float] needs-llvm-components: systemz +//[z13_soft_float]~? WARN must be disabled to ensure that the ABI of the current target can be implemented correctly #![feature(no_core, repr_simd, s390x_target_feature)] #![no_core] diff --git a/tests/ui/abi/simd-abi-checks-s390x.z10.stderr b/tests/ui/abi/simd-abi-checks-s390x.z10.stderr index c1c4e90f3cf8..e1cfa373c63c 100644 --- a/tests/ui/abi/simd-abi-checks-s390x.z10.stderr +++ b/tests/ui/abi/simd-abi-checks-s390x.z10.stderr @@ -1,5 +1,5 @@ error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:38:1 + --> $DIR/simd-abi-checks-s390x.rs:39:1 | LL | extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -7,7 +7,7 @@ LL | extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:43:1 + --> $DIR/simd-abi-checks-s390x.rs:44:1 | LL | extern "C" fn vector_ret(x: &i8x16) -> i8x16 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -15,7 +15,7 @@ LL | extern "C" fn vector_ret(x: &i8x16) -> i8x16 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:89:1 + --> $DIR/simd-abi-checks-s390x.rs:90:1 | LL | / extern "C" fn vector_transparent_wrapper_ret_small( LL | | x: &TransparentWrapper, @@ -25,7 +25,7 @@ LL | | ) -> TransparentWrapper { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:96:1 + --> $DIR/simd-abi-checks-s390x.rs:97:1 | LL | / extern "C" fn vector_transparent_wrapper_ret( LL | | x: &TransparentWrapper, @@ -35,7 +35,7 @@ LL | | ) -> TransparentWrapper { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:111:1 + --> $DIR/simd-abi-checks-s390x.rs:112:1 | LL | extern "C" fn vector_arg_small(x: i8x8) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -43,7 +43,7 @@ LL | extern "C" fn vector_arg_small(x: i8x8) -> i64 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:116:1 + --> $DIR/simd-abi-checks-s390x.rs:117:1 | LL | extern "C" fn vector_arg(x: i8x16) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -51,7 +51,7 @@ LL | extern "C" fn vector_arg(x: i8x16) -> i64 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:127:1 + --> $DIR/simd-abi-checks-s390x.rs:128:1 | LL | extern "C" fn vector_wrapper_arg_small(x: Wrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -59,7 +59,7 @@ LL | extern "C" fn vector_wrapper_arg_small(x: Wrapper) -> i64 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:132:1 + --> $DIR/simd-abi-checks-s390x.rs:133:1 | LL | extern "C" fn vector_wrapper_arg(x: Wrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -67,7 +67,7 @@ LL | extern "C" fn vector_wrapper_arg(x: Wrapper) -> i64 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:143:1 + --> $DIR/simd-abi-checks-s390x.rs:144:1 | LL | extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -75,7 +75,7 @@ LL | extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:148:1 + --> $DIR/simd-abi-checks-s390x.rs:149:1 | LL | extern "C" fn vector_transparent_wrapper_arg(x: TransparentWrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here diff --git a/tests/ui/abi/simd-abi-checks-s390x.z13_no_vector.stderr b/tests/ui/abi/simd-abi-checks-s390x.z13_no_vector.stderr index c1c4e90f3cf8..e1cfa373c63c 100644 --- a/tests/ui/abi/simd-abi-checks-s390x.z13_no_vector.stderr +++ b/tests/ui/abi/simd-abi-checks-s390x.z13_no_vector.stderr @@ -1,5 +1,5 @@ error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:38:1 + --> $DIR/simd-abi-checks-s390x.rs:39:1 | LL | extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -7,7 +7,7 @@ LL | extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:43:1 + --> $DIR/simd-abi-checks-s390x.rs:44:1 | LL | extern "C" fn vector_ret(x: &i8x16) -> i8x16 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -15,7 +15,7 @@ LL | extern "C" fn vector_ret(x: &i8x16) -> i8x16 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:89:1 + --> $DIR/simd-abi-checks-s390x.rs:90:1 | LL | / extern "C" fn vector_transparent_wrapper_ret_small( LL | | x: &TransparentWrapper, @@ -25,7 +25,7 @@ LL | | ) -> TransparentWrapper { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:96:1 + --> $DIR/simd-abi-checks-s390x.rs:97:1 | LL | / extern "C" fn vector_transparent_wrapper_ret( LL | | x: &TransparentWrapper, @@ -35,7 +35,7 @@ LL | | ) -> TransparentWrapper { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:111:1 + --> $DIR/simd-abi-checks-s390x.rs:112:1 | LL | extern "C" fn vector_arg_small(x: i8x8) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -43,7 +43,7 @@ LL | extern "C" fn vector_arg_small(x: i8x8) -> i64 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:116:1 + --> $DIR/simd-abi-checks-s390x.rs:117:1 | LL | extern "C" fn vector_arg(x: i8x16) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -51,7 +51,7 @@ LL | extern "C" fn vector_arg(x: i8x16) -> i64 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:127:1 + --> $DIR/simd-abi-checks-s390x.rs:128:1 | LL | extern "C" fn vector_wrapper_arg_small(x: Wrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -59,7 +59,7 @@ LL | extern "C" fn vector_wrapper_arg_small(x: Wrapper) -> i64 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:132:1 + --> $DIR/simd-abi-checks-s390x.rs:133:1 | LL | extern "C" fn vector_wrapper_arg(x: Wrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -67,7 +67,7 @@ LL | extern "C" fn vector_wrapper_arg(x: Wrapper) -> i64 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:143:1 + --> $DIR/simd-abi-checks-s390x.rs:144:1 | LL | extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -75,7 +75,7 @@ LL | extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:148:1 + --> $DIR/simd-abi-checks-s390x.rs:149:1 | LL | extern "C" fn vector_transparent_wrapper_arg(x: TransparentWrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here diff --git a/tests/ui/abi/simd-abi-checks-s390x.z13_soft_float.stderr b/tests/ui/abi/simd-abi-checks-s390x.z13_soft_float.stderr index c1c4e90f3cf8..577fcc23bf16 100644 --- a/tests/ui/abi/simd-abi-checks-s390x.z13_soft_float.stderr +++ b/tests/ui/abi/simd-abi-checks-s390x.z13_soft_float.stderr @@ -1,5 +1,10 @@ +warning: target feature `soft-float` must be disabled to ensure that the ABI of the current target can be implemented correctly + | + = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116344 + error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:38:1 + --> $DIR/simd-abi-checks-s390x.rs:39:1 | LL | extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -7,7 +12,7 @@ LL | extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:43:1 + --> $DIR/simd-abi-checks-s390x.rs:44:1 | LL | extern "C" fn vector_ret(x: &i8x16) -> i8x16 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -15,7 +20,7 @@ LL | extern "C" fn vector_ret(x: &i8x16) -> i8x16 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:89:1 + --> $DIR/simd-abi-checks-s390x.rs:90:1 | LL | / extern "C" fn vector_transparent_wrapper_ret_small( LL | | x: &TransparentWrapper, @@ -25,7 +30,7 @@ LL | | ) -> TransparentWrapper { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:96:1 + --> $DIR/simd-abi-checks-s390x.rs:97:1 | LL | / extern "C" fn vector_transparent_wrapper_ret( LL | | x: &TransparentWrapper, @@ -35,7 +40,7 @@ LL | | ) -> TransparentWrapper { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:111:1 + --> $DIR/simd-abi-checks-s390x.rs:112:1 | LL | extern "C" fn vector_arg_small(x: i8x8) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -43,7 +48,7 @@ LL | extern "C" fn vector_arg_small(x: i8x8) -> i64 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:116:1 + --> $DIR/simd-abi-checks-s390x.rs:117:1 | LL | extern "C" fn vector_arg(x: i8x16) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -51,7 +56,7 @@ LL | extern "C" fn vector_arg(x: i8x16) -> i64 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:127:1 + --> $DIR/simd-abi-checks-s390x.rs:128:1 | LL | extern "C" fn vector_wrapper_arg_small(x: Wrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -59,7 +64,7 @@ LL | extern "C" fn vector_wrapper_arg_small(x: Wrapper) -> i64 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:132:1 + --> $DIR/simd-abi-checks-s390x.rs:133:1 | LL | extern "C" fn vector_wrapper_arg(x: Wrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -67,7 +72,7 @@ LL | extern "C" fn vector_wrapper_arg(x: Wrapper) -> i64 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:143:1 + --> $DIR/simd-abi-checks-s390x.rs:144:1 | LL | extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -75,12 +80,12 @@ LL | extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:148:1 + --> $DIR/simd-abi-checks-s390x.rs:149:1 | LL | extern "C" fn vector_transparent_wrapper_arg(x: TransparentWrapper) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -error: aborting due to 10 previous errors +error: aborting due to 10 previous errors; 1 warning emitted From f8302a7c15d69b91ff20080ddcf8c44c81ec32ac Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 10 Sep 2025 23:01:15 +0200 Subject: [PATCH 366/682] add release sequence test --- .../miri/tests/pass/0weak_memory/weak.rs | 42 +++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/src/tools/miri/tests/pass/0weak_memory/weak.rs b/src/tools/miri/tests/pass/0weak_memory/weak.rs index 0dd661d3e9b4..75c6e8f1a879 100644 --- a/src/tools/miri/tests/pass/0weak_memory/weak.rs +++ b/src/tools/miri/tests/pass/0weak_memory/weak.rs @@ -35,7 +35,7 @@ fn spin_until(loc: &AtomicUsize, val: usize) -> usize { /// Check that the function produces the intended set of outcomes. #[track_caller] -fn check_all_outcomes( +fn check_all_outcomes( expected: impl IntoIterator, generate: impl Fn() -> T, ) { @@ -47,7 +47,7 @@ fn check_all_outcomes( let tries = expected.len() * 12; for i in 0..tries { let val = generate(); - assert!(expected.contains(&val), "got an unexpected value: {val}"); + assert!(expected.contains(&val), "got an unexpected value: {val:?}"); seen.insert(val); if i > tries / 2 && expected.len() == seen.len() { // We saw everything and we did quite a few tries, let's avoid wasting time. @@ -57,7 +57,7 @@ fn check_all_outcomes( // Let's see if we saw them all. for val in expected { if !seen.contains(&val) { - panic!("did not get value that should be possible: {val}"); + panic!("did not get value that should be possible: {val:?}"); } } } @@ -163,10 +163,46 @@ fn faa_replaced_by_load() { }); } +/// Checking that the weaker release sequence example from +/// can actually produce the +/// new behavior (`Some(0)` in our version). +fn weaker_release_sequences() { + check_all_outcomes([None, Some(0), Some(1)], || { + let x = static_atomic(0); + let y = static_atomic(0); + + let t1 = spawn(move || { + x.store(2, Relaxed); + }); + let t2 = spawn(move || { + y.store(1, Relaxed); + x.store(1, Release); + x.store(3, Relaxed); + }); + let t3 = spawn(move || { + if x.load(Acquire) == 3 { + // In C++11, if we read the 3 here, and if the store of 1 was just before the store + // of 3 in mo order (which it is because we fix the schedule), this forms a release + // sequence, meaning we acquire the release store of 1, and we can thus never see + // the value 0. + // In C++20, this is no longer a release sequence, so 0 can now be observed. + Some(y.load(Relaxed)) + } else { + None + } + }); + + t1.join().unwrap(); + t2.join().unwrap(); + t3.join().unwrap() + }); +} + pub fn main() { relaxed(); seq_cst(); initialization_write(false); initialization_write(true); faa_replaced_by_load(); + weaker_release_sequences(); } From 1558e65c9eec9ee0309e1920266e0a4325b63784 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 10 Sep 2025 21:40:24 +0200 Subject: [PATCH 367/682] Restore the test intention of several MBE trait bound modifier tests --- compiler/rustc_parse/src/parser/ty.rs | 6 +- ...sync-trait-bound-theoretical-regression.rs | 17 ++-- ...-trait-bound-theoretical-regression.stderr | 8 +- ...o-bare-trait-objects-const-trait-bounds.rs | 33 ++++--- ...re-trait-objects-const-trait-bounds.stderr | 22 ----- ...onst-trait-bound-theoretical-regression.rs | 30 ++++-- ...-trait-bound-theoretical-regression.stderr | 94 ++++++++++++------- .../const-traits/macro-dyn-const-2015.rs | 15 ++- 8 files changed, 124 insertions(+), 101 deletions(-) delete mode 100644 tests/ui/traits/const-traits/macro-bare-trait-objects-const-trait-bounds.stderr diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 6168647183fd..138e828a2304 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -92,10 +92,10 @@ fn can_continue_type_after_non_fn_ident(t: &Token) -> bool { } fn can_begin_dyn_bound_in_edition_2015(t: &Token) -> bool { - // `Not`, `Tilde` & `Const` are deliberately not part of this list to + // `!`, `const`, `[`, `async` are deliberately not part of this list to // contain the number of potential regressions esp. in MBE code. - // `Const` would regress `rfc-2632-const-trait-impl/mbe-dyn-const-2015.rs`. - // `Not` would regress `dyn!(...)` macro calls in Rust 2015. + // `const` and `[` would regress UI test `macro-dyn-const-2015.rs`. + // `!` would regress `dyn!(...)` macro calls in Rust 2015. t.is_path_start() || t.is_lifetime() || t == &TokenKind::Question diff --git a/tests/ui/async-await/async-fn/macro-async-trait-bound-theoretical-regression.rs b/tests/ui/async-await/async-fn/macro-async-trait-bound-theoretical-regression.rs index ea67831b68e6..a13a255d5361 100644 --- a/tests/ui/async-await/async-fn/macro-async-trait-bound-theoretical-regression.rs +++ b/tests/ui/async-await/async-fn/macro-async-trait-bound-theoretical-regression.rs @@ -1,21 +1,22 @@ // Demonstrates and records a theoretical regressions / breaking changes caused by the // introduction of async trait bounds. -// Setting the edition to 2018 since we don't regress `demo! { dyn async }` in Rust <2018. +// Setting the edition to >2015 since we didn't regress `demo! { dyn async }` in Rust 2015. //@ edition:2018 macro_rules! demo { - ($ty:ty) => { compile_error!("ty"); }; + ($ty:ty) => { compile_error!("ty"); }; // KEEP THIS RULE FIRST AND AS IS! //~^ ERROR ty //~| ERROR ty - (impl $c:ident Trait) => {}; - (dyn $c:ident Trait) => {}; + + // DON'T MODIFY THE MATCHERS BELOW UNLESS THE ASYNC TRAIT MODIFIER SYNTAX CHANGES! + + (impl $c:ident Trait) => { /* KEEP THIS EMPTY! */ }; + (dyn $c:ident Trait) => { /* KEEP THIS EMPTY! */ }; } -demo! { impl async Trait } -//~^ ERROR `async` trait bounds are unstable +demo! { impl async Trait } //~ ERROR `async` trait bounds are unstable -demo! { dyn async Trait } -//~^ ERROR `async` trait bounds are unstable +demo! { dyn async Trait } //~ ERROR `async` trait bounds are unstable fn main() {} diff --git a/tests/ui/async-await/async-fn/macro-async-trait-bound-theoretical-regression.stderr b/tests/ui/async-await/async-fn/macro-async-trait-bound-theoretical-regression.stderr index 6c3044e64d20..0292c53fb1c1 100644 --- a/tests/ui/async-await/async-fn/macro-async-trait-bound-theoretical-regression.stderr +++ b/tests/ui/async-await/async-fn/macro-async-trait-bound-theoretical-regression.stderr @@ -1,7 +1,7 @@ error: ty --> $DIR/macro-async-trait-bound-theoretical-regression.rs:8:19 | -LL | ($ty:ty) => { compile_error!("ty"); }; +LL | ($ty:ty) => { compile_error!("ty"); }; // KEEP THIS RULE FIRST AND AS IS! | ^^^^^^^^^^^^^^^^^^^^ ... LL | demo! { impl async Trait } @@ -12,7 +12,7 @@ LL | demo! { impl async Trait } error: ty --> $DIR/macro-async-trait-bound-theoretical-regression.rs:8:19 | -LL | ($ty:ty) => { compile_error!("ty"); }; +LL | ($ty:ty) => { compile_error!("ty"); }; // KEEP THIS RULE FIRST AND AS IS! | ^^^^^^^^^^^^^^^^^^^^ ... LL | demo! { dyn async Trait } @@ -21,7 +21,7 @@ LL | demo! { dyn async Trait } = note: this error originates in the macro `demo` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0658]: `async` trait bounds are unstable - --> $DIR/macro-async-trait-bound-theoretical-regression.rs:15:14 + --> $DIR/macro-async-trait-bound-theoretical-regression.rs:18:14 | LL | demo! { impl async Trait } | ^^^^^ @@ -32,7 +32,7 @@ LL | demo! { impl async Trait } = help: use the desugared name of the async trait, such as `AsyncFn` error[E0658]: `async` trait bounds are unstable - --> $DIR/macro-async-trait-bound-theoretical-regression.rs:18:13 + --> $DIR/macro-async-trait-bound-theoretical-regression.rs:20:13 | LL | demo! { dyn async Trait } | ^^^^^ diff --git a/tests/ui/traits/const-traits/macro-bare-trait-objects-const-trait-bounds.rs b/tests/ui/traits/const-traits/macro-bare-trait-objects-const-trait-bounds.rs index ee04f74c8a64..9b03ec7553d1 100644 --- a/tests/ui/traits/const-traits/macro-bare-trait-objects-const-trait-bounds.rs +++ b/tests/ui/traits/const-traits/macro-bare-trait-objects-const-trait-bounds.rs @@ -1,24 +1,23 @@ -// Ensure that we don't consider `const Trait` to -// match the macro fragment specifier `ty` as that would be a breaking -// change theoretically speaking. Syntactically trait object types can -// be "bare", i.e., lack the prefix `dyn`. -// By contrast, `?Trait` *does* match `ty` and therefore an arm like -// `?$Trait:path` would never be reached. -// See `parser/macro/mbe-bare-trait-object-maybe-trait-bound.rs`. -// `[const] Trait` is already an error for a `ty` fragment, -// so we do not need to prevent that. +// Ensure that we don't consider `const Trait` to match the macro fragment specifier `ty` +// as that would be a breaking change theoretically speaking. +// +// Syntactically trait object types can be "bare", i.e., lack the prefix `dyn`. +// By contrast, `?Trait` *does* match `ty` and therefore an arm like `?$Trait:path` +// would never be reached. See `parser/macro/macro-bare-trait-object-maybe-trait-bound.rs`. + +//@ check-pass (KEEP THIS AS A PASSING TEST!) macro_rules! check { - ($Type:ty) => { - compile_error!("ty"); - }; - (const $Trait:path) => {}; - ([const] $Trait:path) => { [const] Trait }; + ($ty:ty) => { compile_error!("ty"); }; // KEEP THIS RULE FIRST AND AS IS! + + // DON'T MODIFY THE MATCHERS BELOW UNLESS THE CONST TRAIT MODIFIER SYNTAX CHANGES! + + (const $Trait:path) => { /* KEEP THIS EMPTY! */ }; + // We don't need to check `[const] Trait` here since that matches the `ty` fragment + // already anyway since `[` may begin a slice or array type. However, it'll then + // subsequently fail due to #146122 (section 3). } check! { const Trait } -check! { [const] Trait } -//~^ ERROR: expected identifier, found `]` -//~| ERROR: const trait impls are experimental fn main() {} diff --git a/tests/ui/traits/const-traits/macro-bare-trait-objects-const-trait-bounds.stderr b/tests/ui/traits/const-traits/macro-bare-trait-objects-const-trait-bounds.stderr deleted file mode 100644 index a4e77154b17d..000000000000 --- a/tests/ui/traits/const-traits/macro-bare-trait-objects-const-trait-bounds.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error: expected identifier, found `]` - --> $DIR/macro-bare-trait-objects-const-trait-bounds.rs:20:16 - | -LL | ($Type:ty) => { - | -------- while parsing argument for this `ty` macro fragment -... -LL | check! { [const] Trait } - | ^ expected identifier - -error[E0658]: const trait impls are experimental - --> $DIR/macro-bare-trait-objects-const-trait-bounds.rs:20:11 - | -LL | check! { [const] Trait } - | ^^^^^ - | - = note: see issue #143874 for more information - = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/traits/const-traits/macro-const-trait-bound-theoretical-regression.rs b/tests/ui/traits/const-traits/macro-const-trait-bound-theoretical-regression.rs index 35e964eacec4..3f70ffa7d10e 100644 --- a/tests/ui/traits/const-traits/macro-const-trait-bound-theoretical-regression.rs +++ b/tests/ui/traits/const-traits/macro-const-trait-bound-theoretical-regression.rs @@ -1,22 +1,32 @@ // Demonstrates and records a theoretical regressions / breaking changes caused by the -// introduction of const trait bounds. +// introduction of `const` and `[const]` trait bounds. -// Setting the edition to 2018 since we don't regress `demo! { dyn const }` in Rust <2018. +// Setting the edition to >2015 since we didn't regress `demo! { dyn const }` in Rust 2015. +// See also test `traits/const-traits/macro-dyn-const-2015.rs`. //@ edition:2018 trait Trait {} macro_rules! demo { - (impl $c:ident Trait) => { impl $c Trait {} }; - //~^ ERROR inherent - //~| WARN trait objects without an explicit `dyn` are deprecated - //~| WARN this is accepted in the current edition - (dyn $c:ident Trait) => { dyn $c Trait {} }; //~ ERROR macro expansion + ($ty:ty) => { compile_error!("ty"); }; // KEEP THIS RULE FIRST AND AS IS! + //~^ ERROR ty + //~| ERROR ty + //~| ERROR ty + //~| ERROR ty + + // DON'T MODIFY THE MATCHERS BELOW UNLESS THE CONST TRAIT MODIFIER SYNTAX CHANGES! + + (impl $c:ident Trait) => { /* KEEP THIS EMPTY! */ }; + (dyn $c:ident Trait) => { /* KEEP THIS EMPTY! */ }; + + (impl [const] Trait) => { /* KEEP THIS EMPTY! */ }; + (dyn [const] Trait) => { /* KEEP THIS EMPTY! */ }; } -demo! { impl const Trait } -//~^ ERROR const trait impls are experimental +demo! { impl const Trait } //~ ERROR const trait impls are experimental +demo! { dyn const Trait } //~ ERROR const trait impls are experimental -demo! { dyn const Trait } +demo! { impl [const] Trait } //~ ERROR const trait impls are experimental +demo! { dyn [const] Trait } //~ ERROR const trait impls are experimental fn main() {} diff --git a/tests/ui/traits/const-traits/macro-const-trait-bound-theoretical-regression.stderr b/tests/ui/traits/const-traits/macro-const-trait-bound-theoretical-regression.stderr index 7a4061d9c18e..383936b9df15 100644 --- a/tests/ui/traits/const-traits/macro-const-trait-bound-theoretical-regression.stderr +++ b/tests/ui/traits/const-traits/macro-const-trait-bound-theoretical-regression.stderr @@ -1,31 +1,49 @@ -error: inherent impls cannot be const - --> $DIR/macro-const-trait-bound-theoretical-regression.rs:10:40 +error: ty + --> $DIR/macro-const-trait-bound-theoretical-regression.rs:11:19 | -LL | (impl $c:ident Trait) => { impl $c Trait {} }; - | ^^^^^ inherent impl for this type +LL | ($ty:ty) => { compile_error!("ty"); }; // KEEP THIS RULE FIRST AND AS IS! + | ^^^^^^^^^^^^^^^^^^^^ ... LL | demo! { impl const Trait } - | -------------------------- - | | | - | | const because of this - | in this macro invocation + | -------------------------- in this macro invocation | - = note: only trait implementations may be annotated with `const` = note: this error originates in the macro `demo` (in Nightly builds, run with -Z macro-backtrace for more info) -error: macro expansion ignores keyword `dyn` and any tokens following - --> $DIR/macro-const-trait-bound-theoretical-regression.rs:14:31 +error: ty + --> $DIR/macro-const-trait-bound-theoretical-regression.rs:11:19 | -LL | (dyn $c:ident Trait) => { dyn $c Trait {} }; - | ^^^ +LL | ($ty:ty) => { compile_error!("ty"); }; // KEEP THIS RULE FIRST AND AS IS! + | ^^^^^^^^^^^^^^^^^^^^ ... LL | demo! { dyn const Trait } - | ------------------------- caused by the macro expansion here + | ------------------------- in this macro invocation | - = note: the usage of `demo!` is likely invalid in item context + = note: this error originates in the macro `demo` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: ty + --> $DIR/macro-const-trait-bound-theoretical-regression.rs:11:19 + | +LL | ($ty:ty) => { compile_error!("ty"); }; // KEEP THIS RULE FIRST AND AS IS! + | ^^^^^^^^^^^^^^^^^^^^ +... +LL | demo! { impl [const] Trait } + | ---------------------------- in this macro invocation + | + = note: this error originates in the macro `demo` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: ty + --> $DIR/macro-const-trait-bound-theoretical-regression.rs:11:19 + | +LL | ($ty:ty) => { compile_error!("ty"); }; // KEEP THIS RULE FIRST AND AS IS! + | ^^^^^^^^^^^^^^^^^^^^ +... +LL | demo! { dyn [const] Trait } + | --------------------------- in this macro invocation + | + = note: this error originates in the macro `demo` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0658]: const trait impls are experimental - --> $DIR/macro-const-trait-bound-theoretical-regression.rs:17:14 + --> $DIR/macro-const-trait-bound-theoretical-regression.rs:26:14 | LL | demo! { impl const Trait } | ^^^^^ @@ -34,24 +52,36 @@ LL | demo! { impl const Trait } = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/macro-const-trait-bound-theoretical-regression.rs:10:40 +error[E0658]: const trait impls are experimental + --> $DIR/macro-const-trait-bound-theoretical-regression.rs:27:13 | -LL | (impl $c:ident Trait) => { impl $c Trait {} }; - | ^^^^^ -... -LL | demo! { impl const Trait } - | -------------------------- in this macro invocation +LL | demo! { dyn const Trait } + | ^^^^^ | - = warning: this is accepted in the current edition (Rust 2018) but is a hard error in Rust 2021! - = note: for more information, see - = note: `#[warn(bare_trait_objects)]` (part of `#[warn(rust_2021_compatibility)]`) on by default - = note: this warning originates in the macro `demo` (in Nightly builds, run with -Z macro-backtrace for more info) -help: you might have intended to implement this trait for a given type - | -LL | (impl $c:ident Trait) => { impl $c Trait for /* Type */ {} }; - | ++++++++++++++ + = note: see issue #143874 for more information + = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 3 previous errors; 1 warning emitted +error[E0658]: const trait impls are experimental + --> $DIR/macro-const-trait-bound-theoretical-regression.rs:29:14 + | +LL | demo! { impl [const] Trait } + | ^^^^^^^ + | + = note: see issue #143874 for more information + = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: const trait impls are experimental + --> $DIR/macro-const-trait-bound-theoretical-regression.rs:30:13 + | +LL | demo! { dyn [const] Trait } + | ^^^^^^^ + | + = note: see issue #143874 for more information + = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 8 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/traits/const-traits/macro-dyn-const-2015.rs b/tests/ui/traits/const-traits/macro-dyn-const-2015.rs index fadfbe667887..0dfa482b9861 100644 --- a/tests/ui/traits/const-traits/macro-dyn-const-2015.rs +++ b/tests/ui/traits/const-traits/macro-dyn-const-2015.rs @@ -1,14 +1,19 @@ -// Ensure that the introduction of const trait bound didn't regress this code in Rust 2015. -// See also `mbe-const-trait-bound-theoretical-regression.rs`. +// Ensure that the introduction of `const` and `[const]` trait bounds didn't regress this +// Rust 2015 code. See also test `macro-const-trait-bound-theoretical-regression.rs`. //@ edition: 2015 -//@ check-pass +//@ check-pass (KEEP THIS AS A PASSING TEST!) macro_rules! check { - ($ty:ty) => { compile_error!("ty"); }; - (dyn $c:ident) => {}; + ($ty:ty) => { compile_error!("ty"); }; // KEEP THIS RULE FIRST AND AS IS! + + // DON'T MODIFY THE MATCHERS BELOW UNLESS THE CONST TRAIT MODIFIER SYNTAX CHANGES! + + (dyn $c:ident) => { /* KEEP THIS EMPTY! */ }; + (dyn [$c:ident]) => { /* KEEP THIS EMPTY! */ }; } check! { dyn const } +check! { dyn [const] } fn main() {} From f5dad62d4cdf139fb1c6cf2fa24ac525167501cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 10 Sep 2025 21:41:00 +0200 Subject: [PATCH 368/682] Less greedily parse `[const]` bounds --- compiler/rustc_parse/src/parser/ty.rs | 13 ++++++---- .../macro-maybe-const-trait-bounds.rs | 25 +++++++++++++++++++ 2 files changed, 33 insertions(+), 5 deletions(-) create mode 100644 tests/ui/traits/const-traits/macro-maybe-const-trait-bounds.rs diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 138e828a2304..23aaafac934e 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -1015,12 +1015,18 @@ impl<'a> Parser<'a> { || self.check(exp!(Tilde)) || self.check_keyword(exp!(For)) || self.check(exp!(OpenParen)) - || self.check(exp!(OpenBracket)) + || self.can_begin_maybe_const_bound() || self.check_keyword(exp!(Const)) || self.check_keyword(exp!(Async)) || self.check_keyword(exp!(Use)) } + fn can_begin_maybe_const_bound(&mut self) -> bool { + self.check(exp!(OpenBracket)) + && self.look_ahead(1, |t| t.is_keyword(kw::Const)) + && self.look_ahead(2, |t| *t == token::CloseBracket) + } + /// Parse a bound. /// /// ```ebnf @@ -1199,10 +1205,7 @@ impl<'a> Parser<'a> { let span = tilde.to(self.prev_token.span); self.psess.gated_spans.gate(sym::const_trait_impl, span); BoundConstness::Maybe(span) - } else if self.check(exp!(OpenBracket)) - && self.look_ahead(1, |t| t.is_keyword(kw::Const)) - && self.look_ahead(2, |t| *t == token::CloseBracket) - { + } else if self.can_begin_maybe_const_bound() { let start = self.token.span; self.bump(); self.expect_keyword(exp!(Const)).unwrap(); diff --git a/tests/ui/traits/const-traits/macro-maybe-const-trait-bounds.rs b/tests/ui/traits/const-traits/macro-maybe-const-trait-bounds.rs new file mode 100644 index 000000000000..75077577b8c6 --- /dev/null +++ b/tests/ui/traits/const-traits/macro-maybe-const-trait-bounds.rs @@ -0,0 +1,25 @@ +// Ensure that we don't consider `[` to begin trait bounds to contain breakages. +// Only `[const]` in its entirety begins a trait bound. +// See also test `macro-const-trait-bound-theoretical-regression.rs`. + +//@ check-pass (KEEP THIS AS A PASSING TEST!) +// Setting the edition to >2015 since we didn't regress `check! { dyn [const] Trait }` in Rust 2015. +// See also test `traits/const-traits/macro-dyn-const-2015.rs`. +//@ edition:2018 + +macro_rules! check { + ($ty:ty) => { compile_error!("ty"); }; // KEEP THIS RULE FIRST AND AS IS! + + // DON'T MODIFY THE MATCHERS BELOW UNLESS THE CONST TRAIT MODIFIER SYNTAX CHANGES! + + (dyn [$($any:tt)*] Trait) => { /* KEEP THIS EMPTY! */ }; + (impl [$($any:tt)*] Trait) => { /* KEEP THIS EMPTY! */ }; +} + +check!(dyn [T] Trait); + +// issue: +check!(impl [T] Trait); +check!(impl [T: Bound] Trait); + +fn main() {} From 5f2b04652fe006439aaa52271997a69499dca021 Mon Sep 17 00:00:00 2001 From: Christian Poveda Date: Wed, 10 Sep 2025 16:32:04 -0500 Subject: [PATCH 369/682] Improve `core::ops` coverage --- library/coretests/tests/lib.rs | 1 + library/coretests/tests/ops.rs | 74 +++++++++++++++++++++++++++++++++- 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index b5658a9970fe..1bdaa6965f64 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -81,6 +81,7 @@ #![feature(next_index)] #![feature(non_exhaustive_omitted_patterns_lint)] #![feature(numfmt)] +#![feature(one_sided_range)] #![feature(option_reduce)] #![feature(pattern)] #![feature(peekable_next_if_map)] diff --git a/library/coretests/tests/ops.rs b/library/coretests/tests/ops.rs index 501e0f33fe4c..921010774e28 100644 --- a/library/coretests/tests/ops.rs +++ b/library/coretests/tests/ops.rs @@ -2,7 +2,8 @@ mod control_flow; mod from_residual; use core::ops::{ - Bound, Deref, DerefMut, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive, + Bound, Deref, DerefMut, OneSidedRange, OneSidedRangeBound, Range, RangeBounds, RangeFrom, + RangeFull, RangeInclusive, RangeTo, RangeToInclusive, }; // Test the Range structs and syntax. @@ -70,6 +71,35 @@ fn test_range_to_inclusive() { let _ = RangeToInclusive { end: 42 }; } +#[test] +fn test_range_contains() { + assert!(!(1u32..5).contains(&0u32)); + assert!((1u32..5).contains(&1u32)); + assert!((1u32..5).contains(&4u32)); + assert!(!(1u32..5).contains(&5u32)); + assert!(!(1u32..5).contains(&6u32)); +} + +#[test] +fn test_range_to_contains() { + assert!(!(1u32..=5).contains(&0)); + assert!((1u32..=5).contains(&1)); + assert!((1u32..=5).contains(&4)); + assert!((1u32..=5).contains(&5)); + assert!(!(1u32..=5).contains(&6)); +} + +// This test covers `RangeBounds::contains` when the start is excluded. +#[test] +fn test_range_bounds_contains() { + let r = (Bound::Excluded(1u32), Bound::Included(5u32)); + assert!(!r.contains(&0)); + assert!(!r.contains(&1)); + assert!(r.contains(&3)); + assert!(r.contains(&5)); + assert!(!r.contains(&6)); +} + #[test] fn test_range_is_empty() { assert!(!(0.0..10.0).is_empty()); @@ -91,6 +121,34 @@ fn test_range_is_empty() { assert!((f32::NAN..=f32::NAN).is_empty()); } +#[test] +fn test_range_inclusive_end_bound() { + let mut r = 1u32..=1; + r.next().unwrap(); + assert!(!r.contains(&1)); +} + +#[test] +fn test_range_bounds() { + let r = (Bound::Included(1u32), Bound::Excluded(5u32)); + assert!(!r.contains(&0)); + assert!(r.contains(&1)); + assert!(r.contains(&3)); + assert!(!r.contains(&5)); + assert!(!r.contains(&6)); + + let r = (Bound::::Unbounded, Bound::Unbounded); + assert!(r.contains(&0)); + assert!(r.contains(&u32::MAX)); +} + +#[test] +fn test_one_sided_range_bound() { + assert!(matches!((..1u32).bound(), (OneSidedRangeBound::End, 1))); + assert!(matches!((1u32..).bound(), (OneSidedRangeBound::StartInclusive, 1))); + assert!(matches!((..=1u32).bound(), (OneSidedRangeBound::EndInclusive, 1))); +} + #[test] fn test_bound_cloned_unbounded() { assert_eq!(Bound::<&u32>::Unbounded.cloned(), Bound::Unbounded); @@ -240,3 +298,17 @@ fn deref_on_ref() { fn test_not_never() { if !return () {} } + +#[test] +fn test_fmt() { + let mut r = 1..=1; + assert_eq!(format!("{:?}", r), "1..=1"); + r.next().unwrap(); + assert_eq!(format!("{:?}", r), "1..=1 (exhausted)"); + + assert_eq!(format!("{:?}", 1..1), "1..1"); + assert_eq!(format!("{:?}", 1..), "1.."); + assert_eq!(format!("{:?}", ..1), "..1"); + assert_eq!(format!("{:?}", ..=1), "..=1"); + assert_eq!(format!("{:?}", ..), ".."); +} From 789c8408ec0e6ed4e299fb13b047282ed1046e2e Mon Sep 17 00:00:00 2001 From: Christian Poveda Ruiz <31802960+pvdrz@users.noreply.github.com> Date: Wed, 10 Sep 2025 16:50:45 -0500 Subject: [PATCH 370/682] Update library/coretests/tests/ops.rs Co-authored-by: Jubilee --- library/coretests/tests/ops.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/coretests/tests/ops.rs b/library/coretests/tests/ops.rs index 921010774e28..121718f2167e 100644 --- a/library/coretests/tests/ops.rs +++ b/library/coretests/tests/ops.rs @@ -89,7 +89,8 @@ fn test_range_to_contains() { assert!(!(1u32..=5).contains(&6)); } -// This test covers `RangeBounds::contains` when the start is excluded. +// This test covers `RangeBounds::contains` when the start is excluded, +// which cannot be directly expressed by Rust's built-in range syntax. #[test] fn test_range_bounds_contains() { let r = (Bound::Excluded(1u32), Bound::Included(5u32)); From a7528e7ec3e13ebe00c19a92381b93be8cce8ec6 Mon Sep 17 00:00:00 2001 From: Christian Poveda Date: Wed, 10 Sep 2025 16:41:13 -0500 Subject: [PATCH 371/682] Improve `core::array` coverage --- library/coretests/tests/array.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/library/coretests/tests/array.rs b/library/coretests/tests/array.rs index 30ccbbc32031..c4a8fc74feca 100644 --- a/library/coretests/tests/array.rs +++ b/library/coretests/tests/array.rs @@ -717,3 +717,10 @@ fn array_map_drops_unmapped_elements_on_panic() { assert_eq!(counter.load(Ordering::SeqCst), MAX); } } + +// This covers the `PartialEq::<[T]>::eq` impl for `[T; N]` when it returns false. +#[test] +fn array_eq() { + let not_true = [0u8] == [].as_slice(); + assert!(!not_true); +} From 3d3769a0d384aee773d538b53baef9005f759ac0 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Wed, 10 Sep 2025 23:36:11 +0100 Subject: [PATCH 372/682] Add missing links in platform-support.md --- src/doc/rustc/src/platform-support.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 3c91d1250577..24945f1fa51e 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -149,8 +149,8 @@ target | std | notes [`aarch64-apple-ios-sim`](platform-support/apple-ios.md) | ✓ | Apple iOS Simulator on ARM64 [`aarch64-linux-android`](platform-support/android.md) | ✓ | ARM64 Android [`aarch64-unknown-fuchsia`](platform-support/fuchsia.md) | ✓ | ARM64 Fuchsia -`aarch64-unknown-none` | * | Bare ARM64, hardfloat -`aarch64-unknown-none-softfloat` | * | Bare ARM64, softfloat +[`aarch64-unknown-none`](platform-support/aarch64-unknown-none.md) | * | Bare ARM64, hardfloat +[`aarch64-unknown-none-softfloat`](platform-support/aarch64-unknown-none.md) | * | Bare ARM64, softfloat [`aarch64-unknown-uefi`](platform-support/unknown-uefi.md) | ? | ARM64 UEFI [`arm-linux-androideabi`](platform-support/android.md) | ✓ | Armv6 Android `arm-unknown-linux-musleabi` | ✓ | Armv6 Linux with musl 1.2.3 From 5a742229eca120eda8226207d8415ed9755408ad Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Wed, 10 Sep 2025 23:37:08 +0100 Subject: [PATCH 373/682] Add aarch64-unknown-none to SUMMARY.md --- src/doc/rustc/src/SUMMARY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index e866f3c0556a..e4bf33dd8a0e 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -48,6 +48,7 @@ - [\*-apple-visionos](platform-support/apple-visionos.md) - [aarch64-nintendo-switch-freestanding](platform-support/aarch64-nintendo-switch-freestanding.md) - [aarch64-unknown-linux-musl](platform-support/aarch64-unknown-linux-musl.md) + - [aarch64-unknown-none*](platform-support/aarch64-unknown-none.md) - [aarch64_be-unknown-none-softfloat](platform-support/aarch64_be-unknown-none-softfloat.md) - [aarch64_be-unknown-linux-musl](platform-support/aarch64_be-unknown-linux-musl.md) - [amdgcn-amd-amdhsa](platform-support/amdgcn-amd-amdhsa.md) From 409a1fbcba4214b0badf458801038f75fba5c707 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Wed, 10 Sep 2025 23:37:46 +0100 Subject: [PATCH 374/682] Update aarch64-unknown-none.md to include -softfloat too --- src/doc/rustc/src/platform-support/aarch64-unknown-none.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc/src/platform-support/aarch64-unknown-none.md b/src/doc/rustc/src/platform-support/aarch64-unknown-none.md index 0c1d47796198..f3c0764b5bf7 100644 --- a/src/doc/rustc/src/platform-support/aarch64-unknown-none.md +++ b/src/doc/rustc/src/platform-support/aarch64-unknown-none.md @@ -1,8 +1,8 @@ -# `aarch64-unknown-none` +# `aarch64-unknown-none` and `aarch64-unknown-none-softfloat` **Tier: 2** -Bare-metal target for CPUs in the Armv8-A architecture family, running in AArch64 mode. +Bare-metal targets for CPUs in the Armv8-A architecture family, running in AArch64 mode. For the AArch32 mode carried over from Armv7-A, see [`armv7a-none-eabi`](armv7a-none-eabi.md) instead. From 458f3873d65045cbf7e9950caa089a9fc0d9cd5a Mon Sep 17 00:00:00 2001 From: Daniel Paoliello Date: Wed, 10 Sep 2025 15:39:13 -0700 Subject: [PATCH 375/682] Bump miow to 0.60.1 --- Cargo.lock | 72 +++--------------------------------------------------- 1 file changed, 3 insertions(+), 69 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8be13e84596d..2dc6e8ff1032 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2347,11 +2347,11 @@ dependencies = [ [[package]] name = "miow" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "359f76430b20a79f9e20e115b3428614e654f04fab314482fc0fda0ebd3c6044" +checksum = "536bfad37a309d62069485248eeaba1e8d9853aaf951caaeaed0585a95346f08" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.60.2", ] [[package]] @@ -6314,15 +6314,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-sys" version = "0.52.0" @@ -6350,21 +6341,6 @@ dependencies = [ "windows-targets 0.53.3", ] -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - [[package]] name = "windows-targets" version = "0.52.6" @@ -6407,12 +6383,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -6425,12 +6395,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -6443,12 +6407,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -6473,12 +6431,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -6491,12 +6443,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -6509,12 +6455,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -6527,12 +6467,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" From fc58d8f5cce592998d0ba43b25e445eb6b96c827 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Thu, 11 Sep 2025 09:06:56 +0800 Subject: [PATCH 376/682] Add regression test for assert desugaring change Using the MCVE reported in RUST-145770. --- tests/ui/macros/assert-desugaring-145770.rs | 22 +++++++++++++++++++ .../ui/macros/assert-desugaring-145770.stderr | 14 ++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 tests/ui/macros/assert-desugaring-145770.rs create mode 100644 tests/ui/macros/assert-desugaring-145770.stderr diff --git a/tests/ui/macros/assert-desugaring-145770.rs b/tests/ui/macros/assert-desugaring-145770.rs new file mode 100644 index 000000000000..9eb850f0380c --- /dev/null +++ b/tests/ui/macros/assert-desugaring-145770.rs @@ -0,0 +1,22 @@ +//! Regression test for #145770. +//! +//! Changing the `assert!` desugaring from an `if !cond {}` to `match` expression is +//! backwards-incompatible, and may need to be done over an edition boundary or limit editions for +//! which the desguaring change impacts. + +#[derive(Debug)] +struct F { + data: bool +} + +impl std::ops::Not for F { + type Output = bool; + fn not(self) -> Self::Output { !self.data } +} + +fn main() { + let f = F { data: true }; + + assert!(f); + //~^ ERROR mismatched types +} diff --git a/tests/ui/macros/assert-desugaring-145770.stderr b/tests/ui/macros/assert-desugaring-145770.stderr new file mode 100644 index 000000000000..875ea8f7c329 --- /dev/null +++ b/tests/ui/macros/assert-desugaring-145770.stderr @@ -0,0 +1,14 @@ +error[E0308]: mismatched types + --> $DIR/assert-desugaring-145770.rs:20:11 + | +LL | assert!(f); + | ^ expected `bool`, found `F` + | +help: you might have meant to use field `data` whose type is `bool` + | +LL | assert!(f.data); + | +++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. From b38a86f4d7c28ae9ab153b87c7e45037e56306fb Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Thu, 11 Sep 2025 09:00:01 +0800 Subject: [PATCH 377/682] Revert "Rollup merge of #122661 - estebank:assert-macro-span, r=petrochenkov" This reverts commit 1eeb8e8b151d1da7daa73837a25dc5f7a1a7fa28, reversing changes made to 324bf2b9fd8bf9661e7045c8a93f5ff0ec1a8ca5. Unfortunately the assert desugaring change is not backwards compatible, see RUST-145770. Code such as ```rust #[derive(Debug)] struct F { data: bool } impl std::ops::Not for F { type Output = bool; fn not(self) -> Self::Output { !self.data } } fn main() { let f = F { data: true }; assert!(f); } ``` would be broken by the assert desugaring change. We may need to land the change over an edition boundary, or limit the editions that the desugaring change impacts. --- compiler/rustc_builtin_macros/src/assert.rs | 35 +++++++--------- .../src/error_reporting/infer/mod.rs | 40 ++----------------- .../src/missing_asserts_for_indexing.rs | 12 +++--- src/tools/clippy/tests/ui/const_is_empty.rs | 1 - .../clippy/tests/ui/const_is_empty.stderr | 10 +---- .../clippy/tests/ui/incompatible_msrv.rs | 2 +- tests/ui/codemap_tests/issue-28308.rs | 16 +------- tests/ui/codemap_tests/issue-28308.stderr | 28 +++---------- tests/ui/consts/control-flow/assert.stderr | 4 +- ...st_monomorphization_error_backtrace.stderr | 8 ++-- .../const-expr-generic-err.stderr | 4 +- tests/ui/macros/assert-desugaring-145770.rs | 3 +- .../ui/macros/assert-desugaring-145770.stderr | 14 ------- tests/ui/macros/assert-macro-lifetimes.rs | 8 ---- tests/ui/mir/alignment/packed.rs | 2 +- .../ui/simd/const-err-trumps-simd-err.stderr | 4 +- tests/ui/transmutability/uninhabited.stderr | 12 +++--- 17 files changed, 53 insertions(+), 150 deletions(-) delete mode 100644 tests/ui/macros/assert-desugaring-145770.stderr delete mode 100644 tests/ui/macros/assert-macro-lifetimes.rs diff --git a/compiler/rustc_builtin_macros/src/assert.rs b/compiler/rustc_builtin_macros/src/assert.rs index 013258a1b4ef..855da5caa312 100644 --- a/compiler/rustc_builtin_macros/src/assert.rs +++ b/compiler/rustc_builtin_macros/src/assert.rs @@ -1,8 +1,8 @@ mod context; -use rustc_ast::token::{self, Delimiter}; +use rustc_ast::token::Delimiter; use rustc_ast::tokenstream::{DelimSpan, TokenStream}; -use rustc_ast::{DelimArgs, Expr, ExprKind, MacCall, Path, PathSegment}; +use rustc_ast::{DelimArgs, Expr, ExprKind, MacCall, Path, PathSegment, UnOp, token}; use rustc_ast_pretty::pprust; use rustc_errors::PResult; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult}; @@ -29,7 +29,7 @@ pub(crate) fn expand_assert<'cx>( // `core::panic` and `std::panic` are different macros, so we use call-site // context to pick up whichever is currently in scope. - let call_site_span = cx.with_call_site_ctxt(cond_expr.span); + let call_site_span = cx.with_call_site_ctxt(span); let panic_path = || { if use_panic_2021(span) { @@ -63,7 +63,7 @@ pub(crate) fn expand_assert<'cx>( }), })), ); - assert_cond_check(cx, call_site_span, cond_expr, then) + expr_if_not(cx, call_site_span, cond_expr, then, None) } // If `generic_assert` is enabled, generates rich captured outputs // @@ -88,33 +88,26 @@ pub(crate) fn expand_assert<'cx>( )), )], ); - assert_cond_check(cx, call_site_span, cond_expr, then) + expr_if_not(cx, call_site_span, cond_expr, then, None) }; ExpandResult::Ready(MacEager::expr(expr)) } -/// `assert!($cond_expr, $custom_message)` struct Assert { cond_expr: Box, custom_message: Option, } -/// `match { true => {} _ => }` -fn assert_cond_check(cx: &ExtCtxt<'_>, span: Span, cond: Box, then: Box) -> Box { - // Instead of expanding to `if ! { }`, we expand to - // `match { true => {} _ => }`. - // This allows us to always complain about mismatched types instead of "cannot apply unary - // operator `!` to type `X`" when passing an invalid ``, while also allowing `` to - // be `&true`. - let els = cx.expr_block(cx.block(span, thin_vec![])); - let mut arms = thin_vec![]; - arms.push(cx.arm(span, cx.pat_lit(span, cx.expr_bool(span, true)), els)); - arms.push(cx.arm(span, cx.pat_wild(span), then)); - - // We wrap the `match` in a statement to limit the length of any borrows introduced in the - // condition. - cx.expr_block(cx.block(span, [cx.stmt_expr(cx.expr_match(span, cond, arms))].into())) +// if !{ ... } { ... } else { ... } +fn expr_if_not( + cx: &ExtCtxt<'_>, + span: Span, + cond: Box, + then: Box, + els: Option>, +) -> Box { + cx.expr_if(span, cx.expr(span, ExprKind::Unary(UnOp::Not, cond)), then, els) } fn parse_assert<'a>(cx: &ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PResult<'a, Assert> { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index d71110521ffa..e18e294635b5 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -1618,18 +1618,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { { let e = self.tcx.erase_and_anonymize_regions(e); let f = self.tcx.erase_and_anonymize_regions(f); - let mut expected = with_forced_trimmed_paths!(e.sort_string(self.tcx)); - let mut found = with_forced_trimmed_paths!(f.sort_string(self.tcx)); - if let ObligationCauseCode::Pattern { span, .. } = cause.code() - && let Some(span) = span - && !span.from_expansion() - && cause.span.from_expansion() - { - // When the type error comes from a macro like `assert!()`, and we are pointing at - // code the user wrote the cause and effect are reversed as the expected value is - // what the macro expanded to. - (found, expected) = (expected, found); - } + let expected = with_forced_trimmed_paths!(e.sort_string(self.tcx)); + let found = with_forced_trimmed_paths!(f.sort_string(self.tcx)); if expected == found { label_or_note(span, terr.to_string(self.tcx)); } else { @@ -2152,9 +2142,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ) -> Option<(DiagStyledString, DiagStyledString)> { match values { ValuePairs::Regions(exp_found) => self.expected_found_str(exp_found), - ValuePairs::Terms(exp_found) => { - self.expected_found_str_term(cause, exp_found, long_ty_path) - } + ValuePairs::Terms(exp_found) => self.expected_found_str_term(exp_found, long_ty_path), ValuePairs::Aliases(exp_found) => self.expected_found_str(exp_found), ValuePairs::ExistentialTraitRef(exp_found) => self.expected_found_str(exp_found), ValuePairs::ExistentialProjection(exp_found) => self.expected_found_str(exp_found), @@ -2193,7 +2181,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { fn expected_found_str_term( &self, - cause: &ObligationCause<'tcx>, exp_found: ty::error::ExpectedFound>, long_ty_path: &mut Option, ) -> Option<(DiagStyledString, DiagStyledString)> { @@ -2201,27 +2188,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if exp_found.references_error() { return None; } - let (mut expected, mut found) = (exp_found.expected, exp_found.found); - if let ObligationCauseCode::Pattern { span, .. } = cause.code() - && let Some(span) = span - && !span.from_expansion() - && cause.span.from_expansion() - { - // When the type error comes from a macro like `assert!()`, and we are pointing at - // code the user wrote, the cause and effect are reversed as the expected value is - // what the macro expanded to. So if the user provided a `Type` when the macro is - // written in such a way that a `bool` was expected, we want to print: - // = note: expected `bool` - // found `Type`" - // but as far as the compiler is concerned, after expansion what was expected was `Type` - // = note: expected `Type` - // found `bool`" - // so we reverse them here to match user expectation. - (expected, found) = (found, expected); - } - - Some(match (expected.kind(), found.kind()) { + Some(match (exp_found.expected.kind(), exp_found.found.kind()) { (ty::TermKind::Ty(expected), ty::TermKind::Ty(found)) => { let (mut exp, mut fnd) = self.cmp(expected, found); // Use the terminal width as the basis to determine when to compress the printed diff --git a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs index 788a04357b1e..cf0c85990b15 100644 --- a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs +++ b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs @@ -11,7 +11,7 @@ use rustc_ast::{BinOpKind, LitKind, RangeLimits}; use rustc_data_structures::packed::Pu128; use rustc_data_structures::unhash::UnindexMap; use rustc_errors::{Applicability, Diag}; -use rustc_hir::{Body, Expr, ExprKind}; +use rustc_hir::{Block, Body, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::source_map::Spanned; @@ -135,12 +135,12 @@ fn assert_len_expr<'hir>( cx: &LateContext<'_>, expr: &'hir Expr<'hir>, ) -> Option<(LengthComparison, usize, &'hir Expr<'hir>)> { - let (cmp, asserted_len, slice_len) = if let Some( - higher::IfLetOrMatch::Match(cond, [_, then], _) - ) = higher::IfLetOrMatch::parse(cx, expr) - && let ExprKind::Binary(bin_op, left, right) = &cond.kind + let (cmp, asserted_len, slice_len) = if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr) + && let ExprKind::Unary(UnOp::Not, condition) = &cond.kind + && let ExprKind::Binary(bin_op, left, right) = &condition.kind // check if `then` block has a never type expression - && cx.typeck_results().expr_ty(then.body).is_never() + && let ExprKind::Block(Block { expr: Some(then_expr), .. }, _) = then.kind + && cx.typeck_results().expr_ty(then_expr).is_never() { len_comparison(bin_op.node, left, right)? } else if let Some((macro_call, bin_op)) = first_node_macro_backtrace(cx, expr).find_map(|macro_call| { diff --git a/src/tools/clippy/tests/ui/const_is_empty.rs b/src/tools/clippy/tests/ui/const_is_empty.rs index 63c6342a323c..8bb4f0e5d975 100644 --- a/src/tools/clippy/tests/ui/const_is_empty.rs +++ b/src/tools/clippy/tests/ui/const_is_empty.rs @@ -196,7 +196,6 @@ fn issue_13106() { const { assert!(EMPTY_STR.is_empty()); - //~^ const_is_empty } const { diff --git a/src/tools/clippy/tests/ui/const_is_empty.stderr b/src/tools/clippy/tests/ui/const_is_empty.stderr index 9a42518698e3..2ba189058e83 100644 --- a/src/tools/clippy/tests/ui/const_is_empty.stderr +++ b/src/tools/clippy/tests/ui/const_is_empty.stderr @@ -158,16 +158,10 @@ LL | let _ = val.is_empty(); | ^^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:198:17 - | -LL | assert!(EMPTY_STR.is_empty()); - | ^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:203:9 + --> tests/ui/const_is_empty.rs:202:9 | LL | EMPTY_STR.is_empty(); | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 28 previous errors +error: aborting due to 27 previous errors diff --git a/src/tools/clippy/tests/ui/incompatible_msrv.rs b/src/tools/clippy/tests/ui/incompatible_msrv.rs index 882f909e30c9..f7f21e1850d0 100644 --- a/src/tools/clippy/tests/ui/incompatible_msrv.rs +++ b/src/tools/clippy/tests/ui/incompatible_msrv.rs @@ -1,6 +1,6 @@ #![warn(clippy::incompatible_msrv)] #![feature(custom_inner_attributes)] -#![allow(stable_features, clippy::diverging_sub_expression)] +#![allow(stable_features)] #![feature(strict_provenance)] // For use in test #![clippy::msrv = "1.3.0"] diff --git a/tests/ui/codemap_tests/issue-28308.rs b/tests/ui/codemap_tests/issue-28308.rs index b0e04d0f1f6b..81493f8c4531 100644 --- a/tests/ui/codemap_tests/issue-28308.rs +++ b/tests/ui/codemap_tests/issue-28308.rs @@ -1,16 +1,4 @@ fn main() { - assert!("foo"); //~ ERROR mismatched types - //~^ NOTE expected `bool`, found `str` - //~| NOTE in this expansion of assert! - let x = Some(&1); - assert!(x); //~ ERROR mismatched types - //~^ NOTE expected `bool`, found `Option<&{integer}>` - //~| NOTE expected enum `bool` - //~| NOTE in this expansion of assert! - //~| NOTE in this expansion of assert! - assert!(x, ""); //~ ERROR mismatched types - //~^ NOTE expected `bool`, found `Option<&{integer}>` - //~| NOTE expected enum `bool` - //~| NOTE in this expansion of assert! - //~| NOTE in this expansion of assert! + assert!("foo"); + //~^ ERROR cannot apply unary operator `!` } diff --git a/tests/ui/codemap_tests/issue-28308.stderr b/tests/ui/codemap_tests/issue-28308.stderr index e84ceb44aacb..7bc9e05dfc02 100644 --- a/tests/ui/codemap_tests/issue-28308.stderr +++ b/tests/ui/codemap_tests/issue-28308.stderr @@ -1,27 +1,9 @@ -error[E0308]: mismatched types - --> $DIR/issue-28308.rs:2:13 +error[E0600]: cannot apply unary operator `!` to type `&'static str` + --> $DIR/issue-28308.rs:2:5 | LL | assert!("foo"); - | ^^^^^ expected `bool`, found `str` + | ^^^^^^^^^^^^^^ cannot apply unary operator `!` -error[E0308]: mismatched types - --> $DIR/issue-28308.rs:6:13 - | -LL | assert!(x); - | ^ expected `bool`, found `Option<&{integer}>` - | - = note: expected enum `bool` - found type `Option<&{integer}>` +error: aborting due to 1 previous error -error[E0308]: mismatched types - --> $DIR/issue-28308.rs:11:13 - | -LL | assert!(x, ""); - | ^ expected `bool`, found `Option<&{integer}>` - | - = note: expected enum `bool` - found type `Option<&{integer}>` - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0600`. diff --git a/tests/ui/consts/control-flow/assert.stderr b/tests/ui/consts/control-flow/assert.stderr index deaad6abbccf..026097a6ba0e 100644 --- a/tests/ui/consts/control-flow/assert.stderr +++ b/tests/ui/consts/control-flow/assert.stderr @@ -1,8 +1,8 @@ error[E0080]: evaluation panicked: assertion failed: false - --> $DIR/assert.rs:5:23 + --> $DIR/assert.rs:5:15 | LL | const _: () = assert!(false); - | ^^^^^ evaluation of `_` failed here + | ^^^^^^^^^^^^^^ evaluation of `_` failed here error: aborting due to 1 previous error diff --git a/tests/ui/generics/post_monomorphization_error_backtrace.stderr b/tests/ui/generics/post_monomorphization_error_backtrace.stderr index 92c7df73638f..6953414f0c23 100644 --- a/tests/ui/generics/post_monomorphization_error_backtrace.stderr +++ b/tests/ui/generics/post_monomorphization_error_backtrace.stderr @@ -1,8 +1,8 @@ error[E0080]: evaluation panicked: assertion failed: std::mem::size_of::() == 0 - --> $DIR/post_monomorphization_error_backtrace.rs:6:31 + --> $DIR/post_monomorphization_error_backtrace.rs:6:23 | LL | const V: () = assert!(std::mem::size_of::() == 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `assert_zst::F::::V` failed here + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `assert_zst::F::::V` failed here note: erroneous constant encountered --> $DIR/post_monomorphization_error_backtrace.rs:14:5 @@ -17,10 +17,10 @@ LL | assert_zst::() | ^^^^^^^^^^^^^^^^^ error[E0080]: evaluation panicked: assertion failed: std::mem::size_of::() == 0 - --> $DIR/post_monomorphization_error_backtrace.rs:6:31 + --> $DIR/post_monomorphization_error_backtrace.rs:6:23 | LL | const V: () = assert!(std::mem::size_of::() == 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `assert_zst::F::::V` failed here + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `assert_zst::F::::V` failed here note: erroneous constant encountered --> $DIR/post_monomorphization_error_backtrace.rs:14:5 diff --git a/tests/ui/inline-const/const-expr-generic-err.stderr b/tests/ui/inline-const/const-expr-generic-err.stderr index e053e88db172..26039ba6d444 100644 --- a/tests/ui/inline-const/const-expr-generic-err.stderr +++ b/tests/ui/inline-const/const-expr-generic-err.stderr @@ -1,8 +1,8 @@ error[E0080]: evaluation panicked: assertion failed: std::mem::size_of::() == 0 - --> $DIR/const-expr-generic-err.rs:4:21 + --> $DIR/const-expr-generic-err.rs:4:13 | LL | const { assert!(std::mem::size_of::() == 0); } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `foo::::{constant#0}` failed here + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `foo::::{constant#0}` failed here note: erroneous constant encountered --> $DIR/const-expr-generic-err.rs:4:5 diff --git a/tests/ui/macros/assert-desugaring-145770.rs b/tests/ui/macros/assert-desugaring-145770.rs index 9eb850f0380c..d56e771ecfb0 100644 --- a/tests/ui/macros/assert-desugaring-145770.rs +++ b/tests/ui/macros/assert-desugaring-145770.rs @@ -4,6 +4,8 @@ //! backwards-incompatible, and may need to be done over an edition boundary or limit editions for //! which the desguaring change impacts. +//@ check-pass + #[derive(Debug)] struct F { data: bool @@ -18,5 +20,4 @@ fn main() { let f = F { data: true }; assert!(f); - //~^ ERROR mismatched types } diff --git a/tests/ui/macros/assert-desugaring-145770.stderr b/tests/ui/macros/assert-desugaring-145770.stderr deleted file mode 100644 index 875ea8f7c329..000000000000 --- a/tests/ui/macros/assert-desugaring-145770.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/assert-desugaring-145770.rs:20:11 - | -LL | assert!(f); - | ^ expected `bool`, found `F` - | -help: you might have meant to use field `data` whose type is `bool` - | -LL | assert!(f.data); - | +++++ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/macros/assert-macro-lifetimes.rs b/tests/ui/macros/assert-macro-lifetimes.rs deleted file mode 100644 index cc2592832047..000000000000 --- a/tests/ui/macros/assert-macro-lifetimes.rs +++ /dev/null @@ -1,8 +0,0 @@ -//@ check-pass -#[derive(PartialEq, Eq, Hash)] -struct S; -fn main() { - let foo = std::rc::Rc::new(std::cell::RefCell::new(std::collections::HashMap::::new())); - // Ensure that the lifetimes of the borrow do not leak past the end of `main`. - assert!(matches!(foo.borrow().get(&S).unwrap(), S)) -} diff --git a/tests/ui/mir/alignment/packed.rs b/tests/ui/mir/alignment/packed.rs index aa79880a21a2..cf908365e1a5 100644 --- a/tests/ui/mir/alignment/packed.rs +++ b/tests/ui/mir/alignment/packed.rs @@ -12,7 +12,7 @@ fn main() { // Test that we can use addr_of! to get the address of a packed member which according to its // type is not aligned, but because it is a projection from a packed type is a valid place. let ptr0 = std::ptr::addr_of!(memory[0].tail); - let ptr1 = std::ptr::addr_of!(memory[1].tail); + let ptr1 = std::ptr::addr_of!(memory[0].tail); // Even if ptr0 happens to be aligned by chance, ptr1 is not. assert!(!ptr0.is_aligned() || !ptr1.is_aligned()); diff --git a/tests/ui/simd/const-err-trumps-simd-err.stderr b/tests/ui/simd/const-err-trumps-simd-err.stderr index 6d25a28c92c5..93d1fce637f2 100644 --- a/tests/ui/simd/const-err-trumps-simd-err.stderr +++ b/tests/ui/simd/const-err-trumps-simd-err.stderr @@ -1,8 +1,8 @@ error[E0080]: evaluation panicked: assertion failed: LANE < 4 - --> $DIR/const-err-trumps-simd-err.rs:17:21 + --> $DIR/const-err-trumps-simd-err.rs:17:13 | LL | const { assert!(LANE < 4); } // the error should be here... - | ^^^^^^^^ evaluation of `get_elem::<4>::{constant#0}` failed here + | ^^^^^^^^^^^^^^^^^ evaluation of `get_elem::<4>::{constant#0}` failed here note: erroneous constant encountered --> $DIR/const-err-trumps-simd-err.rs:17:5 diff --git a/tests/ui/transmutability/uninhabited.stderr b/tests/ui/transmutability/uninhabited.stderr index 9f289852809c..4757daec9978 100644 --- a/tests/ui/transmutability/uninhabited.stderr +++ b/tests/ui/transmutability/uninhabited.stderr @@ -41,10 +41,10 @@ LL | | }> | |__________^ required by this bound in `is_maybe_transmutable` error[E0080]: evaluation panicked: assertion failed: false - --> $DIR/uninhabited.rs:41:17 + --> $DIR/uninhabited.rs:41:9 | LL | assert!(false); - | ^^^^^ evaluation of `yawning_void_struct::_` failed here + | ^^^^^^^^^^^^^^ evaluation of `yawning_void_struct::_` failed here error[E0277]: `()` cannot be safely transmuted into `yawning_void_enum::Void` --> $DIR/uninhabited.rs:71:41 @@ -68,10 +68,10 @@ LL | | }> | |__________^ required by this bound in `is_maybe_transmutable` error[E0080]: evaluation panicked: assertion failed: false - --> $DIR/uninhabited.rs:63:17 + --> $DIR/uninhabited.rs:63:9 | LL | assert!(false); - | ^^^^^ evaluation of `yawning_void_enum::_` failed here + | ^^^^^^^^^^^^^^ evaluation of `yawning_void_enum::_` failed here error[E0277]: `u128` cannot be safely transmuted into `DistantVoid` --> $DIR/uninhabited.rs:92:43 @@ -95,10 +95,10 @@ LL | | }> | |__________^ required by this bound in `is_maybe_transmutable` error[E0080]: evaluation panicked: assertion failed: false - --> $DIR/uninhabited.rs:87:17 + --> $DIR/uninhabited.rs:87:9 | LL | assert!(false); - | ^^^^^ evaluation of `distant_void::_` failed here + | ^^^^^^^^^^^^^^ evaluation of `distant_void::_` failed here error[E0277]: `Src` cannot be safely transmuted into `issue_126267::Error` --> $DIR/uninhabited.rs:108:42 From ba107fcffba2aefc83e199286a5ccdbb29f69d5d Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Thu, 11 Sep 2025 09:00:01 +0800 Subject: [PATCH 378/682] Revert "Rollup merge of #122661 - estebank:assert-macro-span, r=petrochenkov" This reverts commit 1eeb8e8b151d1da7daa73837a25dc5f7a1a7fa28, reversing changes made to 324bf2b9fd8bf9661e7045c8a93f5ff0ec1a8ca5. Unfortunately the assert desugaring change is not backwards compatible, see RUST-145770. Code such as ```rust #[derive(Debug)] struct F { data: bool } impl std::ops::Not for F { type Output = bool; fn not(self) -> Self::Output { !self.data } } fn main() { let f = F { data: true }; assert!(f); } ``` would be broken by the assert desugaring change. We may need to land the change over an edition boundary, or limit the editions that the desugaring change impacts. --- clippy_lints/src/missing_asserts_for_indexing.rs | 12 ++++++------ tests/ui/const_is_empty.rs | 1 - tests/ui/const_is_empty.stderr | 10 ++-------- tests/ui/incompatible_msrv.rs | 2 +- 4 files changed, 9 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/missing_asserts_for_indexing.rs b/clippy_lints/src/missing_asserts_for_indexing.rs index 788a04357b1e..cf0c85990b15 100644 --- a/clippy_lints/src/missing_asserts_for_indexing.rs +++ b/clippy_lints/src/missing_asserts_for_indexing.rs @@ -11,7 +11,7 @@ use rustc_ast::{BinOpKind, LitKind, RangeLimits}; use rustc_data_structures::packed::Pu128; use rustc_data_structures::unhash::UnindexMap; use rustc_errors::{Applicability, Diag}; -use rustc_hir::{Body, Expr, ExprKind}; +use rustc_hir::{Block, Body, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::source_map::Spanned; @@ -135,12 +135,12 @@ fn assert_len_expr<'hir>( cx: &LateContext<'_>, expr: &'hir Expr<'hir>, ) -> Option<(LengthComparison, usize, &'hir Expr<'hir>)> { - let (cmp, asserted_len, slice_len) = if let Some( - higher::IfLetOrMatch::Match(cond, [_, then], _) - ) = higher::IfLetOrMatch::parse(cx, expr) - && let ExprKind::Binary(bin_op, left, right) = &cond.kind + let (cmp, asserted_len, slice_len) = if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr) + && let ExprKind::Unary(UnOp::Not, condition) = &cond.kind + && let ExprKind::Binary(bin_op, left, right) = &condition.kind // check if `then` block has a never type expression - && cx.typeck_results().expr_ty(then.body).is_never() + && let ExprKind::Block(Block { expr: Some(then_expr), .. }, _) = then.kind + && cx.typeck_results().expr_ty(then_expr).is_never() { len_comparison(bin_op.node, left, right)? } else if let Some((macro_call, bin_op)) = first_node_macro_backtrace(cx, expr).find_map(|macro_call| { diff --git a/tests/ui/const_is_empty.rs b/tests/ui/const_is_empty.rs index 63c6342a323c..8bb4f0e5d975 100644 --- a/tests/ui/const_is_empty.rs +++ b/tests/ui/const_is_empty.rs @@ -196,7 +196,6 @@ fn issue_13106() { const { assert!(EMPTY_STR.is_empty()); - //~^ const_is_empty } const { diff --git a/tests/ui/const_is_empty.stderr b/tests/ui/const_is_empty.stderr index 9a42518698e3..2ba189058e83 100644 --- a/tests/ui/const_is_empty.stderr +++ b/tests/ui/const_is_empty.stderr @@ -158,16 +158,10 @@ LL | let _ = val.is_empty(); | ^^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:198:17 - | -LL | assert!(EMPTY_STR.is_empty()); - | ^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:203:9 + --> tests/ui/const_is_empty.rs:202:9 | LL | EMPTY_STR.is_empty(); | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 28 previous errors +error: aborting due to 27 previous errors diff --git a/tests/ui/incompatible_msrv.rs b/tests/ui/incompatible_msrv.rs index 882f909e30c9..f7f21e1850d0 100644 --- a/tests/ui/incompatible_msrv.rs +++ b/tests/ui/incompatible_msrv.rs @@ -1,6 +1,6 @@ #![warn(clippy::incompatible_msrv)] #![feature(custom_inner_attributes)] -#![allow(stable_features, clippy::diverging_sub_expression)] +#![allow(stable_features)] #![feature(strict_provenance)] // For use in test #![clippy::msrv = "1.3.0"] From 2d87bc3e6bfc9e6ccda01a5898980b1e0a706708 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 11 Sep 2025 10:23:02 +0800 Subject: [PATCH 379/682] Fix empty generic param list for generate_function Example --- ```rust struct Foo(S); impl Foo { fn foo(&self) { self.bar()$0; } } ``` **Before this PR**: ```rust struct Foo(S); impl Foo { fn foo(&self) { self.bar(); } fn bar<>(&self) ${0:-> _} { todo!() } } ``` **After this PR**: ```rust struct Foo(S); impl Foo { fn foo(&self) { self.bar(); } fn bar(&self) ${0:-> _} { todo!() } } ``` --- .../src/handlers/generate_function.rs | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs index 88ed6f9ce124..a9cf2c1bae1a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs @@ -364,11 +364,13 @@ impl FunctionBuilder { Visibility::Crate => Some(make::visibility_pub_crate()), Visibility::Pub => Some(make::visibility_pub()), }; + let type_params = + self.generic_param_list.filter(|list| list.generic_params().next().is_some()); let fn_def = make::fn_( None, visibility, self.fn_name, - self.generic_param_list, + type_params, self.where_clause, self.params, self.fn_body, @@ -2415,6 +2417,33 @@ impl Foo { ) } + #[test] + fn create_method_with_unused_generics() { + check_assist( + generate_function, + r#" +struct Foo(S); +impl Foo { + fn foo(&self) { + self.bar()$0; + } +} +"#, + r#" +struct Foo(S); +impl Foo { + fn foo(&self) { + self.bar(); + } + + fn bar(&self) ${0:-> _} { + todo!() + } +} +"#, + ) + } + #[test] fn create_function_with_async() { check_assist( From 68bff8cb9db58d7006f609971e6961646b44dbe9 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Thu, 11 Sep 2025 04:53:20 +0000 Subject: [PATCH 380/682] Prepare for merging from rust-lang/rust This updates the rust-version file to f4665ab8368ad2e8a86d4390ae35c28bdd9561bb. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 5b3139b25092..b71a0d219d77 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -a09fbe2c8372643a27a8082236120f95ed4e6bba +f4665ab8368ad2e8a86d4390ae35c28bdd9561bb From e54cc43867efe10c73a6063ef36fcc5aa8570ffb Mon Sep 17 00:00:00 2001 From: Tsukasa OI Date: Thu, 11 Sep 2025 06:26:51 +0000 Subject: [PATCH 381/682] RISC-V: "Lower" requirements of `aes64im` This instruction is incorrectly categorized as the same one as `aes64ks1i` and `aes64ks2` (that should require `zkne || zknd` but currently require `zkne && zknd`) but `aes64im` only requires the Zknd extension. This commit fixes the category of this intrinsic (lowering the requirements from the Rust perspective but it does not actually lower it from the RISC-V perspective). --- library/stdarch/crates/core_arch/src/riscv64/zk.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/stdarch/crates/core_arch/src/riscv64/zk.rs b/library/stdarch/crates/core_arch/src/riscv64/zk.rs index c6af750bbc57..a30653cbe088 100644 --- a/library/stdarch/crates/core_arch/src/riscv64/zk.rs +++ b/library/stdarch/crates/core_arch/src/riscv64/zk.rs @@ -176,7 +176,7 @@ pub fn aes64ks2(rs1: u64, rs2: u64) -> u64 { /// Version: v1.0.1 /// /// Section: 3.9 -#[target_feature(enable = "zkne", enable = "zknd")] +#[target_feature(enable = "zknd")] #[cfg_attr(test, assert_instr(aes64im))] #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] From 9fe101b6ea204256bffef357c646012d571416ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Wed, 10 Sep 2025 16:25:57 +0200 Subject: [PATCH 382/682] Remove unused import from sys/pal/hermit/os.rs This fixes ``` error: unused import: `str` --> library/std/src/sys/pal/hermit/os.rs:6:22 | 6 | use crate::{fmt, io, str}; | ^^^ | = note: `-D unused-imports` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(unused_imports)]` ``` This was caused by 845311a065a5638c516ed96c73b09862b176b329. --- library/std/src/sys/pal/hermit/os.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/sys/pal/hermit/os.rs b/library/std/src/sys/pal/hermit/os.rs index 0fe713a503bd..9681964ed9b2 100644 --- a/library/std/src/sys/pal/hermit/os.rs +++ b/library/std/src/sys/pal/hermit/os.rs @@ -3,7 +3,7 @@ use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; use crate::path::{self, PathBuf}; use crate::sys::unsupported; -use crate::{fmt, io, str}; +use crate::{fmt, io}; pub fn errno() -> i32 { unsafe { hermit_abi::get_errno() } From af5355cc91fb48ed3d1bf31200b4311f801d24a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Wed, 10 Sep 2025 15:26:59 +0200 Subject: [PATCH 383/682] Implement `Socket::take_error` for Hermit --- library/std/src/sys/net/connection/socket/hermit.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/std/src/sys/net/connection/socket/hermit.rs b/library/std/src/sys/net/connection/socket/hermit.rs index f49821657d94..5200eaa5786a 100644 --- a/library/std/src/sys/net/connection/socket/hermit.rs +++ b/library/std/src/sys/net/connection/socket/hermit.rs @@ -304,7 +304,8 @@ impl Socket { } pub fn take_error(&self) -> io::Result> { - unimplemented!() + let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?; + if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } } // This is used by sys_common code to abstract over Windows and Unix. From 092802640f760d5a59b644110b8b0186a743ee2f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 11 Sep 2025 10:04:32 +0200 Subject: [PATCH 384/682] rwlock tests: fix miri macos test regression --- library/std/tests/sync/rwlock.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/std/tests/sync/rwlock.rs b/library/std/tests/sync/rwlock.rs index eca15d2a4adb..2554316e9071 100644 --- a/library/std/tests/sync/rwlock.rs +++ b/library/std/tests/sync/rwlock.rs @@ -50,7 +50,7 @@ nonpoison_and_poison_unwrap_test!( // FIXME: On macOS we use a provenance-incorrect implementation and Miri // catches that issue with a chance of around 1/1000. // See for details. -#[cfg_attr(all(miri, target_os = "macos"), ignore)] +#[cfg(not(all(miri, target_os = "macos")))] nonpoison_and_poison_unwrap_test!( name: frob, test_body: { @@ -315,7 +315,7 @@ nonpoison_and_poison_unwrap_test!( // FIXME: On macOS we use a provenance-incorrect implementation and Miri catches that issue. // See for details. -#[cfg_attr(all(miri, target_os = "macos"), ignore)] +#[cfg(not(all(miri, target_os = "macos")))] nonpoison_and_poison_unwrap_test!( name: test_downgrade_observe, test_body: { @@ -362,7 +362,7 @@ nonpoison_and_poison_unwrap_test!( // FIXME: On macOS we use a provenance-incorrect implementation and Miri catches that issue. // See for details. -#[cfg_attr(all(miri, target_os = "macos"), ignore)] +#[cfg(not(all(miri, target_os = "macos")))] nonpoison_and_poison_unwrap_test!( name: test_downgrade_atomic, test_body: { From fd48528d185f59f60e301bce1e01d670ff4bdb30 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 10 Sep 2025 00:17:22 +0200 Subject: [PATCH 385/682] c-variadic: allow inherent methods to be c-variadic --- .../rustc_ast_passes/src/ast_validation.rs | 54 ++++++++++--------- tests/ui/c-variadic/inherent-method.rs | 45 ++++++++++++++++ tests/ui/c-variadic/not-async.rs | 10 +++- tests/ui/c-variadic/not-async.stderr | 30 ++++++++--- tests/ui/c-variadic/trait-method.rs | 40 ++++++++++++++ tests/ui/c-variadic/trait-method.stderr | 14 +++++ .../variadic-ffi-semantic-restrictions.rs | 10 ++-- .../variadic-ffi-semantic-restrictions.stderr | 20 +++++-- 8 files changed, 179 insertions(+), 44 deletions(-) create mode 100644 tests/ui/c-variadic/inherent-method.rs create mode 100644 tests/ui/c-variadic/trait-method.rs create mode 100644 tests/ui/c-variadic/trait-method.stderr diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index a6ef89b553db..cdb3062c5917 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -696,36 +696,38 @@ impl<'a> AstValidator<'a> { match fn_ctxt { FnCtxt::Foreign => return, - FnCtxt::Free => match sig.header.ext { - Extern::Implicit(_) => { - if !matches!(sig.header.safety, Safety::Unsafe(_)) { - self.dcx().emit_err(errors::CVariadicMustBeUnsafe { - span: variadic_param.span, - unsafe_span: sig.safety_span(), - }); - } - } - Extern::Explicit(StrLit { symbol_unescaped, .. }, _) => { - if !matches!(symbol_unescaped, sym::C | sym::C_dash_unwind) { - self.dcx().emit_err(errors::CVariadicBadExtern { - span: variadic_param.span, - abi: symbol_unescaped, - extern_span: sig.extern_span(), - }); + FnCtxt::Free | FnCtxt::Assoc(AssocCtxt::Impl { of_trait: false }) => { + match sig.header.ext { + Extern::Implicit(_) => { + if !matches!(sig.header.safety, Safety::Unsafe(_)) { + self.dcx().emit_err(errors::CVariadicMustBeUnsafe { + span: variadic_param.span, + unsafe_span: sig.safety_span(), + }); + } } + Extern::Explicit(StrLit { symbol_unescaped, .. }, _) => { + if !matches!(symbol_unescaped, sym::C | sym::C_dash_unwind) { + self.dcx().emit_err(errors::CVariadicBadExtern { + span: variadic_param.span, + abi: symbol_unescaped, + extern_span: sig.extern_span(), + }); + } - if !matches!(sig.header.safety, Safety::Unsafe(_)) { - self.dcx().emit_err(errors::CVariadicMustBeUnsafe { - span: variadic_param.span, - unsafe_span: sig.safety_span(), - }); + if !matches!(sig.header.safety, Safety::Unsafe(_)) { + self.dcx().emit_err(errors::CVariadicMustBeUnsafe { + span: variadic_param.span, + unsafe_span: sig.safety_span(), + }); + } + } + Extern::None => { + let err = errors::CVariadicNoExtern { span: variadic_param.span }; + self.dcx().emit_err(err); } } - Extern::None => { - let err = errors::CVariadicNoExtern { span: variadic_param.span }; - self.dcx().emit_err(err); - } - }, + } FnCtxt::Assoc(_) => { // For now, C variable argument lists are unsupported in associated functions. let err = errors::CVariadicAssociatedFunction { span: variadic_param.span }; diff --git a/tests/ui/c-variadic/inherent-method.rs b/tests/ui/c-variadic/inherent-method.rs new file mode 100644 index 000000000000..537bae7b3f0f --- /dev/null +++ b/tests/ui/c-variadic/inherent-method.rs @@ -0,0 +1,45 @@ +//@ run-pass +#![feature(c_variadic)] + +#[repr(transparent)] +struct S(i32); + +impl S { + unsafe extern "C" fn associated_function(mut ap: ...) -> i32 { + unsafe { ap.arg() } + } + + unsafe extern "C" fn method_owned(self, mut ap: ...) -> i32 { + self.0 + unsafe { ap.arg::() } + } + + unsafe extern "C" fn method_ref(&self, mut ap: ...) -> i32 { + self.0 + unsafe { ap.arg::() } + } + + unsafe extern "C" fn method_mut(&mut self, mut ap: ...) -> i32 { + self.0 + unsafe { ap.arg::() } + } + + unsafe extern "C" fn fat_pointer(self: Box, mut ap: ...) -> i32 { + self.0 + unsafe { ap.arg::() } + } +} + +fn main() { + unsafe { + assert_eq!(S::associated_function(32), 32); + assert_eq!(S(100).method_owned(32), 132); + assert_eq!(S(100).method_ref(32), 132); + assert_eq!(S(100).method_mut(32), 132); + assert_eq!(S::fat_pointer(Box::new(S(100)), 32), 132); + + type Method = unsafe extern "C" fn(T, ...) -> i32; + + assert_eq!((S::associated_function as unsafe extern "C" fn(...) -> i32)(32), 32); + assert_eq!((S::method_owned as Method<_>)(S(100), 32), 132); + assert_eq!((S::method_ref as Method<_>)(&S(100), 32), 132); + assert_eq!((S::method_mut as Method<_>)(&mut S(100), 32), 132); + assert_eq!((S::fat_pointer as Method<_>)(Box::new(S(100)), 32), 132); + } +} diff --git a/tests/ui/c-variadic/not-async.rs b/tests/ui/c-variadic/not-async.rs index 45a7e1f8972b..bdb51a9a4323 100644 --- a/tests/ui/c-variadic/not-async.rs +++ b/tests/ui/c-variadic/not-async.rs @@ -2,6 +2,14 @@ #![feature(c_variadic)] #![crate_type = "lib"] -async unsafe extern "C" fn cannot_be_async(x: isize, ...) {} +async unsafe extern "C" fn fn_cannot_be_async(x: isize, ...) {} //~^ ERROR functions cannot be both `async` and C-variadic //~| ERROR hidden type for `impl Future` captures lifetime that does not appear in bounds + +struct S; + +impl S { + async unsafe extern "C" fn method_cannot_be_async(x: isize, ...) {} + //~^ ERROR functions cannot be both `async` and C-variadic + //~| ERROR hidden type for `impl Future` captures lifetime that does not appear in bounds +} diff --git a/tests/ui/c-variadic/not-async.stderr b/tests/ui/c-variadic/not-async.stderr index b8caf0d8bd85..eab4c4f0822c 100644 --- a/tests/ui/c-variadic/not-async.stderr +++ b/tests/ui/c-variadic/not-async.stderr @@ -1,19 +1,35 @@ error: functions cannot be both `async` and C-variadic --> $DIR/not-async.rs:5:1 | -LL | async unsafe extern "C" fn cannot_be_async(x: isize, ...) {} - | ^^^^^ `async` because of this ^^^ C-variadic because of this +LL | async unsafe extern "C" fn fn_cannot_be_async(x: isize, ...) {} + | ^^^^^ `async` because of this ^^^ C-variadic because of this + +error: functions cannot be both `async` and C-variadic + --> $DIR/not-async.rs:12:5 + | +LL | async unsafe extern "C" fn method_cannot_be_async(x: isize, ...) {} + | ^^^^^ `async` because of this ^^^ C-variadic because of this error[E0700]: hidden type for `impl Future` captures lifetime that does not appear in bounds - --> $DIR/not-async.rs:5:59 + --> $DIR/not-async.rs:5:62 | -LL | async unsafe extern "C" fn cannot_be_async(x: isize, ...) {} - | --------------------------------------------------------- ^^ +LL | async unsafe extern "C" fn fn_cannot_be_async(x: isize, ...) {} + | ------------------------------------------------------------ ^^ | | | opaque type defined here | - = note: hidden type `{async fn body of cannot_be_async()}` captures lifetime `'_` + = note: hidden type `{async fn body of fn_cannot_be_async()}` captures lifetime `'_` -error: aborting due to 2 previous errors +error[E0700]: hidden type for `impl Future` captures lifetime that does not appear in bounds + --> $DIR/not-async.rs:12:70 + | +LL | async unsafe extern "C" fn method_cannot_be_async(x: isize, ...) {} + | ---------------------------------------------------------------- ^^ + | | + | opaque type defined here + | + = note: hidden type `{async fn body of S::method_cannot_be_async()}` captures lifetime `'_` + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0700`. diff --git a/tests/ui/c-variadic/trait-method.rs b/tests/ui/c-variadic/trait-method.rs new file mode 100644 index 000000000000..4c468c1907bd --- /dev/null +++ b/tests/ui/c-variadic/trait-method.rs @@ -0,0 +1,40 @@ +// For now C-variadic arguments in trait methods are rejected, though we aim to lift this +// restriction in the future. In particular we need to think about the interaction with +// `dyn Trait` and the `ReifyShim`s that it may generate for methods. +#![feature(c_variadic)] +#![crate_type = "lib"] +struct S; + +impl S { + unsafe extern "C" fn associated_function(mut ap: ...) -> i32 { + unsafe { ap.arg() } + } + + unsafe extern "C" fn method(&self, mut ap: ...) -> i32 { + unsafe { ap.arg() } + } +} + +trait T { + unsafe extern "C" fn trait_associated_function(mut ap: ...) -> i32 { + //~^ ERROR: associated functions cannot have a C variable argument list + unsafe { ap.arg() } + } + + unsafe extern "C" fn trait_method(&self, mut ap: ...) -> i32 { + //~^ ERROR: associated functions cannot have a C variable argument list + unsafe { ap.arg() } + } +} + +impl T for S {} + +fn main() { + unsafe { + assert_eq!(S::associated_function(32), 32); + assert_eq!(S.method(32), 32); + + assert_eq!(S::trait_associated_function(32), 32); + assert_eq!(S.trait_method(32), 32); + } +} diff --git a/tests/ui/c-variadic/trait-method.stderr b/tests/ui/c-variadic/trait-method.stderr new file mode 100644 index 000000000000..35f6e24ef710 --- /dev/null +++ b/tests/ui/c-variadic/trait-method.stderr @@ -0,0 +1,14 @@ +error: associated functions cannot have a C variable argument list + --> $DIR/trait-method.rs:19:52 + | +LL | unsafe extern "C" fn trait_associated_function(mut ap: ...) -> i32 { + | ^^^^^^^^^^^ + +error: associated functions cannot have a C variable argument list + --> $DIR/trait-method.rs:24:46 + | +LL | unsafe extern "C" fn trait_method(&self, mut ap: ...) -> i32 { + | ^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs index 4db056f15a51..566c4d30245c 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs @@ -53,17 +53,17 @@ struct X; impl X { fn i_f1(x: isize, ...) {} - //~^ ERROR associated functions cannot have a C variable argument list + //~^ ERROR `...` is not supported for non-extern functions fn i_f2(...) {} - //~^ ERROR associated functions cannot have a C variable argument list + //~^ ERROR `...` is not supported for non-extern functions fn i_f3(..., x: isize, ...) {} - //~^ ERROR associated functions cannot have a C variable argument list + //~^ ERROR `...` is not supported for non-extern functions //~| ERROR `...` must be the last argument of a C-variadic function fn i_f4(..., x: isize, ...) {} - //~^ ERROR associated functions cannot have a C variable argument list + //~^ ERROR `...` is not supported for non-extern functions //~| ERROR `...` must be the last argument of a C-variadic function const fn i_f5(x: isize, ...) {} - //~^ ERROR associated functions cannot have a C variable argument list + //~^ ERROR `...` is not supported for non-extern functions //~| ERROR functions cannot be both `const` and C-variadic //~| ERROR destructor of `VaListImpl<'_>` cannot be evaluated at compile-time } diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr index 0cd78318de6a..c18d857d14e0 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr @@ -132,17 +132,21 @@ error: `...` must be the last argument of a C-variadic function LL | fn e_f2(..., x: isize); | ^^^ -error: associated functions cannot have a C variable argument list +error: `...` is not supported for non-extern functions --> $DIR/variadic-ffi-semantic-restrictions.rs:55:23 | LL | fn i_f1(x: isize, ...) {} | ^^^ + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list -error: associated functions cannot have a C variable argument list +error: `...` is not supported for non-extern functions --> $DIR/variadic-ffi-semantic-restrictions.rs:57:13 | LL | fn i_f2(...) {} | ^^^ + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error: `...` must be the last argument of a C-variadic function --> $DIR/variadic-ffi-semantic-restrictions.rs:59:13 @@ -150,11 +154,13 @@ error: `...` must be the last argument of a C-variadic function LL | fn i_f3(..., x: isize, ...) {} | ^^^ -error: associated functions cannot have a C variable argument list +error: `...` is not supported for non-extern functions --> $DIR/variadic-ffi-semantic-restrictions.rs:59:28 | LL | fn i_f3(..., x: isize, ...) {} | ^^^ + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error: `...` must be the last argument of a C-variadic function --> $DIR/variadic-ffi-semantic-restrictions.rs:62:13 @@ -162,11 +168,13 @@ error: `...` must be the last argument of a C-variadic function LL | fn i_f4(..., x: isize, ...) {} | ^^^ -error: associated functions cannot have a C variable argument list +error: `...` is not supported for non-extern functions --> $DIR/variadic-ffi-semantic-restrictions.rs:62:28 | LL | fn i_f4(..., x: isize, ...) {} | ^^^ + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error: functions cannot be both `const` and C-variadic --> $DIR/variadic-ffi-semantic-restrictions.rs:65:5 @@ -176,11 +184,13 @@ LL | const fn i_f5(x: isize, ...) {} | | | `const` because of this -error: associated functions cannot have a C variable argument list +error: `...` is not supported for non-extern functions --> $DIR/variadic-ffi-semantic-restrictions.rs:65:29 | LL | const fn i_f5(x: isize, ...) {} | ^^^ + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error: associated functions cannot have a C variable argument list --> $DIR/variadic-ffi-semantic-restrictions.rs:72:23 From 01e83adc88653123fee444fdb930c16dd08da82d Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 10 Sep 2025 17:53:41 +0200 Subject: [PATCH 386/682] c-variadic: allow trait methods to be c-variadic but a C-variadic method makes a trait dyn-incompatible. That is because methods from dyn traits, when cast to a function pointer, create a shim. That shim can't really forward the c-variadic arguments. --- compiler/rustc_ast_passes/messages.ftl | 2 - .../rustc_ast_passes/src/ast_validation.rs | 63 ++++++++--------- compiler/rustc_ast_passes/src/errors.rs | 7 -- compiler/rustc_middle/src/traits/mod.rs | 6 ++ .../src/traits/dyn_compatibility.rs | 3 + tests/ui/c-variadic/not-dyn-compatible.rs | 35 ++++++++++ tests/ui/c-variadic/not-dyn-compatible.stderr | 21 ++++++ tests/ui/c-variadic/trait-method.rs | 67 ++++++++++++++----- tests/ui/c-variadic/trait-method.stderr | 14 ---- .../feature-gates/feature-gate-c_variadic.rs | 1 - .../feature-gate-c_variadic.stderr | 8 +-- .../variadic-ffi-semantic-restrictions.rs | 8 +-- .../variadic-ffi-semantic-restrictions.stderr | 16 +++-- 13 files changed, 160 insertions(+), 91 deletions(-) create mode 100644 tests/ui/c-variadic/not-dyn-compatible.rs create mode 100644 tests/ui/c-variadic/not-dyn-compatible.stderr delete mode 100644 tests/ui/c-variadic/trait-method.stderr diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 8dcf3e3aa388..ba79fc63fad5 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -64,8 +64,6 @@ ast_passes_body_in_extern = incorrect `{$kind}` inside `extern` block ast_passes_bound_in_context = bounds on `type`s in {$ctx} have no effect -ast_passes_c_variadic_associated_function = associated functions cannot have a C variable argument list - ast_passes_c_variadic_bad_extern = `...` is not supported for `extern "{$abi}"` functions .label = `extern "{$abi}"` because of this .help = only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index cdb3062c5917..c6dd625c2286 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -696,43 +696,36 @@ impl<'a> AstValidator<'a> { match fn_ctxt { FnCtxt::Foreign => return, - FnCtxt::Free | FnCtxt::Assoc(AssocCtxt::Impl { of_trait: false }) => { - match sig.header.ext { - Extern::Implicit(_) => { - if !matches!(sig.header.safety, Safety::Unsafe(_)) { - self.dcx().emit_err(errors::CVariadicMustBeUnsafe { - span: variadic_param.span, - unsafe_span: sig.safety_span(), - }); - } - } - Extern::Explicit(StrLit { symbol_unescaped, .. }, _) => { - if !matches!(symbol_unescaped, sym::C | sym::C_dash_unwind) { - self.dcx().emit_err(errors::CVariadicBadExtern { - span: variadic_param.span, - abi: symbol_unescaped, - extern_span: sig.extern_span(), - }); - } - - if !matches!(sig.header.safety, Safety::Unsafe(_)) { - self.dcx().emit_err(errors::CVariadicMustBeUnsafe { - span: variadic_param.span, - unsafe_span: sig.safety_span(), - }); - } - } - Extern::None => { - let err = errors::CVariadicNoExtern { span: variadic_param.span }; - self.dcx().emit_err(err); + FnCtxt::Free | FnCtxt::Assoc(_) => match sig.header.ext { + Extern::Implicit(_) => { + if !matches!(sig.header.safety, Safety::Unsafe(_)) { + self.dcx().emit_err(errors::CVariadicMustBeUnsafe { + span: variadic_param.span, + unsafe_span: sig.safety_span(), + }); } } - } - FnCtxt::Assoc(_) => { - // For now, C variable argument lists are unsupported in associated functions. - let err = errors::CVariadicAssociatedFunction { span: variadic_param.span }; - self.dcx().emit_err(err); - } + Extern::Explicit(StrLit { symbol_unescaped, .. }, _) => { + if !matches!(symbol_unescaped, sym::C | sym::C_dash_unwind) { + self.dcx().emit_err(errors::CVariadicBadExtern { + span: variadic_param.span, + abi: symbol_unescaped, + extern_span: sig.extern_span(), + }); + } + + if !matches!(sig.header.safety, Safety::Unsafe(_)) { + self.dcx().emit_err(errors::CVariadicMustBeUnsafe { + span: variadic_param.span, + unsafe_span: sig.safety_span(), + }); + } + } + Extern::None => { + let err = errors::CVariadicNoExtern { span: variadic_param.span }; + self.dcx().emit_err(err); + } + }, } } diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index ae805042c549..b1b1ad60c248 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -318,13 +318,6 @@ pub(crate) struct ExternItemAscii { pub block: Span, } -#[derive(Diagnostic)] -#[diag(ast_passes_c_variadic_associated_function)] -pub(crate) struct CVariadicAssociatedFunction { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(ast_passes_c_variadic_no_extern)] #[help] diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index ab8a31429539..0c7bddf60d97 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -823,6 +823,9 @@ impl DynCompatibilityViolation { DynCompatibilityViolation::Method(name, MethodViolationCode::AsyncFn, _) => { format!("method `{name}` is `async`").into() } + DynCompatibilityViolation::Method(name, MethodViolationCode::CVariadic, _) => { + format!("method `{name}` is C-variadic").into() + } DynCompatibilityViolation::Method( name, MethodViolationCode::WhereClauseReferencesSelf, @@ -977,6 +980,9 @@ pub enum MethodViolationCode { /// e.g., `fn foo
()` Generic, + /// e.g., `fn (mut ap: ...)` + CVariadic, + /// the method's receiver (`self` argument) can't be dispatched on UndispatchableReceiver(Option), } diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index bcd11d6918d9..3260dd712b9e 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -426,6 +426,9 @@ fn virtual_call_violations_for_method<'tcx>( if let Some(code) = contains_illegal_impl_trait_in_trait(tcx, method.def_id, sig.output()) { errors.push(code); } + if sig.skip_binder().c_variadic { + errors.push(MethodViolationCode::CVariadic); + } // We can't monomorphize things like `fn foo(...)`. let own_counts = tcx.generics_of(method.def_id).own_counts(); diff --git a/tests/ui/c-variadic/not-dyn-compatible.rs b/tests/ui/c-variadic/not-dyn-compatible.rs new file mode 100644 index 000000000000..b40a13e58476 --- /dev/null +++ b/tests/ui/c-variadic/not-dyn-compatible.rs @@ -0,0 +1,35 @@ +// Traits where a method is c-variadic are not dyn compatible. +// +// Creating a function pointer from a method on an `&dyn T` value creates a ReifyShim. +// This shim cannot reliably forward C-variadic arguments. Thus the trait as a whole +// is dyn-incompatible to prevent invalid shims from being created. +#![feature(c_variadic)] + +#[repr(transparent)] +struct Struct(u64); + +trait Trait { + fn get(&self) -> u64; + + unsafe extern "C" fn dyn_method_ref(&self, mut ap: ...) -> u64 { + self.get() + unsafe { ap.arg::() } + } +} + +impl Trait for Struct { + fn get(&self) -> u64 { + self.0 + } +} + +fn main() { + unsafe { + let dyn_object: &dyn Trait = &Struct(64); + //~^ ERROR the trait `Trait` is not dyn compatible + assert_eq!(dyn_object.dyn_method_ref(100), 164); + assert_eq!( + (Trait::dyn_method_ref as unsafe extern "C" fn(_, ...) -> u64)(dyn_object, 100), + 164 + ); + } +} diff --git a/tests/ui/c-variadic/not-dyn-compatible.stderr b/tests/ui/c-variadic/not-dyn-compatible.stderr new file mode 100644 index 000000000000..76630600c511 --- /dev/null +++ b/tests/ui/c-variadic/not-dyn-compatible.stderr @@ -0,0 +1,21 @@ +error[E0038]: the trait `Trait` is not dyn compatible + --> $DIR/not-dyn-compatible.rs:27:30 + | +LL | let dyn_object: &dyn Trait = &Struct(64); + | ^^^^^ `Trait` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit + --> $DIR/not-dyn-compatible.rs:14:26 + | +LL | trait Trait { + | ----- this trait is not dyn compatible... +... +LL | unsafe extern "C" fn dyn_method_ref(&self, mut ap: ...) -> u64 { + | ^^^^^^^^^^^^^^ ...because method `dyn_method_ref` is C-variadic + = help: consider moving `dyn_method_ref` to another trait + = help: only type `Struct` implements `Trait`; consider using it directly instead. + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/c-variadic/trait-method.rs b/tests/ui/c-variadic/trait-method.rs index 4c468c1907bd..97da0706a3a1 100644 --- a/tests/ui/c-variadic/trait-method.rs +++ b/tests/ui/c-variadic/trait-method.rs @@ -1,40 +1,73 @@ -// For now C-variadic arguments in trait methods are rejected, though we aim to lift this -// restriction in the future. In particular we need to think about the interaction with -// `dyn Trait` and the `ReifyShim`s that it may generate for methods. +//@ run-pass #![feature(c_variadic)] -#![crate_type = "lib"] -struct S; -impl S { +#[repr(transparent)] +struct Struct(i32); + +impl Struct { unsafe extern "C" fn associated_function(mut ap: ...) -> i32 { unsafe { ap.arg() } } unsafe extern "C" fn method(&self, mut ap: ...) -> i32 { - unsafe { ap.arg() } + self.0 + unsafe { ap.arg::() } } } -trait T { +trait Trait: Sized { + fn get(&self) -> i32; + unsafe extern "C" fn trait_associated_function(mut ap: ...) -> i32 { - //~^ ERROR: associated functions cannot have a C variable argument list unsafe { ap.arg() } } - unsafe extern "C" fn trait_method(&self, mut ap: ...) -> i32 { - //~^ ERROR: associated functions cannot have a C variable argument list - unsafe { ap.arg() } + unsafe extern "C" fn trait_method_owned(self, mut ap: ...) -> i32 { + self.get() + unsafe { ap.arg::() } + } + + unsafe extern "C" fn trait_method_ref(&self, mut ap: ...) -> i32 { + self.get() + unsafe { ap.arg::() } + } + + unsafe extern "C" fn trait_method_mut(&mut self, mut ap: ...) -> i32 { + self.get() + unsafe { ap.arg::() } + } + + unsafe extern "C" fn trait_fat_pointer(self: Box, mut ap: ...) -> i32 { + self.get() + unsafe { ap.arg::() } } } -impl T for S {} +impl Trait for Struct { + fn get(&self) -> i32 { + self.0 + } +} fn main() { unsafe { - assert_eq!(S::associated_function(32), 32); - assert_eq!(S.method(32), 32); + assert_eq!(Struct::associated_function(32), 32); + assert_eq!(Struct(100).method(32), 132); - assert_eq!(S::trait_associated_function(32), 32); - assert_eq!(S.trait_method(32), 32); + assert_eq!(Struct::trait_associated_function(32), 32); + assert_eq!(Struct(100).trait_method_owned(32), 132); + assert_eq!(Struct(100).trait_method_ref(32), 132); + assert_eq!(Struct(100).trait_method_mut(32), 132); + assert_eq!(Struct::trait_fat_pointer(Box::new(Struct(100)), 32), 132); + + assert_eq!(::trait_associated_function(32), 32); + assert_eq!(Trait::trait_method_owned(Struct(100), 32), 132); + assert_eq!(Trait::trait_method_ref(&Struct(100), 32), 132); + assert_eq!(Trait::trait_method_mut(&mut Struct(100), 32), 132); + assert_eq!(Trait::trait_fat_pointer(Box::new(Struct(100)), 32), 132); + + type Associated = unsafe extern "C" fn(...) -> i32; + type Method = unsafe extern "C" fn(T, ...) -> i32; + + assert_eq!((Struct::trait_associated_function as Associated)(32), 32); + assert_eq!((Struct::trait_method_owned as Method<_>)(Struct(100), 32), 132); + assert_eq!((Struct::trait_method_ref as Method<_>)(&Struct(100), 32), 132); + assert_eq!((Struct::trait_method_mut as Method<_>)(&mut Struct(100), 32), 132); + assert_eq!((Struct::trait_fat_pointer as Method<_>)(Box::new(Struct(100)), 32), 132); } } diff --git a/tests/ui/c-variadic/trait-method.stderr b/tests/ui/c-variadic/trait-method.stderr deleted file mode 100644 index 35f6e24ef710..000000000000 --- a/tests/ui/c-variadic/trait-method.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: associated functions cannot have a C variable argument list - --> $DIR/trait-method.rs:19:52 - | -LL | unsafe extern "C" fn trait_associated_function(mut ap: ...) -> i32 { - | ^^^^^^^^^^^ - -error: associated functions cannot have a C variable argument list - --> $DIR/trait-method.rs:24:46 - | -LL | unsafe extern "C" fn trait_method(&self, mut ap: ...) -> i32 { - | ^^^^^^^^^^^ - -error: aborting due to 2 previous errors - diff --git a/tests/ui/feature-gates/feature-gate-c_variadic.rs b/tests/ui/feature-gates/feature-gate-c_variadic.rs index 88d91dbd081f..649816b48d78 100644 --- a/tests/ui/feature-gates/feature-gate-c_variadic.rs +++ b/tests/ui/feature-gates/feature-gate-c_variadic.rs @@ -6,5 +6,4 @@ pub unsafe extern "C" fn test(_: i32, ap: ...) {} trait Trait { unsafe extern "C" fn trait_test(_: i32, ap: ...) {} //~^ ERROR C-variadic functions are unstable - //~| ERROR associated functions cannot have a C variable argument list } diff --git a/tests/ui/feature-gates/feature-gate-c_variadic.stderr b/tests/ui/feature-gates/feature-gate-c_variadic.stderr index 808aa20948d0..ae880093b980 100644 --- a/tests/ui/feature-gates/feature-gate-c_variadic.stderr +++ b/tests/ui/feature-gates/feature-gate-c_variadic.stderr @@ -1,9 +1,3 @@ -error: associated functions cannot have a C variable argument list - --> $DIR/feature-gate-c_variadic.rs:7:45 - | -LL | unsafe extern "C" fn trait_test(_: i32, ap: ...) {} - | ^^^^^^^ - error[E0658]: C-variadic functions are unstable --> $DIR/feature-gate-c_variadic.rs:3:1 | @@ -24,6 +18,6 @@ LL | unsafe extern "C" fn trait_test(_: i32, ap: ...) {} = help: add `#![feature(c_variadic)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs index 566c4d30245c..415472176d9c 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs @@ -70,13 +70,13 @@ impl X { trait T { fn t_f1(x: isize, ...) {} - //~^ ERROR associated functions cannot have a C variable argument list + //~^ ERROR `...` is not supported for non-extern functions fn t_f2(x: isize, ...); - //~^ ERROR associated functions cannot have a C variable argument list + //~^ ERROR `...` is not supported for non-extern functions fn t_f3(...) {} - //~^ ERROR associated functions cannot have a C variable argument list + //~^ ERROR `...` is not supported for non-extern functions fn t_f4(...); - //~^ ERROR associated functions cannot have a C variable argument list + //~^ ERROR `...` is not supported for non-extern functions fn t_f5(..., x: isize) {} //~^ ERROR `...` must be the last argument of a C-variadic function fn t_f6(..., x: isize); diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr index c18d857d14e0..da9c9b0f7609 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr @@ -192,29 +192,37 @@ LL | const fn i_f5(x: isize, ...) {} | = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list -error: associated functions cannot have a C variable argument list +error: `...` is not supported for non-extern functions --> $DIR/variadic-ffi-semantic-restrictions.rs:72:23 | LL | fn t_f1(x: isize, ...) {} | ^^^ + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list -error: associated functions cannot have a C variable argument list +error: `...` is not supported for non-extern functions --> $DIR/variadic-ffi-semantic-restrictions.rs:74:23 | LL | fn t_f2(x: isize, ...); | ^^^ + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list -error: associated functions cannot have a C variable argument list +error: `...` is not supported for non-extern functions --> $DIR/variadic-ffi-semantic-restrictions.rs:76:13 | LL | fn t_f3(...) {} | ^^^ + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list -error: associated functions cannot have a C variable argument list +error: `...` is not supported for non-extern functions --> $DIR/variadic-ffi-semantic-restrictions.rs:78:13 | LL | fn t_f4(...); | ^^^ + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error: `...` must be the last argument of a C-variadic function --> $DIR/variadic-ffi-semantic-restrictions.rs:80:13 From ad6004d95343031f3ac0898fe3582410fcc28f0d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 11 Sep 2025 11:11:40 +0200 Subject: [PATCH 387/682] add release sequence test --- src/tools/miri/src/concurrency/data_race.rs | 9 +++-- src/tools/miri/src/concurrency/weak_memory.rs | 8 +++-- .../miri/tests/pass/0weak_memory/weak.rs | 33 +++++++++++++++++++ 3 files changed, 46 insertions(+), 4 deletions(-) diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs index de60e80b9f7e..16cac7e6d04c 100644 --- a/src/tools/miri/src/concurrency/data_race.rs +++ b/src/tools/miri/src/concurrency/data_race.rs @@ -276,7 +276,7 @@ struct MemoryCellClocks { /// zero on each write operation. read: VClock, - /// Atomic access, acquire, release sequence tracking clocks. + /// Atomic access tracking clocks. /// For non-atomic memory this value is set to None. /// For atomic memory, each byte carries this information. atomic_ops: Option>, @@ -555,7 +555,8 @@ impl MemoryCellClocks { // The handling of release sequences was changed in C++20 and so // the code here is different to the paper since now all relaxed // stores block release sequences. The exception for same-thread - // relaxed stores has been removed. + // relaxed stores has been removed. We always overwrite the `sync_vector`, + // meaning the previous release sequence is broken. let atomic = self.atomic_mut_unwrap(); atomic.sync_vector.clone_from(&thread_clocks.fence_release); Ok(()) @@ -571,6 +572,8 @@ impl MemoryCellClocks { ) -> Result<(), DataRace> { self.atomic_write_detect(thread_clocks, index, access_size)?; let atomic = self.atomic_mut_unwrap(); + // This *joining* of `sync_vector` implements release sequences: future + // reads of this location will acquire our clock *and* what was here before. atomic.sync_vector.join(&thread_clocks.clock); Ok(()) } @@ -585,6 +588,8 @@ impl MemoryCellClocks { ) -> Result<(), DataRace> { self.atomic_write_detect(thread_clocks, index, access_size)?; let atomic = self.atomic_mut_unwrap(); + // This *joining* of `sync_vector` implements release sequences: future + // reads of this location will acquire our fence clock *and* what was here before. atomic.sync_vector.join(&thread_clocks.fence_release); Ok(()) } diff --git a/src/tools/miri/src/concurrency/weak_memory.rs b/src/tools/miri/src/concurrency/weak_memory.rs index a752ef7e4548..caa27422d2f2 100644 --- a/src/tools/miri/src/concurrency/weak_memory.rs +++ b/src/tools/miri/src/concurrency/weak_memory.rs @@ -159,9 +159,12 @@ struct StoreElement { #[derive(Debug, Clone, PartialEq, Eq, Default)] struct LoadInfo { - /// Timestamp of first loads from this store element by each thread + /// Timestamp of first loads from this store element by each thread. timestamps: FxHashMap, - /// Whether this store element has been read by an SC load + /// Whether this store element has been read by an SC load. + /// This is crucial to ensure we respect coherence-ordered-before. Concretely we use + /// this to ensure that if a store element is seen by an SC load, then all later SC loads + /// cannot see `mo`-earlier store elements. sc_loaded: bool, } @@ -476,6 +479,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } let range = alloc_range(base_offset, place.layout.size); let buffer = alloc_buffers.get_or_create_store_buffer_mut(range, Some(init))?; + // The RMW always reads from the most recent store. buffer.read_from_last_store(global, threads, atomic == AtomicRwOrd::SeqCst); buffer.buffered_write(new_val, global, threads, atomic == AtomicRwOrd::SeqCst)?; } diff --git a/src/tools/miri/tests/pass/0weak_memory/weak.rs b/src/tools/miri/tests/pass/0weak_memory/weak.rs index 75c6e8f1a879..44791d7cd7c5 100644 --- a/src/tools/miri/tests/pass/0weak_memory/weak.rs +++ b/src/tools/miri/tests/pass/0weak_memory/weak.rs @@ -198,11 +198,44 @@ fn weaker_release_sequences() { }); } +/// Ensuring normal release sequences (with RMWs) still work correctly. +fn release_sequence() { + check_all_outcomes([None, Some(1)], || { + let x = static_atomic(0); + let y = static_atomic(0); + + let t1 = spawn(move || { + x.store(2, Relaxed); + }); + let t2 = spawn(move || { + y.store(1, Relaxed); + x.store(1, Release); + x.swap(3, Relaxed); + }); + let t3 = spawn(move || { + if x.load(Acquire) == 3 { + // If we read 3 here, we are seeing the result of the `x.swap` above, which + // was relaxed but forms a release sequence with the `x.store` (since we know + // `t1` will not be scheduled in between). This means there is a release sequence, + // so we acquire the `y.store` and cannot see the original value `0` any more. + Some(y.load(Relaxed)) + } else { + None + } + }); + + t1.join().unwrap(); + t2.join().unwrap(); + t3.join().unwrap() + }); +} + pub fn main() { relaxed(); seq_cst(); initialization_write(false); initialization_write(true); faa_replaced_by_load(); + release_sequence(); weaker_release_sequences(); } From 8d1403cb9b42b99c7b35139ac93f8102b589f007 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 11 Sep 2025 11:43:53 +0200 Subject: [PATCH 388/682] Change the default value of `gcc.download-ci-gcc` to `true` --- src/bootstrap/defaults/bootstrap.dist.toml | 4 ++++ src/bootstrap/src/core/config/mod.rs | 2 +- src/bootstrap/src/utils/change_tracker.rs | 5 +++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/defaults/bootstrap.dist.toml b/src/bootstrap/defaults/bootstrap.dist.toml index 9daf9faac14a..b111a20f8d86 100644 --- a/src/bootstrap/defaults/bootstrap.dist.toml +++ b/src/bootstrap/defaults/bootstrap.dist.toml @@ -14,6 +14,10 @@ compiletest-use-stage0-libtest = false [llvm] download-ci-llvm = false +# Most users installing from source want to build all parts of the project from source. +[gcc] +download-ci-gcc = false + [rust] # We have several defaults in bootstrap that depend on whether the channel is `dev` (e.g. `omit-git-hash` and `download-ci-llvm`). # Make sure they don't get set when installing from source. diff --git a/src/bootstrap/src/core/config/mod.rs b/src/bootstrap/src/core/config/mod.rs index 5999348a7fe8..05a5dfc0bc5a 100644 --- a/src/bootstrap/src/core/config/mod.rs +++ b/src/bootstrap/src/core/config/mod.rs @@ -422,10 +422,10 @@ impl std::str::FromStr for RustcLto { #[derive(Default, Clone)] pub enum GccCiMode { /// Build GCC from the local `src/gcc` submodule. - #[default] BuildLocally, /// Try to download GCC from CI. /// If it is not available on CI, it will be built locally instead. + #[default] DownloadFromCi, } diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 01309072927d..03b39882e30a 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -541,4 +541,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "Added a new option `rust.break-on-ice` to control if internal compiler errors cause a debug break on Windows.", }, + ChangeInfo { + change_id: 146435, + severity: ChangeSeverity::Info, + summary: "The default value of the `gcc.download-ci-gcc` option has been changed to `true`.", + }, ]; From 0e29865434db8d44109715515ea00479936fa668 Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 10 Sep 2025 13:26:42 +0200 Subject: [PATCH 389/682] consider the `sub_unification_table` in `stalled_on` --- .../src/solve/eval_ctxt/mod.rs | 49 ++++++++++++------- .../rustc_next_trait_solver/src/solve/mod.rs | 8 +-- 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 5cd597d4fe6b..f1c1801dd3f1 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -407,20 +407,21 @@ where // If we have run this goal before, and it was stalled, check that any of the goal's // args have changed. Otherwise, we don't need to re-run the goal because it'll remain // stalled, since it'll canonicalize the same way and evaluation is pure. - if let Some(stalled_on) = stalled_on - && !stalled_on.stalled_vars.iter().any(|value| self.delegate.is_changed_arg(*value)) - && !self - .delegate - .opaque_types_storage_num_entries() - .needs_reevaluation(stalled_on.num_opaques) + if let Some(GoalStalledOn { num_opaques, ref stalled_vars, ref sub_roots, stalled_cause }) = + stalled_on + && !stalled_vars.iter().any(|value| self.delegate.is_changed_arg(*value)) + && !sub_roots + .iter() + .any(|&vid| self.delegate.sub_unification_table_root_var(vid) != vid) + && !self.delegate.opaque_types_storage_num_entries().needs_reevaluation(num_opaques) { return Ok(( NestedNormalizationGoals::empty(), GoalEvaluation { goal, - certainty: Certainty::Maybe(stalled_on.stalled_cause), + certainty: Certainty::Maybe(stalled_cause), has_changed: HasChanged::No, - stalled_on: Some(stalled_on), + stalled_on, }, )); } @@ -476,16 +477,6 @@ where HasChanged::No => { let mut stalled_vars = orig_values; - // Remove the canonicalized universal vars, since we only care about stalled existentials. - stalled_vars.retain(|arg| match arg.kind() { - ty::GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Infer(_)), - ty::GenericArgKind::Const(ct) => { - matches!(ct.kind(), ty::ConstKind::Infer(_)) - } - // Lifetimes can never stall goals. - ty::GenericArgKind::Lifetime(_) => false, - }); - // Remove the unconstrained RHS arg, which is expected to have changed. if let Some(normalizes_to) = goal.predicate.as_normalizes_to() { let normalizes_to = normalizes_to.skip_binder(); @@ -497,6 +488,27 @@ where stalled_vars.swap_remove(idx); } + // Remove the canonicalized universal vars, since we only care about stalled existentials. + let mut sub_roots = Vec::new(); + stalled_vars.retain(|arg| match arg.kind() { + // Lifetimes can never stall goals. + ty::GenericArgKind::Lifetime(_) => false, + ty::GenericArgKind::Type(ty) => match ty.kind() { + ty::Infer(ty::TyVar(vid)) => { + sub_roots.push(self.delegate.sub_unification_table_root_var(vid)); + true + } + ty::Infer(_) => true, + ty::Param(_) | ty::Placeholder(_) => false, + _ => unreachable!("unexpected orig_value: {ty:?}"), + }, + ty::GenericArgKind::Const(ct) => match ct.kind() { + ty::ConstKind::Infer(_) => true, + ty::ConstKind::Param(_) | ty::ConstKind::Placeholder(_) => false, + _ => unreachable!("unexpected orig_value: {ct:?}"), + }, + }); + Some(GoalStalledOn { num_opaques: canonical_goal .canonical @@ -505,6 +517,7 @@ where .opaque_types .len(), stalled_vars, + sub_roots, stalled_cause, }) } diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index db3460c4ff67..cd27c9c26c1f 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -24,7 +24,7 @@ mod trait_goals; use derive_where::derive_where; use rustc_type_ir::inherent::*; pub use rustc_type_ir::solve::*; -use rustc_type_ir::{self as ty, Interner, TypingMode}; +use rustc_type_ir::{self as ty, Interner, TyVid, TypingMode}; use tracing::instrument; pub use self::eval_ctxt::{ @@ -418,11 +418,6 @@ pub struct GoalEvaluation { pub has_changed: HasChanged, /// If the [`Certainty`] was `Maybe`, then keep track of whether the goal has changed /// before rerunning it. - /// - /// We knowingly ignore the `sub_root` of our inference variables here. This means we - /// may not reevaluate a goal even though a change to the `sub_root` could cause a goal - /// to make progress. Tracking them adds additional complexity for an incredibly minor - /// type inference improvement. We could look into properly handling this in the future. pub stalled_on: Option>, } @@ -431,6 +426,7 @@ pub struct GoalEvaluation { pub struct GoalStalledOn { pub num_opaques: usize, pub stalled_vars: Vec, + pub sub_roots: Vec, /// The cause that will be returned on subsequent evaluations if this goal remains stalled. pub stalled_cause: MaybeCause, } From cf224ea1fbe2fb7eb64812288338a2c4cfe1d084 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 28 Apr 2025 18:16:34 +0000 Subject: [PATCH 390/682] incompletely prefer opaque type bounds when self type bottoms out in infer --- compiler/rustc_middle/src/ty/context.rs | 6 +- .../src/solve/assembly/mod.rs | 137 +++++++++++++++++- .../src/solve/effect_goals.rs | 3 +- .../src/solve/eval_ctxt/mod.rs | 31 ++++ .../src/solve/normalizes_to/mod.rs | 7 +- .../src/solve/trait_goals.rs | 3 +- compiler/rustc_type_ir/src/interner.rs | 1 + ...nce-constraints-from-blanket-2.next.stderr | 16 ++ ...id-inference-constraints-from-blanket-2.rs | 31 ++++ ...id-inference-constraints-from-blanket-3.rs | 25 ++++ ...nference-constraints-from-blanket-3.stderr | 25 ++++ ...void-inference-constraints-from-blanket.rs | 25 ++++ .../multiple-opaques-ambig.rs | 23 +++ .../non-defining-uses/multiple-opaques-ok.rs | 37 +++++ .../non-defining-uses/no-rigid-alias.rs | 27 ++++ .../non-defining-uses/use-blanket-impl.rs | 19 +++ .../use-item-bound-over-blanket-impl.rs | 30 ++++ .../non-defining-uses/use-item-bound.rs | 25 ++++ 18 files changed, 457 insertions(+), 14 deletions(-) create mode 100644 tests/ui/impl-trait/non-defining-uses/avoid-inference-constraints-from-blanket-2.next.stderr create mode 100644 tests/ui/impl-trait/non-defining-uses/avoid-inference-constraints-from-blanket-2.rs create mode 100644 tests/ui/impl-trait/non-defining-uses/avoid-inference-constraints-from-blanket-3.rs create mode 100644 tests/ui/impl-trait/non-defining-uses/avoid-inference-constraints-from-blanket-3.stderr create mode 100644 tests/ui/impl-trait/non-defining-uses/avoid-inference-constraints-from-blanket.rs create mode 100644 tests/ui/impl-trait/non-defining-uses/multiple-opaques-ambig.rs create mode 100644 tests/ui/impl-trait/non-defining-uses/multiple-opaques-ok.rs create mode 100644 tests/ui/impl-trait/non-defining-uses/no-rigid-alias.rs create mode 100644 tests/ui/impl-trait/non-defining-uses/use-blanket-impl.rs create mode 100644 tests/ui/impl-trait/non-defining-uses/use-item-bound-over-blanket-impl.rs create mode 100644 tests/ui/impl-trait/non-defining-uses/use-item-bound.rs diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 218ac2cfbc19..8ea767dccd3b 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -651,7 +651,11 @@ impl<'tcx> Interner for TyCtxt<'tcx> { | ty::Bound(_, _) => bug!("unexpected self type: {self_ty}"), } - let trait_impls = tcx.trait_impls_of(trait_def_id); + #[allow(rustc::usage_of_type_ir_traits)] + self.for_each_blanket_impl(trait_def_id, f) + } + fn for_each_blanket_impl(self, trait_def_id: DefId, mut f: impl FnMut(DefId)) { + let trait_impls = self.trait_impls_of(trait_def_id); for &impl_def_id in trait_impls.blanket_impls() { f(impl_def_id); } diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index e3cf0330b14a..fb777496e31e 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -11,8 +11,9 @@ use rustc_type_ir::lang_items::SolverTraitLangItem; use rustc_type_ir::search_graph::CandidateHeadUsages; use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::{ - self as ty, Interner, TypeFlags, TypeFoldable, TypeSuperVisitable, TypeVisitable, - TypeVisitableExt as _, TypeVisitor, TypingMode, Upcast as _, elaborate, + self as ty, Interner, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, Upcast, + elaborate, }; use tracing::{debug, instrument}; @@ -187,6 +188,7 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal, impl_def_id: I::ImplId, + then: impl FnOnce(&mut EvalCtxt<'_, D>, Certainty) -> QueryResult, ) -> Result, NoSolution>; /// If the predicate contained an error, we want to avoid emitting unnecessary trait @@ -365,6 +367,15 @@ pub(super) enum AssembleCandidatesFrom { EnvAndBounds, } +impl AssembleCandidatesFrom { + fn should_assemble_impl_candidates(&self) -> bool { + match self { + AssembleCandidatesFrom::All => true, + AssembleCandidatesFrom::EnvAndBounds => false, + } + } +} + /// This is currently used to track the [CandidateHeadUsages] of all failed `ParamEnv` /// candidates. This is then used to ignore their head usages in case there's another /// always applicable `ParamEnv` candidate. Look at how `param_env_head_usages` is @@ -397,14 +408,15 @@ where return (candidates, failed_candidate_info); }; + let goal: Goal = goal + .with(self.cx(), goal.predicate.with_replaced_self_ty(self.cx(), normalized_self_ty)); + if normalized_self_ty.is_ty_var() { debug!("self type has been normalized to infer"); - candidates.extend(self.forced_ambiguity(MaybeCause::Ambiguity)); + self.try_assemble_bounds_via_registered_opaques(goal, assemble_from, &mut candidates); return (candidates, failed_candidate_info); } - let goal: Goal = goal - .with(self.cx(), goal.predicate.with_replaced_self_ty(self.cx(), normalized_self_ty)); // Vars that show up in the rest of the goal substs may have been constrained by // normalizing the self type as well, since type variables are not uniquified. let goal = self.resolve_vars_if_possible(goal); @@ -484,8 +496,9 @@ where if cx.impl_is_default(impl_def_id) { return; } - - match G::consider_impl_candidate(self, goal, impl_def_id) { + match G::consider_impl_candidate(self, goal, impl_def_id, |ecx, certainty| { + ecx.evaluate_added_goals_and_make_canonical_response(certainty) + }) { Ok(candidate) => candidates.push(candidate), Err(NoSolution) => (), } @@ -943,6 +956,116 @@ where } } + /// If the self type is the hidden type of an opaque, try to assemble + /// candidates for it by consider its item bounds and by using blanket + /// impls. This is used to incompletely guide type inference when handling + /// non-defining uses in the defining scope. + /// + /// We otherwise just fail fail with ambiguity. Even if we're using an + /// opaque type item bound or a blank impls, we still force its certainty + /// to be `Maybe` so that we properly prove this goal later. + /// + /// See + /// for why this is necessary. + fn try_assemble_bounds_via_registered_opaques>( + &mut self, + goal: Goal, + assemble_from: AssembleCandidatesFrom, + candidates: &mut Vec>, + ) { + let self_ty = goal.predicate.self_ty(); + // If the self type is sub unified with any opaque type, we + // also look at blanket impls for it. + let mut assemble_blanket_impls = false; + for alias_ty in self.opaques_with_sub_unified_hidden_type(self_ty) { + assemble_blanket_impls = true; + debug!("self ty is sub unified with {alias_ty:?}"); + + struct ReplaceOpaque { + cx: I, + alias_ty: ty::AliasTy, + self_ty: I::Ty, + } + impl TypeFolder for ReplaceOpaque { + fn cx(&self) -> I { + self.cx + } + fn fold_ty(&mut self, ty: I::Ty) -> I::Ty { + if let ty::Alias(ty::Opaque, alias_ty) = ty.kind() { + if alias_ty == self.alias_ty { + return self.self_ty; + } + } + ty.super_fold_with(self) + } + } + + // We look at all item-bounds of the opaque, replacing the + // opaque with the current self type before considering + // them as a candidate. Imagine e've got `?x: Trait` + // and `?x` has been sub-unified with the hidden type of + // `impl Trait`, We take the item bound `opaque: Trait` + // and replace all occurrences of `opaque` with `?x`. This results + // in a `?x: Trait` alias-bound candidate. + for item_bound in self + .cx() + .item_self_bounds(alias_ty.def_id) + .iter_instantiated(self.cx(), alias_ty.args) + { + let assumption = + item_bound.fold_with(&mut ReplaceOpaque { cx: self.cx(), alias_ty, self_ty }); + candidates.extend(G::probe_and_match_goal_against_assumption( + self, + CandidateSource::AliasBound, + goal, + assumption, + |ecx| { + // We want to reprove this goal once we've inferred the + // hidden type, so we force the certainty to `Maybe`. + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + }, + )); + } + } + + // We also need to consider blanket impls for not-yet-defined opaque types. + // + // See tests/ui/impl-trait/non-defining-uses/use-blanket-impl.rs for an example. + if assemble_blanket_impls && assemble_from.should_assemble_impl_candidates() { + let cx = self.cx(); + cx.for_each_blanket_impl(goal.predicate.trait_def_id(cx), |impl_def_id| { + // For every `default impl`, there's always a non-default `impl` + // that will *also* apply. There's no reason to register a candidate + // for this impl, since it is *not* proof that the trait goal holds. + if cx.impl_is_default(impl_def_id) { + return; + } + + match G::consider_impl_candidate(self, goal, impl_def_id, |ecx, certainty| { + if ecx.shallow_resolve(self_ty).is_ty_var() { + // We force the certainty of impl candidates to be `Maybe`. + let certainty = certainty.and(Certainty::AMBIGUOUS); + ecx.evaluate_added_goals_and_make_canonical_response(certainty) + } else { + // We don't want to use impls if they constrain the opaque. + // + // FIXME(trait-system-refactor-initiative#229): This isn't + // perfect yet as it still allows us to incorrectly constrain + // other inference variables. + Err(NoSolution) + } + }) { + Ok(candidate) => candidates.push(candidate), + Err(NoSolution) => (), + } + }); + } + + if candidates.is_empty() { + candidates.extend(self.forced_ambiguity(MaybeCause::Ambiguity)); + } + } + /// Assemble and merge candidates for goals which are related to an underlying trait /// goal. Right now, this is normalizes-to and host effect goals. /// diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index 6646857a1368..cb72c1cd92b8 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -124,6 +124,7 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal, impl_def_id: I::ImplId, + then: impl FnOnce(&mut EvalCtxt<'_, D>, Certainty) -> QueryResult, ) -> Result, NoSolution> { let cx = ecx.cx(); @@ -175,7 +176,7 @@ where }); ecx.add_goals(GoalSource::ImplWhereBound, const_conditions); - ecx.evaluate_added_goals_and_make_canonical_response(certainty) + then(ecx, certainty) }) } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index f1c1801dd3f1..3e3a5246f3d7 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -1060,6 +1060,10 @@ where self.delegate.resolve_vars_if_possible(value) } + pub(super) fn shallow_resolve(&self, ty: I::Ty) -> I::Ty { + self.delegate.shallow_resolve(ty) + } + pub(super) fn eager_resolve_region(&self, r: I::Region) -> I::Region { if let ty::ReVar(vid) = r.kind() { self.delegate.opportunistic_resolve_lt_var(vid) @@ -1176,6 +1180,33 @@ where ) -> bool { may_use_unstable_feature(&**self.delegate, param_env, symbol) } + + pub(crate) fn opaques_with_sub_unified_hidden_type( + &self, + self_ty: I::Ty, + ) -> impl Iterator> + use<'a, D, I> { + let delegate = self.delegate; + delegate + .clone_opaque_types_lookup_table() + .into_iter() + .chain(delegate.clone_duplicate_opaque_types()) + .filter_map(move |(key, hidden_ty)| { + if let ty::Infer(ty::TyVar(self_vid)) = self_ty.kind() { + if let ty::Infer(ty::TyVar(hidden_vid)) = hidden_ty.kind() { + if delegate.sub_unification_table_root_var(self_vid) + == delegate.sub_unification_table_root_var(hidden_vid) + { + return Some(ty::AliasTy::new_from_args( + delegate.cx(), + key.def_id.into(), + key.args, + )); + } + } + } + None + }) + } } /// Eagerly replace aliases with inference variables, emitting `AliasRelate` diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index 6e6be829f521..54b92ebac1de 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -194,6 +194,7 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal>, impl_def_id: I::ImplId, + then: impl FnOnce(&mut EvalCtxt<'_, D>, Certainty) -> QueryResult, ) -> Result, NoSolution> { let cx = ecx.cx(); @@ -314,8 +315,7 @@ where // nested goal for consistency. ty::TypingMode::Coherence => { ecx.add_goal(GoalSource::Misc, goal.with(cx, PredicateKind::Ambiguous)); - return ecx - .evaluate_added_goals_and_make_canonical_response(Certainty::Yes); + return then(ecx, Certainty::Yes); } ty::TypingMode::Analysis { .. } | ty::TypingMode::Borrowck { .. } @@ -325,8 +325,7 @@ where goal, goal.predicate.alias, ); - return ecx - .evaluate_added_goals_and_make_canonical_response(Certainty::Yes); + return then(ecx, Certainty::Yes); } } } else { diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 3e720a47f32e..a69e867289ce 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -55,6 +55,7 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal>, impl_def_id: I::ImplId, + then: impl FnOnce(&mut EvalCtxt<'_, D>, Certainty) -> QueryResult, ) -> Result, NoSolution> { let cx = ecx.cx(); @@ -112,7 +113,7 @@ where .map(|pred| goal.with(cx, pred)), ); - ecx.evaluate_added_goals_and_make_canonical_response(maximal_certainty) + then(ecx, maximal_certainty) }) } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 3a83b1739ffa..cf58aec14a68 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -347,6 +347,7 @@ pub trait Interner: self_ty: Self::Ty, f: impl FnMut(Self::ImplId), ); + fn for_each_blanket_impl(self, trait_def_id: Self::TraitId, f: impl FnMut(Self::ImplId)); fn has_item_definition(self, def_id: Self::DefId) -> bool; diff --git a/tests/ui/impl-trait/non-defining-uses/avoid-inference-constraints-from-blanket-2.next.stderr b/tests/ui/impl-trait/non-defining-uses/avoid-inference-constraints-from-blanket-2.next.stderr new file mode 100644 index 000000000000..86ac1bdad041 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/avoid-inference-constraints-from-blanket-2.next.stderr @@ -0,0 +1,16 @@ +error[E0308]: mismatched types + --> $DIR/avoid-inference-constraints-from-blanket-2.rs:27:18 + | +LL | let _: u32 = x; + | --- ^ expected `u32`, found `u64` + | | + | expected due to this + | +help: you can convert a `u64` to a `u32` and panic if the converted value doesn't fit + | +LL | let _: u32 = x.try_into().unwrap(); + | ++++++++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/impl-trait/non-defining-uses/avoid-inference-constraints-from-blanket-2.rs b/tests/ui/impl-trait/non-defining-uses/avoid-inference-constraints-from-blanket-2.rs new file mode 100644 index 000000000000..b4f853de4aad --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/avoid-inference-constraints-from-blanket-2.rs @@ -0,0 +1,31 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[current] check-pass + +// Regression test for trait-system-refactor-initiative#205. Avoid +// constraining other impl arguments when applying blanket impls. + +// FIXME(-Znext-solver): This currently incompletely constrains the +// argument of `opaque: Trait` using the blanket impl of trait. +// Ideally we don't do that. + +trait Trait {} + +impl Trait for T {} +impl Trait for u64 {} + +fn impls_trait, U>(_: U) -> T { + todo!() +} + +fn foo() -> impl Sized { + let x = Default::default(); + if false { + return impls_trait::<_, _>(x); + } + let _: u32 = x; + //[next]~^ ERROR mismatched types + 1u64 +} +fn main() {} diff --git a/tests/ui/impl-trait/non-defining-uses/avoid-inference-constraints-from-blanket-3.rs b/tests/ui/impl-trait/non-defining-uses/avoid-inference-constraints-from-blanket-3.rs new file mode 100644 index 000000000000..2f29cb4ee6b4 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/avoid-inference-constraints-from-blanket-3.rs @@ -0,0 +1,25 @@ +//@ compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +#![allow(unconditional_recursion)] + +// Regression test for trait-system-refactor-initiative#205. Avoid +// constraining other impl arguments when applying blanket impls, +// especially if the nested where-bounds of the blanket impl don't +// actually apply for the opaque. + +// FIXME(-Znext-solver): This currently incompletely constrains the +// argument of `opaque: Trait` using the blanket impl of trait. +// Ideally we don't do that. + +trait Trait {} + +impl Trait for T {} +impl Trait for String {} +fn impls_trait, U>(_: T) {} + +fn test() -> impl Sized { + let x = test(); + impls_trait(x); //~ ERROR the trait bound `String: Trait` is not satisfied + String::new() +} +fn main() {} diff --git a/tests/ui/impl-trait/non-defining-uses/avoid-inference-constraints-from-blanket-3.stderr b/tests/ui/impl-trait/non-defining-uses/avoid-inference-constraints-from-blanket-3.stderr new file mode 100644 index 000000000000..a5d19b484811 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/avoid-inference-constraints-from-blanket-3.stderr @@ -0,0 +1,25 @@ +error[E0277]: the trait bound `String: Trait` is not satisfied + --> $DIR/avoid-inference-constraints-from-blanket-3.rs:22:5 + | +LL | impls_trait(x); + | ^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String` + | + = help: the trait `Trait` is not implemented for `String` + but trait `Trait` is implemented for it + = help: for that trait implementation, expected `u64`, found `u32` +note: required for `String` to implement `Trait` + --> $DIR/avoid-inference-constraints-from-blanket-3.rs:16:15 + | +LL | impl Trait for T {} + | ---- ^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here +note: required by a bound in `impls_trait` + --> $DIR/avoid-inference-constraints-from-blanket-3.rs:18:19 + | +LL | fn impls_trait, U>(_: T) {} + | ^^^^^^^^ required by this bound in `impls_trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/impl-trait/non-defining-uses/avoid-inference-constraints-from-blanket.rs b/tests/ui/impl-trait/non-defining-uses/avoid-inference-constraints-from-blanket.rs new file mode 100644 index 000000000000..bb3acfde5bc8 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/avoid-inference-constraints-from-blanket.rs @@ -0,0 +1,25 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass + +// Regression test for trait-system-refactor-initiative#205. Avoid constraining +// the opaque type when applying blanket impls. + +trait Trait {} + +impl Trait for T {} +impl Trait for u64 {} + +fn impls_trait, U>() -> T { + todo!() +} + +fn foo() -> impl Sized { + if false { + // `opaque: Trait` shouldn't constrain `opaque` to `u32` via the blanket impl + return impls_trait::<_, u32>(); + } + 1u64 +} +fn main() {} diff --git a/tests/ui/impl-trait/non-defining-uses/multiple-opaques-ambig.rs b/tests/ui/impl-trait/non-defining-uses/multiple-opaques-ambig.rs new file mode 100644 index 000000000000..e7aaf6fa135a --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/multiple-opaques-ambig.rs @@ -0,0 +1,23 @@ +//@ compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass +#![allow(unconditional_recursion)] + +// Regression test for trait-system-refactor-initiative#182. If multiple +// opaque types result in different item bounds, do not apply them. + +trait Trait {} +impl Trait for U {} + +fn impls_trait, U>(_: T) -> U { + todo!() +} + +fn overlap() -> (impl Trait, impl Trait) { + let mut x = overlap::().0; + x = overlap::().1; + let u = impls_trait(x); + let _: u32 = u; + ((), ()) +} +fn main() {} diff --git a/tests/ui/impl-trait/non-defining-uses/multiple-opaques-ok.rs b/tests/ui/impl-trait/non-defining-uses/multiple-opaques-ok.rs new file mode 100644 index 000000000000..d91efe181e35 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/multiple-opaques-ok.rs @@ -0,0 +1,37 @@ +//@ compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass +#![allow(unconditional_recursion)] + +// Regression test for trait-system-refactor-initiative#18, making sure +// we support being sub unified with more than 1 opaque type. + +trait Id { + type This; +} +impl Id for &'static str { + type This = &'static str; +} +fn to_assoc(x: T) -> ::This { + todo!() +} + +fn mirror1() -> (impl Id, impl Sized) { + let mut opaque = mirror1().0; + opaque = mirror1().1; + let x = to_assoc(opaque); + // `?x` equals both opaques, make sure we still use the applicable + // item bound. + x.len(); + (x, x) +} +fn mirror2() -> (impl Sized, impl Id) { + let mut opaque = mirror2().0; + opaque = mirror2().1; + let x = to_assoc(opaque); + // `?x` equals both opaques, make sure we still use the applicable + // item bound. + x.len(); + (x, x) +} +fn main() {} diff --git a/tests/ui/impl-trait/non-defining-uses/no-rigid-alias.rs b/tests/ui/impl-trait/non-defining-uses/no-rigid-alias.rs new file mode 100644 index 000000000000..fca5db3e20f8 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/no-rigid-alias.rs @@ -0,0 +1,27 @@ +//@ compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass +#![allow(unconditional_recursion)] + +// Test for trait-system-refactor-initiative#182 making sure +// that we don't incorrectly normalize to rigid aliases if the +// opaque type only has a trait bound. + +trait Id { + type This; +} +impl Id for Vec { + type This = Vec; +} +fn to_assoc(x: T) -> ::This { + todo!() +} + +fn mirror(x: Vec) -> impl Id { + let x = to_assoc(mirror(x)); + // `?x` equals ` as Id>::This`. We should not infer `?x` + // to be a rigid alias here. + let _: Vec = x; + x +} +fn main() {} diff --git a/tests/ui/impl-trait/non-defining-uses/use-blanket-impl.rs b/tests/ui/impl-trait/non-defining-uses/use-blanket-impl.rs new file mode 100644 index 000000000000..50bb3995b944 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/use-blanket-impl.rs @@ -0,0 +1,19 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass + +// Regression test for trait-system-refactor-initiative#196. +fn iterator(b: bool) -> impl Iterator { + if b { + // We need to eagerly figure out the type of `i` here by using + // the `::Item` obligation. This means + // we not only have to consider item bounds, but also blanket impls. + for i in iterator(false) { + i.len(); + } + } + + vec![].into_iter() +} +fn main() {} diff --git a/tests/ui/impl-trait/non-defining-uses/use-item-bound-over-blanket-impl.rs b/tests/ui/impl-trait/non-defining-uses/use-item-bound-over-blanket-impl.rs new file mode 100644 index 000000000000..7c2766ade3fa --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/use-item-bound-over-blanket-impl.rs @@ -0,0 +1,30 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass +#![allow(unconditional_recursion)] + +// Regression test for trait-system-refactor-initiative#182. + +trait Id { + type This; +} +impl Id for T { + type This = T; +} +fn to_assoc(x: T) -> ::This { + x +} + +fn mirror(x: Vec) -> impl Id> { + let x = to_assoc(mirror(x)); + // `?x` equals ` as Id>::This`. We need to eagerly infer the + // type of `?x` to prevent this method call from resulting in an error. + // + // We could use both the item bound to normalize to `Vec`, or the + // blanket impl to normalize to `opaque::`. We have to go with the + // item bound. + x.len(); + x +} +fn main() {} diff --git a/tests/ui/impl-trait/non-defining-uses/use-item-bound.rs b/tests/ui/impl-trait/non-defining-uses/use-item-bound.rs new file mode 100644 index 000000000000..36dcbacbe6fd --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/use-item-bound.rs @@ -0,0 +1,25 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass +#![allow(unconditional_recursion)] +// Regression test for trait-system-refactor-initiative#182. + +trait Id { + type This; +} +impl Id for Vec { + type This = Vec; +} +fn to_assoc(x: T) -> ::This { + todo!() +} + +fn mirror(x: Vec) -> impl Id> { + let x = to_assoc(mirror(x)); + // `?x` equals ` as Id>::This`. We need to eagerly infer the + // type of `?x` to prevent this method call from resulting in an error. + x.len(); + x +} +fn main() {} From c3bd8b6f387b6b6409347916fe901ccad7ec137a Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Tue, 9 Sep 2025 18:01:24 -0400 Subject: [PATCH 391/682] Update cargo submodule --- src/tools/cargo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/cargo b/src/tools/cargo index 761c4658d007..24bb93c388fb 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 761c4658d0079d607e6d33cf0c060e61a617cad3 +Subproject commit 24bb93c388fb8c211a37986539f24a819dc669d3 From 35979519ae8125537e765e2adc75ad41cda7a9da Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Thu, 11 Sep 2025 21:52:42 +0800 Subject: [PATCH 392/682] bootstrap: Build jemalloc for LoongArch with support for 16K pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By default, jemalloc is configured with a 4K page size. If the host’s page size is larger than this, it will crash at runtime. This patch raises the page size to 16K. --- src/bootstrap/src/core/build_steps/compile.rs | 17 ++++++++++------- src/bootstrap/src/core/build_steps/tool.rs | 14 ++++++++++---- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 0b75e85772f8..1458b0beefa8 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1383,14 +1383,17 @@ pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetS } } - // Build jemalloc on AArch64 with support for page sizes up to 64K - // See: https://github.com/rust-lang/rust/pull/135081 // See also the "JEMALLOC_SYS_WITH_LG_PAGE" setting in the tool build step. - if builder.config.jemalloc(target) - && target.starts_with("aarch64") - && env::var_os("JEMALLOC_SYS_WITH_LG_PAGE").is_none() - { - cargo.env("JEMALLOC_SYS_WITH_LG_PAGE", "16"); + if builder.config.jemalloc(target) && env::var_os("JEMALLOC_SYS_WITH_LG_PAGE").is_none() { + // Build jemalloc on AArch64 with support for page sizes up to 64K + // See: https://github.com/rust-lang/rust/pull/135081 + if target.starts_with("aarch64") { + cargo.env("JEMALLOC_SYS_WITH_LG_PAGE", "16"); + } + // Build jemalloc on LoongArch with support for page sizes up to 16K + else if target.starts_with("loongarch") { + cargo.env("JEMALLOC_SYS_WITH_LG_PAGE", "14"); + } } } diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 6870bf3eddcd..dcc4898cae16 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -228,12 +228,18 @@ pub fn prepare_tool_cargo( // own copy cargo.env("LZMA_API_STATIC", "1"); - // Build jemalloc on AArch64 with support for page sizes up to 64K - // See: https://github.com/rust-lang/rust/pull/135081 // Note that `miri` always uses jemalloc. As such, there is no checking of the jemalloc build flag. // See also the "JEMALLOC_SYS_WITH_LG_PAGE" setting in the compile build step. - if target.starts_with("aarch64") && env::var_os("JEMALLOC_SYS_WITH_LG_PAGE").is_none() { - cargo.env("JEMALLOC_SYS_WITH_LG_PAGE", "16"); + if env::var_os("JEMALLOC_SYS_WITH_LG_PAGE").is_none() { + // Build jemalloc on AArch64 with support for page sizes up to 64K + // See: https://github.com/rust-lang/rust/pull/135081 + if target.starts_with("aarch64") { + cargo.env("JEMALLOC_SYS_WITH_LG_PAGE", "16"); + } + // Build jemalloc on LoongArch with support for page sizes up to 16K + else if target.starts_with("loongarch") { + cargo.env("JEMALLOC_SYS_WITH_LG_PAGE", "14"); + } } // CFG_RELEASE is needed by rustfmt (and possibly other tools) which From 24d195bc07da61a0ab74ac166d659f1924a604ea Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 11 Sep 2025 15:03:53 +0200 Subject: [PATCH 393/682] weak_memory: fix sync clock handling when loading from old store elements --- src/tools/miri/src/concurrency/data_race.rs | 40 ++++++--- src/tools/miri/src/concurrency/weak_memory.rs | 87 +++++++++++++------ .../miri/tests/pass/0weak_memory/weak.rs | 44 +++++++--- 3 files changed, 121 insertions(+), 50 deletions(-) diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs index 16cac7e6d04c..1ad9ace1b5d1 100644 --- a/src/tools/miri/src/concurrency/data_race.rs +++ b/src/tools/miri/src/concurrency/data_race.rs @@ -183,6 +183,9 @@ struct AtomicMemoryCellClocks { /// contains the vector of timestamps that will /// happen-before a thread if an acquire-load is /// performed on the data. + /// + /// With weak memory emulation, this is the clock of the most recent write. It is then only used + /// for release sequences, to integrate the most recent clock into the next one for RMWs. sync_vector: VClock, /// The size of accesses to this atomic location. @@ -504,10 +507,11 @@ impl MemoryCellClocks { thread_clocks: &mut ThreadClockSet, index: VectorIdx, access_size: Size, + sync_clock: Option<&VClock>, ) -> Result<(), DataRace> { self.atomic_read_detect(thread_clocks, index, access_size)?; - if let Some(atomic) = self.atomic() { - thread_clocks.clock.join(&atomic.sync_vector); + if let Some(sync_clock) = sync_clock.or_else(|| self.atomic().map(|a| &a.sync_vector)) { + thread_clocks.clock.join(sync_clock); } Ok(()) } @@ -520,10 +524,11 @@ impl MemoryCellClocks { thread_clocks: &mut ThreadClockSet, index: VectorIdx, access_size: Size, + sync_clock: Option<&VClock>, ) -> Result<(), DataRace> { self.atomic_read_detect(thread_clocks, index, access_size)?; - if let Some(atomic) = self.atomic() { - thread_clocks.fence_acquire.join(&atomic.sync_vector); + if let Some(sync_clock) = sync_clock.or_else(|| self.atomic().map(|a| &a.sync_vector)) { + thread_clocks.fence_acquire.join(sync_clock); } Ok(()) } @@ -736,8 +741,8 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { } let scalar = this.allow_data_races_ref(move |this| this.read_scalar(place))?; - let buffered_scalar = this.buffered_atomic_read(place, atomic, scalar, || { - this.validate_atomic_load(place, atomic) + let buffered_scalar = this.buffered_atomic_read(place, atomic, scalar, |sync_clock| { + this.validate_atomic_load(place, atomic, sync_clock) })?; interp_ok(buffered_scalar.ok_or_else(|| err_ub!(InvalidUninitBytes(None)))?) } @@ -935,7 +940,7 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { this.validate_atomic_rmw(place, success)?; this.buffered_atomic_rmw(new, place, success, old.to_scalar())?; } else { - this.validate_atomic_load(place, fail)?; + this.validate_atomic_load(place, fail, /* can use latest sync clock */ None)?; // A failed compare exchange is equivalent to a load, reading from the latest store // in the modification order. // Since `old` is only a value and not the store element, we need to separately @@ -1177,6 +1182,18 @@ impl VClockAlloc { }))? } + /// Return the release/acquire synchronization clock for the given memory range. + pub(super) fn sync_clock(&self, access_range: AllocRange) -> VClock { + let alloc_ranges = self.alloc_ranges.borrow(); + let mut clock = VClock::default(); + for (_, mem_clocks) in alloc_ranges.iter(access_range.start, access_range.size) { + if let Some(atomic) = mem_clocks.atomic() { + clock.join(&atomic.sync_vector); + } + } + clock + } + /// Detect data-races for an unsynchronized read operation. It will not perform /// data-race detection if `race_detecting()` is false, either due to no threads /// being created or if it is temporarily disabled during a racy read or write @@ -1453,6 +1470,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { &self, place: &MPlaceTy<'tcx>, atomic: AtomicReadOrd, + sync_clock: Option<&VClock>, ) -> InterpResult<'tcx> { let this = self.eval_context_ref(); this.validate_atomic_op( @@ -1461,9 +1479,9 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { AccessType::AtomicLoad, move |memory, clocks, index, atomic| { if atomic == AtomicReadOrd::Relaxed { - memory.load_relaxed(&mut *clocks, index, place.layout.size) + memory.load_relaxed(&mut *clocks, index, place.layout.size, sync_clock) } else { - memory.load_acquire(&mut *clocks, index, place.layout.size) + memory.load_acquire(&mut *clocks, index, place.layout.size, sync_clock) } }, ) @@ -1508,9 +1526,9 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { AccessType::AtomicRmw, move |memory, clocks, index, _| { if acquire { - memory.load_acquire(clocks, index, place.layout.size)?; + memory.load_acquire(clocks, index, place.layout.size, None)?; } else { - memory.load_relaxed(clocks, index, place.layout.size)?; + memory.load_relaxed(clocks, index, place.layout.size, None)?; } if release { memory.rmw_release(clocks, index, place.layout.size) diff --git a/src/tools/miri/src/concurrency/weak_memory.rs b/src/tools/miri/src/concurrency/weak_memory.rs index caa27422d2f2..6ebade6a568a 100644 --- a/src/tools/miri/src/concurrency/weak_memory.rs +++ b/src/tools/miri/src/concurrency/weak_memory.rs @@ -62,8 +62,11 @@ //! You can refer to test cases in weak_memory/extra_cpp.rs and weak_memory/extra_cpp_unsafe.rs for examples of these operations. // Our and the author's own implementation (tsan11) of the paper have some deviations from the provided operational semantics in §5.3: -// 1. In the operational semantics, store elements keep a copy of the atomic object's vector clock (AtomicCellClocks::sync_vector in miri), -// but this is not used anywhere so it's omitted here. +// 1. In the operational semantics, loads acquire the vector clock of the atomic location +// irrespective of which store buffer element is loaded. That's incorrect; the synchronization clock +// needs to be tracked per-store-buffer-element. (The paper has a field "clocks" for that purpose, +// but it is not actuallt used.) tsan11 does this correctly +// (https://github.com/ChrisLidbury/tsan11/blob/ecbd6b81e9b9454e01cba78eb9d88684168132c7/lib/tsan/rtl/tsan_relaxed.cc#L305). // // 2. In the operational semantics, each store element keeps the timestamp of a thread when it loads from the store. // If the same thread loads from the same store element multiple times, then the timestamps at all loads are saved in a list of load elements. @@ -138,16 +141,17 @@ enum LoadRecency { #[derive(Debug, Clone, PartialEq, Eq)] struct StoreElement { - /// The identifier of the vector index, corresponding to a thread - /// that performed the store. - store_index: VectorIdx, + /// The thread that performed the store. + store_thread: VectorIdx, + /// The timestamp of the storing thread when it performed the store + store_timestamp: VTimestamp, + + /// The vector clock that can be acquired by loading this store. + sync_clock: VClock, /// Whether this store is SC. is_seqcst: bool, - /// The timestamp of the storing thread when it performed the store - timestamp: VTimestamp, - /// The value of this store. `None` means uninitialized. // FIXME: Currently, we cannot represent partial initialization. val: Option, @@ -246,8 +250,10 @@ impl<'tcx> StoreBuffer { let store_elem = StoreElement { // The thread index and timestamp of the initialisation write // are never meaningfully used, so it's fine to leave them as 0 - store_index: VectorIdx::from(0), - timestamp: VTimestamp::ZERO, + store_thread: VectorIdx::from(0), + store_timestamp: VTimestamp::ZERO, + // The initialization write is non-atomic so nothing can be acquired. + sync_clock: VClock::default(), val: init, is_seqcst: false, load_info: RefCell::new(LoadInfo::default()), @@ -276,7 +282,7 @@ impl<'tcx> StoreBuffer { thread_mgr: &ThreadManager<'_>, is_seqcst: bool, rng: &mut (impl rand::Rng + ?Sized), - validate: impl FnOnce() -> InterpResult<'tcx>, + validate: impl FnOnce(Option<&VClock>) -> InterpResult<'tcx>, ) -> InterpResult<'tcx, (Option, LoadRecency)> { // Having a live borrow to store_buffer while calling validate_atomic_load is fine // because the race detector doesn't touch store_buffer @@ -293,7 +299,7 @@ impl<'tcx> StoreBuffer { // after we've picked a store element from the store buffer, as presented // in ATOMIC LOAD rule of the paper. This is because fetch_store // requires access to ThreadClockSet.clock, which is updated by the race detector - validate()?; + validate(Some(&store_elem.sync_clock))?; let (index, clocks) = global.active_thread_state(thread_mgr); let loaded = store_elem.load_impl(index, &clocks, is_seqcst); @@ -306,10 +312,11 @@ impl<'tcx> StoreBuffer { global: &DataRaceState, thread_mgr: &ThreadManager<'_>, is_seqcst: bool, + sync_clock: VClock, ) -> InterpResult<'tcx> { let (index, clocks) = global.active_thread_state(thread_mgr); - self.store_impl(val, index, &clocks.clock, is_seqcst); + self.store_impl(val, index, &clocks.clock, is_seqcst, sync_clock); interp_ok(()) } @@ -336,7 +343,9 @@ impl<'tcx> StoreBuffer { return false; } - keep_searching = if store_elem.timestamp <= clocks.clock[store_elem.store_index] { + keep_searching = if store_elem.store_timestamp + <= clocks.clock[store_elem.store_thread] + { // CoWR: if a store happens-before the current load, // then we can't read-from anything earlier in modification order. // C++20 §6.9.2.2 [intro.races] paragraph 18 @@ -348,7 +357,7 @@ impl<'tcx> StoreBuffer { // then we cannot read-from anything earlier in modification order. // C++20 §6.9.2.2 [intro.races] paragraph 16 false - } else if store_elem.timestamp <= clocks.write_seqcst[store_elem.store_index] + } else if store_elem.store_timestamp <= clocks.write_seqcst[store_elem.store_thread] && store_elem.is_seqcst { // The current non-SC load, which may be sequenced-after an SC fence, @@ -356,7 +365,7 @@ impl<'tcx> StoreBuffer { // C++17 §32.4 [atomics.order] paragraph 4 false } else if is_seqcst - && store_elem.timestamp <= clocks.read_seqcst[store_elem.store_index] + && store_elem.store_timestamp <= clocks.read_seqcst[store_elem.store_thread] { // The current SC load cannot read-before the last store sequenced-before // the last SC fence. @@ -394,17 +403,19 @@ impl<'tcx> StoreBuffer { } } - /// ATOMIC STORE IMPL in the paper (except we don't need the location's vector clock) + /// ATOMIC STORE IMPL in the paper fn store_impl( &mut self, val: Scalar, index: VectorIdx, thread_clock: &VClock, is_seqcst: bool, + sync_clock: VClock, ) { let store_elem = StoreElement { - store_index: index, - timestamp: thread_clock[index], + store_thread: index, + store_timestamp: thread_clock[index], + sync_clock, // In the language provided in the paper, an atomic store takes the value from a // non-atomic memory location. // But we already have the immediate value here so we don't need to do the memory @@ -422,7 +433,7 @@ impl<'tcx> StoreBuffer { // so that in a later SC load, only the last SC store (i.e. this one) or stores that // aren't ordered by hb with the last SC is picked. self.buffer.iter_mut().rev().for_each(|elem| { - if elem.timestamp <= thread_clock[elem.store_index] { + if elem.store_timestamp <= thread_clock[elem.store_thread] { elem.is_seqcst = true; } }) @@ -465,7 +476,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let (alloc_id, base_offset, ..) = this.ptr_get_alloc_id(place.ptr(), 0)?; if let ( crate::AllocExtra { - data_race: AllocDataRaceHandler::Vclocks(_, Some(alloc_buffers)), + data_race: AllocDataRaceHandler::Vclocks(data_race_clocks, Some(alloc_buffers)), .. }, crate::MiriMachine { @@ -478,20 +489,29 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { global.sc_write(threads); } let range = alloc_range(base_offset, place.layout.size); + let sync_clock = data_race_clocks.sync_clock(range); let buffer = alloc_buffers.get_or_create_store_buffer_mut(range, Some(init))?; // The RMW always reads from the most recent store. buffer.read_from_last_store(global, threads, atomic == AtomicRwOrd::SeqCst); - buffer.buffered_write(new_val, global, threads, atomic == AtomicRwOrd::SeqCst)?; + buffer.buffered_write( + new_val, + global, + threads, + atomic == AtomicRwOrd::SeqCst, + sync_clock, + )?; } interp_ok(()) } + /// The argument to `validate` is the synchronization clock of the memory that is being read, + /// if we are reading from a store buffer element. fn buffered_atomic_read( &self, place: &MPlaceTy<'tcx>, atomic: AtomicReadOrd, latest_in_mo: Scalar, - validate: impl FnOnce() -> InterpResult<'tcx>, + validate: impl FnOnce(Option<&VClock>) -> InterpResult<'tcx>, ) -> InterpResult<'tcx, Option> { let this = self.eval_context_ref(); 'fallback: { @@ -529,7 +549,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } // Race detector or weak memory disabled, simply read the latest value - validate()?; + validate(None)?; interp_ok(Some(latest_in_mo)) } @@ -537,6 +557,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// /// `init` says with which value to initialize the store buffer in case there wasn't a store /// buffer for this memory range before. + /// + /// Must be called *after* `validate_atomic_store` to ensure that `sync_clock` is up-to-date. fn buffered_atomic_write( &mut self, val: Scalar, @@ -548,7 +570,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let (alloc_id, base_offset, ..) = this.ptr_get_alloc_id(dest.ptr(), 0)?; if let ( crate::AllocExtra { - data_race: AllocDataRaceHandler::Vclocks(_, Some(alloc_buffers)), + data_race: AllocDataRaceHandler::Vclocks(data_race_clocks, Some(alloc_buffers)), .. }, crate::MiriMachine { @@ -560,9 +582,18 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { global.sc_write(threads); } - let buffer = alloc_buffers - .get_or_create_store_buffer_mut(alloc_range(base_offset, dest.layout.size), init)?; - buffer.buffered_write(val, global, threads, atomic == AtomicWriteOrd::SeqCst)?; + let range = alloc_range(base_offset, dest.layout.size); + // It's a bit annoying that we have to go back to the data race part to get the clock... + // but it does make things a lot simpler. + let sync_clock = data_race_clocks.sync_clock(range); + let buffer = alloc_buffers.get_or_create_store_buffer_mut(range, init)?; + buffer.buffered_write( + val, + global, + threads, + atomic == AtomicWriteOrd::SeqCst, + sync_clock, + )?; } // Caller should've written to dest with the vanilla scalar write, we do nothing here diff --git a/src/tools/miri/tests/pass/0weak_memory/weak.rs b/src/tools/miri/tests/pass/0weak_memory/weak.rs index 44791d7cd7c5..c752fc114ba5 100644 --- a/src/tools/miri/tests/pass/0weak_memory/weak.rs +++ b/src/tools/miri/tests/pass/0weak_memory/weak.rs @@ -44,7 +44,7 @@ fn check_all_outcomes( let expected: HashSet = HashSet::from_iter(expected); let mut seen = HashSet::new(); // Let's give it N times as many tries as we are expecting values. - let tries = expected.len() * 12; + let tries = expected.len() * 16; for i in 0..tries { let val = generate(); assert!(expected.contains(&val), "got an unexpected value: {val:?}"); @@ -205,19 +205,16 @@ fn release_sequence() { let y = static_atomic(0); let t1 = spawn(move || { - x.store(2, Relaxed); - }); - let t2 = spawn(move || { y.store(1, Relaxed); x.store(1, Release); x.swap(3, Relaxed); }); - let t3 = spawn(move || { + let t2 = spawn(move || { if x.load(Acquire) == 3 { - // If we read 3 here, we are seeing the result of the `x.swap` above, which - // was relaxed but forms a release sequence with the `x.store` (since we know - // `t1` will not be scheduled in between). This means there is a release sequence, - // so we acquire the `y.store` and cannot see the original value `0` any more. + // If we read 3 here, we are seeing the result of the `x.swap` above, which was + // relaxed but forms a release sequence with the `x.store`. This means there is a + // release sequence, so we acquire the `y.store` and cannot see the original value + // `0` any more. Some(y.load(Relaxed)) } else { None @@ -225,8 +222,32 @@ fn release_sequence() { }); t1.join().unwrap(); - t2.join().unwrap(); - t3.join().unwrap() + t2.join().unwrap() + }); +} + +/// Ensure that when we read from an outdated release store, we acquire its clock. +fn old_release_store() { + check_all_outcomes([None, Some(1)], || { + let x = static_atomic(0); + let y = static_atomic(0); + + let t1 = spawn(move || { + y.store(1, Relaxed); + x.store(1, Release); // this is what we want to read from + x.store(3, Relaxed); + }); + let t2 = spawn(move || { + if x.load(Acquire) == 1 { + // We must have acquired the `y.store` so we cannot see the initial value any more. + Some(y.load(Relaxed)) + } else { + None + } + }); + + t1.join().unwrap(); + t2.join().unwrap() }); } @@ -238,4 +259,5 @@ pub fn main() { faa_replaced_by_load(); release_sequence(); weaker_release_sequences(); + old_release_store(); } From 2b5b191965571142cff8fab04aad9f243041ca19 Mon Sep 17 00:00:00 2001 From: Johannes Hostert Date: Thu, 11 Sep 2025 17:00:59 +0200 Subject: [PATCH 394/682] Fix miri issue 4579 by checking if the strong protector is actually "active". Where "active" means that the accessed bit is set. This also reverts miri PR 3831. --- .../src/borrow_tracker/tree_borrows/tree.rs | 2 ++ .../zero-sized-protected.stack.stderr | 15 --------- .../zero-sized-protected.tree.stderr | 32 ------------------- .../both_borrows/zero-sized-protected.rs | 7 ++-- 4 files changed, 4 insertions(+), 52 deletions(-) delete mode 100644 src/tools/miri/tests/fail/both_borrows/zero-sized-protected.stack.stderr delete mode 100644 src/tools/miri/tests/fail/both_borrows/zero-sized-protected.tree.stderr rename src/tools/miri/tests/{fail => pass}/both_borrows/zero-sized-protected.rs (53%) diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs index 1f29bcfc2b03..22bd63bd6b6f 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs @@ -756,6 +756,8 @@ impl<'tcx> Tree { // Don't check for protector if it is a Cell (see `unsafe_cell_deallocate` in `interior_mutability.rs`). // Related to https://github.com/rust-lang/rust/issues/55005. && !perm.permission().is_cell() + // Only trigger UB if the accessed bit is set, i.e. if the protector is actually protecting this offset. See #4579. + && perm.is_accessed() { Err(TransitionError::ProtectedDealloc) } else { diff --git a/src/tools/miri/tests/fail/both_borrows/zero-sized-protected.stack.stderr b/src/tools/miri/tests/fail/both_borrows/zero-sized-protected.stack.stderr deleted file mode 100644 index 3e4a7ccac363..000000000000 --- a/src/tools/miri/tests/fail/both_borrows/zero-sized-protected.stack.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: Undefined Behavior: entering unreachable code - --> tests/fail/both_borrows/zero-sized-protected.rs:LL:CC - | -LL | unsafe { std::hint::unreachable_unchecked() }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here - | - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = note: BACKTRACE: - = note: inside `main` at tests/fail/both_borrows/zero-sized-protected.rs:LL:CC - -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/both_borrows/zero-sized-protected.tree.stderr b/src/tools/miri/tests/fail/both_borrows/zero-sized-protected.tree.stderr deleted file mode 100644 index 66a7d7794e96..000000000000 --- a/src/tools/miri/tests/fail/both_borrows/zero-sized-protected.tree.stderr +++ /dev/null @@ -1,32 +0,0 @@ -error: Undefined Behavior: deallocation through (root of the allocation) at ALLOC[0x0] is forbidden - --> tests/fail/both_borrows/zero-sized-protected.rs:LL:CC - | -LL | unsafe { dealloc(ptr, l) }; - | ^^^^^^^^^^^^^^^ Undefined Behavior occurred here - | - = 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: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information - = help: the allocation of the accessed tag (root of the allocation) also contains the strongly protected tag - = help: the strongly protected tag disallows deallocations -help: the accessed tag was created here - --> tests/fail/both_borrows/zero-sized-protected.rs:LL:CC - | -LL | let ptr = unsafe { alloc(l) }; - | ^^^^^^^^ -help: the strongly protected tag was created here, in the initial state Reserved - --> tests/fail/both_borrows/zero-sized-protected.rs:LL:CC - | -LL | fn test(_x: &mut (), ptr: *mut u8, l: Layout) { - | ^^ - = note: BACKTRACE (of the first span): - = note: inside `test` at tests/fail/both_borrows/zero-sized-protected.rs:LL:CC -note: inside `main` - --> tests/fail/both_borrows/zero-sized-protected.rs:LL:CC - | -LL | unsafe { test(&mut *ptr.cast::<()>(), ptr, l) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -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/both_borrows/zero-sized-protected.rs b/src/tools/miri/tests/pass/both_borrows/zero-sized-protected.rs similarity index 53% rename from src/tools/miri/tests/fail/both_borrows/zero-sized-protected.rs rename to src/tools/miri/tests/pass/both_borrows/zero-sized-protected.rs index df9a73a444ed..23d283ff4a19 100644 --- a/src/tools/miri/tests/fail/both_borrows/zero-sized-protected.rs +++ b/src/tools/miri/tests/pass/both_borrows/zero-sized-protected.rs @@ -3,16 +3,13 @@ use std::alloc::{Layout, alloc, dealloc}; // `x` is strongly protected but covers zero bytes. -// Let's see if deallocating the allocation x points to is UB: -// in TB, it is UB, but in SB it is not. +// This should never be UB. fn test(_x: &mut (), ptr: *mut u8, l: Layout) { - unsafe { dealloc(ptr, l) }; //~[tree] ERROR: /deallocation .* is forbidden/ + unsafe { dealloc(ptr, l) }; } fn main() { let l = Layout::from_size_align(1, 1).unwrap(); let ptr = unsafe { alloc(l) }; unsafe { test(&mut *ptr.cast::<()>(), ptr, l) }; - // In SB the test would pass if it weren't for this line. - unsafe { std::hint::unreachable_unchecked() }; //~[stack] ERROR: unreachable } From 68217c79b246bceecca23d8d3862af71b97bff0d Mon Sep 17 00:00:00 2001 From: Ifeanyi Orizu Date: Mon, 8 Sep 2025 16:09:59 -0500 Subject: [PATCH 395/682] Minor refactoring --- .../crates/rust-analyzer/src/flycheck.rs | 8 +- .../src/handlers/notification.rs | 274 ++++++++++-------- .../crates/rust-analyzer/src/reload.rs | 20 +- 3 files changed, 166 insertions(+), 136 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index 01ac75c09aee..315c45d5b639 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -104,11 +104,11 @@ pub(crate) enum FlycheckConfig { } impl FlycheckConfig { - pub(crate) fn invocation_strategy_once(&self) -> bool { + pub(crate) fn invocation_strategy(&self) -> InvocationStrategy { match self { - FlycheckConfig::CargoCommand { .. } => false, + FlycheckConfig::CargoCommand { .. } => InvocationStrategy::PerWorkspace, FlycheckConfig::CustomCommand { invocation_strategy, .. } => { - *invocation_strategy == InvocationStrategy::Once + invocation_strategy.clone() } } } @@ -529,7 +529,7 @@ impl FlycheckActor { if let Some(command_handle) = self.command_handle.take() { tracing::debug!( command = ?command_handle, - "did cancel flycheck" + "did cancel flycheck" ); command_handle.cancel(); self.command_receiver.take(); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs index fb743e73c74a..68c91a653940 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs @@ -1,9 +1,11 @@ //! This module is responsible for implementing handlers for Language Server //! Protocol. This module specifically handles notifications. -use std::ops::{Deref, Not as _}; +use std::{ + ops::{Deref, Not as _}, + panic::UnwindSafe, +}; -use ide_db::base_db::salsa::Cancelled; use itertools::Itertools; use lsp_types::{ CancelParams, DidChangeConfigurationParams, DidChangeTextDocumentParams, @@ -16,7 +18,7 @@ use vfs::{AbsPathBuf, ChangeKind, VfsPath}; use crate::{ config::{Config, ConfigChange}, - flycheck::Target, + flycheck::{InvocationStrategy, Target}, global_state::{FetchWorkspaceRequest, GlobalState}, lsp::{from_proto, utils::apply_document_changes}, lsp_ext::{self, RunFlycheckParams}, @@ -301,131 +303,165 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { let file_id = state.vfs.read().0.file_id(&vfs_path); if let Some((file_id, vfs::FileExcluded::No)) = file_id { let world = state.snapshot(); - let invocation_strategy_once = state.config.flycheck(None).invocation_strategy_once(); + let invocation_strategy = state.config.flycheck(None).invocation_strategy(); let may_flycheck_workspace = state.config.flycheck_workspace(None); - let mut workspace_check_triggered = false; - let task = move || -> std::result::Result<(), Cancelled> { - let saved_file = vfs_path.as_path().map(|p| p.to_owned()); - if invocation_strategy_once { - world.flycheck[0].restart_workspace(saved_file.clone()); - } - let target = TargetSpec::for_file(&world, file_id)?.and_then(|it| { - let tgt_kind = it.target_kind(); - let (tgt_name, root, package) = match it { - TargetSpec::Cargo(c) => (c.target, c.workspace_root, c.package), - _ => return None, - }; - - let tgt = match tgt_kind { - project_model::TargetKind::Bin => Target::Bin(tgt_name), - project_model::TargetKind::Example => Target::Example(tgt_name), - project_model::TargetKind::Test => Target::Test(tgt_name), - project_model::TargetKind::Bench => Target::Benchmark(tgt_name), - _ => return Some((None, root, package)), - }; - - Some((Some(tgt), root, package)) - }); - tracing::debug!(?target, "flycheck target"); - // we have a specific non-library target, attempt to only check that target, nothing - // else will be affected - let mut package_workspace_idx = None; - if let Some((target, root, package)) = target { - // trigger a package check if we have a non-library target as that can't affect - // anything else in the workspace OR if we're not allowed to check the workspace as - // the user opted into package checks then - let package_check_allowed = target.is_some() || !may_flycheck_workspace; - if package_check_allowed { - let workspace = world.workspaces.iter().position(|ws| match &ws.kind { - project_model::ProjectWorkspaceKind::Cargo { cargo, .. } - | project_model::ProjectWorkspaceKind::DetachedFile { - cargo: Some((cargo, _, _)), - .. - } => *cargo.workspace_root() == root, - _ => false, - }); - if let Some(idx) = workspace { - package_workspace_idx = Some(idx); - world.flycheck[idx].restart_for_package(package, target); - } + let task: Box ide::Cancellable<()> + Send + UnwindSafe> = + match invocation_strategy { + InvocationStrategy::Once => { + Box::new(move || { + // FIXME: Because triomphe::Arc's auto UnwindSafe impl requires that the inner type + // be UnwindSafe, and FlycheckHandle is not UnwindSafe, `word.flycheck` cannot + // be captured directly. std::sync::Arc has an UnwindSafe impl that only requires + // that the inner type be RefUnwindSafe, so if we were using that one we wouldn't + // have this problem. Remove the line below when triomphe::Arc has an UnwindSafe impl + // like std::sync::Arc's. + let world = world; + stdx::always!( + world.flycheck.len() == 1, + "should have exactly one flycheck handle when invocation strategy is once" + ); + let saved_file = vfs_path.as_path().map(ToOwned::to_owned); + world.flycheck[0].restart_workspace(saved_file); + Ok(()) + }) } - } + InvocationStrategy::PerWorkspace => { + Box::new(move || { + let target = TargetSpec::for_file(&world, file_id)?.and_then(|it| { + let tgt_kind = it.target_kind(); + let (tgt_name, root, package) = match it { + TargetSpec::Cargo(c) => (c.target, c.workspace_root, c.package), + _ => return None, + }; - if !may_flycheck_workspace { - return Ok(()); - } + let tgt = match tgt_kind { + project_model::TargetKind::Bin => Target::Bin(tgt_name), + project_model::TargetKind::Example => Target::Example(tgt_name), + project_model::TargetKind::Test => Target::Test(tgt_name), + project_model::TargetKind::Bench => Target::Benchmark(tgt_name), + _ => return Some((None, root, package)), + }; - // Trigger flychecks for all workspaces that depend on the saved file - // Crates containing or depending on the saved file - let crate_ids = world - .analysis - .crates_for(file_id)? - .into_iter() - .flat_map(|id| world.analysis.transitive_rev_deps(id)) - .flatten() - .unique() - .collect::>(); - tracing::debug!(?crate_ids, "flycheck crate ids"); - let crate_root_paths: Vec<_> = crate_ids - .iter() - .filter_map(|&crate_id| { - world - .analysis - .crate_root(crate_id) - .map(|file_id| { - world.file_id_to_file_path(file_id).as_path().map(ToOwned::to_owned) - }) - .transpose() - }) - .collect::>()?; - let crate_root_paths: Vec<_> = crate_root_paths.iter().map(Deref::deref).collect(); - tracing::debug!(?crate_root_paths, "flycheck crate roots"); + Some((Some(tgt), root, package)) + }); + tracing::debug!(?target, "flycheck target"); + // we have a specific non-library target, attempt to only check that target, nothing + // else will be affected + let mut package_workspace_idx = None; + if let Some((target, root, package)) = target { + // trigger a package check if we have a non-library target as that can't affect + // anything else in the workspace OR if we're not allowed to check the workspace as + // the user opted into package checks then + let package_check_allowed = target.is_some() || !may_flycheck_workspace; + if package_check_allowed { + package_workspace_idx = + world.workspaces.iter().position(|ws| match &ws.kind { + project_model::ProjectWorkspaceKind::Cargo { + cargo, + .. + } + | project_model::ProjectWorkspaceKind::DetachedFile { + cargo: Some((cargo, _, _)), + .. + } => *cargo.workspace_root() == root, + _ => false, + }); + if let Some(idx) = package_workspace_idx { + world.flycheck[idx].restart_for_package(package, target); + } + } + } - // Find all workspaces that have at least one target containing the saved file - let workspace_ids = world - .workspaces - .iter() - .enumerate() - .filter(|&(idx, _)| match package_workspace_idx { - Some(pkg_idx) => idx != pkg_idx, - None => true, - }) - .filter(|&(_, ws)| match &ws.kind { - project_model::ProjectWorkspaceKind::Cargo { cargo, .. } - | project_model::ProjectWorkspaceKind::DetachedFile { - cargo: Some((cargo, _, _)), - .. - } => cargo.packages().any(|pkg| { - cargo[pkg] - .targets + if !may_flycheck_workspace { + return Ok(()); + } + + // Trigger flychecks for all workspaces that depend on the saved file + // Crates containing or depending on the saved file + let crate_ids: Vec<_> = world + .analysis + .crates_for(file_id)? + .into_iter() + .flat_map(|id| world.analysis.transitive_rev_deps(id)) + .flatten() + .unique() + .collect(); + tracing::debug!(?crate_ids, "flycheck crate ids"); + let crate_root_paths: Vec<_> = crate_ids .iter() - .any(|&it| crate_root_paths.contains(&cargo[it].root.as_path())) - }), - project_model::ProjectWorkspaceKind::Json(project) => project - .crates() - .any(|(_, krate)| crate_root_paths.contains(&krate.root_module.as_path())), - project_model::ProjectWorkspaceKind::DetachedFile { .. } => false, - }); + .filter_map(|&crate_id| { + world + .analysis + .crate_root(crate_id) + .map(|file_id| { + world + .file_id_to_file_path(file_id) + .as_path() + .map(ToOwned::to_owned) + }) + .transpose() + }) + .collect::>()?; + let crate_root_paths: Vec<_> = + crate_root_paths.iter().map(Deref::deref).collect(); + tracing::debug!(?crate_root_paths, "flycheck crate roots"); - // Find and trigger corresponding flychecks - 'flychecks: for flycheck in world.flycheck.iter() { - for (id, _) in workspace_ids.clone() { - if id == flycheck.id() { - workspace_check_triggered = true; - flycheck.restart_workspace(saved_file.clone()); - continue 'flychecks; - } + // Find all workspaces that have at least one target containing the saved file + let workspace_ids = + world.workspaces.iter().enumerate().filter(|&(idx, ws)| { + let ws_contains_file = match &ws.kind { + project_model::ProjectWorkspaceKind::Cargo { + cargo, .. + } + | project_model::ProjectWorkspaceKind::DetachedFile { + cargo: Some((cargo, _, _)), + .. + } => cargo.packages().any(|pkg| { + cargo[pkg].targets.iter().any(|&it| { + crate_root_paths.contains(&cargo[it].root.as_path()) + }) + }), + project_model::ProjectWorkspaceKind::Json(project) => { + project.crates().any(|(_, krate)| { + crate_root_paths.contains(&krate.root_module.as_path()) + }) + } + project_model::ProjectWorkspaceKind::DetachedFile { + .. + } => false, + }; + let is_pkg_ws = match package_workspace_idx { + Some(pkg_idx) => pkg_idx == idx, + None => false, + }; + ws_contains_file && !is_pkg_ws + }); + + let saved_file = vfs_path.as_path().map(ToOwned::to_owned); + let mut workspace_check_triggered = false; + // Find and trigger corresponding flychecks + 'flychecks: for flycheck in world.flycheck.iter() { + for (id, _) in workspace_ids.clone() { + if id == flycheck.id() { + workspace_check_triggered = true; + flycheck.restart_workspace(saved_file.clone()); + continue 'flychecks; + } + } + } + + // No specific flycheck was triggered, so let's trigger all of them. + if !workspace_check_triggered && package_workspace_idx.is_none() { + for flycheck in world.flycheck.iter() { + flycheck.restart_workspace(saved_file.clone()); + } + } + Ok(()) + }) } - } - // No specific flycheck was triggered, so let's trigger all of them. - if !workspace_check_triggered && package_workspace_idx.is_none() { - for flycheck in world.flycheck.iter() { - flycheck.restart_workspace(saved_file.clone()); - } - } - Ok(()) - }; + }; + state.task_pool.handle.spawn_with_sender(stdx::thread::ThreadIntent::Worker, move |_| { if let Err(e) = std::panic::catch_unwind(task) { tracing::error!("flycheck task panicked: {e:?}") diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index a8a54930c6e5..27738fee5140 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -318,7 +318,7 @@ impl GlobalState { } } - let mut workspaces = linked_projects + let mut workspaces: Vec<_> = linked_projects .iter() .map(|project| match project { LinkedProject::ProjectManifest(manifest) => { @@ -339,7 +339,7 @@ impl GlobalState { Ok(workspace) } }) - .collect::>(); + .collect(); let mut i = 0; while i < workspaces.len() { @@ -848,23 +848,17 @@ impl GlobalState { fn reload_flycheck(&mut self) { let _p = tracing::info_span!("GlobalState::reload_flycheck").entered(); let config = self.config.flycheck(None); - let sender = self.flycheck_sender.clone(); - let invocation_strategy = match config { - FlycheckConfig::CargoCommand { .. } => { - crate::flycheck::InvocationStrategy::PerWorkspace - } - FlycheckConfig::CustomCommand { ref invocation_strategy, .. } => { - invocation_strategy.clone() - } - }; - let next_gen = self.flycheck.iter().map(|f| f.generation() + 1).max().unwrap_or_default(); + let sender = &self.flycheck_sender; + let invocation_strategy = config.invocation_strategy(); + let next_gen = + self.flycheck.iter().map(FlycheckHandle::generation).max().unwrap_or_default() + 1; self.flycheck = match invocation_strategy { crate::flycheck::InvocationStrategy::Once => { vec![FlycheckHandle::spawn( 0, next_gen, - sender, + sender.clone(), config, None, self.config.root_path().clone(), From bfd5a264c7e02ee831aab919c272539cf9395a1d Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 11 Sep 2025 18:05:07 +0200 Subject: [PATCH 396/682] Correctly handle literal search on paths --- src/librustdoc/html/static/js/search.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index b01b596da682..3b84ae2bed06 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -3639,7 +3639,7 @@ class DocSearch { if (contains.length === 0) { return 0; } - const maxPathEditDistance = Math.floor( + const maxPathEditDistance = parsedQuery.literalSearch ? 0 : Math.floor( contains.reduce((acc, next) => acc + next.length, 0) / 3, ); let ret_dist = maxPathEditDistance + 1; @@ -3650,7 +3650,9 @@ class DocSearch { let dist_total = 0; for (let x = 0; x < clength; ++x) { const [p, c] = [path[i + x], contains[x]]; - if (Math.floor((p.length - c.length) / 3) <= maxPathEditDistance && + if (parsedQuery.literalSearch && p !== c) { + continue pathiter; + } else if (Math.floor((p.length - c.length) / 3) <= maxPathEditDistance && p.indexOf(c) !== -1 ) { // discount distance on substring match From 04a1dd1c175cb0fc18419e0261f99bf35199ab59 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 11 Sep 2025 18:05:21 +0200 Subject: [PATCH 397/682] Add regression test for literal search on paths --- tests/rustdoc-js/literal-path.js | 23 +++++++++++++++++++++++ tests/rustdoc-js/literal-path.rs | 7 +++++++ 2 files changed, 30 insertions(+) create mode 100644 tests/rustdoc-js/literal-path.js create mode 100644 tests/rustdoc-js/literal-path.rs diff --git a/tests/rustdoc-js/literal-path.js b/tests/rustdoc-js/literal-path.js new file mode 100644 index 000000000000..ea999ea1a6f0 --- /dev/null +++ b/tests/rustdoc-js/literal-path.js @@ -0,0 +1,23 @@ +// exact-check + +// This test ensures that literal search is always applied on elements of the path. + +const EXPECTED = [ + { + 'query': '"some::path"', + 'others': [ + { 'path': 'literal_path::some', 'name': 'Path' }, + ], + }, + { + 'query': '"somea::path"', + 'others': [ + { 'path': 'literal_path::somea', 'name': 'Path' }, + ], + }, + { + 'query': '"soma::path"', + 'others': [ + ], + }, +]; diff --git a/tests/rustdoc-js/literal-path.rs b/tests/rustdoc-js/literal-path.rs new file mode 100644 index 000000000000..fa7685fd966a --- /dev/null +++ b/tests/rustdoc-js/literal-path.rs @@ -0,0 +1,7 @@ +pub mod some { + pub struct Path; +} + +pub mod somea { + pub struct Path; +} From 18d0dcbc8e61a0c9f7ffead9245b273fd26467a5 Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Thu, 11 Sep 2025 11:29:19 -0400 Subject: [PATCH 398/682] fix config for poison macro test Signed-off-by: Connor Tsui --- library/std/tests/sync/condvar.rs | 18 +++++++++--------- library/std/tests/sync/lib.rs | 3 +++ library/std/tests/sync/mutex.rs | 4 ++-- library/std/tests/sync/rwlock.rs | 8 ++++---- 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/library/std/tests/sync/condvar.rs b/library/std/tests/sync/condvar.rs index 1d712a643003..1b1c33efad58 100644 --- a/library/std/tests/sync/condvar.rs +++ b/library/std/tests/sync/condvar.rs @@ -17,7 +17,7 @@ nonpoison_and_poison_unwrap_test!( } ); -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. nonpoison_and_poison_unwrap_test!( name: notify_one, test_body: { @@ -38,7 +38,7 @@ nonpoison_and_poison_unwrap_test!( } ); -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. nonpoison_and_poison_unwrap_test!( name: notify_all, test_body: { @@ -79,7 +79,7 @@ nonpoison_and_poison_unwrap_test!( } ); -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. nonpoison_and_poison_unwrap_test!( name: test_mutex_arc_condvar, test_body: { @@ -116,7 +116,7 @@ nonpoison_and_poison_unwrap_test!( } ); -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. nonpoison_and_poison_unwrap_test!( name: wait_while, test_body: { @@ -141,7 +141,7 @@ nonpoison_and_poison_unwrap_test!( } ); -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. nonpoison_and_poison_unwrap_test!( name: wait_timeout_wait, test_body: { @@ -164,7 +164,7 @@ nonpoison_and_poison_unwrap_test!( } ); -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. nonpoison_and_poison_unwrap_test!( name: wait_timeout_while_wait, test_body: { @@ -180,7 +180,7 @@ nonpoison_and_poison_unwrap_test!( } ); -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. nonpoison_and_poison_unwrap_test!( name: wait_timeout_while_instant_satisfy, test_body: { @@ -197,7 +197,7 @@ nonpoison_and_poison_unwrap_test!( } ); -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. nonpoison_and_poison_unwrap_test!( name: wait_timeout_while_wake, test_body: { @@ -226,7 +226,7 @@ nonpoison_and_poison_unwrap_test!( } ); -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. nonpoison_and_poison_unwrap_test!( name: wait_timeout_wake, test_body: { diff --git a/library/std/tests/sync/lib.rs b/library/std/tests/sync/lib.rs index ac1dbebcc5cb..6d25aa2cf3db 100644 --- a/library/std/tests/sync/lib.rs +++ b/library/std/tests/sync/lib.rs @@ -58,6 +58,9 @@ fn result_unwrap(x: Result) -> T { /// a no-op (the identity function). /// /// The test names will be prefiex with `poison_` or `nonpoison_`. +/// +/// Important: `cfg_attr()` will not apply attributes to both tests. +/// See for more information. macro_rules! nonpoison_and_poison_unwrap_test { ( name: $name:ident, diff --git a/library/std/tests/sync/mutex.rs b/library/std/tests/sync/mutex.rs index 612c75c7aef5..2445764001b8 100644 --- a/library/std/tests/sync/mutex.rs +++ b/library/std/tests/sync/mutex.rs @@ -266,7 +266,7 @@ nonpoison_and_poison_unwrap_test!( } ); -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +#[cfg(panic = "unwind")] // Requires unwinding support. nonpoison_and_poison_unwrap_test!( name: test_panics, test_body: { @@ -297,7 +297,7 @@ nonpoison_and_poison_unwrap_test!( } ); -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +#[cfg(panic = "unwind")] // Requires unwinding support. nonpoison_and_poison_unwrap_test!( name: test_mutex_arc_access_in_unwind, test_body: { diff --git a/library/std/tests/sync/rwlock.rs b/library/std/tests/sync/rwlock.rs index eca15d2a4adb..65d8bac71945 100644 --- a/library/std/tests/sync/rwlock.rs +++ b/library/std/tests/sync/rwlock.rs @@ -50,7 +50,7 @@ nonpoison_and_poison_unwrap_test!( // FIXME: On macOS we use a provenance-incorrect implementation and Miri // catches that issue with a chance of around 1/1000. // See for details. -#[cfg_attr(all(miri, target_os = "macos"), ignore)] +#[cfg(not(all(miri, target_os = "macos")))] nonpoison_and_poison_unwrap_test!( name: frob, test_body: { @@ -124,7 +124,7 @@ nonpoison_and_poison_unwrap_test!( } ); -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +#[cfg(panic = "unwind")] // Requires unwinding support. nonpoison_and_poison_unwrap_test!( name: test_rw_arc_access_in_unwind, test_body: { @@ -315,7 +315,7 @@ nonpoison_and_poison_unwrap_test!( // FIXME: On macOS we use a provenance-incorrect implementation and Miri catches that issue. // See for details. -#[cfg_attr(all(miri, target_os = "macos"), ignore)] +#[cfg(not(all(miri, target_os = "macos")))] nonpoison_and_poison_unwrap_test!( name: test_downgrade_observe, test_body: { @@ -362,7 +362,7 @@ nonpoison_and_poison_unwrap_test!( // FIXME: On macOS we use a provenance-incorrect implementation and Miri catches that issue. // See for details. -#[cfg_attr(all(miri, target_os = "macos"), ignore)] +#[cfg(not(all(miri, target_os = "macos")))] nonpoison_and_poison_unwrap_test!( name: test_downgrade_atomic, test_body: { From 256aa0d9414f52664dd6e9e97d90fe485a203630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Thu, 11 Sep 2025 18:35:15 +0200 Subject: [PATCH 399/682] Fix `libgccjit` symlink when we build GCC locally --- src/bootstrap/src/core/build_steps/gcc.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/build_steps/gcc.rs b/src/bootstrap/src/core/build_steps/gcc.rs index 77c9622a9bf0..717dea37e9e6 100644 --- a/src/bootstrap/src/core/build_steps/gcc.rs +++ b/src/bootstrap/src/core/build_steps/gcc.rs @@ -32,6 +32,10 @@ pub struct GccOutput { impl GccOutput { /// Install the required libgccjit library file(s) to the specified `path`. pub fn install_to(&self, builder: &Builder<'_>, directory: &Path) { + if builder.config.dry_run() { + return; + } + // At build time, cg_gcc has to link to libgccjit.so (the unversioned symbol). // However, at runtime, it will by default look for libgccjit.so.0. // So when we install the built libgccjit.so file to the target `directory`, we add it there @@ -39,8 +43,16 @@ impl GccOutput { let mut target_filename = self.libgccjit.file_name().unwrap().to_str().unwrap().to_string(); target_filename.push_str(".0"); + // If we build libgccjit ourselves, then `self.libgccjit` can actually be a symlink. + // In that case, we have to resolve it first, otherwise we'd create a symlink to a symlink, + // which wouldn't work. + let actual_libgccjit_path = t!( + self.libgccjit.canonicalize(), + format!("Cannot find libgccjit at {}", self.libgccjit.display()) + ); + let dst = directory.join(target_filename); - builder.copy_link(&self.libgccjit, &dst, FileType::NativeLibrary); + builder.copy_link(&actual_libgccjit_path, &dst, FileType::NativeLibrary); } } From 6354c51d2b6ddf61e791b1b89f395e2350f88817 Mon Sep 17 00:00:00 2001 From: Connor Tsui <87130162+connortsui20@users.noreply.github.com> Date: Thu, 11 Sep 2025 13:08:44 -0400 Subject: [PATCH 400/682] update doc comment Co-authored-by: Ralf Jung --- library/std/tests/sync/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/tests/sync/lib.rs b/library/std/tests/sync/lib.rs index 6d25aa2cf3db..23112e102844 100644 --- a/library/std/tests/sync/lib.rs +++ b/library/std/tests/sync/lib.rs @@ -59,7 +59,7 @@ fn result_unwrap(x: Result) -> T { /// /// The test names will be prefiex with `poison_` or `nonpoison_`. /// -/// Important: `cfg_attr()` will not apply attributes to both tests. +/// Important: most attributes (except `cfg`) will not work properly! (They are only applied to the first test.) /// See for more information. macro_rules! nonpoison_and_poison_unwrap_test { ( From 16b34c6f7056701bbe66892e2d5300d80eae0830 Mon Sep 17 00:00:00 2001 From: Johannes Hostert Date: Thu, 11 Sep 2025 19:15:50 +0200 Subject: [PATCH 401/682] move zero-sized protector dealloc test --- .../pass/both_borrows/basic_aliasing_model.rs | 10 ++++++++++ .../pass/both_borrows/zero-sized-protected.rs | 15 --------------- 2 files changed, 10 insertions(+), 15 deletions(-) delete mode 100644 src/tools/miri/tests/pass/both_borrows/zero-sized-protected.rs diff --git a/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs b/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs index 82976326a8df..115e232dde4c 100644 --- a/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs +++ b/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs @@ -1,6 +1,7 @@ //@revisions: stack tree //@[tree]compile-flags: -Zmiri-tree-borrows #![feature(allocator_api)] +use std::alloc::{Layout, alloc, dealloc}; use std::cell::Cell; use std::ptr; @@ -305,5 +306,14 @@ fn zst() { let ptr = &raw mut *b as *mut (); drop(b); let _ref = &mut *ptr; + + // zero-sized protectors do not affect deallocation + fn with_protector(_x: &mut (), ptr: *mut u8, l: Layout) { + // `_x` here is strongly protected but covers zero bytes. + unsafe { dealloc(ptr, l) }; + } + let l = Layout::from_size_align(1, 1).unwrap(); + let ptr = alloc(l); + with_protector(&mut *ptr.cast::<()>(), ptr, l); } } diff --git a/src/tools/miri/tests/pass/both_borrows/zero-sized-protected.rs b/src/tools/miri/tests/pass/both_borrows/zero-sized-protected.rs deleted file mode 100644 index 23d283ff4a19..000000000000 --- a/src/tools/miri/tests/pass/both_borrows/zero-sized-protected.rs +++ /dev/null @@ -1,15 +0,0 @@ -//@revisions: stack tree -//@[tree]compile-flags: -Zmiri-tree-borrows -use std::alloc::{Layout, alloc, dealloc}; - -// `x` is strongly protected but covers zero bytes. -// This should never be UB. -fn test(_x: &mut (), ptr: *mut u8, l: Layout) { - unsafe { dealloc(ptr, l) }; -} - -fn main() { - let l = Layout::from_size_align(1, 1).unwrap(); - let ptr = unsafe { alloc(l) }; - unsafe { test(&mut *ptr.cast::<()>(), ptr, l) }; -} From 472721b34f73e7a5e6870c9443c35e3ab29958d9 Mon Sep 17 00:00:00 2001 From: binarycat Date: Thu, 11 Sep 2025 12:47:22 -0500 Subject: [PATCH 402/682] bootstrap: rustdoc-js tests can now be filtered by js files --- src/bootstrap/src/core/build_steps/test.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 4f839bdf7b83..723ba80eaf82 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -2086,11 +2086,25 @@ HELP: You can add it into `bootstrap.toml` in `rust.codegen-backends = [{name:?} } // Get paths from cmd args - let paths = match &builder.config.cmd { + let mut paths = match &builder.config.cmd { Subcommand::Test { .. } => &builder.config.paths[..], _ => &[], }; + // in rustdoc-js mode, allow filters to be rs files or js files. + // use a late-initialized Vec to avoid cloning for other modes. + let mut paths_v; + if mode == "rustdoc-js" { + paths_v = paths.to_vec(); + for p in &mut paths_v { + if let Some(ext) = p.extension() + && ext == "js" + { + p.set_extension("rs"); + } + } + paths = &paths_v; + } // Get test-args by striping suite path let mut test_args: Vec<&str> = paths .iter() From 6e12c54bcddb34b085497bf5ca0a6c02387992f0 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 11 Sep 2025 20:09:54 +0200 Subject: [PATCH 403/682] fix(len_zero): don't eagerly call `GenericArgs::type_at` Results in an ICE if the output type has no generic type params --- clippy_lints/src/len_zero.rs | 6 +++--- tests/ui/crashes/ice-15657.rs | 11 +++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 tests/ui/crashes/ice-15657.rs diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index f44a5fdf715e..28a0fbc05115 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -371,9 +371,9 @@ fn parse_len_output<'tcx>(cx: &LateContext<'tcx>, sig: FnSig<'tcx>) -> Option Some(LenOutput::Integral), - ty::Adt(adt, subs) if subs.type_at(0).is_integral() => match cx.tcx.get_diagnostic_name(adt.did()) { - Some(sym::Option) => Some(LenOutput::Option(adt.did())), - Some(sym::Result) => Some(LenOutput::Result(adt.did())), + ty::Adt(adt, subs) => match cx.tcx.get_diagnostic_name(adt.did()) { + Some(sym::Option) => subs.type_at(0).is_integral().then(|| LenOutput::Option(adt.did())), + Some(sym::Result) => subs.type_at(0).is_integral().then(|| LenOutput::Result(adt.did())), _ => None, }, _ => None, diff --git a/tests/ui/crashes/ice-15657.rs b/tests/ui/crashes/ice-15657.rs new file mode 100644 index 000000000000..c6f6506cd8d0 --- /dev/null +++ b/tests/ui/crashes/ice-15657.rs @@ -0,0 +1,11 @@ +//@check-pass +#![warn(clippy::len_zero)] + +pub struct S1; +pub struct S2; + +impl S1 { + pub fn len(&self) -> S2 { + S2 + } +} From 0e5281cea815c5a61ba7e4b8c4dfaaca2c587ca3 Mon Sep 17 00:00:00 2001 From: klensy Date: Thu, 11 Sep 2025 21:40:25 +0300 Subject: [PATCH 404/682] remove unused macro --- .../rustc_llvm/llvm-wrapper/PassWrapper.cpp | 116 ------------------ 1 file changed, 116 deletions(-) diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 3bb1533c2fe2..d970c4a54227 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -79,122 +79,6 @@ extern "C" void LLVMRustTimeTraceProfilerFinish(const char *FileName) { timeTraceProfilerCleanup(); } -#ifdef LLVM_COMPONENT_X86 -#define SUBTARGET_X86 SUBTARGET(X86) -#else -#define SUBTARGET_X86 -#endif - -#ifdef LLVM_COMPONENT_ARM -#define SUBTARGET_ARM SUBTARGET(ARM) -#else -#define SUBTARGET_ARM -#endif - -#ifdef LLVM_COMPONENT_AARCH64 -#define SUBTARGET_AARCH64 SUBTARGET(AArch64) -#else -#define SUBTARGET_AARCH64 -#endif - -#ifdef LLVM_COMPONENT_AVR -#define SUBTARGET_AVR SUBTARGET(AVR) -#else -#define SUBTARGET_AVR -#endif - -#ifdef LLVM_COMPONENT_M68k -#define SUBTARGET_M68K SUBTARGET(M68k) -#else -#define SUBTARGET_M68K -#endif - -#ifdef LLVM_COMPONENT_CSKY -#define SUBTARGET_CSKY SUBTARGET(CSKY) -#else -#define SUBTARGET_CSKY -#endif - -#ifdef LLVM_COMPONENT_MIPS -#define SUBTARGET_MIPS SUBTARGET(Mips) -#else -#define SUBTARGET_MIPS -#endif - -#ifdef LLVM_COMPONENT_POWERPC -#define SUBTARGET_PPC SUBTARGET(PPC) -#else -#define SUBTARGET_PPC -#endif - -#ifdef LLVM_COMPONENT_SYSTEMZ -#define SUBTARGET_SYSTEMZ SUBTARGET(SystemZ) -#else -#define SUBTARGET_SYSTEMZ -#endif - -#ifdef LLVM_COMPONENT_MSP430 -#define SUBTARGET_MSP430 SUBTARGET(MSP430) -#else -#define SUBTARGET_MSP430 -#endif - -#ifdef LLVM_COMPONENT_RISCV -#define SUBTARGET_RISCV SUBTARGET(RISCV) -#else -#define SUBTARGET_RISCV -#endif - -#ifdef LLVM_COMPONENT_SPARC -#define SUBTARGET_SPARC SUBTARGET(Sparc) -#else -#define SUBTARGET_SPARC -#endif - -#ifdef LLVM_COMPONENT_XTENSA -#define SUBTARGET_XTENSA SUBTARGET(XTENSA) -#else -#define SUBTARGET_XTENSA -#endif - -#ifdef LLVM_COMPONENT_HEXAGON -#define SUBTARGET_HEXAGON SUBTARGET(Hexagon) -#else -#define SUBTARGET_HEXAGON -#endif - -#ifdef LLVM_COMPONENT_LOONGARCH -#define SUBTARGET_LOONGARCH SUBTARGET(LoongArch) -#else -#define SUBTARGET_LOONGARCH -#endif - -#define GEN_SUBTARGETS \ - SUBTARGET_X86 \ - SUBTARGET_ARM \ - SUBTARGET_AARCH64 \ - SUBTARGET_AVR \ - SUBTARGET_M68K \ - SUBTARGET_CSKY \ - SUBTARGET_MIPS \ - SUBTARGET_PPC \ - SUBTARGET_SYSTEMZ \ - SUBTARGET_MSP430 \ - SUBTARGET_SPARC \ - SUBTARGET_HEXAGON \ - SUBTARGET_XTENSA \ - SUBTARGET_RISCV \ - SUBTARGET_LOONGARCH - -#define SUBTARGET(x) \ - namespace llvm { \ - extern const SubtargetFeatureKV x##FeatureKV[]; \ - extern const SubtargetFeatureKV x##SubTypeKV[]; \ - } - -GEN_SUBTARGETS -#undef SUBTARGET - // This struct and various functions are sort of a hack right now, but the // problem is that we've got in-memory LLVM modules after we generate and // optimize all codegen-units for one compilation in rustc. To be compatible From acb35038ff21f7f41866b8ea36fed72d9fbc6640 Mon Sep 17 00:00:00 2001 From: klensy Date: Thu, 11 Sep 2025 21:43:18 +0300 Subject: [PATCH 405/682] remove unused getLongestEntryLength --- compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index d970c4a54227..91d11ba317a6 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -224,14 +224,6 @@ static FloatABI::ABIType fromRust(LLVMRustFloatABI RustFloatAbi) { report_fatal_error("Bad FloatABI."); } -/// getLongestEntryLength - Return the length of the longest entry in the table. -template static size_t getLongestEntryLength(ArrayRef Table) { - size_t MaxLen = 0; - for (auto &I : Table) - MaxLen = std::max(MaxLen, std::strlen(I.Key)); - return MaxLen; -} - extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM, RustStringRef OutStr) { ArrayRef CPUTable = From bb31725e679c48e75e54c8ec88c7e654c5509d37 Mon Sep 17 00:00:00 2001 From: sayantn Date: Fri, 12 Sep 2025 01:20:34 +0530 Subject: [PATCH 406/682] Remove big-endian swizzles from `vreinterpret` --- .../core_arch/src/aarch64/neon/generated.rs | 811 -- .../src/arm_shared/neon/generated.rs | 8475 ----------------- .../spec/neon/aarch64.spec.yml | 2 + .../spec/neon/arm_shared.spec.yml | 4 + 4 files changed, 6 insertions(+), 9286 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs b/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs index 554a809db8db..855261aaecfd 100644 --- a/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs +++ b/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs @@ -21477,7 +21477,6 @@ pub fn vrecpxh_f16(a: f16) -> f16 { #[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_f16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] @@ -21486,21 +21485,8 @@ pub fn vreinterpret_f64_f16(a: float16x4_t) -> float64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_f16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f64_f16(a: float16x4_t) -> float64x1_t { - let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_f16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] @@ -21509,24 +21495,8 @@ pub fn vreinterpretq_f64_f16(a: float16x8_t) -> float64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_f16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_f16(a: float16x8_t) -> float64x2_t { - let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] @@ -21535,23 +21505,8 @@ pub fn vreinterpret_f16_f64(a: float64x1_t) -> float16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f16_f64(a: float64x1_t) -> float16x4_t { - unsafe { - let ret_val: float16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] #[cfg(not(target_arch = "arm64ec"))] @@ -21560,24 +21515,8 @@ pub fn vreinterpretq_f16_f64(a: float64x2_t) -> float16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f16_f64(a: float64x2_t) -> float16x8_t { - let a: float64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: float16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_p128)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -21585,22 +21524,8 @@ pub fn vreinterpretq_f64_p128(a: p128) -> float64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_p128)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_p128(a: p128) -> float64x2_t { - unsafe { - let ret_val: float64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -21608,20 +21533,8 @@ pub fn vreinterpret_f64_f32(a: float32x2_t) -> float64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f64_f32(a: float32x2_t) -> float64x1_t { - let a: float32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -21629,20 +21542,8 @@ pub fn vreinterpret_p64_f32(a: float32x2_t) -> poly64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_p64_f32(a: float32x2_t) -> poly64x1_t { - let a: float32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -21650,23 +21551,8 @@ pub fn vreinterpretq_f64_f32(a: float32x4_t) -> float64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_f32(a: float32x4_t) -> float64x2_t { - let a: float32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: float64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -21674,23 +21560,8 @@ pub fn vreinterpretq_p64_f32(a: float32x4_t) -> poly64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_p64_f32(a: float32x4_t) -> poly64x2_t { - let a: float32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: poly64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -21698,22 +21569,8 @@ pub fn vreinterpret_f32_f64(a: float64x1_t) -> float32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f32_f64(a: float64x1_t) -> float32x2_t { - unsafe { - let ret_val: float32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -21721,22 +21578,8 @@ pub fn vreinterpret_s8_f64(a: float64x1_t) -> int8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_s8_f64(a: float64x1_t) -> int8x8_t { - unsafe { - let ret_val: int8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -21744,22 +21587,8 @@ pub fn vreinterpret_s16_f64(a: float64x1_t) -> int16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_s16_f64(a: float64x1_t) -> int16x4_t { - unsafe { - let ret_val: int16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -21767,19 +21596,6 @@ pub fn vreinterpret_s32_f64(a: float64x1_t) -> int32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_s32_f64(a: float64x1_t) -> int32x2_t { - unsafe { - let ret_val: int32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_f64)"] #[inline] #[target_feature(enable = "neon")] @@ -21791,7 +21607,6 @@ pub fn vreinterpret_s64_f64(a: float64x1_t) -> int64x1_t { #[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -21799,22 +21614,8 @@ pub fn vreinterpret_u8_f64(a: float64x1_t) -> uint8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_u8_f64(a: float64x1_t) -> uint8x8_t { - unsafe { - let ret_val: uint8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -21822,22 +21623,8 @@ pub fn vreinterpret_u16_f64(a: float64x1_t) -> uint16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_u16_f64(a: float64x1_t) -> uint16x4_t { - unsafe { - let ret_val: uint16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -21845,19 +21632,6 @@ pub fn vreinterpret_u32_f64(a: float64x1_t) -> uint32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_u32_f64(a: float64x1_t) -> uint32x2_t { - unsafe { - let ret_val: uint32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_f64)"] #[inline] #[target_feature(enable = "neon")] @@ -21869,7 +21643,6 @@ pub fn vreinterpret_u64_f64(a: float64x1_t) -> uint64x1_t { #[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -21877,22 +21650,8 @@ pub fn vreinterpret_p8_f64(a: float64x1_t) -> poly8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_p8_f64(a: float64x1_t) -> poly8x8_t { - unsafe { - let ret_val: poly8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -21900,19 +21659,6 @@ pub fn vreinterpret_p16_f64(a: float64x1_t) -> poly16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_p16_f64(a: float64x1_t) -> poly16x4_t { - unsafe { - let ret_val: poly16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_f64)"] #[inline] #[target_feature(enable = "neon")] @@ -21924,7 +21670,6 @@ pub fn vreinterpret_p64_f64(a: float64x1_t) -> poly64x1_t { #[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -21932,20 +21677,8 @@ pub fn vreinterpretq_p128_f64(a: float64x2_t) -> p128 { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_p128_f64(a: float64x2_t) -> p128 { - let a: float64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -21953,23 +21686,8 @@ pub fn vreinterpretq_f32_f64(a: float64x2_t) -> float32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f32_f64(a: float64x2_t) -> float32x4_t { - let a: float64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: float32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -21977,27 +21695,8 @@ pub fn vreinterpretq_s8_f64(a: float64x2_t) -> int8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_s8_f64(a: float64x2_t) -> int8x16_t { - let a: float64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22005,23 +21704,8 @@ pub fn vreinterpretq_s16_f64(a: float64x2_t) -> int16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_s16_f64(a: float64x2_t) -> int16x8_t { - let a: float64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22029,23 +21713,8 @@ pub fn vreinterpretq_s32_f64(a: float64x2_t) -> int32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_s32_f64(a: float64x2_t) -> int32x4_t { - let a: float64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22053,23 +21722,8 @@ pub fn vreinterpretq_s64_f64(a: float64x2_t) -> int64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_s64_f64(a: float64x2_t) -> int64x2_t { - let a: float64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22077,27 +21731,8 @@ pub fn vreinterpretq_u8_f64(a: float64x2_t) -> uint8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_u8_f64(a: float64x2_t) -> uint8x16_t { - let a: float64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22105,23 +21740,8 @@ pub fn vreinterpretq_u16_f64(a: float64x2_t) -> uint16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_u16_f64(a: float64x2_t) -> uint16x8_t { - let a: float64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22129,23 +21749,8 @@ pub fn vreinterpretq_u32_f64(a: float64x2_t) -> uint32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_u32_f64(a: float64x2_t) -> uint32x4_t { - let a: float64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22153,23 +21758,8 @@ pub fn vreinterpretq_u64_f64(a: float64x2_t) -> uint64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_u64_f64(a: float64x2_t) -> uint64x2_t { - let a: float64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22177,27 +21767,8 @@ pub fn vreinterpretq_p8_f64(a: float64x2_t) -> poly8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_p8_f64(a: float64x2_t) -> poly8x16_t { - let a: float64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: poly8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22205,23 +21776,8 @@ pub fn vreinterpretq_p16_f64(a: float64x2_t) -> poly16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_p16_f64(a: float64x2_t) -> poly16x8_t { - let a: float64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: poly16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_f64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22229,23 +21785,8 @@ pub fn vreinterpretq_p64_f64(a: float64x2_t) -> poly64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_f64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_p64_f64(a: float64x2_t) -> poly64x2_t { - let a: float64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: poly64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22253,20 +21794,8 @@ pub fn vreinterpret_f64_s8(a: int8x8_t) -> float64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f64_s8(a: int8x8_t) -> float64x1_t { - let a: int8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22274,24 +21803,8 @@ pub fn vreinterpretq_f64_s8(a: int8x16_t) -> float64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_s8(a: int8x16_t) -> float64x2_t { - let a: int8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22299,20 +21812,8 @@ pub fn vreinterpret_f64_s16(a: int16x4_t) -> float64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f64_s16(a: int16x4_t) -> float64x1_t { - let a: int16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22320,23 +21821,8 @@ pub fn vreinterpretq_f64_s16(a: int16x8_t) -> float64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_s16(a: int16x8_t) -> float64x2_t { - let a: int16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22344,20 +21830,8 @@ pub fn vreinterpret_f64_s32(a: int32x2_t) -> float64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f64_s32(a: int32x2_t) -> float64x1_t { - let a: int32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22365,20 +21839,6 @@ pub fn vreinterpretq_f64_s32(a: int32x4_t) -> float64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_s32(a: int32x4_t) -> float64x2_t { - let a: int32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: float64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_s64)"] #[inline] #[target_feature(enable = "neon")] @@ -22399,7 +21859,6 @@ pub fn vreinterpret_p64_s64(a: int64x1_t) -> poly64x1_t { #[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_s64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22407,23 +21866,8 @@ pub fn vreinterpretq_f64_s64(a: int64x2_t) -> float64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_s64(a: int64x2_t) -> float64x2_t { - let a: int64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: float64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_s64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22431,23 +21875,8 @@ pub fn vreinterpretq_p64_s64(a: int64x2_t) -> poly64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_p64_s64(a: int64x2_t) -> poly64x2_t { - let a: int64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: poly64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22455,20 +21884,8 @@ pub fn vreinterpret_f64_u8(a: uint8x8_t) -> float64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f64_u8(a: uint8x8_t) -> float64x1_t { - let a: uint8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22476,24 +21893,8 @@ pub fn vreinterpretq_f64_u8(a: uint8x16_t) -> float64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_u8(a: uint8x16_t) -> float64x2_t { - let a: uint8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22501,20 +21902,8 @@ pub fn vreinterpret_f64_u16(a: uint16x4_t) -> float64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f64_u16(a: uint16x4_t) -> float64x1_t { - let a: uint16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22522,23 +21911,8 @@ pub fn vreinterpretq_f64_u16(a: uint16x8_t) -> float64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_u16(a: uint16x8_t) -> float64x2_t { - let a: uint16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22546,20 +21920,8 @@ pub fn vreinterpret_f64_u32(a: uint32x2_t) -> float64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_u32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f64_u32(a: uint32x2_t) -> float64x1_t { - let a: uint32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22567,20 +21929,6 @@ pub fn vreinterpretq_f64_u32(a: uint32x4_t) -> float64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_u32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_u32(a: uint32x4_t) -> float64x2_t { - let a: uint32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: float64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_u64)"] #[inline] #[target_feature(enable = "neon")] @@ -22601,7 +21949,6 @@ pub fn vreinterpret_p64_u64(a: uint64x1_t) -> poly64x1_t { #[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_u64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22609,23 +21956,8 @@ pub fn vreinterpretq_f64_u64(a: uint64x2_t) -> float64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_u64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_u64(a: uint64x2_t) -> float64x2_t { - let a: uint64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: float64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_u64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22633,23 +21965,8 @@ pub fn vreinterpretq_p64_u64(a: uint64x2_t) -> poly64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_u64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_p64_u64(a: uint64x2_t) -> poly64x2_t { - let a: uint64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: poly64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_p8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22657,20 +21974,8 @@ pub fn vreinterpret_f64_p8(a: poly8x8_t) -> float64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_p8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f64_p8(a: poly8x8_t) -> float64x1_t { - let a: poly8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_p8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22678,24 +21983,8 @@ pub fn vreinterpretq_f64_p8(a: poly8x16_t) -> float64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_p8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_p8(a: poly8x16_t) -> float64x2_t { - let a: poly8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_p16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22703,20 +21992,8 @@ pub fn vreinterpret_f64_p16(a: poly16x4_t) -> float64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_p16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f64_p16(a: poly16x4_t) -> float64x1_t { - let a: poly16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_p16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22724,23 +22001,8 @@ pub fn vreinterpretq_f64_p16(a: poly16x8_t) -> float64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_p16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_p16(a: poly16x8_t) -> float64x2_t { - let a: poly16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_p64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22748,19 +22010,6 @@ pub fn vreinterpret_f32_p64(a: poly64x1_t) -> float32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_p64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpret_f32_p64(a: poly64x1_t) -> float32x2_t { - unsafe { - let ret_val: float32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f64_p64)"] #[inline] #[target_feature(enable = "neon")] @@ -22790,7 +22039,6 @@ pub fn vreinterpret_u64_p64(a: poly64x1_t) -> uint64x1_t { #[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_p64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22798,23 +22046,8 @@ pub fn vreinterpretq_f32_p64(a: poly64x2_t) -> float32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_p64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f32_p64(a: poly64x2_t) -> float32x4_t { - let a: poly64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: float32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_p64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22822,23 +22055,8 @@ pub fn vreinterpretq_f64_p64(a: poly64x2_t) -> float64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f64_p64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_f64_p64(a: poly64x2_t) -> float64x2_t { - let a: poly64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: float64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_p64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] @@ -22846,43 +22064,14 @@ pub fn vreinterpretq_s64_p64(a: poly64x2_t) -> int64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_p64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_s64_p64(a: poly64x2_t) -> int64x2_t { - let a: poly64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_p64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[stable(feature = "neon_intrinsics", since = "1.59.0")] #[cfg_attr(test, assert_instr(nop))] pub fn vreinterpretq_u64_p64(a: poly64x2_t) -> uint64x2_t { unsafe { transmute(a) } } -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_p64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[stable(feature = "neon_intrinsics", since = "1.59.0")] -#[cfg_attr(test, assert_instr(nop))] -pub fn vreinterpretq_u64_p64(a: poly64x2_t) -> uint64x2_t { - let a: poly64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} #[doc = "Floating-point round to 32-bit integer, using current rounding mode"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vrnd32x_f32)"] #[inline] diff --git a/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs b/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs index b5ba792b18ae..e4e4e040f468 100644 --- a/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs +++ b/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs @@ -42089,7 +42089,6 @@ pub fn vrecpsq_f32(a: float32x4_t, b: float32x4_t) -> float32x4_t { #[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42103,29 +42102,8 @@ pub fn vreinterpret_f32_f16(a: float16x4_t) -> float32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_f16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f32_f16(a: float16x4_t) -> float32x2_t { - let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: float32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42139,29 +42117,8 @@ pub fn vreinterpret_s8_f16(a: float16x4_t) -> int8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_f16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_s8_f16(a: float16x4_t) -> int8x8_t { - let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42175,29 +42132,8 @@ pub fn vreinterpret_s16_f16(a: float16x4_t) -> int16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_f16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_s16_f16(a: float16x4_t) -> int16x4_t { - let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42211,29 +42147,8 @@ pub fn vreinterpret_s32_f16(a: float16x4_t) -> int32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_f16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_s32_f16(a: float16x4_t) -> int32x2_t { - let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42247,26 +42162,8 @@ pub fn vreinterpret_s64_f16(a: float16x4_t) -> int64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_f16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_s64_f16(a: float16x4_t) -> int64x1_t { - let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42280,29 +42177,8 @@ pub fn vreinterpret_u8_f16(a: float16x4_t) -> uint8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_f16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_u8_f16(a: float16x4_t) -> uint8x8_t { - let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42316,29 +42192,8 @@ pub fn vreinterpret_u16_f16(a: float16x4_t) -> uint16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_f16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_u16_f16(a: float16x4_t) -> uint16x4_t { - let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42352,29 +42207,8 @@ pub fn vreinterpret_u32_f16(a: float16x4_t) -> uint32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_f16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_u32_f16(a: float16x4_t) -> uint32x2_t { - let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42388,26 +42222,8 @@ pub fn vreinterpret_u64_f16(a: float16x4_t) -> uint64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_f16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_u64_f16(a: float16x4_t) -> uint64x1_t { - let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42421,29 +42237,8 @@ pub fn vreinterpret_p8_f16(a: float16x4_t) -> poly8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_f16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_p8_f16(a: float16x4_t) -> poly8x8_t { - let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: poly8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42457,29 +42252,8 @@ pub fn vreinterpret_p16_f16(a: float16x4_t) -> poly16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_f16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_p16_f16(a: float16x4_t) -> poly16x4_t { - let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: poly16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42493,29 +42267,8 @@ pub fn vreinterpretq_f32_f16(a: float16x8_t) -> float32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_f16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f32_f16(a: float16x8_t) -> float32x4_t { - let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42529,33 +42282,8 @@ pub fn vreinterpretq_s8_f16(a: float16x8_t) -> int8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_f16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_s8_f16(a: float16x8_t) -> int8x16_t { - let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42569,29 +42297,8 @@ pub fn vreinterpretq_s16_f16(a: float16x8_t) -> int16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_f16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_s16_f16(a: float16x8_t) -> int16x8_t { - let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42605,29 +42312,8 @@ pub fn vreinterpretq_s32_f16(a: float16x8_t) -> int32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_f16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_s32_f16(a: float16x8_t) -> int32x4_t { - let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42641,29 +42327,8 @@ pub fn vreinterpretq_s64_f16(a: float16x8_t) -> int64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_f16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_s64_f16(a: float16x8_t) -> int64x2_t { - let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42677,33 +42342,8 @@ pub fn vreinterpretq_u8_f16(a: float16x8_t) -> uint8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_f16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_u8_f16(a: float16x8_t) -> uint8x16_t { - let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42717,29 +42357,8 @@ pub fn vreinterpretq_u16_f16(a: float16x8_t) -> uint16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_f16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_u16_f16(a: float16x8_t) -> uint16x8_t { - let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42753,29 +42372,8 @@ pub fn vreinterpretq_u32_f16(a: float16x8_t) -> uint32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_f16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_u32_f16(a: float16x8_t) -> uint32x4_t { - let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42789,29 +42387,8 @@ pub fn vreinterpretq_u64_f16(a: float16x8_t) -> uint64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_f16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_u64_f16(a: float16x8_t) -> uint64x2_t { - let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42825,33 +42402,8 @@ pub fn vreinterpretq_p8_f16(a: float16x8_t) -> poly8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_f16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_p8_f16(a: float16x8_t) -> poly8x16_t { - let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42865,29 +42417,8 @@ pub fn vreinterpretq_p16_f16(a: float16x8_t) -> poly16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_f16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_p16_f16(a: float16x8_t) -> poly16x8_t { - let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_f32)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42901,29 +42432,8 @@ pub fn vreinterpret_f16_f32(a: float32x2_t) -> float16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_f32(a: float32x2_t) -> float16x4_t { - let a: float32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: float16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_f32)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42937,29 +42447,8 @@ pub fn vreinterpretq_f16_f32(a: float32x4_t) -> float16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_f32(a: float32x4_t) -> float16x8_t { - let a: float32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: float16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_s8)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -42973,29 +42462,8 @@ pub fn vreinterpret_f16_s8(a: int8x8_t) -> float16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_s8(a: int8x8_t) -> float16x4_t { - let a: int8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_s8)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -43009,30 +42477,8 @@ pub fn vreinterpretq_f16_s8(a: int8x16_t) -> float16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_s8(a: int8x16_t) -> float16x8_t { - let a: int8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_s16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -43046,29 +42492,8 @@ pub fn vreinterpret_f16_s16(a: int16x4_t) -> float16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_s16(a: int16x4_t) -> float16x4_t { - let a: int16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: float16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_s16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -43082,29 +42507,8 @@ pub fn vreinterpretq_f16_s16(a: int16x8_t) -> float16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_s16(a: int16x8_t) -> float16x8_t { - let a: int16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_s32)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -43118,29 +42522,8 @@ pub fn vreinterpret_f16_s32(a: int32x2_t) -> float16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_s32(a: int32x2_t) -> float16x4_t { - let a: int32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: float16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_s32)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -43154,29 +42537,8 @@ pub fn vreinterpretq_f16_s32(a: int32x4_t) -> float16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_s32(a: int32x4_t) -> float16x8_t { - let a: int32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: float16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_s64)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -43190,28 +42552,8 @@ pub fn vreinterpret_f16_s64(a: int64x1_t) -> float16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_s64(a: int64x1_t) -> float16x4_t { - unsafe { - let ret_val: float16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_s64)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -43225,29 +42567,8 @@ pub fn vreinterpretq_f16_s64(a: int64x2_t) -> float16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_s64(a: int64x2_t) -> float16x8_t { - let a: int64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: float16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_u8)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -43261,29 +42582,8 @@ pub fn vreinterpret_f16_u8(a: uint8x8_t) -> float16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_u8(a: uint8x8_t) -> float16x4_t { - let a: uint8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_u8)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -43297,30 +42597,8 @@ pub fn vreinterpretq_f16_u8(a: uint8x16_t) -> float16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_u8(a: uint8x16_t) -> float16x8_t { - let a: uint8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_u16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -43334,29 +42612,8 @@ pub fn vreinterpret_f16_u16(a: uint16x4_t) -> float16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_u16(a: uint16x4_t) -> float16x4_t { - let a: uint16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: float16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_u16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -43370,29 +42627,8 @@ pub fn vreinterpretq_f16_u16(a: uint16x8_t) -> float16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_u16(a: uint16x8_t) -> float16x8_t { - let a: uint16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_u32)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -43406,29 +42642,8 @@ pub fn vreinterpret_f16_u32(a: uint32x2_t) -> float16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_u32)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_u32(a: uint32x2_t) -> float16x4_t { - let a: uint32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: float16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_u32)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -43442,29 +42657,8 @@ pub fn vreinterpretq_f16_u32(a: uint32x4_t) -> float16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_u32)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_u32(a: uint32x4_t) -> float16x8_t { - let a: uint32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: float16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_u64)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -43478,28 +42672,8 @@ pub fn vreinterpret_f16_u64(a: uint64x1_t) -> float16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_u64)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_u64(a: uint64x1_t) -> float16x4_t { - unsafe { - let ret_val: float16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_u64)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -43513,29 +42687,8 @@ pub fn vreinterpretq_f16_u64(a: uint64x2_t) -> float16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_u64)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_u64(a: uint64x2_t) -> float16x8_t { - let a: uint64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: float16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_p8)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -43549,29 +42702,8 @@ pub fn vreinterpret_f16_p8(a: poly8x8_t) -> float16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_p8)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_p8(a: poly8x8_t) -> float16x4_t { - let a: poly8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_p8)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -43585,30 +42717,8 @@ pub fn vreinterpretq_f16_p8(a: poly8x16_t) -> float16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_p8)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_p8(a: poly8x16_t) -> float16x8_t { - let a: poly8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_p16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -43622,29 +42732,8 @@ pub fn vreinterpret_f16_p16(a: poly16x4_t) -> float16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_p16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_p16(a: poly16x4_t) -> float16x4_t { - let a: poly16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: float16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_p16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -43658,29 +42747,8 @@ pub fn vreinterpretq_f16_p16(a: poly16x8_t) -> float16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_p16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_p16(a: poly16x8_t) -> float16x8_t { - let a: poly16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_p128)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -43694,28 +42762,8 @@ pub fn vreinterpretq_f16_p128(a: p128) -> float16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_p128)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_p128(a: p128) -> float16x8_t { - unsafe { - let ret_val: float16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -43729,26 +42777,8 @@ pub fn vreinterpret_p64_f16(a: float16x4_t) -> poly64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_f16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_p64_f16(a: float16x4_t) -> poly64x1_t { - let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -43762,26 +42792,8 @@ pub fn vreinterpretq_p128_f16(a: float16x8_t) -> p128 { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_f16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_p128_f16(a: float16x8_t) -> p128 { - let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_f16)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -43795,29 +42807,8 @@ pub fn vreinterpretq_p64_f16(a: float16x8_t) -> poly64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_f16)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_p64_f16(a: float16x8_t) -> poly64x2_t { - let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_p64)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -43831,28 +42822,8 @@ pub fn vreinterpret_f16_p64(a: poly64x1_t) -> float16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f16_p64)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpret_f16_p64(a: poly64x1_t) -> float16x4_t { - unsafe { - let ret_val: float16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_p64)"] #[inline] -#[cfg(target_endian = "little")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[cfg_attr( @@ -43866,29 +42837,8 @@ pub fn vreinterpretq_f16_p64(a: poly64x2_t) -> float16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f16_p64)"] -#[inline] -#[cfg(target_endian = "big")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[target_feature(enable = "neon,fp16")] -#[unstable(feature = "stdarch_neon_f16", issue = "136306")] -#[cfg(not(target_arch = "arm64ec"))] -pub fn vreinterpretq_f16_p64(a: poly64x2_t) -> float16x8_t { - let a: poly64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: float16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_p128)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -43908,34 +42858,8 @@ pub fn vreinterpretq_f32_p128(a: p128) -> float32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_p128)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_f32_p128(a: p128) -> float32x4_t { - unsafe { - let ret_val: float32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -43955,35 +42879,8 @@ pub fn vreinterpret_s8_f32(a: float32x2_t) -> int8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s8_f32(a: float32x2_t) -> int8x8_t { - let a: float32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -44003,35 +42900,8 @@ pub fn vreinterpret_s16_f32(a: float32x2_t) -> int16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s16_f32(a: float32x2_t) -> int16x4_t { - let a: float32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -44051,35 +42921,8 @@ pub fn vreinterpret_s32_f32(a: float32x2_t) -> int32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s32_f32(a: float32x2_t) -> int32x2_t { - let a: float32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -44099,32 +42942,8 @@ pub fn vreinterpret_s64_f32(a: float32x2_t) -> int64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s64_f32(a: float32x2_t) -> int64x1_t { - let a: float32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -44144,35 +42963,8 @@ pub fn vreinterpret_u8_f32(a: float32x2_t) -> uint8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u8_f32(a: float32x2_t) -> uint8x8_t { - let a: float32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -44192,35 +42984,8 @@ pub fn vreinterpret_u16_f32(a: float32x2_t) -> uint16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u16_f32(a: float32x2_t) -> uint16x4_t { - let a: float32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -44240,35 +43005,8 @@ pub fn vreinterpret_u32_f32(a: float32x2_t) -> uint32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u32_f32(a: float32x2_t) -> uint32x2_t { - let a: float32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -44288,32 +43026,8 @@ pub fn vreinterpret_u64_f32(a: float32x2_t) -> uint64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u64_f32(a: float32x2_t) -> uint64x1_t { - let a: float32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -44333,35 +43047,8 @@ pub fn vreinterpret_p8_f32(a: float32x2_t) -> poly8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p8_f32(a: float32x2_t) -> poly8x8_t { - let a: float32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: poly8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -44381,35 +43068,8 @@ pub fn vreinterpret_p16_f32(a: float32x2_t) -> poly16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p16_f32(a: float32x2_t) -> poly16x4_t { - let a: float32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: poly16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -44429,32 +43089,8 @@ pub fn vreinterpretq_p128_f32(a: float32x4_t) -> p128 { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p128_f32(a: float32x4_t) -> p128 { - let a: float32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -44474,39 +43110,8 @@ pub fn vreinterpretq_s8_f32(a: float32x4_t) -> int8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s8_f32(a: float32x4_t) -> int8x16_t { - let a: float32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -44526,35 +43131,8 @@ pub fn vreinterpretq_s16_f32(a: float32x4_t) -> int16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s16_f32(a: float32x4_t) -> int16x8_t { - let a: float32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -44574,35 +43152,8 @@ pub fn vreinterpretq_s32_f32(a: float32x4_t) -> int32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s32_f32(a: float32x4_t) -> int32x4_t { - let a: float32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -44622,35 +43173,8 @@ pub fn vreinterpretq_s64_f32(a: float32x4_t) -> int64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s64_f32(a: float32x4_t) -> int64x2_t { - let a: float32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -44670,39 +43194,8 @@ pub fn vreinterpretq_u8_f32(a: float32x4_t) -> uint8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u8_f32(a: float32x4_t) -> uint8x16_t { - let a: float32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -44722,35 +43215,8 @@ pub fn vreinterpretq_u16_f32(a: float32x4_t) -> uint16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u16_f32(a: float32x4_t) -> uint16x8_t { - let a: float32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -44770,35 +43236,8 @@ pub fn vreinterpretq_u32_f32(a: float32x4_t) -> uint32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u32_f32(a: float32x4_t) -> uint32x4_t { - let a: float32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -44818,35 +43257,8 @@ pub fn vreinterpretq_u64_f32(a: float32x4_t) -> uint64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u64_f32(a: float32x4_t) -> uint64x2_t { - let a: float32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -44866,39 +43278,8 @@ pub fn vreinterpretq_p8_f32(a: float32x4_t) -> poly8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p8_f32(a: float32x4_t) -> poly8x16_t { - let a: float32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: poly8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_f32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -44918,35 +43299,8 @@ pub fn vreinterpretq_p16_f32(a: float32x4_t) -> poly16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_f32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p16_f32(a: float32x4_t) -> poly16x8_t { - let a: float32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: poly16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -44966,35 +43320,8 @@ pub fn vreinterpret_f32_s8(a: int8x8_t) -> float32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_f32_s8(a: int8x8_t) -> float32x2_t { - let a: int8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -45014,35 +43341,8 @@ pub fn vreinterpret_s16_s8(a: int8x8_t) -> int16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s16_s8(a: int8x8_t) -> int16x4_t { - let a: int8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -45062,35 +43362,8 @@ pub fn vreinterpret_s32_s8(a: int8x8_t) -> int32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s32_s8(a: int8x8_t) -> int32x2_t { - let a: int8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -45110,32 +43383,8 @@ pub fn vreinterpret_s64_s8(a: int8x8_t) -> int64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s64_s8(a: int8x8_t) -> int64x1_t { - let a: int8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -45155,35 +43404,8 @@ pub fn vreinterpret_u8_s8(a: int8x8_t) -> uint8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u8_s8(a: int8x8_t) -> uint8x8_t { - let a: int8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -45203,35 +43425,8 @@ pub fn vreinterpret_u16_s8(a: int8x8_t) -> uint16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u16_s8(a: int8x8_t) -> uint16x4_t { - let a: int8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -45251,35 +43446,8 @@ pub fn vreinterpret_u32_s8(a: int8x8_t) -> uint32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u32_s8(a: int8x8_t) -> uint32x2_t { - let a: int8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -45299,32 +43467,8 @@ pub fn vreinterpret_u64_s8(a: int8x8_t) -> uint64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u64_s8(a: int8x8_t) -> uint64x1_t { - let a: int8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -45344,35 +43488,8 @@ pub fn vreinterpret_p8_s8(a: int8x8_t) -> poly8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p8_s8(a: int8x8_t) -> poly8x8_t { - let a: int8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -45392,35 +43509,8 @@ pub fn vreinterpret_p16_s8(a: int8x8_t) -> poly16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p16_s8(a: int8x8_t) -> poly16x4_t { - let a: int8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -45440,36 +43530,8 @@ pub fn vreinterpretq_f32_s8(a: int8x16_t) -> float32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_f32_s8(a: int8x16_t) -> float32x4_t { - let a: int8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -45489,36 +43551,8 @@ pub fn vreinterpretq_s16_s8(a: int8x16_t) -> int16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s16_s8(a: int8x16_t) -> int16x8_t { - let a: int8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -45538,36 +43572,8 @@ pub fn vreinterpretq_s32_s8(a: int8x16_t) -> int32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s32_s8(a: int8x16_t) -> int32x4_t { - let a: int8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -45587,36 +43593,8 @@ pub fn vreinterpretq_s64_s8(a: int8x16_t) -> int64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s64_s8(a: int8x16_t) -> int64x2_t { - let a: int8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -45636,40 +43614,8 @@ pub fn vreinterpretq_u8_s8(a: int8x16_t) -> uint8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u8_s8(a: int8x16_t) -> uint8x16_t { - let a: int8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -45689,36 +43635,8 @@ pub fn vreinterpretq_u16_s8(a: int8x16_t) -> uint16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u16_s8(a: int8x16_t) -> uint16x8_t { - let a: int8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -45738,36 +43656,8 @@ pub fn vreinterpretq_u32_s8(a: int8x16_t) -> uint32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u32_s8(a: int8x16_t) -> uint32x4_t { - let a: int8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -45787,36 +43677,8 @@ pub fn vreinterpretq_u64_s8(a: int8x16_t) -> uint64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u64_s8(a: int8x16_t) -> uint64x2_t { - let a: int8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -45836,40 +43698,8 @@ pub fn vreinterpretq_p8_s8(a: int8x16_t) -> poly8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p8_s8(a: int8x16_t) -> poly8x16_t { - let a: int8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -45889,36 +43719,8 @@ pub fn vreinterpretq_p16_s8(a: int8x16_t) -> poly16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p16_s8(a: int8x16_t) -> poly16x8_t { - let a: int8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -45938,35 +43740,8 @@ pub fn vreinterpret_f32_s16(a: int16x4_t) -> float32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_f32_s16(a: int16x4_t) -> float32x2_t { - let a: int16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: float32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -45986,35 +43761,8 @@ pub fn vreinterpret_s8_s16(a: int16x4_t) -> int8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s8_s16(a: int16x4_t) -> int8x8_t { - let a: int16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -46034,35 +43782,8 @@ pub fn vreinterpret_s32_s16(a: int16x4_t) -> int32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s32_s16(a: int16x4_t) -> int32x2_t { - let a: int16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -46082,32 +43803,8 @@ pub fn vreinterpret_s64_s16(a: int16x4_t) -> int64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s64_s16(a: int16x4_t) -> int64x1_t { - let a: int16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -46127,35 +43824,8 @@ pub fn vreinterpret_u8_s16(a: int16x4_t) -> uint8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u8_s16(a: int16x4_t) -> uint8x8_t { - let a: int16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -46175,35 +43845,8 @@ pub fn vreinterpret_u16_s16(a: int16x4_t) -> uint16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u16_s16(a: int16x4_t) -> uint16x4_t { - let a: int16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -46223,35 +43866,8 @@ pub fn vreinterpret_u32_s16(a: int16x4_t) -> uint32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u32_s16(a: int16x4_t) -> uint32x2_t { - let a: int16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -46271,32 +43887,8 @@ pub fn vreinterpret_u64_s16(a: int16x4_t) -> uint64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u64_s16(a: int16x4_t) -> uint64x1_t { - let a: int16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -46316,35 +43908,8 @@ pub fn vreinterpret_p8_s16(a: int16x4_t) -> poly8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p8_s16(a: int16x4_t) -> poly8x8_t { - let a: int16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: poly8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -46364,35 +43929,8 @@ pub fn vreinterpret_p16_s16(a: int16x4_t) -> poly16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p16_s16(a: int16x4_t) -> poly16x4_t { - let a: int16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: poly16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -46412,35 +43950,8 @@ pub fn vreinterpretq_f32_s16(a: int16x8_t) -> float32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_f32_s16(a: int16x8_t) -> float32x4_t { - let a: int16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -46460,39 +43971,8 @@ pub fn vreinterpretq_s8_s16(a: int16x8_t) -> int8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s8_s16(a: int16x8_t) -> int8x16_t { - let a: int16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -46512,35 +43992,8 @@ pub fn vreinterpretq_s32_s16(a: int16x8_t) -> int32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s32_s16(a: int16x8_t) -> int32x4_t { - let a: int16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -46560,35 +44013,8 @@ pub fn vreinterpretq_s64_s16(a: int16x8_t) -> int64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s64_s16(a: int16x8_t) -> int64x2_t { - let a: int16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -46608,39 +44034,8 @@ pub fn vreinterpretq_u8_s16(a: int16x8_t) -> uint8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u8_s16(a: int16x8_t) -> uint8x16_t { - let a: int16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -46660,35 +44055,8 @@ pub fn vreinterpretq_u16_s16(a: int16x8_t) -> uint16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u16_s16(a: int16x8_t) -> uint16x8_t { - let a: int16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -46708,35 +44076,8 @@ pub fn vreinterpretq_u32_s16(a: int16x8_t) -> uint32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u32_s16(a: int16x8_t) -> uint32x4_t { - let a: int16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -46756,35 +44097,8 @@ pub fn vreinterpretq_u64_s16(a: int16x8_t) -> uint64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u64_s16(a: int16x8_t) -> uint64x2_t { - let a: int16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -46804,39 +44118,8 @@ pub fn vreinterpretq_p8_s16(a: int16x8_t) -> poly8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p8_s16(a: int16x8_t) -> poly8x16_t { - let a: int16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -46856,35 +44139,8 @@ pub fn vreinterpretq_p16_s16(a: int16x8_t) -> poly16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p16_s16(a: int16x8_t) -> poly16x8_t { - let a: int16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -46904,35 +44160,8 @@ pub fn vreinterpret_f32_s32(a: int32x2_t) -> float32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_f32_s32(a: int32x2_t) -> float32x2_t { - let a: int32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: float32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -46952,35 +44181,8 @@ pub fn vreinterpret_s8_s32(a: int32x2_t) -> int8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s8_s32(a: int32x2_t) -> int8x8_t { - let a: int32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -47000,35 +44202,8 @@ pub fn vreinterpret_s16_s32(a: int32x2_t) -> int16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s16_s32(a: int32x2_t) -> int16x4_t { - let a: int32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -47048,32 +44223,8 @@ pub fn vreinterpret_s64_s32(a: int32x2_t) -> int64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s64_s32(a: int32x2_t) -> int64x1_t { - let a: int32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -47093,35 +44244,8 @@ pub fn vreinterpret_u8_s32(a: int32x2_t) -> uint8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u8_s32(a: int32x2_t) -> uint8x8_t { - let a: int32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -47141,35 +44265,8 @@ pub fn vreinterpret_u16_s32(a: int32x2_t) -> uint16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u16_s32(a: int32x2_t) -> uint16x4_t { - let a: int32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -47189,35 +44286,8 @@ pub fn vreinterpret_u32_s32(a: int32x2_t) -> uint32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u32_s32(a: int32x2_t) -> uint32x2_t { - let a: int32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -47237,32 +44307,8 @@ pub fn vreinterpret_u64_s32(a: int32x2_t) -> uint64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u64_s32(a: int32x2_t) -> uint64x1_t { - let a: int32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -47282,35 +44328,8 @@ pub fn vreinterpret_p8_s32(a: int32x2_t) -> poly8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p8_s32(a: int32x2_t) -> poly8x8_t { - let a: int32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: poly8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -47330,35 +44349,8 @@ pub fn vreinterpret_p16_s32(a: int32x2_t) -> poly16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p16_s32(a: int32x2_t) -> poly16x4_t { - let a: int32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: poly16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -47378,35 +44370,8 @@ pub fn vreinterpretq_f32_s32(a: int32x4_t) -> float32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_f32_s32(a: int32x4_t) -> float32x4_t { - let a: int32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: float32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -47426,39 +44391,8 @@ pub fn vreinterpretq_s8_s32(a: int32x4_t) -> int8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s8_s32(a: int32x4_t) -> int8x16_t { - let a: int32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -47478,35 +44412,8 @@ pub fn vreinterpretq_s16_s32(a: int32x4_t) -> int16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s16_s32(a: int32x4_t) -> int16x8_t { - let a: int32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -47526,35 +44433,8 @@ pub fn vreinterpretq_s64_s32(a: int32x4_t) -> int64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s64_s32(a: int32x4_t) -> int64x2_t { - let a: int32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -47574,39 +44454,8 @@ pub fn vreinterpretq_u8_s32(a: int32x4_t) -> uint8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u8_s32(a: int32x4_t) -> uint8x16_t { - let a: int32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -47626,35 +44475,8 @@ pub fn vreinterpretq_u16_s32(a: int32x4_t) -> uint16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u16_s32(a: int32x4_t) -> uint16x8_t { - let a: int32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -47674,35 +44496,8 @@ pub fn vreinterpretq_u32_s32(a: int32x4_t) -> uint32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u32_s32(a: int32x4_t) -> uint32x4_t { - let a: int32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -47722,35 +44517,8 @@ pub fn vreinterpretq_u64_s32(a: int32x4_t) -> uint64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u64_s32(a: int32x4_t) -> uint64x2_t { - let a: int32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -47770,39 +44538,8 @@ pub fn vreinterpretq_p8_s32(a: int32x4_t) -> poly8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p8_s32(a: int32x4_t) -> poly8x16_t { - let a: int32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: poly8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -47822,35 +44559,8 @@ pub fn vreinterpretq_p16_s32(a: int32x4_t) -> poly16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p16_s32(a: int32x4_t) -> poly16x8_t { - let a: int32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: poly16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_s64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -47870,34 +44580,8 @@ pub fn vreinterpret_f32_s64(a: int64x1_t) -> float32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_f32_s64(a: int64x1_t) -> float32x2_t { - unsafe { - let ret_val: float32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_s64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -47917,34 +44601,8 @@ pub fn vreinterpret_s8_s64(a: int64x1_t) -> int8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s8_s64(a: int64x1_t) -> int8x8_t { - unsafe { - let ret_val: int8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_s64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -47964,34 +44622,8 @@ pub fn vreinterpret_s16_s64(a: int64x1_t) -> int16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s16_s64(a: int64x1_t) -> int16x4_t { - unsafe { - let ret_val: int16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_s64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -48011,34 +44643,8 @@ pub fn vreinterpret_s32_s64(a: int64x1_t) -> int32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s32_s64(a: int64x1_t) -> int32x2_t { - unsafe { - let ret_val: int32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_s64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -48058,34 +44664,8 @@ pub fn vreinterpret_u8_s64(a: int64x1_t) -> uint8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u8_s64(a: int64x1_t) -> uint8x8_t { - unsafe { - let ret_val: uint8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_s64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -48105,34 +44685,8 @@ pub fn vreinterpret_u16_s64(a: int64x1_t) -> uint16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u16_s64(a: int64x1_t) -> uint16x4_t { - unsafe { - let ret_val: uint16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_s64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -48152,31 +44706,6 @@ pub fn vreinterpret_u32_s64(a: int64x1_t) -> uint32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u32_s64(a: int64x1_t) -> uint32x2_t { - unsafe { - let ret_val: uint32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_s64)"] #[inline] #[target_feature(enable = "neon")] @@ -48200,7 +44729,6 @@ pub fn vreinterpret_u64_s64(a: int64x1_t) -> uint64x1_t { #[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_s64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -48220,34 +44748,8 @@ pub fn vreinterpret_p8_s64(a: int64x1_t) -> poly8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p8_s64(a: int64x1_t) -> poly8x8_t { - unsafe { - let ret_val: poly8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_s64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -48267,34 +44769,8 @@ pub fn vreinterpret_p16_s64(a: int64x1_t) -> poly16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p16_s64(a: int64x1_t) -> poly16x4_t { - unsafe { - let ret_val: poly16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_s64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -48314,35 +44790,8 @@ pub fn vreinterpretq_f32_s64(a: int64x2_t) -> float32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_f32_s64(a: int64x2_t) -> float32x4_t { - let a: int64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: float32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_s64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -48362,39 +44811,8 @@ pub fn vreinterpretq_s8_s64(a: int64x2_t) -> int8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s8_s64(a: int64x2_t) -> int8x16_t { - let a: int64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_s64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -48414,35 +44832,8 @@ pub fn vreinterpretq_s16_s64(a: int64x2_t) -> int16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s16_s64(a: int64x2_t) -> int16x8_t { - let a: int64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_s64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -48462,35 +44853,8 @@ pub fn vreinterpretq_s32_s64(a: int64x2_t) -> int32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s32_s64(a: int64x2_t) -> int32x4_t { - let a: int64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_s64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -48510,39 +44874,8 @@ pub fn vreinterpretq_u8_s64(a: int64x2_t) -> uint8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u8_s64(a: int64x2_t) -> uint8x16_t { - let a: int64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_s64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -48562,35 +44895,8 @@ pub fn vreinterpretq_u16_s64(a: int64x2_t) -> uint16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u16_s64(a: int64x2_t) -> uint16x8_t { - let a: int64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_s64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -48610,35 +44916,8 @@ pub fn vreinterpretq_u32_s64(a: int64x2_t) -> uint32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u32_s64(a: int64x2_t) -> uint32x4_t { - let a: int64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_s64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -48658,35 +44937,8 @@ pub fn vreinterpretq_u64_s64(a: int64x2_t) -> uint64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u64_s64(a: int64x2_t) -> uint64x2_t { - let a: int64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_s64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -48706,39 +44958,8 @@ pub fn vreinterpretq_p8_s64(a: int64x2_t) -> poly8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p8_s64(a: int64x2_t) -> poly8x16_t { - let a: int64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: poly8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_s64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -48758,35 +44979,8 @@ pub fn vreinterpretq_p16_s64(a: int64x2_t) -> poly16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p16_s64(a: int64x2_t) -> poly16x8_t { - let a: int64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: poly16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -48806,35 +45000,8 @@ pub fn vreinterpret_f32_u8(a: uint8x8_t) -> float32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_f32_u8(a: uint8x8_t) -> float32x2_t { - let a: uint8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -48854,35 +45021,8 @@ pub fn vreinterpret_s8_u8(a: uint8x8_t) -> int8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s8_u8(a: uint8x8_t) -> int8x8_t { - let a: uint8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -48902,35 +45042,8 @@ pub fn vreinterpret_s16_u8(a: uint8x8_t) -> int16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s16_u8(a: uint8x8_t) -> int16x4_t { - let a: uint8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -48950,35 +45063,8 @@ pub fn vreinterpret_s32_u8(a: uint8x8_t) -> int32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s32_u8(a: uint8x8_t) -> int32x2_t { - let a: uint8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -48998,32 +45084,8 @@ pub fn vreinterpret_s64_u8(a: uint8x8_t) -> int64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s64_u8(a: uint8x8_t) -> int64x1_t { - let a: uint8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -49043,35 +45105,8 @@ pub fn vreinterpret_u16_u8(a: uint8x8_t) -> uint16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u16_u8(a: uint8x8_t) -> uint16x4_t { - let a: uint8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -49091,35 +45126,8 @@ pub fn vreinterpret_u32_u8(a: uint8x8_t) -> uint32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u32_u8(a: uint8x8_t) -> uint32x2_t { - let a: uint8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -49139,32 +45147,8 @@ pub fn vreinterpret_u64_u8(a: uint8x8_t) -> uint64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u64_u8(a: uint8x8_t) -> uint64x1_t { - let a: uint8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -49184,35 +45168,8 @@ pub fn vreinterpret_p8_u8(a: uint8x8_t) -> poly8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p8_u8(a: uint8x8_t) -> poly8x8_t { - let a: uint8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -49232,35 +45189,8 @@ pub fn vreinterpret_p16_u8(a: uint8x8_t) -> poly16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p16_u8(a: uint8x8_t) -> poly16x4_t { - let a: uint8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -49280,36 +45210,8 @@ pub fn vreinterpretq_f32_u8(a: uint8x16_t) -> float32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_f32_u8(a: uint8x16_t) -> float32x4_t { - let a: uint8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -49329,40 +45231,8 @@ pub fn vreinterpretq_s8_u8(a: uint8x16_t) -> int8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s8_u8(a: uint8x16_t) -> int8x16_t { - let a: uint8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -49382,36 +45252,8 @@ pub fn vreinterpretq_s16_u8(a: uint8x16_t) -> int16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s16_u8(a: uint8x16_t) -> int16x8_t { - let a: uint8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -49431,36 +45273,8 @@ pub fn vreinterpretq_s32_u8(a: uint8x16_t) -> int32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s32_u8(a: uint8x16_t) -> int32x4_t { - let a: uint8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -49480,36 +45294,8 @@ pub fn vreinterpretq_s64_u8(a: uint8x16_t) -> int64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s64_u8(a: uint8x16_t) -> int64x2_t { - let a: uint8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -49529,36 +45315,8 @@ pub fn vreinterpretq_u16_u8(a: uint8x16_t) -> uint16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u16_u8(a: uint8x16_t) -> uint16x8_t { - let a: uint8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -49578,36 +45336,8 @@ pub fn vreinterpretq_u32_u8(a: uint8x16_t) -> uint32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u32_u8(a: uint8x16_t) -> uint32x4_t { - let a: uint8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -49627,36 +45357,8 @@ pub fn vreinterpretq_u64_u8(a: uint8x16_t) -> uint64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u64_u8(a: uint8x16_t) -> uint64x2_t { - let a: uint8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -49676,40 +45378,8 @@ pub fn vreinterpretq_p8_u8(a: uint8x16_t) -> poly8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p8_u8(a: uint8x16_t) -> poly8x16_t { - let a: uint8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -49729,36 +45399,8 @@ pub fn vreinterpretq_p16_u8(a: uint8x16_t) -> poly16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p16_u8(a: uint8x16_t) -> poly16x8_t { - let a: uint8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -49778,35 +45420,8 @@ pub fn vreinterpret_f32_u16(a: uint16x4_t) -> float32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_f32_u16(a: uint16x4_t) -> float32x2_t { - let a: uint16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: float32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -49826,35 +45441,8 @@ pub fn vreinterpret_s8_u16(a: uint16x4_t) -> int8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s8_u16(a: uint16x4_t) -> int8x8_t { - let a: uint16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -49874,35 +45462,8 @@ pub fn vreinterpret_s16_u16(a: uint16x4_t) -> int16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s16_u16(a: uint16x4_t) -> int16x4_t { - let a: uint16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -49922,35 +45483,8 @@ pub fn vreinterpret_s32_u16(a: uint16x4_t) -> int32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s32_u16(a: uint16x4_t) -> int32x2_t { - let a: uint16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -49970,32 +45504,8 @@ pub fn vreinterpret_s64_u16(a: uint16x4_t) -> int64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s64_u16(a: uint16x4_t) -> int64x1_t { - let a: uint16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50015,35 +45525,8 @@ pub fn vreinterpret_u8_u16(a: uint16x4_t) -> uint8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u8_u16(a: uint16x4_t) -> uint8x8_t { - let a: uint16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50063,35 +45546,8 @@ pub fn vreinterpret_u32_u16(a: uint16x4_t) -> uint32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u32_u16(a: uint16x4_t) -> uint32x2_t { - let a: uint16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50111,32 +45567,8 @@ pub fn vreinterpret_u64_u16(a: uint16x4_t) -> uint64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u64_u16(a: uint16x4_t) -> uint64x1_t { - let a: uint16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50156,35 +45588,8 @@ pub fn vreinterpret_p8_u16(a: uint16x4_t) -> poly8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p8_u16(a: uint16x4_t) -> poly8x8_t { - let a: uint16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: poly8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50204,35 +45609,8 @@ pub fn vreinterpret_p16_u16(a: uint16x4_t) -> poly16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p16_u16(a: uint16x4_t) -> poly16x4_t { - let a: uint16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: poly16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50252,35 +45630,8 @@ pub fn vreinterpretq_f32_u16(a: uint16x8_t) -> float32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_f32_u16(a: uint16x8_t) -> float32x4_t { - let a: uint16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50300,39 +45651,8 @@ pub fn vreinterpretq_s8_u16(a: uint16x8_t) -> int8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s8_u16(a: uint16x8_t) -> int8x16_t { - let a: uint16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50352,35 +45672,8 @@ pub fn vreinterpretq_s16_u16(a: uint16x8_t) -> int16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s16_u16(a: uint16x8_t) -> int16x8_t { - let a: uint16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50400,35 +45693,8 @@ pub fn vreinterpretq_s32_u16(a: uint16x8_t) -> int32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s32_u16(a: uint16x8_t) -> int32x4_t { - let a: uint16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50448,35 +45714,8 @@ pub fn vreinterpretq_s64_u16(a: uint16x8_t) -> int64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s64_u16(a: uint16x8_t) -> int64x2_t { - let a: uint16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50496,39 +45735,8 @@ pub fn vreinterpretq_u8_u16(a: uint16x8_t) -> uint8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u8_u16(a: uint16x8_t) -> uint8x16_t { - let a: uint16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50548,35 +45756,8 @@ pub fn vreinterpretq_u32_u16(a: uint16x8_t) -> uint32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u32_u16(a: uint16x8_t) -> uint32x4_t { - let a: uint16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50596,35 +45777,8 @@ pub fn vreinterpretq_u64_u16(a: uint16x8_t) -> uint64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u64_u16(a: uint16x8_t) -> uint64x2_t { - let a: uint16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50644,39 +45798,8 @@ pub fn vreinterpretq_p8_u16(a: uint16x8_t) -> poly8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p8_u16(a: uint16x8_t) -> poly8x16_t { - let a: uint16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50696,35 +45819,8 @@ pub fn vreinterpretq_p16_u16(a: uint16x8_t) -> poly16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p16_u16(a: uint16x8_t) -> poly16x8_t { - let a: uint16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50744,35 +45840,8 @@ pub fn vreinterpret_f32_u32(a: uint32x2_t) -> float32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_u32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_f32_u32(a: uint32x2_t) -> float32x2_t { - let a: uint32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: float32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50792,35 +45861,8 @@ pub fn vreinterpret_s8_u32(a: uint32x2_t) -> int8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_u32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s8_u32(a: uint32x2_t) -> int8x8_t { - let a: uint32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50840,35 +45882,8 @@ pub fn vreinterpret_s16_u32(a: uint32x2_t) -> int16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_u32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s16_u32(a: uint32x2_t) -> int16x4_t { - let a: uint32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50888,35 +45903,8 @@ pub fn vreinterpret_s32_u32(a: uint32x2_t) -> int32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_u32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s32_u32(a: uint32x2_t) -> int32x2_t { - let a: uint32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50936,32 +45924,8 @@ pub fn vreinterpret_s64_u32(a: uint32x2_t) -> int64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_u32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s64_u32(a: uint32x2_t) -> int64x1_t { - let a: uint32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -50981,35 +45945,8 @@ pub fn vreinterpret_u8_u32(a: uint32x2_t) -> uint8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_u32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u8_u32(a: uint32x2_t) -> uint8x8_t { - let a: uint32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51029,35 +45966,8 @@ pub fn vreinterpret_u16_u32(a: uint32x2_t) -> uint16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_u32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u16_u32(a: uint32x2_t) -> uint16x4_t { - let a: uint32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51077,32 +45987,8 @@ pub fn vreinterpret_u64_u32(a: uint32x2_t) -> uint64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_u32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u64_u32(a: uint32x2_t) -> uint64x1_t { - let a: uint32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51122,35 +46008,8 @@ pub fn vreinterpret_p8_u32(a: uint32x2_t) -> poly8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_u32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p8_u32(a: uint32x2_t) -> poly8x8_t { - let a: uint32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: poly8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51170,35 +46029,8 @@ pub fn vreinterpret_p16_u32(a: uint32x2_t) -> poly16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_u32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p16_u32(a: uint32x2_t) -> poly16x4_t { - let a: uint32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: poly16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51218,35 +46050,8 @@ pub fn vreinterpretq_f32_u32(a: uint32x4_t) -> float32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_u32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_f32_u32(a: uint32x4_t) -> float32x4_t { - let a: uint32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: float32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51266,39 +46071,8 @@ pub fn vreinterpretq_s8_u32(a: uint32x4_t) -> int8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_u32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s8_u32(a: uint32x4_t) -> int8x16_t { - let a: uint32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51318,35 +46092,8 @@ pub fn vreinterpretq_s16_u32(a: uint32x4_t) -> int16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_u32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s16_u32(a: uint32x4_t) -> int16x8_t { - let a: uint32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51366,35 +46113,8 @@ pub fn vreinterpretq_s32_u32(a: uint32x4_t) -> int32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_u32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s32_u32(a: uint32x4_t) -> int32x4_t { - let a: uint32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51414,35 +46134,8 @@ pub fn vreinterpretq_s64_u32(a: uint32x4_t) -> int64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_u32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s64_u32(a: uint32x4_t) -> int64x2_t { - let a: uint32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51462,39 +46155,8 @@ pub fn vreinterpretq_u8_u32(a: uint32x4_t) -> uint8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_u32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u8_u32(a: uint32x4_t) -> uint8x16_t { - let a: uint32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51514,35 +46176,8 @@ pub fn vreinterpretq_u16_u32(a: uint32x4_t) -> uint16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_u32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u16_u32(a: uint32x4_t) -> uint16x8_t { - let a: uint32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51562,35 +46197,8 @@ pub fn vreinterpretq_u64_u32(a: uint32x4_t) -> uint64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_u32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u64_u32(a: uint32x4_t) -> uint64x2_t { - let a: uint32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51610,39 +46218,8 @@ pub fn vreinterpretq_p8_u32(a: uint32x4_t) -> poly8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_u32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p8_u32(a: uint32x4_t) -> poly8x16_t { - let a: uint32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: poly8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51662,35 +46239,8 @@ pub fn vreinterpretq_p16_u32(a: uint32x4_t) -> poly16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_u32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p16_u32(a: uint32x4_t) -> poly16x8_t { - let a: uint32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: poly16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_u64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51710,34 +46260,8 @@ pub fn vreinterpret_f32_u64(a: uint64x1_t) -> float32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_u64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_f32_u64(a: uint64x1_t) -> float32x2_t { - unsafe { - let ret_val: float32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_u64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51757,34 +46281,8 @@ pub fn vreinterpret_s8_u64(a: uint64x1_t) -> int8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_u64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s8_u64(a: uint64x1_t) -> int8x8_t { - unsafe { - let ret_val: int8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_u64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51804,34 +46302,8 @@ pub fn vreinterpret_s16_u64(a: uint64x1_t) -> int16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_u64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s16_u64(a: uint64x1_t) -> int16x4_t { - unsafe { - let ret_val: int16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_u64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51851,31 +46323,6 @@ pub fn vreinterpret_s32_u64(a: uint64x1_t) -> int32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_u64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s32_u64(a: uint64x1_t) -> int32x2_t { - unsafe { - let ret_val: int32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_u64)"] #[inline] #[target_feature(enable = "neon")] @@ -51899,7 +46346,6 @@ pub fn vreinterpret_s64_u64(a: uint64x1_t) -> int64x1_t { #[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_u64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51919,34 +46365,8 @@ pub fn vreinterpret_u8_u64(a: uint64x1_t) -> uint8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_u64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u8_u64(a: uint64x1_t) -> uint8x8_t { - unsafe { - let ret_val: uint8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_u64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -51966,34 +46386,8 @@ pub fn vreinterpret_u16_u64(a: uint64x1_t) -> uint16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_u64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u16_u64(a: uint64x1_t) -> uint16x4_t { - unsafe { - let ret_val: uint16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_u64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52013,34 +46407,8 @@ pub fn vreinterpret_u32_u64(a: uint64x1_t) -> uint32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_u64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u32_u64(a: uint64x1_t) -> uint32x2_t { - unsafe { - let ret_val: uint32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_u64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52060,34 +46428,8 @@ pub fn vreinterpret_p8_u64(a: uint64x1_t) -> poly8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_u64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p8_u64(a: uint64x1_t) -> poly8x8_t { - unsafe { - let ret_val: poly8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_u64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52107,34 +46449,8 @@ pub fn vreinterpret_p16_u64(a: uint64x1_t) -> poly16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_u64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p16_u64(a: uint64x1_t) -> poly16x4_t { - unsafe { - let ret_val: poly16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_u64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52154,35 +46470,8 @@ pub fn vreinterpretq_f32_u64(a: uint64x2_t) -> float32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_u64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_f32_u64(a: uint64x2_t) -> float32x4_t { - let a: uint64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: float32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_u64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52202,39 +46491,8 @@ pub fn vreinterpretq_s8_u64(a: uint64x2_t) -> int8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_u64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s8_u64(a: uint64x2_t) -> int8x16_t { - let a: uint64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_u64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52254,35 +46512,8 @@ pub fn vreinterpretq_s16_u64(a: uint64x2_t) -> int16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_u64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s16_u64(a: uint64x2_t) -> int16x8_t { - let a: uint64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_u64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52302,35 +46533,8 @@ pub fn vreinterpretq_s32_u64(a: uint64x2_t) -> int32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_u64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s32_u64(a: uint64x2_t) -> int32x4_t { - let a: uint64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_u64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52350,35 +46554,8 @@ pub fn vreinterpretq_s64_u64(a: uint64x2_t) -> int64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_u64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s64_u64(a: uint64x2_t) -> int64x2_t { - let a: uint64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_u64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52398,39 +46575,8 @@ pub fn vreinterpretq_u8_u64(a: uint64x2_t) -> uint8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_u64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u8_u64(a: uint64x2_t) -> uint8x16_t { - let a: uint64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_u64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52450,35 +46596,8 @@ pub fn vreinterpretq_u16_u64(a: uint64x2_t) -> uint16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_u64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u16_u64(a: uint64x2_t) -> uint16x8_t { - let a: uint64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_u64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52498,35 +46617,8 @@ pub fn vreinterpretq_u32_u64(a: uint64x2_t) -> uint32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_u64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u32_u64(a: uint64x2_t) -> uint32x4_t { - let a: uint64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_u64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52546,39 +46638,8 @@ pub fn vreinterpretq_p8_u64(a: uint64x2_t) -> poly8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_u64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p8_u64(a: uint64x2_t) -> poly8x16_t { - let a: uint64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: poly8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_u64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52598,35 +46659,8 @@ pub fn vreinterpretq_p16_u64(a: uint64x2_t) -> poly16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_u64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p16_u64(a: uint64x2_t) -> poly16x8_t { - let a: uint64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: poly16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_p8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52646,35 +46680,8 @@ pub fn vreinterpret_f32_p8(a: poly8x8_t) -> float32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_p8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_f32_p8(a: poly8x8_t) -> float32x2_t { - let a: poly8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_p8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52694,35 +46701,8 @@ pub fn vreinterpret_s8_p8(a: poly8x8_t) -> int8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_p8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s8_p8(a: poly8x8_t) -> int8x8_t { - let a: poly8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_p8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52742,35 +46722,8 @@ pub fn vreinterpret_s16_p8(a: poly8x8_t) -> int16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_p8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s16_p8(a: poly8x8_t) -> int16x4_t { - let a: poly8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_p8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52790,35 +46743,8 @@ pub fn vreinterpret_s32_p8(a: poly8x8_t) -> int32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_p8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s32_p8(a: poly8x8_t) -> int32x2_t { - let a: poly8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_p8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52838,32 +46764,8 @@ pub fn vreinterpret_s64_p8(a: poly8x8_t) -> int64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_p8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s64_p8(a: poly8x8_t) -> int64x1_t { - let a: poly8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_p8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52883,35 +46785,8 @@ pub fn vreinterpret_u8_p8(a: poly8x8_t) -> uint8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_p8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u8_p8(a: poly8x8_t) -> uint8x8_t { - let a: poly8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_p8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52931,35 +46806,8 @@ pub fn vreinterpret_u16_p8(a: poly8x8_t) -> uint16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_p8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u16_p8(a: poly8x8_t) -> uint16x4_t { - let a: poly8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_p8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -52979,35 +46827,8 @@ pub fn vreinterpret_u32_p8(a: poly8x8_t) -> uint32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_p8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u32_p8(a: poly8x8_t) -> uint32x2_t { - let a: poly8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_p8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53027,32 +46848,8 @@ pub fn vreinterpret_u64_p8(a: poly8x8_t) -> uint64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_p8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u64_p8(a: poly8x8_t) -> uint64x1_t { - let a: poly8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_p8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53072,35 +46869,8 @@ pub fn vreinterpret_p16_p8(a: poly8x8_t) -> poly16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_p8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p16_p8(a: poly8x8_t) -> poly16x4_t { - let a: poly8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_p8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53120,36 +46890,8 @@ pub fn vreinterpretq_f32_p8(a: poly8x16_t) -> float32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_p8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_f32_p8(a: poly8x16_t) -> float32x4_t { - let a: poly8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_p8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53169,40 +46911,8 @@ pub fn vreinterpretq_s8_p8(a: poly8x16_t) -> int8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_p8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s8_p8(a: poly8x16_t) -> int8x16_t { - let a: poly8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_p8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53222,36 +46932,8 @@ pub fn vreinterpretq_s16_p8(a: poly8x16_t) -> int16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_p8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s16_p8(a: poly8x16_t) -> int16x8_t { - let a: poly8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_p8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53271,36 +46953,8 @@ pub fn vreinterpretq_s32_p8(a: poly8x16_t) -> int32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_p8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s32_p8(a: poly8x16_t) -> int32x4_t { - let a: poly8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_p8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53320,36 +46974,8 @@ pub fn vreinterpretq_s64_p8(a: poly8x16_t) -> int64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_p8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s64_p8(a: poly8x16_t) -> int64x2_t { - let a: poly8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_p8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53369,40 +46995,8 @@ pub fn vreinterpretq_u8_p8(a: poly8x16_t) -> uint8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_p8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u8_p8(a: poly8x16_t) -> uint8x16_t { - let a: poly8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_p8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53422,36 +47016,8 @@ pub fn vreinterpretq_u16_p8(a: poly8x16_t) -> uint16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_p8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u16_p8(a: poly8x16_t) -> uint16x8_t { - let a: poly8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_p8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53471,36 +47037,8 @@ pub fn vreinterpretq_u32_p8(a: poly8x16_t) -> uint32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_p8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u32_p8(a: poly8x16_t) -> uint32x4_t { - let a: poly8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_p8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53520,36 +47058,8 @@ pub fn vreinterpretq_u64_p8(a: poly8x16_t) -> uint64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_p8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u64_p8(a: poly8x16_t) -> uint64x2_t { - let a: poly8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_p8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53569,36 +47079,8 @@ pub fn vreinterpretq_p16_p8(a: poly8x16_t) -> poly16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_p8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p16_p8(a: poly8x16_t) -> poly16x8_t { - let a: poly8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_p16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53618,35 +47100,8 @@ pub fn vreinterpret_f32_p16(a: poly16x4_t) -> float32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_f32_p16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_f32_p16(a: poly16x4_t) -> float32x2_t { - let a: poly16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: float32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_p16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53666,35 +47121,8 @@ pub fn vreinterpret_s8_p16(a: poly16x4_t) -> int8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_p16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s8_p16(a: poly16x4_t) -> int8x8_t { - let a: poly16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_p16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53714,35 +47142,8 @@ pub fn vreinterpret_s16_p16(a: poly16x4_t) -> int16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_p16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s16_p16(a: poly16x4_t) -> int16x4_t { - let a: poly16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_p16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53762,35 +47163,8 @@ pub fn vreinterpret_s32_p16(a: poly16x4_t) -> int32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_p16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s32_p16(a: poly16x4_t) -> int32x2_t { - let a: poly16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: int32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_p16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53810,32 +47184,8 @@ pub fn vreinterpret_s64_p16(a: poly16x4_t) -> int64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s64_p16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s64_p16(a: poly16x4_t) -> int64x1_t { - let a: poly16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_p16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53855,35 +47205,8 @@ pub fn vreinterpret_u8_p16(a: poly16x4_t) -> uint8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_p16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u8_p16(a: poly16x4_t) -> uint8x8_t { - let a: poly16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_p16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53903,35 +47226,8 @@ pub fn vreinterpret_u16_p16(a: poly16x4_t) -> uint16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_p16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u16_p16(a: poly16x4_t) -> uint16x4_t { - let a: poly16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_p16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53951,35 +47247,8 @@ pub fn vreinterpret_u32_p16(a: poly16x4_t) -> uint32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_p16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u32_p16(a: poly16x4_t) -> uint32x2_t { - let a: poly16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: uint32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_p16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -53999,32 +47268,8 @@ pub fn vreinterpret_u64_p16(a: poly16x4_t) -> uint64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u64_p16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u64_p16(a: poly16x4_t) -> uint64x1_t { - let a: poly16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_p16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54044,35 +47289,8 @@ pub fn vreinterpret_p8_p16(a: poly16x4_t) -> poly8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_p16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p8_p16(a: poly16x4_t) -> poly8x8_t { - let a: poly16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: poly8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_p16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54092,35 +47310,8 @@ pub fn vreinterpretq_f32_p16(a: poly16x8_t) -> float32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_f32_p16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_f32_p16(a: poly16x8_t) -> float32x4_t { - let a: poly16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: float32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_p16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54140,39 +47331,8 @@ pub fn vreinterpretq_s8_p16(a: poly16x8_t) -> int8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_p16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s8_p16(a: poly16x8_t) -> int8x16_t { - let a: poly16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_p16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54192,35 +47352,8 @@ pub fn vreinterpretq_s16_p16(a: poly16x8_t) -> int16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_p16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s16_p16(a: poly16x8_t) -> int16x8_t { - let a: poly16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_p16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54240,35 +47373,8 @@ pub fn vreinterpretq_s32_p16(a: poly16x8_t) -> int32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_p16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s32_p16(a: poly16x8_t) -> int32x4_t { - let a: poly16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_p16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54288,35 +47394,8 @@ pub fn vreinterpretq_s64_p16(a: poly16x8_t) -> int64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_p16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s64_p16(a: poly16x8_t) -> int64x2_t { - let a: poly16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: int64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_p16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54336,39 +47415,8 @@ pub fn vreinterpretq_u8_p16(a: poly16x8_t) -> uint8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_p16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u8_p16(a: poly16x8_t) -> uint8x16_t { - let a: poly16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_p16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54388,35 +47436,8 @@ pub fn vreinterpretq_u16_p16(a: poly16x8_t) -> uint16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_p16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u16_p16(a: poly16x8_t) -> uint16x8_t { - let a: poly16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_p16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54436,35 +47457,8 @@ pub fn vreinterpretq_u32_p16(a: poly16x8_t) -> uint32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_p16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u32_p16(a: poly16x8_t) -> uint32x4_t { - let a: poly16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_p16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54484,35 +47478,8 @@ pub fn vreinterpretq_u64_p16(a: poly16x8_t) -> uint64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_p16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u64_p16(a: poly16x8_t) -> uint64x2_t { - let a: poly16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: uint64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_p16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54532,39 +47499,8 @@ pub fn vreinterpretq_p8_p16(a: poly16x8_t) -> poly8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_p16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p8_p16(a: poly16x8_t) -> poly8x16_t { - let a: poly16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_p128)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54584,38 +47520,8 @@ pub fn vreinterpretq_s8_p128(a: p128) -> int8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_p128)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s8_p128(a: p128) -> int8x16_t { - unsafe { - let ret_val: int8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_p128)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54635,34 +47541,8 @@ pub fn vreinterpretq_s16_p128(a: p128) -> int16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_p128)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s16_p128(a: p128) -> int16x8_t { - unsafe { - let ret_val: int16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_p128)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54682,34 +47562,8 @@ pub fn vreinterpretq_s32_p128(a: p128) -> int32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_p128)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s32_p128(a: p128) -> int32x4_t { - unsafe { - let ret_val: int32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_p128)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54729,34 +47583,8 @@ pub fn vreinterpretq_s64_p128(a: p128) -> int64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s64_p128)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s64_p128(a: p128) -> int64x2_t { - unsafe { - let ret_val: int64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_p128)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54776,38 +47604,8 @@ pub fn vreinterpretq_u8_p128(a: p128) -> uint8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_p128)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u8_p128(a: p128) -> uint8x16_t { - unsafe { - let ret_val: uint8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_p128)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54827,34 +47625,8 @@ pub fn vreinterpretq_u16_p128(a: p128) -> uint16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_p128)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u16_p128(a: p128) -> uint16x8_t { - unsafe { - let ret_val: uint16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_p128)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54874,34 +47646,8 @@ pub fn vreinterpretq_u32_p128(a: p128) -> uint32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_p128)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u32_p128(a: p128) -> uint32x4_t { - unsafe { - let ret_val: uint32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_p128)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54921,34 +47667,8 @@ pub fn vreinterpretq_u64_p128(a: p128) -> uint64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u64_p128)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u64_p128(a: p128) -> uint64x2_t { - unsafe { - let ret_val: uint64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_p128)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -54968,38 +47688,8 @@ pub fn vreinterpretq_p8_p128(a: p128) -> poly8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_p128)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p8_p128(a: p128) -> poly8x16_t { - unsafe { - let ret_val: poly8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_p128)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -55019,34 +47709,8 @@ pub fn vreinterpretq_p16_p128(a: p128) -> poly16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_p128)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p16_p128(a: p128) -> poly16x8_t { - unsafe { - let ret_val: poly16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_p128)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -55066,34 +47730,8 @@ pub fn vreinterpretq_p64_p128(a: p128) -> poly64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_p128)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p64_p128(a: p128) -> poly64x2_t { - unsafe { - let ret_val: poly64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -55113,32 +47751,8 @@ pub fn vreinterpret_p64_s8(a: int8x8_t) -> poly64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p64_s8(a: int8x8_t) -> poly64x1_t { - let a: int8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -55158,33 +47772,8 @@ pub fn vreinterpretq_p128_s8(a: int8x16_t) -> p128 { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p128_s8(a: int8x16_t) -> p128 { - let a: int8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_s8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -55204,36 +47793,8 @@ pub fn vreinterpretq_p64_s8(a: int8x16_t) -> poly64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_s8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p64_s8(a: int8x16_t) -> poly64x2_t { - let a: int8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -55253,32 +47814,8 @@ pub fn vreinterpret_p64_s16(a: int16x4_t) -> poly64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p64_s16(a: int16x4_t) -> poly64x1_t { - let a: int16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -55298,32 +47835,8 @@ pub fn vreinterpretq_p128_s16(a: int16x8_t) -> p128 { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p128_s16(a: int16x8_t) -> p128 { - let a: int16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_s16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -55343,35 +47856,8 @@ pub fn vreinterpretq_p64_s16(a: int16x8_t) -> poly64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_s16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p64_s16(a: int16x8_t) -> poly64x2_t { - let a: int16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -55391,32 +47877,8 @@ pub fn vreinterpret_p64_s32(a: int32x2_t) -> poly64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p64_s32(a: int32x2_t) -> poly64x1_t { - let a: int32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -55436,32 +47898,8 @@ pub fn vreinterpretq_p128_s32(a: int32x4_t) -> p128 { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p128_s32(a: int32x4_t) -> p128 { - let a: int32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_s32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -55481,35 +47919,8 @@ pub fn vreinterpretq_p64_s32(a: int32x4_t) -> poly64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_s32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p64_s32(a: int32x4_t) -> poly64x2_t { - let a: int32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: poly64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_s64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -55529,32 +47940,8 @@ pub fn vreinterpretq_p128_s64(a: int64x2_t) -> p128 { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_s64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p128_s64(a: int64x2_t) -> p128 { - let a: int64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -55574,32 +47961,8 @@ pub fn vreinterpret_p64_u8(a: uint8x8_t) -> poly64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p64_u8(a: uint8x8_t) -> poly64x1_t { - let a: uint8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -55619,33 +47982,8 @@ pub fn vreinterpretq_p128_u8(a: uint8x16_t) -> p128 { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p128_u8(a: uint8x16_t) -> p128 { - let a: uint8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_u8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -55665,36 +48003,8 @@ pub fn vreinterpretq_p64_u8(a: uint8x16_t) -> poly64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_u8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p64_u8(a: uint8x16_t) -> poly64x2_t { - let a: uint8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -55714,32 +48024,8 @@ pub fn vreinterpret_p64_u16(a: uint16x4_t) -> poly64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p64_u16(a: uint16x4_t) -> poly64x1_t { - let a: uint16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -55759,32 +48045,8 @@ pub fn vreinterpretq_p128_u16(a: uint16x8_t) -> p128 { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p128_u16(a: uint16x8_t) -> p128 { - let a: uint16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_u16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -55804,35 +48066,8 @@ pub fn vreinterpretq_p64_u16(a: uint16x8_t) -> poly64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_u16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p64_u16(a: uint16x8_t) -> poly64x2_t { - let a: uint16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -55852,32 +48087,8 @@ pub fn vreinterpret_p64_u32(a: uint32x2_t) -> poly64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_u32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p64_u32(a: uint32x2_t) -> poly64x1_t { - let a: uint32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -55897,32 +48108,8 @@ pub fn vreinterpretq_p128_u32(a: uint32x4_t) -> p128 { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_u32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p128_u32(a: uint32x4_t) -> p128 { - let a: uint32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_u32)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -55942,35 +48129,8 @@ pub fn vreinterpretq_p64_u32(a: uint32x4_t) -> poly64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_u32)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p64_u32(a: uint32x4_t) -> poly64x2_t { - let a: uint32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { - let ret_val: poly64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_u64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -55990,32 +48150,8 @@ pub fn vreinterpretq_p128_u64(a: uint64x2_t) -> p128 { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_u64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p128_u64(a: uint64x2_t) -> p128 { - let a: uint64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_p8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56035,32 +48171,8 @@ pub fn vreinterpret_p64_p8(a: poly8x8_t) -> poly64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_p8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p64_p8(a: poly8x8_t) -> poly64x1_t { - let a: poly8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_p8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56080,33 +48192,8 @@ pub fn vreinterpretq_p128_p8(a: poly8x16_t) -> p128 { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_p8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p128_p8(a: poly8x16_t) -> p128 { - let a: poly8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_p8)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56126,36 +48213,8 @@ pub fn vreinterpretq_p64_p8(a: poly8x16_t) -> poly64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_p8)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p64_p8(a: poly8x16_t) -> poly64x2_t { - let a: poly8x16_t = - unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_p16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56175,32 +48234,8 @@ pub fn vreinterpret_p64_p16(a: poly16x4_t) -> poly64x1_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p64_p16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p64_p16(a: poly16x4_t) -> poly64x1_t { - let a: poly16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_p16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56220,32 +48255,8 @@ pub fn vreinterpretq_p128_p16(a: poly16x8_t) -> p128 { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_p16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p128_p16(a: poly16x8_t) -> p128 { - let a: poly16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_p16)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56265,35 +48276,8 @@ pub fn vreinterpretq_p64_p16(a: poly16x8_t) -> poly64x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p64_p16)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p64_p16(a: poly16x8_t) -> poly64x2_t { - let a: poly16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; - unsafe { - let ret_val: poly64x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_p64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56313,34 +48297,8 @@ pub fn vreinterpret_s8_p64(a: poly64x1_t) -> int8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s8_p64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s8_p64(a: poly64x1_t) -> int8x8_t { - unsafe { - let ret_val: int8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_p64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56360,34 +48318,8 @@ pub fn vreinterpret_s16_p64(a: poly64x1_t) -> int16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s16_p64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s16_p64(a: poly64x1_t) -> int16x4_t { - unsafe { - let ret_val: int16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_p64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56407,34 +48339,8 @@ pub fn vreinterpret_s32_p64(a: poly64x1_t) -> int32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_s32_p64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_s32_p64(a: poly64x1_t) -> int32x2_t { - unsafe { - let ret_val: int32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_p64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56454,34 +48360,8 @@ pub fn vreinterpret_u8_p64(a: poly64x1_t) -> uint8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u8_p64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u8_p64(a: poly64x1_t) -> uint8x8_t { - unsafe { - let ret_val: uint8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_p64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56501,34 +48381,8 @@ pub fn vreinterpret_u16_p64(a: poly64x1_t) -> uint16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u16_p64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u16_p64(a: poly64x1_t) -> uint16x4_t { - unsafe { - let ret_val: uint16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_p64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56548,34 +48402,8 @@ pub fn vreinterpret_u32_p64(a: poly64x1_t) -> uint32x2_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_u32_p64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_u32_p64(a: poly64x1_t) -> uint32x2_t { - unsafe { - let ret_val: uint32x2_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_p64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56595,34 +48423,8 @@ pub fn vreinterpret_p8_p64(a: poly64x1_t) -> poly8x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p8_p64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p8_p64(a: poly64x1_t) -> poly8x8_t { - unsafe { - let ret_val: poly8x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_p64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56642,34 +48444,8 @@ pub fn vreinterpret_p16_p64(a: poly64x1_t) -> poly16x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpret_p16_p64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpret_p16_p64(a: poly64x1_t) -> poly16x4_t { - unsafe { - let ret_val: poly16x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_p64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56689,32 +48465,8 @@ pub fn vreinterpretq_p128_p64(a: poly64x2_t) -> p128 { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p128_p64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p128_p64(a: poly64x2_t) -> p128 { - let a: poly64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { transmute(a) } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_p64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56734,39 +48486,8 @@ pub fn vreinterpretq_s8_p64(a: poly64x2_t) -> int8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s8_p64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s8_p64(a: poly64x2_t) -> int8x16_t { - let a: poly64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_p64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56786,35 +48507,8 @@ pub fn vreinterpretq_s16_p64(a: poly64x2_t) -> int16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s16_p64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s16_p64(a: poly64x2_t) -> int16x8_t { - let a: poly64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_p64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56834,35 +48528,8 @@ pub fn vreinterpretq_s32_p64(a: poly64x2_t) -> int32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_s32_p64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_s32_p64(a: poly64x2_t) -> int32x4_t { - let a: poly64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: int32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_p64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56882,39 +48549,8 @@ pub fn vreinterpretq_u8_p64(a: poly64x2_t) -> uint8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u8_p64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u8_p64(a: poly64x2_t) -> uint8x16_t { - let a: poly64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_p64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56934,35 +48570,8 @@ pub fn vreinterpretq_u16_p64(a: poly64x2_t) -> uint16x8_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u16_p64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u16_p64(a: poly64x2_t) -> uint16x8_t { - let a: poly64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_p64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -56982,35 +48591,8 @@ pub fn vreinterpretq_u32_p64(a: poly64x2_t) -> uint32x4_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_u32_p64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_u32_p64(a: poly64x2_t) -> uint32x4_t { - let a: poly64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: uint32x4_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [3, 2, 1, 0]) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_p64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -57030,39 +48612,8 @@ pub fn vreinterpretq_p8_p64(a: poly64x2_t) -> poly8x16_t { unsafe { transmute(a) } } #[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p8_p64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p8_p64(a: poly64x2_t) -> poly8x16_t { - let a: poly64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: poly8x16_t = transmute(a); - simd_shuffle!( - ret_val, - ret_val, - [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] - ) - } -} -#[doc = "Vector reinterpret cast operation"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_p64)"] #[inline] -#[cfg(target_endian = "little")] #[target_feature(enable = "neon,aes")] #[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] @@ -57081,32 +48632,6 @@ pub fn vreinterpretq_p8_p64(a: poly64x2_t) -> poly8x16_t { pub fn vreinterpretq_p16_p64(a: poly64x2_t) -> poly16x8_t { unsafe { transmute(a) } } -#[doc = "Vector reinterpret cast operation"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vreinterpretq_p16_p64)"] -#[inline] -#[cfg(target_endian = "big")] -#[target_feature(enable = "neon,aes")] -#[cfg_attr(target_arch = "arm", target_feature(enable = "v8"))] -#[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] -#[cfg_attr( - all(test, any(target_arch = "aarch64", target_arch = "arm64ec")), - assert_instr(nop) -)] -#[cfg_attr( - not(target_arch = "arm"), - stable(feature = "neon_intrinsics", since = "1.59.0") -)] -#[cfg_attr( - target_arch = "arm", - unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") -)] -pub fn vreinterpretq_p16_p64(a: poly64x2_t) -> poly16x8_t { - let a: poly64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; - unsafe { - let ret_val: poly16x8_t = transmute(a); - simd_shuffle!(ret_val, ret_val, [7, 6, 5, 4, 3, 2, 1, 0]) - } -} #[doc = "Reversing vector elements (swap endianness)"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vrev16_p8)"] #[inline] diff --git a/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml b/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml index a1a837bc6106..ccdcea980e1b 100644 --- a/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml +++ b/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml @@ -8781,6 +8781,7 @@ intrinsics: - [float64x1_t, float32x2_t] - [float32x4_t, float64x2_t] - [float64x2_t, float32x4_t] + big_endian_inverse: false compose: - FnCall: [transmute, [a]] @@ -8801,6 +8802,7 @@ intrinsics: # q - [float64x2_t, float16x8_t] - [float16x8_t, float64x2_t] + big_endian_inverse: false compose: - FnCall: [transmute, [a]] diff --git a/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml b/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml index 43dd3b903150..61a3a5853632 100644 --- a/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml +++ b/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml @@ -8480,6 +8480,7 @@ intrinsics: - [poly16x8_t, p128] - [int8x16_t, p128] - [uint8x16_t, p128] + big_endian_inverse: false compose: - FnCall: [transmute, [a]] @@ -8717,6 +8718,7 @@ intrinsics: - [poly8x16_t, float32x4_t] - [poly16x8_t, float32x4_t] - [p128, float32x4_t] + big_endian_inverse: false compose: - FnCall: [transmute, [a]] @@ -8780,6 +8782,7 @@ intrinsics: - [float16x8_t, uint16x8_t] - [float16x8_t, uint32x4_t] - [float16x8_t, uint64x2_t] + big_endian_inverse: false compose: - FnCall: [transmute, [a]] @@ -8804,6 +8807,7 @@ intrinsics: - [poly128_t, float16x8_t] - [float16x8_t, poly128_t] - [float16x8_t, poly64x2_t] + big_endian_inverse: false compose: - FnCall: [transmute, [a]] From 1621ce6a083b33298f83a00aa31633a30775b512 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Thu, 11 Sep 2025 22:04:35 +0100 Subject: [PATCH 407/682] Cleanups from review comments. --- src/doc/rustc/src/platform-support/aarch64-unknown-none.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/doc/rustc/src/platform-support/aarch64-unknown-none.md b/src/doc/rustc/src/platform-support/aarch64-unknown-none.md index f3c0764b5bf7..a9c72d8cc9d8 100644 --- a/src/doc/rustc/src/platform-support/aarch64-unknown-none.md +++ b/src/doc/rustc/src/platform-support/aarch64-unknown-none.md @@ -71,8 +71,8 @@ from the [Arm Developer Website][arm-gnu-toolchain], or possibly from your OS's package manager. To use it, add the following to your `.cargo/config.toml`: ```toml -[target.] -linker = "arm-none-eabi-ld" +[target.aarch64-unknown-none] +linker = "aarch64-none-ld" ``` The GNU linker can also be used by specifying `aarch64-none-gcc` as the @@ -92,8 +92,7 @@ When using the hardfloat targets, the minimum floating-point features assumed are those of the `fp-armv8`, which excludes NEON SIMD support. If your processor supports a different set of floating-point features than the default expectations of `fp-armv8`, then these should also be enabled or disabled as -needed with `-C target-feature=(+/-)`. For example, -`-Ctarget-feature=+neon-fp-armv8`. +needed with `-C target-feature=(+/-)`. [arm-gnu-toolchain]: https://developer.arm.com/Tools%20and%20Software/GNU%20Toolchain From a0d1f5a3fb8b9554cb4ac5d0b8ffc75233a20dda Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Thu, 11 Sep 2025 22:05:24 +0100 Subject: [PATCH 408/682] Insert missing word. --- src/doc/rustc/src/platform-support/aarch64-unknown-none.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc/src/platform-support/aarch64-unknown-none.md b/src/doc/rustc/src/platform-support/aarch64-unknown-none.md index a9c72d8cc9d8..bcdf4271b127 100644 --- a/src/doc/rustc/src/platform-support/aarch64-unknown-none.md +++ b/src/doc/rustc/src/platform-support/aarch64-unknown-none.md @@ -106,8 +106,8 @@ run your code on. ## Start-up and Low-Level Code The [Rust Embedded Devices Working Group Arm Team] maintain the -[`aarch64-cpu`], which may be useful for writing bare-metal code using this -target. +[`aarch64-cpu`] crate, which may be useful for writing bare-metal code using +this target. The *TrustedFirmware* group also maintain [Rust crates for this target](https://github.com/ArmFirmwareCrates). From 321a76b5f95325f20c9e88e87bfb4ddf536b6950 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 11 Sep 2025 23:52:04 +0200 Subject: [PATCH 409/682] c-variadic: test that unsupported c-variadic ABIs are reported correctly --- tests/ui/c-variadic/unsupported-abi.rs | 123 ++++++++ tests/ui/c-variadic/unsupported-abi.stderr | 345 +++++++++++++++++++++ 2 files changed, 468 insertions(+) create mode 100644 tests/ui/c-variadic/unsupported-abi.rs create mode 100644 tests/ui/c-variadic/unsupported-abi.stderr diff --git a/tests/ui/c-variadic/unsupported-abi.rs b/tests/ui/c-variadic/unsupported-abi.rs new file mode 100644 index 000000000000..40179b164ce4 --- /dev/null +++ b/tests/ui/c-variadic/unsupported-abi.rs @@ -0,0 +1,123 @@ +//@ add-core-stubs +//@ needs-llvm-components: x86 +//@ compile-flags: --target=i686-pc-windows-gnu --crate-type=rlib +#![no_core] +#![feature(no_core, lang_items, c_variadic)] + +// Test that ABIs for which C-variadics are not supported report an error. + +extern crate minicore; +use minicore::*; + +#[rustfmt::skip] +mod foreign { + extern "Rust" { fn rust_foreign_explicit(_: ...); } + //~^ ERROR C-variadic functions with the "Rust" calling convention are not supported + extern "C" { fn c_foreign(_: ...); } + extern "C-unwind" { fn c_unwind_foreign(_: ...); } + extern "cdecl" { fn cdecl_foreign(_: ...); } + extern "cdecl-unwind" { fn cdecl_unwind_foreign(_: ...); } + extern "stdcall" { fn stdcall_foreign(_: ...); } + //~^ ERROR C-variadic functions with the "stdcall" calling convention are not supported + extern "stdcall-unwind" { fn stdcall_unwind_foreign(_: ...); } + //~^ ERROR C-variadic functions with the "stdcall-unwind" calling convention are not supported + extern "thiscall" { fn thiscall_foreign(_: ...); } + //~^ ERROR C-variadic functions with the "thiscall" calling convention are not supported + extern "thiscall-unwind" { fn thiscall_unwind_foreign(_: ...); } + //~^ ERROR C-variadic functions with the "thiscall-unwind" calling convention are not supported +} + +#[lang = "va_list"] +struct VaList(*mut u8); + +unsafe fn rust_free(_: ...) {} +//~^ ERROR `...` is not supported for non-extern functions +unsafe extern "Rust" fn rust_free_explicit(_: ...) {} +//~^ ERROR `...` is not supported for `extern "Rust"` functions + +unsafe extern "C" fn c_free(_: ...) {} +unsafe extern "C-unwind" fn c_unwind_free(_: ...) {} + +unsafe extern "cdecl" fn cdecl_free(_: ...) {} +//~^ ERROR `...` is not supported for `extern "cdecl"` functions +unsafe extern "cdecl-unwind" fn cdecl_unwind_free(_: ...) {} +//~^ ERROR `...` is not supported for `extern "cdecl-unwind"` functions +unsafe extern "stdcall" fn stdcall_free(_: ...) {} +//~^ ERROR `...` is not supported for `extern "stdcall"` functions +unsafe extern "stdcall-unwind" fn stdcall_unwind_free(_: ...) {} +//~^ ERROR `...` is not supported for `extern "stdcall-unwind"` functions +unsafe extern "thiscall" fn thiscall_free(_: ...) {} +//~^ ERROR `...` is not supported for `extern "thiscall"` functions +unsafe extern "thiscall-unwind" fn thiscall_unwind_free(_: ...) {} +//~^ ERROR `...` is not supported for `extern "thiscall-unwind"` functions + +struct S; + +impl S { + unsafe fn rust_method(_: ...) {} + //~^ ERROR `...` is not supported for non-extern functions + unsafe extern "Rust" fn rust_method_explicit(_: ...) {} + //~^ ERROR `...` is not supported for `extern "Rust"` functions + + unsafe extern "C" fn c_method(_: ...) {} + unsafe extern "C-unwind" fn c_unwind_method(_: ...) {} + + unsafe extern "cdecl" fn cdecl_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "cdecl"` functions + unsafe extern "cdecl-unwind" fn cdecl_unwind_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "cdecl-unwind"` functions + unsafe extern "stdcall" fn stdcall_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "stdcall"` functions + unsafe extern "stdcall-unwind" fn stdcall_unwind_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "stdcall-unwind"` functions + unsafe extern "thiscall" fn thiscall_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "thiscall"` functions + unsafe extern "thiscall-unwind" fn thiscall_unwind_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "thiscall-unwind"` functions +} + +trait T { + unsafe fn rust_trait_method(_: ...) {} + //~^ ERROR `...` is not supported for non-extern functions + unsafe extern "Rust" fn rust_trait_method_explicit(_: ...) {} + //~^ ERROR `...` is not supported for `extern "Rust"` functions + + unsafe extern "C" fn c_trait_method(_: ...) {} + unsafe extern "C-unwind" fn c_unwind_trait_method(_: ...) {} + + unsafe extern "cdecl" fn cdecl_trait_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "cdecl"` functions + unsafe extern "cdecl-unwind" fn cdecl_unwind_trait_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "cdecl-unwind"` functions + unsafe extern "stdcall" fn stdcall_trait_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "stdcall"` functions + unsafe extern "stdcall-unwind" fn stdcall_unwind_trait_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "stdcall-unwind"` functions + unsafe extern "thiscall" fn thiscall_trait_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "thiscall"` functions + unsafe extern "thiscall-unwind" fn thiscall_unwind_trait_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "thiscall-unwind"` functions +} + +impl T for S { + unsafe fn rust_trait_method(_: ...) {} + //~^ ERROR `...` is not supported for non-extern functions + unsafe extern "Rust" fn rust_trait_method_explicit(_: ...) {} + //~^ ERROR `...` is not supported for `extern "Rust"` functions + + unsafe extern "C" fn c_trait_method(_: ...) {} + unsafe extern "C-unwind" fn c_unwind_trait_method(_: ...) {} + + unsafe extern "cdecl" fn cdecl_trait_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "cdecl"` functions + unsafe extern "cdecl-unwind" fn cdecl_unwind_trait_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "cdecl-unwind"` functions + unsafe extern "stdcall" fn stdcall_trait_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "stdcall"` functions + unsafe extern "stdcall-unwind" fn stdcall_unwind_trait_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "stdcall-unwind"` functions + unsafe extern "thiscall" fn thiscall_trait_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "thiscall"` functions + unsafe extern "thiscall-unwind" fn thiscall_unwind_trait_method(_: ...) {} + //~^ ERROR `...` is not supported for `extern "thiscall-unwind"` functions +} diff --git a/tests/ui/c-variadic/unsupported-abi.stderr b/tests/ui/c-variadic/unsupported-abi.stderr new file mode 100644 index 000000000000..daed91714065 --- /dev/null +++ b/tests/ui/c-variadic/unsupported-abi.stderr @@ -0,0 +1,345 @@ +error: `...` is not supported for non-extern functions + --> $DIR/unsupported-abi.rs:33:21 + | +LL | unsafe fn rust_free(_: ...) {} + | ^^^^^^ + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "Rust"` functions + --> $DIR/unsupported-abi.rs:35:44 + | +LL | unsafe extern "Rust" fn rust_free_explicit(_: ...) {} + | ------------- ^^^^^^ + | | + | `extern "Rust"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "cdecl"` functions + --> $DIR/unsupported-abi.rs:41:37 + | +LL | unsafe extern "cdecl" fn cdecl_free(_: ...) {} + | -------------- ^^^^^^ + | | + | `extern "cdecl"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "cdecl-unwind"` functions + --> $DIR/unsupported-abi.rs:43:51 + | +LL | unsafe extern "cdecl-unwind" fn cdecl_unwind_free(_: ...) {} + | --------------------- ^^^^^^ + | | + | `extern "cdecl-unwind"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "stdcall"` functions + --> $DIR/unsupported-abi.rs:45:41 + | +LL | unsafe extern "stdcall" fn stdcall_free(_: ...) {} + | ---------------- ^^^^^^ + | | + | `extern "stdcall"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "stdcall-unwind"` functions + --> $DIR/unsupported-abi.rs:47:55 + | +LL | unsafe extern "stdcall-unwind" fn stdcall_unwind_free(_: ...) {} + | ----------------------- ^^^^^^ + | | + | `extern "stdcall-unwind"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "thiscall"` functions + --> $DIR/unsupported-abi.rs:49:43 + | +LL | unsafe extern "thiscall" fn thiscall_free(_: ...) {} + | ----------------- ^^^^^^ + | | + | `extern "thiscall"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "thiscall-unwind"` functions + --> $DIR/unsupported-abi.rs:51:57 + | +LL | unsafe extern "thiscall-unwind" fn thiscall_unwind_free(_: ...) {} + | ------------------------ ^^^^^^ + | | + | `extern "thiscall-unwind"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for non-extern functions + --> $DIR/unsupported-abi.rs:57:27 + | +LL | unsafe fn rust_method(_: ...) {} + | ^^^^^^ + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "Rust"` functions + --> $DIR/unsupported-abi.rs:59:50 + | +LL | unsafe extern "Rust" fn rust_method_explicit(_: ...) {} + | ------------- ^^^^^^ + | | + | `extern "Rust"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "cdecl"` functions + --> $DIR/unsupported-abi.rs:65:43 + | +LL | unsafe extern "cdecl" fn cdecl_method(_: ...) {} + | -------------- ^^^^^^ + | | + | `extern "cdecl"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "cdecl-unwind"` functions + --> $DIR/unsupported-abi.rs:67:57 + | +LL | unsafe extern "cdecl-unwind" fn cdecl_unwind_method(_: ...) {} + | --------------------- ^^^^^^ + | | + | `extern "cdecl-unwind"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "stdcall"` functions + --> $DIR/unsupported-abi.rs:69:47 + | +LL | unsafe extern "stdcall" fn stdcall_method(_: ...) {} + | ---------------- ^^^^^^ + | | + | `extern "stdcall"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "stdcall-unwind"` functions + --> $DIR/unsupported-abi.rs:71:61 + | +LL | unsafe extern "stdcall-unwind" fn stdcall_unwind_method(_: ...) {} + | ----------------------- ^^^^^^ + | | + | `extern "stdcall-unwind"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "thiscall"` functions + --> $DIR/unsupported-abi.rs:73:49 + | +LL | unsafe extern "thiscall" fn thiscall_method(_: ...) {} + | ----------------- ^^^^^^ + | | + | `extern "thiscall"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "thiscall-unwind"` functions + --> $DIR/unsupported-abi.rs:75:63 + | +LL | unsafe extern "thiscall-unwind" fn thiscall_unwind_method(_: ...) {} + | ------------------------ ^^^^^^ + | | + | `extern "thiscall-unwind"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for non-extern functions + --> $DIR/unsupported-abi.rs:80:33 + | +LL | unsafe fn rust_trait_method(_: ...) {} + | ^^^^^^ + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "Rust"` functions + --> $DIR/unsupported-abi.rs:82:56 + | +LL | unsafe extern "Rust" fn rust_trait_method_explicit(_: ...) {} + | ------------- ^^^^^^ + | | + | `extern "Rust"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "cdecl"` functions + --> $DIR/unsupported-abi.rs:88:49 + | +LL | unsafe extern "cdecl" fn cdecl_trait_method(_: ...) {} + | -------------- ^^^^^^ + | | + | `extern "cdecl"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "cdecl-unwind"` functions + --> $DIR/unsupported-abi.rs:90:63 + | +LL | unsafe extern "cdecl-unwind" fn cdecl_unwind_trait_method(_: ...) {} + | --------------------- ^^^^^^ + | | + | `extern "cdecl-unwind"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "stdcall"` functions + --> $DIR/unsupported-abi.rs:92:53 + | +LL | unsafe extern "stdcall" fn stdcall_trait_method(_: ...) {} + | ---------------- ^^^^^^ + | | + | `extern "stdcall"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "stdcall-unwind"` functions + --> $DIR/unsupported-abi.rs:94:67 + | +LL | unsafe extern "stdcall-unwind" fn stdcall_unwind_trait_method(_: ...) {} + | ----------------------- ^^^^^^ + | | + | `extern "stdcall-unwind"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "thiscall"` functions + --> $DIR/unsupported-abi.rs:96:55 + | +LL | unsafe extern "thiscall" fn thiscall_trait_method(_: ...) {} + | ----------------- ^^^^^^ + | | + | `extern "thiscall"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "thiscall-unwind"` functions + --> $DIR/unsupported-abi.rs:98:69 + | +LL | unsafe extern "thiscall-unwind" fn thiscall_unwind_trait_method(_: ...) {} + | ------------------------ ^^^^^^ + | | + | `extern "thiscall-unwind"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for non-extern functions + --> $DIR/unsupported-abi.rs:103:33 + | +LL | unsafe fn rust_trait_method(_: ...) {} + | ^^^^^^ + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "Rust"` functions + --> $DIR/unsupported-abi.rs:105:56 + | +LL | unsafe extern "Rust" fn rust_trait_method_explicit(_: ...) {} + | ------------- ^^^^^^ + | | + | `extern "Rust"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "cdecl"` functions + --> $DIR/unsupported-abi.rs:111:49 + | +LL | unsafe extern "cdecl" fn cdecl_trait_method(_: ...) {} + | -------------- ^^^^^^ + | | + | `extern "cdecl"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "cdecl-unwind"` functions + --> $DIR/unsupported-abi.rs:113:63 + | +LL | unsafe extern "cdecl-unwind" fn cdecl_unwind_trait_method(_: ...) {} + | --------------------- ^^^^^^ + | | + | `extern "cdecl-unwind"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "stdcall"` functions + --> $DIR/unsupported-abi.rs:115:53 + | +LL | unsafe extern "stdcall" fn stdcall_trait_method(_: ...) {} + | ---------------- ^^^^^^ + | | + | `extern "stdcall"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "stdcall-unwind"` functions + --> $DIR/unsupported-abi.rs:117:67 + | +LL | unsafe extern "stdcall-unwind" fn stdcall_unwind_trait_method(_: ...) {} + | ----------------------- ^^^^^^ + | | + | `extern "stdcall-unwind"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "thiscall"` functions + --> $DIR/unsupported-abi.rs:119:55 + | +LL | unsafe extern "thiscall" fn thiscall_trait_method(_: ...) {} + | ----------------- ^^^^^^ + | | + | `extern "thiscall"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: `...` is not supported for `extern "thiscall-unwind"` functions + --> $DIR/unsupported-abi.rs:121:69 + | +LL | unsafe extern "thiscall-unwind" fn thiscall_unwind_trait_method(_: ...) {} + | ------------------------ ^^^^^^ + | | + | `extern "thiscall-unwind"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error[E0045]: C-variadic functions with the "Rust" calling convention are not supported + --> $DIR/unsupported-abi.rs:14:22 + | +LL | extern "Rust" { fn rust_foreign_explicit(_: ...); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention + +error[E0045]: C-variadic functions with the "stdcall" calling convention are not supported + --> $DIR/unsupported-abi.rs:20:25 + | +LL | extern "stdcall" { fn stdcall_foreign(_: ...); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention + +error[E0045]: C-variadic functions with the "stdcall-unwind" calling convention are not supported + --> $DIR/unsupported-abi.rs:22:32 + | +LL | extern "stdcall-unwind" { fn stdcall_unwind_foreign(_: ...); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention + +error[E0045]: C-variadic functions with the "thiscall" calling convention are not supported + --> $DIR/unsupported-abi.rs:24:26 + | +LL | extern "thiscall" { fn thiscall_foreign(_: ...); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention + +error[E0045]: C-variadic functions with the "thiscall-unwind" calling convention are not supported + --> $DIR/unsupported-abi.rs:26:33 + | +LL | extern "thiscall-unwind" { fn thiscall_unwind_foreign(_: ...); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention + +error: aborting due to 37 previous errors + +For more information about this error, try `rustc --explain E0045`. From 43a6b1041819c5f5b70faf1ad5833400087dc1b0 Mon Sep 17 00:00:00 2001 From: IoaNNUwU Date: Fri, 12 Sep 2025 00:01:38 +0200 Subject: [PATCH 410/682] Use raw fmt str in format macro --- compiler/rustc_builtin_macros/src/format.rs | 14 ++++-- .../missing-format-specifiers-issue-68293.rs | 29 +++++++++++ ...ssing-format-specifiers-issue-68293.stderr | 49 ++++++++++++++++++- 3 files changed, 87 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index a6c8e7d29cc7..d70888205a51 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -569,6 +569,7 @@ fn make_format_args( detect_foreign_fmt, str_style, fmt_str, + uncooked_fmt_str.1.as_str(), fmt_span, ); } @@ -650,6 +651,7 @@ fn report_missing_placeholders( detect_foreign_fmt: bool, str_style: Option, fmt_str: &str, + uncooked_fmt_str: &str, fmt_span: Span, ) { let mut diag = if let &[(span, named)] = &unused[..] { @@ -773,16 +775,20 @@ fn report_missing_placeholders( diag.note(format!("consider adding {} format specifiers", unused.len())); } } else { - let original_fmt_str = - if fmt_str.len() >= 1 { &fmt_str[..fmt_str.len() - 1] } else { "" }; - let msg = if unused.len() == 1 { "a format specifier".to_string() } else { format!("{} format specifiers", unused.len()) }; - let sugg = format!("\"{}{}\"", original_fmt_str, "{}".repeat(unused.len())); + let sugg = match str_style { + None => format!("\"{}{}\"", uncooked_fmt_str, "{}".repeat(unused.len())), + Some(n_hashes) => format!( + "r{hashes}\"{uncooked_fmt_str}{fmt_specifiers}\"{hashes}", + hashes = "#".repeat(n_hashes), + fmt_specifiers = "{}".repeat(unused.len()) + ), + }; let msg = format!("format specifiers use curly braces, consider adding {msg}"); diag.span_suggestion_verbose(fmt_span, msg, sugg, Applicability::MaybeIncorrect); diff --git a/tests/ui/suggestions/missing-format-specifiers-issue-68293.rs b/tests/ui/suggestions/missing-format-specifiers-issue-68293.rs index 29799624d782..fbede7c41cb2 100644 --- a/tests/ui/suggestions/missing-format-specifiers-issue-68293.rs +++ b/tests/ui/suggestions/missing-format-specifiers-issue-68293.rs @@ -32,4 +32,33 @@ fn missing_format_specifiers_multiple_unused_args() { //~| NOTE consider adding 2 format specifiers } +fn unicode_unused_args() { + panic!("👆", "👆", 1); + //~^ ERROR multiple unused formatting arguments + //~| NOTE multiple missing formatting specifiers + //~| NOTE argument never used + //~| NOTE argument never used + //~| HELP format specifiers use curly braces, consider adding 2 format specifiers +} + +fn raw_str_unused_arg() { + format_args!(r##"lJ𐏿Æ�.𐏿�"##, r#"r}J𐏿Æ" {}"#, 1); + //~^ ERROR multiple unused formatting arguments + //~| NOTE multiple missing formatting specifiers + //~| NOTE argument never used + //~| NOTE argument never used + //~| HELP format specifiers use curly braces, consider adding 2 format specifiers +} + +fn valid_new_lines_unused_args() { + panic!("Expect 2 newlines + +", "👆", 1); + //~^ ERROR multiple unused formatting arguments + //~| NOTE argument never used + //~| NOTE argument never used + //~^^^^^^ NOTE multiple missing formatting specifiers + //~| HELP format specifiers use curly braces, consider adding 2 format specifiers +} + fn main() { } diff --git a/tests/ui/suggestions/missing-format-specifiers-issue-68293.stderr b/tests/ui/suggestions/missing-format-specifiers-issue-68293.stderr index 081409789f5a..7e997241698c 100644 --- a/tests/ui/suggestions/missing-format-specifiers-issue-68293.stderr +++ b/tests/ui/suggestions/missing-format-specifiers-issue-68293.stderr @@ -45,5 +45,52 @@ LL | println!("list: {}", 1, 2, 3); | = note: consider adding 2 format specifiers -error: aborting due to 4 previous errors +error: multiple unused formatting arguments + --> $DIR/missing-format-specifiers-issue-68293.rs:36:17 + | +LL | panic!("👆", "👆", 1); + | ---- ^^^^ ^ argument never used + | | | + | | argument never used + | multiple missing formatting specifiers + | +help: format specifiers use curly braces, consider adding 2 format specifiers + | +LL | panic!("👆{}{}", "👆", 1); + | ++++ + +error: multiple unused formatting arguments + --> $DIR/missing-format-specifiers-issue-68293.rs:45:35 + | +LL | format_args!(r##"lJ𐏿Æ�.𐏿�"##, r#"r}J𐏿Æ" {}"#, 1); + | --------------- ^^^^^^^^^^^^^^ ^ argument never used + | | | + | | argument never used + | multiple missing formatting specifiers + | +help: format specifiers use curly braces, consider adding 2 format specifiers + | +LL | format_args!(r##"lJ𐏿Æ�.𐏿�{}{}"##, r#"r}J𐏿Æ" {}"#, 1); + | ++++ + +error: multiple unused formatting arguments + --> $DIR/missing-format-specifiers-issue-68293.rs:56:4 + | +LL | panic!("Expect 2 newlines + | ____________- +LL | | +LL | | ", "👆", 1); + | | - ^^^^ ^ argument never used + | | | | + | |_| argument never used + | multiple missing formatting specifiers + | +help: format specifiers use curly braces, consider adding 2 format specifiers + | +LL | panic!("Expect 2 newlines +LL | +LL ~ {}{}", "👆", 1); + | + +error: aborting due to 7 previous errors From 1793eec3535f971242637ff095aac9efdc98738b Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Thu, 11 Sep 2025 15:11:16 -0700 Subject: [PATCH 411/682] test: remove an outdated normalization for rustc versions These "you are using $RUSTC_VERSION" help messages were removed in rust-lang/rust#142943, but rust-lang/rust#142681 started before that and merged later, so its normalization is vestigial. --- tests/ui/feature-gates/feature-gate-sanitize.rs | 1 - tests/ui/feature-gates/feature-gate-sanitize.stderr | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/ui/feature-gates/feature-gate-sanitize.rs b/tests/ui/feature-gates/feature-gate-sanitize.rs index 40098d93272c..768417cfae8a 100644 --- a/tests/ui/feature-gates/feature-gate-sanitize.rs +++ b/tests/ui/feature-gates/feature-gate-sanitize.rs @@ -1,4 +1,3 @@ -//@ normalize-stderr: "you are using [0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+)?( \([^)]*\))?" -> "you are using $$RUSTC_VERSION" #![feature(no_sanitize)] //~ ERROR feature has been removed #[sanitize(address = "on")] diff --git a/tests/ui/feature-gates/feature-gate-sanitize.stderr b/tests/ui/feature-gates/feature-gate-sanitize.stderr index 7c38b351916a..513999636a95 100644 --- a/tests/ui/feature-gates/feature-gate-sanitize.stderr +++ b/tests/ui/feature-gates/feature-gate-sanitize.stderr @@ -1,5 +1,5 @@ error[E0557]: feature has been removed - --> $DIR/feature-gate-sanitize.rs:2:12 + --> $DIR/feature-gate-sanitize.rs:1:12 | LL | #![feature(no_sanitize)] | ^^^^^^^^^^^ feature has been removed @@ -8,7 +8,7 @@ LL | #![feature(no_sanitize)] = note: renamed to sanitize(xyz = "on|off") error[E0658]: the `#[sanitize]` attribute is an experimental feature - --> $DIR/feature-gate-sanitize.rs:4:1 + --> $DIR/feature-gate-sanitize.rs:3:1 | LL | #[sanitize(address = "on")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 4c849c73a4b49ffe3a96261d95b65df1421befd9 Mon Sep 17 00:00:00 2001 From: Fang He Date: Fri, 12 Sep 2025 08:18:26 +0800 Subject: [PATCH 412/682] fix typo in comment --- library/std_detect/src/detect/os/linux/aarch64.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std_detect/src/detect/os/linux/aarch64.rs b/library/std_detect/src/detect/os/linux/aarch64.rs index c53dbf3fd9ba..b733b8a9eb23 100644 --- a/library/std_detect/src/detect/os/linux/aarch64.rs +++ b/library/std_detect/src/detect/os/linux/aarch64.rs @@ -212,7 +212,7 @@ impl From for AtHwcap { // Hardware capabilities from bits 32 to 63 should only // be tested on LP64 targets with 64 bits `usize`. - // On ILP32 targets like `aarch64-unknown-linux-gun_ilp32`, + // On ILP32 targets like `aarch64-unknown-linux-gnu_ilp32`, // these hardware capabilities will default to `false`. // https://github.com/rust-lang/rust/issues/146230 #[cfg(target_pointer_width = "64")] From 066a9794dff5d2976f77cd54bac8d6ee8aaa6c6b Mon Sep 17 00:00:00 2001 From: yanglsh Date: Mon, 25 Aug 2025 08:20:01 +0800 Subject: [PATCH 413/682] fix: `invalid_upcast_comparisons` wrongly unmangled macros --- clippy_lints/src/invalid_upcast_comparisons.rs | 13 +++++++++++-- tests/ui/invalid_upcast_comparisons.rs | 12 ++++++++++++ tests/ui/invalid_upcast_comparisons.stderr | 8 +++++++- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/invalid_upcast_comparisons.rs b/clippy_lints/src/invalid_upcast_comparisons.rs index b0ecc5d52ddb..1666e8e5ae32 100644 --- a/clippy_lints/src/invalid_upcast_comparisons.rs +++ b/clippy_lints/src/invalid_upcast_comparisons.rs @@ -1,3 +1,4 @@ +use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf; @@ -9,7 +10,7 @@ use clippy_utils::comparisons; use clippy_utils::comparisons::Rel; use clippy_utils::consts::{ConstEvalCtxt, FullInt}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::source::snippet; +use clippy_utils::source::snippet_with_context; declare_clippy_lint! { /// ### What it does @@ -69,13 +70,21 @@ fn numeric_cast_precast_bounds(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option< fn err_upcast_comparison(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, always: bool) { if let ExprKind::Cast(cast_val, _) = expr.kind { + let mut applicability = Applicability::MachineApplicable; + let (cast_val_snip, _) = snippet_with_context( + cx, + cast_val.span, + expr.span.ctxt(), + "the expression", + &mut applicability, + ); span_lint( cx, INVALID_UPCAST_COMPARISONS, span, format!( "because of the numeric bounds on `{}` prior to casting, this expression is always {}", - snippet(cx, cast_val.span, "the expression"), + cast_val_snip, if always { "true" } else { "false" }, ), ); diff --git a/tests/ui/invalid_upcast_comparisons.rs b/tests/ui/invalid_upcast_comparisons.rs index 4f3194869f43..88ecca65e150 100644 --- a/tests/ui/invalid_upcast_comparisons.rs +++ b/tests/ui/invalid_upcast_comparisons.rs @@ -133,3 +133,15 @@ fn main() { -5 == (u32 as i32); } + +fn issue15662() { + macro_rules! add_one { + ($x:expr) => { + $x + 1 + }; + } + + let x: u8 = 1; + (add_one!(x) as u32) > 300; + //~^ invalid_upcast_comparisons +} diff --git a/tests/ui/invalid_upcast_comparisons.stderr b/tests/ui/invalid_upcast_comparisons.stderr index ef36f18eabc9..cc042a7c4b01 100644 --- a/tests/ui/invalid_upcast_comparisons.stderr +++ b/tests/ui/invalid_upcast_comparisons.stderr @@ -163,5 +163,11 @@ error: because of the numeric bounds on `u8` prior to casting, this expression i LL | -5 >= (u8 as i32); | ^^^^^^^^^^^^^^^^^ -error: aborting due to 27 previous errors +error: because of the numeric bounds on `add_one!(x)` prior to casting, this expression is always false + --> tests/ui/invalid_upcast_comparisons.rs:145:5 + | +LL | (add_one!(x) as u32) > 300; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 28 previous errors From c7c2fdd80472a99ada62e14366c7916f823fe346 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Wed, 10 Sep 2025 05:59:02 +0200 Subject: [PATCH 414/682] ci: Increase `rust.debuginfo-level-tests` to `2` in `x86_64-gnu-debug` job Simply to increase the scope of the testing. Force debuginfo=0 for a handful of tests so that we can have CI prevent regressing on more tests. --- src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile | 2 +- .../ui/async-await/future-sizes/async-awaiting-fut.rs | 2 ++ tests/ui/async-await/future-sizes/large-arg.rs | 2 ++ .../collect-in-promoted-const.noopt.stderr | 6 +++--- .../collect-in-promoted-const.opt.stderr | 10 +++++----- .../required-consts/collect-in-promoted-const.rs | 2 ++ tests/ui/print_type_sizes/async.rs | 2 ++ 7 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile index 7e6a59aaf898..54b13c4397f4 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile @@ -38,7 +38,7 @@ ENV RUST_CONFIGURE_ARGS \ --build=x86_64-unknown-linux-gnu \ --enable-debug \ --enable-lld \ - --set rust.debuginfo-level-tests=1 \ + --set rust.debuginfo-level-tests=2 \ --set llvm.use-linker=lld \ --set target.x86_64-unknown-linux-gnu.linker=clang \ --set target.x86_64-unknown-linux-gnu.cc=clang \ diff --git a/tests/ui/async-await/future-sizes/async-awaiting-fut.rs b/tests/ui/async-await/future-sizes/async-awaiting-fut.rs index b5f59069f85b..7113f591630d 100644 --- a/tests/ui/async-await/future-sizes/async-awaiting-fut.rs +++ b/tests/ui/async-await/future-sizes/async-awaiting-fut.rs @@ -1,3 +1,5 @@ +// FIXME(#61117): Respect debuginfo-level-tests, do not force debuginfo=0 +//@ compile-flags: -C debuginfo=0 //@ compile-flags: -C panic=abort -Z print-type-sizes --crate-type lib //@ needs-deterministic-layouts //@ edition:2021 diff --git a/tests/ui/async-await/future-sizes/large-arg.rs b/tests/ui/async-await/future-sizes/large-arg.rs index 809f7cf1f93c..b05a2b719151 100644 --- a/tests/ui/async-await/future-sizes/large-arg.rs +++ b/tests/ui/async-await/future-sizes/large-arg.rs @@ -1,3 +1,5 @@ +// FIXME(#61117): Respect debuginfo-level-tests, do not force debuginfo=0 +//@ compile-flags: -C debuginfo=0 //@ compile-flags: -C panic=abort -Z print-type-sizes --crate-type=lib //@ needs-deterministic-layouts //@ edition: 2021 diff --git a/tests/ui/consts/required-consts/collect-in-promoted-const.noopt.stderr b/tests/ui/consts/required-consts/collect-in-promoted-const.noopt.stderr index 67f5009c4ffc..c1ae42b3939d 100644 --- a/tests/ui/consts/required-consts/collect-in-promoted-const.noopt.stderr +++ b/tests/ui/consts/required-consts/collect-in-promoted-const.noopt.stderr @@ -1,17 +1,17 @@ error[E0080]: evaluation panicked: explicit panic - --> $DIR/collect-in-promoted-const.rs:9:19 + --> $DIR/collect-in-promoted-const.rs:11:19 | LL | const C: () = panic!(); | ^^^^^^^^ evaluation of `Fail::::C` failed here note: erroneous constant encountered - --> $DIR/collect-in-promoted-const.rs:20:21 + --> $DIR/collect-in-promoted-const.rs:22:21 | LL | let _val = &Fail::::C; | ^^^^^^^^^^^^ note: the above error was encountered while instantiating `fn f::` - --> $DIR/collect-in-promoted-const.rs:25:5 + --> $DIR/collect-in-promoted-const.rs:27:5 | LL | f::(); | ^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-promoted-const.opt.stderr b/tests/ui/consts/required-consts/collect-in-promoted-const.opt.stderr index 5c3edf68d95f..0d7ac48172c8 100644 --- a/tests/ui/consts/required-consts/collect-in-promoted-const.opt.stderr +++ b/tests/ui/consts/required-consts/collect-in-promoted-const.opt.stderr @@ -1,23 +1,23 @@ error[E0080]: evaluation panicked: explicit panic - --> $DIR/collect-in-promoted-const.rs:9:19 + --> $DIR/collect-in-promoted-const.rs:11:19 | LL | const C: () = panic!(); | ^^^^^^^^ evaluation of `Fail::::C` failed here note: erroneous constant encountered - --> $DIR/collect-in-promoted-const.rs:20:21 + --> $DIR/collect-in-promoted-const.rs:22:21 | LL | let _val = &Fail::::C; | ^^^^^^^^^^^^ error[E0080]: evaluation panicked: explicit panic - --> $DIR/collect-in-promoted-const.rs:9:19 + --> $DIR/collect-in-promoted-const.rs:11:19 | LL | const C: () = panic!(); | ^^^^^^^^ evaluation of `Fail::::C` failed here note: erroneous constant encountered - --> $DIR/collect-in-promoted-const.rs:20:21 + --> $DIR/collect-in-promoted-const.rs:22:21 | LL | let _val = &Fail::::C; | ^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | let _val = &Fail::::C; = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` note: the above error was encountered while instantiating `fn f::` - --> $DIR/collect-in-promoted-const.rs:25:5 + --> $DIR/collect-in-promoted-const.rs:27:5 | LL | f::(); | ^^^^^^^^^^ diff --git a/tests/ui/consts/required-consts/collect-in-promoted-const.rs b/tests/ui/consts/required-consts/collect-in-promoted-const.rs index 25c8cb7e8049..498328abe216 100644 --- a/tests/ui/consts/required-consts/collect-in-promoted-const.rs +++ b/tests/ui/consts/required-consts/collect-in-promoted-const.rs @@ -1,6 +1,8 @@ //@revisions: noopt opt //@ build-fail //@[noopt] compile-flags: -Copt-level=0 +// FIXME(#61117): Respect debuginfo-level-tests, do not force debuginfo=0 +//@[opt] compile-flags: -C debuginfo=0 //@[opt] compile-flags: -O //! Make sure we error on erroneous consts even if they get promoted. diff --git a/tests/ui/print_type_sizes/async.rs b/tests/ui/print_type_sizes/async.rs index 951e7cd1012c..b6ec88426345 100644 --- a/tests/ui/print_type_sizes/async.rs +++ b/tests/ui/print_type_sizes/async.rs @@ -1,3 +1,5 @@ +// FIXME(#61117): Respect debuginfo-level-tests, do not force debuginfo=0 +//@ compile-flags: -C debuginfo=0 //@ compile-flags: -C panic=abort -Z print-type-sizes --crate-type lib //@ needs-deterministic-layouts //@ edition:2021 From 44edbc6d54e0bd3f9eea882ed5cd7646d6b79523 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Fri, 12 Sep 2025 04:52:27 +0000 Subject: [PATCH 415/682] Prepare for merging from rust-lang/rust This updates the rust-version file to 2a9bacf6187685931d52346a0ecff2e52bdc91cc. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index b71a0d219d77..b3f8a26875f4 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -f4665ab8368ad2e8a86d4390ae35c28bdd9561bb +2a9bacf6187685931d52346a0ecff2e52bdc91cc From 745b79097f5e0b22819dafc6dcc9a857dc7479d2 Mon Sep 17 00:00:00 2001 From: Teodoro Freund Date: Wed, 10 Sep 2025 08:03:53 +0100 Subject: [PATCH 416/682] Only suggest type in as_underscore when it's suggestable --- clippy_lints/src/casts/as_underscore.rs | 8 ++++---- tests/ui/as_underscore_unfixable.rs | 14 ++++++++++++++ tests/ui/as_underscore_unfixable.stderr | 20 ++++++++++++++++++++ 3 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 tests/ui/as_underscore_unfixable.rs create mode 100644 tests/ui/as_underscore_unfixable.stderr diff --git a/clippy_lints/src/casts/as_underscore.rs b/clippy_lints/src/casts/as_underscore.rs index 3ac486dd63fb..a73e48e5fd5d 100644 --- a/clippy_lints/src/casts/as_underscore.rs +++ b/clippy_lints/src/casts/as_underscore.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::Applicability; use rustc_hir::{Expr, Ty, TyKind}; use rustc_lint::LateContext; -use rustc_middle::ty; +use rustc_middle::ty::IsSuggestable; use super::AS_UNDERSCORE; @@ -10,15 +10,15 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ty: &'tc if matches!(ty.kind, TyKind::Infer(())) { span_lint_and_then(cx, AS_UNDERSCORE, expr.span, "using `as _` conversion", |diag| { let ty_resolved = cx.typeck_results().expr_ty(expr); - if let ty::Error(_) = ty_resolved.kind() { - diag.help("consider giving the type explicitly"); - } else { + if ty_resolved.is_suggestable(cx.tcx, true) { diag.span_suggestion( ty.span, "consider giving the type explicitly", ty_resolved, Applicability::MachineApplicable, ); + } else { + diag.help("consider giving the type explicitly"); } }); } diff --git a/tests/ui/as_underscore_unfixable.rs b/tests/ui/as_underscore_unfixable.rs new file mode 100644 index 000000000000..854feca7174f --- /dev/null +++ b/tests/ui/as_underscore_unfixable.rs @@ -0,0 +1,14 @@ +//@no-rustfix + +#![warn(clippy::as_underscore)] + +fn main() { + // From issue #15282 + let f = async || (); + let _: Box _> = Box::new(f) as _; + //~^ as_underscore + + let barr = || (|| ()); + let _: Box _> = Box::new(barr) as _; + //~^ as_underscore +} diff --git a/tests/ui/as_underscore_unfixable.stderr b/tests/ui/as_underscore_unfixable.stderr new file mode 100644 index 000000000000..7385bea5c650 --- /dev/null +++ b/tests/ui/as_underscore_unfixable.stderr @@ -0,0 +1,20 @@ +error: using `as _` conversion + --> tests/ui/as_underscore_unfixable.rs:8:37 + | +LL | let _: Box _> = Box::new(f) as _; + | ^^^^^^^^^^^^^^^^ + | + = help: consider giving the type explicitly + = note: `-D clippy::as-underscore` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::as_underscore)]` + +error: using `as _` conversion + --> tests/ui/as_underscore_unfixable.rs:12:33 + | +LL | let _: Box _> = Box::new(barr) as _; + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider giving the type explicitly + +error: aborting due to 2 previous errors + From dbfa01b6a19ac62ba559e38c2b95ef26fd90ee39 Mon Sep 17 00:00:00 2001 From: klensy Date: Fri, 12 Sep 2025 12:03:09 +0300 Subject: [PATCH 417/682] remove outdated jsbackend leftovers --- compiler/rustc_llvm/build.rs | 1 - compiler/rustc_llvm/src/lib.rs | 6 ------ 2 files changed, 7 deletions(-) diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs index 9d21d0d22e3e..2cfda7a5fb4b 100644 --- a/compiler/rustc_llvm/build.rs +++ b/compiler/rustc_llvm/build.rs @@ -16,7 +16,6 @@ const OPTIONAL_COMPONENTS: &[&str] = &[ "mips", "powerpc", "systemz", - "jsbackend", "webassembly", "msp430", "sparc", diff --git a/compiler/rustc_llvm/src/lib.rs b/compiler/rustc_llvm/src/lib.rs index ed5edeef1617..14e94121d1cb 100644 --- a/compiler/rustc_llvm/src/lib.rs +++ b/compiler/rustc_llvm/src/lib.rs @@ -179,12 +179,6 @@ pub fn initialize_available_targets() { LLVMInitializeSystemZAsmPrinter, LLVMInitializeSystemZAsmParser ); - init_target!( - llvm_component = "jsbackend", - LLVMInitializeJSBackendTargetInfo, - LLVMInitializeJSBackendTarget, - LLVMInitializeJSBackendTargetMC - ); init_target!( llvm_component = "msp430", LLVMInitializeMSP430TargetInfo, From ff9b1c1d285821fabe3cbf904fc407d62d95fbdc Mon Sep 17 00:00:00 2001 From: Evgenii Zheltonozhskii Date: Fri, 12 Sep 2025 12:39:31 +0300 Subject: [PATCH 418/682] Constify Eq, Ord, PartialOrd --- library/core/src/any.rs | 3 +- library/core/src/array/equality.rs | 46 +++-- library/core/src/ascii/ascii_char.rs | 3 +- library/core/src/cmp.rs | 162 ++++++++++++------ library/core/src/convert/mod.rs | 9 +- library/core/src/num/nonzero.rs | 17 +- library/core/src/ops/control_flow.rs | 3 +- library/core/src/option.rs | 9 +- library/core/src/range.rs | 6 +- library/core/src/result.rs | 3 +- library/core/src/slice/cmp.rs | 43 +++-- library/core/src/str/traits.rs | 3 +- library/core/src/tuple.rs | 12 +- library/coretests/tests/cmp.rs | 6 +- library/coretests/tests/lib.rs | 1 + tests/ui/consts/const_cmp_type_id.rs | 5 +- tests/ui/consts/const_cmp_type_id.stderr | 8 +- tests/ui/consts/fn_trait_refs.rs | 3 +- tests/ui/consts/fn_trait_refs.stderr | 22 --- .../traits/const-traits/const-impl-trait.rs | 4 +- .../const-traits/const-impl-trait.stderr | 17 -- .../const_derives/derive-const-use.rs | 3 +- .../const_derives/derive-const-use.stderr | 11 -- 23 files changed, 226 insertions(+), 173 deletions(-) delete mode 100644 tests/ui/consts/fn_trait_refs.stderr delete mode 100644 tests/ui/traits/const-traits/const-impl-trait.stderr delete mode 100644 tests/ui/traits/const-traits/const_derives/derive-const-use.stderr diff --git a/library/core/src/any.rs b/library/core/src/any.rs index e7d9763d46ef..76ea2d18a82f 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -705,7 +705,8 @@ impl dyn Any + Send + Sync { /// std::mem::forget(fake_one_ring); /// } /// ``` -#[derive(Clone, Copy, Eq, PartialOrd, Ord)] +#[derive(Copy, PartialOrd, Ord)] +#[derive_const(Clone, Eq)] #[stable(feature = "rust1", since = "1.0.0")] #[lang = "type_id"] pub struct TypeId { diff --git a/library/core/src/array/equality.rs b/library/core/src/array/equality.rs index 1ad2cca64a34..c2c7ccf0daa2 100644 --- a/library/core/src/array/equality.rs +++ b/library/core/src/array/equality.rs @@ -1,9 +1,10 @@ use crate::cmp::BytewiseEq; #[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq<[U; N]> for [T; N] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl const PartialEq<[U; N]> for [T; N] where - T: PartialEq, + T: [const] PartialEq, { #[inline] fn eq(&self, other: &[U; N]) -> bool { @@ -16,9 +17,10 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq<[U]> for [T; N] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl const PartialEq<[U]> for [T; N] where - T: PartialEq, + T: [const] PartialEq, { #[inline] fn eq(&self, other: &[U]) -> bool { @@ -37,9 +39,10 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq<[U; N]> for [T] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl const PartialEq<[U; N]> for [T] where - T: PartialEq, + T: [const] PartialEq, { #[inline] fn eq(&self, other: &[U; N]) -> bool { @@ -58,9 +61,10 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq<&[U]> for [T; N] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl const PartialEq<&[U]> for [T; N] where - T: PartialEq, + T: [const] PartialEq, { #[inline] fn eq(&self, other: &&[U]) -> bool { @@ -73,9 +77,10 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq<[U; N]> for &[T] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl const PartialEq<[U; N]> for &[T] where - T: PartialEq, + T: [const] PartialEq, { #[inline] fn eq(&self, other: &[U; N]) -> bool { @@ -88,9 +93,10 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq<&mut [U]> for [T; N] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl const PartialEq<&mut [U]> for [T; N] where - T: PartialEq, + T: [const] PartialEq, { #[inline] fn eq(&self, other: &&mut [U]) -> bool { @@ -103,9 +109,10 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq<[U; N]> for &mut [T] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl const PartialEq<[U; N]> for &mut [T] where - T: PartialEq, + T: [const] PartialEq, { #[inline] fn eq(&self, other: &[U; N]) -> bool { @@ -122,14 +129,18 @@ where // __impl_slice_eq2! { [A; $N], &'b mut [B; $N] } #[stable(feature = "rust1", since = "1.0.0")] -impl Eq for [T; N] {} +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl const Eq for [T; N] {} +#[const_trait] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] trait SpecArrayEq: Sized { fn spec_eq(a: &[Self; N], b: &[Other; N]) -> bool; fn spec_ne(a: &[Self; N], b: &[Other; N]) -> bool; } -impl, Other, const N: usize> SpecArrayEq for T { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl, Other, const N: usize> const SpecArrayEq for T { default fn spec_eq(a: &[Self; N], b: &[Other; N]) -> bool { a[..] == b[..] } @@ -138,7 +149,8 @@ impl, Other, const N: usize> SpecArrayEq for T { } } -impl, U, const N: usize> SpecArrayEq for T { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl, U, const N: usize> const SpecArrayEq for T { fn spec_eq(a: &[T; N], b: &[U; N]) -> bool { // SAFETY: Arrays are compared element-wise, and don't add any padding // between elements, so when the elements are `BytewiseEq`, we can diff --git a/library/core/src/ascii/ascii_char.rs b/library/core/src/ascii/ascii_char.rs index 49d540314a24..178af2c0e3b4 100644 --- a/library/core/src/ascii/ascii_char.rs +++ b/library/core/src/ascii/ascii_char.rs @@ -54,7 +54,8 @@ use crate::{assert_unsafe_precondition, fmt}; /// [chart]: https://www.unicode.org/charts/PDF/U0000.pdf /// [NIST FIPS 1-2]: https://nvlpubs.nist.gov/nistpubs/Legacy/FIPS/fipspub1-2-1977.pdf /// [NamesList]: https://www.unicode.org/Public/15.0.0/ucd/NamesList.txt -#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[derive(Copy, Hash)] +#[derive_const(Clone, Eq, PartialEq, Ord, PartialOrd)] #[unstable(feature = "ascii_char", issue = "110998")] #[repr(u8)] pub enum AsciiChar { diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 0812322f3fbb..94536f25b419 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -29,7 +29,7 @@ mod bytewise; pub(crate) use bytewise::BytewiseEq; use self::Ordering::*; -use crate::marker::PointeeSized; +use crate::marker::{Destruct, PointeeSized}; use crate::ops::ControlFlow; /// Trait for comparisons using the equality operator. @@ -334,7 +334,9 @@ pub macro PartialEq($item:item) { #[doc(alias = "!=")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "Eq"] -pub trait Eq: PartialEq + PointeeSized { +#[const_trait] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +pub trait Eq: [const] PartialEq + PointeeSized { // this method is used solely by `impl Eq or #[derive(Eq)]` to assert that every component of a // type implements `Eq` itself. The current deriving infrastructure means doing this assertion // without using a method on this trait is nearly impossible. @@ -380,8 +382,8 @@ pub struct AssertParamIsEq { /// /// assert_eq!(2.cmp(&1), Ordering::Greater); /// ``` -#[derive(Clone, Copy, Eq, PartialOrd, Ord, Debug, Hash)] -#[derive_const(PartialEq)] +#[derive(Copy, Debug, Hash)] +#[derive_const(Clone, Eq, PartialOrd, Ord, PartialEq)] #[stable(feature = "rust1", since = "1.0.0")] // This is a lang item only so that `BinOp::Cmp` in MIR can return it. // It has no special behavior, but does require that the three variants @@ -635,7 +637,11 @@ impl Ordering { #[inline] #[must_use] #[stable(feature = "ordering_chaining", since = "1.17.0")] - pub fn then_with Ordering>(self, f: F) -> Ordering { + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + pub const fn then_with(self, f: F) -> Ordering + where + F: [const] FnOnce() -> Ordering + [const] Destruct, + { match self { Equal => f(), _ => self, @@ -659,13 +665,15 @@ impl Ordering { /// v.sort_by_key(|&num| (num > 3, Reverse(num))); /// assert_eq!(v, vec![3, 2, 1, 6, 5, 4]); /// ``` -#[derive(PartialEq, Eq, Debug, Copy, Default, Hash)] +#[derive(Copy, Debug, Hash)] +#[derive_const(PartialEq, Eq, Default)] #[stable(feature = "reverse_cmp_key", since = "1.19.0")] #[repr(transparent)] pub struct Reverse(#[stable(feature = "reverse_cmp_key", since = "1.19.0")] pub T); #[stable(feature = "reverse_cmp_key", since = "1.19.0")] -impl PartialOrd for Reverse { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl const PartialOrd for Reverse { #[inline] fn partial_cmp(&self, other: &Reverse) -> Option { other.0.partial_cmp(&self.0) @@ -690,7 +698,8 @@ impl PartialOrd for Reverse { } #[stable(feature = "reverse_cmp_key", since = "1.19.0")] -impl Ord for Reverse { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl const Ord for Reverse { #[inline] fn cmp(&self, other: &Reverse) -> Ordering { other.0.cmp(&self.0) @@ -957,7 +966,9 @@ impl Clone for Reverse { #[doc(alias = ">=")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "Ord"] -pub trait Ord: Eq + PartialOrd + PointeeSized { +#[const_trait] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +pub trait Ord: [const] Eq + [const] PartialOrd + PointeeSized { /// This method returns an [`Ordering`] between `self` and `other`. /// /// By convention, `self.cmp(&other)` returns the ordering matching the expression @@ -1011,7 +1022,7 @@ pub trait Ord: Eq + PartialOrd + PointeeSized { #[rustc_diagnostic_item = "cmp_ord_max"] fn max(self, other: Self) -> Self where - Self: Sized, + Self: Sized + [const] Destruct, { if other < self { self } else { other } } @@ -1050,7 +1061,7 @@ pub trait Ord: Eq + PartialOrd + PointeeSized { #[rustc_diagnostic_item = "cmp_ord_min"] fn min(self, other: Self) -> Self where - Self: Sized, + Self: Sized + [const] Destruct, { if other < self { other } else { self } } @@ -1076,7 +1087,7 @@ pub trait Ord: Eq + PartialOrd + PointeeSized { #[stable(feature = "clamp", since = "1.50.0")] fn clamp(self, min: Self, max: Self) -> Self where - Self: Sized, + Self: Sized + [const] Destruct, { assert!(min <= max); if self < min { @@ -1341,6 +1352,8 @@ pub macro Ord($item:item) { )] #[rustc_diagnostic_item = "PartialOrd"] #[allow(multiple_supertrait_upcastable)] // FIXME(sized_hierarchy): remove this +#[const_trait] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] pub trait PartialOrd: PartialEq + PointeeSized { /// This method returns an ordering between `self` and `other` values if one exists. /// @@ -1481,13 +1494,14 @@ pub trait PartialOrd: PartialEq + PointeeSized { } } -fn default_chaining_impl( +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +const fn default_chaining_impl( lhs: &T, rhs: &U, - p: impl FnOnce(Ordering) -> bool, + p: impl [const] FnOnce(Ordering) -> bool + [const] Destruct, ) -> ControlFlow where - T: PartialOrd + PointeeSized, + T: [const] PartialOrd + PointeeSized, U: PointeeSized, { // It's important that this only call `partial_cmp` once, not call `eq` then @@ -1545,7 +1559,8 @@ pub macro PartialOrd($item:item) { #[must_use] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "cmp_min"] -pub fn min(v1: T, v2: T) -> T { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +pub const fn min(v1: T, v2: T) -> T { v1.min(v2) } @@ -1575,7 +1590,12 @@ pub fn min(v1: T, v2: T) -> T { #[inline] #[must_use] #[stable(feature = "cmp_min_max_by", since = "1.53.0")] -pub fn min_by Ordering>(v1: T, v2: T, compare: F) -> T { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +pub const fn min_by Ordering>( + v1: T, + v2: T, + compare: F, +) -> T { if compare(&v1, &v2).is_le() { v1 } else { v2 } } @@ -1600,7 +1620,13 @@ pub fn min_by Ordering>(v1: T, v2: T, compare: F) -> T { #[inline] #[must_use] #[stable(feature = "cmp_min_max_by", since = "1.53.0")] -pub fn min_by_key K, K: Ord>(v1: T, v2: T, mut f: F) -> T { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +pub const fn min_by_key(v1: T, v2: T, mut f: F) -> T +where + T: [const] Destruct, + F: [const] FnMut(&T) -> K + [const] Destruct, + K: [const] Ord + [const] Destruct, +{ if f(&v2) < f(&v1) { v2 } else { v1 } } @@ -1640,7 +1666,8 @@ pub fn min_by_key K, K: Ord>(v1: T, v2: T, mut f: F) -> T { #[must_use] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "cmp_max"] -pub fn max(v1: T, v2: T) -> T { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +pub const fn max(v1: T, v2: T) -> T { v1.max(v2) } @@ -1670,7 +1697,12 @@ pub fn max(v1: T, v2: T) -> T { #[inline] #[must_use] #[stable(feature = "cmp_min_max_by", since = "1.53.0")] -pub fn max_by Ordering>(v1: T, v2: T, compare: F) -> T { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +pub const fn max_by Ordering>( + v1: T, + v2: T, + compare: F, +) -> T { if compare(&v1, &v2).is_gt() { v1 } else { v2 } } @@ -1695,7 +1727,13 @@ pub fn max_by Ordering>(v1: T, v2: T, compare: F) -> T { #[inline] #[must_use] #[stable(feature = "cmp_min_max_by", since = "1.53.0")] -pub fn max_by_key K, K: Ord>(v1: T, v2: T, mut f: F) -> T { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +pub const fn max_by_key(v1: T, v2: T, mut f: F) -> T +where + T: [const] Destruct, + F: [const] FnMut(&T) -> K + [const] Destruct, + K: [const] Ord + [const] Destruct, +{ if f(&v2) < f(&v1) { v1 } else { v2 } } @@ -1739,9 +1777,10 @@ pub fn max_by_key K, K: Ord>(v1: T, v2: T, mut f: F) -> T { #[inline] #[must_use] #[unstable(feature = "cmp_minmax", issue = "115939")] -pub fn minmax(v1: T, v2: T) -> [T; 2] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +pub const fn minmax(v1: T, v2: T) -> [T; 2] where - T: Ord, + T: [const] Ord, { if v2 < v1 { [v2, v1] } else { [v1, v2] } } @@ -1773,9 +1812,10 @@ where #[inline] #[must_use] #[unstable(feature = "cmp_minmax", issue = "115939")] -pub fn minmax_by(v1: T, v2: T, compare: F) -> [T; 2] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +pub const fn minmax_by(v1: T, v2: T, compare: F) -> [T; 2] where - F: FnOnce(&T, &T) -> Ordering, + F: [const] FnOnce(&T, &T) -> Ordering, { if compare(&v1, &v2).is_le() { [v1, v2] } else { [v2, v1] } } @@ -1801,10 +1841,11 @@ where #[inline] #[must_use] #[unstable(feature = "cmp_minmax", issue = "115939")] -pub fn minmax_by_key(v1: T, v2: T, mut f: F) -> [T; 2] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +pub const fn minmax_by_key(v1: T, v2: T, mut f: F) -> [T; 2] where - F: FnMut(&T) -> K, - K: Ord, + F: [const] FnMut(&T) -> K + [const] Destruct, + K: [const] Ord + [const] Destruct, { if f(&v2) < f(&v1) { [v2, v1] } else { [v1, v2] } } @@ -1830,7 +1871,8 @@ mod impls { } #[stable(feature = "rust1", since = "1.0.0")] - impl PartialEq for () { + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl const PartialEq for () { #[inline] fn eq(&self, _other: &()) -> bool { true @@ -1848,7 +1890,8 @@ mod impls { macro_rules! eq_impl { ($($t:ty)*) => ($( #[stable(feature = "rust1", since = "1.0.0")] - impl Eq for $t {} + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl const Eq for $t {} )*) } @@ -1896,7 +1939,8 @@ mod impls { macro_rules! partial_ord_impl { ($($t:ty)*) => ($( #[stable(feature = "rust1", since = "1.0.0")] - impl PartialOrd for $t { + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl const PartialOrd for $t { #[inline] fn partial_cmp(&self, other: &Self) -> Option { match (*self <= *other, *self >= *other) { @@ -1913,7 +1957,8 @@ mod impls { } #[stable(feature = "rust1", since = "1.0.0")] - impl PartialOrd for () { + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl const PartialOrd for () { #[inline] fn partial_cmp(&self, _: &()) -> Option { Some(Equal) @@ -1921,7 +1966,8 @@ mod impls { } #[stable(feature = "rust1", since = "1.0.0")] - impl PartialOrd for bool { + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl const PartialOrd for bool { #[inline] fn partial_cmp(&self, other: &bool) -> Option { Some(self.cmp(other)) @@ -1935,7 +1981,8 @@ mod impls { macro_rules! ord_impl { ($($t:ty)*) => ($( #[stable(feature = "rust1", since = "1.0.0")] - impl PartialOrd for $t { + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl const PartialOrd for $t { #[inline] fn partial_cmp(&self, other: &Self) -> Option { Some(crate::intrinsics::three_way_compare(*self, *other)) @@ -1945,7 +1992,8 @@ mod impls { } #[stable(feature = "rust1", since = "1.0.0")] - impl Ord for $t { + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl const Ord for $t { #[inline] fn cmp(&self, other: &Self) -> Ordering { crate::intrinsics::three_way_compare(*self, *other) @@ -1955,7 +2003,8 @@ mod impls { } #[stable(feature = "rust1", since = "1.0.0")] - impl Ord for () { + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl const Ord for () { #[inline] fn cmp(&self, _other: &()) -> Ordering { Equal @@ -1963,7 +2012,8 @@ mod impls { } #[stable(feature = "rust1", since = "1.0.0")] - impl Ord for bool { + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl const Ord for bool { #[inline] fn cmp(&self, other: &bool) -> Ordering { // Casting to i8's and converting the difference to an Ordering generates @@ -1998,7 +2048,8 @@ mod impls { ord_impl! { char usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } #[unstable(feature = "never_type", issue = "35121")] - impl PartialEq for ! { + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl const PartialEq for ! { #[inline] fn eq(&self, _: &!) -> bool { *self @@ -2006,10 +2057,12 @@ mod impls { } #[unstable(feature = "never_type", issue = "35121")] - impl Eq for ! {} + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl const Eq for ! {} #[unstable(feature = "never_type", issue = "35121")] - impl PartialOrd for ! { + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl const PartialOrd for ! { #[inline] fn partial_cmp(&self, _: &!) -> Option { *self @@ -2017,7 +2070,8 @@ mod impls { } #[unstable(feature = "never_type", issue = "35121")] - impl Ord for ! { + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl const Ord for ! { #[inline] fn cmp(&self, _: &!) -> Ordering { *self @@ -2042,9 +2096,10 @@ mod impls { } } #[stable(feature = "rust1", since = "1.0.0")] - impl PartialOrd<&B> for &A + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl const PartialOrd<&B> for &A where - A: PartialOrd, + A: [const] PartialOrd, { #[inline] fn partial_cmp(&self, other: &&B) -> Option { @@ -2084,9 +2139,10 @@ mod impls { } } #[stable(feature = "rust1", since = "1.0.0")] - impl Ord for &A + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl const Ord for &A where - A: Ord, + A: [const] Ord, { #[inline] fn cmp(&self, other: &Self) -> Ordering { @@ -2094,7 +2150,8 @@ mod impls { } } #[stable(feature = "rust1", since = "1.0.0")] - impl Eq for &A where A: Eq {} + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl const Eq for &A where A: [const] Eq {} // &mut pointers @@ -2114,9 +2171,10 @@ mod impls { } } #[stable(feature = "rust1", since = "1.0.0")] - impl PartialOrd<&mut B> for &mut A + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl const PartialOrd<&mut B> for &mut A where - A: PartialOrd, + A: [const] PartialOrd, { #[inline] fn partial_cmp(&self, other: &&mut B) -> Option { @@ -2156,9 +2214,10 @@ mod impls { } } #[stable(feature = "rust1", since = "1.0.0")] - impl Ord for &mut A + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl const Ord for &mut A where - A: Ord, + A: [const] Ord, { #[inline] fn cmp(&self, other: &Self) -> Ordering { @@ -2166,7 +2225,8 @@ mod impls { } } #[stable(feature = "rust1", since = "1.0.0")] - impl Eq for &mut A where A: Eq {} + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl const Eq for &mut A where A: [const] Eq {} #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs index a3cfd85974a4..89cda30c0303 100644 --- a/library/core/src/convert/mod.rs +++ b/library/core/src/convert/mod.rs @@ -963,17 +963,20 @@ impl const PartialEq for Infallible { } #[stable(feature = "convert_infallible", since = "1.34.0")] -impl Eq for Infallible {} +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl const Eq for Infallible {} #[stable(feature = "convert_infallible", since = "1.34.0")] -impl PartialOrd for Infallible { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl const PartialOrd for Infallible { fn partial_cmp(&self, _other: &Self) -> Option { match *self {} } } #[stable(feature = "convert_infallible", since = "1.34.0")] -impl Ord for Infallible { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl const Ord for Infallible { fn cmp(&self, _other: &Self) -> crate::cmp::Ordering { match *self {} } diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 0f38081c355d..1b7c28bb95aa 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -4,7 +4,7 @@ use super::{IntErrorKind, ParseIntError}; use crate::clone::UseCloned; use crate::cmp::Ordering; use crate::hash::{Hash, Hasher}; -use crate::marker::{Freeze, StructuralPartialEq}; +use crate::marker::{Destruct, Freeze, StructuralPartialEq}; use crate::ops::{BitOr, BitOrAssign, Div, DivAssign, Neg, Rem, RemAssign}; use crate::panic::{RefUnwindSafe, UnwindSafe}; use crate::str::FromStr; @@ -220,12 +220,14 @@ where impl StructuralPartialEq for NonZero where T: ZeroablePrimitive + StructuralPartialEq {} #[stable(feature = "nonzero", since = "1.28.0")] -impl Eq for NonZero where T: ZeroablePrimitive + Eq {} +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl const Eq for NonZero where T: ZeroablePrimitive + [const] Eq {} #[stable(feature = "nonzero", since = "1.28.0")] -impl PartialOrd for NonZero +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl const PartialOrd for NonZero where - T: ZeroablePrimitive + PartialOrd, + T: ZeroablePrimitive + [const] PartialOrd, { #[inline] fn partial_cmp(&self, other: &Self) -> Option { @@ -254,9 +256,12 @@ where } #[stable(feature = "nonzero", since = "1.28.0")] -impl Ord for NonZero +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl const Ord for NonZero where - T: ZeroablePrimitive + Ord, + // FIXME(const_hack): the T: ~const Destruct should be inferred from the Self: ~const Destruct. + // See https://github.com/rust-lang/rust/issues/144207 + T: ZeroablePrimitive + [const] Ord + [const] Destruct, { #[inline] fn cmp(&self, other: &Self) -> Ordering { diff --git a/library/core/src/ops/control_flow.rs b/library/core/src/ops/control_flow.rs index 73b74d53323f..b760a7c4e21e 100644 --- a/library/core/src/ops/control_flow.rs +++ b/library/core/src/ops/control_flow.rs @@ -83,7 +83,8 @@ use crate::{convert, ops}; #[must_use] // ControlFlow should not implement PartialOrd or Ord, per RFC 3058: // https://rust-lang.github.io/rfcs/3058-try-trait-v2.html#traits-for-controlflow -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Copy, Debug, Hash)] +#[derive_const(Clone, PartialEq, Eq)] pub enum ControlFlow { /// Move on to the next phase of the operation as normal. #[stable(feature = "control_flow_enum_type", since = "1.55.0")] diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 198636c67d0b..886d581b0a6b 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -585,7 +585,8 @@ use crate::{cmp, convert, hint, mem, slice}; /// The `Option` type. See [the module level documentation](self) for more. #[doc(search_unbox)] -#[derive(Copy, Eq, Debug, Hash)] +#[derive(Copy, Debug, Hash)] +#[derive_const(Eq)] #[rustc_diagnostic_item = "Option"] #[lang = "Option"] #[stable(feature = "rust1", since = "1.0.0")] @@ -2363,7 +2364,8 @@ impl const PartialEq for Option { // https://github.com/rust-lang/rust/issues/49892, although still // not optimal. #[stable(feature = "rust1", since = "1.0.0")] -impl PartialOrd for Option { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl const PartialOrd for Option { #[inline] fn partial_cmp(&self, other: &Self) -> Option { match (self, other) { @@ -2376,7 +2378,8 @@ impl PartialOrd for Option { } #[stable(feature = "rust1", since = "1.0.0")] -impl Ord for Option { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl const Ord for Option { #[inline] fn cmp(&self, other: &Self) -> cmp::Ordering { match (self, other) { diff --git a/library/core/src/range.rs b/library/core/src/range.rs index 8c49123fdaf5..a096a8ceafc8 100644 --- a/library/core/src/range.rs +++ b/library/core/src/range.rs @@ -49,7 +49,8 @@ pub use crate::ops::{Bound, IntoBounds, OneSidedRange, RangeBounds, RangeFull, R /// assert_eq!(3 + 4 + 5, Range::from(3..6).into_iter().sum()); /// ``` #[lang = "RangeCopy"] -#[derive(Clone, Copy, Default, PartialEq, Eq, Hash)] +#[derive(Copy, Hash)] +#[derive_const(Clone, Default, PartialEq, Eq)] #[unstable(feature = "new_range_api", issue = "125687")] pub struct Range { /// The lower bound of the range (inclusive). @@ -424,7 +425,8 @@ impl const From> for RangeInclusive { /// assert_eq!(2 + 3 + 4, RangeFrom::from(2..).into_iter().take(3).sum()); /// ``` #[lang = "RangeFromCopy"] -#[derive(Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Copy, Hash)] +#[derive_const(Clone, PartialEq, Eq)] #[unstable(feature = "new_range_api", issue = "125687")] pub struct RangeFrom { /// The lower bound of the range (inclusive). diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 7dffab9b316f..5c1f64bfe14a 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -542,7 +542,8 @@ use crate::{convert, fmt, hint}; /// /// See the [module documentation](self) for details. #[doc(search_unbox)] -#[derive(Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] +#[derive(Copy, Debug, Hash)] +#[derive_const(PartialEq, PartialOrd, Eq, Ord)] #[must_use = "this `Result` may be an `Err` variant, which should be handled"] #[rustc_diagnostic_item = "Result"] #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/core/src/slice/cmp.rs b/library/core/src/slice/cmp.rs index 0d8013069840..103630aba0f7 100644 --- a/library/core/src/slice/cmp.rs +++ b/library/core/src/slice/cmp.rs @@ -23,7 +23,8 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl Eq for [T] {} +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl const Eq for [T] {} /// Implements comparison of slices [lexicographically](Ord#lexicographical-comparison). #[stable(feature = "rust1", since = "1.0.0")] @@ -34,7 +35,7 @@ impl Ord for [T] { } #[inline] -fn as_underlying(x: ControlFlow) -> u8 { +const fn as_underlying(x: ControlFlow) -> u8 { // SAFETY: This will only compile if `bool` and `ControlFlow` have the same // size (which isn't guaranteed but this is libcore). Because they have the same // size, it's a niched implementation, which in one byte means there can't be @@ -154,12 +155,16 @@ where } #[doc(hidden)] +#[const_trait] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] // intermediate trait for specialization of slice's PartialOrd trait SlicePartialOrd: Sized { fn partial_compare(left: &[Self], right: &[Self]) -> Option; } #[doc(hidden)] +#[const_trait] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] // intermediate trait for specialization of slice's PartialOrd chaining methods trait SliceChain: Sized { fn chaining_lt(left: &[Self], right: &[Self]) -> ControlFlow; @@ -231,14 +236,17 @@ where } */ -impl SlicePartialOrd for A { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl const SlicePartialOrd for A { fn partial_compare(left: &[A], right: &[A]) -> Option { Some(SliceOrd::compare(left, right)) } } #[rustc_specialization_trait] -trait AlwaysApplicableOrd: SliceOrd + Ord {} +#[const_trait] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +trait AlwaysApplicableOrd: [const] SliceOrd + [const] Ord {} macro_rules! always_applicable_ord { ($([$($p:tt)*] $t:ty,)*) => { @@ -257,6 +265,8 @@ always_applicable_ord! { } #[doc(hidden)] +#[const_trait] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] // intermediate trait for specialization of slice's Ord trait SliceOrd: Sized { fn compare(left: &[Self], right: &[Self]) -> Ordering; @@ -282,17 +292,24 @@ impl SliceOrd for A { /// * For every `x` and `y` of this type, `Ord(x, y)` must return the same /// value as `Ord::cmp(transmute::<_, u8>(x), transmute::<_, u8>(y))`. #[rustc_specialization_trait] -unsafe trait UnsignedBytewiseOrd: Ord {} +#[const_trait] +unsafe trait UnsignedBytewiseOrd: [const] Ord {} -unsafe impl UnsignedBytewiseOrd for bool {} -unsafe impl UnsignedBytewiseOrd for u8 {} -unsafe impl UnsignedBytewiseOrd for NonZero {} -unsafe impl UnsignedBytewiseOrd for Option> {} -unsafe impl UnsignedBytewiseOrd for ascii::Char {} +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +unsafe impl const UnsignedBytewiseOrd for bool {} +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +unsafe impl const UnsignedBytewiseOrd for u8 {} +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +unsafe impl const UnsignedBytewiseOrd for NonZero {} +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +unsafe impl const UnsignedBytewiseOrd for Option> {} +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +unsafe impl const UnsignedBytewiseOrd for ascii::Char {} // `compare_bytes` compares a sequence of unsigned bytes lexicographically, so // use it if the requirements for `UnsignedBytewiseOrd` are fulfilled. -impl SliceOrd for A { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl const SliceOrd for A { #[inline] fn compare(left: &[Self], right: &[Self]) -> Ordering { // Since the length of a slice is always less than or equal to @@ -317,7 +334,9 @@ impl SliceOrd for A { } // Don't generate our own chaining loops for `memcmp`-able things either. -impl SliceChain for A { + +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl const SliceChain for A { #[inline] fn chaining_lt(left: &[Self], right: &[Self]) -> ControlFlow { match SliceOrd::compare(left, right) { diff --git a/library/core/src/str/traits.rs b/library/core/src/str/traits.rs index 5adae62b7e65..a7cc943994c5 100644 --- a/library/core/src/str/traits.rs +++ b/library/core/src/str/traits.rs @@ -32,7 +32,8 @@ impl const PartialEq for str { } #[stable(feature = "rust1", since = "1.0.0")] -impl Eq for str {} +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl const Eq for str {} /// Implements comparison operations on strings. /// diff --git a/library/core/src/tuple.rs b/library/core/src/tuple.rs index 2cdee1803a9a..3892f8310769 100644 --- a/library/core/src/tuple.rs +++ b/library/core/src/tuple.rs @@ -23,7 +23,8 @@ macro_rules! tuple_impls { maybe_tuple_doc! { $($T)+ @ #[stable(feature = "rust1", since = "1.0.0")] - impl<$($T: PartialEq),+> PartialEq for ($($T,)+) { + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl<$($T: [const] PartialEq),+> const PartialEq for ($($T,)+) { #[inline] fn eq(&self, other: &($($T,)+)) -> bool { $( ${ignore($T)} self.${index()} == other.${index()} )&&+ @@ -38,7 +39,8 @@ macro_rules! tuple_impls { maybe_tuple_doc! { $($T)+ @ #[stable(feature = "rust1", since = "1.0.0")] - impl<$($T: Eq),+> Eq for ($($T,)+) + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl<$($T: [const] Eq),+> const Eq for ($($T,)+) {} } @@ -66,7 +68,8 @@ macro_rules! tuple_impls { maybe_tuple_doc! { $($T)+ @ #[stable(feature = "rust1", since = "1.0.0")] - impl<$($T: PartialOrd),+> PartialOrd for ($($T,)+) + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl<$($T: [const] PartialOrd),+> const PartialOrd for ($($T,)+) { #[inline] fn partial_cmp(&self, other: &($($T,)+)) -> Option { @@ -110,7 +113,8 @@ macro_rules! tuple_impls { maybe_tuple_doc! { $($T)+ @ #[stable(feature = "rust1", since = "1.0.0")] - impl<$($T: Ord),+> Ord for ($($T,)+) + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl<$($T: [const] Ord),+> const Ord for ($($T,)+) { #[inline] fn cmp(&self, other: &($($T,)+)) -> Ordering { diff --git a/library/coretests/tests/cmp.rs b/library/coretests/tests/cmp.rs index 6c4e2146f914..55e35a4a7250 100644 --- a/library/coretests/tests/cmp.rs +++ b/library/coretests/tests/cmp.rs @@ -215,19 +215,18 @@ fn cmp_default() { assert_eq!(Fool(false), Fool(true)); } -/* FIXME(#110395) mod const_cmp { use super::*; struct S(i32); - impl PartialEq for S { + impl const PartialEq for S { fn eq(&self, other: &Self) -> bool { self.0 == other.0 } } - impl PartialOrd for S { + impl const PartialOrd for S { fn partial_cmp(&self, other: &Self) -> Option { let ret = match (self.0, other.0) { (a, b) if a > b => Ordering::Greater, @@ -247,4 +246,3 @@ mod const_cmp { const _: () = assert!(S(0) < S(1)); const _: () = assert!(S(1) > S(0)); } -*/ diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 1bdaa6965f64..04fa0aeba650 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -15,6 +15,7 @@ #![feature(cfg_target_has_reliable_f16_f128)] #![feature(char_max_len)] #![feature(clone_to_uninit)] +#![feature(const_cmp)] #![feature(const_convert)] #![feature(const_destruct)] #![feature(const_eval_select)] diff --git a/tests/ui/consts/const_cmp_type_id.rs b/tests/ui/consts/const_cmp_type_id.rs index ff44876c5c49..dc0701d3b1d6 100644 --- a/tests/ui/consts/const_cmp_type_id.rs +++ b/tests/ui/consts/const_cmp_type_id.rs @@ -9,8 +9,7 @@ fn main() { assert!(TypeId::of::() == TypeId::of::()); assert!(TypeId::of::<()>() != TypeId::of::()); let _a = TypeId::of::() < TypeId::of::(); - //~^ ERROR: cannot call non-const operator in constants - // can't assert `_a` because it is not deterministic - // FIXME(const_trait_impl) make it pass + //~^ ERROR: the trait bound `TypeId: const PartialOrd` is not satisfied + // FIXME(const_trait_impl) make it pass; requires const comparison of pointers (#53020) } } diff --git a/tests/ui/consts/const_cmp_type_id.stderr b/tests/ui/consts/const_cmp_type_id.stderr index 05b94caef79e..62a1677c0d93 100644 --- a/tests/ui/consts/const_cmp_type_id.stderr +++ b/tests/ui/consts/const_cmp_type_id.stderr @@ -1,13 +1,9 @@ -error[E0015]: cannot call non-const operator in constants +error[E0277]: the trait bound `TypeId: const PartialOrd` is not satisfied --> $DIR/const_cmp_type_id.rs:11:18 | LL | let _a = TypeId::of::() < TypeId::of::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: impl defined here, but it is not `const` - --> $SRC_DIR/core/src/any.rs:LL:COL - = note: calls in constants are limited to constant functions, tuple structs and tuple variants error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0015`. +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/consts/fn_trait_refs.rs b/tests/ui/consts/fn_trait_refs.rs index e475c0a1b6fd..e4a62e18c7b5 100644 --- a/tests/ui/consts/fn_trait_refs.rs +++ b/tests/ui/consts/fn_trait_refs.rs @@ -1,6 +1,5 @@ -//@ known-bug: #110395 +//@ check-pass -#![feature(const_fn_trait_ref_impls)] #![feature(fn_traits)] #![feature(unboxed_closures)] #![feature(const_trait_impl)] diff --git a/tests/ui/consts/fn_trait_refs.stderr b/tests/ui/consts/fn_trait_refs.stderr deleted file mode 100644 index bbe0714801c4..000000000000 --- a/tests/ui/consts/fn_trait_refs.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error[E0635]: unknown feature `const_fn_trait_ref_impls` - --> $DIR/fn_trait_refs.rs:3:12 - | -LL | #![feature(const_fn_trait_ref_impls)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0277]: the trait bound `(i32, i32, i32): const PartialEq` is not satisfied - --> $DIR/fn_trait_refs.rs:71:17 - | -LL | assert!(test_one == (1, 1, 1)); - | ^^^^^^^^^^^^^^^^^^^^^ - -error[E0277]: the trait bound `(i32, i32): const PartialEq` is not satisfied - --> $DIR/fn_trait_refs.rs:74:17 - | -LL | assert!(test_two == (2, 2)); - | ^^^^^^^^^^^^^^^^^^ - -error: aborting due to 3 previous errors - -Some errors have detailed explanations: E0277, E0635. -For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/const-impl-trait.rs b/tests/ui/traits/const-traits/const-impl-trait.rs index da28d9a47c31..c89aaa62d992 100644 --- a/tests/ui/traits/const-traits/const-impl-trait.rs +++ b/tests/ui/traits/const-traits/const-impl-trait.rs @@ -1,7 +1,5 @@ +//@ check-pass //@ compile-flags: -Znext-solver -//@ known-bug: #110395 - -// Broken until `(): const PartialEq` #![allow(incomplete_features)] #![feature(const_trait_impl, const_cmp, const_destruct)] diff --git a/tests/ui/traits/const-traits/const-impl-trait.stderr b/tests/ui/traits/const-traits/const-impl-trait.stderr deleted file mode 100644 index cbb68d8c9839..000000000000 --- a/tests/ui/traits/const-traits/const-impl-trait.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error[E0277]: the trait bound `(): const PartialEq` is not satisfied - --> $DIR/const-impl-trait.rs:34:17 - | -LL | assert!(cmp(&())); - | --- ^^^ - | | - | required by a bound introduced by this call - | -note: required by a bound in `cmp` - --> $DIR/const-impl-trait.rs:11:23 - | -LL | const fn cmp(a: &impl [const] PartialEq) -> bool { - | ^^^^^^^^^^^^^^^^^ required by this bound in `cmp` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/const_derives/derive-const-use.rs b/tests/ui/traits/const-traits/const_derives/derive-const-use.rs index 1a3012de06f8..78c25ccd6e59 100644 --- a/tests/ui/traits/const-traits/const_derives/derive-const-use.rs +++ b/tests/ui/traits/const-traits/const_derives/derive-const-use.rs @@ -1,5 +1,4 @@ -//@ known-bug: #110395 - +//@ check-pass #![feature(const_trait_impl, const_default, const_cmp, derive_const)] pub struct A; diff --git a/tests/ui/traits/const-traits/const_derives/derive-const-use.stderr b/tests/ui/traits/const-traits/const_derives/derive-const-use.stderr deleted file mode 100644 index 4ea11a0c7ed9..000000000000 --- a/tests/ui/traits/const-traits/const_derives/derive-const-use.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0277]: the trait bound `(): [const] PartialEq` is not satisfied - --> $DIR/derive-const-use.rs:16:14 - | -LL | #[derive_const(Default, PartialEq)] - | --------- in this derive macro expansion -LL | pub struct S((), A); - | ^^ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. From 889be7860b0a0cbe82615787d4823bd57af40040 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Wed, 10 Sep 2025 11:52:26 +0200 Subject: [PATCH 419/682] sort array trait implementation suggestions correctly --- .../traits/fulfillment_errors.rs | 18 +++++++++++++++--- .../ui/consts/missing-larger-array-impl.stderr | 10 +++++----- .../issue-71394-no-from-impl.stderr | 6 +++--- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index c82043f02223..149f5e638b1a 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -27,8 +27,8 @@ use rustc_middle::ty::print::{ with_forced_trimmed_paths, }; use rustc_middle::ty::{ - self, TraitRef, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, - Upcast, + self, GenericArgKind, TraitRef, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitableExt, Upcast, }; use rustc_middle::{bug, span_bug}; use rustc_span::{BytePos, DUMMY_SP, STDLIB_STABLE_CRATES, Span, Symbol, sym}; @@ -2316,7 +2316,19 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { cand }) .collect(); - impl_candidates.sort_by_key(|cand| (cand.similarity, cand.trait_ref.to_string())); + impl_candidates.sort_by_key(|cand| { + // When suggesting array types, sort them by the length of the array, not lexicographically (#135098) + let len = if let GenericArgKind::Type(ty) = cand.trait_ref.args[0].kind() + && let ty::Array(_, len) = ty.kind() + { + // Deprioritize suggestions for parameterized arrays. + len.try_to_target_usize(self.tcx).unwrap_or(u64::MAX) + } else { + 0 + }; + + (cand.similarity, len, cand.trait_ref.to_string()) + }); let mut impl_candidates: Vec<_> = impl_candidates.into_iter().map(|cand| cand.trait_ref).collect(); impl_candidates.dedup(); diff --git a/tests/ui/consts/missing-larger-array-impl.stderr b/tests/ui/consts/missing-larger-array-impl.stderr index ff4fa36d684c..33d7a46339b2 100644 --- a/tests/ui/consts/missing-larger-array-impl.stderr +++ b/tests/ui/consts/missing-larger-array-impl.stderr @@ -8,11 +8,11 @@ LL | <[X; 35] as Default>::default(); &[T] &mut [T] [T; 0] - [T; 10] - [T; 11] - [T; 12] - [T; 13] - [T; 14] + [T; 1] + [T; 2] + [T; 3] + [T; 4] + [T; 5] and 27 others error: aborting due to 1 previous error diff --git a/tests/ui/suggestions/issue-71394-no-from-impl.stderr b/tests/ui/suggestions/issue-71394-no-from-impl.stderr index 31f8f1d455ab..9e068c311ae0 100644 --- a/tests/ui/suggestions/issue-71394-no-from-impl.stderr +++ b/tests/ui/suggestions/issue-71394-no-from-impl.stderr @@ -5,14 +5,14 @@ LL | let _: &[i8] = data.into(); | ^^^^ the trait `From<&[u8]>` is not implemented for `&[i8]` | = help: the following other types implement trait `From`: - `[T; 10]` implements `From<(T, T, T, T, T, T, T, T, T, T)>` - `[T; 11]` implements `From<(T, T, T, T, T, T, T, T, T, T, T)>` - `[T; 12]` implements `From<(T, T, T, T, T, T, T, T, T, T, T, T)>` `[T; 1]` implements `From<(T,)>` `[T; 2]` implements `From<(T, T)>` `[T; 3]` implements `From<(T, T, T)>` `[T; 4]` implements `From<(T, T, T, T)>` `[T; 5]` implements `From<(T, T, T, T, T)>` + `[T; 6]` implements `From<(T, T, T, T, T, T)>` + `[T; 7]` implements `From<(T, T, T, T, T, T, T)>` + `[T; 8]` implements `From<(T, T, T, T, T, T, T, T)>` and 6 others = note: required for `&[u8]` to implement `Into<&[i8]>` From fefc9793740e88585a28ed966100718df9dbe522 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20Neusch=C3=A4fer?= Date: Fri, 12 Sep 2025 13:31:49 +0200 Subject: [PATCH 420/682] bootstrap: Show target in "No such target exists" message This makes it a little easier to pinpoint the issue. --- src/bootstrap/src/core/sanity.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index 04cf63f1c6d4..68202500d979 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -287,7 +287,7 @@ than building it. if !has_target { panic!( - "No such target exists in the target list,\n\ + "{target_str}: No such target exists in the target list,\n\ make sure to correctly specify the location \ of the JSON specification file \ for custom targets!\n\ From 8b5335cbba6f68b1e3727e1de6095d2e126f08e0 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 12 Sep 2025 13:34:22 +0200 Subject: [PATCH 421/682] disable broken parts of CI for now --- src/tools/miri/.github/workflows/ci.yml | 33 +++++++++++++------------ 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index c0fed96d4e6e..e1a948c92faa 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -31,21 +31,22 @@ jobs: os: ubuntu-24.04-arm multiarch: armhf gcc_cross: arm-linux-gnueabihf - - host_target: riscv64gc-unknown-linux-gnu - os: ubuntu-latest - multiarch: riscv64 - gcc_cross: riscv64-linux-gnu - qemu: true - - host_target: s390x-unknown-linux-gnu - os: ubuntu-latest - multiarch: s390x - gcc_cross: s390x-linux-gnu - qemu: true - - host_target: powerpc64le-unknown-linux-gnu - os: ubuntu-latest - multiarch: ppc64el - gcc_cross: powerpc64le-linux-gnu - qemu: true + # Disabled due to Ubuntu repo trouble + # - host_target: riscv64gc-unknown-linux-gnu + # os: ubuntu-latest + # multiarch: riscv64 + # gcc_cross: riscv64-linux-gnu + # qemu: true + # - host_target: s390x-unknown-linux-gnu + # os: ubuntu-latest + # multiarch: s390x + # gcc_cross: s390x-linux-gnu + # qemu: true + # - host_target: powerpc64le-unknown-linux-gnu + # os: ubuntu-latest + # multiarch: ppc64el + # gcc_cross: powerpc64le-linux-gnu + # qemu: true - host_target: aarch64-apple-darwin os: macos-latest - host_target: i686-pc-windows-msvc @@ -67,7 +68,7 @@ jobs: - name: install multiarch if: ${{ matrix.multiarch != '' }} run: | - # s390x, ppc64el need Ubuntu Ports to be in the mirror list + # s390x, ppc64el, riscv64 need Ubuntu Ports to be in the mirror list sudo bash -c "echo 'https://ports.ubuntu.com/ priority:4' >> /etc/apt/apt-mirrors.txt" # Add architecture sudo dpkg --add-architecture ${{ matrix.multiarch }} From 221eb1f0d51a97ec6ccb67427635370797d37225 Mon Sep 17 00:00:00 2001 From: Tsukasa OI Date: Fri, 12 Sep 2025 11:50:25 +0000 Subject: [PATCH 422/682] intrinsic-test: Make Clippy happy --- library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs | 2 +- library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs b/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs index b019abab2134..65c179ef0d08 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs @@ -113,7 +113,7 @@ fn json_to_intrinsic( Ok(Intrinsic { name, arguments, - results: results, + results, arch_tags: intr.architectures, }) } diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs index 312cbee692a5..c6b964a9ce4e 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs @@ -253,7 +253,7 @@ pub fn generate_rust_test_loop( } /// Generate the specializations (unique sequences of const-generic arguments) for this intrinsic. -fn generate_rust_specializations<'a>( +fn generate_rust_specializations( constraints: &mut impl Iterator>, ) -> Vec> { let mut specializations = vec![vec![]]; From a3b7aad20f6ff2a4af0e96c543ac300650c97d5e Mon Sep 17 00:00:00 2001 From: Tsukasa OI Date: Fri, 12 Sep 2025 11:50:51 +0000 Subject: [PATCH 423/682] stdarch-gen-arm: Make Clippy happy --- library/stdarch/crates/stdarch-gen-arm/src/load_store_tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/stdarch/crates/stdarch-gen-arm/src/load_store_tests.rs b/library/stdarch/crates/stdarch-gen-arm/src/load_store_tests.rs index 3f3bfed132c7..0f4de83dacb4 100644 --- a/library/stdarch/crates/stdarch-gen-arm/src/load_store_tests.rs +++ b/library/stdarch/crates/stdarch-gen-arm/src/load_store_tests.rs @@ -85,7 +85,7 @@ pub fn generate_load_store_tests( TokenStream::from_str(&PREAMBLE).map_err(|e| format!("Preamble is invalid: {e}"))?; // Only output manual tests for the SVE set let manual_tests = match &load_intrinsics[0].target_features[..] { - [s] if s == "sve" => TokenStream::from_str(&MANUAL_TESTS) + [s] if s == "sve" => TokenStream::from_str(MANUAL_TESTS) .map_err(|e| format!("Manual tests are invalid: {e}"))?, _ => quote!(), }; From 05133f2115940aa581df8831031ea77c2c7c99c8 Mon Sep 17 00:00:00 2001 From: Tsukasa OI Date: Fri, 12 Sep 2025 11:51:38 +0000 Subject: [PATCH 424/682] examples: Make Clippy happy --- library/stdarch/examples/hex.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/library/stdarch/examples/hex.rs b/library/stdarch/examples/hex.rs index 95ffbaf666ce..621f55bc0951 100644 --- a/library/stdarch/examples/hex.rs +++ b/library/stdarch/examples/hex.rs @@ -22,7 +22,6 @@ #![allow( clippy::unwrap_used, clippy::print_stdout, - clippy::unwrap_used, clippy::shadow_reuse, clippy::cast_possible_wrap, clippy::cast_ptr_alignment, From 8a574ca862493b1977749e1a6e3fb85b9356fad7 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 12 Sep 2025 20:07:07 +0800 Subject: [PATCH 425/682] Fix extra semicolon before else in let-stmt Example --- ```rust fn main() { let x = if true { () } $0 else {}; } ``` **Before this PR**: ```rust fn main() { let x = if true { () } else if $1 { $0 }; else {}; } ``` **After this PR**: ```rust fn main() { let x = if true { () } else if $1 { $0 } else {}; } ``` --- .../ide-completion/src/completions/keyword.rs | 40 +++++++++++++++++++ .../ide-completion/src/context/analysis.rs | 12 +++++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs index b3d770997ab0..6162d9837283 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs @@ -269,6 +269,46 @@ fn main() { "#, ); + check_edit( + "else if", + r#" +fn main() { + let x = if true { + () + } $0 else {}; +} +"#, + r#" +fn main() { + let x = if true { + () + } else if $1 { + $0 +} else {}; +} +"#, + ); + + check_edit( + "else if", + r#" +fn main() { + let x = if true { + () + } $0 else if true {}; +} +"#, + r#" +fn main() { + let x = if true { + () + } else if $1 { + $0 +} else if true {}; +} +"#, + ); + check_edit( "else", r#" diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 33b98a33cabf..b33a547dee97 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -970,6 +970,16 @@ fn classify_name_ref<'db>( let after_incomplete_let = |node: SyntaxNode| { prev_expr(node).and_then(|it| it.syntax().parent()).and_then(ast::LetStmt::cast) }; + let before_else_kw = |node: &SyntaxNode| { + node.parent() + .and_then(ast::ExprStmt::cast) + .filter(|stmt| stmt.semicolon_token().is_none()) + .and_then(|stmt| non_trivia_sibling(stmt.syntax().clone().into(), Direction::Next)) + .and_then(NodeOrToken::into_node) + .filter(|next| next.kind() == SyntaxKind::ERROR) + .and_then(|next| next.first_token()) + .is_some_and(|token| token.kind() == SyntaxKind::ELSE_KW) + }; // We do not want to generate path completions when we are sandwiched between an item decl signature and its body. // ex. trait Foo $0 {} @@ -1276,7 +1286,7 @@ fn classify_name_ref<'db>( .parent() .and_then(ast::LetStmt::cast) .is_some_and(|it| it.semicolon_token().is_none()) - || after_incomplete_let && incomplete_expr_stmt.unwrap_or(true); + || after_incomplete_let && incomplete_expr_stmt.unwrap_or(true) && !before_else_kw(it); let in_value = it.parent().and_then(Either::::cast).is_some(); let impl_ = fetch_immediate_impl(sema, original_file, expr.syntax()); From 225e8ff17b593c7621f639c7cd50de2d93c7aee0 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 12 Sep 2025 16:30:41 +0200 Subject: [PATCH 426/682] move _Unwind_RaiseException out of the frame_in_std section --- .../miri/src/shims/windows/foreign_items.rs | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index 7b13f1d90807..c3ca01bbf340 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -820,6 +820,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_int(length.strict_sub(1), dest)?; } + "_Unwind_RaiseException" => { + // This is not formally part of POSIX, but it is very wide-spread on POSIX systems. + // It was originally specified as part of the Itanium C++ ABI: + // https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#base-throw. + // MinGW implements _Unwind_RaiseException on top of SEH exceptions. + if this.tcx.sess.target.env != "gnu" { + throw_unsup_format!( + "`_Unwind_RaiseException` is not supported on non-MinGW Windows", + ); + } + // This function looks and behaves excatly like miri_start_unwind. + let [payload] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.handle_miri_start_unwind(payload)?; + return interp_ok(EmulateItemResult::NeedsUnwind); + } + // Incomplete shims that we "stub out" just to get pre-main initialization code to work. // These shims are enabled only when the caller is in the standard library. "GetProcessHeap" if this.frame_in_std() => { @@ -880,22 +896,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_null(dest)?; } - "_Unwind_RaiseException" => { - // This is not formally part of POSIX, but it is very wide-spread on POSIX systems. - // It was originally specified as part of the Itanium C++ ABI: - // https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#base-throw. - // MinGW implements _Unwind_RaiseException on top of SEH exceptions. - if this.tcx.sess.target.env != "gnu" { - throw_unsup_format!( - "`_Unwind_RaiseException` is not supported on non-MinGW Windows", - ); - } - // This function looks and behaves excatly like miri_start_unwind. - let [payload] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; - this.handle_miri_start_unwind(payload)?; - return interp_ok(EmulateItemResult::NeedsUnwind); - } - _ => return interp_ok(EmulateItemResult::NotSupported), } From baed55cceffaa3b5dd0754436b413fa9aef544af Mon Sep 17 00:00:00 2001 From: Zachary S Date: Fri, 12 Sep 2025 09:49:41 -0500 Subject: [PATCH 427/682] Remove unreachable unsized arg handling in `store_fn_arg/store_arg` in codegen --- compiler/rustc_codegen_gcc/src/intrinsic/mod.rs | 9 ++------- compiler/rustc_codegen_llvm/src/abi.rs | 11 +++-------- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index eb0a5336a1f1..84fa56cf9030 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -730,7 +730,7 @@ impl<'gcc, 'tcx> ArgAbiExt<'gcc, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { if self.is_sized_indirect() { OperandValue::Ref(PlaceValue::new_sized(val, self.layout.align.abi)).store(bx, dst) } else if self.is_unsized_indirect() { - bug!("unsized `ArgAbi` must be handled through `store_fn_arg`"); + bug!("unsized `ArgAbi` cannot be stored"); } else if let PassMode::Cast { ref cast, .. } = self.mode { // FIXME(eddyb): Figure out when the simpler Store is safe, clang // uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}. @@ -797,12 +797,7 @@ impl<'gcc, 'tcx> ArgAbiExt<'gcc, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { OperandValue::Pair(next(), next()).store(bx, dst); } PassMode::Indirect { meta_attrs: Some(_), .. } => { - let place_val = PlaceValue { - llval: next(), - llextra: Some(next()), - align: self.layout.align.abi, - }; - OperandValue::Ref(place_val).store(bx, dst); + bug!("unsized `ArgAbi` cannot be stored"); } PassMode::Direct(_) | PassMode::Indirect { meta_attrs: None, .. } diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index ac7583f56668..11be70411679 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -215,9 +215,9 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { let align = attrs.pointee_align.unwrap_or(self.layout.align.abi); OperandValue::Ref(PlaceValue::new_sized(val, align)).store(bx, dst); } - // Unsized indirect qrguments + // Unsized indirect arguments cannot be stored PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { - bug!("unsized `ArgAbi` must be handled through `store_fn_arg`"); + bug!("unsized `ArgAbi` cannot be stored"); } PassMode::Cast { cast, pad_i32: _ } => { // The ABI mandates that the value is passed as a different struct representation. @@ -272,12 +272,7 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { OperandValue::Pair(next(), next()).store(bx, dst); } PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { - let place_val = PlaceValue { - llval: next(), - llextra: Some(next()), - align: self.layout.align.abi, - }; - OperandValue::Ref(place_val).store(bx, dst); + bug!("unsized `ArgAbi` cannot be stored"); } PassMode::Direct(_) | PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } From 260dd6f7ea7dec25c98ef6299bf1730c7dcf40b3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 12 Sep 2025 17:04:59 +0200 Subject: [PATCH 428/682] make a basic hello world work on wasip2 --- src/tools/miri/README.md | 2 +- src/tools/miri/ci/ci.sh | 2 +- .../miri/src/shims/wasi/foreign_items.rs | 68 +++++++++++++++++++ 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index 517aa343a6d4..7094231b902f 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -220,7 +220,7 @@ degree documented below): - `solaris` / `illumos`: maintained by @devnexen. Supports the entire test suite. - `freebsd`: maintained by @YohDeadfall and @LorrensP-2158466. Supports the entire test suite. - `android`: **maintainer wanted**. Support very incomplete, but a basic "hello world" works. - - `wasi`: **maintainer wanted**. Support very incomplete, not even standard output works, but an empty `main` function works. + - `wasi`: **maintainer wanted**. Support very incomplete, but a basic "hello world" works. - For targets on other operating systems, Miri might fail before even reaching the `main` function. However, even for targets that we do support, the degree of support for accessing platform APIs diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index 6356e33f61e8..bcc110f648b9 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -153,7 +153,7 @@ case $HOST_TARGET in BASIC="empty_main integer heap_alloc libc-mem vec string btreemap" # ensures we have the basics: pre-main code, system allocator UNIX="hello panic/panic panic/unwind concurrency/simple atomic libc-mem libc-misc libc-random env num_cpus" # the things that are very similar across all Unixes, and hence easily supported there TEST_TARGET=aarch64-linux-android run_tests_minimal $BASIC $UNIX time hashmap random thread sync concurrency epoll eventfd - TEST_TARGET=wasm32-wasip2 run_tests_minimal $BASIC wasm + TEST_TARGET=wasm32-wasip2 run_tests_minimal $BASIC hello wasm TEST_TARGET=wasm32-unknown-unknown run_tests_minimal no_std empty_main wasm # this target doesn't really have std TEST_TARGET=thumbv7em-none-eabihf run_tests_minimal no_std ;; diff --git a/src/tools/miri/src/shims/wasi/foreign_items.rs b/src/tools/miri/src/shims/wasi/foreign_items.rs index bfcdbd8130d8..ffc02dc986f9 100644 --- a/src/tools/miri/src/shims/wasi/foreign_items.rs +++ b/src/tools/miri/src/shims/wasi/foreign_items.rs @@ -35,6 +35,74 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_pointer(res, dest)?; } + // Standard input/output + // FIXME: These shims are hacks that just get basic stdout/stderr working. We can't + // constrain them to "std" since std itself uses the wasi crate for this. + "get-stdout" => { + let [] = + this.check_shim_sig(shim_sig!(extern "C" fn() -> i32), link_name, abi, args)?; + this.write_scalar(Scalar::from_i32(1), dest)?; // POSIX FD number for stdout + } + "get-stderr" => { + let [] = + this.check_shim_sig(shim_sig!(extern "C" fn() -> i32), link_name, abi, args)?; + this.write_scalar(Scalar::from_i32(2), dest)?; // POSIX FD number for stderr + } + "[resource-drop]output-stream" => { + let [handle] = + this.check_shim_sig(shim_sig!(extern "C" fn(i32) -> ()), link_name, abi, args)?; + let handle = this.read_scalar(handle)?.to_i32()?; + + if !(handle == 1 || handle == 2) { + throw_unsup_format!("wasm output-stream: unsupported handle"); + } + // We don't actually close these FDs, so this is a NOP. + } + "[method]output-stream.blocking-write-and-flush" => { + let [handle, buf, len, ret_area] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *mut _, usize, *mut _) -> ()), + link_name, + abi, + args, + )?; + let handle = this.read_scalar(handle)?.to_i32()?; + let buf = this.read_pointer(buf)?; + let len = this.read_target_usize(len)?; + let ret_area = this.read_pointer(ret_area)?; + + if len > 4096 { + throw_unsup_format!( + "wasm output-stream.blocking-write-and-flush: buffer too big" + ); + } + let len = usize::try_from(len).unwrap(); + let Some(fd) = this.machine.fds.get(handle) else { + throw_unsup_format!( + "wasm output-stream.blocking-write-and-flush: unsupported handle" + ); + }; + fd.write( + this.machine.communicate(), + buf, + len, + this, + callback!( + @capture<'tcx> { + len: usize, + ret_area: Pointer, + } + |this, result: Result| { + if !matches!(result, Ok(l) if l == len) { + throw_unsup_format!("wasm output-stream.blocking-write-and-flush: returning errors is not supported"); + } + // 0 in the first byte of the ret_area indicates success. + let ret = this.ptr_to_mplace(ret_area, this.machine.layouts.u8); + this.write_null(&ret)?; + interp_ok(()) + }), + )?; + } + _ => return interp_ok(EmulateItemResult::NotSupported), } interp_ok(EmulateItemResult::NeedsReturn) From 5d8e41b656fa2802c8ab52d4c72ad67e8ff345c2 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 12 Sep 2025 17:16:38 +0200 Subject: [PATCH 429/682] Revert "Constify SystemTime methods" This reverts commit 7ce620dd7c6fc3371290b40a1ea28146f0d37031. The const-hacks introduces bugs, and they make the code harder to maintain. Let's wait until we can constify these functions without changing their implementation. --- library/std/src/lib.rs | 5 ---- library/std/src/sys/pal/hermit/time.rs | 32 +++++++-------------- library/std/src/sys/pal/sgx/time.rs | 15 +++------- library/std/src/sys/pal/solid/time.rs | 9 ++---- library/std/src/sys/pal/uefi/time.rs | 27 +++++------------ library/std/src/sys/pal/unix/time.rs | 30 ++++++------------- library/std/src/sys/pal/unsupported/time.rs | 15 +++------- library/std/src/sys/pal/wasip1/time.rs | 25 +++++----------- library/std/src/sys/pal/windows/time.rs | 30 +++++++------------ library/std/src/sys/pal/xous/time.rs | 15 +++------- library/std/src/time.rs | 30 ++++++------------- 11 files changed, 68 insertions(+), 165 deletions(-) diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 5725816c6003..33e3bf0c085d 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -284,7 +284,6 @@ #![feature(core_float_math)] #![feature(decl_macro)] #![feature(deprecated_suggestion)] -#![feature(derive_const)] #![feature(doc_cfg)] #![feature(doc_cfg_hide)] #![feature(doc_masked)] @@ -332,11 +331,7 @@ #![feature(cfg_select)] #![feature(char_internals)] #![feature(clone_to_uninit)] -#![feature(const_cmp)] #![feature(const_convert)] -#![feature(const_ops)] -#![feature(const_option_ops)] -#![feature(const_try)] #![feature(core_intrinsics)] #![feature(core_io_borrowed_buf)] #![feature(drop_guard)] diff --git a/library/std/src/sys/pal/hermit/time.rs b/library/std/src/sys/pal/hermit/time.rs index 89a427ab88ba..f76a5f96c875 100644 --- a/library/std/src/sys/pal/hermit/time.rs +++ b/library/std/src/sys/pal/hermit/time.rs @@ -25,15 +25,8 @@ impl Timespec { Timespec { t: timespec { tv_sec, tv_nsec } } } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - const fn sub_timespec(&self, other: &Timespec) -> Result { - // FIXME: const PartialOrd - let mut cmp = self.t.tv_sec - other.t.tv_sec; - if cmp == 0 { - cmp = self.t.tv_nsec as i64 - other.t.tv_nsec as i64; - } - - if cmp >= 0 { + fn sub_timespec(&self, other: &Timespec) -> Result { + if self >= other { Ok(if self.t.tv_nsec >= other.t.tv_nsec { Duration::new( (self.t.tv_sec - other.t.tv_sec) as u64, @@ -53,22 +46,20 @@ impl Timespec { } } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - const fn checked_add_duration(&self, other: &Duration) -> Option { + fn checked_add_duration(&self, other: &Duration) -> Option { let mut secs = self.t.tv_sec.checked_add_unsigned(other.as_secs())?; // Nano calculations can't overflow because nanos are <1B which fit // in a u32. - let mut nsec = other.subsec_nanos() + self.t.tv_nsec as u32; - if nsec >= NSEC_PER_SEC as u32 { - nsec -= NSEC_PER_SEC as u32; + let mut nsec = other.subsec_nanos() + u32::try_from(self.t.tv_nsec).unwrap(); + if nsec >= NSEC_PER_SEC.try_into().unwrap() { + nsec -= u32::try_from(NSEC_PER_SEC).unwrap(); secs = secs.checked_add(1)?; } Some(Timespec { t: timespec { tv_sec: secs, tv_nsec: nsec as _ } }) } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - const fn checked_sub_duration(&self, other: &Duration) -> Option { + fn checked_sub_duration(&self, other: &Duration) -> Option { let mut secs = self.t.tv_sec.checked_sub_unsigned(other.as_secs())?; // Similar to above, nanos can't overflow. @@ -222,18 +213,15 @@ impl SystemTime { SystemTime(time) } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn sub_time(&self, other: &SystemTime) -> Result { + pub fn sub_time(&self, other: &SystemTime) -> Result { self.0.sub_timespec(&other.0) } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn checked_add_duration(&self, other: &Duration) -> Option { + pub fn checked_add_duration(&self, other: &Duration) -> Option { Some(SystemTime(self.0.checked_add_duration(other)?)) } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn checked_sub_duration(&self, other: &Duration) -> Option { + pub fn checked_sub_duration(&self, other: &Duration) -> Option { Some(SystemTime(self.0.checked_sub_duration(other)?)) } } diff --git a/library/std/src/sys/pal/sgx/time.rs b/library/std/src/sys/pal/sgx/time.rs index 603dae952abd..db4cf2804bf1 100644 --- a/library/std/src/sys/pal/sgx/time.rs +++ b/library/std/src/sys/pal/sgx/time.rs @@ -32,22 +32,15 @@ impl SystemTime { SystemTime(usercalls::insecure_time()) } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn sub_time(&self, other: &SystemTime) -> Result { - // FIXME: ok_or_else with const closures - match self.0.checked_sub(other.0) { - Some(duration) => Ok(duration), - None => Err(other.0 - self.0), - } + pub fn sub_time(&self, other: &SystemTime) -> Result { + self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0) } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn checked_add_duration(&self, other: &Duration) -> Option { + pub fn checked_add_duration(&self, other: &Duration) -> Option { Some(SystemTime(self.0.checked_add(*other)?)) } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn checked_sub_duration(&self, other: &Duration) -> Option { + pub fn checked_sub_duration(&self, other: &Duration) -> Option { Some(SystemTime(self.0.checked_sub(*other)?)) } } diff --git a/library/std/src/sys/pal/solid/time.rs b/library/std/src/sys/pal/solid/time.rs index e35e60df1a0a..c39d715c6a6f 100644 --- a/library/std/src/sys/pal/solid/time.rs +++ b/library/std/src/sys/pal/solid/time.rs @@ -39,8 +39,7 @@ impl SystemTime { Self(t) } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn sub_time(&self, other: &SystemTime) -> Result { + pub fn sub_time(&self, other: &SystemTime) -> Result { if self.0 >= other.0 { Ok(Duration::from_secs((self.0 as u64).wrapping_sub(other.0 as u64))) } else { @@ -48,13 +47,11 @@ impl SystemTime { } } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn checked_add_duration(&self, other: &Duration) -> Option { + pub fn checked_add_duration(&self, other: &Duration) -> Option { Some(SystemTime(self.0.checked_add_unsigned(other.as_secs())?)) } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn checked_sub_duration(&self, other: &Duration) -> Option { + pub fn checked_sub_duration(&self, other: &Duration) -> Option { Some(SystemTime(self.0.checked_sub_unsigned(other.as_secs())?)) } } diff --git a/library/std/src/sys/pal/uefi/time.rs b/library/std/src/sys/pal/uefi/time.rs index 36ce3f7ef962..c6636626fd58 100644 --- a/library/std/src/sys/pal/uefi/time.rs +++ b/library/std/src/sys/pal/uefi/time.rs @@ -80,32 +80,19 @@ impl SystemTime { .unwrap_or_else(|| panic!("time not implemented on this platform")) } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn sub_time(&self, other: &SystemTime) -> Result { - // FIXME: ok_or_else with const closures - match self.0.checked_sub(other.0) { - Some(duration) => Ok(duration), - None => Err(other.0 - self.0), - } + pub fn sub_time(&self, other: &SystemTime) -> Result { + self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0) } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn checked_add_duration(&self, other: &Duration) -> Option { - let temp = self.0.checked_add(*other)?; + pub fn checked_add_duration(&self, other: &Duration) -> Option { + let temp = Self(self.0.checked_add(*other)?); // Check if can be represented in UEFI - // FIXME: const PartialOrd - let mut cmp = temp.as_secs() - MAX_UEFI_TIME.0.as_secs(); - if cmp == 0 { - cmp = temp.subsec_nanos() as u64 - MAX_UEFI_TIME.0.subsec_nanos() as u64; - } - - if cmp <= 0 { Some(SystemTime(temp)) } else { None } + if temp <= MAX_UEFI_TIME { Some(temp) } else { None } } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn checked_sub_duration(&self, other: &Duration) -> Option { - Some(SystemTime(self.0.checked_sub(*other)?)) + pub fn checked_sub_duration(&self, other: &Duration) -> Option { + self.0.checked_sub(*other).map(Self) } } diff --git a/library/std/src/sys/pal/unix/time.rs b/library/std/src/sys/pal/unix/time.rs index 328fe0bc9603..bd7f74fea6a9 100644 --- a/library/std/src/sys/pal/unix/time.rs +++ b/library/std/src/sys/pal/unix/time.rs @@ -38,18 +38,15 @@ impl SystemTime { SystemTime { t: Timespec::now(libc::CLOCK_REALTIME) } } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn sub_time(&self, other: &SystemTime) -> Result { + pub fn sub_time(&self, other: &SystemTime) -> Result { self.t.sub_timespec(&other.t) } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn checked_add_duration(&self, other: &Duration) -> Option { + pub fn checked_add_duration(&self, other: &Duration) -> Option { Some(SystemTime { t: self.t.checked_add_duration(other)? }) } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn checked_sub_duration(&self, other: &Duration) -> Option { + pub fn checked_sub_duration(&self, other: &Duration) -> Option { Some(SystemTime { t: self.t.checked_sub_duration(other)? }) } } @@ -136,15 +133,8 @@ impl Timespec { Timespec::new(t.tv_sec as i64, t.tv_nsec as i64).unwrap() } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn sub_timespec(&self, other: &Timespec) -> Result { - // FIXME: const PartialOrd - let mut cmp = self.tv_sec - other.tv_sec; - if cmp == 0 { - cmp = self.tv_nsec.as_inner() as i64 - other.tv_nsec.as_inner() as i64; - } - - if cmp >= 0 { + pub fn sub_timespec(&self, other: &Timespec) -> Result { + if self >= other { // NOTE(eddyb) two aspects of this `if`-`else` are required for LLVM // to optimize it into a branchless form (see also #75545): // @@ -179,8 +169,7 @@ impl Timespec { } } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn checked_add_duration(&self, other: &Duration) -> Option { + pub fn checked_add_duration(&self, other: &Duration) -> Option { let mut secs = self.tv_sec.checked_add_unsigned(other.as_secs())?; // Nano calculations can't overflow because nanos are <1B which fit @@ -190,11 +179,10 @@ impl Timespec { nsec -= NSEC_PER_SEC as u32; secs = secs.checked_add(1)?; } - Some(unsafe { Timespec::new_unchecked(secs, nsec as i64) }) + Some(unsafe { Timespec::new_unchecked(secs, nsec.into()) }) } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn checked_sub_duration(&self, other: &Duration) -> Option { + pub fn checked_sub_duration(&self, other: &Duration) -> Option { let mut secs = self.tv_sec.checked_sub_unsigned(other.as_secs())?; // Similar to above, nanos can't overflow. @@ -203,7 +191,7 @@ impl Timespec { nsec += NSEC_PER_SEC as i32; secs = secs.checked_sub(1)?; } - Some(unsafe { Timespec::new_unchecked(secs, nsec as i64) }) + Some(unsafe { Timespec::new_unchecked(secs, nsec.into()) }) } #[allow(dead_code)] diff --git a/library/std/src/sys/pal/unsupported/time.rs b/library/std/src/sys/pal/unsupported/time.rs index 0c3879170440..6d67b538a96b 100644 --- a/library/std/src/sys/pal/unsupported/time.rs +++ b/library/std/src/sys/pal/unsupported/time.rs @@ -31,22 +31,15 @@ impl SystemTime { panic!("time not implemented on this platform") } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn sub_time(&self, other: &SystemTime) -> Result { - // FIXME: ok_or_else with const closures - match self.0.checked_sub(other.0) { - Some(duration) => Ok(duration), - None => Err(other.0 - self.0), - } + pub fn sub_time(&self, other: &SystemTime) -> Result { + self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0) } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn checked_add_duration(&self, other: &Duration) -> Option { + pub fn checked_add_duration(&self, other: &Duration) -> Option { Some(SystemTime(self.0.checked_add(*other)?)) } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn checked_sub_duration(&self, other: &Duration) -> Option { + pub fn checked_sub_duration(&self, other: &Duration) -> Option { Some(SystemTime(self.0.checked_sub(*other)?)) } } diff --git a/library/std/src/sys/pal/wasip1/time.rs b/library/std/src/sys/pal/wasip1/time.rs index 892661b312b2..0d8d0b59ac14 100644 --- a/library/std/src/sys/pal/wasip1/time.rs +++ b/library/std/src/sys/pal/wasip1/time.rs @@ -43,34 +43,23 @@ impl SystemTime { SystemTime(current_time(wasi::CLOCKID_REALTIME)) } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn from_wasi_timestamp(ts: wasi::Timestamp) -> SystemTime { + pub fn from_wasi_timestamp(ts: wasi::Timestamp) -> SystemTime { SystemTime(Duration::from_nanos(ts)) } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn to_wasi_timestamp(&self) -> Option { - // FIXME: const TryInto - let ns = self.0.as_nanos(); - if ns <= u64::MAX as u128 { Some(ns as u64) } else { None } + pub fn to_wasi_timestamp(&self) -> Option { + self.0.as_nanos().try_into().ok() } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn sub_time(&self, other: &SystemTime) -> Result { - // FIXME: ok_or_else with const closures - match self.0.checked_sub(other.0) { - Some(duration) => Ok(duration), - None => Err(other.0 - self.0), - } + pub fn sub_time(&self, other: &SystemTime) -> Result { + self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0) } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn checked_add_duration(&self, other: &Duration) -> Option { + pub fn checked_add_duration(&self, other: &Duration) -> Option { Some(SystemTime(self.0.checked_add(*other)?)) } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn checked_sub_duration(&self, other: &Duration) -> Option { + pub fn checked_sub_duration(&self, other: &Duration) -> Option { Some(SystemTime(self.0.checked_sub(*other)?)) } } diff --git a/library/std/src/sys/pal/windows/time.rs b/library/std/src/sys/pal/windows/time.rs index f8f9a9fd8180..0d31b80e56af 100644 --- a/library/std/src/sys/pal/windows/time.rs +++ b/library/std/src/sys/pal/windows/time.rs @@ -72,8 +72,7 @@ impl SystemTime { } } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - const fn from_intervals(intervals: i64) -> SystemTime { + fn from_intervals(intervals: i64) -> SystemTime { SystemTime { t: c::FILETIME { dwLowDateTime: intervals as u32, @@ -82,13 +81,11 @@ impl SystemTime { } } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - const fn intervals(&self) -> i64 { + fn intervals(&self) -> i64 { (self.t.dwLowDateTime as i64) | ((self.t.dwHighDateTime as i64) << 32) } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn sub_time(&self, other: &SystemTime) -> Result { + pub fn sub_time(&self, other: &SystemTime) -> Result { let me = self.intervals(); let other = other.intervals(); if me >= other { @@ -98,14 +95,12 @@ impl SystemTime { } } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn checked_add_duration(&self, other: &Duration) -> Option { + pub fn checked_add_duration(&self, other: &Duration) -> Option { let intervals = self.intervals().checked_add(checked_dur2intervals(other)?)?; Some(SystemTime::from_intervals(intervals)) } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn checked_sub_duration(&self, other: &Duration) -> Option { + pub fn checked_sub_duration(&self, other: &Duration) -> Option { let intervals = self.intervals().checked_sub(checked_dur2intervals(other)?)?; Some(SystemTime::from_intervals(intervals)) } @@ -155,18 +150,15 @@ impl Hash for SystemTime { } } -#[rustc_const_unstable(feature = "const_system_time", issue = "144517")] -const fn checked_dur2intervals(dur: &Duration) -> Option { - // FIXME: const TryInto - let secs = dur - .as_secs() +fn checked_dur2intervals(dur: &Duration) -> Option { + dur.as_secs() .checked_mul(INTERVALS_PER_SEC)? - .checked_add(dur.subsec_nanos() as u64 / 100)?; - if secs <= i64::MAX as u64 { Some(secs.cast_signed()) } else { None } + .checked_add(dur.subsec_nanos() as u64 / 100)? + .try_into() + .ok() } -#[rustc_const_unstable(feature = "const_system_time", issue = "144517")] -const fn intervals2dur(intervals: u64) -> Duration { +fn intervals2dur(intervals: u64) -> Duration { Duration::new(intervals / INTERVALS_PER_SEC, ((intervals % INTERVALS_PER_SEC) * 100) as u32) } diff --git a/library/std/src/sys/pal/xous/time.rs b/library/std/src/sys/pal/xous/time.rs index d737416436e6..ae8be81c0b7c 100644 --- a/library/std/src/sys/pal/xous/time.rs +++ b/library/std/src/sys/pal/xous/time.rs @@ -43,22 +43,15 @@ impl SystemTime { SystemTime { 0: Duration::from_millis((upper as u64) << 32 | lower as u64) } } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn sub_time(&self, other: &SystemTime) -> Result { - // FIXME: ok_or_else with const closures - match self.0.checked_sub(other.0) { - Some(duration) => Ok(duration), - None => Err(other.0 - self.0), - } + pub fn sub_time(&self, other: &SystemTime) -> Result { + self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0) } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn checked_add_duration(&self, other: &Duration) -> Option { + pub fn checked_add_duration(&self, other: &Duration) -> Option { Some(SystemTime(self.0.checked_add(*other)?)) } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn checked_sub_duration(&self, other: &Duration) -> Option { + pub fn checked_sub_duration(&self, other: &Duration) -> Option { Some(SystemTime(self.0.checked_sub(*other)?)) } } diff --git a/library/std/src/time.rs b/library/std/src/time.rs index 84fbb4c2fe4b..31187adb6fea 100644 --- a/library/std/src/time.rs +++ b/library/std/src/time.rs @@ -551,13 +551,8 @@ impl SystemTime { /// println!("{difference:?}"); /// ``` #[stable(feature = "time2", since = "1.8.0")] - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn duration_since(&self, earlier: SystemTime) -> Result { - // FIXME: map_err in const - match self.0.sub_time(&earlier.0) { - Ok(time) => Ok(time), - Err(err) => Err(SystemTimeError(err)), - } + pub fn duration_since(&self, earlier: SystemTime) -> Result { + self.0.sub_time(&earlier.0).map_err(SystemTimeError) } /// Returns the difference from this system time to the @@ -594,8 +589,7 @@ impl SystemTime { /// `SystemTime` (which means it's inside the bounds of the underlying data structure), `None` /// otherwise. #[stable(feature = "time_checked_add", since = "1.34.0")] - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn checked_add(&self, duration: Duration) -> Option { + pub fn checked_add(&self, duration: Duration) -> Option { self.0.checked_add_duration(&duration).map(SystemTime) } @@ -603,15 +597,13 @@ impl SystemTime { /// `SystemTime` (which means it's inside the bounds of the underlying data structure), `None` /// otherwise. #[stable(feature = "time_checked_add", since = "1.34.0")] - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn checked_sub(&self, duration: Duration) -> Option { + pub fn checked_sub(&self, duration: Duration) -> Option { self.0.checked_sub_duration(&duration).map(SystemTime) } } #[stable(feature = "time2", since = "1.8.0")] -#[rustc_const_unstable(feature = "const_ops", issue = "143802")] -impl const Add for SystemTime { +impl Add for SystemTime { type Output = SystemTime; /// # Panics @@ -624,16 +616,14 @@ impl const Add for SystemTime { } #[stable(feature = "time_augmented_assignment", since = "1.9.0")] -#[rustc_const_unstable(feature = "const_ops", issue = "143802")] -impl const AddAssign for SystemTime { +impl AddAssign for SystemTime { fn add_assign(&mut self, other: Duration) { *self = *self + other; } } #[stable(feature = "time2", since = "1.8.0")] -#[rustc_const_unstable(feature = "const_ops", issue = "143802")] -impl const Sub for SystemTime { +impl Sub for SystemTime { type Output = SystemTime; fn sub(self, dur: Duration) -> SystemTime { @@ -642,8 +632,7 @@ impl const Sub for SystemTime { } #[stable(feature = "time_augmented_assignment", since = "1.9.0")] -#[rustc_const_unstable(feature = "const_ops", issue = "143802")] -impl const SubAssign for SystemTime { +impl SubAssign for SystemTime { fn sub_assign(&mut self, other: Duration) { *self = *self - other; } @@ -710,8 +699,7 @@ impl SystemTimeError { /// ``` #[must_use] #[stable(feature = "time2", since = "1.8.0")] - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn duration(&self) -> Duration { + pub fn duration(&self) -> Duration { self.0 } } From a70d78a55286a85cf5e06958929f5a6071fa1c67 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Fri, 14 Mar 2025 09:58:46 +0100 Subject: [PATCH 430/682] Implement more features for GenMC mode - Handling Compare-Exchange operations. - Limitation: Compare-Exchange currently ignores possibility of spurious failures. - Limitation: Compare-Exchange failure memory ordering is ignored. - Upgrade compare-exchange success ordering to avoid reporting non-existent bugs. - Add warnings for GenMC mode for unsupported features. - Add a lot of tests, including translation of GenMC litmus tests and Loom tests. - Cleanup --- .../genmc-sys/cpp/include/MiriInterface.hpp | 82 +++++++++---- .../cpp/src/MiriInterface/EventHandling.cpp | 65 +++++++++++ .../genmc-sys/cpp/src/MiriInterface/Setup.cpp | 8 +- src/tools/miri/genmc-sys/src/lib.rs | 25 ++++ .../miri/src/concurrency/genmc/helper.rs | 82 ++++++++++++- src/tools/miri/src/concurrency/genmc/mod.rs | 109 +++++++++++++++--- src/tools/miri/src/concurrency/genmc/run.rs | 4 +- src/tools/miri/src/diagnostics.rs | 26 +++++ .../genmc/fail/data_race/mpu2_rels_rlx.rs | 45 ++++++++ .../genmc/fail/data_race/mpu2_rels_rlx.stderr | 19 +++ .../fail/loom/store_buffering.genmc.stderr | 13 +++ .../loom/store_buffering.non_genmc.stderr | 13 +++ .../tests/genmc/fail/loom/store_buffering.rs | 57 +++++++++ .../atomics/cas_failure_ord_racy_key_init.rs | 70 +++++++++++ .../cas_failure_ord_racy_key_init.stderr | 22 ++++ .../tests/genmc/pass/atomics/cas_simple.rs | 34 ++++++ .../genmc/pass/atomics/cas_simple.stderr | 2 + .../miri/tests/genmc/pass/litmus/IRIWish.rs | 4 +- .../tests/genmc/pass/litmus/MPU2_rels_acqf.rs | 67 +++++++++++ .../genmc/pass/litmus/MPU2_rels_acqf.stderr | 38 ++++++ .../miri/tests/genmc/pass/litmus/Z6_U.rs | 7 +- .../miri/tests/genmc/pass/litmus/casdep.rs | 37 ++++++ .../tests/genmc/pass/litmus/casdep.stderr | 2 + src/tools/miri/tests/genmc/pass/litmus/ccr.rs | 34 ++++++ .../miri/tests/genmc/pass/litmus/ccr.stderr | 2 + src/tools/miri/tests/genmc/pass/litmus/cii.rs | 35 ++++++ .../miri/tests/genmc/pass/litmus/cii.stderr | 2 + 27 files changed, 850 insertions(+), 54 deletions(-) create mode 100644 src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.rs create mode 100644 src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.stderr create mode 100644 src/tools/miri/tests/genmc/fail/loom/store_buffering.genmc.stderr create mode 100644 src/tools/miri/tests/genmc/fail/loom/store_buffering.non_genmc.stderr create mode 100644 src/tools/miri/tests/genmc/fail/loom/store_buffering.rs create mode 100644 src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.rs create mode 100644 src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.stderr create mode 100644 src/tools/miri/tests/genmc/pass/atomics/cas_simple.rs create mode 100644 src/tools/miri/tests/genmc/pass/atomics/cas_simple.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/casdep.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/casdep.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/ccr.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/ccr.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/cii.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/cii.stderr diff --git a/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp index b07693758432..662eb0e173ca 100644 --- a/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp +++ b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp @@ -34,6 +34,7 @@ struct SchedulingResult; struct LoadResult; struct StoreResult; struct ReadModifyWriteResult; +struct CompareExchangeResult; // GenMC uses `int` for its thread IDs. using ThreadId = int; @@ -100,6 +101,17 @@ struct MiriGenmcShim : private GenMCDriver { GenmcScalar rhs_value, GenmcScalar old_val ); + [[nodiscard]] CompareExchangeResult handle_compare_exchange( + ThreadId thread_id, + uint64_t address, + uint64_t size, + GenmcScalar expected_value, + GenmcScalar new_value, + GenmcScalar old_val, + MemOrdering success_ordering, + MemOrdering fail_load_ordering, + bool can_fail_spuriously + ); [[nodiscard]] StoreResult handle_store( ThreadId thread_id, uint64_t address, @@ -231,15 +243,15 @@ constexpr auto get_global_alloc_static_mask() -> uint64_t { namespace GenmcScalarExt { inline GenmcScalar uninit() { return GenmcScalar { - /* value: */ 0, - /* is_init: */ false, + .value = 0, + .is_init = false, }; } inline GenmcScalar from_sval(SVal sval) { return GenmcScalar { - /* value: */ sval.get(), - /* is_init: */ true, + .value = sval.get(), + .is_init = true, }; } @@ -252,22 +264,22 @@ inline SVal to_sval(GenmcScalar scalar) { namespace LoadResultExt { inline LoadResult no_value() { return LoadResult { - /* error: */ std::unique_ptr(nullptr), - /* has_value: */ false, - /* read_value: */ GenmcScalarExt::uninit(), + .error = std::unique_ptr(nullptr), + .has_value = false, + .read_value = GenmcScalarExt::uninit(), }; } inline LoadResult from_value(SVal read_value) { - return LoadResult { /* error: */ std::unique_ptr(nullptr), - /* has_value: */ true, - /* read_value: */ GenmcScalarExt::from_sval(read_value) }; + return LoadResult { .error = std::unique_ptr(nullptr), + .has_value = true, + .read_value = GenmcScalarExt::from_sval(read_value) }; } inline LoadResult from_error(std::unique_ptr error) { - return LoadResult { /* error: */ std::move(error), - /* has_value: */ false, - /* read_value: */ GenmcScalarExt::uninit() }; + return LoadResult { .error = std::move(error), + .has_value = false, + .read_value = GenmcScalarExt::uninit() }; } } // namespace LoadResultExt @@ -278,26 +290,50 @@ inline StoreResult ok(bool is_coherence_order_maximal_write) { } inline StoreResult from_error(std::unique_ptr error) { - return StoreResult { /* error: */ std::move(error), - /* is_coherence_order_maximal_write: */ false }; + return StoreResult { .error = std::move(error), .is_coherence_order_maximal_write = false }; } } // namespace StoreResultExt namespace ReadModifyWriteResultExt { inline ReadModifyWriteResult ok(SVal old_value, SVal new_value, bool is_coherence_order_maximal_write) { - return ReadModifyWriteResult { /* error: */ std::unique_ptr(nullptr), - /* old_value: */ GenmcScalarExt::from_sval(old_value), - /* new_value: */ GenmcScalarExt::from_sval(new_value), - is_coherence_order_maximal_write }; + return ReadModifyWriteResult { .error = std::unique_ptr(nullptr), + .old_value = GenmcScalarExt::from_sval(old_value), + .new_value = GenmcScalarExt::from_sval(new_value), + .is_coherence_order_maximal_write = + is_coherence_order_maximal_write }; } inline ReadModifyWriteResult from_error(std::unique_ptr error) { - return ReadModifyWriteResult { /* error: */ std::move(error), - /* old_value: */ GenmcScalarExt::uninit(), - /* new_value: */ GenmcScalarExt::uninit(), - /* is_coherence_order_maximal_write: */ false }; + return ReadModifyWriteResult { .error = std::move(error), + .old_value = GenmcScalarExt::uninit(), + .new_value = GenmcScalarExt::uninit(), + .is_coherence_order_maximal_write = false }; } } // namespace ReadModifyWriteResultExt +namespace CompareExchangeResultExt { +inline CompareExchangeResult success(SVal old_value, bool is_coherence_order_maximal_write) { + return CompareExchangeResult { .error = nullptr, + .old_value = GenmcScalarExt::from_sval(old_value), + .is_success = true, + .is_coherence_order_maximal_write = + is_coherence_order_maximal_write }; +} + +inline CompareExchangeResult failure(SVal old_value) { + return CompareExchangeResult { .error = nullptr, + .old_value = GenmcScalarExt::from_sval(old_value), + .is_success = false, + .is_coherence_order_maximal_write = false }; +} + +inline CompareExchangeResult from_error(std::unique_ptr error) { + return CompareExchangeResult { .error = std::move(error), + .old_value = GenmcScalarExt::uninit(), + .is_success = false, + .is_coherence_order_maximal_write = false }; +} +} // namespace CompareExchangeResultExt + #endif /* GENMC_MIRI_INTERFACE_HPP */ diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp index cd28e0d148f5..05c82641df94 100644 --- a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp @@ -155,6 +155,71 @@ void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) { ); } +[[nodiscard]] auto MiriGenmcShim::handle_compare_exchange( + ThreadId thread_id, + uint64_t address, + uint64_t size, + GenmcScalar expected_value, + GenmcScalar new_value, + GenmcScalar old_val, + MemOrdering success_ordering, + MemOrdering fail_load_ordering, + bool can_fail_spuriously +) -> CompareExchangeResult { + // NOTE: Both the store and load events should get the same `ordering`, it should not be split + // into a load and a store component. This means we can have for example `AcqRel` loads and + // stores, but this is intended for CAS operations. + + // FIXME(GenMC): properly handle failure memory ordering. + + auto expectedVal = GenmcScalarExt::to_sval(expected_value); + auto new_val = GenmcScalarExt::to_sval(new_value); + + const auto load_ret = handle_load_reset_if_none( + thread_id, + success_ordering, + SAddr(address), + ASize(size), + AType::Unsigned, // The type is only used for printing. + expectedVal, + new_val + ); + if (const auto* err = std::get_if(&load_ret)) + return CompareExchangeResultExt::from_error(format_error(*err)); + const auto* ret_val = std::get_if(&load_ret); + ERROR_ON(nullptr == ret_val, "Unimplemented: load returned unexpected result."); + const auto read_old_val = *ret_val; + if (read_old_val != expectedVal) + return CompareExchangeResultExt::failure(read_old_val); + + // FIXME(GenMC): Add support for modelling spurious failures. + + const auto storePos = inc_pos(thread_id); + const auto store_ret = GenMCDriver::handleStore( + storePos, + success_ordering, + SAddr(address), + ASize(size), + AType::Unsigned, // The type is only used for printing. + new_val + ); + if (const auto* err = std::get_if(&store_ret)) + return CompareExchangeResultExt::from_error(format_error(*err)); + const auto* store_ret_val = std::get_if(&store_ret); + ERROR_ON( + nullptr == store_ret_val, + "Unimplemented: compare-exchange store returned unexpected result." + ); + + // FIXME(genmc,mixed-accesses): Use the value that GenMC returns from handleStore (once + // available). + const auto& g = getExec().getGraph(); + return CompareExchangeResultExt::success( + read_old_val, + /* is_coherence_order_maximal_write */ g.co_max(SAddr(address))->getPos() == storePos + ); +} + /**** Memory (de)allocation ****/ auto MiriGenmcShim::handle_malloc(ThreadId thread_id, uint64_t size, uint64_t alignment) diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp index 5a53fee05921..a17a83aa06e1 100644 --- a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp @@ -152,10 +152,10 @@ static auto to_genmc_verbosity_level(const LogLevel log_level) -> VerbosityLevel // Miri already ensures that memory accesses are valid, so this check doesn't matter. // We check that the address is static, but skip checking if it is part of an actual // allocation. - /* isStaticallyAllocated: */ [](SAddr addr) { return addr.isStatic(); }, + .isStaticallyAllocated = [](SAddr addr) { return addr.isStatic(); }, // FIXME(genmc,error reporting): Once a proper a proper API for passing such information is // implemented in GenMC, Miri should use it to improve the produced error messages. - /* getStaticName: */ [](SAddr addr) { return "[UNKNOWN STATIC]"; }, + .getStaticName = [](SAddr addr) { return "[UNKNOWN STATIC]"; }, // This function is called to get the initial value stored at the given address. // // From a Miri perspective, this API doesn't work very well: most memory starts out @@ -177,10 +177,10 @@ static auto to_genmc_verbosity_level(const LogLevel log_level) -> VerbosityLevel // FIXME(genmc): implement proper support for uninitialized memory in GenMC. Ideally, the // initial value getter would return an `optional`, since the memory location may be // uninitialized. - /* initValGetter: */ [](const AAccess& a) { return SVal(0xDEAD); }, + .initValGetter = [](const AAccess& a) { return SVal(0xDEAD); }, // Miri serves non-atomic loads from its own memory and these GenMC checks are wrong in // that case. This should no longer be required with proper mixed-size access support. - /* skipUninitLoadChecks: */ [](MemOrdering ord) { return ord == MemOrdering::NotAtomic; }, + .skipUninitLoadChecks = [](MemOrdering ord) { return ord == MemOrdering::NotAtomic; }, }; driver->setInterpCallbacks(std::move(interpreter_callbacks)); diff --git a/src/tools/miri/genmc-sys/src/lib.rs b/src/tools/miri/genmc-sys/src/lib.rs index 1de4c4eb5e8e..31bc2606adc0 100644 --- a/src/tools/miri/genmc-sys/src/lib.rs +++ b/src/tools/miri/genmc-sys/src/lib.rs @@ -198,6 +198,19 @@ mod ffi { is_coherence_order_maximal_write: bool, } + #[must_use] + #[derive(Debug)] + struct CompareExchangeResult { + /// If there was an error, it will be stored in `error`, otherwise it is `None`. + error: UniquePtr, + /// The value that was read by the compare-exchange. + old_value: GenmcScalar, + /// `true` if compare_exchange op was successful. + is_success: bool, + /// `true` if the write should also be reflected in Miri's memory representation. + is_coherence_order_maximal_write: bool, + } + /**** These are GenMC types that we have to copy-paste here since cxx does not support "importing" externally defined C++ types. ****/ @@ -313,6 +326,18 @@ mod ffi { rhs_value: GenmcScalar, old_value: GenmcScalar, ) -> ReadModifyWriteResult; + fn handle_compare_exchange( + self: Pin<&mut MiriGenmcShim>, + thread_id: i32, + address: u64, + size: u64, + expected_value: GenmcScalar, + new_value: GenmcScalar, + old_value: GenmcScalar, + success_ordering: MemOrdering, + fail_load_ordering: MemOrdering, + can_fail_spuriously: bool, + ) -> CompareExchangeResult; fn handle_store( self: Pin<&mut MiriGenmcShim>, thread_id: i32, diff --git a/src/tools/miri/src/concurrency/genmc/helper.rs b/src/tools/miri/src/concurrency/genmc/helper.rs index 2a84ca203667..48a5ec8bb260 100644 --- a/src/tools/miri/src/concurrency/genmc/helper.rs +++ b/src/tools/miri/src/concurrency/genmc/helper.rs @@ -1,20 +1,52 @@ +use std::sync::RwLock; + use genmc_sys::{MemOrdering, RMWBinOp}; use rustc_abi::Size; use rustc_const_eval::interpret::{InterpResult, interp_ok}; +use rustc_data_structures::fx::FxHashSet; use rustc_middle::mir; use rustc_middle::ty::ScalarInt; +use rustc_span::Span; use tracing::debug; use super::GenmcScalar; +use crate::diagnostics::EvalContextExt; use crate::intrinsics::AtomicRmwOp; use crate::{ - AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, MiriInterpCx, Scalar, - throw_unsup_format, + AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, InterpCx, MiriInterpCx, + MiriMachine, NonHaltingDiagnostic, Scalar, throw_unsup_format, }; /// Maximum size memory access in bytes that GenMC supports. pub(super) const MAX_ACCESS_SIZE: u64 = 8; +/// Type for storing spans for already emitted warnings. +pub(super) type WarningCache = RwLock>; + +#[derive(Default)] +pub(super) struct Warnings { + pub(super) compare_exchange_failure_ordering: WarningCache, + pub(super) compare_exchange_weak: WarningCache, +} + +/// Emit a warning if it hasn't already been reported for current span. +pub(super) fn emit_warning<'tcx>( + ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + cache: &WarningCache, + diagnostic: impl FnOnce() -> NonHaltingDiagnostic, +) { + let span = ecx.machine.current_span(); + if cache.read().unwrap().contains(&span) { + return; + } + // This span has not yet been reported, so we insert it into the cache and report it. + let mut cache = cache.write().unwrap(); + if cache.insert(span) { + // Some other thread may have added this span while we didn't hold the lock, so we only emit it if the insertions succeeded. + ecx.emit_diagnostic(diagnostic()); + } +} + /// This function is used to split up a large memory access into aligned, non-overlapping chunks of a limited size. /// Returns an iterator over the chunks, yielding `(base address, size)` of each chunk, ordered by address. pub fn split_access(address: Size, size: Size) -> impl Iterator { @@ -112,7 +144,53 @@ impl AtomicFenceOrd { } } +/// Since GenMC ignores the failure memory ordering and Miri should not detect bugs that don't actually exist, we upgrade the success ordering if required. +/// This means that Miri running in GenMC mode will not explore all possible executions allowed under the RC11 memory model. +/// FIXME(genmc): remove this once GenMC properly supports the failure memory ordering. +pub(super) fn maybe_upgrade_compare_exchange_success_orderings( + success: AtomicRwOrd, + failure: AtomicReadOrd, +) -> AtomicRwOrd { + use AtomicReadOrd::*; + let (success_read, success_write) = success.split_memory_orderings(); + let upgraded_success_read = match (success_read, failure) { + (_, SeqCst) | (SeqCst, _) => SeqCst, + (Acquire, _) | (_, Acquire) => Acquire, + (Relaxed, Relaxed) => Relaxed, + }; + AtomicRwOrd::from_split_memory_orderings(upgraded_success_read, success_write) +} + impl AtomicRwOrd { + /// Split up an atomic read-write memory ordering into a separate read and write ordering. + pub(super) fn split_memory_orderings(self) -> (AtomicReadOrd, AtomicWriteOrd) { + match self { + AtomicRwOrd::Relaxed => (AtomicReadOrd::Relaxed, AtomicWriteOrd::Relaxed), + AtomicRwOrd::Acquire => (AtomicReadOrd::Acquire, AtomicWriteOrd::Relaxed), + AtomicRwOrd::Release => (AtomicReadOrd::Relaxed, AtomicWriteOrd::Release), + AtomicRwOrd::AcqRel => (AtomicReadOrd::Acquire, AtomicWriteOrd::Release), + AtomicRwOrd::SeqCst => (AtomicReadOrd::SeqCst, AtomicWriteOrd::SeqCst), + } + } + + /// Split up an atomic read-write memory ordering into a separate read and write ordering. + fn from_split_memory_orderings( + read_ordering: AtomicReadOrd, + write_ordering: AtomicWriteOrd, + ) -> Self { + match (read_ordering, write_ordering) { + (AtomicReadOrd::Relaxed, AtomicWriteOrd::Relaxed) => AtomicRwOrd::Relaxed, + (AtomicReadOrd::Acquire, AtomicWriteOrd::Relaxed) => AtomicRwOrd::Acquire, + (AtomicReadOrd::Relaxed, AtomicWriteOrd::Release) => AtomicRwOrd::Release, + (AtomicReadOrd::Acquire, AtomicWriteOrd::Release) => AtomicRwOrd::AcqRel, + (AtomicReadOrd::SeqCst, AtomicWriteOrd::SeqCst) => AtomicRwOrd::SeqCst, + _ => + panic!( + "Unsupported memory ordering combination ({read_ordering:?}, {write_ordering:?})" + ), + } + } + pub(super) fn to_genmc(self) -> MemOrdering { match self { AtomicRwOrd::Relaxed => MemOrdering::Relaxed, diff --git a/src/tools/miri/src/concurrency/genmc/mod.rs b/src/tools/miri/src/concurrency/genmc/mod.rs index c1da86c67336..7270c6681060 100644 --- a/src/tools/miri/src/concurrency/genmc/mod.rs +++ b/src/tools/miri/src/concurrency/genmc/mod.rs @@ -13,15 +13,16 @@ use tracing::{debug, info}; use self::global_allocations::{EvalContextExt as _, GlobalAllocationHandler}; use self::helper::{ - MAX_ACCESS_SIZE, genmc_scalar_to_scalar, scalar_to_genmc_scalar, to_genmc_rmw_op, + MAX_ACCESS_SIZE, Warnings, emit_warning, genmc_scalar_to_scalar, + maybe_upgrade_compare_exchange_success_orderings, scalar_to_genmc_scalar, to_genmc_rmw_op, }; use self::thread_id_map::ThreadIdMap; use crate::concurrency::genmc::helper::split_access; use crate::intrinsics::AtomicRmwOp; use crate::{ AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, MemoryKind, MiriConfig, - MiriMachine, MiriMemoryKind, Scalar, TerminationInfo, ThreadId, ThreadManager, VisitProvenance, - VisitWith, + MiriMachine, MiriMemoryKind, NonHaltingDiagnostic, Scalar, TerminationInfo, ThreadId, + ThreadManager, VisitProvenance, VisitWith, }; mod config; @@ -81,16 +82,22 @@ impl PerExecutionState { } } -#[derive(Debug)] struct GlobalState { /// Keep track of global allocations, to ensure they keep the same address across different executions, even if the order of allocations changes. /// The `AllocId` for globals is stable across executions, so we can use it as an identifier. global_allocations: GlobalAllocationHandler, + + /// Cache for which warnings have already been shown to the user. + /// `None` if warnings are disabled. + warning_cache: Option, } impl GlobalState { - fn new(target_usize_max: u64) -> Self { - Self { global_allocations: GlobalAllocationHandler::new(target_usize_max) } + fn new(target_usize_max: u64, print_warnings: bool) -> Self { + Self { + global_allocations: GlobalAllocationHandler::new(target_usize_max), + warning_cache: print_warnings.then(Default::default), + } } } @@ -348,21 +355,91 @@ impl GenmcCtx { /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized. pub(crate) fn atomic_compare_exchange<'tcx>( &self, - _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - _address: Size, - _size: Size, - _expected_old_value: Scalar, - _new_value: Scalar, - _success: AtomicRwOrd, - _fail: AtomicReadOrd, - _can_fail_spuriously: bool, - _old_value: Scalar, + ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + address: Size, + size: Size, + expected_old_value: Scalar, + new_value: Scalar, + success: AtomicRwOrd, + fail: AtomicReadOrd, + can_fail_spuriously: bool, + old_value: Scalar, ) -> InterpResult<'tcx, (Scalar, Option, bool)> { assert!( !self.get_alloc_data_races(), "atomic compare-exchange with data race checking disabled." ); - throw_unsup_format!("FIXME(genmc): Add support for atomic compare_exchange.") + assert_ne!(0, size.bytes()); + assert!( + size.bytes() <= MAX_ACCESS_SIZE, + "GenMC currently does not support atomic accesses larger than {} bytes (got {} bytes)", + MAX_ACCESS_SIZE, + size.bytes() + ); + + // Upgrade the success memory ordering to equal the failure ordering, since GenMC currently ignores the failure ordering. + // FIXME(genmc): remove this once GenMC properly supports the failure memory ordering. + let upgraded_success_ordering = + maybe_upgrade_compare_exchange_success_orderings(success, fail); + + if let Some(warning_cache) = &self.global_state.warning_cache { + // FIXME(genmc): remove once GenMC supports failure memory ordering in `compare_exchange`. + let (effective_failure_ordering, _) = + upgraded_success_ordering.split_memory_orderings(); + // Return a warning if the actual orderings don't match the upgraded ones. + if success != upgraded_success_ordering || effective_failure_ordering != fail { + emit_warning(ecx, &warning_cache.compare_exchange_failure_ordering, || { + NonHaltingDiagnostic::GenmcCompareExchangeOrderingMismatch { + success_ordering: success, + upgraded_success_ordering, + failure_ordering: fail, + effective_failure_ordering, + } + }); + } + // FIXME(genmc): remove once GenMC implements spurious failures for `compare_exchange_weak`. + if can_fail_spuriously { + emit_warning(ecx, &warning_cache.compare_exchange_weak, || { + NonHaltingDiagnostic::GenmcCompareExchangeWeak + }); + } + } + + debug!( + "GenMC: atomic_compare_exchange, address: {address:?}, size: {size:?} (expect: {expected_old_value:?}, new: {new_value:?}, old_value: {old_value:?}, {success:?}, orderings: {fail:?}), can fail spuriously: {can_fail_spuriously}" + ); + + let thread_infos = self.exec_state.thread_id_manager.borrow(); + let genmc_tid = thread_infos.get_genmc_tid(ecx.machine.threads.active_thread()); + + let cas_result = self.handle.borrow_mut().pin_mut().handle_compare_exchange( + genmc_tid, + address.bytes(), + size.bytes(), + scalar_to_genmc_scalar(ecx, expected_old_value)?, + scalar_to_genmc_scalar(ecx, new_value)?, + scalar_to_genmc_scalar(ecx, old_value)?, + upgraded_success_ordering.to_genmc(), + fail.to_genmc(), + can_fail_spuriously, + ); + + if let Some(error) = cas_result.error.as_ref() { + // FIXME(genmc): error handling + throw_ub_format!("{}", error.to_string_lossy()); + } + + let return_scalar = genmc_scalar_to_scalar(ecx, cas_result.old_value, size)?; + debug!( + "GenMC: atomic_compare_exchange: result: {cas_result:?}, returning scalar: {return_scalar:?}" + ); + // The write can only be a co-maximal write if the CAS succeeded. + assert!(cas_result.is_success || !cas_result.is_coherence_order_maximal_write); + interp_ok(( + return_scalar, + cas_result.is_coherence_order_maximal_write.then_some(new_value), + cas_result.is_success, + )) } /// Inform GenMC about a non-atomic memory load diff --git a/src/tools/miri/src/concurrency/genmc/run.rs b/src/tools/miri/src/concurrency/genmc/run.rs index 24d13e6375b1..7e4ed816a76a 100644 --- a/src/tools/miri/src/concurrency/genmc/run.rs +++ b/src/tools/miri/src/concurrency/genmc/run.rs @@ -23,7 +23,9 @@ pub fn run_genmc_mode<'tcx>( // There exists only one `global_state` per full run in GenMC mode. // It is shared by all `GenmcCtx` in this run. // FIXME(genmc): implement multithreading once GenMC supports it. - let global_state = Arc::new(GlobalState::new(tcx.target_usize_max())); + // FIXME(genmc): disable warnings in estimation mode once it is added. + let global_state = + Arc::new(GlobalState::new(tcx.target_usize_max(), /* print_warnings */ true)); let genmc_ctx = Rc::new(GenmcCtx::new(config, global_state)); // `rep` is used to report the progress, Miri will panic on wrap-around. diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 0842307caf9a..15f7ccbabca6 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -141,6 +141,13 @@ pub enum NonHaltingDiagnostic { ptr: Pointer, }, ExternTypeReborrow, + GenmcCompareExchangeWeak, + GenmcCompareExchangeOrderingMismatch { + success_ordering: AtomicRwOrd, + upgraded_success_ordering: AtomicRwOrd, + failure_ordering: AtomicReadOrd, + effective_failure_ordering: AtomicReadOrd, + }, } /// Level of Miri specific diagnostics @@ -637,6 +644,8 @@ impl<'tcx> MiriMachine<'tcx> { ("sharing memory with a native function".to_string(), DiagLevel::Warning), ExternTypeReborrow => ("reborrow of reference to `extern type`".to_string(), DiagLevel::Warning), + GenmcCompareExchangeWeak | GenmcCompareExchangeOrderingMismatch { .. } => + ("GenMC might miss possible behaviors of this code".to_string(), DiagLevel::Warning), CreatedPointerTag(..) | PoppedPointerTag(..) | CreatedAlloc(..) @@ -675,6 +684,23 @@ impl<'tcx> MiriMachine<'tcx> { format!("weak memory emulation: outdated value returned from load at {ptr}"), ExternTypeReborrow => format!("reborrow of a reference to `extern type` is not properly supported"), + GenmcCompareExchangeWeak => + "GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures." + .to_string(), + GenmcCompareExchangeOrderingMismatch { + success_ordering, + upgraded_success_ordering, + failure_ordering, + effective_failure_ordering, + } => { + let was_upgraded_msg = if success_ordering != upgraded_success_ordering { + format!("Success ordering '{success_ordering:?}' was upgraded to '{upgraded_success_ordering:?}' to match failure ordering '{failure_ordering:?}'") + } else { + assert_ne!(failure_ordering, effective_failure_ordering); + format!("Due to success ordering '{success_ordering:?}', the failure ordering '{failure_ordering:?}' is treated like '{effective_failure_ordering:?}'") + }; + format!("GenMC currently does not model the failure ordering for `compare_exchange`. {was_upgraded_msg}. Miri with GenMC might miss bugs related to this memory access.") + } }; let notes = match &e { diff --git a/src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.rs b/src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.rs new file mode 100644 index 000000000000..32954a643b34 --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.rs @@ -0,0 +1,45 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's test `wrong/racy/MPU2+rels+rlx`. +// Test if Miri with GenMC can detect the data race on `X`. +// The data race only occurs if thread 1 finishes, then threads 3 and 4 run, then thread 2. +// +// This data race is hard to detect for Miri without GenMC, requiring -Zmiri-many-seeds=0..1024 at the time this test was created. + +// FIXME(genmc): once Miri-GenMC error reporting is improved, ensure that it correctly points to the two spans involved in the data race. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::*; + +use genmc::spawn_pthread_closure; +static mut X: u64 = 0; +static Y: AtomicUsize = AtomicUsize::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + let _t1 = spawn_pthread_closure(|| { + X = 1; + Y.store(0, Release); + Y.store(1, Relaxed); + }); + let _t2 = spawn_pthread_closure(|| { + if Y.load(Relaxed) > 2 { + X = 2; //~ ERROR: Undefined Behavior: Non-atomic race + } + }); + let _t3 = spawn_pthread_closure(|| { + let _ = Y.compare_exchange(2, 3, Relaxed, Relaxed); + }); + + let _t4 = spawn_pthread_closure(|| { + let _ = Y.compare_exchange(1, 2, Relaxed, Relaxed); + }); + } + 0 +} diff --git a/src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.stderr b/src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.stderr new file mode 100644 index 000000000000..946d9a2124b8 --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.stderr @@ -0,0 +1,19 @@ +error: Undefined Behavior: Non-atomic race + --> tests/genmc/fail/data_race/mpu2_rels_rlx.rs:LL:CC + | +LL | X = 2; + | ^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE on thread `unnamed-ID`: + = note: inside closure at tests/genmc/fail/data_race/mpu2_rels_rlx.rs:LL:CC + = note: inside ` as std::ops::FnOnce<()>>::call_once` at RUSTLIB/alloc/src/boxed.rs:LL:CC +note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/fail/data_race/mpu2_rels_rlx.rs:LL:CC}>` + --> tests/genmc/fail/data_race/../../../utils/genmc.rs:LL:CC + | +LL | f(); + | ^^^ + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/genmc/fail/loom/store_buffering.genmc.stderr b/src/tools/miri/tests/genmc/fail/loom/store_buffering.genmc.stderr new file mode 100644 index 000000000000..a6c3ed7055e7 --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/loom/store_buffering.genmc.stderr @@ -0,0 +1,13 @@ +error: abnormal termination: the program aborted execution + --> tests/genmc/fail/loom/store_buffering.rs:LL:CC + | +LL | std::process::abort(); + | ^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here + | + = note: BACKTRACE: + = note: inside `miri_start` at tests/genmc/fail/loom/store_buffering.rs:LL:CC + +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/genmc/fail/loom/store_buffering.non_genmc.stderr b/src/tools/miri/tests/genmc/fail/loom/store_buffering.non_genmc.stderr new file mode 100644 index 000000000000..a6c3ed7055e7 --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/loom/store_buffering.non_genmc.stderr @@ -0,0 +1,13 @@ +error: abnormal termination: the program aborted execution + --> tests/genmc/fail/loom/store_buffering.rs:LL:CC + | +LL | std::process::abort(); + | ^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here + | + = note: BACKTRACE: + = note: inside `miri_start` at tests/genmc/fail/loom/store_buffering.rs:LL:CC + +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/genmc/fail/loom/store_buffering.rs b/src/tools/miri/tests/genmc/fail/loom/store_buffering.rs new file mode 100644 index 000000000000..4955dfd8a77a --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/loom/store_buffering.rs @@ -0,0 +1,57 @@ +//@ revisions: non_genmc genmc +//@[genmc] compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: Copyright (c) 2019 Carl Lerche + +// This is the test `store_buffering` from `loom/test/litmus.rs`, adapted for Miri-GenMC. +// https://github.com/tokio-rs/loom/blob/dbf32b04bae821c64be44405a0bb72ca08741558/tests/litmus.rs + +// This test shows the comparison between running Miri with or without GenMC. +// Without GenMC, Miri requires multiple iterations of the loop to detect the error. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // For normal Miri, we need multiple repetitions, but GenMC should find the bug with only 1. + const REPS: usize = if cfg!(non_genmc) { 128 } else { 1 }; + for _ in 0..REPS { + // New atomics every iterations, so they don't influence each other. + let x = AtomicUsize::new(0); + let y = AtomicUsize::new(0); + + // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. + x.store(0, Relaxed); + y.store(0, Relaxed); + + let mut a: usize = 1234; + let mut b: usize = 1234; + unsafe { + let ids = [ + spawn_pthread_closure(|| { + x.store(1, Relaxed); + a = y.load(Relaxed) + }), + spawn_pthread_closure(|| { + y.store(1, Relaxed); + b = x.load(Relaxed) + }), + ]; + join_pthreads(ids); + } + if (a, b) == (0, 0) { + std::process::abort(); //~ ERROR: abnormal termination + } + } + + 0 +} diff --git a/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.rs b/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.rs new file mode 100644 index 000000000000..8a77d54a64f8 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.rs @@ -0,0 +1,70 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -Zmiri-ignore-leaks + +// Adapted from: `impl LazyKey`, `fn lazy_init`: rust/library/std/src/sys/thread_local/key/racy.rs +// Two threads race to initialize a key, which is just an index into an array in this test. +// The (Acquire, Release) orderings on the compare_exchange prevent any data races for reading from `VALUES[key]`. + +// FIXME(genmc): GenMC does not model the failure ordering of compare_exchange currently. +// Miri thus upgrades the success ordering to prevent showing any false data races in cases like this one. +// Once GenMC supports the failure ordering, this test should work without the upgrading. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +const KEY_SENTVAL: usize = usize::MAX; + +static KEY: AtomicUsize = AtomicUsize::new(KEY_SENTVAL); + +static mut VALUES: [usize; 2] = [0, 0]; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + KEY.store(KEY_SENTVAL, Relaxed); + + unsafe { + let mut a = 0; + let mut b = 0; + let ids = [ + spawn_pthread_closure(|| { + VALUES[0] = 42; + let key = get_or_init(0); + if key > 2 { + std::process::abort(); + } + a = VALUES[key]; + }), + spawn_pthread_closure(|| { + VALUES[1] = 1234; + let key = get_or_init(1); + if key > 2 { + std::process::abort(); + } + b = VALUES[key]; + }), + ]; + join_pthreads(ids); + if a != b { + std::process::abort(); + } + } + 0 +} + +fn get_or_init(key: usize) -> usize { + match KEY.compare_exchange(KEY_SENTVAL, key, Release, Acquire) { + // The CAS succeeded, so we've created the actual key. + Ok(_) => key, + // If someone beat us to the punch, use their key instead. + // The `Acquire` failure ordering means we synchronized with the `Release` compare_exchange in the thread that wrote the other key. + // We can thus read from `VALUES[other_key]` without a data race. + Err(other_key) => other_key, + } +} diff --git a/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.stderr b/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.stderr new file mode 100644 index 000000000000..d97b8b3d92f4 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.stderr @@ -0,0 +1,22 @@ +warning: GenMC currently does not model the failure ordering for `compare_exchange`. Success ordering 'Release' was upgraded to 'AcqRel' to match failure ordering 'Acquire'. Miri with GenMC might miss bugs related to this memory access. + --> tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.rs:LL:CC + | +LL | match KEY.compare_exchange(KEY_SENTVAL, key, Release, Acquire) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code + | + = note: BACKTRACE on thread `unnamed-ID`: + = note: inside `get_or_init` at tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.rs:LL:CC +note: inside closure + --> tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.rs:LL:CC + | +LL | let key = get_or_init(0); + | ^^^^^^^^^^^^^^ + = note: inside ` as std::ops::FnOnce<()>>::call_once` at RUSTLIB/alloc/src/boxed.rs:LL:CC +note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.rs:LL:CC}>` + --> tests/genmc/pass/atomics/../../../utils/genmc.rs:LL:CC + | +LL | f(); + | ^^^ + +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 2 diff --git a/src/tools/miri/tests/genmc/pass/atomics/cas_simple.rs b/src/tools/miri/tests/genmc/pass/atomics/cas_simple.rs new file mode 100644 index 000000000000..e32c7cdf80c4 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/atomics/cas_simple.rs @@ -0,0 +1,34 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Test the basic functionality of compare_exchange. + +#![no_main] + +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::*; + +static VALUE: AtomicUsize = AtomicUsize::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + VALUE.store(1, SeqCst); + + // Expect success: + if VALUE.compare_exchange(1, 2, SeqCst, SeqCst) != Ok(1) { + std::process::abort(); + } + // New value should be written: + if 2 != VALUE.load(SeqCst) { + std::process::abort() + } + + // Expect failure: + if VALUE.compare_exchange(1234, 42, SeqCst, SeqCst) != Err(2) { + std::process::abort(); + } + // Value should be unchanged: + if 2 != VALUE.load(SeqCst) { + std::process::abort() + } + 0 +} diff --git a/src/tools/miri/tests/genmc/pass/atomics/cas_simple.stderr b/src/tools/miri/tests/genmc/pass/atomics/cas_simple.stderr new file mode 100644 index 000000000000..3b22247ee44c --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/atomics/cas_simple.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 1 diff --git a/src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs b/src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs index 163556dcc897..c81573d59d1d 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs @@ -57,9 +57,7 @@ fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { join_pthreads(ids); // Print the values to check that we get all of them: - if writeln!(MiriStderr, "{results:?}").is_err() { - std::process::abort(); - } + writeln!(MiriStderr, "{results:?}").unwrap_or_else(|_| std::process::abort()); 0 } diff --git a/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.rs b/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.rs new file mode 100644 index 000000000000..9bb156a99970 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.rs @@ -0,0 +1,67 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "litmus/MPU2+rels+acqf" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; +#[path = "../../../utils/mod.rs"] +mod utils; + +use std::fmt::Write; +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; +use crate::utils::*; + +// Note: the GenMC equivalent of this test (genmc/tests/correct/litmus/MPU2+rels+acqf/mpu2+rels+acqf.c) uses non-atomic accesses for `X` with disabled race detection. +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + Y.store(0, Relaxed); + + unsafe { + let mut a = Ok(1234); + let mut b = Ok(1234); + let mut c = 1234; + let ids = [ + spawn_pthread_closure(|| { + X.store(1, Relaxed); + + Y.store(0, Release); + Y.store(1, Relaxed); + }), + spawn_pthread_closure(|| { + a = Y.compare_exchange(2, 3, Relaxed, Relaxed); + }), + spawn_pthread_closure(|| { + b = Y.compare_exchange(1, 2, Relaxed, Relaxed); + }), + spawn_pthread_closure(|| { + c = Y.load(Acquire); + if c > 2 { + std::sync::atomic::fence(Acquire); + X.store(2, Relaxed); + } + }), + ]; + join_pthreads(ids); + + // Print the values to check that we get all of them: + writeln!( + MiriStderr, + "X={}, Y={}, a={a:?}, b={b:?}, c={c}", + X.load(Relaxed), + Y.load(Relaxed) + ) + .unwrap_or_else(|_| std::process::abort()); + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.stderr b/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.stderr new file mode 100644 index 000000000000..4551c00b0575 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.stderr @@ -0,0 +1,38 @@ +X=1, Y=2, a=Err(1), b=Ok(1), c=2 +X=1, Y=2, a=Err(1), b=Ok(1), c=1 +X=1, Y=2, a=Err(1), b=Ok(1), c=0 +X=1, Y=2, a=Err(1), b=Ok(1), c=0 +X=2, Y=3, a=Ok(2), b=Ok(1), c=3 +X=1, Y=3, a=Ok(2), b=Ok(1), c=3 +X=1, Y=3, a=Ok(2), b=Ok(1), c=2 +X=1, Y=3, a=Ok(2), b=Ok(1), c=1 +X=1, Y=3, a=Ok(2), b=Ok(1), c=0 +X=1, Y=3, a=Ok(2), b=Ok(1), c=0 +X=1, Y=1, a=Err(1), b=Err(0), c=1 +X=1, Y=1, a=Err(1), b=Err(0), c=0 +X=1, Y=1, a=Err(1), b=Err(0), c=0 +X=1, Y=1, a=Err(1), b=Err(0), c=1 +X=1, Y=1, a=Err(1), b=Err(0), c=0 +X=1, Y=1, a=Err(1), b=Err(0), c=0 +X=1, Y=2, a=Err(0), b=Ok(1), c=2 +X=1, Y=2, a=Err(0), b=Ok(1), c=1 +X=1, Y=2, a=Err(0), b=Ok(1), c=0 +X=1, Y=2, a=Err(0), b=Ok(1), c=0 +X=1, Y=1, a=Err(0), b=Err(0), c=1 +X=1, Y=1, a=Err(0), b=Err(0), c=0 +X=1, Y=1, a=Err(0), b=Err(0), c=0 +X=1, Y=1, a=Err(0), b=Err(0), c=1 +X=1, Y=1, a=Err(0), b=Err(0), c=0 +X=1, Y=1, a=Err(0), b=Err(0), c=0 +X=1, Y=2, a=Err(0), b=Ok(1), c=2 +X=1, Y=2, a=Err(0), b=Ok(1), c=1 +X=1, Y=2, a=Err(0), b=Ok(1), c=0 +X=1, Y=2, a=Err(0), b=Ok(1), c=0 +X=1, Y=1, a=Err(0), b=Err(0), c=1 +X=1, Y=1, a=Err(0), b=Err(0), c=0 +X=1, Y=1, a=Err(0), b=Err(0), c=0 +X=1, Y=1, a=Err(0), b=Err(0), c=1 +X=1, Y=1, a=Err(0), b=Err(0), c=0 +X=1, Y=1, a=Err(0), b=Err(0), c=0 +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 36 diff --git a/src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs index ac5c3724254d..f96679b23a5c 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs @@ -55,11 +55,8 @@ fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { join_pthreads(ids); // Print the values to check that we get all of them: - if writeln!(MiriStderr, "a={a}, b={b}, X={}, Y={}", X.load(Relaxed), Y.load(Relaxed)) - .is_err() - { - std::process::abort(); - } + writeln!(MiriStderr, "a={a}, b={b}, X={}, Y={}", X.load(Relaxed), Y.load(Relaxed)) + .unwrap_or_else(|_| std::process::abort()); 0 } } diff --git a/src/tools/miri/tests/genmc/pass/litmus/casdep.rs b/src/tools/miri/tests/genmc/pass/litmus/casdep.rs new file mode 100644 index 000000000000..c376d96b111c --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/casdep.rs @@ -0,0 +1,37 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's test "litmus/casdep". + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); +static Z: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + Y.store(0, Relaxed); + Z.store(0, Relaxed); + + unsafe { + spawn_pthread_closure(|| { + let a = X.load(Relaxed); + let _b = Y.compare_exchange(a, 1, Relaxed, Relaxed); + Z.store(a, Relaxed); + }); + spawn_pthread_closure(|| { + Y.store(2, Relaxed); + }); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/casdep.stderr b/src/tools/miri/tests/genmc/pass/litmus/casdep.stderr new file mode 100644 index 000000000000..01701dfe6918 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/casdep.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 2 diff --git a/src/tools/miri/tests/genmc/pass/litmus/ccr.rs b/src/tools/miri/tests/genmc/pass/litmus/ccr.rs new file mode 100644 index 000000000000..865895b4dcd9 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/ccr.rs @@ -0,0 +1,34 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's test "litmus/ccr". + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + + unsafe { + spawn_pthread_closure(|| { + let expected = 0; + let _ = X.compare_exchange(expected, 42, Relaxed, Relaxed); + }); + spawn_pthread_closure(|| { + let expected = 0; + let _ = X.compare_exchange(expected, 17, Relaxed, Relaxed); + X.load(Relaxed); + }); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/ccr.stderr b/src/tools/miri/tests/genmc/pass/litmus/ccr.stderr new file mode 100644 index 000000000000..01701dfe6918 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/ccr.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 2 diff --git a/src/tools/miri/tests/genmc/pass/litmus/cii.rs b/src/tools/miri/tests/genmc/pass/litmus/cii.rs new file mode 100644 index 000000000000..663c806e667c --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/cii.rs @@ -0,0 +1,35 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's test "litmus/cii". + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + + unsafe { + spawn_pthread_closure(|| { + let expected = 1; + let _ = X.compare_exchange(expected, 2, Relaxed, Relaxed); + }); + spawn_pthread_closure(|| { + X.fetch_add(1, Relaxed); + }); + spawn_pthread_closure(|| { + X.fetch_add(1, Relaxed); + }); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/cii.stderr b/src/tools/miri/tests/genmc/pass/litmus/cii.stderr new file mode 100644 index 000000000000..be75e68fde77 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/cii.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 6 From 18059a0053d186ab47cde823dff64fe490bfbed0 Mon Sep 17 00:00:00 2001 From: Christian Poveda Date: Thu, 11 Sep 2025 15:11:25 -0500 Subject: [PATCH 431/682] Improve `core::alloc` coverage --- library/coretests/tests/alloc.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/library/coretests/tests/alloc.rs b/library/coretests/tests/alloc.rs index 72fdf82c1f8c..a4af6fd32a11 100644 --- a/library/coretests/tests/alloc.rs +++ b/library/coretests/tests/alloc.rs @@ -55,6 +55,30 @@ fn layout_array_edge_cases() { } } +#[test] +fn layout_errors() { + let layout = Layout::new::<[u8; 2]>(); + // Should error if the alignment is not a power of two. + assert!(layout.align_to(3).is_err()); + + // The remaining assertions ensure that the methods error on arithmetic overflow as the + // alignment cannot overflow `isize`. + let size = layout.size(); + let size_max = isize::MAX as usize; + let align_max = size_max / size; + + assert!(layout.align_to(size_max + 1).is_err()); + + assert!(layout.repeat(align_max).is_ok()); + assert!(layout.repeat(align_max + 1).is_err()); + + assert!(layout.repeat_packed(align_max).is_ok()); + assert!(layout.repeat_packed(align_max + 1).is_err()); + + let next = Layout::from_size_align(size_max, 1).unwrap(); + assert!(layout.extend(next).is_err()); +} + #[test] fn layout_debug_shows_log2_of_alignment() { // `Debug` is not stable, but here's what it does right now From 68a3adad2ae1a979b29325b434967e1801145d42 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 16 Aug 2025 21:27:05 +0200 Subject: [PATCH 432/682] other clean-up --- .../src/functions/not_unsafe_ptr_arg_deref.rs | 4 ++-- clippy_lints/src/matches/manual_utils.rs | 4 ++-- clippy_utils/src/ty/mod.rs | 23 ++++++++----------- 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs index 906bbd006d46..72f879530405 100644 --- a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -4,7 +4,7 @@ use rustc_middle::ty; use rustc_span::def_id::LocalDefId; use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::type_is_unsafe_function; +use clippy_utils::ty::is_unsafe_fn; use clippy_utils::visitors::for_each_expr; use clippy_utils::{iter_input_pats, path_to_local}; @@ -51,7 +51,7 @@ fn check_raw_ptr<'tcx>( let typeck = cx.tcx.typeck_body(body.id()); let _: Option = for_each_expr(cx, body.value, |e| { match e.kind { - hir::ExprKind::Call(f, args) if type_is_unsafe_function(cx, typeck.expr_ty(f)) => { + hir::ExprKind::Call(f, args) if is_unsafe_fn(cx, typeck.expr_ty(f)) => { for arg in args { check_arg(cx, &raw_ptrs, arg); } diff --git a/clippy_lints/src/matches/manual_utils.rs b/clippy_lints/src/matches/manual_utils.rs index dbae71bbb1b0..c5cb1ce1b3fd 100644 --- a/clippy_lints/src/matches/manual_utils.rs +++ b/clippy_lints/src/matches/manual_utils.rs @@ -2,7 +2,7 @@ use crate::map_unit_fn::OPTION_MAP_UNIT_FN; use crate::matches::MATCH_AS_REF; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{is_copy, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function}; +use clippy_utils::ty::{is_copy, is_type_diagnostic_item, is_unsafe_fn, peel_mid_ty_refs_is_mutable}; use clippy_utils::{ CaptureKind, can_move_expr_to_closure, expr_requires_coercion, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res, path_to_local_id, peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, @@ -191,7 +191,7 @@ fn can_pass_as_func<'tcx>(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Ex ExprKind::Call(func, [arg]) if path_to_local_id(arg, binding) && cx.typeck_results().expr_adjustments(arg).is_empty() - && !type_is_unsafe_function(cx, cx.typeck_results().expr_ty(func).peel_refs()) => + && !is_unsafe_fn(cx, cx.typeck_results().expr_ty(func).peel_refs()) => { Some(func) }, diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 8e302f9d2ad1..ae6a4ae7fb2d 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -43,13 +43,8 @@ pub use type_certainty::expr_type_is_certain; /// Lower a [`hir::Ty`] to a [`rustc_middle::ty::Ty`]. pub fn ty_from_hir_ty<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> { cx.maybe_typeck_results() - .and_then(|results| { - if results.hir_owner == hir_ty.hir_id.owner { - results.node_type_opt(hir_ty.hir_id) - } else { - None - } - }) + .filter(|results| results.hir_owner == hir_ty.hir_id.owner) + .and_then(|results| results.node_type_opt(hir_ty.hir_id)) .unwrap_or_else(|| lower_ty(cx.tcx, hir_ty)) } @@ -475,6 +470,11 @@ pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default()) } +/// Returns `true` if `ty` denotes an `unsafe fn`. +pub fn is_unsafe_fn<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + ty.is_fn() && ty.fn_sig(cx.tcx).safety().is_unsafe() +} + /// Peels off all references on the type. Returns the underlying type, the number of references /// removed, and whether the pointer is ultimately mutable or not. pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) { @@ -488,15 +488,10 @@ pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) { f(ty, 0, Mutability::Mut) } -/// Returns `true` if the given type is an `unsafe` function. -pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - ty.is_fn() && ty.fn_sig(cx.tcx).safety().is_unsafe() -} - /// Returns the base type for HIR references and pointers. pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> { - match ty.kind { - TyKind::Ptr(ref mut_ty) | TyKind::Ref(_, ref mut_ty) => walk_ptrs_hir_ty(mut_ty.ty), + match &ty.kind { + TyKind::Ptr(mut_ty) | TyKind::Ref(_, mut_ty) => walk_ptrs_hir_ty(mut_ty.ty), _ => ty, } } From 31c7e621c2067808f2a93942073e55971e4e9c3f Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 16 Aug 2025 21:36:38 +0200 Subject: [PATCH 433/682] reorganize `peel_*_ty_refs` functions - give `ty::walk_ptrs_hir_ty` a more standard name `peel_hir_ty_refs_and_ptrs` - move it out of `ty`, since that's for `middle::ty::Ty` - remove `ty::walk_ptrs_ty_depth` for equivalent `peel_middle_ty_refs` - rename the latter into `ty::peel_and_count_ty_refs` - incorporate mutability tracking (from `ty::peel_mid_ty_refs_is_mutable`) into that --- clippy_lints/src/dereference.rs | 7 ++-- clippy_lints/src/manual_abs_diff.rs | 6 +-- .../src/manual_slice_size_calculation.rs | 3 +- clippy_lints/src/matches/manual_ok_err.rs | 16 +++----- clippy_lints/src/matches/manual_utils.rs | 7 ++-- clippy_lints/src/matches/single_match.rs | 8 ++-- clippy_lints/src/methods/implicit_clone.rs | 6 +-- .../src/methods/inefficient_to_string.rs | 4 +- clippy_lints/src/methods/mut_mutex_lock.rs | 5 +-- .../src/methods/unnecessary_to_owned.rs | 13 +++--- clippy_lints/src/methods/useless_asref.rs | 6 +-- clippy_lints/src/redundant_clone.rs | 4 +- clippy_lints/src/redundant_slicing.rs | 8 ++-- clippy_lints/src/size_of_ref.rs | 5 ++- clippy_lints/src/unused_peekable.rs | 6 +-- clippy_utils/src/lib.rs | 13 +++--- clippy_utils/src/ty/mod.rs | 41 +++++-------------- 17 files changed, 66 insertions(+), 92 deletions(-) diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 9aa2f3cf0a5b..9337816c80d9 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::ty::{adjust_derefs_manually_drop, implements_trait, is_manually_drop}; +use clippy_utils::ty::{adjust_derefs_manually_drop, implements_trait, is_manually_drop, peel_and_count_ty_refs}; use clippy_utils::{ DefinedTy, ExprUseNode, expr_use_ctxt, get_parent_expr, is_block_like, is_lint_allowed, path_to_local, - peel_middle_ty_refs, }; use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::FxIndexMap; @@ -942,7 +941,7 @@ fn report<'tcx>( let (expr_str, expr_is_macro_call) = snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app); let ty = typeck.expr_ty(expr); - let (_, ref_count) = peel_middle_ty_refs(ty); + let (_, ref_count, _) = peel_and_count_ty_refs(ty); let deref_str = if ty_changed_count >= ref_count && ref_count != 0 { // a deref call changing &T -> &U requires two deref operators the first time // this occurs. One to remove the reference, a second to call the deref impl. @@ -1045,7 +1044,7 @@ fn report<'tcx>( if let ty::Ref(_, dst, _) = data.adjusted_ty.kind() && dst.is_slice() { - let (src, n_src_refs) = peel_middle_ty_refs(ty); + let (src, n_src_refs, _) = peel_and_count_ty_refs(ty); if n_src_refs >= 2 && src.is_array() { return; } diff --git a/clippy_lints/src/manual_abs_diff.rs b/clippy_lints/src/manual_abs_diff.rs index 288f27db8ca2..5814b6815a1e 100644 --- a/clippy_lints/src/manual_abs_diff.rs +++ b/clippy_lints/src/manual_abs_diff.rs @@ -4,8 +4,8 @@ use clippy_utils::higher::If; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::HasSession as _; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{eq_expr_value, peel_blocks, peel_middle_ty_refs, span_contains_comment}; +use clippy_utils::ty::{is_type_diagnostic_item, peel_and_count_ty_refs}; +use clippy_utils::{eq_expr_value, peel_blocks, span_contains_comment}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -107,7 +107,7 @@ impl ManualAbsDiff { |ty| is_type_diagnostic_item(cx, ty, sym::Duration) && self.msrv.meets(cx, msrvs::DURATION_ABS_DIFF); let a_ty = cx.typeck_results().expr_ty(a).peel_refs(); - let (b_ty, b_n_refs) = peel_middle_ty_refs(cx.typeck_results().expr_ty(b)); + let (b_ty, b_n_refs, _) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(b)); (a_ty == b_ty && (is_int(a_ty) || is_duration(a_ty))).then_some((a_ty, b_n_refs)) } diff --git a/clippy_lints/src/manual_slice_size_calculation.rs b/clippy_lints/src/manual_slice_size_calculation.rs index 0c09a47c9651..de12fa29d02c 100644 --- a/clippy_lints/src/manual_slice_size_calculation.rs +++ b/clippy_lints/src/manual_slice_size_calculation.rs @@ -2,6 +2,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_context; +use clippy_utils::ty::peel_and_count_ty_refs; use clippy_utils::{expr_or_init, is_in_const_context, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; @@ -102,7 +103,7 @@ fn simplify_half<'tcx>( && let ExprKind::MethodCall(method_path, receiver, [], _) = expr1.kind && method_path.ident.name == sym::len && let receiver_ty = cx.typeck_results().expr_ty(receiver) - && let (receiver_ty, refs_count) = clippy_utils::ty::walk_ptrs_ty_depth(receiver_ty) + && let (receiver_ty, refs_count, _) = peel_and_count_ty_refs(receiver_ty) && let ty::Slice(ty1) = receiver_ty.kind() // expr2 is `size_of::()`? && let ExprKind::Call(func, []) = expr2.kind diff --git a/clippy_lints/src/matches/manual_ok_err.rs b/clippy_lints/src/matches/manual_ok_err.rs index edbb556fd976..a8490d6aa7d8 100644 --- a/clippy_lints/src/matches/manual_ok_err.rs +++ b/clippy_lints/src/matches/manual_ok_err.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, reindent_multiline}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{option_arg_ty, peel_mid_ty_refs_is_mutable}; +use clippy_utils::ty::{option_arg_ty, peel_and_count_ty_refs}; use clippy_utils::{get_parent_expr, is_res_lang_ctor, path_res, peel_blocks, span_contains_comment}; use rustc_ast::{BindingMode, Mutability}; use rustc_errors::Applicability; @@ -135,15 +135,11 @@ fn apply_lint(cx: &LateContext<'_>, expr: &Expr<'_>, scrutinee: &Expr<'_>, is_ok let scrut = Sugg::hir_with_applicability(cx, scrutinee, "..", &mut app).maybe_paren(); let scrutinee_ty = cx.typeck_results().expr_ty(scrutinee); - let (_, n_ref, mutability) = peel_mid_ty_refs_is_mutable(scrutinee_ty); - let prefix = if n_ref > 0 { - if mutability == Mutability::Mut { - ".as_mut()" - } else { - ".as_ref()" - } - } else { - "" + let (_, _, mutability) = peel_and_count_ty_refs(scrutinee_ty); + let prefix = match mutability { + Some(Mutability::Mut) => ".as_mut()", + Some(Mutability::Not) => ".as_ref()", + None => "", }; let sugg = format!("{scrut}{prefix}.{method}()"); diff --git a/clippy_lints/src/matches/manual_utils.rs b/clippy_lints/src/matches/manual_utils.rs index c5cb1ce1b3fd..d4bfdb7e440d 100644 --- a/clippy_lints/src/matches/manual_utils.rs +++ b/clippy_lints/src/matches/manual_utils.rs @@ -2,7 +2,7 @@ use crate::map_unit_fn::OPTION_MAP_UNIT_FN; use crate::matches::MATCH_AS_REF; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{is_copy, is_type_diagnostic_item, is_unsafe_fn, peel_mid_ty_refs_is_mutable}; +use clippy_utils::ty::{is_copy, is_type_diagnostic_item, is_unsafe_fn, peel_and_count_ty_refs}; use clippy_utils::{ CaptureKind, can_move_expr_to_closure, expr_requires_coercion, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res, path_to_local_id, peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, @@ -30,8 +30,9 @@ pub(super) fn check_with<'tcx, F>( where F: Fn(&LateContext<'tcx>, &'tcx Pat<'_>, &'tcx Expr<'_>, SyntaxContext) -> Option>, { - let (scrutinee_ty, ty_ref_count, ty_mutability) = - peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee)); + let (scrutinee_ty, ty_ref_count, ty_mutability) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(scrutinee)); + let ty_mutability = ty_mutability.unwrap_or(Mutability::Mut); + if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::Option) && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Option)) { diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index bcf079b70070..83939d325794 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -2,10 +2,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{ SpanRangeExt, expr_block, snippet, snippet_block_with_context, snippet_with_applicability, snippet_with_context, }; -use clippy_utils::ty::implements_trait; -use clippy_utils::{ - is_lint_allowed, is_unit_expr, peel_blocks, peel_hir_pat_refs, peel_middle_ty_refs, peel_n_hir_expr_refs, -}; +use clippy_utils::ty::{implements_trait, peel_and_count_ty_refs}; +use clippy_utils::{is_lint_allowed, is_unit_expr, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs}; use core::ops::ControlFlow; use rustc_arena::DroplessArena; use rustc_errors::{Applicability, Diag}; @@ -133,7 +131,7 @@ fn report_single_pattern( let (pat, pat_ref_count) = peel_hir_pat_refs(arm.pat); let (msg, sugg) = if let PatKind::Expr(_) = pat.kind - && let (ty, ty_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(ex)) + && let (ty, ty_ref_count, _) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(ex)) && let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait() && let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait() && (ty.is_integral() diff --git a/clippy_lints/src/methods/implicit_clone.rs b/clippy_lints/src/methods/implicit_clone.rs index 8a976d1b4dc0..0ba84919395c 100644 --- a/clippy_lints/src/methods/implicit_clone.rs +++ b/clippy_lints/src/methods/implicit_clone.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::implements_trait; -use clippy_utils::{is_diag_item_method, is_diag_trait_item, peel_middle_ty_refs, sym}; +use clippy_utils::ty::{implements_trait, peel_and_count_ty_refs}; +use clippy_utils::{is_diag_item_method, is_diag_trait_item, sym}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -14,7 +14,7 @@ pub fn check(cx: &LateContext<'_>, method_name: Symbol, expr: &hir::Expr<'_>, re && is_clone_like(cx, method_name, method_def_id) && let return_type = cx.typeck_results().expr_ty(expr) && let input_type = cx.typeck_results().expr_ty(recv) - && let (input_type, ref_count) = peel_middle_ty_refs(input_type) + && let (input_type, ref_count, _) = peel_and_count_ty_refs(input_type) && !(ref_count > 0 && is_diag_trait_item(cx, method_def_id, sym::ToOwned)) && let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did())) && return_type == input_type diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index 4ed7de81ea3d..47195fdd65f5 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_type_lang_item, walk_ptrs_ty_depth}; +use clippy_utils::ty::{is_type_lang_item, peel_and_count_ty_refs}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -24,7 +24,7 @@ pub fn check( && let Some(args) = cx.typeck_results().node_args_opt(expr.hir_id) && let arg_ty = cx.typeck_results().expr_ty_adjusted(receiver) && let self_ty = args.type_at(0) - && let (deref_self_ty, deref_count) = walk_ptrs_ty_depth(self_ty) + && let (deref_self_ty, deref_count, _) = peel_and_count_ty_refs(self_ty) && deref_count >= 1 && specializes_tostring(cx, deref_self_ty) { diff --git a/clippy_lints/src/methods/mut_mutex_lock.rs b/clippy_lints/src/methods/mut_mutex_lock.rs index 4235af882b0c..9d2c5e6232d6 100644 --- a/clippy_lints/src/methods/mut_mutex_lock.rs +++ b/clippy_lints/src/methods/mut_mutex_lock.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::expr_custom_deref_adjustment; -use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; +use clippy_utils::ty::{is_type_diagnostic_item, peel_and_count_ty_refs}; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; use rustc_lint::LateContext; @@ -10,8 +10,7 @@ use super::MUT_MUTEX_LOCK; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>, recv: &'tcx Expr<'tcx>, name_span: Span) { if matches!(expr_custom_deref_adjustment(cx, recv), None | Some(Mutability::Mut)) - && let (_, ref_depth, Mutability::Mut) = peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(recv)) - && ref_depth >= 1 + && let (_, _, Some(Mutability::Mut)) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(recv)) && let Some(method_id) = cx.typeck_results().type_dependent_def_id(ex.hir_id) && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Mutex) diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index c1f4904af7c4..640931a82899 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -3,11 +3,12 @@ use super::unnecessary_iter_cloned::{self, is_into_iter}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{SpanRangeExt, snippet}; -use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item}; +use clippy_utils::ty::{ + get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item, peel_and_count_ty_refs, +}; use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::{ - fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, is_expr_temporary_value, peel_middle_ty_refs, - return_ty, sym, + fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, is_expr_temporary_value, return_ty, sym, }; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -119,8 +120,8 @@ fn check_addr_of_expr( }, ] = adjustments[..] && let receiver_ty = cx.typeck_results().expr_ty(receiver) - && let (target_ty, n_target_refs) = peel_middle_ty_refs(*target_ty) - && let (receiver_ty, n_receiver_refs) = peel_middle_ty_refs(receiver_ty) + && let (target_ty, n_target_refs, _) = peel_and_count_ty_refs(*target_ty) + && let (receiver_ty, n_receiver_refs, _) = peel_and_count_ty_refs(receiver_ty) // Only flag cases satisfying at least one of the following three conditions: // * the referent and receiver types are distinct // * the referent/receiver type is a copyable array @@ -385,7 +386,7 @@ fn check_other_call_arg<'tcx>( && let fn_sig = cx.tcx.fn_sig(callee_def_id).instantiate_identity().skip_binder() && let Some(i) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == maybe_arg.hir_id) && let Some(input) = fn_sig.inputs().get(i) - && let (input, n_refs) = peel_middle_ty_refs(*input) + && let (input, n_refs, _) = peel_and_count_ty_refs(*input) && let (trait_predicates, _) = get_input_traits_and_projections(cx, callee_def_id, input) && let Some(sized_def_id) = cx.tcx.lang_items().sized_trait() && let Some(meta_sized_def_id) = cx.tcx.lang_items().meta_sized_trait() diff --git a/clippy_lints/src/methods/useless_asref.rs b/clippy_lints/src/methods/useless_asref.rs index 38fad239f679..e56f4b80d017 100644 --- a/clippy_lints/src/methods/useless_asref.rs +++ b/clippy_lints/src/methods/useless_asref.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{implements_trait, should_call_clone_as_function, walk_ptrs_ty_depth}; +use clippy_utils::ty::{implements_trait, peel_and_count_ty_refs, should_call_clone_as_function}; use clippy_utils::{get_parent_expr, is_diag_trait_item, path_to_local_id, peel_blocks, strip_pat_refs}; use rustc_errors::Applicability; use rustc_hir::{self as hir, LangItem}; @@ -50,8 +50,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: Symbo // check if the type after `as_ref` or `as_mut` is the same as before let rcv_ty = cx.typeck_results().expr_ty(recvr); let res_ty = cx.typeck_results().expr_ty(expr); - let (base_res_ty, res_depth) = walk_ptrs_ty_depth(res_ty); - let (base_rcv_ty, rcv_depth) = walk_ptrs_ty_depth(rcv_ty); + let (base_res_ty, res_depth, _) = peel_and_count_ty_refs(res_ty); + let (base_rcv_ty, rcv_depth, _) = peel_and_count_ty_refs(rcv_ty); if base_rcv_ty == base_res_ty && rcv_depth >= res_depth { if let Some(parent) = get_parent_expr(cx, expr) { // allow the `as_ref` or `as_mut` if it is followed by another method call diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 1d58cdd26d88..de6766cbe94a 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::fn_has_unsatisfiable_preds; use clippy_utils::mir::{LocalUsage, PossibleBorrowerMap, visit_local_usage}; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::{has_drop, is_copy, is_type_lang_item, walk_ptrs_ty_depth}; +use clippy_utils::ty::{has_drop, is_copy, is_type_lang_item, peel_and_count_ty_refs}; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, LangItem, def_id}; @@ -263,7 +263,7 @@ fn is_call_with_ref_arg<'tcx>( && args.len() == 1 && let mir::Operand::Move(mir::Place { local, .. }) = &args[0].node && let ty::FnDef(def_id, _) = *func.ty(mir, cx.tcx).kind() - && let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].node.ty(mir, cx.tcx)) + && let (inner_ty, 1, _) = peel_and_count_ty_refs(args[0].node.ty(mir, cx.tcx)) && !is_copy(cx, inner_ty) { Some((def_id, *local, inner_ty, destination.as_local()?)) diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs index 324a05cdcc0c..a358eff2ce55 100644 --- a/clippy_lints/src/redundant_slicing.rs +++ b/clippy_lints/src/redundant_slicing.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::get_parent_expr; use clippy_utils::source::snippet_with_context; -use clippy_utils::ty::is_type_lang_item; -use clippy_utils::{get_parent_expr, peel_middle_ty_refs}; +use clippy_utils::ty::{is_type_lang_item, peel_and_count_ty_refs}; use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; @@ -82,8 +82,8 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { && let ExprKind::Index(indexed, range, _) = addressee.kind && is_type_lang_item(cx, cx.typeck_results().expr_ty_adjusted(range), LangItem::RangeFull) { - let (expr_ty, expr_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(expr)); - let (indexed_ty, indexed_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(indexed)); + let (expr_ty, expr_ref_count, _) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(expr)); + let (indexed_ty, indexed_ref_count, _) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(indexed)); let parent_expr = get_parent_expr(cx, expr); let needs_parens_for_prefix = parent_expr.is_some_and(|parent| cx.precedence(parent) > ExprPrecedence::Prefix); diff --git a/clippy_lints/src/size_of_ref.rs b/clippy_lints/src/size_of_ref.rs index 60d923bcd77e..606e852aae9e 100644 --- a/clippy_lints/src/size_of_ref.rs +++ b/clippy_lints/src/size_of_ref.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::{path_def_id, peel_middle_ty_refs}; +use clippy_utils::path_def_id; +use clippy_utils::ty::peel_and_count_ty_refs; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -59,7 +60,7 @@ impl LateLintPass<'_> for SizeOfRef { && let Some(def_id) = path_def_id(cx, path) && cx.tcx.is_diagnostic_item(sym::mem_size_of_val, def_id) && let arg_ty = cx.typeck_results().expr_ty(arg) - && peel_middle_ty_refs(arg_ty).1 > 1 + && peel_and_count_ty_refs(arg_ty).1 > 1 { span_lint_and_help( cx, diff --git a/clippy_lints/src/unused_peekable.rs b/clippy_lints/src/unused_peekable.rs index 1f5351e32aac..5224b62e9fc7 100644 --- a/clippy_lints/src/unused_peekable.rs +++ b/clippy_lints/src/unused_peekable.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; +use clippy_utils::ty::{is_type_diagnostic_item, peel_and_count_ty_refs}; use clippy_utils::{fn_def_id, is_trait_method, path_to_local_id, peel_ref_operators, sym}; use rustc_ast::Mutability; use rustc_hir::intravisit::{Visitor, walk_expr}; @@ -61,7 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedPeekable { && let Some(init) = local.init && !init.span.from_expansion() && let Some(ty) = cx.typeck_results().expr_ty_opt(init) - && let (ty, _, Mutability::Mut) = peel_mid_ty_refs_is_mutable(ty) + && let (ty, _, None | Some(Mutability::Mut)) = peel_and_count_ty_refs(ty) && is_type_diagnostic_item(cx, ty, sym::IterPeekable) { let mut vis = PeekableVisitor::new(cx, binding); @@ -211,7 +211,7 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> { fn arg_is_mut_peekable(cx: &LateContext<'_>, arg: &Expr<'_>) -> bool { if let Some(ty) = cx.typeck_results().expr_ty_opt(arg) - && let (ty, _, Mutability::Mut) = peel_mid_ty_refs_is_mutable(ty) + && let (ty, _, None | Some(Mutability::Mut)) = peel_and_count_ty_refs(ty) && is_type_diagnostic_item(cx, ty, sym::IterPeekable) { true diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 14b64eb4d54e..5223cd872a68 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -2380,15 +2380,12 @@ pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) } } -/// Peels off all references on the type. Returns the underlying type and the number of references -/// removed. -pub fn peel_middle_ty_refs(mut ty: Ty<'_>) -> (Ty<'_>, usize) { - let mut count = 0; - while let rustc_ty::Ref(_, dest_ty, _) = ty.kind() { - ty = *dest_ty; - count += 1; +/// Returns the base type for HIR references and pointers. +pub fn peel_hir_ty_refs_and_ptrs<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> { + match &ty.kind { + TyKind::Ptr(mut_ty) | TyKind::Ref(_, mut_ty) => peel_hir_ty_refs_and_ptrs(mut_ty.ty), + _ => ty, } - (ty, count) } /// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index ae6a4ae7fb2d..5991c36c5fe3 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -11,7 +11,7 @@ use rustc_hir as hir; use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::{Expr, FnDecl, LangItem, TyKind, find_attr}; +use rustc_hir::{Expr, FnDecl, LangItem, find_attr}; use rustc_hir_analysis::lower_ty; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; @@ -476,36 +476,17 @@ pub fn is_unsafe_fn<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { } /// Peels off all references on the type. Returns the underlying type, the number of references -/// removed, and whether the pointer is ultimately mutable or not. -pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) { - fn f(ty: Ty<'_>, count: usize, mutability: Mutability) -> (Ty<'_>, usize, Mutability) { - match ty.kind() { - ty::Ref(_, ty, Mutability::Mut) => f(*ty, count + 1, mutability), - ty::Ref(_, ty, Mutability::Not) => f(*ty, count + 1, Mutability::Not), - _ => (ty, count, mutability), - } +/// removed, and, if there were any such references, whether the pointer is ultimately mutable or +/// not. +pub fn peel_and_count_ty_refs(mut ty: Ty<'_>) -> (Ty<'_>, usize, Option) { + let mut count = 0; + let mut mutbl = None; + while let ty::Ref(_, dest_ty, m) = ty.kind() { + ty = *dest_ty; + count += 1; + mutbl.replace(mutbl.map_or(*m, |mutbl: Mutability| mutbl.min(*m))); } - f(ty, 0, Mutability::Mut) -} - -/// Returns the base type for HIR references and pointers. -pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> { - match &ty.kind { - TyKind::Ptr(mut_ty) | TyKind::Ref(_, mut_ty) => walk_ptrs_hir_ty(mut_ty.ty), - _ => ty, - } -} - -/// Returns the base type for references and raw pointers, and count reference -/// depth. -pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) { - fn inner(ty: Ty<'_>, depth: usize) -> (Ty<'_>, usize) { - match ty.kind() { - ty::Ref(_, ty, _) => inner(*ty, depth + 1), - _ => (ty, depth), - } - } - inner(ty, 0) + (ty, count, mutbl) } /// Returns `true` if types `a` and `b` are same types having same `Const` generic args, From 8938bb268d72deae0bb8a7a1eebe7563949ebc60 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 10 Sep 2025 13:50:11 +0200 Subject: [PATCH 434/682] clean-up tests --- tests/ui/multiple_unsafe_ops_per_block.rs | 82 ++++++----- tests/ui/multiple_unsafe_ops_per_block.stderr | 136 +++++++++--------- 2 files changed, 108 insertions(+), 110 deletions(-) diff --git a/tests/ui/multiple_unsafe_ops_per_block.rs b/tests/ui/multiple_unsafe_ops_per_block.rs index 016fd89a7b7a..8f5408010dc7 100644 --- a/tests/ui/multiple_unsafe_ops_per_block.rs +++ b/tests/ui/multiple_unsafe_ops_per_block.rs @@ -1,10 +1,6 @@ //@needs-asm-support //@aux-build:proc_macros.rs -#![allow(unused)] -#![allow(deref_nullptr)] -#![allow(clippy::unnecessary_operation)] -#![allow(dropping_copy_types)] -#![allow(clippy::assign_op_pattern)] +#![expect(clippy::unnecessary_operation, dropping_copy_types)] #![warn(clippy::multiple_unsafe_ops_per_block)] extern crate proc_macros; @@ -105,17 +101,17 @@ fn correct3() { } } -// tests from the issue (https://github.com/rust-lang/rust-clippy/issues/10064) +fn issue10064() { + unsafe fn read_char_bad(ptr: *const u8) -> char { + unsafe { char::from_u32_unchecked(*ptr.cast::()) } + //~^ multiple_unsafe_ops_per_block + } -unsafe fn read_char_bad(ptr: *const u8) -> char { - unsafe { char::from_u32_unchecked(*ptr.cast::()) } - //~^ multiple_unsafe_ops_per_block -} - -// no lint -unsafe fn read_char_good(ptr: *const u8) -> char { - let int_value = unsafe { *ptr.cast::() }; - unsafe { core::char::from_u32_unchecked(int_value) } + // no lint + unsafe fn read_char_good(ptr: *const u8) -> char { + let int_value = unsafe { *ptr.cast::() }; + unsafe { core::char::from_u32_unchecked(int_value) } + } } // no lint @@ -126,39 +122,41 @@ fn issue10259() { }); } -fn _fn_ptr(x: unsafe fn()) { - unsafe { - //~^ multiple_unsafe_ops_per_block - x(); - x(); - } -} - -fn _assoc_const() { - trait X { - const X: unsafe fn(); - } - fn _f() { +fn issue10367() { + fn fn_ptr(x: unsafe fn()) { unsafe { //~^ multiple_unsafe_ops_per_block - T::X(); - T::X(); + x(); + x(); + } + } + + fn assoc_const() { + trait X { + const X: unsafe fn(); + } + fn _f() { + unsafe { + //~^ multiple_unsafe_ops_per_block + T::X(); + T::X(); + } + } + } + + fn field_fn_ptr(x: unsafe fn()) { + struct X(unsafe fn()); + let x = X(x); + unsafe { + //~^ multiple_unsafe_ops_per_block + x.0(); + x.0(); } } } -fn _field_fn_ptr(x: unsafe fn()) { - struct X(unsafe fn()); - let x = X(x); - unsafe { - //~^ multiple_unsafe_ops_per_block - x.0(); - x.0(); - } -} - -// await expands to an unsafe block with several operations, but this is fine.: #11312 -async fn await_desugaring_silent() { +// await expands to an unsafe block with several operations, but this is fine. +async fn issue11312() { async fn helper() {} helper().await; diff --git a/tests/ui/multiple_unsafe_ops_per_block.stderr b/tests/ui/multiple_unsafe_ops_per_block.stderr index 3130cecc2525..895532187170 100644 --- a/tests/ui/multiple_unsafe_ops_per_block.stderr +++ b/tests/ui/multiple_unsafe_ops_per_block.stderr @@ -1,5 +1,5 @@ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:38:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:34:5 | LL | / unsafe { LL | | @@ -9,12 +9,12 @@ LL | | } | |_____^ | note: modification of a mutable static occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:40:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:36:9 | LL | STATIC += 1; | ^^^^^^^^^^^ note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:41:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:37:9 | LL | not_very_safe(); | ^^^^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL | not_very_safe(); = help: to override `-D warnings` add `#[allow(clippy::multiple_unsafe_ops_per_block)]` error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:48:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:44:5 | LL | / unsafe { LL | | @@ -32,18 +32,18 @@ LL | | } | |_____^ | note: union field access occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:50:14 + --> tests/ui/multiple_unsafe_ops_per_block.rs:46:14 | LL | drop(u.u); | ^^^ note: raw pointer dereference occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:51:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:47:9 | LL | *raw_ptr(); | ^^^^^^^^^^ error: this `unsafe` block contains 3 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:56:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:52:5 | LL | / unsafe { LL | | @@ -54,23 +54,23 @@ LL | | } | |_____^ | note: inline assembly used here - --> tests/ui/multiple_unsafe_ops_per_block.rs:58:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:54:9 | LL | asm!("nop"); | ^^^^^^^^^^^ note: unsafe method call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:59:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:55:9 | LL | sample.not_very_safe(); | ^^^^^^^^^^^^^^^^^^^^^^ note: modification of a mutable static occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:60:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:56:9 | LL | STATIC = 0; | ^^^^^^^^^^ error: this `unsafe` block contains 6 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:66:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:62:5 | LL | / unsafe { LL | | @@ -82,115 +82,115 @@ LL | | } | |_____^ | note: union field access occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:68:14 + --> tests/ui/multiple_unsafe_ops_per_block.rs:64:14 | LL | drop(u.u); | ^^^ note: access of a mutable static occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:69:14 + --> tests/ui/multiple_unsafe_ops_per_block.rs:65:14 | LL | drop(STATIC); | ^^^^^^ note: unsafe method call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:70:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:66:9 | LL | sample.not_very_safe(); | ^^^^^^^^^^^^^^^^^^^^^^ note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:71:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:67:9 | LL | not_very_safe(); | ^^^^^^^^^^^^^^^ note: raw pointer dereference occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:72:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:68:9 | LL | *raw_ptr(); | ^^^^^^^^^^ note: inline assembly used here - --> tests/ui/multiple_unsafe_ops_per_block.rs:73:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:69:9 | LL | asm!("nop"); | ^^^^^^^^^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:111:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:106:9 | -LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:111:14 + --> tests/ui/multiple_unsafe_ops_per_block.rs:106:18 | -LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: raw pointer dereference occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:111:39 + --> tests/ui/multiple_unsafe_ops_per_block.rs:106:43 | -LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } - | ^^^^^^^^^^^^^^^^^^ +LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } + | ^^^^^^^^^^^^^^^^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:130:5 - | -LL | / unsafe { -LL | | -LL | | x(); -LL | | x(); -LL | | } - | |_____^ - | -note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:132:9 - | -LL | x(); - | ^^^ -note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:133:9 - | -LL | x(); - | ^^^ - -error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:142:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:127:9 | LL | / unsafe { LL | | -LL | | T::X(); -LL | | T::X(); +LL | | x(); +LL | | x(); LL | | } | |_________^ | note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:144:13 + --> tests/ui/multiple_unsafe_ops_per_block.rs:129:13 | -LL | T::X(); - | ^^^^^^ +LL | x(); + | ^^^ note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:145:13 + --> tests/ui/multiple_unsafe_ops_per_block.rs:130:13 | -LL | T::X(); - | ^^^^^^ +LL | x(); + | ^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:153:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:139:13 | -LL | / unsafe { +LL | / unsafe { LL | | -LL | | x.0(); -LL | | x.0(); -LL | | } - | |_____^ +LL | | T::X(); +LL | | T::X(); +LL | | } + | |_____________^ | note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:155:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:141:17 | -LL | x.0(); - | ^^^^^ +LL | T::X(); + | ^^^^^^ note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:156:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:142:17 | -LL | x.0(); - | ^^^^^ +LL | T::X(); + | ^^^^^^ + +error: this `unsafe` block contains 2 unsafe operations, expected only one + --> tests/ui/multiple_unsafe_ops_per_block.rs:150:9 + | +LL | / unsafe { +LL | | +LL | | x.0(); +LL | | x.0(); +LL | | } + | |_________^ + | +note: unsafe function call occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:152:13 + | +LL | x.0(); + | ^^^^^ +note: unsafe function call occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:153:13 + | +LL | x.0(); + | ^^^^^ error: aborting due to 8 previous errors From 51e3b6238d66fb9f70f2750fed3a6ee2f610ce9d Mon Sep 17 00:00:00 2001 From: Christian Poveda Date: Fri, 12 Sep 2025 11:22:07 -0500 Subject: [PATCH 435/682] Improve `core::char` coverage --- library/coretests/tests/char.rs | 43 +++++++++++++++++++++++++++++++++ library/coretests/tests/lib.rs | 1 + 2 files changed, 44 insertions(+) diff --git a/library/coretests/tests/char.rs b/library/coretests/tests/char.rs index 852f073bae18..6f94065b2d92 100644 --- a/library/coretests/tests/char.rs +++ b/library/coretests/tests/char.rs @@ -220,6 +220,7 @@ fn test_escape_default() { } assert_eq!(string('\n'), "\\n"); assert_eq!(string('\r'), "\\r"); + assert_eq!(string('\t'), "\\t"); assert_eq!(string('\''), "\\'"); assert_eq!(string('"'), "\\\""); assert_eq!(string(' '), " "); @@ -417,3 +418,45 @@ fn eu_iterator_specializations() { check('\u{12340}'); check('\u{10FFFF}'); } + +#[test] +#[should_panic] +fn test_from_digit_radix_too_high() { + let _ = char::from_digit(0, 37); +} + +#[test] +fn test_from_digit_invalid_radix() { + assert!(char::from_digit(10, 9).is_none()); +} + +#[test] +#[should_panic] +fn test_to_digit_radix_too_low() { + let _ = 'a'.to_digit(1); +} + +#[test] +#[should_panic] +fn test_to_digit_radix_too_high() { + let _ = 'a'.to_digit(37); +} + +#[test] +fn test_as_ascii_invalid() { + assert!('❤'.as_ascii().is_none()); +} + +#[test] +#[should_panic] +fn test_encode_utf8_raw_buffer_too_small() { + let mut buf = [0u8; 1]; + let _ = char::encode_utf8_raw('ß'.into(), &mut buf); +} + +#[test] +#[should_panic] +fn test_encode_utf16_raw_buffer_too_small() { + let mut buf = [0u16; 1]; + let _ = char::encode_utf16_raw('𐐷'.into(), &mut buf); +} diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 1bdaa6965f64..3b5334391ac9 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -13,6 +13,7 @@ #![feature(bool_to_result)] #![feature(bstr)] #![feature(cfg_target_has_reliable_f16_f128)] +#![feature(char_internals)] #![feature(char_max_len)] #![feature(clone_to_uninit)] #![feature(const_convert)] From da6d23b116471cdaccd60e8096c97cae259cc79e Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 10 Sep 2025 13:28:44 +0200 Subject: [PATCH 436/682] fix: ignore unsafe ops from `.await` desugaring --- .../src/multiple_unsafe_ops_per_block.rs | 8 ++ tests/ui/multiple_unsafe_ops_per_block.rs | 49 ++++++- tests/ui/multiple_unsafe_ops_per_block.stderr | 123 +++++++++++++----- 3 files changed, 149 insertions(+), 31 deletions(-) diff --git a/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/clippy_lints/src/multiple_unsafe_ops_per_block.rs index c6c27e22b90e..bc5e72270f4e 100644 --- a/clippy_lints/src/multiple_unsafe_ops_per_block.rs +++ b/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -1,3 +1,4 @@ +use clippy_utils::desugar_await; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::visitors::{Descend, Visitable, for_each_expr}; use core::ops::ControlFlow::Continue; @@ -97,6 +98,13 @@ fn collect_unsafe_exprs<'tcx>( ) { for_each_expr(cx, node, |expr| { match expr.kind { + // The `await` itself will desugar to two unsafe calls, but we should ignore those. + // Instead, check the expression that is `await`ed + _ if let Some(e) = desugar_await(expr) => { + collect_unsafe_exprs(cx, e, unsafe_ops); + return Continue(Descend::No); + }, + ExprKind::InlineAsm(_) => unsafe_ops.push(("inline assembly used here", expr.span)), ExprKind::Field(e, _) => { diff --git a/tests/ui/multiple_unsafe_ops_per_block.rs b/tests/ui/multiple_unsafe_ops_per_block.rs index 8f5408010dc7..132673d5164a 100644 --- a/tests/ui/multiple_unsafe_ops_per_block.rs +++ b/tests/ui/multiple_unsafe_ops_per_block.rs @@ -1,6 +1,10 @@ //@needs-asm-support //@aux-build:proc_macros.rs -#![expect(clippy::unnecessary_operation, dropping_copy_types)] +#![expect( + dropping_copy_types, + clippy::unnecessary_operation, + clippy::unnecessary_literal_unwrap +)] #![warn(clippy::multiple_unsafe_ops_per_block)] extern crate proc_macros; @@ -162,4 +166,47 @@ async fn issue11312() { helper().await; } +async fn issue13879() { + async fn foo() {} + + // no lint: nothing unsafe beyond the `await` which we ignore + unsafe { + foo().await; + } + + // no lint: only one unsafe call beyond the `await` + unsafe { + not_very_safe(); + foo().await; + } + + // lint: two unsafe calls beyond the `await` + unsafe { + //~^ multiple_unsafe_ops_per_block + not_very_safe(); + STATIC += 1; + foo().await; + } + + async unsafe fn foo_unchecked() {} + + // no lint: only one unsafe call in the `await`ed expr + unsafe { + foo_unchecked().await; + } + + // lint: one unsafe call in the `await`ed expr, and one outside + unsafe { + //~^ multiple_unsafe_ops_per_block + not_very_safe(); + foo_unchecked().await; + } + + // lint: two unsafe calls in the `await`ed expr + unsafe { + //~^ multiple_unsafe_ops_per_block + Some(foo_unchecked()).unwrap_unchecked().await; + } +} + fn main() {} diff --git a/tests/ui/multiple_unsafe_ops_per_block.stderr b/tests/ui/multiple_unsafe_ops_per_block.stderr index 895532187170..922a464c6b6e 100644 --- a/tests/ui/multiple_unsafe_ops_per_block.stderr +++ b/tests/ui/multiple_unsafe_ops_per_block.stderr @@ -1,5 +1,5 @@ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:34:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:38:5 | LL | / unsafe { LL | | @@ -9,12 +9,12 @@ LL | | } | |_____^ | note: modification of a mutable static occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:36:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:40:9 | LL | STATIC += 1; | ^^^^^^^^^^^ note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:37:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:41:9 | LL | not_very_safe(); | ^^^^^^^^^^^^^^^ @@ -22,7 +22,7 @@ LL | not_very_safe(); = help: to override `-D warnings` add `#[allow(clippy::multiple_unsafe_ops_per_block)]` error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:44:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:48:5 | LL | / unsafe { LL | | @@ -32,18 +32,18 @@ LL | | } | |_____^ | note: union field access occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:46:14 + --> tests/ui/multiple_unsafe_ops_per_block.rs:50:14 | LL | drop(u.u); | ^^^ note: raw pointer dereference occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:47:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:51:9 | LL | *raw_ptr(); | ^^^^^^^^^^ error: this `unsafe` block contains 3 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:52:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:56:5 | LL | / unsafe { LL | | @@ -54,23 +54,23 @@ LL | | } | |_____^ | note: inline assembly used here - --> tests/ui/multiple_unsafe_ops_per_block.rs:54:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:58:9 | LL | asm!("nop"); | ^^^^^^^^^^^ note: unsafe method call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:55:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:59:9 | LL | sample.not_very_safe(); | ^^^^^^^^^^^^^^^^^^^^^^ note: modification of a mutable static occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:56:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:60:9 | LL | STATIC = 0; | ^^^^^^^^^^ error: this `unsafe` block contains 6 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:62:5 + --> tests/ui/multiple_unsafe_ops_per_block.rs:66:5 | LL | / unsafe { LL | | @@ -82,55 +82,55 @@ LL | | } | |_____^ | note: union field access occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:64:14 + --> tests/ui/multiple_unsafe_ops_per_block.rs:68:14 | LL | drop(u.u); | ^^^ note: access of a mutable static occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:65:14 + --> tests/ui/multiple_unsafe_ops_per_block.rs:69:14 | LL | drop(STATIC); | ^^^^^^ note: unsafe method call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:66:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:70:9 | LL | sample.not_very_safe(); | ^^^^^^^^^^^^^^^^^^^^^^ note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:67:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:71:9 | LL | not_very_safe(); | ^^^^^^^^^^^^^^^ note: raw pointer dereference occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:68:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:72:9 | LL | *raw_ptr(); | ^^^^^^^^^^ note: inline assembly used here - --> tests/ui/multiple_unsafe_ops_per_block.rs:69:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:73:9 | LL | asm!("nop"); | ^^^^^^^^^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:106:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:110:9 | LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:106:18 + --> tests/ui/multiple_unsafe_ops_per_block.rs:110:18 | LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: raw pointer dereference occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:106:43 + --> tests/ui/multiple_unsafe_ops_per_block.rs:110:43 | LL | unsafe { char::from_u32_unchecked(*ptr.cast::()) } | ^^^^^^^^^^^^^^^^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:127:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:131:9 | LL | / unsafe { LL | | @@ -140,18 +140,18 @@ LL | | } | |_________^ | note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:129:13 + --> tests/ui/multiple_unsafe_ops_per_block.rs:133:13 | LL | x(); | ^^^ note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:130:13 + --> tests/ui/multiple_unsafe_ops_per_block.rs:134:13 | LL | x(); | ^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:139:13 + --> tests/ui/multiple_unsafe_ops_per_block.rs:143:13 | LL | / unsafe { LL | | @@ -161,18 +161,18 @@ LL | | } | |_____________^ | note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:141:17 + --> tests/ui/multiple_unsafe_ops_per_block.rs:145:17 | LL | T::X(); | ^^^^^^ note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:142:17 + --> tests/ui/multiple_unsafe_ops_per_block.rs:146:17 | LL | T::X(); | ^^^^^^ error: this `unsafe` block contains 2 unsafe operations, expected only one - --> tests/ui/multiple_unsafe_ops_per_block.rs:150:9 + --> tests/ui/multiple_unsafe_ops_per_block.rs:154:9 | LL | / unsafe { LL | | @@ -182,15 +182,78 @@ LL | | } | |_________^ | note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:152:13 + --> tests/ui/multiple_unsafe_ops_per_block.rs:156:13 | LL | x.0(); | ^^^^^ note: unsafe function call occurs here - --> tests/ui/multiple_unsafe_ops_per_block.rs:153:13 + --> tests/ui/multiple_unsafe_ops_per_block.rs:157:13 | LL | x.0(); | ^^^^^ -error: aborting due to 8 previous errors +error: this `unsafe` block contains 2 unsafe operations, expected only one + --> tests/ui/multiple_unsafe_ops_per_block.rs:184:5 + | +LL | / unsafe { +LL | | +LL | | not_very_safe(); +LL | | STATIC += 1; +LL | | foo().await; +LL | | } + | |_____^ + | +note: unsafe function call occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:186:9 + | +LL | not_very_safe(); + | ^^^^^^^^^^^^^^^ +note: modification of a mutable static occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:187:9 + | +LL | STATIC += 1; + | ^^^^^^^^^^^ + +error: this `unsafe` block contains 2 unsafe operations, expected only one + --> tests/ui/multiple_unsafe_ops_per_block.rs:199:5 + | +LL | / unsafe { +LL | | +LL | | not_very_safe(); +LL | | foo_unchecked().await; +LL | | } + | |_____^ + | +note: unsafe function call occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:201:9 + | +LL | not_very_safe(); + | ^^^^^^^^^^^^^^^ +note: unsafe function call occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:202:9 + | +LL | foo_unchecked().await; + | ^^^^^^^^^^^^^^^ + +error: this `unsafe` block contains 2 unsafe operations, expected only one + --> tests/ui/multiple_unsafe_ops_per_block.rs:206:5 + | +LL | / unsafe { +LL | | +LL | | Some(foo_unchecked()).unwrap_unchecked().await; +LL | | } + | |_____^ + | +note: unsafe method call occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:208:9 + | +LL | Some(foo_unchecked()).unwrap_unchecked().await; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: unsafe function call occurs here + --> tests/ui/multiple_unsafe_ops_per_block.rs:208:14 + | +LL | Some(foo_unchecked()).unwrap_unchecked().await; + | ^^^^^^^^^^^^^^^ + +error: aborting due to 11 previous errors From 2e652d7d13ff9f1944b92a4f20248a61a96fe71c Mon Sep 17 00:00:00 2001 From: Christian Poveda Date: Fri, 12 Sep 2025 11:34:54 -0500 Subject: [PATCH 437/682] Improve `core::fmt` coverage --- library/coretests/tests/fmt/builders.rs | 15 +++++++++++++ library/coretests/tests/fmt/mod.rs | 30 +++++++++++++++++++++++++ library/coretests/tests/fmt/num.rs | 6 +++++ library/coretests/tests/lib.rs | 1 + 4 files changed, 52 insertions(+) diff --git a/library/coretests/tests/fmt/builders.rs b/library/coretests/tests/fmt/builders.rs index ba4801f5912b..156eebb1e9d2 100644 --- a/library/coretests/tests/fmt/builders.rs +++ b/library/coretests/tests/fmt/builders.rs @@ -173,6 +173,21 @@ mod debug_struct { format!("{Bar:#?}") ); } + + #[test] + fn test_field_with() { + struct Foo; + impl fmt::Debug for Foo { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_struct("Foo") + .field_with("bar", |f| f.write_str("true")) + .field_with("baz", |f| f.write_str("false")) + .finish() + } + } + + assert_eq!("Foo {\n bar: true,\n baz: false,\n}", format!("{Foo:#?}")) + } } mod debug_tuple { diff --git a/library/coretests/tests/fmt/mod.rs b/library/coretests/tests/fmt/mod.rs index 16f116d25901..586e890befe0 100644 --- a/library/coretests/tests/fmt/mod.rs +++ b/library/coretests/tests/fmt/mod.rs @@ -57,6 +57,36 @@ fn test_fmt_debug_of_raw_pointers() { check_fmt(vtable as *const dyn Debug, "Pointer { addr: ", ", metadata: DynMetadata("); } +#[test] +fn test_fmt_debug_of_mut_reference() { + let mut x: u32 = 0; + + assert_eq!(format!("{:?}", &mut x), "0"); +} + +#[test] +fn test_default_write_impls() { + use core::fmt::Write; + + struct Buf(String); + + impl Write for Buf { + fn write_str(&mut self, s: &str) -> core::fmt::Result { + self.0.write_str(s) + } + } + + let mut buf = Buf(String::new()); + buf.write_char('a').unwrap(); + + assert_eq!(buf.0, "a"); + + let mut buf = Buf(String::new()); + buf.write_fmt(format_args!("a")).unwrap(); + + assert_eq!(buf.0, "a"); +} + #[test] fn test_estimated_capacity() { assert_eq!(format_args!("").estimated_capacity(), 0); diff --git a/library/coretests/tests/fmt/num.rs b/library/coretests/tests/fmt/num.rs index 4c145b484ddd..052b16db5214 100644 --- a/library/coretests/tests/fmt/num.rs +++ b/library/coretests/tests/fmt/num.rs @@ -323,3 +323,9 @@ fn test_format_debug_hex() { assert_eq!(format!("{:02x?}", b"Foo\0"), "[46, 6f, 6f, 00]"); assert_eq!(format!("{:02X?}", b"Foo\0"), "[46, 6F, 6F, 00]"); } + +#[test] +#[should_panic = "Formatting argument out of range"] +fn test_rt_width_too_long() { + let _ = format!("Hello {:width$}!", "x", width = u16::MAX as usize + 1); +} diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 1bdaa6965f64..6f42f1832682 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -31,6 +31,7 @@ #![feature(core_private_bignum)] #![feature(core_private_diy_float)] #![feature(cstr_display)] +#![feature(debug_closure_helpers)] #![feature(dec2flt)] #![feature(drop_guard)] #![feature(duration_constants)] From 8f84324ec9d66ed8329331b4ade07ab7f4b124fa Mon Sep 17 00:00:00 2001 From: Valdemar Erk Date: Wed, 10 Sep 2025 07:04:37 +0200 Subject: [PATCH 438/682] autofix for `rest_pat_in_fully_bound_structs` --- .../matches/rest_pat_in_fully_bound_struct.rs | 10 ++- .../ui/rest_pat_in_fully_bound_structs.fixed | 61 +++++++++++++++++++ .../ui/rest_pat_in_fully_bound_structs.stderr | 18 +++++- 3 files changed, 83 insertions(+), 6 deletions(-) create mode 100644 tests/ui/rest_pat_in_fully_bound_structs.fixed diff --git a/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs b/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs index ae09c2e87d6b..1130d82ab78f 100644 --- a/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs +++ b/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs @@ -7,7 +7,7 @@ use super::REST_PAT_IN_FULLY_BOUND_STRUCTS; pub(crate) fn check(cx: &LateContext<'_>, pat: &Pat<'_>) { if !pat.span.from_expansion() - && let PatKind::Struct(QPath::Resolved(_, path), fields, Some(_)) = pat.kind + && let PatKind::Struct(QPath::Resolved(_, path), fields, Some(dotdot)) = pat.kind && let Some(def_id) = path.res.opt_def_id() && let ty = cx.tcx.type_of(def_id).instantiate_identity() && let ty::Adt(def, _) = ty.kind() @@ -15,14 +15,18 @@ pub(crate) fn check(cx: &LateContext<'_>, pat: &Pat<'_>) { && fields.len() == def.non_enum_variant().fields.len() && !def.non_enum_variant().is_field_list_non_exhaustive() { - #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then( cx, REST_PAT_IN_FULLY_BOUND_STRUCTS, pat.span, "unnecessary use of `..` pattern in struct binding. All fields were already bound", |diag| { - diag.help("consider removing `..` from this binding"); + diag.span_suggestion_verbose( + dotdot, + "consider removing `..` from this binding", + "", + rustc_errors::Applicability::MachineApplicable, + ); }, ); } diff --git a/tests/ui/rest_pat_in_fully_bound_structs.fixed b/tests/ui/rest_pat_in_fully_bound_structs.fixed new file mode 100644 index 000000000000..ac200b3b19b7 --- /dev/null +++ b/tests/ui/rest_pat_in_fully_bound_structs.fixed @@ -0,0 +1,61 @@ +#![warn(clippy::rest_pat_in_fully_bound_structs)] +#![allow(clippy::struct_field_names)] + +struct A { + a: i32, + b: i64, + c: &'static str, +} + +macro_rules! foo { + ($param:expr) => { + match $param { + A { a: 0, b: 0, c: "", .. } => {}, + _ => {}, + } + }; +} + +fn main() { + let a_struct = A { a: 5, b: 42, c: "A" }; + + match a_struct { + A { a: 5, b: 42, c: "", } => {}, // Lint + //~^ rest_pat_in_fully_bound_structs + A { a: 0, b: 0, c: "", } => {}, // Lint + //~^ rest_pat_in_fully_bound_structs + _ => {}, + } + + match a_struct { + A { a: 5, b: 42, .. } => {}, + A { a: 0, b: 0, c: "", } => {}, // Lint + //~^ rest_pat_in_fully_bound_structs + _ => {}, + } + + // No lint + match a_struct { + A { a: 5, .. } => {}, + A { a: 0, b: 0, .. } => {}, + _ => {}, + } + + // No lint + foo!(a_struct); + + #[non_exhaustive] + struct B { + a: u32, + b: u32, + c: u64, + } + + let b_struct = B { a: 5, b: 42, c: 342 }; + + match b_struct { + B { a: 5, b: 42, .. } => {}, + B { a: 0, b: 0, c: 128, .. } => {}, // No Lint + _ => {}, + } +} diff --git a/tests/ui/rest_pat_in_fully_bound_structs.stderr b/tests/ui/rest_pat_in_fully_bound_structs.stderr index d048933ddb7b..8a2da302b9ef 100644 --- a/tests/ui/rest_pat_in_fully_bound_structs.stderr +++ b/tests/ui/rest_pat_in_fully_bound_structs.stderr @@ -4,9 +4,13 @@ error: unnecessary use of `..` pattern in struct binding. All fields were alread LL | A { a: 5, b: 42, c: "", .. } => {}, // Lint | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: consider removing `..` from this binding = note: `-D clippy::rest-pat-in-fully-bound-structs` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::rest_pat_in_fully_bound_structs)]` +help: consider removing `..` from this binding + | +LL - A { a: 5, b: 42, c: "", .. } => {}, // Lint +LL + A { a: 5, b: 42, c: "", } => {}, // Lint + | error: unnecessary use of `..` pattern in struct binding. All fields were already bound --> tests/ui/rest_pat_in_fully_bound_structs.rs:25:9 @@ -14,7 +18,11 @@ error: unnecessary use of `..` pattern in struct binding. All fields were alread LL | A { a: 0, b: 0, c: "", .. } => {}, // Lint | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: consider removing `..` from this binding +help: consider removing `..` from this binding + | +LL - A { a: 0, b: 0, c: "", .. } => {}, // Lint +LL + A { a: 0, b: 0, c: "", } => {}, // Lint + | error: unnecessary use of `..` pattern in struct binding. All fields were already bound --> tests/ui/rest_pat_in_fully_bound_structs.rs:32:9 @@ -22,7 +30,11 @@ error: unnecessary use of `..` pattern in struct binding. All fields were alread LL | A { a: 0, b: 0, c: "", .. } => {}, // Lint | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: consider removing `..` from this binding +help: consider removing `..` from this binding + | +LL - A { a: 0, b: 0, c: "", .. } => {}, // Lint +LL + A { a: 0, b: 0, c: "", } => {}, // Lint + | error: aborting due to 3 previous errors From dd4562a18f4a91e40ba701019af33ab80c1b2886 Mon Sep 17 00:00:00 2001 From: Augie Fackler Date: Fri, 12 Sep 2025 14:31:08 -0400 Subject: [PATCH 439/682] tests: update new test to accept new lifetime format Same change as rust-lang/rust@258915a55539593423c3d4c30f7b741f65c56e51, just for a newly written test. --- tests/codegen-llvm/c-variadic-lifetime.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/codegen-llvm/c-variadic-lifetime.rs b/tests/codegen-llvm/c-variadic-lifetime.rs index 5b2f8af18c81..c6d3602ef51a 100644 --- a/tests/codegen-llvm/c-variadic-lifetime.rs +++ b/tests/codegen-llvm/c-variadic-lifetime.rs @@ -8,7 +8,7 @@ #[unsafe(no_mangle)] unsafe extern "C" fn variadic(a: f64, mut args: ...) -> f64 { - // CHECK: call void @llvm.lifetime.start.p0(i64 {{[0-9]+}}, ptr nonnull %args) + // CHECK: call void @llvm.lifetime.start.p0({{(i64 [0-9]+, )?}}ptr nonnull %args) // CHECK: call void @llvm.va_start.p0(ptr nonnull %args) let b = args.arg::(); @@ -17,5 +17,5 @@ unsafe extern "C" fn variadic(a: f64, mut args: ...) -> f64 { a + b + c // CHECK: call void @llvm.va_end.p0(ptr nonnull %args) - // CHECK: call void @llvm.lifetime.end.p0(i64 {{[0-9]+}}, ptr nonnull %args) + // CHECK: call void @llvm.lifetime.end.p0({{(i64 [0-9]+, )?}}ptr nonnull %args) } From a0bb9cc57db551289cba9fefde086e45cb82733f Mon Sep 17 00:00:00 2001 From: Noratrieb <48135649+Noratrieb@users.noreply.github.com> Date: Sat, 26 Jul 2025 12:15:19 +0200 Subject: [PATCH 440/682] Introduce `target_spec_enum` macro to avoid duplication With this macro we only need to enumerate every variant once. This saves a lot of duplication already between the definition, the `FromStr` impl and the `ToJson` impl. It also enables us to do further things with it like JSON schema generation. --- compiler/rustc_target/src/lib.rs | 60 ++ compiler/rustc_target/src/spec/mod.rs | 907 ++++++-------------------- 2 files changed, 248 insertions(+), 719 deletions(-) diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs index 91657fef8032..c6a23745b04e 100644 --- a/compiler/rustc_target/src/lib.rs +++ b/compiler/rustc_target/src/lib.rs @@ -72,3 +72,63 @@ fn find_relative_libdir(sysroot: &Path) -> std::borrow::Cow<'static, str> { Some(libdir) => libdir.into(), } } + +macro_rules! target_spec_enum { + ( + $( #[$attr:meta] )* + pub enum $name:ident { + $( + $( #[$variant_attr:meta] )* + $variant:ident = $string:literal, + )* + } + parse_error_type = $parse_error_type:literal; + ) => { + $( #[$attr] )* + #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] + pub enum $name { + $( + $( #[$variant_attr] )* + $variant, + )* + } + + impl FromStr for $name { + type Err = String; + + fn from_str(s: &str) -> Result { + Ok(match s { + $( $string => Self::$variant, )* + _ => { + let all = [$( concat!("'", $string, "'") ),*].join(", "); + return Err(format!("invalid {}: '{s}'. allowed values: {all}", $parse_error_type)); + } + }) + } + } + + impl $name { + pub fn desc(&self) -> &'static str { + match self { + $( Self::$variant => $string, )* + } + } + } + + impl crate::json::ToJson for $name { + fn to_json(&self) -> crate::json::Json { + self.desc().to_json() + } + } + + crate::json::serde_deserialize_from_str!($name); + + + impl std::fmt::Display for $name { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(self.desc()) + } + } + }; +} +use target_spec_enum; diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 4d9f06c568bd..239ac7bf5c4d 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -183,50 +183,15 @@ impl LinkerFlavorCli { } } -#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub enum LldFlavor { - Wasm, - Ld64, - Ld, - Link, -} - -impl LldFlavor { - pub fn as_str(&self) -> &'static str { - match self { - LldFlavor::Wasm => "wasm", - LldFlavor::Ld64 => "darwin", - LldFlavor::Ld => "gnu", - LldFlavor::Link => "link", - } +crate::target_spec_enum! { + pub enum LldFlavor { + Wasm = "wasm", + Ld64 = "darwin", + Ld = "gnu", + Link = "link", } -} -impl FromStr for LldFlavor { - type Err = String; - - fn from_str(s: &str) -> Result { - Ok(match s { - "darwin" => LldFlavor::Ld64, - "gnu" => LldFlavor::Ld, - "link" => LldFlavor::Link, - "wasm" => LldFlavor::Wasm, - _ => { - return Err( - "invalid value for lld flavor: '{s}', expected one of 'darwin', 'gnu', 'link', 'wasm'" - .into(), - ); - } - }) - } -} - -crate::json::serde_deserialize_from_str!(LldFlavor); - -impl ToJson for LldFlavor { - fn to_json(&self) -> Json { - self.as_str().to_json() - } + parse_error_type = "LLD flavor"; } impl LinkerFlavor { @@ -823,10 +788,14 @@ impl LinkerFeatures { } } -#[derive(Clone, Copy, Debug, PartialEq, Hash, Encodable, Decodable, HashStable_Generic)] -pub enum PanicStrategy { - Unwind, - Abort, +crate::target_spec_enum! { + #[derive(Encodable, Decodable, HashStable_Generic)] + pub enum PanicStrategy { + Unwind = "unwind", + Abort = "abort", + } + + parse_error_type = "panic strategy"; } #[derive(Clone, Copy, Debug, PartialEq, Hash, Encodable, Decodable, HashStable_Generic)] @@ -838,13 +807,6 @@ pub enum OnBrokenPipe { } impl PanicStrategy { - pub fn desc(&self) -> &str { - match *self { - PanicStrategy::Unwind => "unwind", - PanicStrategy::Abort => "abort", - } - } - pub const fn desc_symbol(&self) -> Symbol { match *self { PanicStrategy::Unwind => sym::unwind, @@ -857,24 +819,16 @@ impl PanicStrategy { } } -impl FromStr for PanicStrategy { - type Err = String; - fn from_str(s: &str) -> Result { - Ok(match s { - "unwind" => PanicStrategy::Unwind, - "abort" => PanicStrategy::Abort, - _ => { - return Err(format!( - "'{}' is not a valid value for \ - panic-strategy. Use 'unwind' or 'abort'.", - s - )); - } - }) +crate::target_spec_enum! { + pub enum RelroLevel { + Full = "full", + Partial = "partial", + Off = "off", + None = "none", } -} -crate::json::serde_deserialize_from_str!(PanicStrategy); + parse_error_type = "relro level"; +} impl IntoDiagArg for PanicStrategy { fn into_diag_arg(self, _: &mut Option) -> DiagArgValue { @@ -882,110 +836,16 @@ impl IntoDiagArg for PanicStrategy { } } -impl ToJson for PanicStrategy { - fn to_json(&self) -> Json { - match *self { - PanicStrategy::Abort => "abort".to_json(), - PanicStrategy::Unwind => "unwind".to_json(), - } +crate::target_spec_enum! { + pub enum SymbolVisibility { + Hidden = "hidden", + Protected = "protected", + Interposable = "interposable", } + + parse_error_type = "symbol visibility"; } -#[derive(Clone, Copy, Debug, PartialEq, Hash)] -pub enum RelroLevel { - Full, - Partial, - Off, - None, -} - -impl RelroLevel { - pub fn desc(&self) -> &str { - match *self { - RelroLevel::Full => "full", - RelroLevel::Partial => "partial", - RelroLevel::Off => "off", - RelroLevel::None => "none", - } - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Hash)] -pub enum SymbolVisibility { - Hidden, - Protected, - Interposable, -} - -impl SymbolVisibility { - pub fn desc(&self) -> &str { - match *self { - SymbolVisibility::Hidden => "hidden", - SymbolVisibility::Protected => "protected", - SymbolVisibility::Interposable => "interposable", - } - } -} - -impl FromStr for SymbolVisibility { - type Err = String; - - fn from_str(s: &str) -> Result { - match s { - "hidden" => Ok(SymbolVisibility::Hidden), - "protected" => Ok(SymbolVisibility::Protected), - "interposable" => Ok(SymbolVisibility::Interposable), - _ => Err(format!( - "'{}' is not a valid value for \ - symbol-visibility. Use 'hidden', 'protected, or 'interposable'.", - s - )), - } - } -} - -crate::json::serde_deserialize_from_str!(SymbolVisibility); - -impl ToJson for SymbolVisibility { - fn to_json(&self) -> Json { - match *self { - SymbolVisibility::Hidden => "hidden".to_json(), - SymbolVisibility::Protected => "protected".to_json(), - SymbolVisibility::Interposable => "interposable".to_json(), - } - } -} - -impl FromStr for RelroLevel { - type Err = String; - - fn from_str(s: &str) -> Result { - match s { - "full" => Ok(RelroLevel::Full), - "partial" => Ok(RelroLevel::Partial), - "off" => Ok(RelroLevel::Off), - "none" => Ok(RelroLevel::None), - _ => Err(format!( - "'{}' is not a valid value for \ - relro-level. Use 'full', 'partial, 'off', or 'none'.", - s - )), - } - } -} - -crate::json::serde_deserialize_from_str!(RelroLevel); - -impl ToJson for RelroLevel { - fn to_json(&self) -> Json { - match *self { - RelroLevel::Full => "full".to_json(), - RelroLevel::Partial => "partial".to_json(), - RelroLevel::Off => "off".to_json(), - RelroLevel::None => "None".to_json(), - } - } -} #[derive(Clone, Debug, PartialEq, Hash)] pub enum SmallDataThresholdSupport { @@ -1026,76 +886,31 @@ impl ToJson for SmallDataThresholdSupport { } } -#[derive(Clone, Copy, Debug, PartialEq, Hash)] -pub enum MergeFunctions { - Disabled, - Trampolines, - Aliases, -} - -impl MergeFunctions { - pub fn desc(&self) -> &str { - match *self { - MergeFunctions::Disabled => "disabled", - MergeFunctions::Trampolines => "trampolines", - MergeFunctions::Aliases => "aliases", - } +crate::target_spec_enum! { + pub enum MergeFunctions { + Disabled = "disabled", + Trampolines = "trampolines", + Aliases = "aliases", } + + parse_error_type = "value for merge-functions"; } -impl FromStr for MergeFunctions { - type Err = String; - - fn from_str(s: &str) -> Result { - match s { - "disabled" => Ok(MergeFunctions::Disabled), - "trampolines" => Ok(MergeFunctions::Trampolines), - "aliases" => Ok(MergeFunctions::Aliases), - _ => Err(format!( - "'{}' is not a valid value for \ - merge-functions. Use 'disabled', \ - 'trampolines', or 'aliases'.", - s - )), - } +crate::target_spec_enum! { + pub enum RelocModel { + Static = "static", + Pic = "pic", + Pie = "pie", + DynamicNoPic = "dynamic-no-pic", + Ropi = "ropi", + Rwpi = "rwpi", + RopiRwpi = "ropi-rwpi", } -} -crate::json::serde_deserialize_from_str!(MergeFunctions); - -impl ToJson for MergeFunctions { - fn to_json(&self) -> Json { - match *self { - MergeFunctions::Disabled => "disabled".to_json(), - MergeFunctions::Trampolines => "trampolines".to_json(), - MergeFunctions::Aliases => "aliases".to_json(), - } - } -} - -#[derive(Clone, Copy, PartialEq, Hash, Debug)] -pub enum RelocModel { - Static, - Pic, - Pie, - DynamicNoPic, - Ropi, - Rwpi, - RopiRwpi, + parse_error_type = "relocation model"; } impl RelocModel { - pub fn desc(&self) -> &str { - match *self { - RelocModel::Static => "static", - RelocModel::Pic => "pic", - RelocModel::Pie => "pie", - RelocModel::DynamicNoPic => "dynamic-no-pic", - RelocModel::Ropi => "ropi", - RelocModel::Rwpi => "rwpi", - RelocModel::RopiRwpi => "ropi-rwpi", - } - } pub const fn desc_symbol(&self) -> Symbol { match *self { RelocModel::Static => kw::Static, @@ -1121,236 +936,75 @@ impl RelocModel { } } -impl FromStr for RelocModel { - type Err = String; - - fn from_str(s: &str) -> Result { - Ok(match s { - "static" => RelocModel::Static, - "pic" => RelocModel::Pic, - "pie" => RelocModel::Pie, - "dynamic-no-pic" => RelocModel::DynamicNoPic, - "ropi" => RelocModel::Ropi, - "rwpi" => RelocModel::Rwpi, - "ropi-rwpi" => RelocModel::RopiRwpi, - _ => { - return Err(format!( - "invalid relocation model '{s}'. - Run `rustc --print relocation-models` to \ - see the list of supported values.'" - )); - } - }) +crate::target_spec_enum! { + pub enum CodeModel { + Tiny = "tiny", + Small = "small", + Kernel = "kernel", + Medium = "medium", + Large = "large", } + + parse_error_type = "code model"; } -crate::json::serde_deserialize_from_str!(RelocModel); - -impl ToJson for RelocModel { - fn to_json(&self) -> Json { - self.desc().to_json() +crate::target_spec_enum! { + /// The float ABI setting to be configured in the LLVM target machine. + pub enum FloatAbi { + Soft = "soft", + Hard = "hard", } + + parse_error_type = "float abi"; } -#[derive(Clone, Copy, PartialEq, Hash, Debug)] -pub enum CodeModel { - Tiny, - Small, - Kernel, - Medium, - Large, -} - -impl FromStr for CodeModel { - type Err = String; - - fn from_str(s: &str) -> Result { - Ok(match s { - "tiny" => CodeModel::Tiny, - "small" => CodeModel::Small, - "kernel" => CodeModel::Kernel, - "medium" => CodeModel::Medium, - "large" => CodeModel::Large, - _ => { - return Err(format!( - "'{s}' is not a valid code model. \ - Run `rustc --print code-models` to \ - see the list of supported values." - )); - } - }) +crate::target_spec_enum! { + /// The Rustc-specific variant of the ABI used for this target. + pub enum RustcAbi { + /// On x86-32 only: make use of SSE and SSE2 for ABI purposes. + X86Sse2 = "x86-sse2", + /// On x86-32/64 only: do not use any FPU or SIMD registers for the ABI. + X86Softfloat = "x86-softfloat", } + + parse_error_type = "rustc abi"; } -crate::json::serde_deserialize_from_str!(CodeModel); - -impl ToJson for CodeModel { - fn to_json(&self) -> Json { - match *self { - CodeModel::Tiny => "tiny", - CodeModel::Small => "small", - CodeModel::Kernel => "kernel", - CodeModel::Medium => "medium", - CodeModel::Large => "large", - } - .to_json() +crate::target_spec_enum! { + pub enum TlsModel { + GeneralDynamic = "global-dynamic", + LocalDynamic = "local-dynamic", + InitialExec = "initial-exec", + LocalExec = "local-exec", + Emulated = "emulated", } + + parse_error_type = "TLS model"; } -/// The float ABI setting to be configured in the LLVM target machine. -#[derive(Clone, Copy, PartialEq, Hash, Debug)] -pub enum FloatAbi { - Soft, - Hard, -} - -impl FromStr for FloatAbi { - type Err = String; - - fn from_str(s: &str) -> Result { - Ok(match s { - "soft" => FloatAbi::Soft, - "hard" => FloatAbi::Hard, - _ => { - return Err(format!( - "'{}' is not a valid value for \ - llvm-floatabi. Use 'soft' or 'hard'.", - s - )); - } - }) +crate::target_spec_enum! { + /// Everything is flattened to a single enum to make the json encoding/decoding less annoying. + pub enum LinkOutputKind { + /// Dynamically linked non position-independent executable. + DynamicNoPicExe = "dynamic-nopic-exe", + /// Dynamically linked position-independent executable. + DynamicPicExe = "dynamic-pic-exe", + /// Statically linked non position-independent executable. + StaticNoPicExe = "static-nopic-exe", + /// Statically linked position-independent executable. + StaticPicExe = "static-pic-exe", + /// Regular dynamic library ("dynamically linked"). + DynamicDylib = "dynamic-dylib", + /// Dynamic library with bundled libc ("statically linked"). + StaticDylib = "static-dylib", + /// WASI module with a lifetime past the _initialize entry point + WasiReactorExe = "wasi-reactor-exe", } -} -crate::json::serde_deserialize_from_str!(FloatAbi); - -impl ToJson for FloatAbi { - fn to_json(&self) -> Json { - match *self { - FloatAbi::Soft => "soft", - FloatAbi::Hard => "hard", - } - .to_json() - } -} - -/// The Rustc-specific variant of the ABI used for this target. -#[derive(Clone, Copy, PartialEq, Hash, Debug)] -pub enum RustcAbi { - /// On x86-32 only: make use of SSE and SSE2 for ABI purposes. - X86Sse2, - /// On x86-32/64 only: do not use any FPU or SIMD registers for the ABI. - X86Softfloat, -} - -impl FromStr for RustcAbi { - type Err = String; - - fn from_str(s: &str) -> Result { - Ok(match s { - "x86-sse2" => RustcAbi::X86Sse2, - "x86-softfloat" => RustcAbi::X86Softfloat, - _ => { - return Err(format!( - "'{s}' is not a valid value for rustc-abi. \ - Use 'x86-softfloat' or leave the field unset." - )); - } - }) - } -} - -crate::json::serde_deserialize_from_str!(RustcAbi); - -impl ToJson for RustcAbi { - fn to_json(&self) -> Json { - match *self { - RustcAbi::X86Sse2 => "x86-sse2", - RustcAbi::X86Softfloat => "x86-softfloat", - } - .to_json() - } -} - -#[derive(Clone, Copy, PartialEq, Hash, Debug)] -pub enum TlsModel { - GeneralDynamic, - LocalDynamic, - InitialExec, - LocalExec, - Emulated, -} - -impl FromStr for TlsModel { - type Err = String; - - fn from_str(s: &str) -> Result { - Ok(match s { - // Note the difference "general" vs "global" difference. The model name is "general", - // but the user-facing option name is "global" for consistency with other compilers. - "global-dynamic" => TlsModel::GeneralDynamic, - "local-dynamic" => TlsModel::LocalDynamic, - "initial-exec" => TlsModel::InitialExec, - "local-exec" => TlsModel::LocalExec, - "emulated" => TlsModel::Emulated, - _ => { - return Err(format!( - "'{s}' is not a valid TLS model. \ - Run `rustc --print tls-models` to \ - see the list of supported values." - )); - } - }) - } -} - -crate::json::serde_deserialize_from_str!(TlsModel); - -impl ToJson for TlsModel { - fn to_json(&self) -> Json { - match *self { - TlsModel::GeneralDynamic => "global-dynamic", - TlsModel::LocalDynamic => "local-dynamic", - TlsModel::InitialExec => "initial-exec", - TlsModel::LocalExec => "local-exec", - TlsModel::Emulated => "emulated", - } - .to_json() - } -} - -/// Everything is flattened to a single enum to make the json encoding/decoding less annoying. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] -pub enum LinkOutputKind { - /// Dynamically linked non position-independent executable. - DynamicNoPicExe, - /// Dynamically linked position-independent executable. - DynamicPicExe, - /// Statically linked non position-independent executable. - StaticNoPicExe, - /// Statically linked position-independent executable. - StaticPicExe, - /// Regular dynamic library ("dynamically linked"). - DynamicDylib, - /// Dynamic library with bundled libc ("statically linked"). - StaticDylib, - /// WASI module with a lifetime past the _initialize entry point - WasiReactorExe, + parse_error_type = "CRT object kind"; } impl LinkOutputKind { - fn as_str(&self) -> &'static str { - match self { - LinkOutputKind::DynamicNoPicExe => "dynamic-nopic-exe", - LinkOutputKind::DynamicPicExe => "dynamic-pic-exe", - LinkOutputKind::StaticNoPicExe => "static-nopic-exe", - LinkOutputKind::StaticPicExe => "static-pic-exe", - LinkOutputKind::DynamicDylib => "dynamic-dylib", - LinkOutputKind::StaticDylib => "static-dylib", - LinkOutputKind::WasiReactorExe => "wasi-reactor-exe", - } - } - pub fn can_link_dylib(self) -> bool { match self { LinkOutputKind::StaticNoPicExe | LinkOutputKind::StaticPicExe => false, @@ -1363,166 +1017,59 @@ impl LinkOutputKind { } } -impl FromStr for LinkOutputKind { - type Err = String; - - fn from_str(s: &str) -> Result { - Ok(match s { - "dynamic-nopic-exe" => LinkOutputKind::DynamicNoPicExe, - "dynamic-pic-exe" => LinkOutputKind::DynamicPicExe, - "static-nopic-exe" => LinkOutputKind::StaticNoPicExe, - "static-pic-exe" => LinkOutputKind::StaticPicExe, - "dynamic-dylib" => LinkOutputKind::DynamicDylib, - "static-dylib" => LinkOutputKind::StaticDylib, - "wasi-reactor-exe" => LinkOutputKind::WasiReactorExe, - _ => { - return Err(format!( - "invalid value for CRT object kind. \ - Use '(dynamic,static)-(nopic,pic)-exe' or \ - '(dynamic,static)-dylib' or 'wasi-reactor-exe'" - )); - } - }) - } -} - -crate::json::serde_deserialize_from_str!(LinkOutputKind); - -impl fmt::Display for LinkOutputKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(self.as_str()) - } -} - pub type LinkArgs = BTreeMap>>; pub type LinkArgsCli = BTreeMap>>; -/// Which kind of debuginfo does the target use? -/// -/// Useful in determining whether a target supports Split DWARF (a target with -/// `DebuginfoKind::Dwarf` and supporting `SplitDebuginfo::Unpacked` for example). -#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] -pub enum DebuginfoKind { - /// DWARF debuginfo (such as that used on `x86_64_unknown_linux_gnu`). - #[default] - Dwarf, - /// DWARF debuginfo in dSYM files (such as on Apple platforms). - DwarfDsym, - /// Program database files (such as on Windows). - Pdb, -} - -impl DebuginfoKind { - fn as_str(&self) -> &'static str { - match self { - DebuginfoKind::Dwarf => "dwarf", - DebuginfoKind::DwarfDsym => "dwarf-dsym", - DebuginfoKind::Pdb => "pdb", - } - } -} - -impl FromStr for DebuginfoKind { - type Err = String; - - fn from_str(s: &str) -> Result { - Ok(match s { - "dwarf" => DebuginfoKind::Dwarf, - "dwarf-dsym" => DebuginfoKind::DwarfDsym, - "pdb" => DebuginfoKind::Pdb, - _ => { - return Err(format!( - "'{s}' is not a valid value for debuginfo-kind. Use 'dwarf', \ - 'dwarf-dsym' or 'pdb'." - )); - } - }) - } -} - -crate::json::serde_deserialize_from_str!(DebuginfoKind); - -impl ToJson for DebuginfoKind { - fn to_json(&self) -> Json { - self.as_str().to_json() - } -} - -impl fmt::Display for DebuginfoKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(self.as_str()) - } -} - -#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] -pub enum SplitDebuginfo { - /// Split debug-information is disabled, meaning that on supported platforms - /// you can find all debug information in the executable itself. This is - /// only supported for ELF effectively. +crate::target_spec_enum! { + /// Which kind of debuginfo does the target use? /// - /// * Windows - not supported - /// * macOS - don't run `dsymutil` - /// * ELF - `.debug_*` sections - #[default] - Off, + /// Useful in determining whether a target supports Split DWARF (a target with + /// `DebuginfoKind::Dwarf` and supporting `SplitDebuginfo::Unpacked` for example). + #[derive(Default)] + pub enum DebuginfoKind { + /// DWARF debuginfo (such as that used on `x86_64_unknown_linux_gnu`). + #[default] + Dwarf = "dwarf", + /// DWARF debuginfo in dSYM files (such as on Apple platforms). + DwarfDsym = "dwarf-dsym", + /// Program database files (such as on Windows). + Pdb = "pdb", + } - /// Split debug-information can be found in a "packed" location separate - /// from the final artifact. This is supported on all platforms. - /// - /// * Windows - `*.pdb` - /// * macOS - `*.dSYM` (run `dsymutil`) - /// * ELF - `*.dwp` (run `thorin`) - Packed, - - /// Split debug-information can be found in individual object files on the - /// filesystem. The main executable may point to the object files. - /// - /// * Windows - not supported - /// * macOS - supported, scattered object files - /// * ELF - supported, scattered `*.dwo` or `*.o` files (see `SplitDwarfKind`) - Unpacked, + parse_error_type = "debuginfo kind"; } -impl SplitDebuginfo { - fn as_str(&self) -> &'static str { - match self { - SplitDebuginfo::Off => "off", - SplitDebuginfo::Packed => "packed", - SplitDebuginfo::Unpacked => "unpacked", - } +crate::target_spec_enum! { + #[derive(Default)] + pub enum SplitDebuginfo { + /// Split debug-information is disabled, meaning that on supported platforms + /// you can find all debug information in the executable itself. This is + /// only supported for ELF effectively. + /// + /// * Windows - not supported + /// * macOS - don't run `dsymutil` + /// * ELF - `.debug_*` sections + #[default] + Off = "off", + + /// Split debug-information can be found in a "packed" location separate + /// from the final artifact. This is supported on all platforms. + /// + /// * Windows - `*.pdb` + /// * macOS - `*.dSYM` (run `dsymutil`) + /// * ELF - `*.dwp` (run `thorin`) + Packed = "packed", + + /// Split debug-information can be found in individual object files on the + /// filesystem. The main executable may point to the object files. + /// + /// * Windows - not supported + /// * macOS - supported, scattered object files + /// * ELF - supported, scattered `*.dwo` or `*.o` files (see `SplitDwarfKind`) + Unpacked = "unpacked", } -} -impl FromStr for SplitDebuginfo { - type Err = String; - - fn from_str(s: &str) -> Result { - Ok(match s { - "off" => SplitDebuginfo::Off, - "unpacked" => SplitDebuginfo::Unpacked, - "packed" => SplitDebuginfo::Packed, - _ => { - return Err(format!( - "'{s}' is not a valid value for \ - split-debuginfo. Use 'off', 'unpacked', or 'packed'.", - )); - } - }) - } -} - -crate::json::serde_deserialize_from_str!(SplitDebuginfo); - -impl ToJson for SplitDebuginfo { - fn to_json(&self) -> Json { - self.as_str().to_json() - } -} - -impl fmt::Display for SplitDebuginfo { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(self.as_str()) - } + parse_error_type = "split debuginfo"; } into_diag_arg_using_display!(SplitDebuginfo); @@ -1699,17 +1246,20 @@ impl ToJson for SanitizerSet { } } -#[derive(Clone, Copy, PartialEq, Hash, Debug)] -pub enum FramePointer { - /// Forces the machine code generator to always preserve the frame pointers. - Always, - /// Forces the machine code generator to preserve the frame pointers except for the leaf - /// functions (i.e. those that don't call other functions). - NonLeaf, - /// Allows the machine code generator to omit the frame pointers. - /// - /// This option does not guarantee that the frame pointers will be omitted. - MayOmit, +crate::target_spec_enum! { + pub enum FramePointer { + /// Forces the machine code generator to always preserve the frame pointers. + Always = "always", + /// Forces the machine code generator to preserve the frame pointers except for the leaf + /// functions (i.e. those that don't call other functions). + NonLeaf = "non-leaf", + /// Allows the machine code generator to omit the frame pointers. + /// + /// This option does not guarantee that the frame pointers will be omitted. + MayOmit = "may-omit", + } + + parse_error_type = "frame pointer"; } impl FramePointer { @@ -1726,93 +1276,43 @@ impl FramePointer { } } -impl FromStr for FramePointer { - type Err = String; - fn from_str(s: &str) -> Result { - Ok(match s { - "always" => Self::Always, - "non-leaf" => Self::NonLeaf, - "may-omit" => Self::MayOmit, - _ => return Err(format!("'{s}' is not a valid value for frame-pointer")), - }) +crate::target_spec_enum! { + /// Controls use of stack canaries. + pub enum StackProtector { + /// Disable stack canary generation. + None = "none", + + /// On LLVM, mark all generated LLVM functions with the `ssp` attribute (see + /// llvm/docs/LangRef.rst). This triggers stack canary generation in + /// functions which contain an array of a byte-sized type with more than + /// eight elements. + Basic = "basic", + + /// On LLVM, mark all generated LLVM functions with the `sspstrong` + /// attribute (see llvm/docs/LangRef.rst). This triggers stack canary + /// generation in functions which either contain an array, or which take + /// the address of a local variable. + Strong = "strong", + + /// Generate stack canaries in all functions. + All = "all", } -} -crate::json::serde_deserialize_from_str!(FramePointer); - -impl ToJson for FramePointer { - fn to_json(&self) -> Json { - match *self { - Self::Always => "always", - Self::NonLeaf => "non-leaf", - Self::MayOmit => "may-omit", - } - .to_json() - } -} - -/// Controls use of stack canaries. -#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq)] -pub enum StackProtector { - /// Disable stack canary generation. - None, - - /// On LLVM, mark all generated LLVM functions with the `ssp` attribute (see - /// llvm/docs/LangRef.rst). This triggers stack canary generation in - /// functions which contain an array of a byte-sized type with more than - /// eight elements. - Basic, - - /// On LLVM, mark all generated LLVM functions with the `sspstrong` - /// attribute (see llvm/docs/LangRef.rst). This triggers stack canary - /// generation in functions which either contain an array, or which take - /// the address of a local variable. - Strong, - - /// Generate stack canaries in all functions. - All, -} - -impl StackProtector { - fn as_str(&self) -> &'static str { - match self { - StackProtector::None => "none", - StackProtector::Basic => "basic", - StackProtector::Strong => "strong", - StackProtector::All => "all", - } - } -} - -impl FromStr for StackProtector { - type Err = (); - - fn from_str(s: &str) -> Result { - Ok(match s { - "none" => StackProtector::None, - "basic" => StackProtector::Basic, - "strong" => StackProtector::Strong, - "all" => StackProtector::All, - _ => return Err(()), - }) - } -} - -impl fmt::Display for StackProtector { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(self.as_str()) - } + parse_error_type = "stack protector"; } into_diag_arg_using_display!(StackProtector); -#[derive(PartialEq, Clone, Debug)] -pub enum BinaryFormat { - Coff, - Elf, - MachO, - Wasm, - Xcoff, +crate::target_spec_enum! { + pub enum BinaryFormat { + Coff = "coff", + Elf = "elf", + MachO = "mach-o", + Wasm = "wasm", + Xcoff = "xcoff", + } + + parse_error_type = "binary format"; } impl BinaryFormat { @@ -1828,37 +1328,6 @@ impl BinaryFormat { } } -impl FromStr for BinaryFormat { - type Err = String; - fn from_str(s: &str) -> Result { - match s { - "coff" => Ok(Self::Coff), - "elf" => Ok(Self::Elf), - "mach-o" => Ok(Self::MachO), - "wasm" => Ok(Self::Wasm), - "xcoff" => Ok(Self::Xcoff), - _ => Err(format!( - "'{s}' is not a valid value for binary_format. \ - Use 'coff', 'elf', 'mach-o', 'wasm' or 'xcoff' " - )), - } - } -} - -crate::json::serde_deserialize_from_str!(BinaryFormat); - -impl ToJson for BinaryFormat { - fn to_json(&self) -> Json { - match self { - Self::Coff => "coff", - Self::Elf => "elf", - Self::MachO => "mach-o", - Self::Wasm => "wasm", - Self::Xcoff => "xcoff", - } - .to_json() - } -} impl ToJson for Align { fn to_json(&self) -> Json { From 43d45ef2da29aa438c32b5c872bc7a367ef2a2ab Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Fri, 12 Sep 2025 11:32:51 -0700 Subject: [PATCH 441/682] rustdoc-search: reduce async machinery in value lookups This commit is a mirrored change from stringdex that makes `at()` not always return a promise, which is fine because we can still `await` it. --- src/librustdoc/html/static/js/search.js | 8 +- src/librustdoc/html/static/js/stringdex.d.ts | 2 +- src/librustdoc/html/static/js/stringdex.js | 99 +++++++++++--------- 3 files changed, 58 insertions(+), 51 deletions(-) diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 3b84ae2bed06..339b101d4711 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -1758,12 +1758,8 @@ class DocSearch { const l = crateNames.length; const names = []; for (let i = 0; i < l; ++i) { - names.push(crateNames.at(i).then(name => { - if (name === undefined) { - return ""; - } - return this.utf8decoder.decode(name); - })); + const name = await crateNames.at(i); + names.push(name === undefined ? "" : this.utf8decoder.decode(name)); } return Promise.all(names); } diff --git a/src/librustdoc/html/static/js/stringdex.d.ts b/src/librustdoc/html/static/js/stringdex.d.ts index 2eb1fdf95d84..71c6bfdf4814 100644 --- a/src/librustdoc/html/static/js/stringdex.d.ts +++ b/src/librustdoc/html/static/js/stringdex.d.ts @@ -29,7 +29,7 @@ declare namespace stringdex { */ interface DataColumn { isEmpty(id: number): boolean; - at(id: number): Promise; + at(id: number): Promise|Uint8Array|undefined; search(name: Uint8Array|string): Promise; searchLev(name: Uint8Array|string): AsyncGenerator; length: number, diff --git a/src/librustdoc/html/static/js/stringdex.js b/src/librustdoc/html/static/js/stringdex.js index b7f605a10351..4c4659628778 100644 --- a/src/librustdoc/html/static/js/stringdex.js +++ b/src/librustdoc/html/static/js/stringdex.js @@ -2261,7 +2261,7 @@ function loadDatabase(hooks) { this.hashes = hashes; this.emptyset = emptyset; this.name = name; - /** @type {{"hash": Uint8Array, "data": Promise?, "end": number}[]} */ + /** @type {{"hash": Uint8Array, "data": Uint8Array[]?, "end": number}[]} */ this.buckets = []; this.bucket_keys = []; const l = counts.length; @@ -2295,65 +2295,76 @@ function loadDatabase(hooks) { /** * Look up a cell by row ID. * @param {number} id - * @returns {Promise} + * @returns {Promise|Uint8Array|undefined} */ - async at(id) { + at(id) { if (this.emptyset.contains(id)) { - return Promise.resolve(EMPTY_UINT8); + return EMPTY_UINT8; } else { let idx = -1; while (this.bucket_keys[idx + 1] <= id) { idx += 1; } if (idx === -1 || idx >= this.bucket_keys.length) { - return Promise.resolve(undefined); + return undefined; } else { const start = this.bucket_keys[idx]; - const {hash, end} = this.buckets[idx]; + const bucket = this.buckets[idx]; let data = this.buckets[idx].data; if (data === null) { - const dataSansEmptysetOrig = await registry.dataLoadByNameAndHash( - this.name, - hash, - ); - // After the `await` resolves, another task might fill - // in the data. If so, we should use that. - data = this.buckets[idx].data; - if (data !== null) { - return (await data)[id - start]; - } - const dataSansEmptyset = [...dataSansEmptysetOrig]; - /** @type {(Uint8Array[])|null} */ - let dataWithEmptyset = null; - let pos = start; - let insertCount = 0; - while (pos < end) { - if (this.emptyset.contains(pos)) { - if (dataWithEmptyset === null) { - dataWithEmptyset = dataSansEmptyset.splice(0, insertCount); - } else if (insertCount !== 0) { - dataWithEmptyset.push( - ...dataSansEmptyset.splice(0, insertCount), - ); - } - insertCount = 0; - dataWithEmptyset.push(EMPTY_UINT8); - } else { - insertCount += 1; - } - pos += 1; - } - data = Promise.resolve( - dataWithEmptyset === null ? - dataSansEmptyset : - dataWithEmptyset.concat(dataSansEmptyset), - ); - this.buckets[idx].data = data; + return this.atAsyncFetch(id, start, bucket); + } else { + return data[id - start]; } - return (await data)[id - start]; } } } + /** + * Look up a cell by row ID. + * @param {number} id + * @param {number} start + * @param {{hash: Uint8Array, data: Uint8Array[] | null, end: number}} bucket + * @returns {Promise} + */ + async atAsyncFetch(id, start, bucket) { + const {hash, end} = bucket; + const dataSansEmptysetOrig = await registry.dataLoadByNameAndHash( + this.name, + hash, + ); + // After the `await` resolves, another task might fill + // in the data. If so, we should use that. + let data = bucket.data; + if (data !== null) { + return data[id - start]; + } + const dataSansEmptyset = [...dataSansEmptysetOrig]; + /** @type {(Uint8Array[])|null} */ + let dataWithEmptyset = null; + let pos = start; + let insertCount = 0; + while (pos < end) { + if (this.emptyset.contains(pos)) { + if (dataWithEmptyset === null) { + dataWithEmptyset = dataSansEmptyset.splice(0, insertCount); + } else if (insertCount !== 0) { + dataWithEmptyset.push( + ...dataSansEmptyset.splice(0, insertCount), + ); + } + insertCount = 0; + dataWithEmptyset.push(EMPTY_UINT8); + } else { + insertCount += 1; + } + pos += 1; + } + data = dataWithEmptyset === null ? + dataSansEmptyset : + dataWithEmptyset.concat(dataSansEmptyset); + bucket.data = data; + return data[id - start]; + } /** * Search by exact substring * @param {Uint8Array|string} name From 957fa10d50787a4c6c6d8a35be5af6bd43ba1770 Mon Sep 17 00:00:00 2001 From: Oneirical Date: Wed, 20 Aug 2025 14:02:50 -0400 Subject: [PATCH 442/682] Add test batch 3 --- .../invalid-self-parameter-type-56806.rs} | 1 + .../invalid-self-parameter-type-56806.stderr} | 2 +- .../traits-associated-consts-ice-56870.rs} | 1 + ...icate-associated-type-resolution-59326.rs} | 1 + ...mpl-trait-member-type-resolution-57399.rs} | 2 +- .../function-comparison-errors-59488.rs} | 1 + .../function-comparison-errors-59488.stderr} | 20 +++++++++---------- .../boxed-value-matching-57741.rs} | 1 + .../boxed-value-matching-57741.stderr} | 4 ++-- ...ferencing-boxed-enum-in-match-57741.fixed} | 1 + ...ereferencing-boxed-enum-in-match-57741.rs} | 1 + ...erencing-boxed-enum-in-match-57741.stderr} | 8 ++++---- .../generic-typed-nested-closures-59494.rs} | 1 + ...eneric-typed-nested-closures-59494.stderr} | 2 +- ...mono-item-collector-default-impl-58375.rs} | 1 + ...t-implementation-coherence-check-57162.rs} | 1 + .../oncecell-const-init-57781.rs} | 1 + .../impl-copy-function-debuginfo-58463.rs} | 1 + .../spurious-dyn-compat-errors-58734.rs} | 1 + .../spurious-dyn-compat-errors-58734.stderr} | 6 +++--- ...ric-associated-type-deref-target-56237.rs} | 1 + .../hrtb-associated-type-leak-check-55731.rs} | 1 + ...b-associated-type-leak-check-55731.stderr} | 2 +- .../auxiliary/reexported-trait-56175.rs | 17 ++++++++++++++++ ...s-suggested-without-extern-crate-56175.rs} | 3 ++- ...ggested-without-extern-crate-56175.stderr} | 12 +++++------ .../auxiliary/aux-57271-lib.rs} | 0 ...utually-recursive-infinite-types-57271.rs} | 7 ++++--- ...lly-recursive-infinite-types-57271.stderr} | 2 +- tests/ui/issues/issue-56943.rs | 8 -------- .../raw-identifier-for-function-57198.rs} | 1 + .../auxiliary/aux-56943.rs} | 0 .../type-mismatch-in-extern-crate-56943.rs | 9 +++++++++ ...ype-mismatch-in-extern-crate-56943.stderr} | 6 +++--- .../pub-use-handling-in-modules-56128.rs} | 0 .../invalid-variable-definition-55587.rs} | 1 + .../invalid-variable-definition-55587.stderr} | 2 +- .../missing-type-in-scope-58712.rs} | 1 + .../missing-type-in-scope-58712.stderr} | 4 ++-- .../park-timeout-wakeup-59020.rs} | 1 + .../invalid-self-constructor-56835.rs} | 1 + .../invalid-self-constructor-56835.stderr} | 4 ++-- ...tible-types-in-try-expression-59756.fixed} | 0 ...mpatible-types-in-try-expression-59756.rs} | 1 + ...ible-types-in-try-expression-59756.stderr} | 2 +- .../negative-bound-not-supported-58857.rs} | 1 + ...negative-bound-not-supported-58857.stderr} | 2 +- ...generic-trait-impl-aliased-array-58212.rs} | 1 + .../trait-object-lifetime-bounds-57156.rs} | 1 + .../trait-objects-with-supertraits-56229.rs} | 1 + ...onstructor-type-args-not-allowed-57924.rs} | 1 + ...ructor-type-args-not-allowed-57924.stderr} | 2 +- .../self-constructor-type-error-56199.rs} | 1 + .../self-constructor-type-error-56199.stderr} | 8 ++++---- 54 files changed, 104 insertions(+), 57 deletions(-) rename tests/ui/{issues/issue-56806.rs => abi/invalid-self-parameter-type-56806.rs} (72%) rename tests/ui/{issues/issue-56806.stderr => abi/invalid-self-parameter-type-56806.stderr} (91%) rename tests/ui/{issues/issue-56870.rs => associated-consts/traits-associated-consts-ice-56870.rs} (93%) rename tests/ui/{issues/issue-59326.rs => associated-types/duplicate-associated-type-resolution-59326.rs} (89%) rename tests/ui/{issues/issue-57399-self-return-impl-trait.rs => associated-types/impl-trait-member-type-resolution-57399.rs} (81%) rename tests/ui/{issues/issue-59488.rs => binop/function-comparison-errors-59488.rs} (95%) rename tests/ui/{issues/issue-59488.stderr => binop/function-comparison-errors-59488.stderr} (84%) rename tests/ui/{issues/issue-57741-dereference-boxed-value/issue-57741-1.rs => box/boxed-value-matching-57741.rs} (89%) rename tests/ui/{issues/issue-57741-dereference-boxed-value/issue-57741-1.stderr => box/boxed-value-matching-57741.stderr} (88%) rename tests/ui/{issues/issue-57741-dereference-boxed-value/issue-57741.fixed => box/dereferencing-boxed-enum-in-match-57741.fixed} (92%) rename tests/ui/{issues/issue-57741-dereference-boxed-value/issue-57741.rs => box/dereferencing-boxed-enum-in-match-57741.rs} (92%) rename tests/ui/{issues/issue-57741-dereference-boxed-value/issue-57741.stderr => box/dereferencing-boxed-enum-in-match-57741.stderr} (87%) rename tests/ui/{issues/issue-59494.rs => closures/generic-typed-nested-closures-59494.rs} (91%) rename tests/ui/{issues/issue-59494.stderr => closures/generic-typed-nested-closures-59494.stderr} (83%) rename tests/ui/{issues/issue-58375-monomorphize-default-impls.rs => codegen/mono-item-collector-default-impl-58375.rs} (90%) rename tests/ui/{issues/issue-57162.rs => coherence/trait-implementation-coherence-check-57162.rs} (67%) rename tests/ui/{issues/issue-57781.rs => consts/oncecell-const-init-57781.rs} (86%) rename tests/ui/{issues/issue-58463.rs => debuginfo/impl-copy-function-debuginfo-58463.rs} (67%) rename tests/ui/{issues/issue-58734.rs => dyn-compatibility/spurious-dyn-compat-errors-58734.rs} (91%) rename tests/ui/{issues/issue-58734.stderr => dyn-compatibility/spurious-dyn-compat-errors-58734.stderr} (91%) rename tests/ui/{issues/issue-56237.rs => generics/generic-associated-type-deref-target-56237.rs} (77%) rename tests/ui/{issues/issue-55731.rs => higher-ranked/hrtb-associated-type-leak-check-55731.rs} (95%) rename tests/ui/{issues/issue-55731.stderr => higher-ranked/hrtb-associated-type-leak-check-55731.stderr} (90%) create mode 100644 tests/ui/imports/auxiliary/reexported-trait-56175.rs rename tests/ui/{issues/issue-56175.rs => imports/private-types-suggested-without-extern-crate-56175.rs} (59%) rename tests/ui/{issues/issue-56175.stderr => imports/private-types-suggested-without-extern-crate-56175.stderr} (82%) rename tests/ui/{issues/auxiliary/issue-57271-lib.rs => infinite/auxiliary/aux-57271-lib.rs} (100%) rename tests/ui/{issues/issue-57271.rs => infinite/mutually-recursive-infinite-types-57271.rs} (72%) rename tests/ui/{issues/issue-57271.stderr => infinite/mutually-recursive-infinite-types-57271.stderr} (92%) delete mode 100644 tests/ui/issues/issue-56943.rs rename tests/ui/{issues/issue-57198-pass.rs => keyword/raw-identifier-for-function-57198.rs} (60%) rename tests/ui/{issues/auxiliary/issue-56943.rs => mismatched_types/auxiliary/aux-56943.rs} (100%) create mode 100644 tests/ui/mismatched_types/type-mismatch-in-extern-crate-56943.rs rename tests/ui/{issues/issue-56943.stderr => mismatched_types/type-mismatch-in-extern-crate-56943.stderr} (54%) rename tests/ui/{issues/issue-56128.rs => modules/pub-use-handling-in-modules-56128.rs} (100%) rename tests/ui/{issues/issue-55587.rs => parser/invalid-variable-definition-55587.rs} (67%) rename tests/ui/{issues/issue-55587.stderr => parser/invalid-variable-definition-55587.stderr} (88%) rename tests/ui/{issues/issue-58712.rs => resolve/missing-type-in-scope-58712.rs} (84%) rename tests/ui/{issues/issue-58712.stderr => resolve/missing-type-in-scope-58712.stderr} (85%) rename tests/ui/{issues/issue-59020.rs => std/park-timeout-wakeup-59020.rs} (92%) rename tests/ui/{issues/issue-56835.rs => structs/invalid-self-constructor-56835.rs} (83%) rename tests/ui/{issues/issue-56835.stderr => structs/invalid-self-constructor-56835.stderr} (83%) rename tests/ui/{issues/issue-59756.fixed => suggestions/incompatible-types-in-try-expression-59756.fixed} (100%) rename tests/ui/{issues/issue-59756.rs => suggestions/incompatible-types-in-try-expression-59756.rs} (89%) rename tests/ui/{issues/issue-59756.stderr => suggestions/incompatible-types-in-try-expression-59756.stderr} (88%) rename tests/ui/{issues/issue-58857.rs => trait-bounds/negative-bound-not-supported-58857.rs} (71%) rename tests/ui/{issues/issue-58857.stderr => trait-bounds/negative-bound-not-supported-58857.stderr} (71%) rename tests/ui/{issues/issue-58212.rs => traits/generic-trait-impl-aliased-array-58212.rs} (82%) rename tests/ui/{issues/issue-57156.rs => traits/trait-object-lifetime-bounds-57156.rs} (88%) rename tests/ui/{issues/issue-56229.rs => traits/trait-objects-with-supertraits-56229.rs} (90%) rename tests/ui/{issues/issue-57924.rs => typeck/self-constructor-type-args-not-allowed-57924.rs} (78%) rename tests/ui/{issues/issue-57924.stderr => typeck/self-constructor-type-args-not-allowed-57924.stderr} (83%) rename tests/ui/{issues/issue-56199.rs => typeck/self-constructor-type-error-56199.rs} (91%) rename tests/ui/{issues/issue-56199.stderr => typeck/self-constructor-type-error-56199.stderr} (79%) diff --git a/tests/ui/issues/issue-56806.rs b/tests/ui/abi/invalid-self-parameter-type-56806.rs similarity index 72% rename from tests/ui/issues/issue-56806.rs rename to tests/ui/abi/invalid-self-parameter-type-56806.rs index b1dac26d65a1..60229df30057 100644 --- a/tests/ui/issues/issue-56806.rs +++ b/tests/ui/abi/invalid-self-parameter-type-56806.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/56806 pub trait Trait { fn dyn_instead_of_self(self: Box); //~^ ERROR invalid `self` parameter type diff --git a/tests/ui/issues/issue-56806.stderr b/tests/ui/abi/invalid-self-parameter-type-56806.stderr similarity index 91% rename from tests/ui/issues/issue-56806.stderr rename to tests/ui/abi/invalid-self-parameter-type-56806.stderr index ec50d863758d..ac249b8f1088 100644 --- a/tests/ui/issues/issue-56806.stderr +++ b/tests/ui/abi/invalid-self-parameter-type-56806.stderr @@ -1,5 +1,5 @@ error[E0307]: invalid `self` parameter type: `Box<(dyn Trait + 'static)>` - --> $DIR/issue-56806.rs:2:34 + --> $DIR/invalid-self-parameter-type-56806.rs:3:34 | LL | fn dyn_instead_of_self(self: Box); | ^^^^^^^^^^^^^^ diff --git a/tests/ui/issues/issue-56870.rs b/tests/ui/associated-consts/traits-associated-consts-ice-56870.rs similarity index 93% rename from tests/ui/issues/issue-56870.rs rename to tests/ui/associated-consts/traits-associated-consts-ice-56870.rs index fc6deedd029f..0c5a2b847730 100644 --- a/tests/ui/issues/issue-56870.rs +++ b/tests/ui/associated-consts/traits-associated-consts-ice-56870.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/56870 //@ build-pass // Regression test for #56870: Internal compiler error (traits & associated consts) diff --git a/tests/ui/issues/issue-59326.rs b/tests/ui/associated-types/duplicate-associated-type-resolution-59326.rs similarity index 89% rename from tests/ui/issues/issue-59326.rs rename to tests/ui/associated-types/duplicate-associated-type-resolution-59326.rs index e9634ad9fd8f..0439e229e149 100644 --- a/tests/ui/issues/issue-59326.rs +++ b/tests/ui/associated-types/duplicate-associated-type-resolution-59326.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/59326 //@ check-pass trait Service { type S; diff --git a/tests/ui/issues/issue-57399-self-return-impl-trait.rs b/tests/ui/associated-types/impl-trait-member-type-resolution-57399.rs similarity index 81% rename from tests/ui/issues/issue-57399-self-return-impl-trait.rs rename to tests/ui/associated-types/impl-trait-member-type-resolution-57399.rs index bcf1b18a9ff4..3342dd0631a5 100644 --- a/tests/ui/issues/issue-57399-self-return-impl-trait.rs +++ b/tests/ui/associated-types/impl-trait-member-type-resolution-57399.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/57399 //@ check-pass trait T { @@ -12,7 +13,6 @@ struct S { a: A, } - impl From for S<::T> { fn from(a: u32) -> Self { Self { a } diff --git a/tests/ui/issues/issue-59488.rs b/tests/ui/binop/function-comparison-errors-59488.rs similarity index 95% rename from tests/ui/issues/issue-59488.rs rename to tests/ui/binop/function-comparison-errors-59488.rs index 384501e3e5df..8ded781ef954 100644 --- a/tests/ui/issues/issue-59488.rs +++ b/tests/ui/binop/function-comparison-errors-59488.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/59488 fn foo() -> i32 { 42 } diff --git a/tests/ui/issues/issue-59488.stderr b/tests/ui/binop/function-comparison-errors-59488.stderr similarity index 84% rename from tests/ui/issues/issue-59488.stderr rename to tests/ui/binop/function-comparison-errors-59488.stderr index b6611ad63a81..615458bc45b4 100644 --- a/tests/ui/issues/issue-59488.stderr +++ b/tests/ui/binop/function-comparison-errors-59488.stderr @@ -1,5 +1,5 @@ error[E0369]: binary operation `>` cannot be applied to type `fn() -> i32 {foo}` - --> $DIR/issue-59488.rs:14:9 + --> $DIR/function-comparison-errors-59488.rs:15:9 | LL | foo > 12; | --- ^ -- {integer} @@ -12,7 +12,7 @@ LL | foo() > 12; | ++ error[E0308]: mismatched types - --> $DIR/issue-59488.rs:14:11 + --> $DIR/function-comparison-errors-59488.rs:15:11 | LL | foo > 12; | ^^ expected fn item, found `i32` @@ -21,7 +21,7 @@ LL | foo > 12; found type `i32` error[E0369]: binary operation `>` cannot be applied to type `fn(i64) -> i64 {bar}` - --> $DIR/issue-59488.rs:18:9 + --> $DIR/function-comparison-errors-59488.rs:19:9 | LL | bar > 13; | --- ^ -- {integer} @@ -34,7 +34,7 @@ LL | bar(/* i64 */) > 13; | +++++++++++ error[E0308]: mismatched types - --> $DIR/issue-59488.rs:18:11 + --> $DIR/function-comparison-errors-59488.rs:19:11 | LL | bar > 13; | ^^ expected fn item, found `i64` @@ -43,7 +43,7 @@ LL | bar > 13; found type `i64` error[E0369]: binary operation `>` cannot be applied to type `fn() -> i32 {foo}` - --> $DIR/issue-59488.rs:22:9 + --> $DIR/function-comparison-errors-59488.rs:23:9 | LL | foo > foo; | --- ^ --- fn() -> i32 {foo} @@ -56,7 +56,7 @@ LL | foo() > foo(); | ++ ++ error[E0369]: binary operation `>` cannot be applied to type `fn() -> i32 {foo}` - --> $DIR/issue-59488.rs:25:9 + --> $DIR/function-comparison-errors-59488.rs:26:9 | LL | foo > bar; | --- ^ --- fn(i64) -> i64 {bar} @@ -64,7 +64,7 @@ LL | foo > bar; | fn() -> i32 {foo} error[E0308]: mismatched types - --> $DIR/issue-59488.rs:25:11 + --> $DIR/function-comparison-errors-59488.rs:26:11 | LL | foo > bar; | ^^^ expected fn item, found a different fn item @@ -73,7 +73,7 @@ LL | foo > bar; found fn item `fn(i64) -> i64 {bar}` error[E0369]: binary operation `==` cannot be applied to type `fn(usize) -> Foo {Foo::Bar}` - --> $DIR/issue-59488.rs:30:5 + --> $DIR/function-comparison-errors-59488.rs:31:5 | LL | assert_eq!(Foo::Bar, i); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -84,7 +84,7 @@ LL | assert_eq!(Foo::Bar, i); = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: `fn(usize) -> Foo {Foo::Bar}` doesn't implement `Debug` - --> $DIR/issue-59488.rs:30:5 + --> $DIR/function-comparison-errors-59488.rs:31:5 | LL | assert_eq!(Foo::Bar, i); | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Debug` is not implemented for fn item `fn(usize) -> Foo {Foo::Bar}` @@ -92,7 +92,7 @@ LL | assert_eq!(Foo::Bar, i); = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: `fn(usize) -> Foo {Foo::Bar}` doesn't implement `Debug` - --> $DIR/issue-59488.rs:30:5 + --> $DIR/function-comparison-errors-59488.rs:31:5 | LL | assert_eq!(Foo::Bar, i); | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Debug` is not implemented for fn item `fn(usize) -> Foo {Foo::Bar}` diff --git a/tests/ui/issues/issue-57741-dereference-boxed-value/issue-57741-1.rs b/tests/ui/box/boxed-value-matching-57741.rs similarity index 89% rename from tests/ui/issues/issue-57741-dereference-boxed-value/issue-57741-1.rs rename to tests/ui/box/boxed-value-matching-57741.rs index d0aae23b2fce..7e2f089dad82 100644 --- a/tests/ui/issues/issue-57741-dereference-boxed-value/issue-57741-1.rs +++ b/tests/ui/box/boxed-value-matching-57741.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/57741 #![allow(warnings)] // This tests that the `help: consider dereferencing the boxed value` suggestion isn't made diff --git a/tests/ui/issues/issue-57741-dereference-boxed-value/issue-57741-1.stderr b/tests/ui/box/boxed-value-matching-57741.stderr similarity index 88% rename from tests/ui/issues/issue-57741-dereference-boxed-value/issue-57741-1.stderr rename to tests/ui/box/boxed-value-matching-57741.stderr index 76f03bab6d15..33d7a6759adf 100644 --- a/tests/ui/issues/issue-57741-dereference-boxed-value/issue-57741-1.stderr +++ b/tests/ui/box/boxed-value-matching-57741.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-57741-1.rs:14:9 + --> $DIR/boxed-value-matching-57741.rs:15:9 | LL | let y = match x { | - this expression has type `Box` @@ -10,7 +10,7 @@ LL | S::A { a } | S::B { b: a } => a, found enum `S` error[E0308]: mismatched types - --> $DIR/issue-57741-1.rs:14:22 + --> $DIR/boxed-value-matching-57741.rs:15:22 | LL | let y = match x { | - this expression has type `Box` diff --git a/tests/ui/issues/issue-57741-dereference-boxed-value/issue-57741.fixed b/tests/ui/box/dereferencing-boxed-enum-in-match-57741.fixed similarity index 92% rename from tests/ui/issues/issue-57741-dereference-boxed-value/issue-57741.fixed rename to tests/ui/box/dereferencing-boxed-enum-in-match-57741.fixed index 1823f0d3d4cc..ee796b56272d 100644 --- a/tests/ui/issues/issue-57741-dereference-boxed-value/issue-57741.fixed +++ b/tests/ui/box/dereferencing-boxed-enum-in-match-57741.fixed @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/57741 //@ run-rustfix #![allow(warnings)] diff --git a/tests/ui/issues/issue-57741-dereference-boxed-value/issue-57741.rs b/tests/ui/box/dereferencing-boxed-enum-in-match-57741.rs similarity index 92% rename from tests/ui/issues/issue-57741-dereference-boxed-value/issue-57741.rs rename to tests/ui/box/dereferencing-boxed-enum-in-match-57741.rs index 47ab91177e05..3a45a8b56ff1 100644 --- a/tests/ui/issues/issue-57741-dereference-boxed-value/issue-57741.rs +++ b/tests/ui/box/dereferencing-boxed-enum-in-match-57741.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/57741 //@ run-rustfix #![allow(warnings)] diff --git a/tests/ui/issues/issue-57741-dereference-boxed-value/issue-57741.stderr b/tests/ui/box/dereferencing-boxed-enum-in-match-57741.stderr similarity index 87% rename from tests/ui/issues/issue-57741-dereference-boxed-value/issue-57741.stderr rename to tests/ui/box/dereferencing-boxed-enum-in-match-57741.stderr index 62d83a546148..c07387b21bd0 100644 --- a/tests/ui/issues/issue-57741-dereference-boxed-value/issue-57741.stderr +++ b/tests/ui/box/dereferencing-boxed-enum-in-match-57741.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-57741.rs:20:9 + --> $DIR/dereferencing-boxed-enum-in-match-57741.rs:21:9 | LL | let y = match x { | - this expression has type `Box` @@ -14,7 +14,7 @@ LL | let y = match *x { | + error[E0308]: mismatched types - --> $DIR/issue-57741.rs:20:19 + --> $DIR/dereferencing-boxed-enum-in-match-57741.rs:21:19 | LL | let y = match x { | - this expression has type `Box` @@ -29,7 +29,7 @@ LL | let y = match *x { | + error[E0308]: mismatched types - --> $DIR/issue-57741.rs:27:9 + --> $DIR/dereferencing-boxed-enum-in-match-57741.rs:28:9 | LL | let y = match x { | - this expression has type `Box` @@ -44,7 +44,7 @@ LL | let y = match *x { | + error[E0308]: mismatched types - --> $DIR/issue-57741.rs:27:22 + --> $DIR/dereferencing-boxed-enum-in-match-57741.rs:28:22 | LL | let y = match x { | - this expression has type `Box` diff --git a/tests/ui/issues/issue-59494.rs b/tests/ui/closures/generic-typed-nested-closures-59494.rs similarity index 91% rename from tests/ui/issues/issue-59494.rs rename to tests/ui/closures/generic-typed-nested-closures-59494.rs index b4d50bd4ce79..04d7b00ff7fa 100644 --- a/tests/ui/issues/issue-59494.rs +++ b/tests/ui/closures/generic-typed-nested-closures-59494.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/59494 fn t7p(f: impl Fn(B) -> C, g: impl Fn(A) -> B) -> impl Fn(A) -> C { move |a: A| -> C { f(g(a)) } } diff --git a/tests/ui/issues/issue-59494.stderr b/tests/ui/closures/generic-typed-nested-closures-59494.stderr similarity index 83% rename from tests/ui/issues/issue-59494.stderr rename to tests/ui/closures/generic-typed-nested-closures-59494.stderr index 33d3e48c1aac..9706fea82a30 100644 --- a/tests/ui/issues/issue-59494.stderr +++ b/tests/ui/closures/generic-typed-nested-closures-59494.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-59494.rs:20:40 + --> $DIR/generic-typed-nested-closures-59494.rs:21:40 | LL | let t7 = |env| |a| |b| t7p(f, g)(((env, a), b)); | ^^^ cyclic type of infinite size diff --git a/tests/ui/issues/issue-58375-monomorphize-default-impls.rs b/tests/ui/codegen/mono-item-collector-default-impl-58375.rs similarity index 90% rename from tests/ui/issues/issue-58375-monomorphize-default-impls.rs rename to tests/ui/codegen/mono-item-collector-default-impl-58375.rs index 769a1176edd3..f00e79e0dc5a 100644 --- a/tests/ui/issues/issue-58375-monomorphize-default-impls.rs +++ b/tests/ui/codegen/mono-item-collector-default-impl-58375.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/58375 // Make sure that the mono-item collector does not crash when trying to // instantiate a default impl for DecodeUtf16<::Item> // See https://github.com/rust-lang/rust/issues/58375 diff --git a/tests/ui/issues/issue-57162.rs b/tests/ui/coherence/trait-implementation-coherence-check-57162.rs similarity index 67% rename from tests/ui/issues/issue-57162.rs rename to tests/ui/coherence/trait-implementation-coherence-check-57162.rs index 5e62d0eb010b..a57e827ca8bf 100644 --- a/tests/ui/issues/issue-57162.rs +++ b/tests/ui/coherence/trait-implementation-coherence-check-57162.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/57162 //@ check-pass trait Foo {} diff --git a/tests/ui/issues/issue-57781.rs b/tests/ui/consts/oncecell-const-init-57781.rs similarity index 86% rename from tests/ui/issues/issue-57781.rs rename to tests/ui/consts/oncecell-const-init-57781.rs index 7f0d2eda9bb8..27426ef25494 100644 --- a/tests/ui/issues/issue-57781.rs +++ b/tests/ui/consts/oncecell-const-init-57781.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/57781 //@ run-pass use std::cell::UnsafeCell; diff --git a/tests/ui/issues/issue-58463.rs b/tests/ui/debuginfo/impl-copy-function-debuginfo-58463.rs similarity index 67% rename from tests/ui/issues/issue-58463.rs rename to tests/ui/debuginfo/impl-copy-function-debuginfo-58463.rs index 6e4b909bc383..72388c36ce48 100644 --- a/tests/ui/issues/issue-58463.rs +++ b/tests/ui/debuginfo/impl-copy-function-debuginfo-58463.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/58463 //@ run-pass //@ compile-flags:-C debuginfo=2 diff --git a/tests/ui/issues/issue-58734.rs b/tests/ui/dyn-compatibility/spurious-dyn-compat-errors-58734.rs similarity index 91% rename from tests/ui/issues/issue-58734.rs rename to tests/ui/dyn-compatibility/spurious-dyn-compat-errors-58734.rs index e5b371f5530d..3e9ebb497a21 100644 --- a/tests/ui/issues/issue-58734.rs +++ b/tests/ui/dyn-compatibility/spurious-dyn-compat-errors-58734.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/58734 trait Trait { fn exists(self) -> (); diff --git a/tests/ui/issues/issue-58734.stderr b/tests/ui/dyn-compatibility/spurious-dyn-compat-errors-58734.stderr similarity index 91% rename from tests/ui/issues/issue-58734.stderr rename to tests/ui/dyn-compatibility/spurious-dyn-compat-errors-58734.stderr index 2336a94f1504..140461283f36 100644 --- a/tests/ui/issues/issue-58734.stderr +++ b/tests/ui/dyn-compatibility/spurious-dyn-compat-errors-58734.stderr @@ -1,5 +1,5 @@ warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/issue-58734.rs:20:5 + --> $DIR/spurious-dyn-compat-errors-58734.rs:21:5 | LL | Trait::nonexistent(()); | ^^^^^ @@ -13,14 +13,14 @@ LL | ::nonexistent(()); | ++++ + error[E0038]: the trait `Trait` is not dyn compatible - --> $DIR/issue-58734.rs:20:5 + --> $DIR/spurious-dyn-compat-errors-58734.rs:21:5 | LL | Trait::nonexistent(()); | ^^^^^ `Trait` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit - --> $DIR/issue-58734.rs:4:8 + --> $DIR/spurious-dyn-compat-errors-58734.rs:5:8 | LL | trait Trait { | ----- this trait is not dyn compatible... diff --git a/tests/ui/issues/issue-56237.rs b/tests/ui/generics/generic-associated-type-deref-target-56237.rs similarity index 77% rename from tests/ui/issues/issue-56237.rs rename to tests/ui/generics/generic-associated-type-deref-target-56237.rs index 3c0a235f3ec2..2050ca377e8c 100644 --- a/tests/ui/issues/issue-56237.rs +++ b/tests/ui/generics/generic-associated-type-deref-target-56237.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/56237 //@ run-pass use std::ops::Deref; diff --git a/tests/ui/issues/issue-55731.rs b/tests/ui/higher-ranked/hrtb-associated-type-leak-check-55731.rs similarity index 95% rename from tests/ui/issues/issue-55731.rs rename to tests/ui/higher-ranked/hrtb-associated-type-leak-check-55731.rs index 7b4f4e2cd3b4..978abd9fcf5a 100644 --- a/tests/ui/issues/issue-55731.rs +++ b/tests/ui/higher-ranked/hrtb-associated-type-leak-check-55731.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/55731 use std::marker::PhantomData; trait DistributedIterator { diff --git a/tests/ui/issues/issue-55731.stderr b/tests/ui/higher-ranked/hrtb-associated-type-leak-check-55731.stderr similarity index 90% rename from tests/ui/issues/issue-55731.stderr rename to tests/ui/higher-ranked/hrtb-associated-type-leak-check-55731.stderr index 2c38041642d3..40ac1d9d041b 100644 --- a/tests/ui/issues/issue-55731.stderr +++ b/tests/ui/higher-ranked/hrtb-associated-type-leak-check-55731.stderr @@ -1,5 +1,5 @@ error: implementation of `DistributedIteratorMulti` is not general enough - --> $DIR/issue-55731.rs:48:5 + --> $DIR/hrtb-associated-type-leak-check-55731.rs:49:5 | LL | / multi(Map { LL | | i: Cloned(PhantomData), diff --git a/tests/ui/imports/auxiliary/reexported-trait-56175.rs b/tests/ui/imports/auxiliary/reexported-trait-56175.rs new file mode 100644 index 000000000000..51a991bef595 --- /dev/null +++ b/tests/ui/imports/auxiliary/reexported-trait-56175.rs @@ -0,0 +1,17 @@ +mod private { + pub trait Trait { + fn trait_method(&self) { + } + } + pub trait TraitB { + fn trait_method_b(&self) { + } + } +} + +pub struct FooStruct; +pub use crate::private::Trait; +impl crate::private::Trait for FooStruct {} + +pub use crate::private::TraitB as TraitBRename; +impl crate::private::TraitB for FooStruct {} diff --git a/tests/ui/issues/issue-56175.rs b/tests/ui/imports/private-types-suggested-without-extern-crate-56175.rs similarity index 59% rename from tests/ui/issues/issue-56175.rs rename to tests/ui/imports/private-types-suggested-without-extern-crate-56175.rs index daffe806a900..ce001edad1b6 100644 --- a/tests/ui/issues/issue-56175.rs +++ b/tests/ui/imports/private-types-suggested-without-extern-crate-56175.rs @@ -1,5 +1,6 @@ +// https://github.com/rust-lang/rust/issues/56175 //@ edition:2018 -//@ aux-crate:reexported_trait=reexported-trait.rs +//@ aux-crate:reexported_trait=reexported-trait-56175.rs fn main() { reexported_trait::FooStruct.trait_method(); diff --git a/tests/ui/issues/issue-56175.stderr b/tests/ui/imports/private-types-suggested-without-extern-crate-56175.stderr similarity index 82% rename from tests/ui/issues/issue-56175.stderr rename to tests/ui/imports/private-types-suggested-without-extern-crate-56175.stderr index df4cd6ce8a70..1e8285c80ac3 100644 --- a/tests/ui/issues/issue-56175.stderr +++ b/tests/ui/imports/private-types-suggested-without-extern-crate-56175.stderr @@ -1,10 +1,10 @@ error[E0599]: no method named `trait_method` found for struct `FooStruct` in the current scope - --> $DIR/issue-56175.rs:5:33 + --> $DIR/private-types-suggested-without-extern-crate-56175.rs:6:33 | LL | reexported_trait::FooStruct.trait_method(); | ^^^^^^^^^^^^ | - ::: $DIR/auxiliary/reexported-trait.rs:3:12 + ::: $DIR/auxiliary/reexported-trait-56175.rs:3:12 | LL | fn trait_method(&self) { | ------------ the method is available for `FooStruct` here @@ -12,7 +12,7 @@ LL | fn trait_method(&self) { = help: items from traits can only be used if the trait is in scope help: trait `Trait` which provides `trait_method` is implemented but not in scope; perhaps you want to import it | -LL + use reexported_trait::Trait; +LL + use reexported_trait_56175::Trait; | help: there is a method `trait_method_b` with a similar name | @@ -20,12 +20,12 @@ LL | reexported_trait::FooStruct.trait_method_b(); | ++ error[E0599]: no method named `trait_method_b` found for struct `FooStruct` in the current scope - --> $DIR/issue-56175.rs:7:33 + --> $DIR/private-types-suggested-without-extern-crate-56175.rs:8:33 | LL | reexported_trait::FooStruct.trait_method_b(); | ^^^^^^^^^^^^^^ | - ::: $DIR/auxiliary/reexported-trait.rs:7:12 + ::: $DIR/auxiliary/reexported-trait-56175.rs:7:12 | LL | fn trait_method_b(&self) { | -------------- the method is available for `FooStruct` here @@ -33,7 +33,7 @@ LL | fn trait_method_b(&self) { = help: items from traits can only be used if the trait is in scope help: trait `TraitB` which provides `trait_method_b` is implemented but not in scope; perhaps you want to import it | -LL + use reexported_trait::TraitBRename; +LL + use reexported_trait_56175::TraitBRename; | help: there is a method `trait_method` with a similar name | diff --git a/tests/ui/issues/auxiliary/issue-57271-lib.rs b/tests/ui/infinite/auxiliary/aux-57271-lib.rs similarity index 100% rename from tests/ui/issues/auxiliary/issue-57271-lib.rs rename to tests/ui/infinite/auxiliary/aux-57271-lib.rs diff --git a/tests/ui/issues/issue-57271.rs b/tests/ui/infinite/mutually-recursive-infinite-types-57271.rs similarity index 72% rename from tests/ui/issues/issue-57271.rs rename to tests/ui/infinite/mutually-recursive-infinite-types-57271.rs index 20d081ecb3ce..cb20770b4869 100644 --- a/tests/ui/issues/issue-57271.rs +++ b/tests/ui/infinite/mutually-recursive-infinite-types-57271.rs @@ -1,8 +1,9 @@ -//@ aux-build:issue-57271-lib.rs +// https://github.com/rust-lang/rust/issues/57271 +//@ aux-build:aux-57271-lib.rs -extern crate issue_57271_lib; +extern crate aux_57271_lib; -use issue_57271_lib::BaseType; +use aux_57271_lib::BaseType; pub enum ObjectType { //~ ERROR recursive types `ObjectType` and `TypeSignature` have infinite size Class(ClassTypeSignature), diff --git a/tests/ui/issues/issue-57271.stderr b/tests/ui/infinite/mutually-recursive-infinite-types-57271.stderr similarity index 92% rename from tests/ui/issues/issue-57271.stderr rename to tests/ui/infinite/mutually-recursive-infinite-types-57271.stderr index a61419c61d74..8bf1b470062b 100644 --- a/tests/ui/issues/issue-57271.stderr +++ b/tests/ui/infinite/mutually-recursive-infinite-types-57271.stderr @@ -1,5 +1,5 @@ error[E0072]: recursive types `ObjectType` and `TypeSignature` have infinite size - --> $DIR/issue-57271.rs:7:1 + --> $DIR/mutually-recursive-infinite-types-57271.rs:8:1 | LL | pub enum ObjectType { | ^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/issues/issue-56943.rs b/tests/ui/issues/issue-56943.rs deleted file mode 100644 index 9664567ec9ea..000000000000 --- a/tests/ui/issues/issue-56943.rs +++ /dev/null @@ -1,8 +0,0 @@ -//@ aux-build:issue-56943.rs - -extern crate issue_56943; - -fn main() { - let _: issue_56943::S = issue_56943::S2; - //~^ ERROR mismatched types [E0308] -} diff --git a/tests/ui/issues/issue-57198-pass.rs b/tests/ui/keyword/raw-identifier-for-function-57198.rs similarity index 60% rename from tests/ui/issues/issue-57198-pass.rs rename to tests/ui/keyword/raw-identifier-for-function-57198.rs index 06f30603c316..41a0cbf4619d 100644 --- a/tests/ui/issues/issue-57198-pass.rs +++ b/tests/ui/keyword/raw-identifier-for-function-57198.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/57198 //@ run-pass mod m { diff --git a/tests/ui/issues/auxiliary/issue-56943.rs b/tests/ui/mismatched_types/auxiliary/aux-56943.rs similarity index 100% rename from tests/ui/issues/auxiliary/issue-56943.rs rename to tests/ui/mismatched_types/auxiliary/aux-56943.rs diff --git a/tests/ui/mismatched_types/type-mismatch-in-extern-crate-56943.rs b/tests/ui/mismatched_types/type-mismatch-in-extern-crate-56943.rs new file mode 100644 index 000000000000..9970b27c847b --- /dev/null +++ b/tests/ui/mismatched_types/type-mismatch-in-extern-crate-56943.rs @@ -0,0 +1,9 @@ +// https://github.com/rust-lang/rust/issues/56943 +//@ aux-build:aux-56943.rs + +extern crate aux_56943; + +fn main() { + let _: aux_56943::S = aux_56943::S2; + //~^ ERROR mismatched types [E0308] +} diff --git a/tests/ui/issues/issue-56943.stderr b/tests/ui/mismatched_types/type-mismatch-in-extern-crate-56943.stderr similarity index 54% rename from tests/ui/issues/issue-56943.stderr rename to tests/ui/mismatched_types/type-mismatch-in-extern-crate-56943.stderr index 60a2e92dc71b..2315267701d1 100644 --- a/tests/ui/issues/issue-56943.stderr +++ b/tests/ui/mismatched_types/type-mismatch-in-extern-crate-56943.stderr @@ -1,8 +1,8 @@ error[E0308]: mismatched types - --> $DIR/issue-56943.rs:6:29 + --> $DIR/type-mismatch-in-extern-crate-56943.rs:7:27 | -LL | let _: issue_56943::S = issue_56943::S2; - | -------------- ^^^^^^^^^^^^^^^ expected `S`, found `S2` +LL | let _: aux_56943::S = aux_56943::S2; + | ------------ ^^^^^^^^^^^^^ expected `S`, found `S2` | | | expected due to this diff --git a/tests/ui/issues/issue-56128.rs b/tests/ui/modules/pub-use-handling-in-modules-56128.rs similarity index 100% rename from tests/ui/issues/issue-56128.rs rename to tests/ui/modules/pub-use-handling-in-modules-56128.rs diff --git a/tests/ui/issues/issue-55587.rs b/tests/ui/parser/invalid-variable-definition-55587.rs similarity index 67% rename from tests/ui/issues/issue-55587.rs rename to tests/ui/parser/invalid-variable-definition-55587.rs index d9100cf555b3..f2c7c0a8e6c3 100644 --- a/tests/ui/issues/issue-55587.rs +++ b/tests/ui/parser/invalid-variable-definition-55587.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/55587 use std::path::Path; fn main() { diff --git a/tests/ui/issues/issue-55587.stderr b/tests/ui/parser/invalid-variable-definition-55587.stderr similarity index 88% rename from tests/ui/issues/issue-55587.stderr rename to tests/ui/parser/invalid-variable-definition-55587.stderr index 7a5d0e281007..08c951582e3f 100644 --- a/tests/ui/issues/issue-55587.stderr +++ b/tests/ui/parser/invalid-variable-definition-55587.stderr @@ -1,5 +1,5 @@ error[E0164]: expected tuple struct or tuple variant, found associated function `Path::new` - --> $DIR/issue-55587.rs:4:9 + --> $DIR/invalid-variable-definition-55587.rs:5:9 | LL | let Path::new(); | ^^^^^^^^^^^ `fn` calls are not allowed in patterns diff --git a/tests/ui/issues/issue-58712.rs b/tests/ui/resolve/missing-type-in-scope-58712.rs similarity index 84% rename from tests/ui/issues/issue-58712.rs rename to tests/ui/resolve/missing-type-in-scope-58712.rs index 930bec6889bc..b9ff74e426df 100644 --- a/tests/ui/issues/issue-58712.rs +++ b/tests/ui/resolve/missing-type-in-scope-58712.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/58712 struct AddrVec { h: H, a: A, diff --git a/tests/ui/issues/issue-58712.stderr b/tests/ui/resolve/missing-type-in-scope-58712.stderr similarity index 85% rename from tests/ui/issues/issue-58712.stderr rename to tests/ui/resolve/missing-type-in-scope-58712.stderr index f4bd4d1e826a..d7e06eee856c 100644 --- a/tests/ui/issues/issue-58712.stderr +++ b/tests/ui/resolve/missing-type-in-scope-58712.stderr @@ -1,5 +1,5 @@ error[E0412]: cannot find type `DeviceId` in this scope - --> $DIR/issue-58712.rs:6:20 + --> $DIR/missing-type-in-scope-58712.rs:7:20 | LL | impl AddrVec { | ^^^^^^^^ not found in this scope @@ -10,7 +10,7 @@ LL | impl AddrVec { | ++++++++++ error[E0412]: cannot find type `DeviceId` in this scope - --> $DIR/issue-58712.rs:8:29 + --> $DIR/missing-type-in-scope-58712.rs:9:29 | LL | pub fn device(&self) -> DeviceId { | ^^^^^^^^ not found in this scope diff --git a/tests/ui/issues/issue-59020.rs b/tests/ui/std/park-timeout-wakeup-59020.rs similarity index 92% rename from tests/ui/issues/issue-59020.rs rename to tests/ui/std/park-timeout-wakeup-59020.rs index 2a34ba52b883..af530bb586c7 100644 --- a/tests/ui/issues/issue-59020.rs +++ b/tests/ui/std/park-timeout-wakeup-59020.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/59020 //@ edition:2018 //@ run-pass //@ needs-threads diff --git a/tests/ui/issues/issue-56835.rs b/tests/ui/structs/invalid-self-constructor-56835.rs similarity index 83% rename from tests/ui/issues/issue-56835.rs rename to tests/ui/structs/invalid-self-constructor-56835.rs index 7132d15ee5fb..fd8763443f04 100644 --- a/tests/ui/issues/issue-56835.rs +++ b/tests/ui/structs/invalid-self-constructor-56835.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/56835 pub struct Foo {} impl Foo { diff --git a/tests/ui/issues/issue-56835.stderr b/tests/ui/structs/invalid-self-constructor-56835.stderr similarity index 83% rename from tests/ui/issues/issue-56835.stderr rename to tests/ui/structs/invalid-self-constructor-56835.stderr index e949ae7b3240..045781ec42bd 100644 --- a/tests/ui/issues/issue-56835.stderr +++ b/tests/ui/structs/invalid-self-constructor-56835.stderr @@ -1,11 +1,11 @@ error: the `Self` constructor can only be used with tuple or unit structs - --> $DIR/issue-56835.rs:4:12 + --> $DIR/invalid-self-constructor-56835.rs:5:12 | LL | fn bar(Self(foo): Self) {} | ^^^^^^^^^ help: use curly brackets: `Self { /* fields */ }` error[E0164]: expected tuple struct or tuple variant, found self constructor `Self` - --> $DIR/issue-56835.rs:4:12 + --> $DIR/invalid-self-constructor-56835.rs:5:12 | LL | fn bar(Self(foo): Self) {} | ^^^^^^^^^ not a tuple struct or tuple variant diff --git a/tests/ui/issues/issue-59756.fixed b/tests/ui/suggestions/incompatible-types-in-try-expression-59756.fixed similarity index 100% rename from tests/ui/issues/issue-59756.fixed rename to tests/ui/suggestions/incompatible-types-in-try-expression-59756.fixed diff --git a/tests/ui/issues/issue-59756.rs b/tests/ui/suggestions/incompatible-types-in-try-expression-59756.rs similarity index 89% rename from tests/ui/issues/issue-59756.rs rename to tests/ui/suggestions/incompatible-types-in-try-expression-59756.rs index de349f43f464..570aa7d373b7 100644 --- a/tests/ui/issues/issue-59756.rs +++ b/tests/ui/suggestions/incompatible-types-in-try-expression-59756.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/59756 //@ run-rustfix //@ ignore-test (rustfix needs multiple suggestions) // diff --git a/tests/ui/issues/issue-59756.stderr b/tests/ui/suggestions/incompatible-types-in-try-expression-59756.stderr similarity index 88% rename from tests/ui/issues/issue-59756.stderr rename to tests/ui/suggestions/incompatible-types-in-try-expression-59756.stderr index 27c07fecd5b5..7182109b5a02 100644 --- a/tests/ui/issues/issue-59756.stderr +++ b/tests/ui/suggestions/incompatible-types-in-try-expression-59756.stderr @@ -1,5 +1,5 @@ error[E0308]: try expression alternatives have incompatible types - --> $DIR/issue-59756.rs:13:5 + --> $DIR/incompatible-types-in-try-expression-59756.rs:13:5 | LL | foo()? | ^^^^^^ expected enum `std::result::Result`, found struct `A` diff --git a/tests/ui/issues/issue-58857.rs b/tests/ui/trait-bounds/negative-bound-not-supported-58857.rs similarity index 71% rename from tests/ui/issues/issue-58857.rs rename to tests/ui/trait-bounds/negative-bound-not-supported-58857.rs index 4350d7e5b403..0a0b70a216be 100644 --- a/tests/ui/issues/issue-58857.rs +++ b/tests/ui/trait-bounds/negative-bound-not-supported-58857.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/58857 struct Conj {a : A} trait Valid {} diff --git a/tests/ui/issues/issue-58857.stderr b/tests/ui/trait-bounds/negative-bound-not-supported-58857.stderr similarity index 71% rename from tests/ui/issues/issue-58857.stderr rename to tests/ui/trait-bounds/negative-bound-not-supported-58857.stderr index ac70bc725e27..05347a891b4f 100644 --- a/tests/ui/issues/issue-58857.stderr +++ b/tests/ui/trait-bounds/negative-bound-not-supported-58857.stderr @@ -1,5 +1,5 @@ error: negative bounds are not supported - --> $DIR/issue-58857.rs:4:9 + --> $DIR/negative-bound-not-supported-58857.rs:5:9 | LL | impl Conj{} | ^ diff --git a/tests/ui/issues/issue-58212.rs b/tests/ui/traits/generic-trait-impl-aliased-array-58212.rs similarity index 82% rename from tests/ui/issues/issue-58212.rs rename to tests/ui/traits/generic-trait-impl-aliased-array-58212.rs index f266db603bf1..a71194ba8064 100644 --- a/tests/ui/issues/issue-58212.rs +++ b/tests/ui/traits/generic-trait-impl-aliased-array-58212.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/58212 //@ check-pass trait FromUnchecked { diff --git a/tests/ui/issues/issue-57156.rs b/tests/ui/traits/trait-object-lifetime-bounds-57156.rs similarity index 88% rename from tests/ui/issues/issue-57156.rs rename to tests/ui/traits/trait-object-lifetime-bounds-57156.rs index 12251509abd2..8f5bef7fe13b 100644 --- a/tests/ui/issues/issue-57156.rs +++ b/tests/ui/traits/trait-object-lifetime-bounds-57156.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/57156 //@ check-pass trait Foo { diff --git a/tests/ui/issues/issue-56229.rs b/tests/ui/traits/trait-objects-with-supertraits-56229.rs similarity index 90% rename from tests/ui/issues/issue-56229.rs rename to tests/ui/traits/trait-objects-with-supertraits-56229.rs index 1c6dd72ed2de..27cae968a6ac 100644 --- a/tests/ui/issues/issue-56229.rs +++ b/tests/ui/traits/trait-objects-with-supertraits-56229.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/56229 //@ check-pass trait Mirror { diff --git a/tests/ui/issues/issue-57924.rs b/tests/ui/typeck/self-constructor-type-args-not-allowed-57924.rs similarity index 78% rename from tests/ui/issues/issue-57924.rs rename to tests/ui/typeck/self-constructor-type-args-not-allowed-57924.rs index 8846912a8ff7..2b3b11c3f380 100644 --- a/tests/ui/issues/issue-57924.rs +++ b/tests/ui/typeck/self-constructor-type-args-not-allowed-57924.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/57924 pub struct Gcm(E); impl Gcm { diff --git a/tests/ui/issues/issue-57924.stderr b/tests/ui/typeck/self-constructor-type-args-not-allowed-57924.stderr similarity index 83% rename from tests/ui/issues/issue-57924.stderr rename to tests/ui/typeck/self-constructor-type-args-not-allowed-57924.stderr index 40435fd0f0a9..b5be5b39eb8f 100644 --- a/tests/ui/issues/issue-57924.stderr +++ b/tests/ui/typeck/self-constructor-type-args-not-allowed-57924.stderr @@ -1,5 +1,5 @@ error[E0109]: type arguments are not allowed on self constructor - --> $DIR/issue-57924.rs:5:16 + --> $DIR/self-constructor-type-args-not-allowed-57924.rs:6:16 | LL | Self::(e) | ---- ^ type argument not allowed diff --git a/tests/ui/issues/issue-56199.rs b/tests/ui/typeck/self-constructor-type-error-56199.rs similarity index 91% rename from tests/ui/issues/issue-56199.rs rename to tests/ui/typeck/self-constructor-type-error-56199.rs index ba11582a9d59..b08d69189807 100644 --- a/tests/ui/issues/issue-56199.rs +++ b/tests/ui/typeck/self-constructor-type-error-56199.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/56199 enum Foo {} struct Bar {} diff --git a/tests/ui/issues/issue-56199.stderr b/tests/ui/typeck/self-constructor-type-error-56199.stderr similarity index 79% rename from tests/ui/issues/issue-56199.stderr rename to tests/ui/typeck/self-constructor-type-error-56199.stderr index eb6d7005979f..6e9d0fcd90c0 100644 --- a/tests/ui/issues/issue-56199.stderr +++ b/tests/ui/typeck/self-constructor-type-error-56199.stderr @@ -1,5 +1,5 @@ error: the `Self` constructor can only be used with tuple or unit structs - --> $DIR/issue-56199.rs:6:17 + --> $DIR/self-constructor-type-error-56199.rs:7:17 | LL | let _ = Self; | ^^^^ @@ -7,7 +7,7 @@ LL | let _ = Self; = help: did you mean to use one of the enum's variants? error: the `Self` constructor can only be used with tuple or unit structs - --> $DIR/issue-56199.rs:8:17 + --> $DIR/self-constructor-type-error-56199.rs:9:17 | LL | let _ = Self(); | ^^^^^^ @@ -15,13 +15,13 @@ LL | let _ = Self(); = help: did you mean to use one of the enum's variants? error: the `Self` constructor can only be used with tuple or unit structs - --> $DIR/issue-56199.rs:15:17 + --> $DIR/self-constructor-type-error-56199.rs:16:17 | LL | let _ = Self; | ^^^^ help: use curly brackets: `Self { /* fields */ }` error: the `Self` constructor can only be used with tuple or unit structs - --> $DIR/issue-56199.rs:17:17 + --> $DIR/self-constructor-type-error-56199.rs:18:17 | LL | let _ = Self(); | ^^^^^^ help: use curly brackets: `Self { /* fields */ }` From f157ce994ea45e9faea9eff89c5f8b3d4ea77b6e Mon Sep 17 00:00:00 2001 From: Noratrieb <48135649+Noratrieb@users.noreply.github.com> Date: Sat, 26 Jul 2025 12:39:31 +0200 Subject: [PATCH 443/682] Add --print target-spec-json-schema This schema is helpful for people writing custom target spec JSON. It can provide autocomplete in the editor, and also serves as documentation when there are documentation comments on the structs, as `schemars` will put them in the schema. --- Cargo.lock | 63 +++++++++++++++++ .../rustc_codegen_ssa/src/back/command.rs | 2 +- compiler/rustc_driver_impl/src/lib.rs | 4 ++ compiler/rustc_session/src/config.rs | 5 +- compiler/rustc_target/Cargo.toml | 1 + compiler/rustc_target/src/lib.rs | 2 + compiler/rustc_target/src/spec/json.rs | 38 ++++++++-- compiler/rustc_target/src/spec/mod.rs | 70 ++++++++++++++++++- src/bootstrap/src/core/build_steps/dist.rs | 24 +++++++ src/bootstrap/src/utils/proc_macro_deps.rs | 1 + src/doc/rustc/src/targets/custom.md | 15 ++++ src/tools/tidy/src/deps.rs | 6 ++ .../help-diff.diff | 2 +- .../unstable-invalid-print-request-help.err | 2 +- tests/run-make/rustc-help/help-v.stdout | 2 +- tests/run-make/rustc-help/help.stdout | 2 +- .../print-without-arg.stderr | 2 +- tests/ui/invalid-compile-flags/print.stderr | 2 +- .../ui/print-request/print-lints-help.stderr | 2 +- 19 files changed, 229 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2dc6e8ff1032..4677d34d2a63 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1191,6 +1191,12 @@ version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8975ffdaa0ef3661bfe02dbdcc06c9f829dfafe6a3c474de366a8d5e44276921" +[[package]] +name = "dyn-clone" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" + [[package]] name = "either" version = "1.15.0" @@ -3122,6 +3128,26 @@ dependencies = [ "thiserror 2.0.15", ] +[[package]] +name = "ref-cast" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "regex" version = "1.11.1" @@ -4577,6 +4603,7 @@ dependencies = [ "rustc_macros", "rustc_serialize", "rustc_span", + "schemars", "serde", "serde_derive", "serde_json", @@ -4900,6 +4927,31 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "schemars" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" +dependencies = [ + "dyn-clone", + "ref-cast", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d020396d1d138dc19f1165df7545479dcd58d93810dc5d646a16e55abefa80" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.106", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -4974,6 +5026,17 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "serde_json" version = "1.0.142" diff --git a/compiler/rustc_codegen_ssa/src/back/command.rs b/compiler/rustc_codegen_ssa/src/back/command.rs index 05351bd6ca37..7420f18aacb3 100644 --- a/compiler/rustc_codegen_ssa/src/back/command.rs +++ b/compiler/rustc_codegen_ssa/src/back/command.rs @@ -109,7 +109,7 @@ impl Command { } Program::Lld(ref p, flavor) => { let mut c = process::Command::new(p); - c.arg("-flavor").arg(flavor.as_str()); + c.arg("-flavor").arg(flavor.desc()); c } }; diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index d00a4c358340..4f875cf99ec2 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -668,6 +668,10 @@ fn print_crate_info( TargetSpecJson => { println_info!("{}", serde_json::to_string_pretty(&sess.target.to_json()).unwrap()); } + TargetSpecJsonSchema => { + let schema = rustc_target::spec::json_schema(); + println_info!("{}", serde_json::to_string_pretty(&schema).unwrap()); + } AllTargetSpecsJson => { let mut targets = BTreeMap::new(); for name in rustc_target::spec::TARGETS { diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 9793d8091e2a..297df7c2c976 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -70,6 +70,7 @@ pub const PRINT_KINDS: &[(&str, PrintKind)] = &[ ("target-libdir", PrintKind::TargetLibdir), ("target-list", PrintKind::TargetList), ("target-spec-json", PrintKind::TargetSpecJson), + ("target-spec-json-schema", PrintKind::TargetSpecJsonSchema), ("tls-models", PrintKind::TlsModels), // tidy-alphabetical-end ]; @@ -1043,6 +1044,7 @@ pub enum PrintKind { TargetLibdir, TargetList, TargetSpecJson, + TargetSpecJsonSchema, TlsModels, // tidy-alphabetical-end } @@ -2323,7 +2325,8 @@ fn is_print_request_stable(print_kind: PrintKind) -> bool { | PrintKind::CheckCfg | PrintKind::CrateRootLintLevels | PrintKind::SupportedCrateTypes - | PrintKind::TargetSpecJson => false, + | PrintKind::TargetSpecJson + | PrintKind::TargetSpecJsonSchema => false, _ => true, } } diff --git a/compiler/rustc_target/Cargo.toml b/compiler/rustc_target/Cargo.toml index 7fabb227d9a5..ecdb6ab5a576 100644 --- a/compiler/rustc_target/Cargo.toml +++ b/compiler/rustc_target/Cargo.toml @@ -14,6 +14,7 @@ rustc_fs_util = { path = "../rustc_fs_util" } rustc_macros = { path = "../rustc_macros" } rustc_serialize = { path = "../rustc_serialize" } rustc_span = { path = "../rustc_span" } +schemars = "1.0.4" serde = "1.0.219" serde_derive = "1.0.219" serde_json = "1.0.59" diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs index c6a23745b04e..b3fe1fffcceb 100644 --- a/compiler/rustc_target/src/lib.rs +++ b/compiler/rustc_target/src/lib.rs @@ -86,9 +86,11 @@ macro_rules! target_spec_enum { ) => { $( #[$attr] )* #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] + #[derive(schemars::JsonSchema)] pub enum $name { $( $( #[$variant_attr] )* + #[serde(rename = $string)] // for JSON schema generation only $variant, )* } diff --git a/compiler/rustc_target/src/spec/json.rs b/compiler/rustc_target/src/spec/json.rs index e9ae5734d5b2..f236be92b3b6 100644 --- a/compiler/rustc_target/src/spec/json.rs +++ b/compiler/rustc_target/src/spec/json.rs @@ -408,12 +408,12 @@ impl ToJson for Target { } } -#[derive(serde_derive::Deserialize)] +#[derive(serde_derive::Deserialize, schemars::JsonSchema)] struct LinkSelfContainedComponentsWrapper { components: Vec, } -#[derive(serde_derive::Deserialize)] +#[derive(serde_derive::Deserialize, schemars::JsonSchema)] #[serde(untagged)] enum TargetFamiliesJson { Array(StaticCow<[StaticCow]>), @@ -429,6 +429,18 @@ impl FromStr for EndianWrapper { } } crate::json::serde_deserialize_from_str!(EndianWrapper); +impl schemars::JsonSchema for EndianWrapper { + fn schema_name() -> std::borrow::Cow<'static, str> { + "Endian".into() + } + fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema { + schemars::json_schema! ({ + "type": "string", + "enum": ["big", "little"] + }) + .into() + } +} /// `ExternAbi` is in `rustc_abi`, which doesn't have access to the macro and serde. struct ExternAbiWrapper(rustc_abi::ExternAbi); @@ -441,8 +453,22 @@ impl FromStr for ExternAbiWrapper { } } crate::json::serde_deserialize_from_str!(ExternAbiWrapper); +impl schemars::JsonSchema for ExternAbiWrapper { + fn schema_name() -> std::borrow::Cow<'static, str> { + "ExternAbi".into() + } + fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema { + let all = + rustc_abi::ExternAbi::ALL_VARIANTS.iter().map(|abi| abi.as_str()).collect::>(); + schemars::json_schema! ({ + "type": "string", + "enum": all, + }) + .into() + } +} -#[derive(serde_derive::Deserialize)] +#[derive(serde_derive::Deserialize, schemars::JsonSchema)] struct TargetSpecJsonMetadata { description: Option>, tier: Option, @@ -450,7 +476,7 @@ struct TargetSpecJsonMetadata { std: Option, } -#[derive(serde_derive::Deserialize)] +#[derive(serde_derive::Deserialize, schemars::JsonSchema)] #[serde(rename_all = "kebab-case")] // Ensure that all unexpected fields get turned into errors. // This helps users stay up to date when the schema changes instead of silently @@ -593,3 +619,7 @@ struct TargetSpecJson { supports_xray: Option, entry_abi: Option, } + +pub fn json_schema() -> schemars::Schema { + schemars::schema_for!(TargetSpecJson) +} diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 239ac7bf5c4d..07fb1ce63f7c 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -70,6 +70,7 @@ mod json; pub use abi_map::{AbiMap, AbiMapping}; pub use base::apple; pub use base::avr::ef_avr_arch; +pub use json::json_schema; /// Linker is called through a C/C++ compiler. #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] @@ -523,6 +524,20 @@ linker_flavor_cli_impls! { } crate::json::serde_deserialize_from_str!(LinkerFlavorCli); +impl schemars::JsonSchema for LinkerFlavorCli { + fn schema_name() -> std::borrow::Cow<'static, str> { + "LinkerFlavor".into() + } + fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema { + let all: Vec<&'static str> = + Self::all().iter().map(|flavor| flavor.desc()).collect::>(); + schemars::json_schema! ({ + "type": "string", + "enum": all + }) + .into() + } +} impl ToJson for LinkerFlavorCli { fn to_json(&self) -> Json { @@ -576,6 +591,18 @@ impl FromStr for LinkSelfContainedDefault { } crate::json::serde_deserialize_from_str!(LinkSelfContainedDefault); +impl schemars::JsonSchema for LinkSelfContainedDefault { + fn schema_name() -> std::borrow::Cow<'static, str> { + "LinkSelfContainedDefault".into() + } + fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema { + schemars::json_schema! ({ + "type": "string", + "enum": ["false", "true", "wasm", "musl", "mingw"] + }) + .into() + } +} impl ToJson for LinkSelfContainedDefault { fn to_json(&self) -> Json { @@ -708,6 +735,20 @@ impl FromStr for LinkSelfContainedComponents { } crate::json::serde_deserialize_from_str!(LinkSelfContainedComponents); +impl schemars::JsonSchema for LinkSelfContainedComponents { + fn schema_name() -> std::borrow::Cow<'static, str> { + "LinkSelfContainedComponents".into() + } + fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema { + let all = + Self::all_components().iter().map(|component| component.as_str()).collect::>(); + schemars::json_schema! ({ + "type": "string", + "enum": all, + }) + .into() + } +} impl ToJson for LinkSelfContainedComponents { fn to_json(&self) -> Json { @@ -846,7 +887,6 @@ crate::target_spec_enum! { parse_error_type = "symbol visibility"; } - #[derive(Clone, Debug, PartialEq, Hash)] pub enum SmallDataThresholdSupport { None, @@ -874,6 +914,18 @@ impl FromStr for SmallDataThresholdSupport { } crate::json::serde_deserialize_from_str!(SmallDataThresholdSupport); +impl schemars::JsonSchema for SmallDataThresholdSupport { + fn schema_name() -> std::borrow::Cow<'static, str> { + "SmallDataThresholdSupport".into() + } + fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema { + schemars::json_schema! ({ + "type": "string", + "pattern": r#"^none|default-for-arch|llvm-module-flag=.+|llvm-arg=.+$"#, + }) + .into() + } +} impl ToJson for SmallDataThresholdSupport { fn to_json(&self) -> Value { @@ -1074,7 +1126,7 @@ crate::target_spec_enum! { into_diag_arg_using_display!(SplitDebuginfo); -#[derive(Clone, Debug, PartialEq, Eq, serde_derive::Deserialize)] +#[derive(Clone, Debug, PartialEq, Eq, serde_derive::Deserialize, schemars::JsonSchema)] #[serde(tag = "kind")] #[serde(rename_all = "kebab-case")] pub enum StackProbeType { @@ -1235,6 +1287,19 @@ impl FromStr for SanitizerSet { } crate::json::serde_deserialize_from_str!(SanitizerSet); +impl schemars::JsonSchema for SanitizerSet { + fn schema_name() -> std::borrow::Cow<'static, str> { + "SanitizerSet".into() + } + fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema { + let all = Self::all().iter().map(|sanitizer| sanitizer.as_str()).collect::>(); + schemars::json_schema! ({ + "type": "string", + "enum": all, + }) + .into() + } +} impl ToJson for SanitizerSet { fn to_json(&self) -> Json { @@ -1328,7 +1393,6 @@ impl BinaryFormat { } } - impl ToJson for Align { fn to_json(&self) -> Json { self.bits().to_json() diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index a5f0718bc01a..99a1062109ad 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -590,6 +590,8 @@ impl Step for Rustc { // Debugger scripts builder.ensure(DebuggerScripts { sysroot: image.to_owned(), target }); + generate_target_spec_json_schema(builder, image); + // HTML copyright files let file_list = builder.ensure(super::run::GenerateCopyright); for file in file_list { @@ -618,6 +620,28 @@ impl Step for Rustc { } } +fn generate_target_spec_json_schema(builder: &Builder<'_>, sysroot: &Path) { + // Since we run rustc in bootstrap, we need to ensure that we use the host compiler. + // We do this by using the stage 1 compiler, which is always compiled for the host, + // even in a cross build. + let stage1_host = builder.compiler(1, builder.host_target); + let mut rustc = command(builder.rustc(stage1_host)).fail_fast(); + rustc + .env("RUSTC_BOOTSTRAP", "1") + .args(["--print=target-spec-json-schema", "-Zunstable-options"]); + let schema = rustc.run_capture(builder).stdout(); + + let schema_dir = tmpdir(builder); + t!(fs::create_dir_all(&schema_dir)); + let schema_file = schema_dir.join("target-spec-json-schema.json"); + t!(std::fs::write(&schema_file, schema)); + + let dst = sysroot.join("etc"); + t!(fs::create_dir_all(&dst)); + + builder.install(&schema_file, &dst, FileType::Regular); +} + /// Copies debugger scripts for `target` into the given compiler `sysroot`. #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct DebuggerScripts { diff --git a/src/bootstrap/src/utils/proc_macro_deps.rs b/src/bootstrap/src/utils/proc_macro_deps.rs index 777c8601aa1d..db2369097d6c 100644 --- a/src/bootstrap/src/utils/proc_macro_deps.rs +++ b/src/bootstrap/src/utils/proc_macro_deps.rs @@ -43,6 +43,7 @@ pub static CRATES: &[&str] = &[ "rustc-hash", "self_cell", "serde", + "serde_derive_internals", "sha2", "smallvec", "stable_deref_trait", diff --git a/src/doc/rustc/src/targets/custom.md b/src/doc/rustc/src/targets/custom.md index 6c1494186a4e..e1750e27f0b7 100644 --- a/src/doc/rustc/src/targets/custom.md +++ b/src/doc/rustc/src/targets/custom.md @@ -16,6 +16,21 @@ rustc +nightly -Z unstable-options --target=wasm32-unknown-unknown --print targe To use a custom target, see the (unstable) [`build-std` feature](../../cargo/reference/unstable.html#build-std) of `cargo`. +
+ +The target JSON properties are not stable and subject to change. +Always pin your compiler version when using custom targets! + +
+ +## JSON Schema + +`rustc` provides a JSON schema for the custom target JSON specification. +Because the schema is subject to change, you should always use the schema from the version of rustc which you are passing the target to. + +It can be found in `etc/target-spec-json-schema.json` in the sysroot (`rustc --print sysroot`) or printed with `rustc +nightly -Zunstable-options --print target-spec-json-schema`. +The existence and name of this schema is, just like the properties of the JSON specification, not stable and subject to change. + ## Custom Target Lookup Path When `rustc` is given an option `--target=TARGET` (where `TARGET` is any string), it uses the following logic: diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 60347b2ea645..a98047614007 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -267,6 +267,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "digest", "displaydoc", "dissimilar", + "dyn-clone", "either", "elsa", "ena", @@ -346,6 +347,8 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "rand_xorshift", // dependency for doc-tests in rustc_thread_pool "rand_xoshiro", "redox_syscall", + "ref-cast", + "ref-cast-impl", "regex", "regex-automata", "regex-syntax", @@ -357,11 +360,14 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "rustix", "ruzstd", // via object in thorin-dwp "ryu", + "schemars", + "schemars_derive", "scoped-tls", "scopeguard", "self_cell", "serde", "serde_derive", + "serde_derive_internals", "serde_json", "serde_path_to_error", "sha1", diff --git a/tests/run-make/print-request-help-stable-unstable/help-diff.diff b/tests/run-make/print-request-help-stable-unstable/help-diff.diff index 07eafca32710..044302a19a01 100644 --- a/tests/run-make/print-request-help-stable-unstable/help-diff.diff +++ b/tests/run-make/print-request-help-stable-unstable/help-diff.diff @@ -2,6 +2,6 @@ error: unknown print request: `xxx` | - = help: valid print requests are: `calling-conventions`, `cfg`, `code-models`, `crate-name`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `tls-models` -+ = help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `tls-models` ++ = help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models` = help: for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information diff --git a/tests/run-make/print-request-help-stable-unstable/unstable-invalid-print-request-help.err b/tests/run-make/print-request-help-stable-unstable/unstable-invalid-print-request-help.err index 50ef340e3dd0..cc6c3c909b36 100644 --- a/tests/run-make/print-request-help-stable-unstable/unstable-invalid-print-request-help.err +++ b/tests/run-make/print-request-help-stable-unstable/unstable-invalid-print-request-help.err @@ -1,5 +1,5 @@ error: unknown print request: `xxx` | - = help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `tls-models` + = help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models` = help: for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information diff --git a/tests/run-make/rustc-help/help-v.stdout b/tests/run-make/rustc-help/help-v.stdout index 3fc297fb08e7..cd161c51ee3b 100644 --- a/tests/run-make/rustc-help/help-v.stdout +++ b/tests/run-make/rustc-help/help-v.stdout @@ -43,7 +43,7 @@ Options: --print [=] Compiler information to print on stdout (or to a file) INFO may be one of - . + . -g Equivalent to -C debuginfo=2 -O Equivalent to -C opt-level=3 -o Write output to FILENAME diff --git a/tests/run-make/rustc-help/help.stdout b/tests/run-make/rustc-help/help.stdout index caffe28f4989..74ec083bdee7 100644 --- a/tests/run-make/rustc-help/help.stdout +++ b/tests/run-make/rustc-help/help.stdout @@ -43,7 +43,7 @@ Options: --print [=] Compiler information to print on stdout (or to a file) INFO may be one of - . + . -g Equivalent to -C debuginfo=2 -O Equivalent to -C opt-level=3 -o Write output to FILENAME diff --git a/tests/ui/invalid-compile-flags/print-without-arg.stderr b/tests/ui/invalid-compile-flags/print-without-arg.stderr index 3048a59d0d00..4163d4e06022 100644 --- a/tests/ui/invalid-compile-flags/print-without-arg.stderr +++ b/tests/ui/invalid-compile-flags/print-without-arg.stderr @@ -3,5 +3,5 @@ error: Argument to option 'print' missing --print [=] Compiler information to print on stdout (or to a file) INFO may be one of - . + . diff --git a/tests/ui/invalid-compile-flags/print.stderr b/tests/ui/invalid-compile-flags/print.stderr index e3374eb1e6e7..e8adbfd87d76 100644 --- a/tests/ui/invalid-compile-flags/print.stderr +++ b/tests/ui/invalid-compile-flags/print.stderr @@ -1,5 +1,5 @@ error: unknown print request: `yyyy` | - = help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `tls-models` + = help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models` = help: for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information diff --git a/tests/ui/print-request/print-lints-help.stderr b/tests/ui/print-request/print-lints-help.stderr index bc48b2fa73cc..297a3aa79e1f 100644 --- a/tests/ui/print-request/print-lints-help.stderr +++ b/tests/ui/print-request/print-lints-help.stderr @@ -1,6 +1,6 @@ error: unknown print request: `lints` | - = help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `tls-models` + = help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models` = help: use `-Whelp` to print a list of lints = help: for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information From 79643ad48a9cd72066d2683fb7744380bb286074 Mon Sep 17 00:00:00 2001 From: Christian Poveda Date: Fri, 12 Sep 2025 13:54:11 -0500 Subject: [PATCH 444/682] Improve `core::hash` coverage --- library/coretests/tests/hash/mod.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/library/coretests/tests/hash/mod.rs b/library/coretests/tests/hash/mod.rs index 1f10a4733b05..043a3ee74233 100644 --- a/library/coretests/tests/hash/mod.rs +++ b/library/coretests/tests/hash/mod.rs @@ -53,12 +53,14 @@ fn test_writer_hasher() { assert_eq!(hash(&5_u16), 5); assert_eq!(hash(&5_u32), 5); assert_eq!(hash(&5_u64), 5); + assert_eq!(hash(&5_u128), 5); assert_eq!(hash(&5_usize), 5); assert_eq!(hash(&5_i8), 5); assert_eq!(hash(&5_i16), 5); assert_eq!(hash(&5_i32), 5); assert_eq!(hash(&5_i64), 5); + assert_eq!(hash(&5_i128), 5); assert_eq!(hash(&5_isize), 5); assert_eq!(hash(&false), 0); @@ -85,6 +87,17 @@ fn test_writer_hasher() { let ptr = ptr::without_provenance_mut::(5_usize); assert_eq!(hash(&ptr), 5); + // Use a newtype to test the `Hash::hash_slice` default implementation. + struct Byte(u8); + + impl Hash for Byte { + fn hash(&self, state: &mut H) { + state.write_u8(self.0) + } + } + + assert_eq!(hash(&[Byte(b'a')]), 97 + 1); + if cfg!(miri) { // Miri cannot hash pointers return; From 4383a4f73b0de8dd1d22ab5d3677f89a9d8a82b4 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Fri, 12 Sep 2025 20:26:41 +0100 Subject: [PATCH 445/682] Remove bullet points from the target maintainer list for the arm bare-metal targets --- src/doc/rustc/src/platform-support/aarch64-unknown-none.md | 2 +- src/doc/rustc/src/platform-support/armebv7r-none-eabi.md | 2 +- src/doc/rustc/src/platform-support/armv7a-none-eabi.md | 2 +- src/doc/rustc/src/platform-support/armv7r-none-eabi.md | 4 ++-- src/doc/rustc/src/platform-support/armv8r-none-eabihf.md | 4 ++-- src/doc/rustc/src/platform-support/thumbv6m-none-eabi.md | 2 +- src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md | 2 +- src/doc/rustc/src/platform-support/thumbv7m-none-eabi.md | 2 +- src/doc/rustc/src/platform-support/thumbv8m.base-none-eabi.md | 2 +- src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md | 2 +- 10 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/doc/rustc/src/platform-support/aarch64-unknown-none.md b/src/doc/rustc/src/platform-support/aarch64-unknown-none.md index bcdf4271b127..cc3a24ba0406 100644 --- a/src/doc/rustc/src/platform-support/aarch64-unknown-none.md +++ b/src/doc/rustc/src/platform-support/aarch64-unknown-none.md @@ -13,7 +13,7 @@ Processors in this family include the [Arm Cortex-A35, 53, 76, etc][aarch64-cpus ## Target maintainers -* [Rust Embedded Devices Working Group Arm Team] +[Rust Embedded Devices Working Group Arm Team] [Rust Embedded Devices Working Group Arm Team]: https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team diff --git a/src/doc/rustc/src/platform-support/armebv7r-none-eabi.md b/src/doc/rustc/src/platform-support/armebv7r-none-eabi.md index a5efd4adb973..d5f828110f16 100644 --- a/src/doc/rustc/src/platform-support/armebv7r-none-eabi.md +++ b/src/doc/rustc/src/platform-support/armebv7r-none-eabi.md @@ -19,7 +19,7 @@ See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all ## Target maintainers -* [@chrisnc](https://github.com/chrisnc) +[@chrisnc](https://github.com/chrisnc) ## Requirements diff --git a/src/doc/rustc/src/platform-support/armv7a-none-eabi.md b/src/doc/rustc/src/platform-support/armv7a-none-eabi.md index 77c463697bf6..0646e7634d7a 100644 --- a/src/doc/rustc/src/platform-support/armv7a-none-eabi.md +++ b/src/doc/rustc/src/platform-support/armv7a-none-eabi.md @@ -18,7 +18,7 @@ See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all ## Target maintainers -* [Rust Embedded Devices Working Group Arm Team] +[Rust Embedded Devices Working Group Arm Team] [Rust Embedded Devices Working Group Arm Team]: https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team diff --git a/src/doc/rustc/src/platform-support/armv7r-none-eabi.md b/src/doc/rustc/src/platform-support/armv7r-none-eabi.md index 36980a36b19a..9cda9c502543 100644 --- a/src/doc/rustc/src/platform-support/armv7r-none-eabi.md +++ b/src/doc/rustc/src/platform-support/armv7r-none-eabi.md @@ -14,8 +14,8 @@ See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all ## Target maintainers -* [@chrisnc](https://github.com/chrisnc) -* [Rust Embedded Devices Working Group Arm Team] +[@chrisnc](https://github.com/chrisnc) +[Rust Embedded Devices Working Group Arm Team] [Rust Embedded Devices Working Group Arm Team]: https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team diff --git a/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md b/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md index ed0102f48e08..eb7f8921ad0e 100644 --- a/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md +++ b/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md @@ -16,8 +16,8 @@ See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all ## Target maintainers -* [@chrisnc](https://github.com/chrisnc) -* [Rust Embedded Devices Working Group Arm Team] +[@chrisnc](https://github.com/chrisnc) +[Rust Embedded Devices Working Group Arm Team] [Rust Embedded Devices Working Group Arm Team]: https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team diff --git a/src/doc/rustc/src/platform-support/thumbv6m-none-eabi.md b/src/doc/rustc/src/platform-support/thumbv6m-none-eabi.md index 746b84435479..ca47d96cf65f 100644 --- a/src/doc/rustc/src/platform-support/thumbv6m-none-eabi.md +++ b/src/doc/rustc/src/platform-support/thumbv6m-none-eabi.md @@ -26,7 +26,7 @@ only option because there is no FPU support in [Armv6-M]. ## Target maintainers -* [Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team) +[Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team) ## Target CPU and Target Feature options diff --git a/src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md b/src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md index 12e28265678c..9de708cda242 100644 --- a/src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md +++ b/src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md @@ -21,7 +21,7 @@ See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all ## Target maintainers -* [Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team) +[Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team) ## Target CPU and Target Feature options diff --git a/src/doc/rustc/src/platform-support/thumbv7m-none-eabi.md b/src/doc/rustc/src/platform-support/thumbv7m-none-eabi.md index 03324b341d07..05c9146e6d06 100644 --- a/src/doc/rustc/src/platform-support/thumbv7m-none-eabi.md +++ b/src/doc/rustc/src/platform-support/thumbv7m-none-eabi.md @@ -22,7 +22,7 @@ only option because there is no FPU support in [Armv7-M]. ## Target maintainers -* [Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team) +[Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team) ## Target CPU and Target Feature options diff --git a/src/doc/rustc/src/platform-support/thumbv8m.base-none-eabi.md b/src/doc/rustc/src/platform-support/thumbv8m.base-none-eabi.md index 4a92e856466c..92585f49bd85 100644 --- a/src/doc/rustc/src/platform-support/thumbv8m.base-none-eabi.md +++ b/src/doc/rustc/src/platform-support/thumbv8m.base-none-eabi.md @@ -22,7 +22,7 @@ only option because there is no FPU support in [Armv8-M] Baseline. ## Target maintainers -* [Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team) +[Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team) ## Target CPU and Target Feature options diff --git a/src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md b/src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md index 9f85d08fa0ac..960cf31567d1 100644 --- a/src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md +++ b/src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md @@ -25,7 +25,7 @@ See [`arm-none-eabi`](arm-none-eabi.md) for information applicable to all ## Target maintainers -* [Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team) +[Rust Embedded Devices Working Group Arm Team](https://github.com/rust-embedded/wg?tab=readme-ov-file#the-arm-team) ## Target CPU and Target Feature options From afe8f81c73244bcc33be39c9ea0f90f090ac14ce Mon Sep 17 00:00:00 2001 From: Christian Poveda Date: Fri, 12 Sep 2025 14:35:20 -0500 Subject: [PATCH 446/682] Improve `core::num` coverage --- library/coretests/tests/num/mod.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/library/coretests/tests/num/mod.rs b/library/coretests/tests/num/mod.rs index 54e54f734f64..913f766ec168 100644 --- a/library/coretests/tests/num/mod.rs +++ b/library/coretests/tests/num/mod.rs @@ -111,6 +111,13 @@ fn from_str_issue7588() { assert_eq!(s, None); } +#[test] +#[should_panic = "radix must lie in the range `[2, 36]`"] +fn from_ascii_radix_panic() { + let radix = 1; + let _parsed = u64::from_str_radix("12345ABCD", radix); +} + #[test] fn test_int_from_str_overflow() { test_parse::("127", Ok(127)); From ea661e9353e5dfa2c37f45ad44e52d97d5006447 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Fri, 12 Sep 2025 20:38:00 +0100 Subject: [PATCH 447/682] Re-order and re-work the aarch64-unknown-none platform support page. Removes a bunch of information that isn't, strictly speaking, target specific. --- .../platform-support/aarch64-unknown-none.md | 77 ++++--------------- 1 file changed, 17 insertions(+), 60 deletions(-) diff --git a/src/doc/rustc/src/platform-support/aarch64-unknown-none.md b/src/doc/rustc/src/platform-support/aarch64-unknown-none.md index cc3a24ba0406..25d99e4f0dea 100644 --- a/src/doc/rustc/src/platform-support/aarch64-unknown-none.md +++ b/src/doc/rustc/src/platform-support/aarch64-unknown-none.md @@ -19,44 +19,19 @@ Processors in this family include the [Arm Cortex-A35, 53, 76, etc][aarch64-cpus ## Target CPU and Target Feature options -It is possible to tell Rust (or LLVM) that you have a specific model of Arm -processor, using the [`-Ctarget-cpu`][target-cpu] option. You can also control -whether Rust (or LLVM) will include instructions that target optional hardware -features, e.g. hardware floating-point, or Advanced SIMD operations, using -[`-Ctarget-feature`][target-feature]. +All AArch64 processors include an FPU. The difference between the `-none` and +`-none-softfloat` targets is whether the FPU is used for passing function arguments. +You may prefer the `-softfloat` target when writing a kernel or interfacing with +pre-compiled binaries that use the soft-float ABI. -It is important to note that selecting a *target-cpu* will typically enable -*all* the optional features available from Arm on that model of CPU and your -particular implementation of that CPU may not have those features available. -In that case, you can use `-Ctarget-feature=-option` to turn off the specific -CPU features you do not have available, leaving you with the optimized -instruction scheduling and support for the features you do have. More details -are available in the detailed target-specific documentation. - -
- -Many target-features are currently unstable and subject to change, and -if you use them you should disassemble the compiler output and manually inspect -it to ensure only appropriate instructions for your CPU have been generated. - -
- -If you wish to use the *target-cpu* and *target-feature* options, you can add -them to your `.cargo/config.toml` file alongside any other flags your project -uses (likely linker related ones): - -```toml -rustflags = [ - # Usual Arm bare-metal linker setup - "-Clink-arg=-Tlink.x", - "-Clink-arg=--nmagic", - # tell Rust we have a Cortex-A72 - "-Ctarget-cpu=cortex-a72", -] - -[build] -target = "aarch64-unknown-none" -``` +When using the hardfloat targets, the minimum floating-point features assumed +are those of the `fp-armv8`, which excludes NEON SIMD support. If your +processor supports a different set of floating-point features than the default +expectations of `fp-armv8`, then these should also be enabled or disabled as +needed with `-C target-feature=(+/-)`. It is also possible to tell Rust (or +LLVM) that you have a specific model of Arm processor, using the +[`-Ctarget-cpu`][target-cpu] option. Doing so may change the default set of +target-features enabled. [target-cpu]: https://doc.rust-lang.org/rustc/codegen-options/index.html#target-cpu [target-feature]: https://doc.rust-lang.org/rustc/codegen-options/index.html#target-feature @@ -72,10 +47,10 @@ package manager. To use it, add the following to your `.cargo/config.toml`: ```toml [target.aarch64-unknown-none] -linker = "aarch64-none-ld" +linker = "aarch64-none-elf-ld" ``` -The GNU linker can also be used by specifying `aarch64-none-gcc` as the +The GNU linker can also be used by specifying `aarch64-none-elf-gcc` as the linker. This is needed when using GCC's link time optimization. These targets don't provide a linker script, so you'll need to bring your own @@ -83,25 +58,12 @@ according to the specific device you are using. Pass `-Clink-arg=-Tyour_script.ld` as a rustc argument to make the linker use `your_script.ld` during linking. -All AArch64 processors include an FPU. The difference between the `-none` and -`-none-softfloat` targets is whether the FPU is used for passing function arguments. -You may prefer the `-softfloat` target when writing a kernel or interfacing with -pre-compiled binaries that use the soft-float ABI. - -When using the hardfloat targets, the minimum floating-point features assumed -are those of the `fp-armv8`, which excludes NEON SIMD support. If your -processor supports a different set of floating-point features than the default -expectations of `fp-armv8`, then these should also be enabled or disabled as -needed with `-C target-feature=(+/-)`. - [arm-gnu-toolchain]: https://developer.arm.com/Tools%20and%20Software/GNU%20Toolchain -## Testing +## Cross-compilation toolchains and C code -This is a cross-compiled target that you will need to emulate during testing. - -The exact emulator that you'll need depends on the specific device you want to -run your code on. +This target supports C code compiled with the `aarch64-none-elf` target +triple and a suitable `-march` or `-mcpu` flag. ## Start-up and Low-Level Code @@ -113,8 +75,3 @@ The *TrustedFirmware* group also maintain [Rust crates for this target](https://github.com/ArmFirmwareCrates). [`aarch64-cpu`]: https://docs.rs/aarch64-cpu - -## Cross-compilation toolchains and C code - -This target supports C code compiled with the `aarch64-unknown-none` target -triple and a suitable `-march` or `-mcpu` flag. From 87ae4dbf5cc1e81db845c659583aa489d532ae8c Mon Sep 17 00:00:00 2001 From: Christian Poveda Date: Fri, 12 Sep 2025 14:41:26 -0500 Subject: [PATCH 448/682] Improve `core::ptr` coverage --- library/coretests/tests/ptr.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/library/coretests/tests/ptr.rs b/library/coretests/tests/ptr.rs index f8774833d0a5..4d5138d539b9 100644 --- a/library/coretests/tests/ptr.rs +++ b/library/coretests/tests/ptr.rs @@ -489,6 +489,14 @@ fn is_aligned() { assert_ne!(ptr.is_aligned_to(8), ptr.wrapping_add(1).is_aligned_to(8)); } +#[test] +#[should_panic = "is_aligned_to: align is not a power-of-two"] +fn invalid_is_aligned() { + let data = 42; + let ptr: *const i32 = &data; + assert!(ptr.is_aligned_to(3)); +} + #[test] fn offset_from() { let mut a = [0; 5]; From 16c218c57ff83c82ba58753c3a67f697e260adc1 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 15 Aug 2025 16:12:35 -0500 Subject: [PATCH 449/682] Introduce trait_item_of --- .../src/diagnostics/mutability_errors.rs | 2 +- compiler/rustc_codegen_ssa/src/codegen_attrs.rs | 16 ++++------------ .../src/collect/predicates_of.rs | 5 ++--- .../rustc_hir_analysis/src/collect/type_of.rs | 4 ++-- compiler/rustc_middle/src/ty/mod.rs | 9 +++++++++ compiler/rustc_passes/src/dead.rs | 12 +++++------- .../src/cfi/typeid/itanium_cxx_abi/transform.rs | 3 +-- compiler/rustc_ty_utils/src/implied_bounds.rs | 2 +- .../src/functions/renamed_function_params.rs | 10 ++-------- .../clippy_lints/src/only_used_in_recursion.rs | 2 +- src/tools/clippy/clippy_lints/src/use_self.rs | 3 +-- 11 files changed, 29 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index ea264c8064ad..6d69040c711d 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -716,7 +716,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { return (false, false, None); }; - let implemented_trait_item = self.infcx.tcx.associated_item(my_def).trait_item_def_id; + let implemented_trait_item = self.infcx.tcx.trait_item_of(my_def); ( true, diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 008340e614de..dc500c363f4a 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -562,15 +562,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { codegen_fn_attrs } -/// If the provided DefId is a method in a trait impl, return the DefId of the method prototype. -fn opt_trait_item(tcx: TyCtxt<'_>, def_id: DefId) -> Option { - let impl_item = tcx.opt_associated_item(def_id)?; - match impl_item.container { - ty::AssocItemContainer::Impl => impl_item.trait_item_def_id, - _ => None, - } -} - fn disabled_sanitizers_for(tcx: TyCtxt<'_>, did: LocalDefId) -> SanitizerSet { // Backtrack to the crate root. let mut disabled = match tcx.opt_local_parent(did) { @@ -600,14 +591,15 @@ fn disabled_sanitizers_for(tcx: TyCtxt<'_>, did: LocalDefId) -> SanitizerSet { /// Checks if the provided DefId is a method in a trait impl for a trait which has track_caller /// applied to the method prototype. fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - let Some(trait_item) = opt_trait_item(tcx, def_id) else { return false }; - tcx.codegen_fn_attrs(trait_item).flags.intersects(CodegenFnAttrFlags::TRACK_CALLER) + tcx.trait_item_of(def_id).is_some_and(|id| { + tcx.codegen_fn_attrs(id).flags.intersects(CodegenFnAttrFlags::TRACK_CALLER) + }) } /// If the provided DefId is a method in a trait impl, return the value of the `#[align]` /// attribute on the method prototype (if any). fn inherited_align<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option { - tcx.codegen_fn_attrs(opt_trait_item(tcx, def_id)?).alignment + tcx.codegen_fn_attrs(tcx.trait_item_of(def_id)?).alignment } /// We now check the #\[rustc_autodiff\] attributes which we generated from the #[autodiff(...)] diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index 126ffabd448c..dd3590f9ac5d 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -111,9 +111,8 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen } Some(ImplTraitInTraitData::Impl { fn_def_id }) => { - let assoc_item = tcx.associated_item(def_id); - let trait_assoc_predicates = - tcx.explicit_predicates_of(assoc_item.trait_item_def_id.unwrap()); + let trait_item_def_id = tcx.trait_item_of(def_id).unwrap(); + let trait_assoc_predicates = tcx.explicit_predicates_of(trait_item_def_id); let impl_assoc_identity_args = ty::GenericArgs::identity_for_item(tcx, def_id); let impl_def_id = tcx.parent(fn_def_id); diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 62125c99d802..a8af73717cd0 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -125,8 +125,8 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_ Some(ty::ImplTraitInTraitData::Impl { fn_def_id }) => { match tcx.collect_return_position_impl_trait_in_trait_tys(fn_def_id) { Ok(map) => { - let assoc_item = tcx.associated_item(def_id); - return map[&assoc_item.trait_item_def_id.unwrap()]; + let trait_item_def_id = tcx.trait_item_of(def_id).unwrap(); + return map[&trait_item_def_id]; } Err(_) => { return ty::EarlyBinder::bind(Ty::new_error_with_message( diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index da17ec1f9f37..5a4ebfe28a4b 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1934,6 +1934,15 @@ impl<'tcx> TyCtxt<'tcx> { Some((parent, def_kind)) } + /// Returns the trait item that is implemented by the given item `DefId`. + pub fn trait_item_of(self, def_id: impl IntoQueryParam) -> Option { + let assoc = self.opt_associated_item(def_id.into_query_param())?; + if assoc.container != AssocItemContainer::Impl { + return None; + } + assoc.trait_item_def_id + } + /// If the given `DefId` is an associated item of a trait, /// returns the `DefId` of the trait; otherwise, returns `None`. pub fn trait_of_assoc(self, def_id: DefId) -> Option { diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index fc33405d455b..f36a7a5b184a 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -484,13 +484,11 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { fn check_impl_or_impl_item_live(&mut self, local_def_id: LocalDefId) -> bool { let (impl_block_id, trait_def_id) = match self.tcx.def_kind(local_def_id) { // assoc impl items of traits are live if the corresponding trait items are live - DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn => ( - self.tcx.local_parent(local_def_id), - self.tcx - .associated_item(local_def_id) - .trait_item_def_id - .and_then(|def_id| def_id.as_local()), - ), + DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn => { + let trait_item_id = + self.tcx.trait_item_of(local_def_id).and_then(|def_id| def_id.as_local()); + (self.tcx.local_parent(local_def_id), trait_item_id) + } // impl items are live if the corresponding traits are live DefKind::Impl { of_trait: true } => ( local_def_id, diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs index d81fa062e010..577a16a0d257 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs @@ -469,8 +469,7 @@ fn implemented_method<'tcx>( let ancestor = if let Some(impl_id) = tcx.impl_of_assoc(instance.def_id()) { // Implementation in an `impl` block trait_ref = tcx.impl_trait_ref(impl_id)?; - let impl_method = tcx.associated_item(instance.def_id()); - method_id = impl_method.trait_item_def_id?; + method_id = tcx.trait_item_of(instance.def_id())?; trait_method = tcx.associated_item(method_id); trait_id = trait_ref.skip_binder().def_id; impl_id diff --git a/compiler/rustc_ty_utils/src/implied_bounds.rs b/compiler/rustc_ty_utils/src/implied_bounds.rs index cdfb93c4e7d5..543f6a3ccf79 100644 --- a/compiler/rustc_ty_utils/src/implied_bounds.rs +++ b/compiler/rustc_ty_utils/src/implied_bounds.rs @@ -107,7 +107,7 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<' // the assumed wf types of the trait's RPITIT GAT. ty::ImplTraitInTraitData::Impl { .. } => { let impl_def_id = tcx.local_parent(def_id); - let rpitit_def_id = tcx.associated_item(def_id).trait_item_def_id.unwrap(); + let rpitit_def_id = tcx.trait_item_of(def_id).unwrap(); let args = ty::GenericArgs::identity_for_item(tcx, def_id).rebase_onto( tcx, impl_def_id.to_def_id(), diff --git a/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs b/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs index f8e8f5544b99..e25611d48817 100644 --- a/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs +++ b/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::{Applicability, MultiSpan}; -use rustc_hir::def_id::{DefId, DefIdSet}; -use rustc_hir::hir_id::OwnerId; +use rustc_hir::def_id::DefIdSet; use rustc_hir::{Impl, ImplItem, ImplItemKind, ItemKind, Node, TraitRef}; use rustc_lint::LateContext; use rustc_span::Span; @@ -19,7 +18,7 @@ pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &ImplItem<'_>, ignored of_trait: Some(of_trait), .. }) = &parent_item.kind - && let Some(did) = trait_item_def_id_of_impl(cx, item.owner_id) + && let Some(did) = cx.tcx.trait_item_of(item.owner_id) && !is_from_ignored_trait(&of_trait.trait_ref, ignored_traits) { let mut param_idents_iter = cx.tcx.hir_body_param_idents(body_id); @@ -87,11 +86,6 @@ impl RenamedFnArgs { } } -/// Get the [`trait_item_def_id`](ImplItemRef::trait_item_def_id) of a relevant impl item. -fn trait_item_def_id_of_impl(cx: &LateContext<'_>, target: OwnerId) -> Option { - cx.tcx.associated_item(target).trait_item_def_id -} - fn is_from_ignored_trait(of_trait: &TraitRef<'_>, ignored_traits: &DefIdSet) -> bool { of_trait .trait_def_id() diff --git a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs index 809a6728e128..c4cad592e367 100644 --- a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs +++ b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs @@ -248,7 +248,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { .tcx .impl_trait_ref(item.owner_id) .map(EarlyBinder::instantiate_identity) - && let Some(trait_item_id) = cx.tcx.associated_item(owner_id).trait_item_def_id + && let Some(trait_item_id) = cx.tcx.trait_item_of(owner_id) { ( trait_item_id, diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs index aeda864b7eb5..8252e6d48694 100644 --- a/src/tools/clippy/clippy_lints/src/use_self.rs +++ b/src/tools/clippy/clippy_lints/src/use_self.rs @@ -151,8 +151,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { // trait, not in the impl of the trait. let trait_method = cx .tcx - .associated_item(impl_item.owner_id) - .trait_item_def_id + .trait_item_of(impl_item.owner_id) .expect("impl method matches a trait method"); let trait_method_sig = cx.tcx.fn_sig(trait_method).instantiate_identity(); let trait_method_sig = cx.tcx.instantiate_bound_regions_with_erased(trait_method_sig); From 8d5078e0490f4a07afc86af60fd78f1f6dabdb4b Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 15 Aug 2025 16:12:35 -0500 Subject: [PATCH 450/682] Introduce trait_item_of --- clippy_lints/src/functions/renamed_function_params.rs | 10 ++-------- clippy_lints/src/only_used_in_recursion.rs | 2 +- clippy_lints/src/use_self.rs | 3 +-- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/functions/renamed_function_params.rs b/clippy_lints/src/functions/renamed_function_params.rs index f8e8f5544b99..e25611d48817 100644 --- a/clippy_lints/src/functions/renamed_function_params.rs +++ b/clippy_lints/src/functions/renamed_function_params.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::{Applicability, MultiSpan}; -use rustc_hir::def_id::{DefId, DefIdSet}; -use rustc_hir::hir_id::OwnerId; +use rustc_hir::def_id::DefIdSet; use rustc_hir::{Impl, ImplItem, ImplItemKind, ItemKind, Node, TraitRef}; use rustc_lint::LateContext; use rustc_span::Span; @@ -19,7 +18,7 @@ pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &ImplItem<'_>, ignored of_trait: Some(of_trait), .. }) = &parent_item.kind - && let Some(did) = trait_item_def_id_of_impl(cx, item.owner_id) + && let Some(did) = cx.tcx.trait_item_of(item.owner_id) && !is_from_ignored_trait(&of_trait.trait_ref, ignored_traits) { let mut param_idents_iter = cx.tcx.hir_body_param_idents(body_id); @@ -87,11 +86,6 @@ impl RenamedFnArgs { } } -/// Get the [`trait_item_def_id`](ImplItemRef::trait_item_def_id) of a relevant impl item. -fn trait_item_def_id_of_impl(cx: &LateContext<'_>, target: OwnerId) -> Option { - cx.tcx.associated_item(target).trait_item_def_id -} - fn is_from_ignored_trait(of_trait: &TraitRef<'_>, ignored_traits: &DefIdSet) -> bool { of_trait .trait_def_id() diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index 809a6728e128..c4cad592e367 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -248,7 +248,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { .tcx .impl_trait_ref(item.owner_id) .map(EarlyBinder::instantiate_identity) - && let Some(trait_item_id) = cx.tcx.associated_item(owner_id).trait_item_def_id + && let Some(trait_item_id) = cx.tcx.trait_item_of(owner_id) { ( trait_item_id, diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index aeda864b7eb5..8252e6d48694 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -151,8 +151,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { // trait, not in the impl of the trait. let trait_method = cx .tcx - .associated_item(impl_item.owner_id) - .trait_item_def_id + .trait_item_of(impl_item.owner_id) .expect("impl method matches a trait method"); let trait_method_sig = cx.tcx.fn_sig(trait_method).instantiate_identity(); let trait_method_sig = cx.tcx.instantiate_bound_regions_with_erased(trait_method_sig); From 5590e55b03722fb7da2d965478deb4b5d62bf97a Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 13 Aug 2025 14:14:37 -0500 Subject: [PATCH 451/682] Rename AssocItemContainer -> AssocContainer --- .../rustc_hir_analysis/src/check/check.rs | 12 +++++------ .../src/check/compare_impl_item.rs | 8 ++++---- .../rustc_hir_analysis/src/check/wfcheck.rs | 8 +++----- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 4 ++-- .../src/fn_ctxt/suggestions.rs | 2 +- compiler/rustc_hir_typeck/src/lib.rs | 2 +- compiler/rustc_hir_typeck/src/method/probe.rs | 8 ++++---- compiler/rustc_lint/src/nonstandard_style.rs | 4 ++-- compiler/rustc_metadata/src/rmeta/encoder.rs | 10 +++++----- compiler/rustc_metadata/src/rmeta/mod.rs | 2 +- .../rustc_metadata/src/rmeta/parameterized.rs | 2 +- compiler/rustc_metadata/src/rmeta/table.rs | 2 +- compiler/rustc_middle/src/query/erase.rs | 2 +- compiler/rustc_middle/src/ty/assoc.rs | 18 ++++++++--------- compiler/rustc_middle/src/ty/instance.rs | 2 +- compiler/rustc_middle/src/ty/mod.rs | 4 ++-- compiler/rustc_public/src/ty.rs | 4 ++-- .../src/unstable/convert/stable/ty.rs | 10 +++++----- .../error_reporting/infer/note_and_explain.rs | 4 ++-- .../src/error_reporting/traits/call_kind.rs | 6 +++--- .../traits/specialize/specialization_graph.rs | 2 +- compiler/rustc_ty_utils/src/assoc.rs | 8 ++++---- compiler/rustc_ty_utils/src/ty.rs | 2 +- src/librustdoc/clean/mod.rs | 20 +++++++++---------- src/librustdoc/clean/types.rs | 8 ++++---- .../clippy/clippy_lints/src/missing_inline.rs | 6 +++--- src/tools/clippy/clippy_utils/src/lib.rs | 4 ++-- 27 files changed, 81 insertions(+), 83 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 08b344638dd5..3887c96fce63 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1009,8 +1009,8 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), res = res.and(check_associated_item(tcx, def_id)); let assoc_item = tcx.associated_item(def_id); match assoc_item.container { - ty::AssocItemContainer::Impl => {} - ty::AssocItemContainer::Trait => { + ty::AssocContainer::Impl => {} + ty::AssocContainer::Trait => { res = res.and(check_trait_item(tcx, def_id)); } } @@ -1026,8 +1026,8 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), res = res.and(check_associated_item(tcx, def_id)); let assoc_item = tcx.associated_item(def_id); match assoc_item.container { - ty::AssocItemContainer::Impl => {} - ty::AssocItemContainer::Trait => { + ty::AssocContainer::Impl => {} + ty::AssocContainer::Trait => { res = res.and(check_trait_item(tcx, def_id)); } } @@ -1043,8 +1043,8 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), let assoc_item = tcx.associated_item(def_id); let has_type = match assoc_item.container { - ty::AssocItemContainer::Impl => true, - ty::AssocItemContainer::Trait => { + ty::AssocContainer::Impl => true, + ty::AssocContainer::Trait => { tcx.ensure_ok().explicit_item_bounds(def_id); tcx.ensure_ok().explicit_item_self_bounds(def_id); if tcx.is_conditionally_const(def_id) { diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index e48272561939..85cd8da2ca4d 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -1449,8 +1449,8 @@ fn compare_self_type<'tcx>( let self_string = |method: ty::AssocItem| { let untransformed_self_ty = match method.container { - ty::AssocItemContainer::Impl => impl_trait_ref.self_ty(), - ty::AssocItemContainer::Trait => tcx.types.self_param, + ty::AssocContainer::Impl => impl_trait_ref.self_ty(), + ty::AssocContainer::Trait => tcx.types.self_param, }; let self_arg_ty = tcx.fn_sig(method.def_id).instantiate_identity().input(0); let (infcx, param_env) = tcx @@ -2458,8 +2458,8 @@ fn param_env_with_gat_bounds<'tcx>( for impl_ty in impl_tys_to_install { let trait_ty = match impl_ty.container { - ty::AssocItemContainer::Trait => impl_ty, - ty::AssocItemContainer::Impl => tcx.associated_item(impl_ty.trait_item_def_id.unwrap()), + ty::AssocContainer::Trait => impl_ty, + ty::AssocContainer::Impl => tcx.associated_item(impl_ty.trait_item_def_id.unwrap()), }; let mut bound_vars: smallvec::SmallVec<[ty::BoundVariableKind; 8]> = diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 22a9446fd4cf..21dc29a81724 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -948,10 +948,8 @@ pub(crate) fn check_associated_item( .coherent_trait(tcx.parent(item.trait_item_def_id.unwrap_or(item_id.into())))?; let self_ty = match item.container { - ty::AssocItemContainer::Trait => tcx.types.self_param, - ty::AssocItemContainer::Impl => { - tcx.type_of(item.container_id(tcx)).instantiate_identity() - } + ty::AssocContainer::Trait => tcx.types.self_param, + ty::AssocContainer::Impl => tcx.type_of(item.container_id(tcx)).instantiate_identity(), }; let span = tcx.def_span(item_id); @@ -978,7 +976,7 @@ pub(crate) fn check_associated_item( check_method_receiver(wfcx, hir_sig, item, self_ty) } ty::AssocKind::Type { .. } => { - if let ty::AssocItemContainer::Trait = item.container { + if let ty::AssocContainer::Trait = item.container { check_associated_type_bounds(wfcx, item, span) } if item.defaultness(tcx).has_value() { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 0b3d50ff2199..7b485acc8bd1 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -1021,7 +1021,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let container_id = assoc_item.container_id(tcx); debug!(?def_id, ?container, ?container_id); match container { - ty::AssocItemContainer::Trait => { + ty::AssocContainer::Trait => { if let Err(e) = callee::check_legal_trait_for_method_call( tcx, path_span, @@ -1033,7 +1033,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.set_tainted_by_errors(e); } } - ty::AssocItemContainer::Impl => { + ty::AssocContainer::Impl => { if segments.len() == 1 { // `::assoc` will end up here, and so // can `T::assoc`. If this came from an diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index d7ddbcc8b53b..1998a1884b70 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1882,7 +1882,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if segment.ident.name == sym::clone && results.type_dependent_def_id(expr.hir_id).is_some_and(|did| { let assoc_item = self.tcx.associated_item(did); - assoc_item.container == ty::AssocItemContainer::Trait + assoc_item.container == ty::AssocContainer::Trait && assoc_item.container_id(self.tcx) == clone_trait_did }) // If that clone call hasn't already dereferenced the self type (i.e. don't give this diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 129de32fd4ad..8ba6c25c0778 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -278,7 +278,7 @@ fn infer_type_if_missing<'tcx>(fcx: &FnCtxt<'_, 'tcx>, node: Node<'tcx>) -> Opti { if let Some(item) = tcx.opt_associated_item(def_id.into()) && let ty::AssocKind::Const { .. } = item.kind - && let ty::AssocItemContainer::Impl = item.container + && let ty::AssocContainer::Impl = item.container && let Some(trait_item_def_id) = item.trait_item_def_id { let impl_def_id = item.container_id(tcx); diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 52a8eff984b9..a52ae354c533 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -18,8 +18,8 @@ use rustc_middle::middle::stability; use rustc_middle::ty::elaborate::supertrait_def_ids; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, simplify_type}; use rustc_middle::ty::{ - self, AssocItem, AssocItemContainer, GenericArgs, GenericArgsRef, GenericParamDefKind, - ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt, Upcast, + self, AssocContainer, AssocItem, GenericArgs, GenericArgsRef, GenericParamDefKind, ParamEnvAnd, + Ty, TyCtxt, TypeVisitableExt, Upcast, }; use rustc_middle::{bug, span_bug}; use rustc_session::lint; @@ -528,7 +528,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ProbeScope::Single(def_id) => { let item = self.tcx.associated_item(def_id); // FIXME(fn_delegation): Delegation to inherent methods is not yet supported. - assert_eq!(item.container, AssocItemContainer::Trait); + assert_eq!(item.container, AssocContainer::Trait); let trait_def_id = self.tcx.parent(def_id); let trait_span = self.tcx.def_span(trait_def_id); @@ -1702,7 +1702,7 @@ impl<'tcx> Pick<'tcx> { tcx.def_path_str(self.item.def_id), )); } - (ty::AssocKind::Const { name }, ty::AssocItemContainer::Trait) => { + (ty::AssocKind::Const { name }, ty::AssocContainer::Trait) => { let def_id = self.item.container_id(tcx); lint.span_suggestion( span, diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index 65075cfecfa4..7b8f17ef93ce 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -30,8 +30,8 @@ pub(crate) enum MethodLateContext { pub(crate) fn method_context(cx: &LateContext<'_>, id: LocalDefId) -> MethodLateContext { let item = cx.tcx.associated_item(id); match item.container { - ty::AssocItemContainer::Trait => MethodLateContext::TraitAutoImpl, - ty::AssocItemContainer::Impl => match cx.tcx.impl_trait_ref(item.container_id(cx.tcx)) { + ty::AssocContainer::Trait => MethodLateContext::TraitAutoImpl, + ty::AssocContainer::Impl => match cx.tcx.impl_trait_ref(item.container_id(cx.tcx)) { Some(_) => MethodLateContext::TraitImpl, None => MethodLateContext::PlainImpl, }, diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index a7e7e9985f4d..0fb5b8fffd18 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -22,7 +22,7 @@ use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::mir::interpret; use rustc_middle::query::Providers; use rustc_middle::traits::specialization_graph; -use rustc_middle::ty::AssocItemContainer; +use rustc_middle::ty::AssocContainer; use rustc_middle::ty::codec::TyEncoder; use rustc_middle::ty::fast_reject::{self, TreatParams}; use rustc_middle::{bug, span_bug}; @@ -1254,8 +1254,8 @@ fn should_encode_type(tcx: TyCtxt<'_>, def_id: LocalDefId, def_kind: DefKind) -> DefKind::AssocTy => { let assoc_item = tcx.associated_item(def_id); match assoc_item.container { - ty::AssocItemContainer::Impl => true, - ty::AssocItemContainer::Trait => assoc_item.defaultness(tcx).has_value(), + ty::AssocContainer::Impl => true, + ty::AssocContainer::Trait => assoc_item.defaultness(tcx).has_value(), } } DefKind::TyParam => { @@ -1729,7 +1729,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.tables.assoc_container.set_some(def_id.index, item.container); match item.container { - AssocItemContainer::Trait => { + AssocContainer::Trait => { if item.is_type() { self.encode_explicit_item_bounds(def_id); self.encode_explicit_item_self_bounds(def_id); @@ -1739,7 +1739,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } } } - AssocItemContainer::Impl => { + AssocContainer::Impl => { if let Some(trait_item_def_id) = item.trait_item_def_id { self.tables.trait_item_def_id.set_some(def_id.index, trait_item_def_id.into()); } diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 1f7d142d3300..a7b1f2c01dd1 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -459,7 +459,7 @@ define_tables! { def_keys: Table>, proc_macro_quoted_spans: Table>, variant_data: Table>, - assoc_container: Table, + assoc_container: Table, macro_definition: Table>, proc_macro: Table, deduced_param_attrs: Table>, diff --git a/compiler/rustc_metadata/src/rmeta/parameterized.rs b/compiler/rustc_metadata/src/rmeta/parameterized.rs index 34180001f804..4b2dc2c814e5 100644 --- a/compiler/rustc_metadata/src/rmeta/parameterized.rs +++ b/compiler/rustc_metadata/src/rmeta/parameterized.rs @@ -102,7 +102,7 @@ trivially_parameterized_over_tcx! { rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault, rustc_middle::mir::ConstQualifs, rustc_middle::ty::AnonConstKind, - rustc_middle::ty::AssocItemContainer, + rustc_middle::ty::AssocContainer, rustc_middle::ty::AsyncDestructor, rustc_middle::ty::Asyncness, rustc_middle::ty::DeducedParamAttrs, diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs index 2cb07a28a8ad..36634bb39d80 100644 --- a/compiler/rustc_metadata/src/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -222,7 +222,7 @@ fixed_size_enum! { } fixed_size_enum! { - ty::AssocItemContainer { + ty::AssocContainer { ( Trait ) ( Impl ) } diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index bea2191c5601..4c00b769237f 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -318,7 +318,7 @@ trivial! { rustc_middle::traits::WellFormedLoc, rustc_middle::ty::adjustment::CoerceUnsizedInfo, rustc_middle::ty::AssocItem, - rustc_middle::ty::AssocItemContainer, + rustc_middle::ty::AssocContainer, rustc_middle::ty::Asyncness, rustc_middle::ty::AsyncDestructor, rustc_middle::ty::BoundVariableKind, diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs index a902a8a61e5a..8a2f62ce8335 100644 --- a/compiler/rustc_middle/src/ty/assoc.rs +++ b/compiler/rustc_middle/src/ty/assoc.rs @@ -11,7 +11,7 @@ use super::{TyCtxt, Visibility}; use crate::ty; #[derive(Clone, Copy, PartialEq, Eq, Debug, HashStable, Hash, Encodable, Decodable)] -pub enum AssocItemContainer { +pub enum AssocContainer { Trait, Impl, } @@ -21,7 +21,7 @@ pub enum AssocItemContainer { pub struct AssocItem { pub def_id: DefId, pub kind: AssocKind, - pub container: AssocItemContainer, + pub container: AssocContainer, /// If this is an item in an impl of a trait then this is the `DefId` of /// the associated item on the trait that this implements. @@ -71,16 +71,16 @@ impl AssocItem { #[inline] pub fn trait_container(&self, tcx: TyCtxt<'_>) -> Option { match self.container { - AssocItemContainer::Impl => None, - AssocItemContainer::Trait => Some(tcx.parent(self.def_id)), + AssocContainer::Impl => None, + AssocContainer::Trait => Some(tcx.parent(self.def_id)), } } #[inline] pub fn impl_container(&self, tcx: TyCtxt<'_>) -> Option { match self.container { - AssocItemContainer::Impl => Some(tcx.parent(self.def_id)), - AssocItemContainer::Trait => None, + AssocContainer::Impl => Some(tcx.parent(self.def_id)), + AssocContainer::Trait => None, } } @@ -157,10 +157,10 @@ impl AssocItem { } let def_id = match (self.container, self.trait_item_def_id) { - (AssocItemContainer::Trait, _) => self.def_id, - (AssocItemContainer::Impl, Some(trait_item_did)) => trait_item_did, + (AssocContainer::Trait, _) => self.def_id, + (AssocContainer::Impl, Some(trait_item_did)) => trait_item_did, // Inherent impl but this attr is only applied to trait assoc items. - (AssocItemContainer::Impl, None) => return true, + (AssocContainer::Impl, None) => return true, }; find_attr!(tcx.get_all_attrs(def_id), AttributeKind::TypeConst(_)) } diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index e76993e0542b..ec2a23bfebee 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -683,7 +683,7 @@ impl<'tcx> Instance<'tcx> { && !matches!( tcx.opt_associated_item(def), Some(ty::AssocItem { - container: ty::AssocItemContainer::Trait, + container: ty::AssocContainer::Trait, .. }) ); diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 5a4ebfe28a4b..a34bf9b52970 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1937,7 +1937,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Returns the trait item that is implemented by the given item `DefId`. pub fn trait_item_of(self, def_id: impl IntoQueryParam) -> Option { let assoc = self.opt_associated_item(def_id.into_query_param())?; - if assoc.container != AssocItemContainer::Impl { + if assoc.container != AssocContainer::Impl { return None; } assoc.trait_item_def_id @@ -2158,7 +2158,7 @@ impl<'tcx> TyCtxt<'tcx> { let Some(item) = self.opt_associated_item(def_id) else { return false; }; - if item.container != ty::AssocItemContainer::Impl { + if item.container != ty::AssocContainer::Impl { return false; } diff --git a/compiler/rustc_public/src/ty.rs b/compiler/rustc_public/src/ty.rs index 1b5f0ed14299..c7422051c362 100644 --- a/compiler/rustc_public/src/ty.rs +++ b/compiler/rustc_public/src/ty.rs @@ -1612,7 +1612,7 @@ crate_def! { pub struct AssocItem { pub def_id: AssocDef, pub kind: AssocKind, - pub container: AssocItemContainer, + pub container: AssocContainer, /// If this is an item in an impl of a trait then this is the `DefId` of /// the associated item on the trait that this implements. @@ -1636,7 +1636,7 @@ pub enum AssocKind { } #[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum AssocItemContainer { +pub enum AssocContainer { Trait, Impl, } diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index 207038db40d6..ace459e79dad 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -1076,14 +1076,14 @@ impl<'tcx> Stable<'tcx> for ty::AssocKind { } } -impl<'tcx> Stable<'tcx> for ty::AssocItemContainer { - type T = crate::ty::AssocItemContainer; +impl<'tcx> Stable<'tcx> for ty::AssocContainer { + type T = crate::ty::AssocContainer; fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &CompilerCtxt<'_, BridgeTys>) -> Self::T { - use crate::ty::AssocItemContainer; + use crate::ty::AssocContainer; match self { - ty::AssocItemContainer::Trait => AssocItemContainer::Trait, - ty::AssocItemContainer::Impl => AssocItemContainer::Impl, + ty::AssocContainer::Trait => AssocContainer::Trait, + ty::AssocContainer::Impl => AssocContainer::Impl, } } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs index e042ce84955e..9fe685b2786e 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs @@ -857,11 +857,11 @@ fn foo(&self) -> Self::T { String::new() } && self.infcx.can_eq(param_env, assoc_ty, found) { let msg = match assoc_item.container { - ty::AssocItemContainer::Trait => { + ty::AssocContainer::Trait => { "associated type defaults can't be assumed inside the \ trait defining them" } - ty::AssocItemContainer::Impl => { + ty::AssocContainer::Impl => { "associated type is `default` and may be overridden" } }; diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs index 8a67e4ccd456..c04a8b3d860e 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs @@ -5,7 +5,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::{LangItem, lang_items}; -use rustc_middle::ty::{AssocItemContainer, GenericArgsRef, Instance, Ty, TyCtxt, TypingEnv}; +use rustc_middle::ty::{AssocContainer, GenericArgsRef, Instance, Ty, TyCtxt, TypingEnv}; use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, sym}; use tracing::debug; @@ -76,8 +76,8 @@ pub fn call_kind<'tcx>( let parent = tcx.opt_associated_item(method_did).and_then(|assoc| { let container_id = assoc.container_id(tcx); match assoc.container { - AssocItemContainer::Impl => tcx.trait_id_of_impl(container_id), - AssocItemContainer::Trait => Some(container_id), + AssocContainer::Impl => tcx.trait_id_of_impl(container_id), + AssocContainer::Trait => Some(container_id), } }); diff --git a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs index 19eb85506b6f..caaf8c2585d3 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs @@ -387,7 +387,7 @@ pub(crate) fn assoc_def( if let Some(assoc_item) = ancestors.leaf_def(tcx, assoc_def_id) { // Ensure that the impl is constrained, otherwise projection may give us // bad unconstrained infer vars. - if assoc_item.item.container == ty::AssocItemContainer::Impl + if assoc_item.item.container == ty::AssocContainer::Impl && let Some(impl_def_id) = assoc_item.item.container_id(tcx).as_local() { tcx.ensure_ok().enforce_impl_non_lifetime_params_are_constrained(impl_def_id)?; diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs index e9629e31482a..8587c952b915 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -101,7 +101,7 @@ fn associated_item_from_trait_item( kind, def_id: owner_id.to_def_id(), trait_item_def_id: Some(owner_id.to_def_id()), - container: ty::AssocItemContainer::Trait, + container: ty::AssocContainer::Trait, } } @@ -122,7 +122,7 @@ fn associated_item_from_impl_item(tcx: TyCtxt<'_>, impl_item: &hir::ImplItem<'_> kind, def_id: owner_id.to_def_id(), trait_item_def_id: impl_item.trait_item_def_id, - container: ty::AssocItemContainer::Impl, + container: ty::AssocContainer::Impl, } } struct RPITVisitor<'a, 'tcx> { @@ -257,7 +257,7 @@ fn associated_type_for_impl_trait_in_trait( }, def_id, trait_item_def_id: None, - container: ty::AssocItemContainer::Trait, + container: ty::AssocContainer::Trait, }); // Copy visility of the containing function. @@ -323,7 +323,7 @@ fn associated_type_for_impl_trait_in_impl( }, def_id, trait_item_def_id: Some(trait_assoc_def_id), - container: ty::AssocItemContainer::Impl, + container: ty::AssocContainer::Impl, }); // Copy visility of the containing function. diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index b22c326b9f2a..315125715380 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -165,7 +165,7 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { if tcx.def_kind(def_id) == DefKind::AssocFn && let assoc_item = tcx.associated_item(def_id) - && assoc_item.container == ty::AssocItemContainer::Trait + && assoc_item.container == ty::AssocContainer::Trait && assoc_item.defaultness(tcx).has_value() { let sig = tcx.fn_sig(def_id).instantiate_identity(); diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 93932936a2e0..b98f174fbbfd 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1295,12 +1295,12 @@ pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocCo simplify::move_bounds_to_generic_parameters(&mut generics); match assoc_item.container { - ty::AssocItemContainer::Impl => ImplAssocConstItem(Box::new(Constant { + ty::AssocContainer::Impl => ImplAssocConstItem(Box::new(Constant { generics, kind: ConstantKind::Extern { def_id: assoc_item.def_id }, type_: ty, })), - ty::AssocItemContainer::Trait => { + ty::AssocContainer::Trait => { if tcx.defaultness(assoc_item.def_id).has_value() { ProvidedAssocConstItem(Box::new(Constant { generics, @@ -1318,10 +1318,10 @@ pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocCo if has_self { let self_ty = match assoc_item.container { - ty::AssocItemContainer::Impl => { + ty::AssocContainer::Impl => { tcx.type_of(assoc_item.container_id(tcx)).instantiate_identity() } - ty::AssocItemContainer::Trait => tcx.types.self_param, + ty::AssocContainer::Trait => tcx.types.self_param, }; let self_param_ty = tcx.fn_sig(assoc_item.def_id).instantiate_identity().input(0).skip_binder(); @@ -1338,13 +1338,13 @@ pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocCo } let provided = match assoc_item.container { - ty::AssocItemContainer::Impl => true, - ty::AssocItemContainer::Trait => assoc_item.defaultness(tcx).has_value(), + ty::AssocContainer::Impl => true, + ty::AssocContainer::Trait => assoc_item.defaultness(tcx).has_value(), }; if provided { let defaultness = match assoc_item.container { - ty::AssocItemContainer::Impl => Some(assoc_item.defaultness(tcx)), - ty::AssocItemContainer::Trait => None, + ty::AssocContainer::Impl => Some(assoc_item.defaultness(tcx)), + ty::AssocContainer::Trait => None, }; MethodItem(item, defaultness) } else { @@ -1375,7 +1375,7 @@ pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocCo } let mut predicates = tcx.explicit_predicates_of(assoc_item.def_id).predicates; - if let ty::AssocItemContainer::Trait = assoc_item.container { + if let ty::AssocContainer::Trait = assoc_item.container { let bounds = tcx.explicit_item_bounds(assoc_item.def_id).iter_identity_copied(); predicates = tcx.arena.alloc_from_iter(bounds.chain(predicates.iter().copied())); } @@ -1386,7 +1386,7 @@ pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocCo ); simplify::move_bounds_to_generic_parameters(&mut generics); - if let ty::AssocItemContainer::Trait = assoc_item.container { + if let ty::AssocContainer::Trait = assoc_item.container { // Move bounds that are (likely) directly attached to the associated type // from the where-clause to the associated type. // There is no guarantee that this is what the user actually wrote but we have diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index dcd67cb7ebc7..84a5f239c8dd 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -692,11 +692,11 @@ impl Item { // won't be printing correct syntax plus the syntax is unstable. match tcx.opt_associated_item(def_id) { Some(ty::AssocItem { - container: ty::AssocItemContainer::Impl, + container: ty::AssocContainer::Impl, trait_item_def_id: Some(_), .. }) - | Some(ty::AssocItem { container: ty::AssocItemContainer::Trait, .. }) => { + | Some(ty::AssocItem { container: ty::AssocContainer::Trait, .. }) => { hir::Constness::NotConst } None | Some(_) => hir::Constness::Const, @@ -781,8 +781,8 @@ impl Item { | MethodItem(..) => { let assoc_item = tcx.associated_item(def_id); let is_trait_item = match assoc_item.container { - ty::AssocItemContainer::Trait => true, - ty::AssocItemContainer::Impl => { + ty::AssocContainer::Trait => true, + ty::AssocContainer::Impl => { // Trait impl items always inherit the impl's visibility -- // we don't want to show `pub`. tcx.impl_trait_ref(tcx.parent(assoc_item.def_id)).is_some() diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs index 28555a610900..490a59ca66d3 100644 --- a/src/tools/clippy/clippy_lints/src/missing_inline.rs +++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs @@ -3,7 +3,7 @@ use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, Attribute, find_attr}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::AssocItemContainer; +use rustc_middle::ty::AssocContainer; use rustc_session::declare_lint_pass; use rustc_span::Span; @@ -166,8 +166,8 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { let assoc_item = cx.tcx.associated_item(impl_item.owner_id); let container_id = assoc_item.container_id(cx.tcx); let trait_def_id = match assoc_item.container { - AssocItemContainer::Trait => Some(container_id), - AssocItemContainer::Impl => cx.tcx.impl_trait_ref(container_id).map(|t| t.skip_binder().def_id), + AssocContainer::Trait => Some(container_id), + AssocContainer::Impl => cx.tcx.impl_trait_ref(container_id).map(|t| t.skip_binder().def_id), }; if let Some(trait_def_id) = trait_def_id diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 14b64eb4d54e..b806e3d8b1bf 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -3250,8 +3250,8 @@ pub fn get_path_from_caller_to_method_type<'tcx>( let assoc_item = tcx.associated_item(method); let def_id = assoc_item.container_id(tcx); match assoc_item.container { - rustc_ty::AssocItemContainer::Trait => get_path_to_callee(tcx, from, def_id), - rustc_ty::AssocItemContainer::Impl => { + rustc_ty::AssocContainer::Trait => get_path_to_callee(tcx, from, def_id), + rustc_ty::AssocContainer::Impl => { let ty = tcx.type_of(def_id).instantiate_identity(); get_path_to_ty(tcx, from, ty, args) }, From d419d51f934f8ab1fcdb82d6a446818d7d626aa0 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 13 Aug 2025 14:14:37 -0500 Subject: [PATCH 452/682] Rename AssocItemContainer -> AssocContainer --- clippy_lints/src/missing_inline.rs | 6 +++--- clippy_utils/src/lib.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index 28555a610900..490a59ca66d3 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -3,7 +3,7 @@ use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, Attribute, find_attr}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::AssocItemContainer; +use rustc_middle::ty::AssocContainer; use rustc_session::declare_lint_pass; use rustc_span::Span; @@ -166,8 +166,8 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { let assoc_item = cx.tcx.associated_item(impl_item.owner_id); let container_id = assoc_item.container_id(cx.tcx); let trait_def_id = match assoc_item.container { - AssocItemContainer::Trait => Some(container_id), - AssocItemContainer::Impl => cx.tcx.impl_trait_ref(container_id).map(|t| t.skip_binder().def_id), + AssocContainer::Trait => Some(container_id), + AssocContainer::Impl => cx.tcx.impl_trait_ref(container_id).map(|t| t.skip_binder().def_id), }; if let Some(trait_def_id) = trait_def_id diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 14b64eb4d54e..b806e3d8b1bf 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -3250,8 +3250,8 @@ pub fn get_path_from_caller_to_method_type<'tcx>( let assoc_item = tcx.associated_item(method); let def_id = assoc_item.container_id(tcx); match assoc_item.container { - rustc_ty::AssocItemContainer::Trait => get_path_to_callee(tcx, from, def_id), - rustc_ty::AssocItemContainer::Impl => { + rustc_ty::AssocContainer::Trait => get_path_to_callee(tcx, from, def_id), + rustc_ty::AssocContainer::Impl => { let ty = tcx.type_of(def_id).instantiate_identity(); get_path_to_ty(tcx, from, ty, args) }, From 88a8bfcaf0398011b621a981483c288d1e3b64e2 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 5 Aug 2025 15:59:44 -0500 Subject: [PATCH 453/682] Introduce hir::ImplItemImplKind --- compiler/rustc_ast_lowering/src/item.rs | 31 ++++++++++++------ compiler/rustc_hir/src/hir.rs | 26 ++++++++++++--- compiler/rustc_hir/src/intravisit.rs | 11 ++++--- .../rustc_hir_analysis/src/collect/type_of.rs | 2 +- compiler/rustc_lint/src/builtin.rs | 7 ++-- compiler/rustc_lint/src/nonstandard_style.rs | 7 +--- compiler/rustc_middle/src/ty/context.rs | 26 ++++++--------- compiler/rustc_passes/src/dead.rs | 32 ++++++++----------- compiler/rustc_passes/src/stability.rs | 3 +- .../src/error_reporting/traits/suggestions.rs | 2 +- compiler/rustc_ty_utils/src/assoc.rs | 12 +++++-- compiler/rustc_ty_utils/src/ty.rs | 9 +++++- .../clippy_lints/src/disallowed_macros.rs | 6 ++-- .../clippy_lints/src/manual_async_fn.rs | 12 ++++--- .../clippy_lints/src/min_ident_chars.rs | 6 ++-- .../clippy_lints/src/missing_const_for_fn.rs | 18 ++++++++--- .../clippy_utils/src/check_proc_macro.rs | 32 +++++++++---------- 17 files changed, 143 insertions(+), 99 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index bb559bd89212..53351f91c46b 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -5,7 +5,9 @@ use rustc_errors::{E0570, ErrorGuaranteed, struct_span_code_err}; use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{DefKind, PerNS, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId}; -use rustc_hir::{self as hir, HirId, LifetimeSource, PredicateOrigin, Target, find_attr}; +use rustc_hir::{ + self as hir, HirId, ImplItemImplKind, LifetimeSource, PredicateOrigin, Target, find_attr, +}; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::span_bug; use rustc_middle::ty::{ResolverAstLowering, TyCtxt}; @@ -1117,20 +1119,31 @@ impl<'hir> LoweringContext<'_, 'hir> { } }; + let span = self.lower_span(i.span); let item = hir::ImplItem { owner_id: hir_id.expect_owner(), ident: self.lower_ident(ident), generics, + impl_kind: if is_in_trait_impl { + ImplItemImplKind::Trait { + defaultness, + trait_item_def_id: self + .resolver + .get_partial_res(i.id) + .and_then(|r| r.expect_full_res().opt_def_id()) + .ok_or_else(|| { + self.dcx().span_delayed_bug( + span, + "could not resolve trait item being implemented", + ) + }), + } + } else { + ImplItemImplKind::Inherent { vis_span: self.lower_span(i.vis.span) } + }, kind, - vis_span: self.lower_span(i.vis.span), - span: self.lower_span(i.span), - defaultness, + span, has_delayed_lints: !self.delayed_lints.is_empty(), - trait_item_def_id: self - .resolver - .get_partial_res(i.id) - .map(|r| r.expect_full_res().opt_def_id()) - .unwrap_or(None), }; self.arena.alloc(item) } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 75551fe4c196..493236718a86 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -3220,12 +3220,21 @@ pub struct ImplItem<'hir> { pub owner_id: OwnerId, pub generics: &'hir Generics<'hir>, pub kind: ImplItemKind<'hir>, - pub defaultness: Defaultness, + pub impl_kind: ImplItemImplKind, pub span: Span, - pub vis_span: Span, pub has_delayed_lints: bool, - /// When we are in a trait impl, link to the trait-item's id. - pub trait_item_def_id: Option, +} + +#[derive(Debug, Clone, Copy, HashStable_Generic)] +pub enum ImplItemImplKind { + Inherent { + vis_span: Span, + }, + Trait { + defaultness: Defaultness, + /// Item in the trait that this item implements + trait_item_def_id: Result, + }, } impl<'hir> ImplItem<'hir> { @@ -3239,6 +3248,13 @@ impl<'hir> ImplItem<'hir> { ImplItemId { owner_id: self.owner_id } } + pub fn vis_span(&self) -> Option { + match self.impl_kind { + ImplItemImplKind::Trait { .. } => None, + ImplItemImplKind::Inherent { vis_span, .. } => Some(vis_span), + } + } + expect_methods_self_kind! { expect_const, (&'hir Ty<'hir>, BodyId), ImplItemKind::Const(ty, body), (ty, *body); expect_fn, (&FnSig<'hir>, BodyId), ImplItemKind::Fn(ty, body), (ty, *body); @@ -4985,7 +5001,7 @@ mod size_asserts { static_assert_size!(GenericBound<'_>, 64); static_assert_size!(Generics<'_>, 56); static_assert_size!(Impl<'_>, 40); - static_assert_size!(ImplItem<'_>, 96); + static_assert_size!(ImplItem<'_>, 88); static_assert_size!(ImplItemKind<'_>, 40); static_assert_size!(Item<'_>, 88); static_assert_size!(ItemKind<'_>, 64); diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 25a7ae239f31..eb682f32111a 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -1257,18 +1257,21 @@ pub fn walk_impl_item<'v, V: Visitor<'v>>( owner_id: _, ident, ref generics, + ref impl_kind, ref kind, - ref defaultness, span: _, - vis_span: _, has_delayed_lints: _, - trait_item_def_id: _, } = *impl_item; try_visit!(visitor.visit_ident(ident)); try_visit!(visitor.visit_generics(generics)); - try_visit!(visitor.visit_defaultness(defaultness)); try_visit!(visitor.visit_id(impl_item.hir_id())); + match impl_kind { + ImplItemImplKind::Inherent { vis_span: _ } => {} + ImplItemImplKind::Trait { defaultness, trait_item_def_id: _ } => { + try_visit!(visitor.visit_defaultness(defaultness)); + } + } match *kind { ImplItemKind::Const(ref ty, body) => { try_visit!(visitor.visit_ty_unambig(ty)); diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index a8af73717cd0..8cbf17162e32 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -198,7 +198,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_ } } ImplItemKind::Type(ty) => { - if tcx.impl_trait_ref(tcx.hir_get_parent_item(hir_id)).is_none() { + if let ImplItemImplKind::Inherent { .. } = item.impl_kind { check_feature_inherent_assoc_ty(tcx, item.span); } diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index c3c0a34df710..d4c83a0cca78 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -29,7 +29,7 @@ use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId}; use rustc_hir::intravisit::FnKind as HirFnKind; -use rustc_hir::{Body, FnDecl, PatKind, PredicateOrigin, find_attr}; +use rustc_hir::{Body, FnDecl, ImplItemImplKind, PatKind, PredicateOrigin, find_attr}; use rustc_middle::bug; use rustc_middle::lint::LevelAndSource; use rustc_middle::ty::layout::LayoutOf; @@ -1321,9 +1321,8 @@ impl<'tcx> LateLintPass<'tcx> for UnreachablePub { } fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) { - // Only lint inherent impl items. - if cx.tcx.associated_item(impl_item.owner_id).trait_item_def_id.is_none() { - self.perform_lint(cx, "item", impl_item.owner_id.def_id, impl_item.vis_span, false); + if let ImplItemImplKind::Inherent { vis_span } = impl_item.impl_kind { + self.perform_lint(cx, "item", impl_item.owner_id.def_id, vis_span, false); } } } diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index 7b8f17ef93ce..7cdd82b66c18 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -38,11 +38,6 @@ pub(crate) fn method_context(cx: &LateContext<'_>, id: LocalDefId) -> MethodLate } } -fn assoc_item_in_trait_impl(cx: &LateContext<'_>, ii: &hir::ImplItem<'_>) -> bool { - let item = cx.tcx.associated_item(ii.owner_id); - item.trait_item_def_id.is_some() -} - declare_lint! { /// The `non_camel_case_types` lint detects types, variants, traits and /// type parameters that don't have camel case names. @@ -602,7 +597,7 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals { fn check_impl_item(&mut self, cx: &LateContext<'_>, ii: &hir::ImplItem<'_>) { if let hir::ImplItemKind::Const(..) = ii.kind - && !assoc_item_in_trait_impl(cx, ii) + && let hir::ImplItemImplKind::Inherent { .. } = ii.impl_kind { NonUpperCaseGlobals::check_upper_case(cx, "associated constant", None, &ii.ident); } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 8ea767dccd3b..79700d485c44 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2276,7 +2276,16 @@ impl<'tcx> TyCtxt<'tcx> { let is_impl_item = match self.hir_node_by_def_id(suitable_region_binding_scope) { Node::Item(..) | Node::TraitItem(..) => false, - Node::ImplItem(..) => self.is_bound_region_in_impl_item(suitable_region_binding_scope), + Node::ImplItem(impl_item) => match impl_item.impl_kind { + // For now, we do not try to target impls of traits. This is + // because this message is going to suggest that the user + // change the fn signature, but they may not be free to do so, + // since the signature must match the trait. + // + // FIXME(#42706) -- in some cases, we could do better here. + hir::ImplItemImplKind::Trait { .. } => true, + _ => false, + }, _ => false, }; @@ -2330,21 +2339,6 @@ impl<'tcx> TyCtxt<'tcx> { None } - /// Checks if the bound region is in Impl Item. - pub fn is_bound_region_in_impl_item(self, suitable_region_binding_scope: LocalDefId) -> bool { - let container_id = self.parent(suitable_region_binding_scope.to_def_id()); - if self.impl_trait_ref(container_id).is_some() { - // For now, we do not try to target impls of traits. This is - // because this message is going to suggest that the user - // change the fn signature, but they may not be free to do so, - // since the signature must match the trait. - // - // FIXME(#42706) -- in some cases, we could do better here. - return true; - } - false - } - /// Determines whether identifiers in the assembly have strict naming rules. /// Currently, only NVPTX* targets need it. pub fn has_strict_asm_symbol_naming(self) -> bool { diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index f36a7a5b184a..3c2c9683a4d1 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -373,31 +373,27 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { /// Automatically generated items marked with `rustc_trivial_field_reads` /// will be ignored for the purposes of dead code analysis (see PR #85200 /// for discussion). - fn should_ignore_item(&mut self, def_id: DefId) -> bool { - if let Some(impl_of) = self.tcx.trait_impl_of_assoc(def_id) { - if !self.tcx.is_automatically_derived(impl_of) { - return false; - } - - if let Some(trait_of) = self.tcx.trait_id_of_impl(impl_of) - && self.tcx.has_attr(trait_of, sym::rustc_trivial_field_reads) + fn should_ignore_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) -> bool { + if let hir::ImplItemImplKind::Trait { .. } = impl_item.impl_kind + && let impl_of = self.tcx.parent(impl_item.owner_id.to_def_id()) + && self.tcx.is_automatically_derived(impl_of) + && let trait_ref = self.tcx.impl_trait_ref(impl_of).unwrap().instantiate_identity() + && self.tcx.has_attr(trait_ref.def_id, sym::rustc_trivial_field_reads) + { + if let ty::Adt(adt_def, _) = trait_ref.self_ty().kind() + && let Some(adt_def_id) = adt_def.did().as_local() { - let trait_ref = self.tcx.impl_trait_ref(impl_of).unwrap().instantiate_identity(); - if let ty::Adt(adt_def, _) = trait_ref.self_ty().kind() - && let Some(adt_def_id) = adt_def.did().as_local() - { - self.ignored_derived_traits.entry(adt_def_id).or_default().insert(trait_of); - } - return true; + self.ignored_derived_traits.entry(adt_def_id).or_default().insert(trait_ref.def_id); } + return true; } false } fn visit_node(&mut self, node: Node<'tcx>) { - if let Node::ImplItem(hir::ImplItem { owner_id, .. }) = node - && self.should_ignore_item(owner_id.to_def_id()) + if let Node::ImplItem(impl_item) = node + && self.should_ignore_impl_item(impl_item) { return; } @@ -439,7 +435,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { } Node::ImplItem(impl_item) => { let item = self.tcx.local_parent(impl_item.owner_id.def_id); - if self.tcx.impl_trait_ref(item).is_none() { + if let hir::ImplItemImplKind::Inherent { .. } = impl_item.impl_kind { //// If it's a type whose items are live, then it's live, too. //// This is done to handle the case where, for example, the static //// method of a private type is used, but the type itself is never diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 71650c6b9b93..7e11b7046d6c 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -486,8 +486,7 @@ impl<'tcx> Visitor<'tcx> for MissingStabilityAnnotations<'tcx> { fn visit_impl_item(&mut self, ii: &'tcx hir::ImplItem<'tcx>) { self.check_compatible_stability(ii.owner_id.def_id); - let impl_def_id = self.tcx.hir_get_parent_item(ii.hir_id()); - if self.tcx.impl_trait_ref(impl_def_id).is_none() { + if let hir::ImplItemImplKind::Inherent { .. } = ii.impl_kind { self.check_missing_stability(ii.owner_id.def_id); self.check_missing_const_stability(ii.owner_id.def_id); } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index cb84d583e6e9..f2f840581cf0 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -421,7 +421,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }) | hir::Node::ImplItem(hir::ImplItem { generics, - trait_item_def_id: None, + impl_kind: hir::ImplItemImplKind::Inherent { .. }, kind: hir::ImplItemKind::Fn(..), .. }) if finder.can_suggest_bound(generics) => { diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs index 8587c952b915..7d257273c0a6 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -2,7 +2,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId}; use rustc_hir::definitions::{DefPathData, DisambiguatorState}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{self as hir, ItemKind}; +use rustc_hir::{self as hir, ImplItemImplKind, ItemKind}; use rustc_middle::query::Providers; use rustc_middle::ty::{self, ImplTraitInTraitData, TyCtxt}; use rustc_middle::{bug, span_bug}; @@ -121,8 +121,11 @@ fn associated_item_from_impl_item(tcx: TyCtxt<'_>, impl_item: &hir::ImplItem<'_> ty::AssocItem { kind, def_id: owner_id.to_def_id(), - trait_item_def_id: impl_item.trait_item_def_id, container: ty::AssocContainer::Impl, + trait_item_def_id: match impl_item.impl_kind { + ImplItemImplKind::Inherent { .. } => None, + ImplItemImplKind::Trait { trait_item_def_id, .. } => trait_item_def_id.ok(), + }, } } struct RPITVisitor<'a, 'tcx> { @@ -190,7 +193,10 @@ fn associated_types_for_impl_traits_in_trait_or_impl<'tcx>( } let did = item.owner_id.def_id.to_def_id(); let item = tcx.hir_impl_item(*item); - let Some(trait_item_def_id) = item.trait_item_def_id else { + let ImplItemImplKind::Trait { + trait_item_def_id: Ok(trait_item_def_id), .. + } = item.impl_kind + else { return Some((did, vec![])); }; let iter = in_trait_def[&trait_item_def_id].iter().map(|&id| { diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 315125715380..b3f2bb6d4b5a 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -88,8 +88,15 @@ fn defaultness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Defaultness { }), .. }) - | hir::Node::ImplItem(hir::ImplItem { defaultness, .. }) + | hir::Node::ImplItem(hir::ImplItem { + impl_kind: hir::ImplItemImplKind::Trait { defaultness, .. }, + .. + }) | hir::Node::TraitItem(hir::TraitItem { defaultness, .. }) => *defaultness, + hir::Node::ImplItem(hir::ImplItem { + impl_kind: hir::ImplItemImplKind::Inherent { .. }, + .. + }) => hir::Defaultness::Final, node => { bug!("`defaultness` called on {:?}", node); } diff --git a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs index 23e7c7251cf1..49cd2671dc00 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs @@ -8,7 +8,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefIdMap; use rustc_hir::{ - AmbigArg, Expr, ExprKind, ForeignItem, HirId, ImplItem, Item, ItemKind, OwnerId, Pat, Path, Stmt, TraitItem, Ty, + AmbigArg, Expr, ExprKind, ForeignItem, HirId, ImplItem, ImplItemImplKind, Item, ItemKind, OwnerId, Pat, Path, Stmt, TraitItem, Ty, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; @@ -177,7 +177,9 @@ impl LateLintPass<'_> for DisallowedMacros { fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &ImplItem<'_>) { self.check(cx, item.span, None); - self.check(cx, item.vis_span, None); + if let ImplItemImplKind::Inherent { vis_span, .. } = item.impl_kind { + self.check(cx, vis_span, None); + } } fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) { diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs index ba1ad599e116..bee3b19b597c 100644 --- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs +++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs @@ -4,7 +4,7 @@ use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{ Block, Body, Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, FnDecl, - FnRetTy, GenericBound, ImplItem, Item, Node, OpaqueTy, TraitRef, Ty, TyKind, + FnRetTy, GenericBound, Node, OpaqueTy, TraitRef, Ty, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::middle::resolve_bound_vars::ResolvedArg; @@ -60,8 +60,11 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { && let ExprKind::Block(block, _) = body.value.kind && block.stmts.is_empty() && let Some(closure_body) = desugared_async_block(cx, block) - && let Node::Item(Item {vis_span, ..}) | Node::ImplItem(ImplItem {vis_span, ..}) = - cx.tcx.hir_node_by_def_id(fn_def_id) + && let Some(vis_span_opt) = match cx.tcx.hir_node_by_def_id(fn_def_id) { + Node::Item(item) => Some(Some(item.vis_span)), + Node::ImplItem(impl_item) => Some(impl_item.vis_span()), + _ => None, + } && !span.from_expansion() { let header_span = span.with_hi(ret_ty.span.hi()); @@ -72,7 +75,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { header_span, "this function can be simplified using the `async fn` syntax", |diag| { - if let Some(vis_snip) = vis_span.get_source_text(cx) + if let Some(vis_span) = vis_span_opt + && let Some(vis_snip) = vis_span.get_source_text(cx) && let Some(header_snip) = header_span.get_source_text(cx) && let Some(ret_pos) = position_before_rarrow(&header_snip) && let Some((_, ret_snip)) = suggested_ret(cx, output) diff --git a/src/tools/clippy/clippy_lints/src/min_ident_chars.rs b/src/tools/clippy/clippy_lints/src/min_ident_chars.rs index dbce29a86318..6258e408217f 100644 --- a/src/tools/clippy/clippy_lints/src/min_ident_chars.rs +++ b/src/tools/clippy/clippy_lints/src/min_ident_chars.rs @@ -5,8 +5,8 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{Visitor, walk_item, walk_trait_item}; use rustc_hir::{ - GenericParamKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, ItemLocalId, Node, Pat, PatKind, TraitItem, - UsePath, + GenericParamKind, HirId, Impl, ImplItem, ImplItemImplKind, ImplItemKind, Item, ItemKind, ItemLocalId, Node, Pat, + PatKind, TraitItem, UsePath, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; @@ -256,7 +256,7 @@ fn is_not_in_trait_impl(cx: &LateContext<'_>, pat: &Pat<'_>, ident: Ident) -> bo } fn get_param_name(impl_item: &ImplItem<'_>, cx: &LateContext<'_>, ident: Ident) -> Option { - if let Some(trait_item_def_id) = impl_item.trait_item_def_id { + if let ImplItemImplKind::Trait { trait_item_def_id: Ok(trait_item_def_id), .. } = impl_item.impl_kind { let trait_param_names = cx.tcx.fn_arg_idents(trait_item_def_id); let ImplItemKind::Fn(_, body_id) = impl_item.kind else { diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs index a6be7581c9a3..a63ad9786262 100644 --- a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs +++ b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs @@ -158,13 +158,23 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { let mir = cx.tcx.optimized_mir(def_id); if let Ok(()) = is_min_const_fn(cx, mir, self.msrv) - && let hir::Node::Item(hir::Item { vis_span, .. }) | hir::Node::ImplItem(hir::ImplItem { vis_span, .. }) = - cx.tcx.hir_node_by_def_id(def_id) + && let node = cx.tcx.hir_node_by_def_id(def_id) + && let Some((item_span, vis_span_opt)) = match node { + hir::Node::Item(item) => Some((item.span, Some(item.vis_span))), + hir::Node::ImplItem(impl_item) => Some((impl_item.span, impl_item.vis_span())), + _ => None, + } { - let suggestion = if vis_span.is_empty() { "const " } else { " const" }; + let (sugg_span, suggestion) = if let Some(vis_span) = vis_span_opt + && !vis_span.is_empty() + { + (vis_span.shrink_to_hi(), " const") + } else { + (item_span.shrink_to_lo(), "const ") + }; span_lint_and_then(cx, MISSING_CONST_FOR_FN, span, "this could be a `const fn`", |diag| { diag.span_suggestion_verbose( - vis_span.shrink_to_hi(), + sugg_span, "make the function `const`", suggestion, Applicability::MachineApplicable, diff --git a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs index c4a759e919b1..1a25c90d735e 100644 --- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs +++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs @@ -19,7 +19,7 @@ use rustc_ast::token::CommentKind; use rustc_hir::intravisit::FnKind; use rustc_hir::{ Block, BlockCheckMode, Body, Closure, Destination, Expr, ExprKind, FieldDef, FnHeader, FnRetTy, HirId, Impl, - ImplItem, ImplItemKind, IsAuto, Item, ItemKind, Lit, LoopSource, MatchSource, MutTy, Node, Path, QPath, Safety, + ImplItem, ImplItemImplKind, ImplItemKind, IsAuto, Item, ItemKind, Lit, LoopSource, MatchSource, MutTy, Node, Path, QPath, Safety, TraitImplHeader, TraitItem, TraitItemKind, Ty, TyKind, UnOp, UnsafeSource, Variant, VariantData, YieldSource, }; use rustc_lint::{EarlyContext, LateContext, LintContext}; @@ -280,16 +280,17 @@ fn trait_item_search_pat(item: &TraitItem<'_>) -> (Pat, Pat) { } fn impl_item_search_pat(item: &ImplItem<'_>) -> (Pat, Pat) { - let (start_pat, end_pat) = match &item.kind { + let (mut start_pat, end_pat) = match &item.kind { ImplItemKind::Const(..) => (Pat::Str("const"), Pat::Str(";")), ImplItemKind::Type(..) => (Pat::Str("type"), Pat::Str(";")), ImplItemKind::Fn(sig, ..) => (fn_header_search_pat(sig.header), Pat::Str("")), }; - if item.vis_span.is_empty() { - (start_pat, end_pat) - } else { - (Pat::Str("pub"), end_pat) - } + if let ImplItemImplKind::Inherent { vis_span, .. } = item.impl_kind + && !vis_span.is_empty() + { + start_pat = Pat::Str("pub"); + }; + (start_pat, end_pat) } fn field_def_search_pat(def: &FieldDef<'_>) -> (Pat, Pat) { @@ -313,21 +314,20 @@ fn variant_search_pat(v: &Variant<'_>) -> (Pat, Pat) { } fn fn_kind_pat(tcx: TyCtxt<'_>, kind: &FnKind<'_>, body: &Body<'_>, hir_id: HirId) -> (Pat, Pat) { - let (start_pat, end_pat) = match kind { + let (mut start_pat, end_pat) = match kind { FnKind::ItemFn(.., header) => (fn_header_search_pat(*header), Pat::Str("")), FnKind::Method(.., sig) => (fn_header_search_pat(sig.header), Pat::Str("")), FnKind::Closure => return (Pat::Str(""), expr_search_pat(tcx, body.value).1), }; - let start_pat = match tcx.hir_node(hir_id) { - Node::Item(Item { vis_span, .. }) | Node::ImplItem(ImplItem { vis_span, .. }) => { - if vis_span.is_empty() { - start_pat - } else { - Pat::Str("pub") + match tcx.hir_node(hir_id) { + Node::Item(Item { vis_span, .. }) + | Node::ImplItem(ImplItem { impl_kind: ImplItemImplKind::Inherent { vis_span, .. }, .. }) => { + if !vis_span.is_empty() { + start_pat = Pat::Str("pub") } }, - Node::TraitItem(_) => start_pat, - _ => Pat::Str(""), + Node::ImplItem(_) | Node::TraitItem(_) => {}, + _ => start_pat = Pat::Str(""), }; (start_pat, end_pat) } From 9615ec7d108399501d7d48f4aeac46561ef31fc8 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 13 Aug 2025 15:24:19 -0500 Subject: [PATCH 454/682] Split AssocContainer::{InherentImpl,TraitImpl} --- .../rustc_hir_analysis/src/check/check.rs | 15 +++--- .../src/check/compare_impl_item.rs | 14 ++++-- .../rustc_hir_analysis/src/check/wfcheck.rs | 7 +-- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 2 +- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 2 +- compiler/rustc_hir_typeck/src/lib.rs | 3 +- compiler/rustc_hir_typeck/src/method/probe.rs | 2 +- compiler/rustc_lint/src/builtin.rs | 13 +++-- compiler/rustc_lint/src/nonstandard_style.rs | 28 ++--------- compiler/rustc_metadata/src/rmeta/decoder.rs | 13 +---- compiler/rustc_metadata/src/rmeta/encoder.rs | 27 ++++------ compiler/rustc_metadata/src/rmeta/mod.rs | 3 +- compiler/rustc_metadata/src/rmeta/table.rs | 7 --- compiler/rustc_middle/src/ty/assoc.rs | 50 ++++++++++++++----- compiler/rustc_middle/src/ty/instance.rs | 41 +++++++-------- compiler/rustc_middle/src/ty/mod.rs | 15 ++---- compiler/rustc_passes/src/stability.rs | 4 +- compiler/rustc_public/src/ty.rs | 8 ++- .../src/unstable/convert/stable/ty.rs | 12 +++-- .../error_reporting/infer/note_and_explain.rs | 2 +- .../src/error_reporting/infer/region.rs | 7 +-- .../src/error_reporting/traits/call_kind.rs | 3 +- .../traits/specialize/specialization_graph.rs | 2 +- compiler/rustc_ty_utils/src/assoc.rs | 29 ++++------- compiler/rustc_ty_utils/src/opaque_types.rs | 2 +- src/librustdoc/clean/mod.rs | 20 ++++---- src/librustdoc/clean/types.rs | 33 +++++------- .../clippy/clippy_lints/src/missing_doc.rs | 11 ++-- .../clippy/clippy_lints/src/missing_inline.rs | 3 +- .../clippy_lints/src/missing_trait_methods.rs | 2 +- src/tools/clippy/clippy_utils/src/lib.rs | 2 +- .../post-analysis-user-facing-param-env.rs | 1 - ...post-analysis-user-facing-param-env.stderr | 19 +------ 33 files changed, 173 insertions(+), 229 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 3887c96fce63..886ebddc75c9 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1009,7 +1009,7 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), res = res.and(check_associated_item(tcx, def_id)); let assoc_item = tcx.associated_item(def_id); match assoc_item.container { - ty::AssocContainer::Impl => {} + ty::AssocContainer::InherentImpl | ty::AssocContainer::TraitImpl(_) => {} ty::AssocContainer::Trait => { res = res.and(check_trait_item(tcx, def_id)); } @@ -1026,7 +1026,7 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), res = res.and(check_associated_item(tcx, def_id)); let assoc_item = tcx.associated_item(def_id); match assoc_item.container { - ty::AssocContainer::Impl => {} + ty::AssocContainer::InherentImpl | ty::AssocContainer::TraitImpl(_) => {} ty::AssocContainer::Trait => { res = res.and(check_trait_item(tcx, def_id)); } @@ -1043,7 +1043,7 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), let assoc_item = tcx.associated_item(def_id); let has_type = match assoc_item.container { - ty::AssocContainer::Impl => true, + ty::AssocContainer::InherentImpl | ty::AssocContainer::TraitImpl(_) => true, ty::AssocContainer::Trait => { tcx.ensure_ok().explicit_item_bounds(def_id); tcx.ensure_ok().explicit_item_self_bounds(def_id); @@ -1177,12 +1177,9 @@ fn check_impl_items_against_trait<'tcx>( for &impl_item in impl_item_refs { let ty_impl_item = tcx.associated_item(impl_item); - let ty_trait_item = if let Some(trait_item_id) = ty_impl_item.trait_item_def_id { - tcx.associated_item(trait_item_id) - } else { - // Checked in `associated_item`. - tcx.dcx().span_delayed_bug(tcx.def_span(impl_item), "missing associated item in trait"); - continue; + let ty_trait_item = match ty_impl_item.expect_trait_impl() { + Ok(trait_item_id) => tcx.associated_item(trait_item_id), + Err(ErrorGuaranteed { .. }) => continue, }; let res = tcx.ensure_ok().compare_impl_item(impl_item.expect_local()); diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 85cd8da2ca4d..84fb09b73904 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -37,7 +37,7 @@ pub(super) fn compare_impl_item( impl_item_def_id: LocalDefId, ) -> Result<(), ErrorGuaranteed> { let impl_item = tcx.associated_item(impl_item_def_id); - let trait_item = tcx.associated_item(impl_item.trait_item_def_id.unwrap()); + let trait_item = tcx.associated_item(impl_item.expect_trait_impl()?); let impl_trait_ref = tcx.impl_trait_ref(impl_item.container_id(tcx)).unwrap().instantiate_identity(); debug!(?impl_trait_ref); @@ -446,7 +446,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( impl_m_def_id: LocalDefId, ) -> Result<&'tcx DefIdMap>>, ErrorGuaranteed> { let impl_m = tcx.associated_item(impl_m_def_id.to_def_id()); - let trait_m = tcx.associated_item(impl_m.trait_item_def_id.unwrap()); + let trait_m = tcx.associated_item(impl_m.expect_trait_impl()?); let impl_trait_ref = tcx.impl_trait_ref(tcx.parent(impl_m_def_id.to_def_id())).unwrap().instantiate_identity(); // First, check a few of the same things as `compare_impl_method`, @@ -1449,7 +1449,9 @@ fn compare_self_type<'tcx>( let self_string = |method: ty::AssocItem| { let untransformed_self_ty = match method.container { - ty::AssocContainer::Impl => impl_trait_ref.self_ty(), + ty::AssocContainer::InherentImpl | ty::AssocContainer::TraitImpl(_) => { + impl_trait_ref.self_ty() + } ty::AssocContainer::Trait => tcx.types.self_param, }; let self_arg_ty = tcx.fn_sig(method.def_id).instantiate_identity().input(0); @@ -2458,8 +2460,12 @@ fn param_env_with_gat_bounds<'tcx>( for impl_ty in impl_tys_to_install { let trait_ty = match impl_ty.container { + ty::AssocContainer::InherentImpl => bug!(), ty::AssocContainer::Trait => impl_ty, - ty::AssocContainer::Impl => tcx.associated_item(impl_ty.trait_item_def_id.unwrap()), + ty::AssocContainer::TraitImpl(Err(_)) => continue, + ty::AssocContainer::TraitImpl(Ok(trait_item_def_id)) => { + tcx.associated_item(trait_item_def_id) + } }; let mut bound_vars: smallvec::SmallVec<[ty::BoundVariableKind; 8]> = diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 21dc29a81724..d33f1f3e12af 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -944,12 +944,13 @@ pub(crate) fn check_associated_item( // Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case // other `Foo` impls are incoherent. - tcx.ensure_ok() - .coherent_trait(tcx.parent(item.trait_item_def_id.unwrap_or(item_id.into())))?; + tcx.ensure_ok().coherent_trait(tcx.parent(item.trait_item_or_self()?))?; let self_ty = match item.container { ty::AssocContainer::Trait => tcx.types.self_param, - ty::AssocContainer::Impl => tcx.type_of(item.container_id(tcx)).instantiate_identity(), + ty::AssocContainer::InherentImpl | ty::AssocContainer::TraitImpl(_) => { + tcx.type_of(item.container_id(tcx)).instantiate_identity() + } }; let span = tcx.def_span(item_id); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 7b485acc8bd1..7370124e800d 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -1033,7 +1033,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.set_tainted_by_errors(e); } } - ty::AssocContainer::Impl => { + ty::AssocContainer::InherentImpl | ty::AssocContainer::TraitImpl(_) => { if segments.len() == 1 { // `::assoc` will end up here, and so // can `T::assoc`. If this came from an diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 94b635c41b4b..44a6084ebd5a 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -2354,7 +2354,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We want it to always point to the trait item. // If we're pointing at an inherent function, we don't need to do anything, // so we fetch the parent and verify if it's a trait item. - && let maybe_trait_item_def_id = assoc_item.trait_item_def_id.unwrap_or(def_id) + && let Ok(maybe_trait_item_def_id) = assoc_item.trait_item_or_self() && let maybe_trait_def_id = self.tcx.parent(maybe_trait_item_def_id) // Just an easy way to check "trait_def_id == Fn/FnMut/FnOnce" && let Some(call_kind) = self.tcx.fn_trait_kind_from_def_id(maybe_trait_def_id) diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 8ba6c25c0778..7f5397a7926d 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -278,8 +278,7 @@ fn infer_type_if_missing<'tcx>(fcx: &FnCtxt<'_, 'tcx>, node: Node<'tcx>) -> Opti { if let Some(item) = tcx.opt_associated_item(def_id.into()) && let ty::AssocKind::Const { .. } = item.kind - && let ty::AssocContainer::Impl = item.container - && let Some(trait_item_def_id) = item.trait_item_def_id + && let ty::AssocContainer::TraitImpl(Ok(trait_item_def_id)) = item.container { let impl_def_id = item.container_id(tcx); let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity(); diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index a52ae354c533..4185f7f6996c 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -1659,7 +1659,7 @@ impl<'tcx> Pick<'tcx> { /// Do not use for type checking. pub(crate) fn differs_from(&self, other: &Self) -> bool { let Self { - item: AssocItem { def_id, kind: _, container: _, trait_item_def_id: _ }, + item: AssocItem { def_id, kind: _, container: _ }, kind: _, import_ids: _, autoderefs: _, diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index d4c83a0cca78..75a0f89321b9 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -34,7 +34,7 @@ use rustc_middle::bug; use rustc_middle::lint::LevelAndSource; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, Upcast, VariantDef}; +use rustc_middle::ty::{self, AssocContainer, Ty, TyCtxt, TypeVisitableExt, Upcast, VariantDef}; use rustc_session::lint::FutureIncompatibilityReason; // hardwired lints from rustc_lint_defs pub use rustc_session::lint::builtin::*; @@ -61,7 +61,6 @@ use crate::lints::{ BuiltinUnreachablePub, BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub, BuiltinWhileTrue, InvalidAsmLabel, }; -use crate::nonstandard_style::{MethodLateContext, method_context}; use crate::{ EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext, fluent_generated as fluent, @@ -469,14 +468,14 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { } fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) { - let context = method_context(cx, impl_item.owner_id.def_id); + let container = cx.tcx.associated_item(impl_item.owner_id.def_id).container; - match context { + match container { // If the method is an impl for a trait, don't doc. - MethodLateContext::TraitImpl => return, - MethodLateContext::TraitAutoImpl => {} + AssocContainer::TraitImpl(_) => return, + AssocContainer::Trait => {} // If the method is an impl for an item with docs_hidden, don't doc. - MethodLateContext::PlainImpl => { + AssocContainer::InherentImpl => { let parent = cx.tcx.hir_get_parent_item(impl_item.hir_id()); let impl_ty = cx.tcx.type_of(parent).instantiate_identity(); let outerdef = match impl_ty.kind() { diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index 7cdd82b66c18..7f643a551bb7 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -7,7 +7,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{FnKind, Visitor}; use rustc_hir::{Attribute, GenericParamKind, PatExprKind, PatKind, find_attr}; use rustc_middle::hir::nested_filter::All; -use rustc_middle::ty; +use rustc_middle::ty::AssocContainer; use rustc_session::config::CrateType; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::def_id::LocalDefId; @@ -20,24 +20,6 @@ use crate::lints::{ }; use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; -#[derive(PartialEq)] -pub(crate) enum MethodLateContext { - TraitAutoImpl, - TraitImpl, - PlainImpl, -} - -pub(crate) fn method_context(cx: &LateContext<'_>, id: LocalDefId) -> MethodLateContext { - let item = cx.tcx.associated_item(id); - match item.container { - ty::AssocContainer::Trait => MethodLateContext::TraitAutoImpl, - ty::AssocContainer::Impl => match cx.tcx.impl_trait_ref(item.container_id(cx.tcx)) { - Some(_) => MethodLateContext::TraitImpl, - None => MethodLateContext::PlainImpl, - }, - } -} - declare_lint! { /// The `non_camel_case_types` lint detects types, variants, traits and /// type parameters that don't have camel case names. @@ -384,8 +366,8 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase { id: LocalDefId, ) { match &fk { - FnKind::Method(ident, sig, ..) => match method_context(cx, id) { - MethodLateContext::PlainImpl => { + FnKind::Method(ident, sig, ..) => match cx.tcx.associated_item(id).container { + AssocContainer::InherentImpl => { if sig.header.abi != ExternAbi::Rust && find_attr!(cx.tcx.get_all_attrs(id), AttributeKind::NoMangle(..)) { @@ -393,10 +375,10 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase { } self.check_snake_case(cx, "method", ident); } - MethodLateContext::TraitAutoImpl => { + AssocContainer::Trait => { self.check_snake_case(cx, "trait method", ident); } - _ => (), + AssocContainer::TraitImpl(_) => {} }, FnKind::ItemFn(ident, _, header) => { // Skip foreign-ABI #[no_mangle] functions (Issue #31924) diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 110b26c62ef0..0c8d1f32e991 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1194,10 +1194,6 @@ impl<'a> CrateMetadataRef<'a> { self.root.tables.default_fields.get(self, id).map(|d| d.decode(self)) } - fn get_trait_item_def_id(self, id: DefIndex) -> Option { - self.root.tables.trait_item_def_id.get(self, id).map(|d| d.decode_from_cdata(self)) - } - fn get_expn_that_defined(self, id: DefIndex, sess: &Session) -> ExpnId { self.root .tables @@ -1359,14 +1355,9 @@ impl<'a> CrateMetadataRef<'a> { } _ => bug!("cannot get associated-item of `{:?}`", self.def_key(id)), }; - let container = self.root.tables.assoc_container.get(self, id).unwrap(); + let container = self.root.tables.assoc_container.get(self, id).unwrap().decode(self); - ty::AssocItem { - kind, - def_id: self.local_def_id(id), - trait_item_def_id: self.get_trait_item_def_id(id), - container, - } + ty::AssocItem { kind, def_id: self.local_def_id(id), container } } fn get_ctor(self, node_id: DefIndex) -> Option<(CtorKind, DefId)> { diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 0fb5b8fffd18..cb603705ce14 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1254,7 +1254,7 @@ fn should_encode_type(tcx: TyCtxt<'_>, def_id: LocalDefId, def_kind: DefKind) -> DefKind::AssocTy => { let assoc_item = tcx.associated_item(def_id); match assoc_item.container { - ty::AssocContainer::Impl => true, + ty::AssocContainer::InherentImpl | ty::AssocContainer::TraitImpl(_) => true, ty::AssocContainer::Trait => assoc_item.defaultness(tcx).has_value(), } } @@ -1726,23 +1726,16 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let item = tcx.associated_item(def_id); self.tables.defaultness.set_some(def_id.index, item.defaultness(tcx)); - self.tables.assoc_container.set_some(def_id.index, item.container); + record!(self.tables.assoc_container[def_id] <- item.container); - match item.container { - AssocContainer::Trait => { - if item.is_type() { - self.encode_explicit_item_bounds(def_id); - self.encode_explicit_item_self_bounds(def_id); - if tcx.is_conditionally_const(def_id) { - record_defaulted_array!(self.tables.explicit_implied_const_bounds[def_id] - <- self.tcx.explicit_implied_const_bounds(def_id).skip_binder()); - } - } - } - AssocContainer::Impl => { - if let Some(trait_item_def_id) = item.trait_item_def_id { - self.tables.trait_item_def_id.set_some(def_id.index, trait_item_def_id.into()); - } + if let AssocContainer::Trait = item.container + && item.is_type() + { + self.encode_explicit_item_bounds(def_id); + self.encode_explicit_item_self_bounds(def_id); + if tcx.is_conditionally_const(def_id) { + record_defaulted_array!(self.tables.explicit_implied_const_bounds[def_id] + <- self.tcx.explicit_implied_const_bounds(def_id).skip_binder()); } } if let ty::AssocKind::Type { data: ty::AssocTypeData::Rpitit(rpitit_info) } = item.kind { diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index a7b1f2c01dd1..720970bbaf93 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -447,7 +447,6 @@ define_tables! { coroutine_by_move_body_def_id: Table, eval_static_initializer: Table>>, trait_def: Table>, - trait_item_def_id: Table, expn_that_defined: Table>, default_fields: Table>, params_in_repr: Table>>, @@ -459,7 +458,7 @@ define_tables! { def_keys: Table>, proc_macro_quoted_spans: Table>, variant_data: Table>, - assoc_container: Table, + assoc_container: Table>, macro_definition: Table>, proc_macro: Table, deduced_param_attrs: Table>, diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs index 36634bb39d80..a882ee4f2b92 100644 --- a/compiler/rustc_metadata/src/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -221,13 +221,6 @@ fixed_size_enum! { } } -fixed_size_enum! { - ty::AssocContainer { - ( Trait ) - ( Impl ) - } -} - fixed_size_enum! { MacroKind { ( Attr ) diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs index 8a2f62ce8335..60181783f9b1 100644 --- a/compiler/rustc_middle/src/ty/assoc.rs +++ b/compiler/rustc_middle/src/ty/assoc.rs @@ -5,7 +5,7 @@ use rustc_hir::def::{DefKind, Namespace}; use rustc_hir::def_id::DefId; use rustc_hir::find_attr; use rustc_macros::{Decodable, Encodable, HashStable}; -use rustc_span::{Ident, Symbol}; +use rustc_span::{ErrorGuaranteed, Ident, Symbol}; use super::{TyCtxt, Visibility}; use crate::ty; @@ -13,7 +13,9 @@ use crate::ty; #[derive(Clone, Copy, PartialEq, Eq, Debug, HashStable, Hash, Encodable, Decodable)] pub enum AssocContainer { Trait, - Impl, + InherentImpl, + /// The `DefId` points to the trait item being implemented. + TraitImpl(Result), } /// Information about an associated item @@ -22,10 +24,6 @@ pub struct AssocItem { pub def_id: DefId, pub kind: AssocKind, pub container: AssocContainer, - - /// If this is an item in an impl of a trait then this is the `DefId` of - /// the associated item on the trait that this implements. - pub trait_item_def_id: Option, } impl AssocItem { @@ -58,6 +56,30 @@ impl AssocItem { tcx.defaultness(self.def_id) } + pub fn expect_trait_impl(&self) -> Result { + let AssocContainer::TraitImpl(trait_item_id) = self.container else { + bug!("expected item to be in a trait impl: {:?}", self.def_id); + }; + trait_item_id + } + + /// If this is a trait impl item, returns the `DefId` of the trait item this implements. + /// Otherwise, returns `DefId` for self. Returns an Err in case the trait item was not + /// resolved successfully. + pub fn trait_item_or_self(&self) -> Result { + match self.container { + AssocContainer::TraitImpl(id) => id, + AssocContainer::Trait | AssocContainer::InherentImpl => Ok(self.def_id), + } + } + + pub fn trait_item_def_id(&self) -> Option { + match self.container { + AssocContainer::TraitImpl(Ok(id)) => Some(id), + _ => None, + } + } + #[inline] pub fn visibility(&self, tcx: TyCtxt<'_>) -> Visibility { tcx.visibility(self.def_id) @@ -71,7 +93,7 @@ impl AssocItem { #[inline] pub fn trait_container(&self, tcx: TyCtxt<'_>) -> Option { match self.container { - AssocContainer::Impl => None, + AssocContainer::InherentImpl | AssocContainer::TraitImpl(_) => None, AssocContainer::Trait => Some(tcx.parent(self.def_id)), } } @@ -79,7 +101,9 @@ impl AssocItem { #[inline] pub fn impl_container(&self, tcx: TyCtxt<'_>) -> Option { match self.container { - AssocContainer::Impl => Some(tcx.parent(self.def_id)), + AssocContainer::InherentImpl | AssocContainer::TraitImpl(_) => { + Some(tcx.parent(self.def_id)) + } AssocContainer::Trait => None, } } @@ -156,11 +180,11 @@ impl AssocItem { return false; } - let def_id = match (self.container, self.trait_item_def_id) { - (AssocContainer::Trait, _) => self.def_id, - (AssocContainer::Impl, Some(trait_item_did)) => trait_item_did, - // Inherent impl but this attr is only applied to trait assoc items. - (AssocContainer::Impl, None) => return true, + let def_id = match self.container { + AssocContainer::Trait => self.def_id, + AssocContainer::TraitImpl(Ok(trait_item_did)) => trait_item_did, + AssocContainer::TraitImpl(Err(_)) => return false, + AssocContainer::InherentImpl => return true, }; find_attr!(tcx.get_all_attrs(def_id), AttributeKind::TypeConst(_)) } diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index ec2a23bfebee..34ead91b4f6d 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -18,8 +18,8 @@ use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::ty::normalize_erasing_regions::NormalizationError; use crate::ty::print::{FmtPrinter, Print}; use crate::ty::{ - self, EarlyBinder, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, - TypeVisitable, TypeVisitableExt, TypeVisitor, + self, AssocContainer, EarlyBinder, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypeFoldable, + TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, }; /// An `InstanceKind` along with the args that are needed to substitute the instance. @@ -611,26 +611,23 @@ impl<'tcx> Instance<'tcx> { debug!(" => fn pointer created for virtual call"); resolved.def = InstanceKind::ReifyShim(def_id, reason); } - // Reify `Trait::method` implementations if KCFI is enabled - // FIXME(maurer) only reify it if it is a vtable-safe function - _ if tcx.sess.is_sanitizer_kcfi_enabled() - && tcx - .opt_associated_item(def_id) - .and_then(|assoc| assoc.trait_item_def_id) - .is_some() => - { - // If this function could also go in a vtable, we need to `ReifyShim` it with - // KCFI because it can only attach one type per function. - resolved.def = InstanceKind::ReifyShim(resolved.def_id(), reason) - } - // Reify `::call`-like method implementations if KCFI is enabled - _ if tcx.sess.is_sanitizer_kcfi_enabled() - && tcx.is_closure_like(resolved.def_id()) => - { - // Reroute through a reify via the *unresolved* instance. The resolved one can't - // be directly reified because it's closure-like. The reify can handle the - // unresolved instance. - resolved = Instance { def: InstanceKind::ReifyShim(def_id, reason), args } + _ if tcx.sess.is_sanitizer_kcfi_enabled() => { + // Reify `::call`-like method implementations + if tcx.is_closure_like(resolved.def_id()) { + // Reroute through a reify via the *unresolved* instance. The resolved one can't + // be directly reified because it's closure-like. The reify can handle the + // unresolved instance. + resolved = Instance { def: InstanceKind::ReifyShim(def_id, reason), args } + // Reify `Trait::method` implementations + // FIXME(maurer) only reify it if it is a vtable-safe function + } else if let Some(assoc) = tcx.opt_associated_item(def_id) + && let AssocContainer::Trait | AssocContainer::TraitImpl(Ok(_)) = + assoc.container + { + // If this function could also go in a vtable, we need to `ReifyShim` it with + // KCFI because it can only attach one type per function. + resolved.def = InstanceKind::ReifyShim(resolved.def_id(), reason) + } } _ => {} } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index a34bf9b52970..d4c001f625e1 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1936,11 +1936,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Returns the trait item that is implemented by the given item `DefId`. pub fn trait_item_of(self, def_id: impl IntoQueryParam) -> Option { - let assoc = self.opt_associated_item(def_id.into_query_param())?; - if assoc.container != AssocContainer::Impl { - return None; - } - assoc.trait_item_def_id + self.opt_associated_item(def_id.into_query_param())?.trait_item_def_id() } /// If the given `DefId` is an associated item of a trait, @@ -2158,17 +2154,12 @@ impl<'tcx> TyCtxt<'tcx> { let Some(item) = self.opt_associated_item(def_id) else { return false; }; - if item.container != ty::AssocContainer::Impl { - return false; - } - let Some(trait_item_def_id) = item.trait_item_def_id else { + let AssocContainer::TraitImpl(Ok(trait_item_def_id)) = item.container else { return false; }; - return !self - .associated_types_for_impl_traits_in_associated_fn(trait_item_def_id) - .is_empty(); + !self.associated_types_for_impl_traits_in_associated_fn(trait_item_def_id).is_empty() } } diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 7e11b7046d6c..2ee1bd0dfd1e 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -21,8 +21,8 @@ use rustc_middle::middle::lib_features::{FeatureStability, LibFeatures}; use rustc_middle::middle::privacy::EffectiveVisibilities; use rustc_middle::middle::stability::{AllowUnstable, Deprecated, DeprecationEntry, EvalResult}; use rustc_middle::query::{LocalCrate, Providers}; -use rustc_middle::ty::TyCtxt; use rustc_middle::ty::print::with_no_trimmed_paths; +use rustc_middle::ty::{AssocContainer, TyCtxt}; use rustc_session::lint; use rustc_session::lint::builtin::{DEPRECATED, INEFFECTIVE_UNSTABLE_TRAIT_IMPL}; use rustc_span::{Span, Symbol, sym}; @@ -710,7 +710,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { for impl_item_ref in items { let impl_item = self.tcx.associated_item(impl_item_ref.owner_id); - if let Some(def_id) = impl_item.trait_item_def_id { + if let AssocContainer::TraitImpl(Ok(def_id)) = impl_item.container { // Pass `None` to skip deprecation warnings. self.tcx.check_stability( def_id, diff --git a/compiler/rustc_public/src/ty.rs b/compiler/rustc_public/src/ty.rs index c7422051c362..bcc77ff849da 100644 --- a/compiler/rustc_public/src/ty.rs +++ b/compiler/rustc_public/src/ty.rs @@ -1613,10 +1613,6 @@ pub struct AssocItem { pub def_id: AssocDef, pub kind: AssocKind, pub container: AssocContainer, - - /// If this is an item in an impl of a trait then this is the `DefId` of - /// the associated item on the trait that this implements. - pub trait_item_def_id: Option, } #[derive(Clone, PartialEq, Debug, Eq, Serialize)] @@ -1637,8 +1633,10 @@ pub enum AssocKind { #[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum AssocContainer { + InherentImpl, + /// The `AssocDef` points to the trait item being implemented. + TraitImpl(AssocDef), Trait, - Impl, } #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize)] diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index ace459e79dad..5131611eb02b 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -1079,11 +1079,18 @@ impl<'tcx> Stable<'tcx> for ty::AssocKind { impl<'tcx> Stable<'tcx> for ty::AssocContainer { type T = crate::ty::AssocContainer; - fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &CompilerCtxt<'_, BridgeTys>) -> Self::T { + fn stable( + &self, + tables: &mut Tables<'_, BridgeTys>, + _: &CompilerCtxt<'_, BridgeTys>, + ) -> Self::T { use crate::ty::AssocContainer; match self { ty::AssocContainer::Trait => AssocContainer::Trait, - ty::AssocContainer::Impl => AssocContainer::Impl, + ty::AssocContainer::InherentImpl => AssocContainer::InherentImpl, + ty::AssocContainer::TraitImpl(trait_item_id) => { + AssocContainer::TraitImpl(tables.assoc_def(trait_item_id.unwrap())) + } } } } @@ -1100,7 +1107,6 @@ impl<'tcx> Stable<'tcx> for ty::AssocItem { def_id: tables.assoc_def(self.def_id), kind: self.kind.stable(tables, cx), container: self.container.stable(tables, cx), - trait_item_def_id: self.trait_item_def_id.map(|did| tables.assoc_def(did)), } } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs index 9fe685b2786e..f997842a607f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs @@ -861,7 +861,7 @@ fn foo(&self) -> Self::T { String::new() } "associated type defaults can't be assumed inside the \ trait defining them" } - ty::AssocContainer::Impl => { + ty::AssocContainer::InherentImpl | ty::AssocContainer::TraitImpl(_) => { "associated type is `default` and may be overridden" } }; diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs index 7369134420c6..518d4fe17e8f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs @@ -571,13 +571,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // but right now it's not really very smart when it comes to implicit `Sized` // predicates and bounds on the trait itself. - let Some(impl_def_id) = self.tcx.associated_item(impl_item_def_id).impl_container(self.tcx) - else { - return; - }; - let Some(trait_ref) = self.tcx.impl_trait_ref(impl_def_id) else { + let Some(impl_def_id) = self.tcx.trait_impl_of_assoc(impl_item_def_id.to_def_id()) else { return; }; + let trait_ref = self.tcx.impl_trait_ref(impl_def_id).unwrap(); let trait_args = trait_ref .instantiate_identity() // Replace the explicit self type with `Self` for better suggestion rendering diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs index c04a8b3d860e..f54ebd76cab0 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs @@ -76,7 +76,8 @@ pub fn call_kind<'tcx>( let parent = tcx.opt_associated_item(method_did).and_then(|assoc| { let container_id = assoc.container_id(tcx); match assoc.container { - AssocContainer::Impl => tcx.trait_id_of_impl(container_id), + AssocContainer::InherentImpl => None, + AssocContainer::TraitImpl(_) => tcx.trait_id_of_impl(container_id), AssocContainer::Trait => Some(container_id), } }); diff --git a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs index caaf8c2585d3..4d0465777dd4 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs @@ -387,7 +387,7 @@ pub(crate) fn assoc_def( if let Some(assoc_item) = ancestors.leaf_def(tcx, assoc_def_id) { // Ensure that the impl is constrained, otherwise projection may give us // bad unconstrained infer vars. - if assoc_item.item.container == ty::AssocContainer::Impl + if let ty::AssocContainer::TraitImpl(_) = assoc_item.item.container && let Some(impl_def_id) = assoc_item.item.container_id(tcx).as_local() { tcx.ensure_ok().enforce_impl_non_lifetime_params_are_constrained(impl_def_id)?; diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs index 7d257273c0a6..84f52e7fc9d2 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -63,7 +63,7 @@ fn associated_items(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssocItems { fn impl_item_implementor_ids(tcx: TyCtxt<'_>, impl_id: DefId) -> DefIdMap { tcx.associated_items(impl_id) .in_definition_order() - .filter_map(|item| item.trait_item_def_id.map(|trait_item| (trait_item, item.def_id))) + .filter_map(|item| item.trait_item_def_id().map(|trait_item| (trait_item, item.def_id))) .collect() } @@ -97,12 +97,7 @@ fn associated_item_from_trait_item( } }; - ty::AssocItem { - kind, - def_id: owner_id.to_def_id(), - trait_item_def_id: Some(owner_id.to_def_id()), - container: ty::AssocContainer::Trait, - } + ty::AssocItem { kind, def_id: owner_id.to_def_id(), container: ty::AssocContainer::Trait } } fn associated_item_from_impl_item(tcx: TyCtxt<'_>, impl_item: &hir::ImplItem<'_>) -> ty::AssocItem { @@ -118,15 +113,13 @@ fn associated_item_from_impl_item(tcx: TyCtxt<'_>, impl_item: &hir::ImplItem<'_> } }; - ty::AssocItem { - kind, - def_id: owner_id.to_def_id(), - container: ty::AssocContainer::Impl, - trait_item_def_id: match impl_item.impl_kind { - ImplItemImplKind::Inherent { .. } => None, - ImplItemImplKind::Trait { trait_item_def_id, .. } => trait_item_def_id.ok(), - }, - } + let container = match impl_item.impl_kind { + ImplItemImplKind::Inherent { .. } => ty::AssocContainer::InherentImpl, + ImplItemImplKind::Trait { trait_item_def_id, .. } => { + ty::AssocContainer::TraitImpl(trait_item_def_id) + } + }; + ty::AssocItem { kind, def_id: owner_id.to_def_id(), container } } struct RPITVisitor<'a, 'tcx> { tcx: TyCtxt<'tcx>, @@ -262,7 +255,6 @@ fn associated_type_for_impl_trait_in_trait( }), }, def_id, - trait_item_def_id: None, container: ty::AssocContainer::Trait, }); @@ -328,8 +320,7 @@ fn associated_type_for_impl_trait_in_impl( }), }, def_id, - trait_item_def_id: Some(trait_assoc_def_id), - container: ty::AssocContainer::Impl, + container: ty::AssocContainer::TraitImpl(Ok(trait_assoc_def_id)), }); // Copy visility of the containing function. diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs index 4a7263d0ccd2..eb3236d30065 100644 --- a/compiler/rustc_ty_utils/src/opaque_types.rs +++ b/compiler/rustc_ty_utils/src/opaque_types.rs @@ -245,7 +245,7 @@ impl<'tcx> TypeVisitor> for OpaqueTypeCollector<'tcx> { for &assoc in self.tcx.associated_items(parent).in_definition_order() { trace!(?assoc); - if assoc.trait_item_def_id != Some(alias_ty.def_id) { + if assoc.expect_trait_impl() != Ok(alias_ty.def_id) { continue; } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index b98f174fbbfd..8c40e42562cf 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1295,11 +1295,13 @@ pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocCo simplify::move_bounds_to_generic_parameters(&mut generics); match assoc_item.container { - ty::AssocContainer::Impl => ImplAssocConstItem(Box::new(Constant { - generics, - kind: ConstantKind::Extern { def_id: assoc_item.def_id }, - type_: ty, - })), + ty::AssocContainer::InherentImpl | ty::AssocContainer::TraitImpl(_) => { + ImplAssocConstItem(Box::new(Constant { + generics, + kind: ConstantKind::Extern { def_id: assoc_item.def_id }, + type_: ty, + })) + } ty::AssocContainer::Trait => { if tcx.defaultness(assoc_item.def_id).has_value() { ProvidedAssocConstItem(Box::new(Constant { @@ -1318,7 +1320,7 @@ pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocCo if has_self { let self_ty = match assoc_item.container { - ty::AssocContainer::Impl => { + ty::AssocContainer::InherentImpl | ty::AssocContainer::TraitImpl(_) => { tcx.type_of(assoc_item.container_id(tcx)).instantiate_identity() } ty::AssocContainer::Trait => tcx.types.self_param, @@ -1338,13 +1340,13 @@ pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocCo } let provided = match assoc_item.container { - ty::AssocContainer::Impl => true, + ty::AssocContainer::InherentImpl | ty::AssocContainer::TraitImpl(_) => true, ty::AssocContainer::Trait => assoc_item.defaultness(tcx).has_value(), }; if provided { let defaultness = match assoc_item.container { - ty::AssocContainer::Impl => Some(assoc_item.defaultness(tcx)), - ty::AssocContainer::Trait => None, + ty::AssocContainer::TraitImpl(_) => Some(assoc_item.defaultness(tcx)), + ty::AssocContainer::InherentImpl | ty::AssocContainer::Trait => None, }; MethodItem(item, defaultness) } else { diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 84a5f239c8dd..bd3f4e9a6f2c 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -690,16 +690,13 @@ impl Item { // rustc's `is_const_fn` returns `true` for associated functions that have an `impl const` parent // or that have a `const trait` parent. Do not display those as `const` in rustdoc because we // won't be printing correct syntax plus the syntax is unstable. - match tcx.opt_associated_item(def_id) { - Some(ty::AssocItem { - container: ty::AssocContainer::Impl, - trait_item_def_id: Some(_), - .. - }) - | Some(ty::AssocItem { container: ty::AssocContainer::Trait, .. }) => { - hir::Constness::NotConst - } - None | Some(_) => hir::Constness::Const, + if let Some(assoc) = tcx.opt_associated_item(def_id) + && let ty::AssocContainer::Trait | ty::AssocContainer::TraitImpl(_) = + assoc.container + { + hir::Constness::NotConst + } else { + hir::Constness::Const } } else { hir::Constness::NotConst @@ -779,17 +776,13 @@ impl Item { | RequiredAssocTypeItem(..) | RequiredMethodItem(..) | MethodItem(..) => { - let assoc_item = tcx.associated_item(def_id); - let is_trait_item = match assoc_item.container { - ty::AssocContainer::Trait => true, - ty::AssocContainer::Impl => { - // Trait impl items always inherit the impl's visibility -- - // we don't want to show `pub`. - tcx.impl_trait_ref(tcx.parent(assoc_item.def_id)).is_some() + match tcx.associated_item(def_id).container { + // Trait impl items always inherit the impl's visibility -- + // we don't want to show `pub`. + ty::AssocContainer::Trait | ty::AssocContainer::TraitImpl(_) => { + return None; } - }; - if is_trait_item { - return None; + ty::AssocContainer::InherentImpl => {} } } _ => {} diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs index 7772051eb5c6..39b5964bd87b 100644 --- a/src/tools/clippy/clippy_lints/src/missing_doc.rs +++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs @@ -16,7 +16,7 @@ use rustc_hir::Attribute; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::Visibility; +use rustc_middle::ty::{AssocContainer, Visibility}; use rustc_session::impl_lint_pass; use rustc_span::def_id::CRATE_DEF_ID; use rustc_span::symbol::kw; @@ -246,12 +246,11 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { // If the method is an impl for a trait, don't doc. - if let Some(cid) = cx.tcx.associated_item(impl_item.owner_id).impl_container(cx.tcx) { - if cx.tcx.impl_trait_ref(cid).is_some() { + match cx.tcx.associated_item(impl_item.owner_id).container { + AssocContainer::Trait | AssocContainer::TraitImpl(_) => { note_prev_span_then_ret!(self.prev_span, impl_item.span); - } - } else { - note_prev_span_then_ret!(self.prev_span, impl_item.span); + }, + AssocContainer::InherentImpl => {} } let (article, desc) = cx.tcx.article_and_description(impl_item.owner_id.to_def_id()); diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs index 490a59ca66d3..6323e728666c 100644 --- a/src/tools/clippy/clippy_lints/src/missing_inline.rs +++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs @@ -167,7 +167,8 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { let container_id = assoc_item.container_id(cx.tcx); let trait_def_id = match assoc_item.container { AssocContainer::Trait => Some(container_id), - AssocContainer::Impl => cx.tcx.impl_trait_ref(container_id).map(|t| t.skip_binder().def_id), + AssocContainer::TraitImpl(_) => cx.tcx.impl_trait_ref(container_id).map(|t| t.skip_binder().def_id), + AssocContainer::InherentImpl => None, }; if let Some(trait_def_id) = trait_def_id diff --git a/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs b/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs index 9cc93bf06531..8e9400e9d583 100644 --- a/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs +++ b/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs @@ -70,7 +70,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingTraitMethods { .tcx .associated_items(item.owner_id) .in_definition_order() - .filter_map(|assoc_item| assoc_item.trait_item_def_id) + .filter_map(|assoc_item| assoc_item.expect_trait_impl().ok()) .collect(); for assoc in cx diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index b806e3d8b1bf..de45d6581662 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -3251,7 +3251,7 @@ pub fn get_path_from_caller_to_method_type<'tcx>( let def_id = assoc_item.container_id(tcx); match assoc_item.container { rustc_ty::AssocContainer::Trait => get_path_to_callee(tcx, from, def_id), - rustc_ty::AssocContainer::Impl => { + rustc_ty::AssocContainer::InherentImpl | rustc_ty::AssocContainer::TraitImpl(_) => { let ty = tcx.type_of(def_id).instantiate_identity(); get_path_to_ty(tcx, from, ty, args) }, diff --git a/tests/ui/const-generics/generic_const_exprs/post-analysis-user-facing-param-env.rs b/tests/ui/const-generics/generic_const_exprs/post-analysis-user-facing-param-env.rs index 478fa3706e8e..9af351ec59f0 100644 --- a/tests/ui/const-generics/generic_const_exprs/post-analysis-user-facing-param-env.rs +++ b/tests/ui/const-generics/generic_const_exprs/post-analysis-user-facing-param-env.rs @@ -11,7 +11,6 @@ where { fn unimplemented(self, _: &Foo) -> Self::Output { //~^ ERROR method `unimplemented` is not a member of trait `std::ops::Add` - //~| ERROR type annotations needed loop {} } } diff --git a/tests/ui/const-generics/generic_const_exprs/post-analysis-user-facing-param-env.stderr b/tests/ui/const-generics/generic_const_exprs/post-analysis-user-facing-param-env.stderr index 29bbd23a4699..37eb895f9a8d 100644 --- a/tests/ui/const-generics/generic_const_exprs/post-analysis-user-facing-param-env.stderr +++ b/tests/ui/const-generics/generic_const_exprs/post-analysis-user-facing-param-env.stderr @@ -3,7 +3,6 @@ error[E0407]: method `unimplemented` is not a member of trait `std::ops::Add` | LL | / fn unimplemented(self, _: &Foo) -> Self::Output { LL | | -LL | | LL | | loop {} LL | | } | |_____^ not a member of trait `std::ops::Add` @@ -39,21 +38,7 @@ LL | impl<'a, const NUM: usize> std::ops::Add<&'a Foo> for Foo = note: expressions using a const parameter must map each value to a distinct output value = note: proving the result of expressions other than the parameter are unique is not supported -error[E0284]: type annotations needed - --> $DIR/post-analysis-user-facing-param-env.rs:12:40 - | -LL | fn unimplemented(self, _: &Foo) -> Self::Output { - | ^^^^^^^^^^^^ cannot infer the value of const parameter `NUM` - | -note: required for `Foo` to implement `Add<&'a Foo>` - --> $DIR/post-analysis-user-facing-param-env.rs:6:28 - | -LL | impl<'a, const NUM: usize> std::ops::Add<&'a Foo> for Foo - | ---------------- ^^^^^^^^^^^^^^^^^^^^^^ ^^^ - | | - | unsatisfied trait bound introduced here +error: aborting due to 3 previous errors; 1 warning emitted -error: aborting due to 4 previous errors; 1 warning emitted - -Some errors have detailed explanations: E0046, E0207, E0284, E0407. +Some errors have detailed explanations: E0046, E0207, E0407. For more information about an error, try `rustc --explain E0046`. From d734981f14d934fd0ffa55ad6a74688227899c5e Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 5 Aug 2025 15:59:44 -0500 Subject: [PATCH 455/682] Introduce hir::ImplItemImplKind --- clippy_lints/src/disallowed_macros.rs | 6 +++-- clippy_lints/src/manual_async_fn.rs | 12 ++++++--- clippy_lints/src/min_ident_chars.rs | 6 ++--- clippy_lints/src/missing_const_for_fn.rs | 18 ++++++++++--- clippy_utils/src/check_proc_macro.rs | 32 ++++++++++++------------ 5 files changed, 45 insertions(+), 29 deletions(-) diff --git a/clippy_lints/src/disallowed_macros.rs b/clippy_lints/src/disallowed_macros.rs index 23e7c7251cf1..49cd2671dc00 100644 --- a/clippy_lints/src/disallowed_macros.rs +++ b/clippy_lints/src/disallowed_macros.rs @@ -8,7 +8,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefIdMap; use rustc_hir::{ - AmbigArg, Expr, ExprKind, ForeignItem, HirId, ImplItem, Item, ItemKind, OwnerId, Pat, Path, Stmt, TraitItem, Ty, + AmbigArg, Expr, ExprKind, ForeignItem, HirId, ImplItem, ImplItemImplKind, Item, ItemKind, OwnerId, Pat, Path, Stmt, TraitItem, Ty, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; @@ -177,7 +177,9 @@ impl LateLintPass<'_> for DisallowedMacros { fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &ImplItem<'_>) { self.check(cx, item.span, None); - self.check(cx, item.vis_span, None); + if let ImplItemImplKind::Inherent { vis_span, .. } = item.impl_kind { + self.check(cx, vis_span, None); + } } fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) { diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs index ba1ad599e116..bee3b19b597c 100644 --- a/clippy_lints/src/manual_async_fn.rs +++ b/clippy_lints/src/manual_async_fn.rs @@ -4,7 +4,7 @@ use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{ Block, Body, Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, FnDecl, - FnRetTy, GenericBound, ImplItem, Item, Node, OpaqueTy, TraitRef, Ty, TyKind, + FnRetTy, GenericBound, Node, OpaqueTy, TraitRef, Ty, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::middle::resolve_bound_vars::ResolvedArg; @@ -60,8 +60,11 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { && let ExprKind::Block(block, _) = body.value.kind && block.stmts.is_empty() && let Some(closure_body) = desugared_async_block(cx, block) - && let Node::Item(Item {vis_span, ..}) | Node::ImplItem(ImplItem {vis_span, ..}) = - cx.tcx.hir_node_by_def_id(fn_def_id) + && let Some(vis_span_opt) = match cx.tcx.hir_node_by_def_id(fn_def_id) { + Node::Item(item) => Some(Some(item.vis_span)), + Node::ImplItem(impl_item) => Some(impl_item.vis_span()), + _ => None, + } && !span.from_expansion() { let header_span = span.with_hi(ret_ty.span.hi()); @@ -72,7 +75,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { header_span, "this function can be simplified using the `async fn` syntax", |diag| { - if let Some(vis_snip) = vis_span.get_source_text(cx) + if let Some(vis_span) = vis_span_opt + && let Some(vis_snip) = vis_span.get_source_text(cx) && let Some(header_snip) = header_span.get_source_text(cx) && let Some(ret_pos) = position_before_rarrow(&header_snip) && let Some((_, ret_snip)) = suggested_ret(cx, output) diff --git a/clippy_lints/src/min_ident_chars.rs b/clippy_lints/src/min_ident_chars.rs index dbce29a86318..6258e408217f 100644 --- a/clippy_lints/src/min_ident_chars.rs +++ b/clippy_lints/src/min_ident_chars.rs @@ -5,8 +5,8 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{Visitor, walk_item, walk_trait_item}; use rustc_hir::{ - GenericParamKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, ItemLocalId, Node, Pat, PatKind, TraitItem, - UsePath, + GenericParamKind, HirId, Impl, ImplItem, ImplItemImplKind, ImplItemKind, Item, ItemKind, ItemLocalId, Node, Pat, + PatKind, TraitItem, UsePath, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; @@ -256,7 +256,7 @@ fn is_not_in_trait_impl(cx: &LateContext<'_>, pat: &Pat<'_>, ident: Ident) -> bo } fn get_param_name(impl_item: &ImplItem<'_>, cx: &LateContext<'_>, ident: Ident) -> Option { - if let Some(trait_item_def_id) = impl_item.trait_item_def_id { + if let ImplItemImplKind::Trait { trait_item_def_id: Ok(trait_item_def_id), .. } = impl_item.impl_kind { let trait_param_names = cx.tcx.fn_arg_idents(trait_item_def_id); let ImplItemKind::Fn(_, body_id) = impl_item.kind else { diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index a6be7581c9a3..a63ad9786262 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -158,13 +158,23 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { let mir = cx.tcx.optimized_mir(def_id); if let Ok(()) = is_min_const_fn(cx, mir, self.msrv) - && let hir::Node::Item(hir::Item { vis_span, .. }) | hir::Node::ImplItem(hir::ImplItem { vis_span, .. }) = - cx.tcx.hir_node_by_def_id(def_id) + && let node = cx.tcx.hir_node_by_def_id(def_id) + && let Some((item_span, vis_span_opt)) = match node { + hir::Node::Item(item) => Some((item.span, Some(item.vis_span))), + hir::Node::ImplItem(impl_item) => Some((impl_item.span, impl_item.vis_span())), + _ => None, + } { - let suggestion = if vis_span.is_empty() { "const " } else { " const" }; + let (sugg_span, suggestion) = if let Some(vis_span) = vis_span_opt + && !vis_span.is_empty() + { + (vis_span.shrink_to_hi(), " const") + } else { + (item_span.shrink_to_lo(), "const ") + }; span_lint_and_then(cx, MISSING_CONST_FOR_FN, span, "this could be a `const fn`", |diag| { diag.span_suggestion_verbose( - vis_span.shrink_to_hi(), + sugg_span, "make the function `const`", suggestion, Applicability::MachineApplicable, diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index c4a759e919b1..1a25c90d735e 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -19,7 +19,7 @@ use rustc_ast::token::CommentKind; use rustc_hir::intravisit::FnKind; use rustc_hir::{ Block, BlockCheckMode, Body, Closure, Destination, Expr, ExprKind, FieldDef, FnHeader, FnRetTy, HirId, Impl, - ImplItem, ImplItemKind, IsAuto, Item, ItemKind, Lit, LoopSource, MatchSource, MutTy, Node, Path, QPath, Safety, + ImplItem, ImplItemImplKind, ImplItemKind, IsAuto, Item, ItemKind, Lit, LoopSource, MatchSource, MutTy, Node, Path, QPath, Safety, TraitImplHeader, TraitItem, TraitItemKind, Ty, TyKind, UnOp, UnsafeSource, Variant, VariantData, YieldSource, }; use rustc_lint::{EarlyContext, LateContext, LintContext}; @@ -280,16 +280,17 @@ fn trait_item_search_pat(item: &TraitItem<'_>) -> (Pat, Pat) { } fn impl_item_search_pat(item: &ImplItem<'_>) -> (Pat, Pat) { - let (start_pat, end_pat) = match &item.kind { + let (mut start_pat, end_pat) = match &item.kind { ImplItemKind::Const(..) => (Pat::Str("const"), Pat::Str(";")), ImplItemKind::Type(..) => (Pat::Str("type"), Pat::Str(";")), ImplItemKind::Fn(sig, ..) => (fn_header_search_pat(sig.header), Pat::Str("")), }; - if item.vis_span.is_empty() { - (start_pat, end_pat) - } else { - (Pat::Str("pub"), end_pat) - } + if let ImplItemImplKind::Inherent { vis_span, .. } = item.impl_kind + && !vis_span.is_empty() + { + start_pat = Pat::Str("pub"); + }; + (start_pat, end_pat) } fn field_def_search_pat(def: &FieldDef<'_>) -> (Pat, Pat) { @@ -313,21 +314,20 @@ fn variant_search_pat(v: &Variant<'_>) -> (Pat, Pat) { } fn fn_kind_pat(tcx: TyCtxt<'_>, kind: &FnKind<'_>, body: &Body<'_>, hir_id: HirId) -> (Pat, Pat) { - let (start_pat, end_pat) = match kind { + let (mut start_pat, end_pat) = match kind { FnKind::ItemFn(.., header) => (fn_header_search_pat(*header), Pat::Str("")), FnKind::Method(.., sig) => (fn_header_search_pat(sig.header), Pat::Str("")), FnKind::Closure => return (Pat::Str(""), expr_search_pat(tcx, body.value).1), }; - let start_pat = match tcx.hir_node(hir_id) { - Node::Item(Item { vis_span, .. }) | Node::ImplItem(ImplItem { vis_span, .. }) => { - if vis_span.is_empty() { - start_pat - } else { - Pat::Str("pub") + match tcx.hir_node(hir_id) { + Node::Item(Item { vis_span, .. }) + | Node::ImplItem(ImplItem { impl_kind: ImplItemImplKind::Inherent { vis_span, .. }, .. }) => { + if !vis_span.is_empty() { + start_pat = Pat::Str("pub") } }, - Node::TraitItem(_) => start_pat, - _ => Pat::Str(""), + Node::ImplItem(_) | Node::TraitItem(_) => {}, + _ => start_pat = Pat::Str(""), }; (start_pat, end_pat) } From b995a55cafcbcafec51aba6c567b32161fa9c2f1 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 13 Aug 2025 14:36:13 -0500 Subject: [PATCH 456/682] Don't store defaultness for inherent impl items --- compiler/rustc_metadata/src/rmeta/encoder.rs | 5 ++++- compiler/rustc_middle/src/query/mod.rs | 1 + compiler/rustc_middle/src/ty/assoc.rs | 5 ++++- compiler/rustc_ty_utils/src/ty.rs | 4 ---- src/librustdoc/clean/mod.rs | 5 ++++- src/librustdoc/passes/collect_intra_doc_links.rs | 2 +- 6 files changed, 14 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index cb603705ce14..db66938457f0 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1725,7 +1725,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let tcx = self.tcx; let item = tcx.associated_item(def_id); - self.tables.defaultness.set_some(def_id.index, item.defaultness(tcx)); + if matches!(item.container, AssocContainer::Trait | AssocContainer::TraitImpl(_)) { + self.tables.defaultness.set_some(def_id.index, item.defaultness(tcx)); + } + record!(self.tables.assoc_container[def_id] <- item.container); if let AssocContainer::Trait = item.container diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 2aa32dfa0d82..0e645a3aae45 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1881,6 +1881,7 @@ rustc_queries! { } /// Returns whether the impl or associated function has the `default` keyword. + /// Note: This will ICE on inherent impl items. Consider using `AssocItem::defaultness`. query defaultness(def_id: DefId) -> hir::Defaultness { desc { |tcx| "looking up whether `{}` has `default`", tcx.def_path_str(def_id) } separate_provide_extern diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs index 60181783f9b1..768646c76302 100644 --- a/compiler/rustc_middle/src/ty/assoc.rs +++ b/compiler/rustc_middle/src/ty/assoc.rs @@ -53,7 +53,10 @@ impl AssocItem { /// /// [`type_of`]: crate::ty::TyCtxt::type_of pub fn defaultness(&self, tcx: TyCtxt<'_>) -> hir::Defaultness { - tcx.defaultness(self.def_id) + match self.container { + AssocContainer::InherentImpl => hir::Defaultness::Final, + AssocContainer::Trait | AssocContainer::TraitImpl(_) => tcx.defaultness(self.def_id), + } } pub fn expect_trait_impl(&self) -> Result { diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index b3f2bb6d4b5a..a5987757dc34 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -93,10 +93,6 @@ fn defaultness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Defaultness { .. }) | hir::Node::TraitItem(hir::TraitItem { defaultness, .. }) => *defaultness, - hir::Node::ImplItem(hir::ImplItem { - impl_kind: hir::ImplItemImplKind::Inherent { .. }, - .. - }) => hir::Defaultness::Final, node => { bug!("`defaultness` called on {:?}", node); } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 8c40e42562cf..5ccacafea015 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1256,7 +1256,10 @@ pub(crate) fn clean_impl_item<'tcx>( })), hir::ImplItemKind::Fn(ref sig, body) => { let m = clean_function(cx, sig, impl_.generics, ParamsSrc::Body(body)); - let defaultness = cx.tcx.defaultness(impl_.owner_id); + let defaultness = match impl_.impl_kind { + hir::ImplItemImplKind::Inherent { .. } => hir::Defaultness::Final, + hir::ImplItemImplKind::Trait { defaultness, .. } => defaultness, + }; MethodItem(m, Some(defaultness)) } hir::ImplItemKind::Type(hir_ty) => { diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 719b7c6ab89c..0da42f38251c 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -210,7 +210,7 @@ impl UrlFragment { &UrlFragment::Item(def_id) => { let kind = match tcx.def_kind(def_id) { DefKind::AssocFn => { - if tcx.defaultness(def_id).has_value() { + if tcx.associated_item(def_id).defaultness(tcx).has_value() { "method." } else { "tymethod." From 324c2141227b3e7d40b1250126396637bb7c530e Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 13 Aug 2025 15:24:19 -0500 Subject: [PATCH 457/682] Split AssocContainer::{InherentImpl,TraitImpl} --- clippy_lints/src/missing_doc.rs | 11 +++++------ clippy_lints/src/missing_inline.rs | 3 ++- clippy_lints/src/missing_trait_methods.rs | 2 +- clippy_utils/src/lib.rs | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index 7772051eb5c6..39b5964bd87b 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -16,7 +16,7 @@ use rustc_hir::Attribute; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::Visibility; +use rustc_middle::ty::{AssocContainer, Visibility}; use rustc_session::impl_lint_pass; use rustc_span::def_id::CRATE_DEF_ID; use rustc_span::symbol::kw; @@ -246,12 +246,11 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { // If the method is an impl for a trait, don't doc. - if let Some(cid) = cx.tcx.associated_item(impl_item.owner_id).impl_container(cx.tcx) { - if cx.tcx.impl_trait_ref(cid).is_some() { + match cx.tcx.associated_item(impl_item.owner_id).container { + AssocContainer::Trait | AssocContainer::TraitImpl(_) => { note_prev_span_then_ret!(self.prev_span, impl_item.span); - } - } else { - note_prev_span_then_ret!(self.prev_span, impl_item.span); + }, + AssocContainer::InherentImpl => {} } let (article, desc) = cx.tcx.article_and_description(impl_item.owner_id.to_def_id()); diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index 490a59ca66d3..6323e728666c 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -167,7 +167,8 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { let container_id = assoc_item.container_id(cx.tcx); let trait_def_id = match assoc_item.container { AssocContainer::Trait => Some(container_id), - AssocContainer::Impl => cx.tcx.impl_trait_ref(container_id).map(|t| t.skip_binder().def_id), + AssocContainer::TraitImpl(_) => cx.tcx.impl_trait_ref(container_id).map(|t| t.skip_binder().def_id), + AssocContainer::InherentImpl => None, }; if let Some(trait_def_id) = trait_def_id diff --git a/clippy_lints/src/missing_trait_methods.rs b/clippy_lints/src/missing_trait_methods.rs index 9cc93bf06531..8e9400e9d583 100644 --- a/clippy_lints/src/missing_trait_methods.rs +++ b/clippy_lints/src/missing_trait_methods.rs @@ -70,7 +70,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingTraitMethods { .tcx .associated_items(item.owner_id) .in_definition_order() - .filter_map(|assoc_item| assoc_item.trait_item_def_id) + .filter_map(|assoc_item| assoc_item.expect_trait_impl().ok()) .collect(); for assoc in cx diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index b806e3d8b1bf..de45d6581662 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -3251,7 +3251,7 @@ pub fn get_path_from_caller_to_method_type<'tcx>( let def_id = assoc_item.container_id(tcx); match assoc_item.container { rustc_ty::AssocContainer::Trait => get_path_to_callee(tcx, from, def_id), - rustc_ty::AssocContainer::Impl => { + rustc_ty::AssocContainer::InherentImpl | rustc_ty::AssocContainer::TraitImpl(_) => { let ty = tcx.type_of(def_id).instantiate_identity(); get_path_to_ty(tcx, from, ty, args) }, From 277e845162299d533ce64a2a6dfa540d4f36fb30 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Fri, 12 Sep 2025 11:36:38 -0700 Subject: [PATCH 458/682] rustdoc-search: delay loading type name IDs until type search This avoids blocking on these lookups, so name-based searches return results more quickly. --- src/librustdoc/html/static/js/rustdoc.d.ts | 25 +- src/librustdoc/html/static/js/search.js | 296 +++++++++++++++------ src/librustdoc/html/static/js/stringdex.js | 2 +- 3 files changed, 233 insertions(+), 90 deletions(-) diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts index 938ccc7d2c32..951eb2291b89 100644 --- a/src/librustdoc/html/static/js/rustdoc.d.ts +++ b/src/librustdoc/html/static/js/rustdoc.d.ts @@ -348,7 +348,7 @@ declare namespace rustdoc { returned: rustdoc.QueryElement[], is_alias: boolean, alias?: string, - original?: rustdoc.Rlow, + item: rustdoc.Row, } /** @@ -533,4 +533,27 @@ declare namespace rustdoc { * Generated by `render_call_locations` in `render/mod.rs`. */ type ScrapedLoc = [[number, number], string, string] + + /** + * Each of these identifiers are used specially by + * type-driven search. Most of them are lang items + * in the compiler. + */ + type TypeNameIds = { + "typeNameIdOfOutput": number, + "typeNameIdOfFnPtr": number, + "typeNameIdOfFn": number, + "typeNameIdOfFnMut": number, + "typeNameIdOfFnOnce": number, + "typeNameIdOfArray": number, + "typeNameIdOfSlice": number, + "typeNameIdOfArrayOrSlice": number, + "typeNameIdOfTuple": number, + "typeNameIdOfUnit": number, + "typeNameIdOfTupleOrUnit": number, + "typeNameIdOfReference": number, + "typeNameIdOfPointer": number, + "typeNameIdOfHof": number, + "typeNameIdOfNever": number, + }; } diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 339b101d4711..482134933a61 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -1190,17 +1190,6 @@ class DocSearch { this.rootPath = rootPath; this.database = database; - this.typeNameIdOfOutput = -1; - this.typeNameIdOfArray = -1; - this.typeNameIdOfSlice = -1; - this.typeNameIdOfArrayOrSlice = -1; - this.typeNameIdOfTuple = -1; - this.typeNameIdOfUnit = -1; - this.typeNameIdOfTupleOrUnit = -1; - this.typeNameIdOfReference = -1; - this.typeNameIdOfPointer = -1; - this.typeNameIdOfHof = -1; - this.utf8decoder = new TextDecoder(); /** @type {Map} */ @@ -1208,14 +1197,49 @@ class DocSearch { } /** - * Load search index. If you do not call this function, `execQuery` - * will never fulfill. + * Load type name ID set. + * + * Each of these identifiers are used specially by + * type-driven search. Most of them are lang items + * in the compiler. + * + * Use this function, which caches the result, and not + * getTypeNameIdsAsync, which is an internal implementation + * detail for this. + * + * @return {Promise|rustdoc.TypeNameIds} */ - async buildIndex() { + getTypeNameIds() { + if (this.typeNameIds) { + return this.typeNameIds; + } const nn = this.database.getData("normalizedName"); if (!nn) { - return; + return { + typeNameIdOfOutput: -1, + typeNameIdOfFnPtr: -1, + typeNameIdOfFn: -1, + typeNameIdOfFnMut: -1, + typeNameIdOfFnOnce: -1, + typeNameIdOfArray: -1, + typeNameIdOfSlice: -1, + typeNameIdOfArrayOrSlice: -1, + typeNameIdOfTuple: -1, + typeNameIdOfUnit: -1, + typeNameIdOfTupleOrUnit: -1, + typeNameIdOfReference: -1, + typeNameIdOfPointer: -1, + typeNameIdOfHof: -1, + typeNameIdOfNever: -1, + }; } + return this.getTypeNameIdsAsync(nn); + } + /** + * @param {stringdex.DataColumn} nn + * @returns {Promise} + */ + async getTypeNameIdsAsync(nn) { // Each of these identifiers are used specially by // type-driven search. const [ @@ -1274,21 +1298,39 @@ class DocSearch { } return -1; }; - this.typeNameIdOfOutput = await first(output, TY_ASSOCTYPE, ""); - this.typeNameIdOfFnPtr = await first(fn, TY_PRIMITIVE, ""); - this.typeNameIdOfFn = await first(fn, TY_TRAIT, "core::ops"); - this.typeNameIdOfFnMut = await first(fnMut, TY_TRAIT, "core::ops"); - this.typeNameIdOfFnOnce = await first(fnOnce, TY_TRAIT, "core::ops"); - this.typeNameIdOfArray = await first(array, TY_PRIMITIVE, ""); - this.typeNameIdOfSlice = await first(slice, TY_PRIMITIVE, ""); - this.typeNameIdOfArrayOrSlice = await first(arrayOrSlice, TY_PRIMITIVE, ""); - this.typeNameIdOfTuple = await first(tuple, TY_PRIMITIVE, ""); - this.typeNameIdOfUnit = await first(unit, TY_PRIMITIVE, ""); - this.typeNameIdOfTupleOrUnit = await first(tupleOrUnit, TY_PRIMITIVE, ""); - this.typeNameIdOfReference = await first(reference, TY_PRIMITIVE, ""); - this.typeNameIdOfPointer = await first(pointer, TY_PRIMITIVE, ""); - this.typeNameIdOfHof = await first(hof, TY_PRIMITIVE, ""); - this.typeNameIdOfNever = await first(never, TY_PRIMITIVE, ""); + const typeNameIdOfOutput = await first(output, TY_ASSOCTYPE, ""); + const typeNameIdOfFnPtr = await first(fn, TY_PRIMITIVE, ""); + const typeNameIdOfFn = await first(fn, TY_TRAIT, "core::ops"); + const typeNameIdOfFnMut = await first(fnMut, TY_TRAIT, "core::ops"); + const typeNameIdOfFnOnce = await first(fnOnce, TY_TRAIT, "core::ops"); + const typeNameIdOfArray = await first(array, TY_PRIMITIVE, ""); + const typeNameIdOfSlice = await first(slice, TY_PRIMITIVE, ""); + const typeNameIdOfArrayOrSlice = await first(arrayOrSlice, TY_PRIMITIVE, ""); + const typeNameIdOfTuple = await first(tuple, TY_PRIMITIVE, ""); + const typeNameIdOfUnit = await first(unit, TY_PRIMITIVE, ""); + const typeNameIdOfTupleOrUnit = await first(tupleOrUnit, TY_PRIMITIVE, ""); + const typeNameIdOfReference = await first(reference, TY_PRIMITIVE, ""); + const typeNameIdOfPointer = await first(pointer, TY_PRIMITIVE, ""); + const typeNameIdOfHof = await first(hof, TY_PRIMITIVE, ""); + const typeNameIdOfNever = await first(never, TY_PRIMITIVE, ""); + this.typeNameIds = { + typeNameIdOfOutput, + typeNameIdOfFnPtr, + typeNameIdOfFn, + typeNameIdOfFnMut, + typeNameIdOfFnOnce, + typeNameIdOfArray, + typeNameIdOfSlice, + typeNameIdOfArrayOrSlice, + typeNameIdOfTuple, + typeNameIdOfUnit, + typeNameIdOfTupleOrUnit, + typeNameIdOfReference, + typeNameIdOfPointer, + typeNameIdOfHof, + typeNameIdOfNever, + }; + return this.typeNameIds; } /** @@ -1816,6 +1858,9 @@ class DocSearch { modulePathData, exactModuleName, exactModulePathData, + parentName, + parentPath, + crate, ] = await Promise.all([ entry && entry.modulePath !== null ? this.getName(entry.modulePath) : null, entry && entry.modulePath !== null ? this.getPathData(entry.modulePath) : null, @@ -1825,6 +1870,13 @@ class DocSearch { entry && entry.exactModulePath !== null ? this.getPathData(entry.exactModulePath) : null, + entry && entry.parent !== null ? + this.getName(entry.parent) : + null, + entry && entry.parent !== null ? + this.getPathData(entry.parent) : + null, + entry ? nonnull(await this.getName(entry.krate)) : "", ]); const name = name_ === null ? "" : name_; const normalizedName = (name.indexOf("_") === -1 ? @@ -1834,12 +1886,9 @@ class DocSearch { (modulePathData.modulePath === "" ? moduleName : `${modulePathData.modulePath}::${moduleName}`); - const [parentName, parentPath] = entry !== null && entry.parent !== null ? - await Promise.all([this.getName(entry.parent), this.getPathData(entry.parent)]) : - [null, null]; return { id, - crate: entry ? nonnull(await this.getName(entry.krate)) : "", + crate, ty: entry ? entry.ty : nonnull(path).ty, name, normalizedName, @@ -2144,6 +2193,7 @@ class DocSearch { * @returns {Promise} */ const formatDisplayTypeSignature = async(obj, typeInfo, elems, returned) => { + const typeNameIds = await this.getTypeNameIds(); const objType = obj.type; if (!objType) { return {type: [], mappedNames: new Map(), whereClause: new Map()}; @@ -2169,10 +2219,12 @@ class DocSearch { return true; }, 0, + typeNameIds, ); return !!fnOutput; }, 0, + typeNameIds, ); } else { const highlighted = unifyFunctionTypes( @@ -2185,6 +2237,7 @@ class DocSearch { return true; }, 0, + typeNameIds, ); if (typeInfo === "elems") { fnInputs = highlighted; @@ -2264,7 +2317,7 @@ class DocSearch { * @returns {Promise} */ const writeHof = async(fnType, result) => { - const hofOutput = fnType.bindings.get(this.typeNameIdOfOutput) || []; + const hofOutput = fnType.bindings.get(typeNameIds.typeNameIdOfOutput) || []; const hofInputs = fnType.generics; pushText(fnType, result); pushText({name: " (", highlighted: false}, result); @@ -2305,11 +2358,14 @@ class DocSearch { * @returns {Promise} */ const writeSpecialPrimitive = async(fnType, result) => { - if (fnType.id === this.typeNameIdOfArray || fnType.id === this.typeNameIdOfSlice || - fnType.id === this.typeNameIdOfTuple || fnType.id === this.typeNameIdOfUnit) { + if (fnType.id === typeNameIds.typeNameIdOfArray || + fnType.id === typeNameIds.typeNameIdOfSlice || + fnType.id === typeNameIds.typeNameIdOfTuple || + fnType.id === typeNameIds.typeNameIdOfUnit + ) { const [ob, sb] = - fnType.id === this.typeNameIdOfArray || - fnType.id === this.typeNameIdOfSlice ? + fnType.id === typeNameIds.typeNameIdOfArray || + fnType.id === typeNameIds.typeNameIdOfSlice ? ["[", "]"] : ["(", ")"]; pushText({ name: ob, highlighted: fnType.highlighted }, result); @@ -2321,7 +2377,7 @@ class DocSearch { ); pushText({ name: sb, highlighted: fnType.highlighted }, result); return true; - } else if (fnType.id === this.typeNameIdOfReference) { + } else if (fnType.id === typeNameIds.typeNameIdOfReference) { pushText({ name: "&", highlighted: fnType.highlighted }, result); let prevHighlighted = false; await onEachBtwnAsync( @@ -2337,7 +2393,7 @@ class DocSearch { }, result), ); return true; - } else if (fnType.id === this.typeNameIdOfPointer) { + } else if (fnType.id === typeNameIds.typeNameIdOfPointer) { pushText({ name: "*", highlighted: fnType.highlighted }, result); if (fnType.generics.length < 2) { pushText({ name: "const ", highlighted: fnType.highlighted }, result); @@ -2357,14 +2413,14 @@ class DocSearch { ); return true; } else if ( - fnType.id === this.typeNameIdOfFn || - fnType.id === this.typeNameIdOfFnMut || - fnType.id === this.typeNameIdOfFnOnce || - fnType.id === this.typeNameIdOfFnPtr + fnType.id === typeNameIds.typeNameIdOfFn || + fnType.id === typeNameIds.typeNameIdOfFnMut || + fnType.id === typeNameIds.typeNameIdOfFnOnce || + fnType.id === typeNameIds.typeNameIdOfFnPtr ) { await writeHof(fnType, result); return true; - } else if (fnType.id === this.typeNameIdOfNever) { + } else if (fnType.id === typeNameIds.typeNameIdOfNever) { pushText({ name: "!", highlighted: fnType.highlighted }, result); return true; } @@ -2422,10 +2478,10 @@ class DocSearch { return; } } else if (fnType.ty === TY_TRAIT && ( - fnType.id === this.typeNameIdOfFn || - fnType.id === this.typeNameIdOfFnMut || - fnType.id === this.typeNameIdOfFnOnce || - fnType.id === this.typeNameIdOfFnPtr + fnType.id === typeNameIds.typeNameIdOfFn || + fnType.id === typeNameIds.typeNameIdOfFnMut || + fnType.id === typeNameIds.typeNameIdOfFnOnce || + fnType.id === typeNameIds.typeNameIdOfFnPtr )) { await writeHof(fnType, result); return; @@ -2536,7 +2592,7 @@ class DocSearch { * Add extra data to result objects, and filter items that have been * marked for removal. * - * @param {[rustdoc.PlainResultObject, rustdoc.Row][]} results + * @param {rustdoc.PlainResultObject[]} results * @param {"sig"|"elems"|"returned"|null} typeInfo * @param {Set} duplicates * @returns {rustdoc.ResultObject[]} @@ -2544,7 +2600,8 @@ class DocSearch { const transformResults = (results, typeInfo, duplicates) => { const out = []; - for (const [result, item] of results) { + for (const result of results) { + const item = result.item; if (item.id !== -1) { const res = buildHrefAndPath(item); // many of these properties don't strictly need to be @@ -2629,7 +2686,7 @@ class DocSearch { const normalizedUserQuery = parsedQuery.userQuery.toLowerCase(); const isMixedCase = normalizedUserQuery !== userQuery; /** - * @type {[rustdoc.PlainResultObject, rustdoc.Row][]} + * @type {rustdoc.PlainResultObject[]} */ const result_list = []; for (const result of results.values()) { @@ -2637,23 +2694,22 @@ class DocSearch { continue; } /** - * @type {rustdoc.Row?} + * @type {rustdoc.Row} */ - const item = await this.getRow(result.id, typeInfo !== null); - if (!item) { - continue; - } + const item = result.item; if (filterCrates !== null && item.crate !== filterCrates) { continue; } if (item) { - result_list.push([result, item]); + result_list.push(result); } else { continue; } } - result_list.sort(([aaa, aai], [bbb, bbi]) => { + result_list.sort((aaa, bbb) => { + const aai = aaa.item; + const bbi = bbb.item; /** @type {number} */ let a; /** @type {number} */ @@ -2800,6 +2856,7 @@ class DocSearch { * @param {number} unboxingDepth * - Limit checks that Ty matches Vec, * but not Vec>>>> + * @param {rustdoc.TypeNameIds} typeNameIds * * @return {rustdoc.HighlightedFunctionType[]|null} * - Returns highlighted results if a match, null otherwise. @@ -2811,6 +2868,7 @@ class DocSearch { mgensIn, solutionCb, unboxingDepth, + typeNameIds, ) { if (unboxingDepth >= UNBOXING_LIMIT) { return null; @@ -2833,7 +2891,12 @@ class DocSearch { && queryElems[0].bindings.size === 0) { const queryElem = queryElems[0]; for (const [i, fnType] of fnTypesIn.entries()) { - if (!unifyFunctionTypeIsMatchCandidate(fnType, queryElem, mgens)) { + if (!unifyFunctionTypeIsMatchCandidate( + fnType, + queryElem, + mgens, + typeNameIds, + )) { continue; } if (fnType.id !== null && @@ -2869,6 +2932,7 @@ class DocSearch { mgens ? new Map(mgens) : null, solutionCb, unboxingDepth, + typeNameIds, ) || fnType.generics, }); return highlighted; @@ -2881,6 +2945,7 @@ class DocSearch { whereClause, mgens, unboxingDepth + 1, + typeNameIds, )) { continue; } @@ -2894,6 +2959,7 @@ class DocSearch { mgens, solutionCb, unboxingDepth + 1, + typeNameIds, ); if (highlightedGenerics) { const highlighted = [...fnTypesIn]; @@ -2912,6 +2978,7 @@ class DocSearch { mgens ? new Map(mgens) : null, solutionCb, unboxingDepth + 1, + typeNameIds, ); if (highlightedGenerics) { const highlighted = [...fnTypesIn]; @@ -2956,7 +3023,12 @@ class DocSearch { let queryElemsTmp = null; for (let i = flast; i >= 0; i -= 1) { const fnType = fnTypes[i]; - if (!unifyFunctionTypeIsMatchCandidate(fnType, queryElem, mgens)) { + if (!unifyFunctionTypeIsMatchCandidate( + fnType, + queryElem, + mgens, + typeNameIds, + )) { continue; } let mgensScratch; @@ -3000,6 +3072,7 @@ class DocSearch { whereClause, mgensScratch, unboxingDepth, + typeNameIds, ); if (!solution) { return false; @@ -3013,6 +3086,7 @@ class DocSearch { simplifiedMgens, solutionCb, unboxingDepth, + typeNameIds, ); if (unifiedGenerics !== null) { unifiedGenericsMgens = simplifiedMgens; @@ -3022,6 +3096,7 @@ class DocSearch { return false; }, unboxingDepth, + typeNameIds, ); if (passesUnification) { passesUnification.length = fl; @@ -3038,6 +3113,7 @@ class DocSearch { unifiedGenericsMgens, solutionCb, unboxingDepth, + typeNameIds, // @ts-expect-error ) : unifiedGenerics.splice(0, v.length)]; })), @@ -3057,6 +3133,7 @@ class DocSearch { whereClause, mgens, unboxingDepth + 1, + typeNameIds, )) { continue; } @@ -3073,6 +3150,7 @@ class DocSearch { mgens, solutionCb, unboxingDepth + 1, + typeNameIds, ); if (passesUnification) { const highlightedGenerics = passesUnification.slice( @@ -3113,6 +3191,7 @@ class DocSearch { * @param {number} unboxingDepth * - Limit checks that Ty matches Vec, * but not Vec>>>> + * @param {rustdoc.TypeNameIds} typeNameIds * * @return {rustdoc.HighlightedFunctionType[]|null} * - Returns highlighted results if a match, null otherwise. @@ -3124,6 +3203,7 @@ class DocSearch { mgensIn, solutionCb, unboxingDepth, + typeNameIds, ) { if (unboxingDepth >= UNBOXING_LIMIT) { return null; @@ -3141,7 +3221,12 @@ class DocSearch { } const fnType = fnTypesIn[0]; const queryElem = queryElems[0]; - if (unifyFunctionTypeIsMatchCandidate(fnType, queryElem, mgens)) { + if (unifyFunctionTypeIsMatchCandidate( + fnType, + queryElem, + mgens, + typeNameIds, + )) { if (fnType.id !== null && fnType.id < 0 && queryElem.id !== null && @@ -3159,6 +3244,7 @@ class DocSearch { mgensScratch, solutionCb, unboxingDepth, + typeNameIds, ); if (fnTypesRemaining) { const highlighted = [fnType, ...fnTypesRemaining]; @@ -3185,6 +3271,7 @@ class DocSearch { whereClause, mgensScratch, unboxingDepth, + typeNameIds, ); if (!solution) { return false; @@ -3198,6 +3285,7 @@ class DocSearch { simplifiedMgens, solutionCb, unboxingDepth, + typeNameIds, ); if (unifiedGenerics !== null) { return true; @@ -3205,6 +3293,7 @@ class DocSearch { } }, unboxingDepth, + typeNameIds, ); if (fnTypesRemaining) { const highlighted = [fnType, ...fnTypesRemaining]; @@ -3223,6 +3312,7 @@ class DocSearch { whereClause, mgens, unboxingDepth + 1, + typeNameIds, )) { let highlightedRemaining; if (fnType.id !== null && fnType.id < 0) { @@ -3244,6 +3334,7 @@ class DocSearch { mgensScratch, solutionCb, unboxingDepth, + typeNameIds, ); if (hl) { highlightedRemaining = hl; @@ -3251,6 +3342,7 @@ class DocSearch { return hl; }, unboxingDepth + 1, + typeNameIds, ); if (highlightedGenerics) { return [Object.assign({ @@ -3278,6 +3370,7 @@ class DocSearch { mgensScratch, solutionCb, unboxingDepth, + typeNameIds, ); if (hl) { highlightedRemaining = hl; @@ -3285,6 +3378,7 @@ class DocSearch { return hl; }, unboxingDepth + 1, + typeNameIds, ); if (highlightedGenerics) { return [Object.assign({}, fnType, { @@ -3310,10 +3404,11 @@ class DocSearch { * * @param {rustdoc.FunctionType} fnType * @param {rustdoc.QueryElement} queryElem + * @param {rustdoc.TypeNameIds} typeNameIds * @param {Map|null} mgensIn - Map query generics to function generics. * @returns {boolean} */ - const unifyFunctionTypeIsMatchCandidate = (fnType, queryElem, mgensIn) => { + const unifyFunctionTypeIsMatchCandidate = (fnType, queryElem, mgensIn, typeNameIds) => { // type filters look like `trait:Read` or `enum:Result` if (!typePassesFilter(queryElem.typeFilter, fnType.ty)) { return false; @@ -3332,21 +3427,23 @@ class DocSearch { } else { // For these special cases, matching code need added to the inverted index. // search_index.rs -> convert_render_type does this - if (queryElem.id === this.typeNameIdOfArrayOrSlice && - (fnType.id === this.typeNameIdOfSlice || fnType.id === this.typeNameIdOfArray) + if (queryElem.id === typeNameIds.typeNameIdOfArrayOrSlice && + (fnType.id === typeNameIds.typeNameIdOfSlice || + fnType.id === typeNameIds.typeNameIdOfArray) ) { // [] matches primitive:array or primitive:slice // if it matches, then we're fine, and this is an appropriate match candidate - } else if (queryElem.id === this.typeNameIdOfTupleOrUnit && - (fnType.id === this.typeNameIdOfTuple || fnType.id === this.typeNameIdOfUnit) + } else if (queryElem.id === typeNameIds.typeNameIdOfTupleOrUnit && + (fnType.id === typeNameIds.typeNameIdOfTuple || + fnType.id === typeNameIds.typeNameIdOfUnit) ) { // () matches primitive:tuple or primitive:unit // if it matches, then we're fine, and this is an appropriate match candidate - } else if (queryElem.id === this.typeNameIdOfHof && ( - fnType.id === this.typeNameIdOfFn || - fnType.id === this.typeNameIdOfFnMut || - fnType.id === this.typeNameIdOfFnOnce || - fnType.id === this.typeNameIdOfFnPtr + } else if (queryElem.id === typeNameIds.typeNameIdOfHof && ( + fnType.id === typeNameIds.typeNameIdOfFn || + fnType.id === typeNameIds.typeNameIdOfFnMut || + fnType.id === typeNameIds.typeNameIdOfFnOnce || + fnType.id === typeNameIds.typeNameIdOfFnPtr )) { // -> matches fn, fnonce, and fnmut // if it matches, then we're fine, and this is an appropriate match candidate @@ -3410,6 +3507,7 @@ class DocSearch { * @param {Map|null} mgensIn - Map query generics to function generics. * Never modified. * @param {number} unboxingDepth + * @param {rustdoc.TypeNameIds} typeNameIds * @returns {false|{ * mgens: [Map|null], simplifiedGenerics: rustdoc.FunctionType[] * }} @@ -3420,6 +3518,7 @@ class DocSearch { whereClause, mgensIn, unboxingDepth, + typeNameIds, ) { if (fnType.bindings.size < queryElem.bindings.size) { return false; @@ -3451,6 +3550,7 @@ class DocSearch { return false; }, unboxingDepth, + typeNameIds, ); return newSolutions; }); @@ -3482,6 +3582,7 @@ class DocSearch { * @param {rustdoc.FunctionType[][]} whereClause - Trait bounds for generic items. * @param {Map|null} mgens - Map query generics to function generics. * @param {number} unboxingDepth + * @param {rustdoc.TypeNameIds} typeNameIds * @returns {boolean} */ function unifyFunctionTypeIsUnboxCandidate( @@ -3490,6 +3591,7 @@ class DocSearch { whereClause, mgens, unboxingDepth, + typeNameIds, ) { if (unboxingDepth >= UNBOXING_LIMIT) { return false; @@ -3508,6 +3610,7 @@ class DocSearch { whereClause, mgens, unboxingDepth, + typeNameIds, ); } else if (fnType.unboxFlag && (fnType.generics.length > 0 || fnType.bindings.size > 0)) { @@ -3521,6 +3624,7 @@ class DocSearch { whereClause, mgens, unboxingDepth, + typeNameIds, ); } return false; @@ -3533,14 +3637,15 @@ class DocSearch { * @param {rustdoc.QueryElement[]} elems * @param {rustdoc.FunctionType[]} list - A list of function types. * @param {rustdoc.FunctionType[][]} where_clause - Trait bounds for generic items. + * @param {rustdoc.TypeNameIds} typeNameIds */ - function containsTypeFromQuery(elems, list, where_clause) { + function containsTypeFromQuery(elems, list, where_clause, typeNameIds) { if (!list) return false; for (const ty of elems) { if (ty.id !== null && ty.id < 0) { continue; } - if (checkIfInList(list, ty, where_clause, null, 0)) { + if (checkIfInList(list, ty, where_clause, null, 0, typeNameIds)) { return true; } } @@ -3556,12 +3661,13 @@ class DocSearch { * @param {rustdoc.FunctionType[][]} whereClause - Trait bounds for generic items. * @param {Map|null} mgens - Map functions generics to query generics. * @param {number} unboxingDepth + * @param {rustdoc.TypeNameIds} typeNameIds * * @return {boolean} - Returns true if found, false otherwise. */ - function checkIfInList(list, elem, whereClause, mgens, unboxingDepth) { + function checkIfInList(list, elem, whereClause, mgens, unboxingDepth, typeNameIds) { for (const entry of list) { - if (checkType(entry, elem, whereClause, mgens, unboxingDepth)) { + if (checkType(entry, elem, whereClause, mgens, unboxingDepth, typeNameIds)) { return true; } } @@ -3576,11 +3682,12 @@ class DocSearch { * @param {rustdoc.QueryElement} elem - The element from the parsed query. * @param {rustdoc.FunctionType[][]} whereClause - Trait bounds for generic items. * @param {Map|null} mgens - Map query generics to function generics. + * @param {number} unboxingDepth + * @param {rustdoc.TypeNameIds} typeNameIds * * @return {boolean} - Returns true if the type matches, false otherwise. */ - // @ts-expect-error - const checkType = (row, elem, whereClause, mgens, unboxingDepth) => { + const checkType = (row, elem, whereClause, mgens, unboxingDepth, typeNameIds) => { if (unboxingDepth >= UNBOXING_LIMIT) { return false; } @@ -3589,9 +3696,9 @@ class DocSearch { row.generics.length === 0 && elem.generics.length === 0 && row.bindings.size === 0 && elem.bindings.size === 0 && // special case - elem.id !== this.typeNameIdOfArrayOrSlice && - elem.id !== this.typeNameIdOfHof && - elem.id !== this.typeNameIdOfTupleOrUnit + elem.id !== typeNameIds.typeNameIdOfArrayOrSlice && + elem.id !== typeNameIds.typeNameIdOfHof && + elem.id !== typeNameIds.typeNameIdOfTupleOrUnit ) { return row.id === elem.id && typePassesFilter(elem.typeFilter, row.ty); } else { @@ -3603,6 +3710,7 @@ class DocSearch { mgens, () => true, unboxingDepth, + typeNameIds, ); } }; @@ -3669,7 +3777,10 @@ class DocSearch { /** * Compute an "edit distance" that ignores missing path elements. * @param {string[]} contains search query path - * @param {rustdoc.Row} row indexed item + * @param {{ + * modulePath: string, + * parent: { path: rustdoc.PathData, name: string}?, + * }} row indexed item * @returns {null|number} edit distance */ function checkRowPath(contains, row) { @@ -3748,7 +3859,7 @@ class DocSearch { is_alias: true, elems: [], // only used in type-based queries returned: [], // only used in type-based queries - original: await this.getRow(alias, false), + item: nonnull(await this.getRow(alias, false)), }; }; /** @@ -3830,6 +3941,7 @@ class DocSearch { elems: [], // only used in type-based queries returned: [], // only used in type-based queries is_alias: false, + item: row, } : null; } else { return { @@ -3844,6 +3956,7 @@ class DocSearch { elems: [], // only used in type-based queries returned: [], // only used in type-based queries is_alias: false, + item: row, }; } }; @@ -4355,9 +4468,10 @@ class DocSearch { }; // finally, we can do the actual unification loop - const [allInputs, allOutput] = await Promise.all([ + const [allInputs, allOutput, typeNameIds] = await Promise.all([ unpackPostingsListAll(inputs, "invertedFunctionInputsIndex"), unpackPostingsListAll(output, "invertedFunctionOutputIndex"), + this.getTypeNameIds(), ]); let checkCounter = 0; /** @@ -4426,12 +4540,18 @@ class DocSearch { mgens, checkTypeMgensForConflict, 0, // unboxing depth + typeNameIds, ); }, 0, // unboxing depth + typeNameIds, )) { return null; } + const item = await this.getRow(id, true); + if (!item) { + return null; + } const result = { id, dist: fnData.elemCount, @@ -4440,8 +4560,9 @@ class DocSearch { elems: inputs, returned: output, is_alias: false, + item, }; - const entry = await this.getEntryData(id); + const entry = item.entry; if ((entry && !isFnLikeTy(entry.ty)) || (isReturnTypeQuery && functionSignature && @@ -4449,6 +4570,7 @@ class DocSearch { output, functionSignature.inputs, functionSignature.where_clause, + typeNameIds, ) ) ) { @@ -5269,7 +5391,6 @@ if (ROOT_PATH === null) { const database = await Stringdex.loadDatabase(hooks); if (typeof window !== "undefined") { docSearch = new DocSearch(ROOT_PATH, database); - await docSearch.buildIndex(); onEachLazy(document.querySelectorAll( ".search-form.loading", ), form => { @@ -5282,7 +5403,6 @@ if (typeof window !== "undefined") { } } else if (typeof exports !== "undefined") { docSearch = new DocSearch(ROOT_PATH, database); - await docSearch.buildIndex(); return { docSearch, DocSearch }; } }; diff --git a/src/librustdoc/html/static/js/stringdex.js b/src/librustdoc/html/static/js/stringdex.js index 4c4659628778..5bdc81e330ef 100644 --- a/src/librustdoc/html/static/js/stringdex.js +++ b/src/librustdoc/html/static/js/stringdex.js @@ -2310,7 +2310,7 @@ function loadDatabase(hooks) { } else { const start = this.bucket_keys[idx]; const bucket = this.buckets[idx]; - let data = this.buckets[idx].data; + const data = this.buckets[idx].data; if (data === null) { return this.atAsyncFetch(id, start, bucket); } else { From 382c964f3ea97d81f75c9716645dcedf081b4f5d Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 13 Sep 2025 00:28:46 +0200 Subject: [PATCH 459/682] small clean-up --- clippy_lints/src/lifetimes.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 149ae5e710c1..aac5d08fd37c 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -745,7 +745,7 @@ fn report_elidable_impl_lifetimes<'tcx>( impl_: &'tcx Impl<'_>, map: &FxIndexMap>, ) { - let single_usages = map + let (elidable_lts, usages): (Vec<_>, Vec<_>) = map .iter() .filter_map(|(def_id, usages)| { if let [ @@ -762,14 +762,12 @@ fn report_elidable_impl_lifetimes<'tcx>( None } }) - .collect::>(); + .unzip(); - if single_usages.is_empty() { + if elidable_lts.is_empty() { return; } - let (elidable_lts, usages): (Vec<_>, Vec<_>) = single_usages.into_iter().unzip(); - report_elidable_lifetimes(cx, impl_.generics, &elidable_lts, &usages, true); } @@ -795,9 +793,7 @@ fn report_elidable_lifetimes( // In principle, the result of the call to `Node::ident` could be `unwrap`ped, as `DefId` should refer to a // `Node::GenericParam`. .filter_map(|&def_id| cx.tcx.hir_node_by_def_id(def_id).ident()) - .map(|ident| ident.to_string()) - .collect::>() - .join(", "); + .format(", "); let elidable_usages: Vec = usages .iter() From ae57f08c1377b304b13fbb922438615d7692d65e Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Sat, 13 Sep 2025 04:52:19 +0000 Subject: [PATCH 460/682] Prepare for merging from rust-lang/rust This updates the rust-version file to 4ba1cf9ade4c8e2fa10676a50ee34594eb161837. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index b3f8a26875f4..e5f0be9ac955 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -2a9bacf6187685931d52346a0ecff2e52bdc91cc +4ba1cf9ade4c8e2fa10676a50ee34594eb161837 From 2baa39e89f59da3b7bb2228ab6e66999063a6101 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 13 Sep 2025 10:41:59 +0200 Subject: [PATCH 461/682] also apply revert to wasip2 --- library/std/src/sys/pal/wasip2/time.rs | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/library/std/src/sys/pal/wasip2/time.rs b/library/std/src/sys/pal/wasip2/time.rs index 980070e7b851..434891839944 100644 --- a/library/std/src/sys/pal/wasip2/time.rs +++ b/library/std/src/sys/pal/wasip2/time.rs @@ -36,34 +36,23 @@ impl SystemTime { SystemTime(Duration::new(now.seconds, now.nanoseconds)) } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn from_wasi_timestamp(ts: wasi::Timestamp) -> SystemTime { + pub fn from_wasi_timestamp(ts: wasi::Timestamp) -> SystemTime { SystemTime(Duration::from_nanos(ts)) } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn to_wasi_timestamp(&self) -> Option { - // FIXME: const TryInto - let ns = self.0.as_nanos(); - if ns <= u64::MAX as u128 { Some(ns as u64) } else { None } + pub fn to_wasi_timestamp(&self) -> Option { + self.0.as_nanos().try_into().ok() } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn sub_time(&self, other: &SystemTime) -> Result { - // FIXME: ok_or_else with const closures - match self.0.checked_sub(other.0) { - Some(duration) => Ok(duration), - None => Err(other.0 - self.0), - } + pub fn sub_time(&self, other: &SystemTime) -> Result { + self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0) } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn checked_add_duration(&self, other: &Duration) -> Option { + pub fn checked_add_duration(&self, other: &Duration) -> Option { Some(SystemTime(self.0.checked_add(*other)?)) } - #[rustc_const_unstable(feature = "const_system_time", issue = "144517")] - pub const fn checked_sub_duration(&self, other: &Duration) -> Option { + pub fn checked_sub_duration(&self, other: &Duration) -> Option { Some(SystemTime(self.0.checked_sub(*other)?)) } } From 25cf36ac78d1b88583f7a1125f9ddf67d8ef8eea Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 13 Sep 2025 01:25:03 +0200 Subject: [PATCH 462/682] clean-up - move tests to `ui-toml/`, since they all use `clippy.toml` - remove the trait test case from `ref_option.rs`, because it's already covered by `ref_option_traits.rs` --- clippy_lints/src/functions/ref_option.rs | 16 ++--- .../ref_option/all/clippy.toml | 0 .../ref_option/private/clippy.toml | 0 .../ref_option/ref_option.all.fixed | 22 ++---- .../ref_option/ref_option.all.stderr | 68 +++++-------------- .../ref_option/ref_option.private.fixed | 22 ++---- .../ref_option/ref_option.private.stderr | 44 ++++-------- .../{ui => ui-toml}/ref_option/ref_option.rs | 22 ++---- .../ref_option/ref_option_traits.all.stderr | 8 +-- .../ref_option_traits.private.stderr | 4 +- .../ref_option/ref_option_traits.rs | 4 +- 11 files changed, 58 insertions(+), 152 deletions(-) rename tests/{ui => ui-toml}/ref_option/all/clippy.toml (100%) rename tests/{ui => ui-toml}/ref_option/private/clippy.toml (100%) rename tests/{ui => ui-toml}/ref_option/ref_option.all.fixed (72%) rename tests/{ui => ui-toml}/ref_option/ref_option.all.stderr (62%) rename tests/{ui => ui-toml}/ref_option/ref_option.private.fixed (72%) rename tests/{ui => ui-toml}/ref_option/ref_option.private.stderr (63%) rename tests/{ui => ui-toml}/ref_option/ref_option.rs (72%) rename tests/{ui => ui-toml}/ref_option/ref_option_traits.all.stderr (85%) rename tests/{ui => ui-toml}/ref_option/ref_option_traits.private.stderr (86%) rename tests/{ui => ui-toml}/ref_option/ref_option_traits.rs (85%) diff --git a/clippy_lints/src/functions/ref_option.rs b/clippy_lints/src/functions/ref_option.rs index 106202d00d40..b4ae258ebbb3 100644 --- a/clippy_lints/src/functions/ref_option.rs +++ b/clippy_lints/src/functions/ref_option.rs @@ -2,22 +2,18 @@ use crate::functions::REF_OPTION; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_trait_impl_item; use clippy_utils::source::snippet; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::option_arg_ty; use rustc_errors::Applicability; -use rustc_hir as hir; use rustc_hir::intravisit::FnKind; -use rustc_hir::{FnDecl, HirId}; +use rustc_hir::{self as hir, FnDecl, HirId}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, GenericArgKind, Mutability, Ty}; +use rustc_middle::ty::{self, Mutability, Ty}; +use rustc_span::Span; use rustc_span::def_id::LocalDefId; -use rustc_span::{Span, sym}; -fn check_ty<'a>(cx: &LateContext<'a>, param: &rustc_hir::Ty<'a>, param_ty: Ty<'a>, fixes: &mut Vec<(Span, String)>) { +fn check_ty<'a>(cx: &LateContext<'a>, param: &hir::Ty<'a>, param_ty: Ty<'a>, fixes: &mut Vec<(Span, String)>) { if let ty::Ref(_, opt_ty, Mutability::Not) = param_ty.kind() - && is_type_diagnostic_item(cx, *opt_ty, sym::Option) - && let ty::Adt(_, opt_gen_args) = opt_ty.kind() - && let [gen_arg] = opt_gen_args.as_slice() - && let GenericArgKind::Type(gen_ty) = gen_arg.kind() + && let Some(gen_ty) = option_arg_ty(cx, *opt_ty) && !gen_ty.is_ref() // Need to gen the original spans, so first parsing mid, and hir parsing afterward && let hir::TyKind::Ref(lifetime, hir::MutTy { ty, .. }) = param.kind diff --git a/tests/ui/ref_option/all/clippy.toml b/tests/ui-toml/ref_option/all/clippy.toml similarity index 100% rename from tests/ui/ref_option/all/clippy.toml rename to tests/ui-toml/ref_option/all/clippy.toml diff --git a/tests/ui/ref_option/private/clippy.toml b/tests/ui-toml/ref_option/private/clippy.toml similarity index 100% rename from tests/ui/ref_option/private/clippy.toml rename to tests/ui-toml/ref_option/private/clippy.toml diff --git a/tests/ui/ref_option/ref_option.all.fixed b/tests/ui-toml/ref_option/ref_option.all.fixed similarity index 72% rename from tests/ui/ref_option/ref_option.all.fixed rename to tests/ui-toml/ref_option/ref_option.all.fixed index 4159e916f199..102d4aaefff4 100644 --- a/tests/ui/ref_option/ref_option.all.fixed +++ b/tests/ui-toml/ref_option/ref_option.all.fixed @@ -1,6 +1,6 @@ //@revisions: private all -//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/private -//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/all +//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/private +//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/all #![allow(unused, clippy::needless_lifetimes, clippy::borrowed_box)] #![warn(clippy::ref_option)] @@ -11,11 +11,11 @@ fn opt_gen(a: Option<&T>) {} //~^ ref_option fn opt_string(a: std::option::Option<&String>) {} //~^ ref_option -fn ret_string<'a>(p: &'a str) -> Option<&'a u8> { +fn ret_u8<'a>(p: &'a str) -> Option<&'a u8> { //~^ ref_option panic!() } -fn ret_string_static() -> Option<&'static u8> { +fn ret_u8_static() -> Option<&'static u8> { //~^ ref_option panic!() } @@ -31,20 +31,6 @@ pub fn pub_opt_string(a: Option<&String>) {} pub fn pub_mult_string(a: Option<&String>, b: Option<&Vec>) {} //~[all]^ ref_option -pub trait PubTrait { - fn pub_trait_opt(&self, a: Option<&Vec>); - //~[all]^ ref_option - fn pub_trait_ret(&self) -> Option<&Vec>; - //~[all]^ ref_option -} - -trait PrivateTrait { - fn trait_opt(&self, a: Option<&String>); - //~^ ref_option - fn trait_ret(&self) -> Option<&String>; - //~^ ref_option -} - pub struct PubStruct; impl PubStruct { diff --git a/tests/ui/ref_option/ref_option.all.stderr b/tests/ui-toml/ref_option/ref_option.all.stderr similarity index 62% rename from tests/ui/ref_option/ref_option.all.stderr rename to tests/ui-toml/ref_option/ref_option.all.stderr index bd43c28336eb..efd7a6b7241f 100644 --- a/tests/ui/ref_option/ref_option.all.stderr +++ b/tests/ui-toml/ref_option/ref_option.all.stderr @@ -1,5 +1,5 @@ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:8:1 + --> tests/ui-toml/ref_option/ref_option.rs:8:1 | LL | fn opt_u8(a: &Option) {} | ^^^^^^^^^^^^^-----------^^^^ @@ -10,7 +10,7 @@ LL | fn opt_u8(a: &Option) {} = help: to override `-D warnings` add `#[allow(clippy::ref_option)]` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:10:1 + --> tests/ui-toml/ref_option/ref_option.rs:10:1 | LL | fn opt_gen(a: &Option) {} | ^^^^^^^^^^^^^^^^^----------^^^^ @@ -18,7 +18,7 @@ LL | fn opt_gen(a: &Option) {} | help: change this to: `Option<&T>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:12:1 + --> tests/ui-toml/ref_option/ref_option.rs:12:1 | LL | fn opt_string(a: &std::option::Option) {} | ^^^^^^^^^^^^^^^^^----------------------------^^^^ @@ -26,10 +26,10 @@ LL | fn opt_string(a: &std::option::Option) {} | help: change this to: `std::option::Option<&String>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:14:1 + --> tests/ui-toml/ref_option/ref_option.rs:14:1 | -LL | fn ret_string<'a>(p: &'a str) -> &'a Option { - | ^ -------------- help: change this to: `Option<&'a u8>` +LL | fn ret_u8<'a>(p: &'a str) -> &'a Option { + | ^ -------------- help: change this to: `Option<&'a u8>` | _| | | LL | | @@ -38,10 +38,10 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:18:1 + --> tests/ui-toml/ref_option/ref_option.rs:18:1 | -LL | fn ret_string_static() -> &'static Option { - | ^ ------------------- help: change this to: `Option<&'static u8>` +LL | fn ret_u8_static() -> &'static Option { + | ^ ------------------- help: change this to: `Option<&'static u8>` | _| | | LL | | @@ -50,7 +50,7 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:22:1 + --> tests/ui-toml/ref_option/ref_option.rs:22:1 | LL | fn mult_string(a: &Option, b: &Option>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,7 +62,7 @@ LL + fn mult_string(a: Option<&String>, b: Option<&Vec>) {} | error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:24:1 + --> tests/ui-toml/ref_option/ref_option.rs:24:1 | LL | fn ret_box<'a>() -> &'a Option> { | ^ ------------------- help: change this to: `Option<&'a Box>` @@ -74,7 +74,7 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:29:1 + --> tests/ui-toml/ref_option/ref_option.rs:29:1 | LL | pub fn pub_opt_string(a: &Option) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^---------------^^^^ @@ -82,7 +82,7 @@ LL | pub fn pub_opt_string(a: &Option) {} | help: change this to: `Option<&String>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:31:1 + --> tests/ui-toml/ref_option/ref_option.rs:31:1 | LL | pub fn pub_mult_string(a: &Option, b: &Option>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -94,39 +94,7 @@ LL + pub fn pub_mult_string(a: Option<&String>, b: Option<&Vec>) {} | error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:35:5 - | -LL | fn pub_trait_opt(&self, a: &Option>); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------^^ - | | - | help: change this to: `Option<&Vec>` - -error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:37:5 - | -LL | fn pub_trait_ret(&self) -> &Option>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------^ - | | - | help: change this to: `Option<&Vec>` - -error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:42:5 - | -LL | fn trait_opt(&self, a: &Option); - | ^^^^^^^^^^^^^^^^^^^^^^^---------------^^ - | | - | help: change this to: `Option<&String>` - -error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:44:5 - | -LL | fn trait_ret(&self) -> &Option; - | ^^^^^^^^^^^^^^^^^^^^^^^---------------^ - | | - | help: change this to: `Option<&String>` - -error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:51:5 + --> tests/ui-toml/ref_option/ref_option.rs:37:5 | LL | pub fn pub_opt_params(&self, a: &Option<()>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^ @@ -134,7 +102,7 @@ LL | pub fn pub_opt_params(&self, a: &Option<()>) {} | help: change this to: `Option<&()>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:53:5 + --> tests/ui-toml/ref_option/ref_option.rs:39:5 | LL | pub fn pub_opt_ret(&self) -> &Option { | ^ --------------- help: change this to: `Option<&String>` @@ -146,7 +114,7 @@ LL | | } | |_____^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:58:5 + --> tests/ui-toml/ref_option/ref_option.rs:44:5 | LL | fn private_opt_params(&self, a: &Option<()>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^ @@ -154,7 +122,7 @@ LL | fn private_opt_params(&self, a: &Option<()>) {} | help: change this to: `Option<&()>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:60:5 + --> tests/ui-toml/ref_option/ref_option.rs:46:5 | LL | fn private_opt_ret(&self) -> &Option { | ^ --------------- help: change this to: `Option<&String>` @@ -165,5 +133,5 @@ LL | | panic!() LL | | } | |_____^ -error: aborting due to 17 previous errors +error: aborting due to 13 previous errors diff --git a/tests/ui/ref_option/ref_option.private.fixed b/tests/ui-toml/ref_option/ref_option.private.fixed similarity index 72% rename from tests/ui/ref_option/ref_option.private.fixed rename to tests/ui-toml/ref_option/ref_option.private.fixed index 3b158befb926..396188b6d407 100644 --- a/tests/ui/ref_option/ref_option.private.fixed +++ b/tests/ui-toml/ref_option/ref_option.private.fixed @@ -1,6 +1,6 @@ //@revisions: private all -//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/private -//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/all +//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/private +//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/all #![allow(unused, clippy::needless_lifetimes, clippy::borrowed_box)] #![warn(clippy::ref_option)] @@ -11,11 +11,11 @@ fn opt_gen(a: Option<&T>) {} //~^ ref_option fn opt_string(a: std::option::Option<&String>) {} //~^ ref_option -fn ret_string<'a>(p: &'a str) -> Option<&'a u8> { +fn ret_u8<'a>(p: &'a str) -> Option<&'a u8> { //~^ ref_option panic!() } -fn ret_string_static() -> Option<&'static u8> { +fn ret_u8_static() -> Option<&'static u8> { //~^ ref_option panic!() } @@ -31,20 +31,6 @@ pub fn pub_opt_string(a: &Option) {} pub fn pub_mult_string(a: &Option, b: &Option>) {} //~[all]^ ref_option -pub trait PubTrait { - fn pub_trait_opt(&self, a: &Option>); - //~[all]^ ref_option - fn pub_trait_ret(&self) -> &Option>; - //~[all]^ ref_option -} - -trait PrivateTrait { - fn trait_opt(&self, a: Option<&String>); - //~^ ref_option - fn trait_ret(&self) -> Option<&String>; - //~^ ref_option -} - pub struct PubStruct; impl PubStruct { diff --git a/tests/ui/ref_option/ref_option.private.stderr b/tests/ui-toml/ref_option/ref_option.private.stderr similarity index 63% rename from tests/ui/ref_option/ref_option.private.stderr rename to tests/ui-toml/ref_option/ref_option.private.stderr index 88c65e429d87..56f38c44f14c 100644 --- a/tests/ui/ref_option/ref_option.private.stderr +++ b/tests/ui-toml/ref_option/ref_option.private.stderr @@ -1,5 +1,5 @@ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:8:1 + --> tests/ui-toml/ref_option/ref_option.rs:8:1 | LL | fn opt_u8(a: &Option) {} | ^^^^^^^^^^^^^-----------^^^^ @@ -10,7 +10,7 @@ LL | fn opt_u8(a: &Option) {} = help: to override `-D warnings` add `#[allow(clippy::ref_option)]` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:10:1 + --> tests/ui-toml/ref_option/ref_option.rs:10:1 | LL | fn opt_gen(a: &Option) {} | ^^^^^^^^^^^^^^^^^----------^^^^ @@ -18,7 +18,7 @@ LL | fn opt_gen(a: &Option) {} | help: change this to: `Option<&T>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:12:1 + --> tests/ui-toml/ref_option/ref_option.rs:12:1 | LL | fn opt_string(a: &std::option::Option) {} | ^^^^^^^^^^^^^^^^^----------------------------^^^^ @@ -26,10 +26,10 @@ LL | fn opt_string(a: &std::option::Option) {} | help: change this to: `std::option::Option<&String>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:14:1 + --> tests/ui-toml/ref_option/ref_option.rs:14:1 | -LL | fn ret_string<'a>(p: &'a str) -> &'a Option { - | ^ -------------- help: change this to: `Option<&'a u8>` +LL | fn ret_u8<'a>(p: &'a str) -> &'a Option { + | ^ -------------- help: change this to: `Option<&'a u8>` | _| | | LL | | @@ -38,10 +38,10 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:18:1 + --> tests/ui-toml/ref_option/ref_option.rs:18:1 | -LL | fn ret_string_static() -> &'static Option { - | ^ ------------------- help: change this to: `Option<&'static u8>` +LL | fn ret_u8_static() -> &'static Option { + | ^ ------------------- help: change this to: `Option<&'static u8>` | _| | | LL | | @@ -50,7 +50,7 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:22:1 + --> tests/ui-toml/ref_option/ref_option.rs:22:1 | LL | fn mult_string(a: &Option, b: &Option>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,7 +62,7 @@ LL + fn mult_string(a: Option<&String>, b: Option<&Vec>) {} | error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:24:1 + --> tests/ui-toml/ref_option/ref_option.rs:24:1 | LL | fn ret_box<'a>() -> &'a Option> { | ^ ------------------- help: change this to: `Option<&'a Box>` @@ -74,23 +74,7 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:42:5 - | -LL | fn trait_opt(&self, a: &Option); - | ^^^^^^^^^^^^^^^^^^^^^^^---------------^^ - | | - | help: change this to: `Option<&String>` - -error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:44:5 - | -LL | fn trait_ret(&self) -> &Option; - | ^^^^^^^^^^^^^^^^^^^^^^^---------------^ - | | - | help: change this to: `Option<&String>` - -error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:58:5 + --> tests/ui-toml/ref_option/ref_option.rs:44:5 | LL | fn private_opt_params(&self, a: &Option<()>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^ @@ -98,7 +82,7 @@ LL | fn private_opt_params(&self, a: &Option<()>) {} | help: change this to: `Option<&()>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option.rs:60:5 + --> tests/ui-toml/ref_option/ref_option.rs:46:5 | LL | fn private_opt_ret(&self) -> &Option { | ^ --------------- help: change this to: `Option<&String>` @@ -109,5 +93,5 @@ LL | | panic!() LL | | } | |_____^ -error: aborting due to 11 previous errors +error: aborting due to 9 previous errors diff --git a/tests/ui/ref_option/ref_option.rs b/tests/ui-toml/ref_option/ref_option.rs similarity index 72% rename from tests/ui/ref_option/ref_option.rs rename to tests/ui-toml/ref_option/ref_option.rs index 35cd94174f8b..7600c58ea703 100644 --- a/tests/ui/ref_option/ref_option.rs +++ b/tests/ui-toml/ref_option/ref_option.rs @@ -1,6 +1,6 @@ //@revisions: private all -//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/private -//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/all +//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/private +//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/all #![allow(unused, clippy::needless_lifetimes, clippy::borrowed_box)] #![warn(clippy::ref_option)] @@ -11,11 +11,11 @@ fn opt_gen(a: &Option) {} //~^ ref_option fn opt_string(a: &std::option::Option) {} //~^ ref_option -fn ret_string<'a>(p: &'a str) -> &'a Option { +fn ret_u8<'a>(p: &'a str) -> &'a Option { //~^ ref_option panic!() } -fn ret_string_static() -> &'static Option { +fn ret_u8_static() -> &'static Option { //~^ ref_option panic!() } @@ -31,20 +31,6 @@ pub fn pub_opt_string(a: &Option) {} pub fn pub_mult_string(a: &Option, b: &Option>) {} //~[all]^ ref_option -pub trait PubTrait { - fn pub_trait_opt(&self, a: &Option>); - //~[all]^ ref_option - fn pub_trait_ret(&self) -> &Option>; - //~[all]^ ref_option -} - -trait PrivateTrait { - fn trait_opt(&self, a: &Option); - //~^ ref_option - fn trait_ret(&self) -> &Option; - //~^ ref_option -} - pub struct PubStruct; impl PubStruct { diff --git a/tests/ui/ref_option/ref_option_traits.all.stderr b/tests/ui-toml/ref_option/ref_option_traits.all.stderr similarity index 85% rename from tests/ui/ref_option/ref_option_traits.all.stderr rename to tests/ui-toml/ref_option/ref_option_traits.all.stderr index 886bf2b03498..61017eba176e 100644 --- a/tests/ui/ref_option/ref_option_traits.all.stderr +++ b/tests/ui-toml/ref_option/ref_option_traits.all.stderr @@ -1,5 +1,5 @@ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:9:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:9:5 | LL | fn pub_trait_opt(&self, a: &Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------^^ @@ -10,7 +10,7 @@ LL | fn pub_trait_opt(&self, a: &Option>); = help: to override `-D warnings` add `#[allow(clippy::ref_option)]` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:11:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:11:5 | LL | fn pub_trait_ret(&self) -> &Option>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------^ @@ -18,7 +18,7 @@ LL | fn pub_trait_ret(&self) -> &Option>; | help: change this to: `Option<&Vec>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:16:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:16:5 | LL | fn trait_opt(&self, a: &Option); | ^^^^^^^^^^^^^^^^^^^^^^^---------------^^ @@ -26,7 +26,7 @@ LL | fn trait_opt(&self, a: &Option); | help: change this to: `Option<&String>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:18:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:18:5 | LL | fn trait_ret(&self) -> &Option; | ^^^^^^^^^^^^^^^^^^^^^^^---------------^ diff --git a/tests/ui/ref_option/ref_option_traits.private.stderr b/tests/ui-toml/ref_option/ref_option_traits.private.stderr similarity index 86% rename from tests/ui/ref_option/ref_option_traits.private.stderr rename to tests/ui-toml/ref_option/ref_option_traits.private.stderr index cfab7fa5734c..d7b0b6e9f836 100644 --- a/tests/ui/ref_option/ref_option_traits.private.stderr +++ b/tests/ui-toml/ref_option/ref_option_traits.private.stderr @@ -1,5 +1,5 @@ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:16:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:16:5 | LL | fn trait_opt(&self, a: &Option); | ^^^^^^^^^^^^^^^^^^^^^^^---------------^^ @@ -10,7 +10,7 @@ LL | fn trait_opt(&self, a: &Option); = help: to override `-D warnings` add `#[allow(clippy::ref_option)]` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui/ref_option/ref_option_traits.rs:18:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:18:5 | LL | fn trait_ret(&self) -> &Option; | ^^^^^^^^^^^^^^^^^^^^^^^---------------^ diff --git a/tests/ui/ref_option/ref_option_traits.rs b/tests/ui-toml/ref_option/ref_option_traits.rs similarity index 85% rename from tests/ui/ref_option/ref_option_traits.rs rename to tests/ui-toml/ref_option/ref_option_traits.rs index 4c773e84f8da..c02d154b448f 100644 --- a/tests/ui/ref_option/ref_option_traits.rs +++ b/tests/ui-toml/ref_option/ref_option_traits.rs @@ -1,7 +1,7 @@ //@no-rustfix: fixes are only done to traits, not the impls //@revisions: private all -//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/private -//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/all +//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/private +//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/all #![warn(clippy::ref_option)] From b901ba725ebb33d3bb3ff4d8438db74a615d9e23 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 13 Sep 2025 01:24:18 +0200 Subject: [PATCH 463/682] fix(ref_option): don't lint in external and proc-macros --- clippy_lints/src/functions/ref_option.rs | 32 +++++++++--- tests/ui-toml/ref_option/ref_option.all.fixed | 49 +++++++++++++++++++ .../ui-toml/ref_option/ref_option.all.stderr | 26 +++++----- .../ref_option/ref_option.private.fixed | 49 +++++++++++++++++++ .../ref_option/ref_option.private.stderr | 18 +++---- tests/ui-toml/ref_option/ref_option.rs | 49 +++++++++++++++++++ .../ref_option/ref_option_traits.all.stderr | 8 +-- .../ref_option_traits.private.stderr | 4 +- tests/ui-toml/ref_option/ref_option_traits.rs | 31 ++++++++++++ 9 files changed, 230 insertions(+), 36 deletions(-) diff --git a/clippy_lints/src/functions/ref_option.rs b/clippy_lints/src/functions/ref_option.rs index b4ae258ebbb3..5dc1b7269b76 100644 --- a/clippy_lints/src/functions/ref_option.rs +++ b/clippy_lints/src/functions/ref_option.rs @@ -1,18 +1,19 @@ use crate::functions::REF_OPTION; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::is_trait_impl_item; use clippy_utils::source::snippet; use clippy_utils::ty::option_arg_ty; +use clippy_utils::{is_from_proc_macro, is_trait_impl_item}; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{self as hir, FnDecl, HirId}; -use rustc_lint::LateContext; +use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty::{self, Mutability, Ty}; use rustc_span::Span; use rustc_span::def_id::LocalDefId; fn check_ty<'a>(cx: &LateContext<'a>, param: &hir::Ty<'a>, param_ty: Ty<'a>, fixes: &mut Vec<(Span, String)>) { - if let ty::Ref(_, opt_ty, Mutability::Not) = param_ty.kind() + if !param.span.in_external_macro(cx.sess().source_map()) + && let ty::Ref(_, opt_ty, Mutability::Not) = param_ty.kind() && let Some(gen_ty) = option_arg_ty(cx, *opt_ty) && !gen_ty.is_ref() // Need to gen the original spans, so first parsing mid, and hir parsing afterward @@ -23,6 +24,7 @@ fn check_ty<'a>(cx: &LateContext<'a>, param: &hir::Ty<'a>, param_ty: Ty<'a>, fix args: [hir::GenericArg::Type(opt_ty)], .. }) = last.args + && !is_from_proc_macro(cx, param) { let lifetime = snippet(cx, lifetime.ident.span, ".."); fixes.push(( @@ -63,21 +65,24 @@ fn check_fn_sig<'a>(cx: &LateContext<'a>, decl: &FnDecl<'a>, span: Span, sig: ty #[allow(clippy::too_many_arguments)] pub(crate) fn check_fn<'a>( cx: &LateContext<'a>, - kind: FnKind<'_>, + kind: FnKind<'a>, decl: &FnDecl<'a>, span: Span, hir_id: HirId, def_id: LocalDefId, - body: &hir::Body<'_>, + body: &hir::Body<'a>, avoid_breaking_exported_api: bool, ) { if avoid_breaking_exported_api && cx.effective_visibilities.is_exported(def_id) { return; } + if span.in_external_macro(cx.sess().source_map()) { + return; + } if let FnKind::Closure = kind { // Compute the span of the closure parameters + return type if set - let span = if let hir::FnRetTy::Return(out_ty) = &decl.output { + let inputs_output_span = if let hir::FnRetTy::Return(out_ty) = &decl.output { if decl.inputs.is_empty() { out_ty.span } else { @@ -96,9 +101,18 @@ pub(crate) fn check_fn<'a>( }; let sig = args.as_closure().sig().skip_binder(); - check_fn_sig(cx, decl, span, sig); + if is_from_proc_macro(cx, &(&kind, body, hir_id, span)) { + return; + } + + check_fn_sig(cx, decl, inputs_output_span, sig); } else if !is_trait_impl_item(cx, hir_id) { let sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder(); + + if is_from_proc_macro(cx, &(&kind, body, hir_id, span)) { + return; + } + check_fn_sig(cx, decl, span, sig); } } @@ -108,8 +122,10 @@ pub(super) fn check_trait_item<'a>( trait_item: &hir::TraitItem<'a>, avoid_breaking_exported_api: bool, ) { - if let hir::TraitItemKind::Fn(ref sig, _) = trait_item.kind + if !trait_item.span.in_external_macro(cx.sess().source_map()) + && let hir::TraitItemKind::Fn(ref sig, _) = trait_item.kind && !(avoid_breaking_exported_api && cx.effective_visibilities.is_exported(trait_item.owner_id.def_id)) + && !is_from_proc_macro(cx, trait_item) { let def_id = trait_item.owner_id.def_id; let ty_sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder(); diff --git a/tests/ui-toml/ref_option/ref_option.all.fixed b/tests/ui-toml/ref_option/ref_option.all.fixed index 102d4aaefff4..f8f097e9a75e 100644 --- a/tests/ui-toml/ref_option/ref_option.all.fixed +++ b/tests/ui-toml/ref_option/ref_option.all.fixed @@ -1,3 +1,4 @@ +//@aux-build:../../ui/auxiliary/proc_macros.rs //@revisions: private all //@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/private //@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/all @@ -62,4 +63,52 @@ fn lambdas() { let x = |a: &Option| -> &Option { panic!() }; } +pub mod external { + proc_macros::external!( + fn opt_u8(a: &Option) {} + fn ret_u8<'a>(p: &'a str) -> &'a Option { + panic!() + } + pub fn pub_opt_u8(a: &Option) {} + + pub struct PubStruct; + impl PubStruct { + pub fn pub_opt_params(&self, a: &Option<()>) {} + pub fn pub_opt_ret(&self) -> &Option { + panic!() + } + + fn private_opt_params(&self, a: &Option<()>) {} + fn private_opt_ret(&self) -> &Option { + panic!() + } + } + ); +} + +pub mod proc_macros { + proc_macros::with_span!( + span + + fn opt_u8(a: &Option) {} + fn ret_u8<'a>(p: &'a str) -> &'a Option { + panic!() + } + pub fn pub_opt_u8(a: &Option) {} + + pub struct PubStruct; + impl PubStruct { + pub fn pub_opt_params(&self, a: &Option<()>) {} + pub fn pub_opt_ret(&self) -> &Option { + panic!() + } + + fn private_opt_params(&self, a: &Option<()>) {} + fn private_opt_ret(&self) -> &Option { + panic!() + } + } + ); +} + fn main() {} diff --git a/tests/ui-toml/ref_option/ref_option.all.stderr b/tests/ui-toml/ref_option/ref_option.all.stderr index efd7a6b7241f..45ce105e0308 100644 --- a/tests/ui-toml/ref_option/ref_option.all.stderr +++ b/tests/ui-toml/ref_option/ref_option.all.stderr @@ -1,5 +1,5 @@ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:8:1 + --> tests/ui-toml/ref_option/ref_option.rs:9:1 | LL | fn opt_u8(a: &Option) {} | ^^^^^^^^^^^^^-----------^^^^ @@ -10,7 +10,7 @@ LL | fn opt_u8(a: &Option) {} = help: to override `-D warnings` add `#[allow(clippy::ref_option)]` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:10:1 + --> tests/ui-toml/ref_option/ref_option.rs:11:1 | LL | fn opt_gen(a: &Option) {} | ^^^^^^^^^^^^^^^^^----------^^^^ @@ -18,7 +18,7 @@ LL | fn opt_gen(a: &Option) {} | help: change this to: `Option<&T>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:12:1 + --> tests/ui-toml/ref_option/ref_option.rs:13:1 | LL | fn opt_string(a: &std::option::Option) {} | ^^^^^^^^^^^^^^^^^----------------------------^^^^ @@ -26,7 +26,7 @@ LL | fn opt_string(a: &std::option::Option) {} | help: change this to: `std::option::Option<&String>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:14:1 + --> tests/ui-toml/ref_option/ref_option.rs:15:1 | LL | fn ret_u8<'a>(p: &'a str) -> &'a Option { | ^ -------------- help: change this to: `Option<&'a u8>` @@ -38,7 +38,7 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:18:1 + --> tests/ui-toml/ref_option/ref_option.rs:19:1 | LL | fn ret_u8_static() -> &'static Option { | ^ ------------------- help: change this to: `Option<&'static u8>` @@ -50,7 +50,7 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:22:1 + --> tests/ui-toml/ref_option/ref_option.rs:23:1 | LL | fn mult_string(a: &Option, b: &Option>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,7 +62,7 @@ LL + fn mult_string(a: Option<&String>, b: Option<&Vec>) {} | error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:24:1 + --> tests/ui-toml/ref_option/ref_option.rs:25:1 | LL | fn ret_box<'a>() -> &'a Option> { | ^ ------------------- help: change this to: `Option<&'a Box>` @@ -74,7 +74,7 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:29:1 + --> tests/ui-toml/ref_option/ref_option.rs:30:1 | LL | pub fn pub_opt_string(a: &Option) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^---------------^^^^ @@ -82,7 +82,7 @@ LL | pub fn pub_opt_string(a: &Option) {} | help: change this to: `Option<&String>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:31:1 + --> tests/ui-toml/ref_option/ref_option.rs:32:1 | LL | pub fn pub_mult_string(a: &Option, b: &Option>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -94,7 +94,7 @@ LL + pub fn pub_mult_string(a: Option<&String>, b: Option<&Vec>) {} | error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:37:5 + --> tests/ui-toml/ref_option/ref_option.rs:38:5 | LL | pub fn pub_opt_params(&self, a: &Option<()>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^ @@ -102,7 +102,7 @@ LL | pub fn pub_opt_params(&self, a: &Option<()>) {} | help: change this to: `Option<&()>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:39:5 + --> tests/ui-toml/ref_option/ref_option.rs:40:5 | LL | pub fn pub_opt_ret(&self) -> &Option { | ^ --------------- help: change this to: `Option<&String>` @@ -114,7 +114,7 @@ LL | | } | |_____^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:44:5 + --> tests/ui-toml/ref_option/ref_option.rs:45:5 | LL | fn private_opt_params(&self, a: &Option<()>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^ @@ -122,7 +122,7 @@ LL | fn private_opt_params(&self, a: &Option<()>) {} | help: change this to: `Option<&()>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:46:5 + --> tests/ui-toml/ref_option/ref_option.rs:47:5 | LL | fn private_opt_ret(&self) -> &Option { | ^ --------------- help: change this to: `Option<&String>` diff --git a/tests/ui-toml/ref_option/ref_option.private.fixed b/tests/ui-toml/ref_option/ref_option.private.fixed index 396188b6d407..4dd14a822067 100644 --- a/tests/ui-toml/ref_option/ref_option.private.fixed +++ b/tests/ui-toml/ref_option/ref_option.private.fixed @@ -1,3 +1,4 @@ +//@aux-build:../../ui/auxiliary/proc_macros.rs //@revisions: private all //@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/private //@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/all @@ -62,4 +63,52 @@ fn lambdas() { let x = |a: &Option| -> &Option { panic!() }; } +pub mod external { + proc_macros::external!( + fn opt_u8(a: &Option) {} + fn ret_u8<'a>(p: &'a str) -> &'a Option { + panic!() + } + pub fn pub_opt_u8(a: &Option) {} + + pub struct PubStruct; + impl PubStruct { + pub fn pub_opt_params(&self, a: &Option<()>) {} + pub fn pub_opt_ret(&self) -> &Option { + panic!() + } + + fn private_opt_params(&self, a: &Option<()>) {} + fn private_opt_ret(&self) -> &Option { + panic!() + } + } + ); +} + +pub mod proc_macros { + proc_macros::with_span!( + span + + fn opt_u8(a: &Option) {} + fn ret_u8<'a>(p: &'a str) -> &'a Option { + panic!() + } + pub fn pub_opt_u8(a: &Option) {} + + pub struct PubStruct; + impl PubStruct { + pub fn pub_opt_params(&self, a: &Option<()>) {} + pub fn pub_opt_ret(&self) -> &Option { + panic!() + } + + fn private_opt_params(&self, a: &Option<()>) {} + fn private_opt_ret(&self) -> &Option { + panic!() + } + } + ); +} + fn main() {} diff --git a/tests/ui-toml/ref_option/ref_option.private.stderr b/tests/ui-toml/ref_option/ref_option.private.stderr index 56f38c44f14c..a63efd60a036 100644 --- a/tests/ui-toml/ref_option/ref_option.private.stderr +++ b/tests/ui-toml/ref_option/ref_option.private.stderr @@ -1,5 +1,5 @@ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:8:1 + --> tests/ui-toml/ref_option/ref_option.rs:9:1 | LL | fn opt_u8(a: &Option) {} | ^^^^^^^^^^^^^-----------^^^^ @@ -10,7 +10,7 @@ LL | fn opt_u8(a: &Option) {} = help: to override `-D warnings` add `#[allow(clippy::ref_option)]` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:10:1 + --> tests/ui-toml/ref_option/ref_option.rs:11:1 | LL | fn opt_gen(a: &Option) {} | ^^^^^^^^^^^^^^^^^----------^^^^ @@ -18,7 +18,7 @@ LL | fn opt_gen(a: &Option) {} | help: change this to: `Option<&T>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:12:1 + --> tests/ui-toml/ref_option/ref_option.rs:13:1 | LL | fn opt_string(a: &std::option::Option) {} | ^^^^^^^^^^^^^^^^^----------------------------^^^^ @@ -26,7 +26,7 @@ LL | fn opt_string(a: &std::option::Option) {} | help: change this to: `std::option::Option<&String>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:14:1 + --> tests/ui-toml/ref_option/ref_option.rs:15:1 | LL | fn ret_u8<'a>(p: &'a str) -> &'a Option { | ^ -------------- help: change this to: `Option<&'a u8>` @@ -38,7 +38,7 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:18:1 + --> tests/ui-toml/ref_option/ref_option.rs:19:1 | LL | fn ret_u8_static() -> &'static Option { | ^ ------------------- help: change this to: `Option<&'static u8>` @@ -50,7 +50,7 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:22:1 + --> tests/ui-toml/ref_option/ref_option.rs:23:1 | LL | fn mult_string(a: &Option, b: &Option>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,7 +62,7 @@ LL + fn mult_string(a: Option<&String>, b: Option<&Vec>) {} | error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:24:1 + --> tests/ui-toml/ref_option/ref_option.rs:25:1 | LL | fn ret_box<'a>() -> &'a Option> { | ^ ------------------- help: change this to: `Option<&'a Box>` @@ -74,7 +74,7 @@ LL | | } | |_^ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:44:5 + --> tests/ui-toml/ref_option/ref_option.rs:45:5 | LL | fn private_opt_params(&self, a: &Option<()>) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^ @@ -82,7 +82,7 @@ LL | fn private_opt_params(&self, a: &Option<()>) {} | help: change this to: `Option<&()>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option.rs:46:5 + --> tests/ui-toml/ref_option/ref_option.rs:47:5 | LL | fn private_opt_ret(&self) -> &Option { | ^ --------------- help: change this to: `Option<&String>` diff --git a/tests/ui-toml/ref_option/ref_option.rs b/tests/ui-toml/ref_option/ref_option.rs index 7600c58ea703..8397c2213d17 100644 --- a/tests/ui-toml/ref_option/ref_option.rs +++ b/tests/ui-toml/ref_option/ref_option.rs @@ -1,3 +1,4 @@ +//@aux-build:../../ui/auxiliary/proc_macros.rs //@revisions: private all //@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/private //@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/all @@ -62,4 +63,52 @@ fn lambdas() { let x = |a: &Option| -> &Option { panic!() }; } +pub mod external { + proc_macros::external!( + fn opt_u8(a: &Option) {} + fn ret_u8<'a>(p: &'a str) -> &'a Option { + panic!() + } + pub fn pub_opt_u8(a: &Option) {} + + pub struct PubStruct; + impl PubStruct { + pub fn pub_opt_params(&self, a: &Option<()>) {} + pub fn pub_opt_ret(&self) -> &Option { + panic!() + } + + fn private_opt_params(&self, a: &Option<()>) {} + fn private_opt_ret(&self) -> &Option { + panic!() + } + } + ); +} + +pub mod proc_macros { + proc_macros::with_span!( + span + + fn opt_u8(a: &Option) {} + fn ret_u8<'a>(p: &'a str) -> &'a Option { + panic!() + } + pub fn pub_opt_u8(a: &Option) {} + + pub struct PubStruct; + impl PubStruct { + pub fn pub_opt_params(&self, a: &Option<()>) {} + pub fn pub_opt_ret(&self) -> &Option { + panic!() + } + + fn private_opt_params(&self, a: &Option<()>) {} + fn private_opt_ret(&self) -> &Option { + panic!() + } + } + ); +} + fn main() {} diff --git a/tests/ui-toml/ref_option/ref_option_traits.all.stderr b/tests/ui-toml/ref_option/ref_option_traits.all.stderr index 61017eba176e..602e148be601 100644 --- a/tests/ui-toml/ref_option/ref_option_traits.all.stderr +++ b/tests/ui-toml/ref_option/ref_option_traits.all.stderr @@ -1,5 +1,5 @@ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option_traits.rs:9:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:10:5 | LL | fn pub_trait_opt(&self, a: &Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------^^ @@ -10,7 +10,7 @@ LL | fn pub_trait_opt(&self, a: &Option>); = help: to override `-D warnings` add `#[allow(clippy::ref_option)]` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option_traits.rs:11:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:12:5 | LL | fn pub_trait_ret(&self) -> &Option>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------^ @@ -18,7 +18,7 @@ LL | fn pub_trait_ret(&self) -> &Option>; | help: change this to: `Option<&Vec>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option_traits.rs:16:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:17:5 | LL | fn trait_opt(&self, a: &Option); | ^^^^^^^^^^^^^^^^^^^^^^^---------------^^ @@ -26,7 +26,7 @@ LL | fn trait_opt(&self, a: &Option); | help: change this to: `Option<&String>` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option_traits.rs:18:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:19:5 | LL | fn trait_ret(&self) -> &Option; | ^^^^^^^^^^^^^^^^^^^^^^^---------------^ diff --git a/tests/ui-toml/ref_option/ref_option_traits.private.stderr b/tests/ui-toml/ref_option/ref_option_traits.private.stderr index d7b0b6e9f836..20bea400edfe 100644 --- a/tests/ui-toml/ref_option/ref_option_traits.private.stderr +++ b/tests/ui-toml/ref_option/ref_option_traits.private.stderr @@ -1,5 +1,5 @@ error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option_traits.rs:16:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:17:5 | LL | fn trait_opt(&self, a: &Option); | ^^^^^^^^^^^^^^^^^^^^^^^---------------^^ @@ -10,7 +10,7 @@ LL | fn trait_opt(&self, a: &Option); = help: to override `-D warnings` add `#[allow(clippy::ref_option)]` error: it is more idiomatic to use `Option<&T>` instead of `&Option` - --> tests/ui-toml/ref_option/ref_option_traits.rs:18:5 + --> tests/ui-toml/ref_option/ref_option_traits.rs:19:5 | LL | fn trait_ret(&self) -> &Option; | ^^^^^^^^^^^^^^^^^^^^^^^---------------^ diff --git a/tests/ui-toml/ref_option/ref_option_traits.rs b/tests/ui-toml/ref_option/ref_option_traits.rs index c02d154b448f..e1477d7f8463 100644 --- a/tests/ui-toml/ref_option/ref_option_traits.rs +++ b/tests/ui-toml/ref_option/ref_option_traits.rs @@ -1,4 +1,5 @@ //@no-rustfix: fixes are only done to traits, not the impls +//@aux-build:../../ui/auxiliary/proc_macros.rs //@revisions: private all //@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/private //@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/all @@ -37,4 +38,34 @@ impl PrivateTrait for PrivateStruct { } } +pub mod external { + proc_macros::external!( + pub trait PubTrait { + fn pub_trait_opt(&self, a: &Option>); + fn pub_trait_ret(&self) -> &Option>; + } + + trait PrivateTrait { + fn trait_opt(&self, a: &Option); + fn trait_ret(&self) -> &Option; + } + ); +} + +pub mod proc_macros { + proc_macros::with_span!( + span + + pub trait PubTrait { + fn pub_trait_opt(&self, a: &Option>); + fn pub_trait_ret(&self) -> &Option>; + } + + trait PrivateTrait { + fn trait_opt(&self, a: &Option); + fn trait_ret(&self) -> &Option; + } + ); +} + fn main() {} From af82c3db6c7259860ffdbe1dfd44b305b7796988 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 9 Sep 2025 16:39:51 +0200 Subject: [PATCH 464/682] tiny clean-up --- clippy_lints/src/returns.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index e0c93153a77a..a3e9e51bf12f 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -162,9 +162,9 @@ impl RetReplacement<'_> { impl Display for RetReplacement<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::Empty => write!(f, ""), - Self::Block => write!(f, "{{}}"), - Self::Unit => write!(f, "()"), + Self::Empty => f.write_str(""), + Self::Block => f.write_str("{}"), + Self::Unit => f.write_str("()"), Self::NeedsPar(inner, _) => write!(f, "({inner})"), Self::Expr(inner, _) => write!(f, "{inner}"), } @@ -228,7 +228,7 @@ impl<'tcx> LateLintPass<'tcx> for Return { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { // we need both a let-binding stmt and an expr if let Some(retexpr) = block.expr - && let Some(stmt) = block.stmts.iter().last() + && let Some(stmt) = block.stmts.last() && let StmtKind::Let(local) = &stmt.kind && local.ty.is_none() && cx.tcx.hir_attrs(local.hir_id).is_empty() @@ -315,7 +315,7 @@ fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>, if let ExprKind::Block(block, _) = expr_kind { if let Some(block_expr) = block.expr { check_final_expr(cx, block_expr, semi_spans, RetReplacement::Empty, None); - } else if let Some(stmt) = block.stmts.iter().last() { + } else if let Some(stmt) = block.stmts.last() { match stmt.kind { StmtKind::Expr(expr) => { check_final_expr(cx, expr, semi_spans, RetReplacement::Empty, None); From 51ee8d8f0ee821d8eae9c0b141f983413d6ad8d3 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 9 Sep 2025 16:01:00 +0200 Subject: [PATCH 465/682] split `returns` module --- clippy_lints/src/returns.rs | 513 ------------------ clippy_lints/src/returns/let_and_return.rs | 86 +++ clippy_lints/src/returns/mod.rs | 140 +++++ clippy_lints/src/returns/needless_return.rs | 260 +++++++++ .../needless_return_with_question_mark.rs | 60 ++ tests/ui/track-diagnostics-clippy.stderr | 2 +- 6 files changed, 547 insertions(+), 514 deletions(-) delete mode 100644 clippy_lints/src/returns.rs create mode 100644 clippy_lints/src/returns/let_and_return.rs create mode 100644 clippy_lints/src/returns/mod.rs create mode 100644 clippy_lints/src/returns/needless_return.rs create mode 100644 clippy_lints/src/returns/needless_return_with_question_mark.rs diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs deleted file mode 100644 index a3e9e51bf12f..000000000000 --- a/clippy_lints/src/returns.rs +++ /dev/null @@ -1,513 +0,0 @@ -use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; -use clippy_utils::source::{SpanRangeExt, snippet_with_context}; -use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::visitors::for_each_expr; -use clippy_utils::{ - binary_expr_needs_parentheses, fn_def_id, is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, - leaks_droppable_temporary_with_limited_lifetime, path_res, path_to_local_id, span_contains_cfg, - span_find_starting_semi, sym, -}; -use core::ops::ControlFlow; -use rustc_ast::MetaItemInner; -use rustc_errors::Applicability; -use rustc_hir::LangItem::ResultErr; -use rustc_hir::intravisit::FnKind; -use rustc_hir::{ - Block, Body, Expr, ExprKind, FnDecl, HirId, ItemKind, LangItem, MatchSource, Node, OwnerNode, PatKind, QPath, Stmt, - StmtKind, -}; -use rustc_lint::{LateContext, LateLintPass, Level, LintContext}; -use rustc_middle::ty::adjustment::Adjust; -use rustc_middle::ty::{self, GenericArgKind, Ty}; -use rustc_session::declare_lint_pass; -use rustc_span::def_id::LocalDefId; -use rustc_span::edition::Edition; -use rustc_span::{BytePos, Pos, Span}; -use std::borrow::Cow; -use std::fmt::Display; - -declare_clippy_lint! { - /// ### What it does - /// Checks for `let`-bindings, which are subsequently - /// returned. - /// - /// ### Why is this bad? - /// It is just extraneous code. Remove it to make your code - /// more rusty. - /// - /// ### Known problems - /// In the case of some temporaries, e.g. locks, eliding the variable binding could lead - /// to deadlocks. See [this issue](https://github.com/rust-lang/rust/issues/37612). - /// This could become relevant if the code is later changed to use the code that would have been - /// bound without first assigning it to a let-binding. - /// - /// ### Example - /// ```no_run - /// fn foo() -> String { - /// let x = String::new(); - /// x - /// } - /// ``` - /// instead, use - /// ```no_run - /// fn foo() -> String { - /// String::new() - /// } - /// ``` - #[clippy::version = "pre 1.29.0"] - pub LET_AND_RETURN, - style, - "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for return statements at the end of a block. - /// - /// ### Why is this bad? - /// Removing the `return` and semicolon will make the code - /// more rusty. - /// - /// ### Example - /// ```no_run - /// fn foo(x: usize) -> usize { - /// return x; - /// } - /// ``` - /// simplify to - /// ```no_run - /// fn foo(x: usize) -> usize { - /// x - /// } - /// ``` - #[clippy::version = "pre 1.29.0"] - pub NEEDLESS_RETURN, - // This lint requires some special handling in `check_final_expr` for `#[expect]`. - // This handling needs to be updated if the group gets changed. This should also - // be caught by tests. - style, - "using a return statement like `return expr;` where an expression would suffice" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for return statements on `Err` paired with the `?` operator. - /// - /// ### Why is this bad? - /// The `return` is unnecessary. - /// - /// Returns may be used to add attributes to the return expression. Return - /// statements with attributes are therefore be accepted by this lint. - /// - /// ### Example - /// ```rust,ignore - /// fn foo(x: usize) -> Result<(), Box> { - /// if x == 0 { - /// return Err(...)?; - /// } - /// Ok(()) - /// } - /// ``` - /// simplify to - /// ```rust,ignore - /// fn foo(x: usize) -> Result<(), Box> { - /// if x == 0 { - /// Err(...)?; - /// } - /// Ok(()) - /// } - /// ``` - /// if paired with `try_err`, use instead: - /// ```rust,ignore - /// fn foo(x: usize) -> Result<(), Box> { - /// if x == 0 { - /// return Err(...); - /// } - /// Ok(()) - /// } - /// ``` - #[clippy::version = "1.73.0"] - pub NEEDLESS_RETURN_WITH_QUESTION_MARK, - style, - "using a return statement like `return Err(expr)?;` where removing it would suffice" -} - -#[derive(PartialEq, Eq)] -enum RetReplacement<'tcx> { - Empty, - Block, - Unit, - NeedsPar(Cow<'tcx, str>, Applicability), - Expr(Cow<'tcx, str>, Applicability), -} - -impl RetReplacement<'_> { - fn sugg_help(&self) -> &'static str { - match self { - Self::Empty | Self::Expr(..) => "remove `return`", - Self::Block => "replace `return` with an empty block", - Self::Unit => "replace `return` with a unit value", - Self::NeedsPar(..) => "remove `return` and wrap the sequence with parentheses", - } - } - - fn applicability(&self) -> Applicability { - match self { - Self::Expr(_, ap) | Self::NeedsPar(_, ap) => *ap, - _ => Applicability::MachineApplicable, - } - } -} - -impl Display for RetReplacement<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Empty => f.write_str(""), - Self::Block => f.write_str("{}"), - Self::Unit => f.write_str("()"), - Self::NeedsPar(inner, _) => write!(f, "({inner})"), - Self::Expr(inner, _) => write!(f, "{inner}"), - } - } -} - -declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN, NEEDLESS_RETURN_WITH_QUESTION_MARK]); - -/// Checks if a return statement is "needed" in the middle of a block, or if it can be removed. This -/// is the case when the enclosing block expression is coerced to some other type, which only works -/// because of the never-ness of `return` expressions -fn stmt_needs_never_type(cx: &LateContext<'_>, stmt_hir_id: HirId) -> bool { - cx.tcx - .hir_parent_iter(stmt_hir_id) - .find_map(|(_, node)| if let Node::Expr(expr) = node { Some(expr) } else { None }) - .is_some_and(|e| { - cx.typeck_results() - .expr_adjustments(e) - .iter() - .any(|adjust| adjust.target != cx.tcx.types.unit && matches!(adjust.kind, Adjust::NeverToAny)) - }) -} - -impl<'tcx> LateLintPass<'tcx> for Return { - fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - if !stmt.span.in_external_macro(cx.sess().source_map()) - && let StmtKind::Semi(expr) = stmt.kind - && let ExprKind::Ret(Some(ret)) = expr.kind - // return Err(...)? desugars to a match - // over a Err(...).branch() - // which breaks down to a branch call, with the callee being - // the constructor of the Err variant - && let ExprKind::Match(maybe_cons, _, MatchSource::TryDesugar(_)) = ret.kind - && let ExprKind::Call(_, [maybe_result_err]) = maybe_cons.kind - && let ExprKind::Call(maybe_constr, _) = maybe_result_err.kind - && is_res_lang_ctor(cx, path_res(cx, maybe_constr), ResultErr) - - // Ensure this is not the final stmt, otherwise removing it would cause a compile error - && let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir_get_parent_item(expr.hir_id)) - && let ItemKind::Fn { body, .. } = item.kind - && let block = cx.tcx.hir_body(body).value - && let ExprKind::Block(block, _) = block.kind - && !is_inside_let_else(cx.tcx, expr) - && let [.., final_stmt] = block.stmts - && final_stmt.hir_id != stmt.hir_id - && !is_from_proc_macro(cx, expr) - && !stmt_needs_never_type(cx, stmt.hir_id) - { - span_lint_and_sugg( - cx, - NEEDLESS_RETURN_WITH_QUESTION_MARK, - expr.span.until(ret.span), - "unneeded `return` statement with `?` operator", - "remove it", - String::new(), - Applicability::MachineApplicable, - ); - } - } - - fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { - // we need both a let-binding stmt and an expr - if let Some(retexpr) = block.expr - && let Some(stmt) = block.stmts.last() - && let StmtKind::Let(local) = &stmt.kind - && local.ty.is_none() - && cx.tcx.hir_attrs(local.hir_id).is_empty() - && let Some(initexpr) = &local.init - && let PatKind::Binding(_, local_id, _, _) = local.pat.kind - && path_to_local_id(retexpr, local_id) - && (cx.sess().edition() >= Edition::Edition2024 || !last_statement_borrows(cx, initexpr)) - && !initexpr.span.in_external_macro(cx.sess().source_map()) - && !retexpr.span.in_external_macro(cx.sess().source_map()) - && !local.span.from_expansion() - && !span_contains_cfg(cx, stmt.span.between(retexpr.span)) - { - span_lint_hir_and_then( - cx, - LET_AND_RETURN, - retexpr.hir_id, - retexpr.span, - "returning the result of a `let` binding from a block", - |err| { - err.span_label(local.span, "unnecessary `let` binding"); - - if let Some(src) = initexpr.span.get_source_text(cx) { - let sugg = if binary_expr_needs_parentheses(initexpr) { - if has_enclosing_paren(&src) { - src.to_owned() - } else { - format!("({src})") - } - } else if !cx.typeck_results().expr_adjustments(retexpr).is_empty() { - if has_enclosing_paren(&src) { - format!("{src} as _") - } else { - format!("({src}) as _") - } - } else { - src.to_owned() - }; - err.multipart_suggestion( - "return the expression directly", - vec![(local.span, String::new()), (retexpr.span, sugg)], - Applicability::MachineApplicable, - ); - } else { - err.span_help(initexpr.span, "this expression can be directly returned"); - } - }, - ); - } - } - - fn check_fn( - &mut self, - cx: &LateContext<'tcx>, - kind: FnKind<'tcx>, - _: &'tcx FnDecl<'tcx>, - body: &'tcx Body<'tcx>, - sp: Span, - _: LocalDefId, - ) { - if sp.from_expansion() { - return; - } - - match kind { - FnKind::Closure => { - // when returning without value in closure, replace this `return` - // with an empty block to prevent invalid suggestion (see #6501) - let replacement = if let ExprKind::Ret(None) = &body.value.kind { - RetReplacement::Block - } else { - RetReplacement::Empty - }; - check_final_expr(cx, body.value, vec![], replacement, None); - }, - FnKind::ItemFn(..) | FnKind::Method(..) => { - check_block_return(cx, &body.value.kind, sp, vec![]); - }, - } - } -} - -// if `expr` is a block, check if there are needless returns in it -fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>, sp: Span, mut semi_spans: Vec) { - if let ExprKind::Block(block, _) = expr_kind { - if let Some(block_expr) = block.expr { - check_final_expr(cx, block_expr, semi_spans, RetReplacement::Empty, None); - } else if let Some(stmt) = block.stmts.last() { - match stmt.kind { - StmtKind::Expr(expr) => { - check_final_expr(cx, expr, semi_spans, RetReplacement::Empty, None); - }, - StmtKind::Semi(semi_expr) => { - // Remove ending semicolons and any whitespace ' ' in between. - // Without `return`, the suggestion might not compile if the semicolon is retained - if let Some(semi_span) = stmt.span.trim_start(semi_expr.span) { - let semi_span_to_remove = - span_find_starting_semi(cx.sess().source_map(), semi_span.with_hi(sp.hi())); - semi_spans.push(semi_span_to_remove); - } - check_final_expr(cx, semi_expr, semi_spans, RetReplacement::Empty, None); - }, - _ => (), - } - } - } -} - -fn check_final_expr<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'tcx>, - semi_spans: Vec, /* containing all the places where we would need to remove semicolons if finding an - * needless return */ - replacement: RetReplacement<'tcx>, - match_ty_opt: Option>, -) { - let peeled_drop_expr = expr.peel_drop_temps(); - match &peeled_drop_expr.kind { - // simple return is always "bad" - ExprKind::Ret(inner) => { - // check if expr return nothing - let ret_span = if inner.is_none() && replacement == RetReplacement::Empty { - extend_span_to_previous_non_ws(cx, peeled_drop_expr.span) - } else { - peeled_drop_expr.span - }; - - let replacement = if let Some(inner_expr) = inner { - // if desugar of `do yeet`, don't lint - if let ExprKind::Call(path_expr, [_]) = inner_expr.kind - && let ExprKind::Path(QPath::LangItem(LangItem::TryTraitFromYeet, ..)) = path_expr.kind - { - return; - } - - let mut applicability = Applicability::MachineApplicable; - let (snippet, _) = snippet_with_context(cx, inner_expr.span, ret_span.ctxt(), "..", &mut applicability); - if binary_expr_needs_parentheses(inner_expr) { - RetReplacement::NeedsPar(snippet, applicability) - } else { - RetReplacement::Expr(snippet, applicability) - } - } else { - match match_ty_opt { - Some(match_ty) => { - match match_ty.kind() { - // If the code got till here with - // tuple not getting detected before it, - // then we are sure it's going to be Unit - // type - ty::Tuple(_) => RetReplacement::Unit, - // We don't want to anything in this case - // cause we can't predict what the user would - // want here - _ => return, - } - }, - None => replacement, - } - }; - - if inner.is_some_and(|inner| leaks_droppable_temporary_with_limited_lifetime(cx, inner)) { - return; - } - - if ret_span.from_expansion() || is_from_proc_macro(cx, expr) { - return; - } - - // Returns may be used to turn an expression into a statement in rustc's AST. - // This allows the addition of attributes, like `#[allow]` (See: clippy#9361) - // `#[expect(clippy::needless_return)]` needs to be handled separately to - // actually fulfill the expectation (clippy::#12998) - match cx.tcx.hir_attrs(expr.hir_id) { - [] => {}, - [attr] => { - if matches!(Level::from_attr(attr), Some((Level::Expect, _))) - && let metas = attr.meta_item_list() - && let Some(lst) = metas - && let [MetaItemInner::MetaItem(meta_item), ..] = lst.as_slice() - && let [tool, lint_name] = meta_item.path.segments.as_slice() - && tool.ident.name == sym::clippy - && matches!( - lint_name.ident.name, - sym::needless_return | sym::style | sym::all | sym::warnings - ) - { - // This is an expectation of the `needless_return` lint - } else { - return; - } - }, - _ => return, - } - - emit_return_lint( - cx, - peeled_drop_expr.span, - ret_span, - semi_spans, - &replacement, - expr.hir_id, - ); - }, - ExprKind::If(_, then, else_clause_opt) => { - check_block_return(cx, &then.kind, peeled_drop_expr.span, semi_spans.clone()); - if let Some(else_clause) = else_clause_opt { - // The `RetReplacement` won't be used there as `else_clause` will be either a block or - // a `if` expression. - check_final_expr(cx, else_clause, semi_spans, RetReplacement::Empty, match_ty_opt); - } - }, - // a match expr, check all arms - // an if/if let expr, check both exprs - // note, if without else is going to be a type checking error anyways - // (except for unit type functions) so we don't match it - ExprKind::Match(_, arms, MatchSource::Normal) => { - let match_ty = cx.typeck_results().expr_ty(peeled_drop_expr); - for arm in *arms { - check_final_expr(cx, arm.body, semi_spans.clone(), RetReplacement::Unit, Some(match_ty)); - } - }, - // if it's a whole block, check it - other_expr_kind => check_block_return(cx, other_expr_kind, peeled_drop_expr.span, semi_spans), - } -} - -fn emit_return_lint( - cx: &LateContext<'_>, - lint_span: Span, - ret_span: Span, - semi_spans: Vec, - replacement: &RetReplacement<'_>, - at: HirId, -) { - span_lint_hir_and_then( - cx, - NEEDLESS_RETURN, - at, - lint_span, - "unneeded `return` statement", - |diag| { - let suggestions = std::iter::once((ret_span, replacement.to_string())) - .chain(semi_spans.into_iter().map(|span| (span, String::new()))) - .collect(); - - diag.multipart_suggestion_verbose(replacement.sugg_help(), suggestions, replacement.applicability()); - }, - ); -} - -fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { - for_each_expr(cx, expr, |e| { - if let Some(def_id) = fn_def_id(cx, e) - && cx - .tcx - .fn_sig(def_id) - .instantiate_identity() - .skip_binder() - .output() - .walk() - .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static())) - { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(()) - } - }) - .is_some() -} - -// Go backwards while encountering whitespace and extend the given Span to that point. -fn extend_span_to_previous_non_ws(cx: &LateContext<'_>, sp: Span) -> Span { - if let Ok(prev_source) = cx.sess().source_map().span_to_prev_source(sp) { - let ws = [b' ', b'\t', b'\n']; - if let Some(non_ws_pos) = prev_source.bytes().rposition(|c| !ws.contains(&c)) { - let len = prev_source.len() - non_ws_pos - 1; - return sp.with_lo(sp.lo() - BytePos::from_usize(len)); - } - } - - sp -} diff --git a/clippy_lints/src/returns/let_and_return.rs b/clippy_lints/src/returns/let_and_return.rs new file mode 100644 index 000000000000..e2002fb36e5a --- /dev/null +++ b/clippy_lints/src/returns/let_and_return.rs @@ -0,0 +1,86 @@ +use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::source::SpanRangeExt; +use clippy_utils::sugg::has_enclosing_paren; +use clippy_utils::visitors::for_each_expr; +use clippy_utils::{binary_expr_needs_parentheses, fn_def_id, path_to_local_id, span_contains_cfg}; +use core::ops::ControlFlow; +use rustc_errors::Applicability; +use rustc_hir::{Block, Expr, PatKind, StmtKind}; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::ty::GenericArgKind; +use rustc_span::edition::Edition; + +use super::LET_AND_RETURN; + +pub(super) fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { + // we need both a let-binding stmt and an expr + if let Some(retexpr) = block.expr + && let Some(stmt) = block.stmts.last() + && let StmtKind::Let(local) = &stmt.kind + && local.ty.is_none() + && cx.tcx.hir_attrs(local.hir_id).is_empty() + && let Some(initexpr) = &local.init + && let PatKind::Binding(_, local_id, _, _) = local.pat.kind + && path_to_local_id(retexpr, local_id) + && (cx.sess().edition() >= Edition::Edition2024 || !last_statement_borrows(cx, initexpr)) + && !initexpr.span.in_external_macro(cx.sess().source_map()) + && !retexpr.span.in_external_macro(cx.sess().source_map()) + && !local.span.from_expansion() + && !span_contains_cfg(cx, stmt.span.between(retexpr.span)) + { + span_lint_hir_and_then( + cx, + LET_AND_RETURN, + retexpr.hir_id, + retexpr.span, + "returning the result of a `let` binding from a block", + |err| { + err.span_label(local.span, "unnecessary `let` binding"); + + if let Some(src) = initexpr.span.get_source_text(cx) { + let sugg = if binary_expr_needs_parentheses(initexpr) { + if has_enclosing_paren(&src) { + src.to_owned() + } else { + format!("({src})") + } + } else if !cx.typeck_results().expr_adjustments(retexpr).is_empty() { + if has_enclosing_paren(&src) { + format!("{src} as _") + } else { + format!("({src}) as _") + } + } else { + src.to_owned() + }; + err.multipart_suggestion( + "return the expression directly", + vec![(local.span, String::new()), (retexpr.span, sugg)], + Applicability::MachineApplicable, + ); + } else { + err.span_help(initexpr.span, "this expression can be directly returned"); + } + }, + ); + } +} +fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + for_each_expr(cx, expr, |e| { + if let Some(def_id) = fn_def_id(cx, e) + && cx + .tcx + .fn_sig(def_id) + .instantiate_identity() + .skip_binder() + .output() + .walk() + .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static())) + { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + }) + .is_some() +} diff --git a/clippy_lints/src/returns/mod.rs b/clippy_lints/src/returns/mod.rs new file mode 100644 index 000000000000..47c6332b9b81 --- /dev/null +++ b/clippy_lints/src/returns/mod.rs @@ -0,0 +1,140 @@ +use rustc_hir::intravisit::FnKind; +use rustc_hir::{Block, Body, FnDecl, Stmt}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; +use rustc_span::Span; +use rustc_span::def_id::LocalDefId; + +mod let_and_return; +mod needless_return; +mod needless_return_with_question_mark; + +declare_clippy_lint! { + /// ### What it does + /// Checks for `let`-bindings, which are subsequently + /// returned. + /// + /// ### Why is this bad? + /// It is just extraneous code. Remove it to make your code + /// more rusty. + /// + /// ### Known problems + /// In the case of some temporaries, e.g. locks, eliding the variable binding could lead + /// to deadlocks. See [this issue](https://github.com/rust-lang/rust/issues/37612). + /// This could become relevant if the code is later changed to use the code that would have been + /// bound without first assigning it to a let-binding. + /// + /// ### Example + /// ```no_run + /// fn foo() -> String { + /// let x = String::new(); + /// x + /// } + /// ``` + /// instead, use + /// ```no_run + /// fn foo() -> String { + /// String::new() + /// } + /// ``` + #[clippy::version = "pre 1.29.0"] + pub LET_AND_RETURN, + style, + "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for return statements at the end of a block. + /// + /// ### Why is this bad? + /// Removing the `return` and semicolon will make the code + /// more rusty. + /// + /// ### Example + /// ```no_run + /// fn foo(x: usize) -> usize { + /// return x; + /// } + /// ``` + /// simplify to + /// ```no_run + /// fn foo(x: usize) -> usize { + /// x + /// } + /// ``` + #[clippy::version = "pre 1.29.0"] + pub NEEDLESS_RETURN, + // This lint requires some special handling in `check_final_expr` for `#[expect]`. + // This handling needs to be updated if the group gets changed. This should also + // be caught by tests. + style, + "using a return statement like `return expr;` where an expression would suffice" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for return statements on `Err` paired with the `?` operator. + /// + /// ### Why is this bad? + /// The `return` is unnecessary. + /// + /// Returns may be used to add attributes to the return expression. Return + /// statements with attributes are therefore be accepted by this lint. + /// + /// ### Example + /// ```rust,ignore + /// fn foo(x: usize) -> Result<(), Box> { + /// if x == 0 { + /// return Err(...)?; + /// } + /// Ok(()) + /// } + /// ``` + /// simplify to + /// ```rust,ignore + /// fn foo(x: usize) -> Result<(), Box> { + /// if x == 0 { + /// Err(...)?; + /// } + /// Ok(()) + /// } + /// ``` + /// if paired with `try_err`, use instead: + /// ```rust,ignore + /// fn foo(x: usize) -> Result<(), Box> { + /// if x == 0 { + /// return Err(...); + /// } + /// Ok(()) + /// } + /// ``` + #[clippy::version = "1.73.0"] + pub NEEDLESS_RETURN_WITH_QUESTION_MARK, + style, + "using a return statement like `return Err(expr)?;` where removing it would suffice" +} + +declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN, NEEDLESS_RETURN_WITH_QUESTION_MARK]); + +impl<'tcx> LateLintPass<'tcx> for Return { + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + needless_return_with_question_mark::check_stmt(cx, stmt); + } + + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { + let_and_return::check_block(cx, block); + } + + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + kind: FnKind<'tcx>, + _: &'tcx FnDecl<'tcx>, + body: &'tcx Body<'tcx>, + sp: Span, + _: LocalDefId, + ) { + needless_return::check_fn(cx, kind, body, sp); + } +} diff --git a/clippy_lints/src/returns/needless_return.rs b/clippy_lints/src/returns/needless_return.rs new file mode 100644 index 000000000000..0a1ba7b5beca --- /dev/null +++ b/clippy_lints/src/returns/needless_return.rs @@ -0,0 +1,260 @@ +use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::source::snippet_with_context; +use clippy_utils::{ + binary_expr_needs_parentheses, is_from_proc_macro, leaks_droppable_temporary_with_limited_lifetime, + span_find_starting_semi, sym, +}; +use rustc_ast::MetaItemInner; +use rustc_errors::Applicability; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{Body, Expr, ExprKind, HirId, LangItem, MatchSource, QPath, StmtKind}; +use rustc_lint::{LateContext, Level, LintContext}; +use rustc_middle::ty::{self, Ty}; +use rustc_span::{BytePos, Pos, Span}; +use std::borrow::Cow; +use std::fmt::Display; + +use super::NEEDLESS_RETURN; + +#[derive(PartialEq, Eq)] +enum RetReplacement<'tcx> { + Empty, + Block, + Unit, + NeedsPar(Cow<'tcx, str>, Applicability), + Expr(Cow<'tcx, str>, Applicability), +} + +impl RetReplacement<'_> { + fn sugg_help(&self) -> &'static str { + match self { + Self::Empty | Self::Expr(..) => "remove `return`", + Self::Block => "replace `return` with an empty block", + Self::Unit => "replace `return` with a unit value", + Self::NeedsPar(..) => "remove `return` and wrap the sequence with parentheses", + } + } + + fn applicability(&self) -> Applicability { + match self { + Self::Expr(_, ap) | Self::NeedsPar(_, ap) => *ap, + _ => Applicability::MachineApplicable, + } + } +} + +impl Display for RetReplacement<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Empty => f.write_str(""), + Self::Block => f.write_str("{}"), + Self::Unit => f.write_str("()"), + Self::NeedsPar(inner, _) => write!(f, "({inner})"), + Self::Expr(inner, _) => write!(f, "{inner}"), + } + } +} + +pub(super) fn check_fn<'tcx>(cx: &LateContext<'tcx>, kind: FnKind<'tcx>, body: &'tcx Body<'tcx>, sp: Span) { + if sp.from_expansion() { + return; + } + + match kind { + FnKind::Closure => { + // when returning without value in closure, replace this `return` + // with an empty block to prevent invalid suggestion (see #6501) + let replacement = if let ExprKind::Ret(None) = &body.value.kind { + RetReplacement::Block + } else { + RetReplacement::Empty + }; + check_final_expr(cx, body.value, vec![], replacement, None); + }, + FnKind::ItemFn(..) | FnKind::Method(..) => { + check_block_return(cx, &body.value.kind, sp, vec![]); + }, + } +} + +// if `expr` is a block, check if there are needless returns in it +fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>, sp: Span, mut semi_spans: Vec) { + if let ExprKind::Block(block, _) = expr_kind { + if let Some(block_expr) = block.expr { + check_final_expr(cx, block_expr, semi_spans, RetReplacement::Empty, None); + } else if let Some(stmt) = block.stmts.last() { + match stmt.kind { + StmtKind::Expr(expr) => { + check_final_expr(cx, expr, semi_spans, RetReplacement::Empty, None); + }, + StmtKind::Semi(semi_expr) => { + // Remove ending semicolons and any whitespace ' ' in between. + // Without `return`, the suggestion might not compile if the semicolon is retained + if let Some(semi_span) = stmt.span.trim_start(semi_expr.span) { + let semi_span_to_remove = + span_find_starting_semi(cx.sess().source_map(), semi_span.with_hi(sp.hi())); + semi_spans.push(semi_span_to_remove); + } + check_final_expr(cx, semi_expr, semi_spans, RetReplacement::Empty, None); + }, + _ => (), + } + } + } +} + +fn check_final_expr<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + semi_spans: Vec, /* containing all the places where we would need to remove semicolons if finding an + * needless return */ + replacement: RetReplacement<'tcx>, + match_ty_opt: Option>, +) { + let peeled_drop_expr = expr.peel_drop_temps(); + match &peeled_drop_expr.kind { + // simple return is always "bad" + ExprKind::Ret(inner) => { + // check if expr return nothing + let ret_span = if inner.is_none() && replacement == RetReplacement::Empty { + extend_span_to_previous_non_ws(cx, peeled_drop_expr.span) + } else { + peeled_drop_expr.span + }; + + let replacement = if let Some(inner_expr) = inner { + // if desugar of `do yeet`, don't lint + if let ExprKind::Call(path_expr, [_]) = inner_expr.kind + && let ExprKind::Path(QPath::LangItem(LangItem::TryTraitFromYeet, ..)) = path_expr.kind + { + return; + } + + let mut applicability = Applicability::MachineApplicable; + let (snippet, _) = snippet_with_context(cx, inner_expr.span, ret_span.ctxt(), "..", &mut applicability); + if binary_expr_needs_parentheses(inner_expr) { + RetReplacement::NeedsPar(snippet, applicability) + } else { + RetReplacement::Expr(snippet, applicability) + } + } else { + match match_ty_opt { + Some(match_ty) => { + match match_ty.kind() { + // If the code got till here with + // tuple not getting detected before it, + // then we are sure it's going to be Unit + // type + ty::Tuple(_) => RetReplacement::Unit, + // We don't want to anything in this case + // cause we can't predict what the user would + // want here + _ => return, + } + }, + None => replacement, + } + }; + + if inner.is_some_and(|inner| leaks_droppable_temporary_with_limited_lifetime(cx, inner)) { + return; + } + + if ret_span.from_expansion() || is_from_proc_macro(cx, expr) { + return; + } + + // Returns may be used to turn an expression into a statement in rustc's AST. + // This allows the addition of attributes, like `#[allow]` (See: clippy#9361) + // `#[expect(clippy::needless_return)]` needs to be handled separately to + // actually fulfill the expectation (clippy::#12998) + match cx.tcx.hir_attrs(expr.hir_id) { + [] => {}, + [attr] => { + if matches!(Level::from_attr(attr), Some((Level::Expect, _))) + && let metas = attr.meta_item_list() + && let Some(lst) = metas + && let [MetaItemInner::MetaItem(meta_item), ..] = lst.as_slice() + && let [tool, lint_name] = meta_item.path.segments.as_slice() + && tool.ident.name == sym::clippy + && matches!( + lint_name.ident.name, + sym::needless_return | sym::style | sym::all | sym::warnings + ) + { + // This is an expectation of the `needless_return` lint + } else { + return; + } + }, + _ => return, + } + + emit_return_lint( + cx, + peeled_drop_expr.span, + ret_span, + semi_spans, + &replacement, + expr.hir_id, + ); + }, + ExprKind::If(_, then, else_clause_opt) => { + check_block_return(cx, &then.kind, peeled_drop_expr.span, semi_spans.clone()); + if let Some(else_clause) = else_clause_opt { + // The `RetReplacement` won't be used there as `else_clause` will be either a block or + // a `if` expression. + check_final_expr(cx, else_clause, semi_spans, RetReplacement::Empty, match_ty_opt); + } + }, + // a match expr, check all arms + // an if/if let expr, check both exprs + // note, if without else is going to be a type checking error anyways + // (except for unit type functions) so we don't match it + ExprKind::Match(_, arms, MatchSource::Normal) => { + let match_ty = cx.typeck_results().expr_ty(peeled_drop_expr); + for arm in *arms { + check_final_expr(cx, arm.body, semi_spans.clone(), RetReplacement::Unit, Some(match_ty)); + } + }, + // if it's a whole block, check it + other_expr_kind => check_block_return(cx, other_expr_kind, peeled_drop_expr.span, semi_spans), + } +} + +fn emit_return_lint( + cx: &LateContext<'_>, + lint_span: Span, + ret_span: Span, + semi_spans: Vec, + replacement: &RetReplacement<'_>, + at: HirId, +) { + span_lint_hir_and_then( + cx, + NEEDLESS_RETURN, + at, + lint_span, + "unneeded `return` statement", + |diag| { + let suggestions = std::iter::once((ret_span, replacement.to_string())) + .chain(semi_spans.into_iter().map(|span| (span, String::new()))) + .collect(); + + diag.multipart_suggestion_verbose(replacement.sugg_help(), suggestions, replacement.applicability()); + }, + ); +} + +// Go backwards while encountering whitespace and extend the given Span to that point. +fn extend_span_to_previous_non_ws(cx: &LateContext<'_>, sp: Span) -> Span { + if let Ok(prev_source) = cx.sess().source_map().span_to_prev_source(sp) { + let ws = [b' ', b'\t', b'\n']; + if let Some(non_ws_pos) = prev_source.bytes().rposition(|c| !ws.contains(&c)) { + let len = prev_source.len() - non_ws_pos - 1; + return sp.with_lo(sp.lo() - BytePos::from_usize(len)); + } + } + + sp +} diff --git a/clippy_lints/src/returns/needless_return_with_question_mark.rs b/clippy_lints/src/returns/needless_return_with_question_mark.rs new file mode 100644 index 000000000000..c05038cd1e50 --- /dev/null +++ b/clippy_lints/src/returns/needless_return_with_question_mark.rs @@ -0,0 +1,60 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::{is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, path_res}; +use rustc_errors::Applicability; +use rustc_hir::LangItem::ResultErr; +use rustc_hir::{ExprKind, HirId, ItemKind, MatchSource, Node, OwnerNode, Stmt, StmtKind}; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::ty::adjustment::Adjust; + +use super::NEEDLESS_RETURN_WITH_QUESTION_MARK; + +pub(super) fn check_stmt<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + if !stmt.span.in_external_macro(cx.sess().source_map()) + && let StmtKind::Semi(expr) = stmt.kind + && let ExprKind::Ret(Some(ret)) = expr.kind + // return Err(...)? desugars to a match + // over a Err(...).branch() + // which breaks down to a branch call, with the callee being + // the constructor of the Err variant + && let ExprKind::Match(maybe_cons, _, MatchSource::TryDesugar(_)) = ret.kind + && let ExprKind::Call(_, [maybe_result_err]) = maybe_cons.kind + && let ExprKind::Call(maybe_constr, _) = maybe_result_err.kind + && is_res_lang_ctor(cx, path_res(cx, maybe_constr), ResultErr) + + // Ensure this is not the final stmt, otherwise removing it would cause a compile error + && let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir_get_parent_item(expr.hir_id)) + && let ItemKind::Fn { body, .. } = item.kind + && let block = cx.tcx.hir_body(body).value + && let ExprKind::Block(block, _) = block.kind + && !is_inside_let_else(cx.tcx, expr) + && let [.., final_stmt] = block.stmts + && final_stmt.hir_id != stmt.hir_id + && !is_from_proc_macro(cx, expr) + && !stmt_needs_never_type(cx, stmt.hir_id) + { + span_lint_and_sugg( + cx, + NEEDLESS_RETURN_WITH_QUESTION_MARK, + expr.span.until(ret.span), + "unneeded `return` statement with `?` operator", + "remove it", + String::new(), + Applicability::MachineApplicable, + ); + } +} + +/// Checks if a return statement is "needed" in the middle of a block, or if it can be removed. +/// This is the case when the enclosing block expression is coerced to some other type, +/// which only works because of the never-ness of `return` expressions +fn stmt_needs_never_type(cx: &LateContext<'_>, stmt_hir_id: HirId) -> bool { + cx.tcx + .hir_parent_iter(stmt_hir_id) + .find_map(|(_, node)| if let Node::Expr(expr) = node { Some(expr) } else { None }) + .is_some_and(|e| { + cx.typeck_results() + .expr_adjustments(e) + .iter() + .any(|adjust| adjust.target != cx.tcx.types.unit && matches!(adjust.kind, Adjust::NeverToAny)) + }) +} diff --git a/tests/ui/track-diagnostics-clippy.stderr b/tests/ui/track-diagnostics-clippy.stderr index d5533877b451..3ceb501463b7 100644 --- a/tests/ui/track-diagnostics-clippy.stderr +++ b/tests/ui/track-diagnostics-clippy.stderr @@ -16,7 +16,7 @@ LL | let d = 42; LL | d | ^ | - = note: -Ztrack-diagnostics: created at clippy_lints/src/returns.rs:LL:CC + = note: -Ztrack-diagnostics: created at clippy_lints/src/returns/let_and_return.rs:LL:CC = note: `-D clippy::let-and-return` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::let_and_return)]` help: return the expression directly From a48c8e337d1717ceeaa930a0a804374aa2e66419 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Sat, 13 Sep 2025 11:24:51 +0200 Subject: [PATCH 466/682] compiletest: Fix `--exact` test filtering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fix only changes the behavior when using `--exact` test filtering, which was quite broken. Before this fix, the following runs 0 tests: $ ./x test tests/run-make/crate-loading -- --exact running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 431 filtered out; finished in 24.95µs With the fix the desired test is run: $ ./x test tests/run-make/crate-loading -- --exact running 1 tests test [run-make] tests/run-make/crate-loading ... ok Without `--exact` the set of run tests is unchanged. This still runs "too many" tests $ ./x test tests/run-make/crate-loading running 3 tests test [run-make] tests/run-make/crate-loading-crate-depends-on-itself ... ok test [run-make] tests/run-make/crate-loading-multiple-candidates ... ok test [run-make] tests/run-make/crate-loading ... ok This still runs the one and only right test: $ ./x test tests/ui/lint/unused/unused-allocation.rs running 1 tests test [ui] tests/ui/lint/unused/unused-allocation.rs ... ok --- src/tools/compiletest/src/directives.rs | 9 ++++- src/tools/compiletest/src/directives/tests.rs | 8 ++-- src/tools/compiletest/src/executor.rs | 11 +++++- src/tools/compiletest/src/lib.rs | 37 ++++++++++++++++--- 4 files changed, 54 insertions(+), 11 deletions(-) diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index 857953072c43..1277fd225eb6 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -1446,6 +1446,7 @@ pub(crate) fn make_test_description( cache: &DirectivesCache, name: String, path: &Utf8Path, + filterable_path: &Utf8Path, src: R, test_revision: Option<&str>, poisoned: &mut bool, @@ -1520,7 +1521,13 @@ pub(crate) fn make_test_description( _ => ShouldPanic::No, }; - CollectedTestDesc { name, ignore, ignore_message, should_panic } + CollectedTestDesc { + name, + filterable_path: filterable_path.to_owned(), + ignore, + ignore_message, + should_panic, + } } fn ignore_cdb(config: &Config, line: &str) -> IgnoreDecision { diff --git a/src/tools/compiletest/src/directives/tests.rs b/src/tools/compiletest/src/directives/tests.rs index 33a02eb29fd5..16cf76be9a53 100644 --- a/src/tools/compiletest/src/directives/tests.rs +++ b/src/tools/compiletest/src/directives/tests.rs @@ -14,6 +14,7 @@ fn make_test_description( config: &Config, name: String, path: &Utf8Path, + filterable_path: &Utf8Path, src: R, revision: Option<&str>, ) -> CollectedTestDesc { @@ -24,6 +25,7 @@ fn make_test_description( &cache, name, path, + filterable_path, src, revision, &mut poisoned, @@ -221,7 +223,7 @@ fn parse_rs(config: &Config, contents: &str) -> EarlyProps { fn check_ignore(config: &Config, contents: &str) -> bool { let tn = String::new(); let p = Utf8Path::new("a.rs"); - let d = make_test_description(&config, tn, p, std::io::Cursor::new(contents), None); + let d = make_test_description(&config, tn, p, p, std::io::Cursor::new(contents), None); d.ignore } @@ -231,9 +233,9 @@ fn should_fail() { let tn = String::new(); let p = Utf8Path::new("a.rs"); - let d = make_test_description(&config, tn.clone(), p, std::io::Cursor::new(""), None); + let d = make_test_description(&config, tn.clone(), p, p, std::io::Cursor::new(""), None); assert_eq!(d.should_panic, ShouldPanic::No); - let d = make_test_description(&config, tn, p, std::io::Cursor::new("//@ should-fail"), None); + let d = make_test_description(&config, tn, p, p, std::io::Cursor::new("//@ should-fail"), None); assert_eq!(d.should_panic, ShouldPanic::Yes); } diff --git a/src/tools/compiletest/src/executor.rs b/src/tools/compiletest/src/executor.rs index b0dc24798b6c..37cc17351d5f 100644 --- a/src/tools/compiletest/src/executor.rs +++ b/src/tools/compiletest/src/executor.rs @@ -12,6 +12,8 @@ use std::num::NonZero; use std::sync::{Arc, Mutex, mpsc}; use std::{env, hint, io, mem, panic, thread}; +use camino::Utf8PathBuf; + use crate::common::{Config, TestPaths}; use crate::output_capture::{self, ConsoleOut}; use crate::panic_hook; @@ -293,8 +295,12 @@ fn filter_tests(opts: &Config, tests: Vec) -> Vec let mut filtered = tests; let matches_filter = |test: &CollectedTest, filter_str: &str| { - let test_name = &test.desc.name; - if opts.filter_exact { test_name == filter_str } else { test_name.contains(filter_str) } + let filterable_path = test.desc.filterable_path.as_str(); + if opts.filter_exact { + filterable_path == filter_str + } else { + filterable_path.contains(filter_str) + } }; // Remove tests that don't match the test filter @@ -339,6 +345,7 @@ pub(crate) struct CollectedTest { /// Information that was historically needed to create a libtest `TestDesc`. pub(crate) struct CollectedTestDesc { pub(crate) name: String, + pub(crate) filterable_path: Utf8PathBuf, pub(crate) ignore: bool, pub(crate) ignore_message: Option>, pub(crate) should_panic: ShouldPanic, diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index f647f96a9bf3..5e349a20ed28 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -317,11 +317,17 @@ pub fn parse_config(args: Vec) -> Config { .free .iter() .map(|f| { + // Here `f` is relative to `./tests/run-make`. So if you run + // + // ./x test tests/run-make/crate-loading + // + // then `f` is "crate-loading". let path = Utf8Path::new(f); let mut iter = path.iter().skip(1); - // We skip the test folder and check if the user passed `rmake.rs`. if iter.next().is_some_and(|s| s == "rmake.rs") && iter.next().is_none() { + // Strip the "rmake.rs" suffix. For example, if `f` is + // "crate-loading/rmake.rs" then this gives us "crate-loading". path.parent().unwrap().to_string() } else { f.to_string() @@ -329,6 +335,12 @@ pub fn parse_config(args: Vec) -> Config { }) .collect::>() } else { + // Note that the filters are relative to the root dir of the different test + // suites. For example, with: + // + // ./x test tests/ui/lint/unused + // + // the filter is "lint/unused". matches.free.clone() }; let compare_mode = matches.opt_str("compare-mode").map(|s| { @@ -911,7 +923,8 @@ fn make_test(cx: &TestCollectorCx, collector: &mut TestCollector, testpaths: &Te collector.tests.extend(revisions.into_iter().map(|revision| { // Create a test name and description to hand over to the executor. let src_file = fs::File::open(&test_path).expect("open test file to parse ignores"); - let test_name = make_test_name(&cx.config, testpaths, revision); + let (test_name, filterable_path) = + make_test_name_and_filterable_path(&cx.config, testpaths, revision); // Create a description struct for the test/revision. // This is where `ignore-*`/`only-*`/`needs-*` directives are handled, // because they historically needed to set the libtest ignored flag. @@ -920,6 +933,7 @@ fn make_test(cx: &TestCollectorCx, collector: &mut TestCollector, testpaths: &Te &cx.cache, test_name, &test_path, + &filterable_path, src_file, revision, &mut collector.poisoned, @@ -1072,7 +1086,11 @@ impl Stamp { } /// Creates a name for this test/revision that can be handed over to the executor. -fn make_test_name(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> String { +fn make_test_name_and_filterable_path( + config: &Config, + testpaths: &TestPaths, + revision: Option<&str>, +) -> (String, Utf8PathBuf) { // Print the name of the file, relative to the sources root. let path = testpaths.file.strip_prefix(&config.src_root).unwrap(); let debugger = match config.debugger { @@ -1084,14 +1102,23 @@ fn make_test_name(config: &Config, testpaths: &TestPaths, revision: Option<&str> None => String::new(), }; - format!( + let name = format!( "[{}{}{}] {}{}", config.mode, debugger, mode_suffix, path, revision.map_or("".to_string(), |rev| format!("#{}", rev)) - ) + ); + + // `path` is the full path from the repo root like, `tests/ui/foo/bar.rs`. + // Filtering is applied without the `tests/ui/` part, so strip that off. + // First strip off "tests" to make sure we don't have some unexpected path. + let mut filterable_path = path.strip_prefix("tests").unwrap().to_owned(); + // Now strip off e.g. "ui" or "run-make" component. + filterable_path = filterable_path.components().skip(1).collect(); + + (name, filterable_path) } /// Checks that test discovery didn't find any tests whose name stem is a prefix From 6f3f1f3aa71c1d8dfdb86ff47255979bcfdb5cda Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 13 Sep 2025 11:11:48 +0200 Subject: [PATCH 467/682] fix(needless_return): FP with `cfg`d code after `return` --- clippy_lints/src/returns/needless_return.rs | 11 ++++++++++- tests/ui/needless_return.fixed | 7 +++++++ tests/ui/needless_return.rs | 7 +++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/returns/needless_return.rs b/clippy_lints/src/returns/needless_return.rs index 0a1ba7b5beca..04739fc1b22a 100644 --- a/clippy_lints/src/returns/needless_return.rs +++ b/clippy_lints/src/returns/needless_return.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::snippet_with_context; use clippy_utils::{ binary_expr_needs_parentheses, is_from_proc_macro, leaks_droppable_temporary_with_limited_lifetime, - span_find_starting_semi, sym, + span_contains_cfg, span_find_starting_semi, sym, }; use rustc_ast::MetaItemInner; use rustc_errors::Applicability; @@ -83,6 +83,15 @@ fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>, if let Some(block_expr) = block.expr { check_final_expr(cx, block_expr, semi_spans, RetReplacement::Empty, None); } else if let Some(stmt) = block.stmts.last() { + if span_contains_cfg( + cx, + Span::between( + stmt.span, + cx.sess().source_map().end_point(block.span), // the closing brace of the block + ), + ) { + return; + } match stmt.kind { StmtKind::Expr(expr) => { check_final_expr(cx, expr, semi_spans, RetReplacement::Empty, None); diff --git a/tests/ui/needless_return.fixed b/tests/ui/needless_return.fixed index d571b97f5194..f5f8bb21e815 100644 --- a/tests/ui/needless_return.fixed +++ b/tests/ui/needless_return.fixed @@ -517,3 +517,10 @@ mod else_ifs { } } } + +fn issue14474() -> u64 { + return 456; + + #[cfg(false)] + 123 +} diff --git a/tests/ui/needless_return.rs b/tests/ui/needless_return.rs index 2e4348ea338c..495516c1c2e5 100644 --- a/tests/ui/needless_return.rs +++ b/tests/ui/needless_return.rs @@ -526,3 +526,10 @@ mod else_ifs { } } } + +fn issue14474() -> u64 { + return 456; + + #[cfg(false)] + 123 +} From d3b3bb9bb2e87da215ce68a6dadba35b3c9e81e0 Mon Sep 17 00:00:00 2001 From: Michele Sessa <62351258+mikysett@users.noreply.github.com> Date: Sat, 13 Sep 2025 15:03:29 +0200 Subject: [PATCH 468/682] Fix small typo in check-cfg.md --- src/doc/rustc/src/check-cfg.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc/src/check-cfg.md b/src/doc/rustc/src/check-cfg.md index 00add2651ae8..4caeaa106b49 100644 --- a/src/doc/rustc/src/check-cfg.md +++ b/src/doc/rustc/src/check-cfg.md @@ -134,7 +134,7 @@ As of `2025-01-02T`, the list of known names is as follows: - `unix` - `windows` -> Starting with 1.85.0, the `test` cfg is consider to be a "userspace" config +> Starting with 1.85.0, the `test` cfg is considered to be a "userspace" config > despite being also set by `rustc` and should be managed by the build system itself. Like with `values(any())`, well known names checking can be disabled by passing `cfg(any())` From fd851204993a2f41dd7a835cf0604d5fe16a118c Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Sat, 13 Sep 2025 10:09:15 -0400 Subject: [PATCH 469/682] Bump version to 1.92.0 --- src/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version b/src/version index 6979a6c0661b..7f229af96476 100644 --- a/src/version +++ b/src/version @@ -1 +1 @@ -1.91.0 +1.92.0 From 365189ef4f508e4645de7607052884b8e7f413a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Fri, 12 Sep 2025 13:54:15 -0700 Subject: [PATCH 470/682] update fixme in compare_method_predicate_entailment resulting from review of EII --- .../rustc_hir_analysis/src/check/compare_impl_item.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index e48272561939..222fe7b5932f 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -298,14 +298,9 @@ fn compare_method_predicate_entailment<'tcx>( // compatible with that of the trait method. We do this by // checking that `impl_fty <: trait_fty`. // - // FIXME. Unfortunately, this doesn't quite work right now because - // associated type normalization is not integrated into subtype - // checks. For the comparison to be valid, we need to - // normalize the associated types in the impl/trait methods - // first. However, because function types bind regions, just - // calling `FnCtxt::normalize` would have no effect on - // any associated types appearing in the fn arguments or return - // type. + // FIXME: We manually instantiate the trait method here as we need + // to manually compute its implied bounds. Otherwise this could just + // be `ocx.sub(impl_sig, trait_sig)`. let mut wf_tys = FxIndexSet::default(); From 3d0eda7af8699b3a9dc51dc339d7d5faae753e97 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Fri, 22 Aug 2025 01:38:58 +0000 Subject: [PATCH 471/682] Introduce ValueSet. --- Cargo.lock | 1 + compiler/rustc_mir_transform/Cargo.toml | 1 + compiler/rustc_mir_transform/src/gvn.rs | 117 +++++++++++++++++++----- 3 files changed, 98 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4677d34d2a63..328641de1e0c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4235,6 +4235,7 @@ name = "rustc_mir_transform" version = "0.0.0" dependencies = [ "either", + "hashbrown", "itertools", "rustc_abi", "rustc_arena", diff --git a/compiler/rustc_mir_transform/Cargo.toml b/compiler/rustc_mir_transform/Cargo.toml index 08c43a4648c6..511c1960e40b 100644 --- a/compiler/rustc_mir_transform/Cargo.toml +++ b/compiler/rustc_mir_transform/Cargo.toml @@ -6,6 +6,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start either = "1" +hashbrown = "0.15" itertools = "0.12" rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index bf6aa800d20f..3765dc5c92ae 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -85,8 +85,10 @@ //! that contain `AllocId`s. use std::borrow::Cow; +use std::hash::{Hash, Hasher}; use either::Either; +use hashbrown::hash_table::{Entry, HashTable}; use itertools::Itertools as _; use rustc_abi::{self as abi, BackendRepr, FIRST_VARIANT, FieldIdx, Primitive, Size, VariantIdx}; use rustc_const_eval::const_eval::DummyMachine; @@ -94,7 +96,7 @@ use rustc_const_eval::interpret::{ ImmTy, Immediate, InterpCx, MemPlaceMeta, MemoryKind, OpTy, Projectable, Scalar, intern_const_alloc_for_constprop, }; -use rustc_data_structures::fx::{FxIndexSet, MutableValues}; +use rustc_data_structures::fx::FxHasher; use rustc_data_structures::graph::dominators::Dominators; use rustc_hir::def::DefKind; use rustc_index::bit_set::DenseBitSet; @@ -152,6 +154,7 @@ impl<'tcx> crate::MirPass<'tcx> for GVN { } newtype_index! { + #[debug_format = "_v{}"] struct VnIndex {} } @@ -213,6 +216,89 @@ enum Value<'tcx> { }, } +/// Stores and deduplicates pairs of `(Value, Ty)` into in `VnIndex` numbered values. +/// +/// This data structure is mostly a partial reimplementation of `FxIndexMap`. +/// We do not use a regular `FxIndexMap` to skip hashing values that are unique by construction, +/// like opaque values, address with provenance and non-deterministic constants. +struct ValueSet<'tcx> { + indices: HashTable, + hashes: IndexVec, + values: IndexVec>, + types: IndexVec>, + /// Counter to generate different values. + next_opaque: usize, +} + +impl<'tcx> ValueSet<'tcx> { + fn new(num_values: usize) -> ValueSet<'tcx> { + ValueSet { + indices: HashTable::with_capacity(num_values), + hashes: IndexVec::with_capacity(num_values), + values: IndexVec::with_capacity(num_values), + types: IndexVec::with_capacity(num_values), + next_opaque: 1, + } + } + + /// Insert a `(Value, Ty)` pair to be deduplicated. + /// Returns `true` as second tuple field if this value did not exist previously. + #[allow(rustc::pass_by_value)] // closures take `&VnIndex` + fn insert(&mut self, ty: Ty<'tcx>, value: Value<'tcx>) -> (VnIndex, bool) { + let hash: u64 = { + let mut h = FxHasher::default(); + value.hash(&mut h); + ty.hash(&mut h); + h.finish() + }; + + let eq = |index: &VnIndex| self.values[*index] == value && self.types[*index] == ty; + let hasher = |index: &VnIndex| self.hashes[*index]; + match self.indices.entry(hash, eq, hasher) { + Entry::Occupied(entry) => { + let index = *entry.get(); + (index, false) + } + Entry::Vacant(entry) => { + let index = self.hashes.push(hash); + entry.insert(index); + let _index = self.values.push(value); + debug_assert_eq!(index, _index); + let _index = self.types.push(ty); + debug_assert_eq!(index, _index); + (index, true) + } + } + } + + /// Increment the opaque index counter return a new unique value. + #[inline] + fn next_opaque(&mut self) -> usize { + let next_opaque = self.next_opaque; + self.next_opaque += 1; + next_opaque + } + + /// Return the `Value` associated with the given `VnIndex`. + #[inline] + fn value(&self, index: VnIndex) -> &Value<'tcx> { + &self.values[index] + } + + /// Return the type associated with the given `VnIndex`. + #[inline] + fn ty(&self, index: VnIndex) -> Ty<'tcx> { + self.types[index] + } + + /// Replace the value associated with `index` with an opaque value. + #[inline] + fn forget(&mut self, index: VnIndex) { + let opaque = self.next_opaque(); + self.values[index] = Value::Opaque(opaque); + } +} + struct VnState<'body, 'tcx> { tcx: TyCtxt<'tcx>, ecx: InterpCx<'tcx, DummyMachine>, @@ -223,11 +309,9 @@ struct VnState<'body, 'tcx> { /// Locals that are assigned that value. // This vector does not hold all the values of `VnIndex` that we create. rev_locals: IndexVec>, - values: FxIndexSet<(Value<'tcx>, Ty<'tcx>)>, + values: ValueSet<'tcx>, /// Values evaluated as constants if possible. evaluated: IndexVec>>, - /// Counter to generate different values. - next_opaque: usize, /// Cache the deref values. derefs: Vec, ssa: &'body SsaLocals, @@ -258,9 +342,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { is_coroutine: body.coroutine.is_some(), locals: IndexVec::from_elem(None, local_decls), rev_locals: IndexVec::with_capacity(num_values), - values: FxIndexSet::with_capacity_and_hasher(num_values, Default::default()), + values: ValueSet::new(num_values), evaluated: IndexVec::with_capacity(num_values), - next_opaque: 1, derefs: Vec::new(), ssa, dominators, @@ -274,8 +357,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { #[instrument(level = "trace", skip(self), ret)] fn insert(&mut self, ty: Ty<'tcx>, value: Value<'tcx>) -> VnIndex { - let (index, new) = self.values.insert_full((value, ty)); - let index = VnIndex::from_usize(index); + let (index, new) = self.values.insert(ty, value); if new { // Grow `evaluated` and `rev_locals` here to amortize the allocations. let evaluated = self.eval_to_const(index); @@ -287,17 +369,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { index } - fn next_opaque(&mut self) -> usize { - let next_opaque = self.next_opaque; - self.next_opaque += 1; - next_opaque - } - /// Create a new `Value` for which we have no information at all, except that it is distinct /// from all the others. #[instrument(level = "trace", skip(self), ret)] fn new_opaque(&mut self, ty: Ty<'tcx>) -> VnIndex { - let value = Value::Opaque(self.next_opaque()); + let value = Value::Opaque(self.values.next_opaque()); self.insert(ty, value) } @@ -311,18 +387,18 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } AddressKind::Address(mutbl) => Ty::new_ptr(self.tcx, pty, mutbl.to_mutbl_lossy()), }; - let value = Value::Address { place, kind, provenance: self.next_opaque() }; + let value = Value::Address { place, kind, provenance: self.values.next_opaque() }; self.insert(ty, value) } #[inline] fn get(&self, index: VnIndex) -> &Value<'tcx> { - &self.values.get_index(index.as_usize()).unwrap().0 + self.values.value(index) } #[inline] fn ty(&self, index: VnIndex) -> Ty<'tcx> { - self.values.get_index(index.as_usize()).unwrap().1 + self.values.ty(index) } /// Record that `local` is assigned `value`. `local` must be SSA. @@ -340,7 +416,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } else { // Multiple mentions of this constant will yield different values, // so assign a different `disambiguator` to ensure they do not get the same `VnIndex`. - let disambiguator = self.next_opaque(); + let disambiguator = self.values.next_opaque(); // `disambiguator: 0` means deterministic. debug_assert_ne!(disambiguator, 0); disambiguator @@ -374,8 +450,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { fn invalidate_derefs(&mut self) { for deref in std::mem::take(&mut self.derefs) { - let opaque = self.next_opaque(); - self.values.get_index_mut2(deref.index()).unwrap().0 = Value::Opaque(opaque); + self.values.forget(deref); } } From 7f34f6e25f143d5c8495aca9ddafb32a87b02303 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Fri, 22 Aug 2025 01:50:21 +0000 Subject: [PATCH 472/682] Do not hash opaques in GVN. --- compiler/rustc_mir_transform/src/gvn.rs | 129 +++++++++++++++++------- 1 file changed, 90 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 3765dc5c92ae..93b60aacbbac 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -100,7 +100,7 @@ use rustc_data_structures::fx::FxHasher; use rustc_data_structures::graph::dominators::Dominators; use rustc_hir::def::DefKind; use rustc_index::bit_set::DenseBitSet; -use rustc_index::{IndexVec, newtype_index}; +use rustc_index::{Idx, IndexVec, newtype_index}; use rustc_middle::bug; use rustc_middle::mir::interpret::GlobalAlloc; use rustc_middle::mir::visit::*; @@ -158,6 +158,14 @@ newtype_index! { struct VnIndex {} } +newtype_index! { + /// Counter type to ensure that all unique values are created using `insert_unique`. + #[debug_format = "_o{}"] + struct VnOpaque { + const DETERMINISTIC = 0; + } +} + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] enum AddressKind { Ref(BorrowKind), @@ -169,14 +177,14 @@ enum Value<'tcx> { // Root values. /// Used to represent values we know nothing about. /// The `usize` is a counter incremented by `new_opaque`. - Opaque(usize), + Opaque(VnOpaque), /// Evaluated or unevaluated constant value. Constant { value: Const<'tcx>, /// Some constants do not have a deterministic value. To avoid merging two instances of the /// same `Const`, we assign them an additional integer index. - // `disambiguator` is 0 iff the constant is deterministic. - disambiguator: usize, + // `disambiguator` is `DETERMINISTIC` iff the constant is deterministic. + disambiguator: VnOpaque, }, /// An aggregate value, either tuple/closure/struct/enum. /// This does not contain unions, as we cannot reason with the value. @@ -195,7 +203,7 @@ enum Value<'tcx> { place: Place<'tcx>, kind: AddressKind, /// Give each borrow and pointer a different provenance, so we don't merge them. - provenance: usize, + provenance: VnOpaque, }, // Extractions. @@ -227,7 +235,7 @@ struct ValueSet<'tcx> { values: IndexVec>, types: IndexVec>, /// Counter to generate different values. - next_opaque: usize, + next_opaque: VnOpaque, } impl<'tcx> ValueSet<'tcx> { @@ -237,14 +245,45 @@ impl<'tcx> ValueSet<'tcx> { hashes: IndexVec::with_capacity(num_values), values: IndexVec::with_capacity(num_values), types: IndexVec::with_capacity(num_values), - next_opaque: 1, + // The first opaque is 1, as 0 means deterministic constant. + next_opaque: VnOpaque::from_u32(1), } } + /// Insert a `(Value, Ty)` pair without hashing or deduplication. + #[inline] + fn insert_unique( + &mut self, + ty: Ty<'tcx>, + value: impl FnOnce(VnOpaque) -> Value<'tcx>, + ) -> VnIndex { + let value = value(self.next_opaque); + self.next_opaque.increment_by(1); + + debug_assert!(match value { + Value::Opaque(_) | Value::Address { .. } => true, + Value::Constant { disambiguator, .. } => disambiguator != DETERMINISTIC, + _ => false, + }); + + let index = self.hashes.push(0); + let _index = self.types.push(ty); + debug_assert_eq!(index, _index); + let _index = self.values.push(value); + debug_assert_eq!(index, _index); + index + } + /// Insert a `(Value, Ty)` pair to be deduplicated. /// Returns `true` as second tuple field if this value did not exist previously. #[allow(rustc::pass_by_value)] // closures take `&VnIndex` fn insert(&mut self, ty: Ty<'tcx>, value: Value<'tcx>) -> (VnIndex, bool) { + debug_assert!(match value { + Value::Opaque(_) | Value::Address { .. } => false, + Value::Constant { disambiguator, .. } => disambiguator == DETERMINISTIC, + _ => true, + }); + let hash: u64 = { let mut h = FxHasher::default(); value.hash(&mut h); @@ -271,14 +310,6 @@ impl<'tcx> ValueSet<'tcx> { } } - /// Increment the opaque index counter return a new unique value. - #[inline] - fn next_opaque(&mut self) -> usize { - let next_opaque = self.next_opaque; - self.next_opaque += 1; - next_opaque - } - /// Return the `Value` associated with the given `VnIndex`. #[inline] fn value(&self, index: VnIndex) -> &Value<'tcx> { @@ -294,8 +325,8 @@ impl<'tcx> ValueSet<'tcx> { /// Replace the value associated with `index` with an opaque value. #[inline] fn forget(&mut self, index: VnIndex) { - let opaque = self.next_opaque(); - self.values[index] = Value::Opaque(opaque); + self.values[index] = Value::Opaque(self.next_opaque); + self.next_opaque.increment_by(1); } } @@ -373,8 +404,12 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { /// from all the others. #[instrument(level = "trace", skip(self), ret)] fn new_opaque(&mut self, ty: Ty<'tcx>) -> VnIndex { - let value = Value::Opaque(self.values.next_opaque()); - self.insert(ty, value) + let index = self.values.insert_unique(ty, Value::Opaque); + let _index = self.evaluated.push(None); + debug_assert_eq!(index, _index); + let _index = self.rev_locals.push(SmallVec::new()); + debug_assert_eq!(index, _index); + index } /// Create a new `Value::Address` distinct from all the others. @@ -387,8 +422,39 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } AddressKind::Address(mutbl) => Ty::new_ptr(self.tcx, pty, mutbl.to_mutbl_lossy()), }; - let value = Value::Address { place, kind, provenance: self.values.next_opaque() }; - self.insert(ty, value) + let index = + self.values.insert_unique(ty, |provenance| Value::Address { place, kind, provenance }); + let evaluated = self.eval_to_const(index); + let _index = self.evaluated.push(evaluated); + debug_assert_eq!(index, _index); + let _index = self.rev_locals.push(SmallVec::new()); + debug_assert_eq!(index, _index); + index + } + + #[instrument(level = "trace", skip(self), ret)] + fn insert_constant(&mut self, value: Const<'tcx>) -> VnIndex { + let (index, new) = if value.is_deterministic() { + // The constant is deterministic, no need to disambiguate. + let constant = Value::Constant { value, disambiguator: DETERMINISTIC }; + self.values.insert(value.ty(), constant) + } else { + // Multiple mentions of this constant will yield different values, + // so assign a different `disambiguator` to ensure they do not get the same `VnIndex`. + let index = self.values.insert_unique(value.ty(), |disambiguator| { + debug_assert_ne!(disambiguator, DETERMINISTIC); + Value::Constant { value, disambiguator } + }); + (index, true) + }; + if new { + let evaluated = self.eval_to_const(index); + let _index = self.evaluated.push(evaluated); + debug_assert_eq!(index, _index); + let _index = self.rev_locals.push(SmallVec::new()); + debug_assert_eq!(index, _index); + } + index } #[inline] @@ -409,33 +475,18 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { self.rev_locals[value].push(local); } - fn insert_constant(&mut self, value: Const<'tcx>) -> VnIndex { - let disambiguator = if value.is_deterministic() { - // The constant is deterministic, no need to disambiguate. - 0 - } else { - // Multiple mentions of this constant will yield different values, - // so assign a different `disambiguator` to ensure they do not get the same `VnIndex`. - let disambiguator = self.values.next_opaque(); - // `disambiguator: 0` means deterministic. - debug_assert_ne!(disambiguator, 0); - disambiguator - }; - self.insert(value.ty(), Value::Constant { value, disambiguator }) - } - fn insert_bool(&mut self, flag: bool) -> VnIndex { // Booleans are deterministic. let value = Const::from_bool(self.tcx, flag); debug_assert!(value.is_deterministic()); - self.insert(self.tcx.types.bool, Value::Constant { value, disambiguator: 0 }) + self.insert(self.tcx.types.bool, Value::Constant { value, disambiguator: DETERMINISTIC }) } fn insert_scalar(&mut self, ty: Ty<'tcx>, scalar: Scalar) -> VnIndex { // Scalars are deterministic. let value = Const::from_scalar(self.tcx, scalar, ty); debug_assert!(value.is_deterministic()); - self.insert(ty, Value::Constant { value, disambiguator: 0 }) + self.insert(ty, Value::Constant { value, disambiguator: DETERMINISTIC }) } fn insert_tuple(&mut self, ty: Ty<'tcx>, values: Vec) -> VnIndex { @@ -1680,7 +1731,7 @@ impl<'tcx> VnState<'_, 'tcx> { // This was already constant in MIR, do not change it. If the constant is not // deterministic, adding an additional mention of it in MIR will not give the same value as // the former mention. - if let Value::Constant { value, disambiguator: 0 } = *self.get(index) { + if let Value::Constant { value, disambiguator: DETERMINISTIC } = *self.get(index) { debug_assert!(value.is_deterministic()); return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: value }); } From 0a911ec97f9ce39eb52dd3e5da6232e733cdee47 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Tue, 9 Sep 2025 01:37:48 +0000 Subject: [PATCH 473/682] Stop counting opaques. --- compiler/rustc_mir_transform/src/gvn.rs | 49 ++++++++++++------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 93b60aacbbac..f8b3e5c6f879 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -100,7 +100,7 @@ use rustc_data_structures::fx::FxHasher; use rustc_data_structures::graph::dominators::Dominators; use rustc_hir::def::DefKind; use rustc_index::bit_set::DenseBitSet; -use rustc_index::{Idx, IndexVec, newtype_index}; +use rustc_index::{IndexVec, newtype_index}; use rustc_middle::bug; use rustc_middle::mir::interpret::GlobalAlloc; use rustc_middle::mir::visit::*; @@ -158,11 +158,16 @@ newtype_index! { struct VnIndex {} } -newtype_index! { - /// Counter type to ensure that all unique values are created using `insert_unique`. - #[debug_format = "_o{}"] - struct VnOpaque { - const DETERMINISTIC = 0; +#[derive(Copy, Clone, Debug, Eq)] +struct VnOpaque; +impl PartialEq for VnOpaque { + fn eq(&self, _: &VnOpaque) -> bool { + unreachable!() + } +} +impl Hash for VnOpaque { + fn hash(&self, _: &mut T) { + unreachable!() } } @@ -183,8 +188,8 @@ enum Value<'tcx> { value: Const<'tcx>, /// Some constants do not have a deterministic value. To avoid merging two instances of the /// same `Const`, we assign them an additional integer index. - // `disambiguator` is `DETERMINISTIC` iff the constant is deterministic. - disambiguator: VnOpaque, + // `disambiguator` is `None` iff the constant is deterministic. + disambiguator: Option, }, /// An aggregate value, either tuple/closure/struct/enum. /// This does not contain unions, as we cannot reason with the value. @@ -234,8 +239,6 @@ struct ValueSet<'tcx> { hashes: IndexVec, values: IndexVec>, types: IndexVec>, - /// Counter to generate different values. - next_opaque: VnOpaque, } impl<'tcx> ValueSet<'tcx> { @@ -245,8 +248,6 @@ impl<'tcx> ValueSet<'tcx> { hashes: IndexVec::with_capacity(num_values), values: IndexVec::with_capacity(num_values), types: IndexVec::with_capacity(num_values), - // The first opaque is 1, as 0 means deterministic constant. - next_opaque: VnOpaque::from_u32(1), } } @@ -257,12 +258,11 @@ impl<'tcx> ValueSet<'tcx> { ty: Ty<'tcx>, value: impl FnOnce(VnOpaque) -> Value<'tcx>, ) -> VnIndex { - let value = value(self.next_opaque); - self.next_opaque.increment_by(1); + let value = value(VnOpaque); debug_assert!(match value { Value::Opaque(_) | Value::Address { .. } => true, - Value::Constant { disambiguator, .. } => disambiguator != DETERMINISTIC, + Value::Constant { disambiguator, .. } => disambiguator.is_some(), _ => false, }); @@ -280,7 +280,7 @@ impl<'tcx> ValueSet<'tcx> { fn insert(&mut self, ty: Ty<'tcx>, value: Value<'tcx>) -> (VnIndex, bool) { debug_assert!(match value { Value::Opaque(_) | Value::Address { .. } => false, - Value::Constant { disambiguator, .. } => disambiguator == DETERMINISTIC, + Value::Constant { disambiguator, .. } => disambiguator.is_none(), _ => true, }); @@ -325,8 +325,7 @@ impl<'tcx> ValueSet<'tcx> { /// Replace the value associated with `index` with an opaque value. #[inline] fn forget(&mut self, index: VnIndex) { - self.values[index] = Value::Opaque(self.next_opaque); - self.next_opaque.increment_by(1); + self.values[index] = Value::Opaque(VnOpaque); } } @@ -436,14 +435,14 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { fn insert_constant(&mut self, value: Const<'tcx>) -> VnIndex { let (index, new) = if value.is_deterministic() { // The constant is deterministic, no need to disambiguate. - let constant = Value::Constant { value, disambiguator: DETERMINISTIC }; + let constant = Value::Constant { value, disambiguator: None }; self.values.insert(value.ty(), constant) } else { // Multiple mentions of this constant will yield different values, // so assign a different `disambiguator` to ensure they do not get the same `VnIndex`. - let index = self.values.insert_unique(value.ty(), |disambiguator| { - debug_assert_ne!(disambiguator, DETERMINISTIC); - Value::Constant { value, disambiguator } + let index = self.values.insert_unique(value.ty(), |disambiguator| Value::Constant { + value, + disambiguator: Some(disambiguator), }); (index, true) }; @@ -479,14 +478,14 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // Booleans are deterministic. let value = Const::from_bool(self.tcx, flag); debug_assert!(value.is_deterministic()); - self.insert(self.tcx.types.bool, Value::Constant { value, disambiguator: DETERMINISTIC }) + self.insert(self.tcx.types.bool, Value::Constant { value, disambiguator: None }) } fn insert_scalar(&mut self, ty: Ty<'tcx>, scalar: Scalar) -> VnIndex { // Scalars are deterministic. let value = Const::from_scalar(self.tcx, scalar, ty); debug_assert!(value.is_deterministic()); - self.insert(ty, Value::Constant { value, disambiguator: DETERMINISTIC }) + self.insert(ty, Value::Constant { value, disambiguator: None }) } fn insert_tuple(&mut self, ty: Ty<'tcx>, values: Vec) -> VnIndex { @@ -1731,7 +1730,7 @@ impl<'tcx> VnState<'_, 'tcx> { // This was already constant in MIR, do not change it. If the constant is not // deterministic, adding an additional mention of it in MIR will not give the same value as // the former mention. - if let Value::Constant { value, disambiguator: DETERMINISTIC } = *self.get(index) { + if let Value::Constant { value, disambiguator: None } = *self.get(index) { debug_assert!(value.is_deterministic()); return Some(ConstOperand { span: DUMMY_SP, user_ty: None, const_: value }); } From d51aec7781186ae0a7ac1073f672d08729d32f6a Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sat, 13 Sep 2025 17:36:46 +0000 Subject: [PATCH 474/682] Add test. --- ...ap.DestinationPropagation.panic-abort.diff | 21 ++++++++ ...p.DestinationPropagation.panic-unwind.diff | 21 ++++++++ tests/mir-opt/dest-prop/aggregate.rs | 49 +++++++++++++++++++ ...ap.DestinationPropagation.panic-abort.diff | 22 +++++++++ ...p.DestinationPropagation.panic-unwind.diff | 22 +++++++++ 5 files changed, 135 insertions(+) create mode 100644 tests/mir-opt/dest-prop/aggregate.rewrap.DestinationPropagation.panic-abort.diff create mode 100644 tests/mir-opt/dest-prop/aggregate.rewrap.DestinationPropagation.panic-unwind.diff create mode 100644 tests/mir-opt/dest-prop/aggregate.rs create mode 100644 tests/mir-opt/dest-prop/aggregate.swap.DestinationPropagation.panic-abort.diff create mode 100644 tests/mir-opt/dest-prop/aggregate.swap.DestinationPropagation.panic-unwind.diff diff --git a/tests/mir-opt/dest-prop/aggregate.rewrap.DestinationPropagation.panic-abort.diff b/tests/mir-opt/dest-prop/aggregate.rewrap.DestinationPropagation.panic-abort.diff new file mode 100644 index 000000000000..876c871cddde --- /dev/null +++ b/tests/mir-opt/dest-prop/aggregate.rewrap.DestinationPropagation.panic-abort.diff @@ -0,0 +1,21 @@ +- // MIR for `rewrap` before DestinationPropagation ++ // MIR for `rewrap` after DestinationPropagation + + fn rewrap() -> (u8,) { + let mut _0: (u8,); + let mut _1: (u8,); + let mut _2: (u8,); + + bb0: { +- (_1.0: u8) = const 0_u8; +- _0 = copy _1; +- _2 = (copy (_0.0: u8),); +- _0 = copy _2; ++ (_0.0: u8) = const 0_u8; ++ nop; ++ _0 = (copy (_0.0: u8),); ++ nop; + return; + } + } + diff --git a/tests/mir-opt/dest-prop/aggregate.rewrap.DestinationPropagation.panic-unwind.diff b/tests/mir-opt/dest-prop/aggregate.rewrap.DestinationPropagation.panic-unwind.diff new file mode 100644 index 000000000000..876c871cddde --- /dev/null +++ b/tests/mir-opt/dest-prop/aggregate.rewrap.DestinationPropagation.panic-unwind.diff @@ -0,0 +1,21 @@ +- // MIR for `rewrap` before DestinationPropagation ++ // MIR for `rewrap` after DestinationPropagation + + fn rewrap() -> (u8,) { + let mut _0: (u8,); + let mut _1: (u8,); + let mut _2: (u8,); + + bb0: { +- (_1.0: u8) = const 0_u8; +- _0 = copy _1; +- _2 = (copy (_0.0: u8),); +- _0 = copy _2; ++ (_0.0: u8) = const 0_u8; ++ nop; ++ _0 = (copy (_0.0: u8),); ++ nop; + return; + } + } + diff --git a/tests/mir-opt/dest-prop/aggregate.rs b/tests/mir-opt/dest-prop/aggregate.rs new file mode 100644 index 000000000000..3c14d1f94f69 --- /dev/null +++ b/tests/mir-opt/dest-prop/aggregate.rs @@ -0,0 +1,49 @@ +//@ test-mir-pass: DestinationPropagation +// EMIT_MIR_FOR_EACH_PANIC_STRATEGY + +#![feature(custom_mir, core_intrinsics)] +#![allow(internal_features)] + +use std::intrinsics::mir::*; +use std::mem::MaybeUninit; + +fn dump_var(_: T) {} + +// EMIT_MIR aggregate.rewrap.DestinationPropagation.diff +#[custom_mir(dialect = "runtime")] +fn rewrap() -> (u8,) { + // CHECK-LABEL: fn rewrap( + // CHECK: (_0.0: u8) = const 0_u8; + // CHECK: _0 = (copy (_0.0: u8),); + mir! { + let _1: (u8,); + let _2: (u8,); + { + _1.0 = 0; + RET = _1; + _2 = (RET.0, ); + RET = _2; + Return() + } + } +} + +// EMIT_MIR aggregate.swap.DestinationPropagation.diff +#[custom_mir(dialect = "runtime")] +fn swap() -> (MaybeUninit<[u8; 10]>, MaybeUninit<[u8; 10]>) { + // CHECK-LABEL: fn swap( + // CHECK: _0 = const + // CHECK: _0 = (copy (_0.1: {{.*}}), copy (_0.0: {{.*}})); + mir! { + let _1: (MaybeUninit<[u8; 10]>, MaybeUninit<[u8; 10]>); + let _2: (MaybeUninit<[u8; 10]>, MaybeUninit<[u8; 10]>); + let _3: (); + { + _1 = const { (MaybeUninit::new([0; 10]), MaybeUninit::new([1; 10])) }; + _2 = _1; + _1 = (_2.1, _2.0); + RET = _1; + Return() + } + } +} diff --git a/tests/mir-opt/dest-prop/aggregate.swap.DestinationPropagation.panic-abort.diff b/tests/mir-opt/dest-prop/aggregate.swap.DestinationPropagation.panic-abort.diff new file mode 100644 index 000000000000..53849573f134 --- /dev/null +++ b/tests/mir-opt/dest-prop/aggregate.swap.DestinationPropagation.panic-abort.diff @@ -0,0 +1,22 @@ +- // MIR for `swap` before DestinationPropagation ++ // MIR for `swap` after DestinationPropagation + + fn swap() -> (MaybeUninit<[u8; 10]>, MaybeUninit<[u8; 10]>) { + let mut _0: (std::mem::MaybeUninit<[u8; 10]>, std::mem::MaybeUninit<[u8; 10]>); + let mut _1: (std::mem::MaybeUninit<[u8; 10]>, std::mem::MaybeUninit<[u8; 10]>); + let mut _2: (std::mem::MaybeUninit<[u8; 10]>, std::mem::MaybeUninit<[u8; 10]>); + let mut _3: (); + + bb0: { +- _1 = const swap::{constant#6}; +- _2 = copy _1; +- _1 = (copy (_2.1: std::mem::MaybeUninit<[u8; 10]>), copy (_2.0: std::mem::MaybeUninit<[u8; 10]>)); +- _0 = copy _1; ++ _0 = const swap::{constant#6}; ++ nop; ++ _0 = (copy (_0.1: std::mem::MaybeUninit<[u8; 10]>), copy (_0.0: std::mem::MaybeUninit<[u8; 10]>)); ++ nop; + return; + } + } + diff --git a/tests/mir-opt/dest-prop/aggregate.swap.DestinationPropagation.panic-unwind.diff b/tests/mir-opt/dest-prop/aggregate.swap.DestinationPropagation.panic-unwind.diff new file mode 100644 index 000000000000..53849573f134 --- /dev/null +++ b/tests/mir-opt/dest-prop/aggregate.swap.DestinationPropagation.panic-unwind.diff @@ -0,0 +1,22 @@ +- // MIR for `swap` before DestinationPropagation ++ // MIR for `swap` after DestinationPropagation + + fn swap() -> (MaybeUninit<[u8; 10]>, MaybeUninit<[u8; 10]>) { + let mut _0: (std::mem::MaybeUninit<[u8; 10]>, std::mem::MaybeUninit<[u8; 10]>); + let mut _1: (std::mem::MaybeUninit<[u8; 10]>, std::mem::MaybeUninit<[u8; 10]>); + let mut _2: (std::mem::MaybeUninit<[u8; 10]>, std::mem::MaybeUninit<[u8; 10]>); + let mut _3: (); + + bb0: { +- _1 = const swap::{constant#6}; +- _2 = copy _1; +- _1 = (copy (_2.1: std::mem::MaybeUninit<[u8; 10]>), copy (_2.0: std::mem::MaybeUninit<[u8; 10]>)); +- _0 = copy _1; ++ _0 = const swap::{constant#6}; ++ nop; ++ _0 = (copy (_0.1: std::mem::MaybeUninit<[u8; 10]>), copy (_0.0: std::mem::MaybeUninit<[u8; 10]>)); ++ nop; + return; + } + } + From cc9225406c75a854a9c91bd9f75ed2cbe9cee4c4 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 10 May 2023 09:19:31 +0000 Subject: [PATCH 475/682] Document `become` keyword --- library/std/src/keyword_docs.rs | 99 +++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs index 1c55824ab906..dc35b05b7502 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/std/src/keyword_docs.rs @@ -1257,6 +1257,105 @@ mod ref_keyword {} /// [`async`]: ../std/keyword.async.html mod return_keyword {} +#[doc(keyword = "become")] +// +/// Perform a tail-call of a function. +/// +///
+/// +/// `feature(explicit_tail_calls)` is currently incomplete and may not work properly. +///
+/// +/// When tail calling a function, instead of its stack frame being added to the +/// stack, the stack frame of the caller is directly replaced with the callee's. +/// This means that as long as a loop in a call graph only uses tail calls, the +/// stack growth will be bounded. +/// +/// This is useful for writing functional-style code (since it prevents recursion +/// from exhausting resources) or for code optimization (since a tail call +/// *might* be cheaper than a normal call, tail calls can be used in a similar +/// manner to computed goto). +/// +/// Example of using `become` to implement functional-style `fold`: +/// ``` +/// #![feature(explicit_tail_calls)] +/// #![expect(incomplete_features)] +/// +/// fn fold(slice: &[T], init: S, f: impl Fn(S, T) -> S) -> S { +/// match slice { +/// // without `become`, on big inputs this could easily overflow the +/// // stack. using a tail call guarantees that the stack will not grow unboundedly +/// [first, rest @ ..] => become fold(rest, f(init, *first), f), +/// [] => init, +/// } +/// } +/// ``` +/// +/// Compilers can already perform "tail call optimization" -- they can replace normal +/// calls with tail calls, although there are no guarantees that this will be done. +/// However, to perform TCO, the call needs to be the last thing that happens +/// in the functions and be returned from it. This requirement is often broken +/// by drop code for locals, which is run after computing the return expression: +/// +/// ``` +/// fn example() { +/// let string = "meow".to_owned(); +/// println!("{string}"); +/// return help(); // this is *not* the last thing that happens in `example`... +/// } +/// +/// // ... because it is desugared to this: +/// fn example_desugared() { +/// let string = "meow".to_owned(); +/// println!("{string}"); +/// let tmp = help(); +/// drop(string); +/// return tmp; +/// } +/// +/// fn help() {} +/// ``` +/// +/// For this reason, `become` also changes the drop order, such that locals are +/// dropped *before* evaluating the call. +/// +/// In order to guarantee that the compiler can perform a tail call, `become` +/// currently has these requirements: +/// 1. callee and caller must have the same ABI, arguments, and return type +/// 2. callee and caller must not have varargs +/// 3. callee and caller must not be marked with `#[track_caller]` +/// 4. callee and caller cannot be a closure +/// (unless it's coerced to a function pointer) +/// +/// It is possible to tail-call a function pointer: +/// ``` +/// #![feature(explicit_tail_calls)] +/// #![expect(incomplete_features)] +/// +/// #[derive(Copy, Clone)] +/// enum Inst { Inc, Dec } +/// +/// fn dispatch(stream: &[Inst], state: u32) -> u32 { +/// const TABLE: &[fn(&[Inst], u32) -> u32] = &[increment, decrement]; +/// match stream { +/// [inst, rest @ ..] => become TABLE[*inst as usize](rest, state), +/// [] => state, +/// } +/// } +/// +/// fn increment(stream: &[Inst], state: u32) -> u32 { +/// become dispatch(stream, state + 1) +/// } +/// +/// fn decrement(stream: &[Inst], state: u32) -> u32 { +/// become dispatch(stream, state - 1) +/// } +/// +/// let program = &[Inst::Inc, Inst::Inc, Inst::Dec, Inst::Inc]; +/// assert_eq!(dispatch(program, 0), 2); +/// ``` +mod become_keyword {} + #[doc(keyword = "self")] // /// The receiver of a method, or the current module. From f13c8c21925d8226811fefaceacd414c9d8f35d9 Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Sat, 13 Sep 2025 19:44:06 +0200 Subject: [PATCH 476/682] fixup `become` kw documentation wrt `#[track_caller]` --- library/std/src/keyword_docs.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs index dc35b05b7502..7ff4af8ede84 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/std/src/keyword_docs.rs @@ -1323,7 +1323,10 @@ mod return_keyword {} /// currently has these requirements: /// 1. callee and caller must have the same ABI, arguments, and return type /// 2. callee and caller must not have varargs -/// 3. callee and caller must not be marked with `#[track_caller]` +/// 3. caller must not be marked with `#[track_caller]` +/// - callee is allowed to be marked with `#[track_caller]` as otherwise +/// adding `#[track_caller]` would be a breaking change. if callee is +/// marked with `#[track_caller]` a tail call is not guaranteed. /// 4. callee and caller cannot be a closure /// (unless it's coerced to a function pointer) /// From aee7d703c58b68611785684bbb8e402c1471992b Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sat, 13 Sep 2025 18:07:22 +0000 Subject: [PATCH 477/682] Mark reads in statements to avoid overlapping assingments. --- compiler/rustc_mir_transform/src/dest_prop.rs | 29 ++++++++++++------- ...ap.DestinationPropagation.panic-abort.diff | 6 ++-- ...p.DestinationPropagation.panic-unwind.diff | 6 ++-- tests/mir-opt/dest-prop/aggregate.rs | 6 ++-- ...ap.DestinationPropagation.panic-abort.diff | 4 +-- ...p.DestinationPropagation.panic-unwind.diff | 4 +-- 6 files changed, 31 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index 9ba2d274691d..7e6f39881b8b 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -567,13 +567,15 @@ fn save_as_intervals<'tcx>( // the written-to locals as live in the second half of the statement. // We also ensure that operands read by terminators conflict with writes by that terminator. // For instance a function call may read args after having written to the destination. - VisitPlacesWith(|place, ctxt| match DefUse::for_place(place, ctxt) { - DefUse::Def | DefUse::Use | DefUse::PartialWrite => { - if let Some(relevant) = relevant.shrink[place.local] { - values.insert(relevant, twostep); + VisitPlacesWith(|place: Place<'tcx>, ctxt| { + if let Some(relevant) = relevant.shrink[place.local] { + match DefUse::for_place(place, ctxt) { + DefUse::Def | DefUse::Use | DefUse::PartialWrite => { + values.insert(relevant, twostep); + } + DefUse::NonUse => {} } } - DefUse::NonUse => {} }) .visit_terminator(term, loc); @@ -590,13 +592,20 @@ fn save_as_intervals<'tcx>( append_at(&mut values, &state, twostep); // Ensure we have a non-zero live range even for dead stores. This is done by marking // all the written-to locals as live in the second half of the statement. - VisitPlacesWith(|place, ctxt| match DefUse::for_place(place, ctxt) { - DefUse::Def | DefUse::PartialWrite => { - if let Some(relevant) = relevant.shrink[place.local] { - values.insert(relevant, twostep); + let is_simple_assignment = + matches!(stmt.kind, StatementKind::Assign(box (_, Rvalue::Use(_)))); + VisitPlacesWith(|place: Place<'tcx>, ctxt| { + if let Some(relevant) = relevant.shrink[place.local] { + match DefUse::for_place(place, ctxt) { + DefUse::Def | DefUse::PartialWrite => { + values.insert(relevant, twostep); + } + DefUse::Use if !is_simple_assignment => { + values.insert(relevant, twostep); + } + DefUse::Use | DefUse::NonUse => {} } } - DefUse::Use | DefUse::NonUse => {} }) .visit_statement(stmt, loc); diff --git a/tests/mir-opt/dest-prop/aggregate.rewrap.DestinationPropagation.panic-abort.diff b/tests/mir-opt/dest-prop/aggregate.rewrap.DestinationPropagation.panic-abort.diff index 876c871cddde..e80660f176b7 100644 --- a/tests/mir-opt/dest-prop/aggregate.rewrap.DestinationPropagation.panic-abort.diff +++ b/tests/mir-opt/dest-prop/aggregate.rewrap.DestinationPropagation.panic-abort.diff @@ -9,12 +9,10 @@ bb0: { - (_1.0: u8) = const 0_u8; - _0 = copy _1; -- _2 = (copy (_0.0: u8),); -- _0 = copy _2; + (_0.0: u8) = const 0_u8; + nop; -+ _0 = (copy (_0.0: u8),); -+ nop; + _2 = (copy (_0.0: u8),); + _0 = copy _2; return; } } diff --git a/tests/mir-opt/dest-prop/aggregate.rewrap.DestinationPropagation.panic-unwind.diff b/tests/mir-opt/dest-prop/aggregate.rewrap.DestinationPropagation.panic-unwind.diff index 876c871cddde..e80660f176b7 100644 --- a/tests/mir-opt/dest-prop/aggregate.rewrap.DestinationPropagation.panic-unwind.diff +++ b/tests/mir-opt/dest-prop/aggregate.rewrap.DestinationPropagation.panic-unwind.diff @@ -9,12 +9,10 @@ bb0: { - (_1.0: u8) = const 0_u8; - _0 = copy _1; -- _2 = (copy (_0.0: u8),); -- _0 = copy _2; + (_0.0: u8) = const 0_u8; + nop; -+ _0 = (copy (_0.0: u8),); -+ nop; + _2 = (copy (_0.0: u8),); + _0 = copy _2; return; } } diff --git a/tests/mir-opt/dest-prop/aggregate.rs b/tests/mir-opt/dest-prop/aggregate.rs index 3c14d1f94f69..636852159eb4 100644 --- a/tests/mir-opt/dest-prop/aggregate.rs +++ b/tests/mir-opt/dest-prop/aggregate.rs @@ -14,7 +14,8 @@ fn dump_var(_: T) {} fn rewrap() -> (u8,) { // CHECK-LABEL: fn rewrap( // CHECK: (_0.0: u8) = const 0_u8; - // CHECK: _0 = (copy (_0.0: u8),); + // CHECK: _2 = (copy (_0.0: u8),); + // CHECK: _0 = copy _2; mir! { let _1: (u8,); let _2: (u8,); @@ -33,7 +34,8 @@ fn rewrap() -> (u8,) { fn swap() -> (MaybeUninit<[u8; 10]>, MaybeUninit<[u8; 10]>) { // CHECK-LABEL: fn swap( // CHECK: _0 = const - // CHECK: _0 = (copy (_0.1: {{.*}}), copy (_0.0: {{.*}})); + // CHECK: _2 = copy _0; + // CHECK: _0 = (copy (_2.1: {{.*}}), copy (_2.0: {{.*}})); mir! { let _1: (MaybeUninit<[u8; 10]>, MaybeUninit<[u8; 10]>); let _2: (MaybeUninit<[u8; 10]>, MaybeUninit<[u8; 10]>); diff --git a/tests/mir-opt/dest-prop/aggregate.swap.DestinationPropagation.panic-abort.diff b/tests/mir-opt/dest-prop/aggregate.swap.DestinationPropagation.panic-abort.diff index 53849573f134..3aaad3aaf692 100644 --- a/tests/mir-opt/dest-prop/aggregate.swap.DestinationPropagation.panic-abort.diff +++ b/tests/mir-opt/dest-prop/aggregate.swap.DestinationPropagation.panic-abort.diff @@ -13,8 +13,8 @@ - _1 = (copy (_2.1: std::mem::MaybeUninit<[u8; 10]>), copy (_2.0: std::mem::MaybeUninit<[u8; 10]>)); - _0 = copy _1; + _0 = const swap::{constant#6}; -+ nop; -+ _0 = (copy (_0.1: std::mem::MaybeUninit<[u8; 10]>), copy (_0.0: std::mem::MaybeUninit<[u8; 10]>)); ++ _2 = copy _0; ++ _0 = (copy (_2.1: std::mem::MaybeUninit<[u8; 10]>), copy (_2.0: std::mem::MaybeUninit<[u8; 10]>)); + nop; return; } diff --git a/tests/mir-opt/dest-prop/aggregate.swap.DestinationPropagation.panic-unwind.diff b/tests/mir-opt/dest-prop/aggregate.swap.DestinationPropagation.panic-unwind.diff index 53849573f134..3aaad3aaf692 100644 --- a/tests/mir-opt/dest-prop/aggregate.swap.DestinationPropagation.panic-unwind.diff +++ b/tests/mir-opt/dest-prop/aggregate.swap.DestinationPropagation.panic-unwind.diff @@ -13,8 +13,8 @@ - _1 = (copy (_2.1: std::mem::MaybeUninit<[u8; 10]>), copy (_2.0: std::mem::MaybeUninit<[u8; 10]>)); - _0 = copy _1; + _0 = const swap::{constant#6}; -+ nop; -+ _0 = (copy (_0.1: std::mem::MaybeUninit<[u8; 10]>), copy (_0.0: std::mem::MaybeUninit<[u8; 10]>)); ++ _2 = copy _0; ++ _0 = (copy (_2.1: std::mem::MaybeUninit<[u8; 10]>), copy (_2.0: std::mem::MaybeUninit<[u8; 10]>)); + nop; return; } From f21d3fbce96b956283bdda0557fb73604a0fd489 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 13 Sep 2025 20:13:37 +0200 Subject: [PATCH 478/682] fix Condvar::wait_timeout docs --- library/std/src/sync/nonpoison/condvar.rs | 9 ++++----- library/std/src/sync/poison/condvar.rs | 9 ++++----- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/library/std/src/sync/nonpoison/condvar.rs b/library/std/src/sync/nonpoison/condvar.rs index 49afdd878182..994fc6816a0d 100644 --- a/library/std/src/sync/nonpoison/condvar.rs +++ b/library/std/src/sync/nonpoison/condvar.rs @@ -198,11 +198,10 @@ impl Condvar { /// the system time. This function is susceptible to spurious wakeups. /// Condition variables normally have a boolean predicate associated with /// them, and the predicate must always be checked each time this function - /// returns to protect against spurious wakeups. Additionally, it is - /// typically desirable for the timeout to not exceed some duration in - /// spite of spurious wakes, thus the sleep-duration is decremented by the - /// amount slept. Alternatively, use the `wait_timeout_while` method - /// to wait with a timeout while a predicate is true. + /// returns to protect against spurious wakeups. Furthermore, since the timeout + /// is given relative to the moment this function is called, it needs to be adjusted + /// when this function is called in a loop. The [`wait_timeout_while`] method + /// lets you wait with a timeout while a predicate is true, taking care of all these concerns. /// /// The returned [`WaitTimeoutResult`] value indicates if the timeout is /// known to have elapsed. diff --git a/library/std/src/sync/poison/condvar.rs b/library/std/src/sync/poison/condvar.rs index 5dc2b510f3a2..de625a6cc5f6 100644 --- a/library/std/src/sync/poison/condvar.rs +++ b/library/std/src/sync/poison/condvar.rs @@ -269,11 +269,10 @@ impl Condvar { /// the system time. This function is susceptible to spurious wakeups. /// Condition variables normally have a boolean predicate associated with /// them, and the predicate must always be checked each time this function - /// returns to protect against spurious wakeups. Additionally, it is - /// typically desirable for the timeout to not exceed some duration in - /// spite of spurious wakes, thus the sleep-duration is decremented by the - /// amount slept. Alternatively, use the `wait_timeout_while` method - /// to wait with a timeout while a predicate is true. + /// returns to protect against spurious wakeups. Furthermore, since the timeout + /// is given relative to the moment this function is called, it needs to be adjusted + /// when this function is called in a loop. The [`wait_timeout_while`] method + /// lets you wait with a timeout while a predicate is true, taking care of all these concerns. /// /// The returned [`WaitTimeoutResult`] value indicates if the timeout is /// known to have elapsed. From afd324aa2c64bb40d4f6236e56855010591a7e90 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Sat, 13 Sep 2025 19:25:44 +0100 Subject: [PATCH 479/682] Note that these targets are bare-metal. This is important to note, as it affects how easy it is to build a binary, and that `#![no_std]` is mandatory. A different PR should probably add this to all the other platform pages. --- src/doc/rustc/src/platform-support/aarch64-unknown-none.md | 3 ++- src/doc/rustc/src/platform-support/armebv7r-none-eabi.md | 3 ++- src/doc/rustc/src/platform-support/armv4t-none-eabi.md | 3 ++- src/doc/rustc/src/platform-support/armv5te-none-eabi.md | 3 ++- src/doc/rustc/src/platform-support/armv7a-none-eabi.md | 5 +++-- src/doc/rustc/src/platform-support/armv7r-none-eabi.md | 3 ++- src/doc/rustc/src/platform-support/armv8r-none-eabihf.md | 3 ++- src/doc/rustc/src/platform-support/thumbv6m-none-eabi.md | 3 ++- src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md | 3 ++- src/doc/rustc/src/platform-support/thumbv7m-none-eabi.md | 3 ++- .../rustc/src/platform-support/thumbv8m.base-none-eabi.md | 3 ++- .../rustc/src/platform-support/thumbv8m.main-none-eabi.md | 3 ++- 12 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/doc/rustc/src/platform-support/aarch64-unknown-none.md b/src/doc/rustc/src/platform-support/aarch64-unknown-none.md index 25d99e4f0dea..7e18e8c157f7 100644 --- a/src/doc/rustc/src/platform-support/aarch64-unknown-none.md +++ b/src/doc/rustc/src/platform-support/aarch64-unknown-none.md @@ -1,6 +1,7 @@ # `aarch64-unknown-none` and `aarch64-unknown-none-softfloat` -**Tier: 2** +* **Tier: 2** +* **Library Support:** core and alloc (bare-metal, `#![no_std]`) Bare-metal targets for CPUs in the Armv8-A architecture family, running in AArch64 mode. diff --git a/src/doc/rustc/src/platform-support/armebv7r-none-eabi.md b/src/doc/rustc/src/platform-support/armebv7r-none-eabi.md index d5f828110f16..3e90319c373e 100644 --- a/src/doc/rustc/src/platform-support/armebv7r-none-eabi.md +++ b/src/doc/rustc/src/platform-support/armebv7r-none-eabi.md @@ -1,6 +1,7 @@ # `armebv7r-none-eabi` and `armebv7r-none-eabihf` -**Tier: 2** +* **Tier: 2** +* **Library Support:** core and alloc (bare-metal, `#![no_std]`) Bare-metal target for CPUs in the Armv7-R architecture family running in Big Endian mode. These processors support dual ARM/Thumb mode, with ARM mode as diff --git a/src/doc/rustc/src/platform-support/armv4t-none-eabi.md b/src/doc/rustc/src/platform-support/armv4t-none-eabi.md index 56f919e2a12b..c6d88762fb1b 100644 --- a/src/doc/rustc/src/platform-support/armv4t-none-eabi.md +++ b/src/doc/rustc/src/platform-support/armv4t-none-eabi.md @@ -1,6 +1,7 @@ # armv4t-none-eabi / thumbv4t-none-eabi -Tier 3 +* **Tier: 3** +* **Library Support:** core and alloc (bare-metal, `#![no_std]`) These two targets are part of the [`arm-none-eabi`](arm-none-eabi.md) target group, and all the information there applies. diff --git a/src/doc/rustc/src/platform-support/armv5te-none-eabi.md b/src/doc/rustc/src/platform-support/armv5te-none-eabi.md index 22287972b7eb..e9f34d4ede8a 100644 --- a/src/doc/rustc/src/platform-support/armv5te-none-eabi.md +++ b/src/doc/rustc/src/platform-support/armv5te-none-eabi.md @@ -1,6 +1,7 @@ # `armv5te-none-eabi` -**Tier: 3** +* **Tier: 3** +* **Library Support:** core and alloc (bare-metal, `#![no_std]`) Bare-metal target for any cpu in the Armv5TE architecture family, supporting ARM/Thumb code interworking (aka `A32`/`T32`), with `A32` code as the default code diff --git a/src/doc/rustc/src/platform-support/armv7a-none-eabi.md b/src/doc/rustc/src/platform-support/armv7a-none-eabi.md index 0646e7634d7a..3dadda86a5f5 100644 --- a/src/doc/rustc/src/platform-support/armv7a-none-eabi.md +++ b/src/doc/rustc/src/platform-support/armv7a-none-eabi.md @@ -1,7 +1,8 @@ # `armv7a-none-eabi` and `armv7a-none-eabihf` -**Tier: 2** for `armv7a-none-eabi` -**Tier: 3** for `armv7a-none-eabihf` +* **Tier: 2** for `armv7a-none-eabi` +* **Tier: 3** for `armv7a-none-eabihf` +* **Library Support:** core and alloc (bare-metal, `#![no_std]`) Bare-metal target for CPUs in the Armv7-A architecture family, supporting dual ARM/Thumb mode, with ARM mode as the default. diff --git a/src/doc/rustc/src/platform-support/armv7r-none-eabi.md b/src/doc/rustc/src/platform-support/armv7r-none-eabi.md index 9cda9c502543..c1252b4a4bf5 100644 --- a/src/doc/rustc/src/platform-support/armv7r-none-eabi.md +++ b/src/doc/rustc/src/platform-support/armv7r-none-eabi.md @@ -1,6 +1,7 @@ # `armv7r-none-eabi` and `armv7r-none-eabihf` -**Tier: 2** +* **Tier: 2** +* **Library Support:** core and alloc (bare-metal, `#![no_std]`) Bare-metal target for CPUs in the Armv7-R architecture family, supporting dual ARM/Thumb mode, with ARM mode as the default. diff --git a/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md b/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md index eb7f8921ad0e..0d5a36c3ee2d 100644 --- a/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md +++ b/src/doc/rustc/src/platform-support/armv8r-none-eabihf.md @@ -1,6 +1,7 @@ # `armv8r-none-eabihf` -**Tier: 3** +* **Tier: 3** +* **Library Support:** core and alloc (bare-metal, `#![no_std]`) Bare-metal target for CPUs in the Armv8-R architecture family, supporting dual ARM/Thumb mode, with ARM mode as the default. diff --git a/src/doc/rustc/src/platform-support/thumbv6m-none-eabi.md b/src/doc/rustc/src/platform-support/thumbv6m-none-eabi.md index ca47d96cf65f..d4bd0b0945ac 100644 --- a/src/doc/rustc/src/platform-support/thumbv6m-none-eabi.md +++ b/src/doc/rustc/src/platform-support/thumbv6m-none-eabi.md @@ -1,6 +1,7 @@ # `thumbv6m-none-eabi` -**Tier: 2** +* **Tier: 2** +* **Library Support:** core and alloc (bare-metal, `#![no_std]`) Bare-metal target for CPUs in the [Armv6-M] architecture family, supporting a subset of the [T32 ISA][t32-isa]. diff --git a/src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md b/src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md index 9de708cda242..98dcf9bd3968 100644 --- a/src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md +++ b/src/doc/rustc/src/platform-support/thumbv7em-none-eabi.md @@ -1,6 +1,7 @@ # `thumbv7em-none-eabi` and `thumbv7em-none-eabihf` -**Tier: 2** +* **Tier: 2** +* **Library Support:** core and alloc (bare-metal, `#![no_std]`) Bare-metal target for CPUs in the [Armv7E-M] architecture family, supporting a subset of the [T32 ISA][t32-isa]. diff --git a/src/doc/rustc/src/platform-support/thumbv7m-none-eabi.md b/src/doc/rustc/src/platform-support/thumbv7m-none-eabi.md index 05c9146e6d06..d8f3970c8bfa 100644 --- a/src/doc/rustc/src/platform-support/thumbv7m-none-eabi.md +++ b/src/doc/rustc/src/platform-support/thumbv7m-none-eabi.md @@ -1,6 +1,7 @@ # `thumbv7m-none-eabi` -**Tier: 2** +* **Tier: 2** +* **Library Support:** core and alloc (bare-metal, `#![no_std]`) Bare-metal target for CPUs in the [Armv7-M] architecture family, supporting a subset of the [T32 ISA][t32-isa]. diff --git a/src/doc/rustc/src/platform-support/thumbv8m.base-none-eabi.md b/src/doc/rustc/src/platform-support/thumbv8m.base-none-eabi.md index 92585f49bd85..b16d450275df 100644 --- a/src/doc/rustc/src/platform-support/thumbv8m.base-none-eabi.md +++ b/src/doc/rustc/src/platform-support/thumbv8m.base-none-eabi.md @@ -1,6 +1,7 @@ # `thumbv8m.base-none-eabi` -**Tier: 2** +* **Tier: 2** +* **Library Support:** core and alloc (bare-metal, `#![no_std]`) Bare-metal target for CPUs in the Baseline [Armv8-M] architecture family, supporting a subset of the [T32 ISA][t32-isa]. diff --git a/src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md b/src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md index 960cf31567d1..a2d515d07ea0 100644 --- a/src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md +++ b/src/doc/rustc/src/platform-support/thumbv8m.main-none-eabi.md @@ -1,6 +1,7 @@ # `thumbv8m.main-none-eabi` and `thumbv8m.main-none-eabihf` -**Tier: 2** +* **Tier: 2** +* **Library Support:** core and alloc (bare-metal, `#![no_std]`) Bare-metal target for CPUs in the Mainline [Armv8-M] architecture family, supporting a subset of the [T32 ISA][t32-isa]. From cd196aa72a638b627ecdcfd3495b986300af83f1 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sat, 13 Sep 2025 18:27:10 +0200 Subject: [PATCH 480/682] Implement `Debug` for `SourceText` This is useful for using it with `dbg!()`. --- clippy_utils/src/source.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index e675291b6f3a..638d32903123 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -215,6 +215,11 @@ impl fmt::Display for SourceText { self.as_str().fmt(f) } } +impl fmt::Debug for SourceText { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.as_str().fmt(f) + } +} fn get_source_range(sm: &SourceMap, sp: Range) -> Option { let start = sm.lookup_byte_offset(sp.start); From 61a35937861cae48b9ade326f75c00e387e6376a Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 11 Sep 2025 23:58:52 +0200 Subject: [PATCH 481/682] c-variadic: document `core::ffi::VaArgSafe` and document `VaList::arg`. --- library/core/src/ffi/va_list.rs | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/library/core/src/ffi/va_list.rs b/library/core/src/ffi/va_list.rs index 88ad11977774..643bd95df846 100644 --- a/library/core/src/ffi/va_list.rs +++ b/library/core/src/ffi/va_list.rs @@ -202,18 +202,23 @@ mod sealed { impl Sealed for *const T {} } -/// Trait which permits the allowed types to be used with [`VaListImpl::arg`]. +/// Types that are valid to read using [`VaListImpl::arg`]. /// /// # Safety /// -/// This trait must only be implemented for types that C passes as varargs without implicit promotion. +/// The standard library implements this trait for primitive types that are +/// expected to have a variable argument application-binary interface (ABI) on all +/// platforms. /// -/// In C varargs, integers smaller than [`c_int`] and floats smaller than [`c_double`] -/// are implicitly promoted to [`c_int`] and [`c_double`] respectively. Implementing this trait for -/// types that are subject to this promotion rule is invalid. +/// When C passes variable arguments, integers smaller than [`c_int`] and floats smaller +/// than [`c_double`] are implicitly promoted to [`c_int`] and [`c_double`] respectively. +/// Implementing this trait for types that are subject to this promotion rule is invalid. /// /// [`c_int`]: core::ffi::c_int /// [`c_double`]: core::ffi::c_double +// We may unseal this trait in the future, but currently our `va_arg` implementations don't support +// types with an alignment larger than 8, or with a non-scalar layout. Inline assembly can be used +// to accept unsupported types in the meantime. pub unsafe trait VaArgSafe: sealed::Sealed {} // i8 and i16 are implicitly promoted to c_int in C, and cannot implement `VaArgSafe`. @@ -233,7 +238,19 @@ unsafe impl VaArgSafe for *mut T {} unsafe impl VaArgSafe for *const T {} impl<'f> VaListImpl<'f> { - /// Advance to the next arg. + /// Advance to and read the next variable argument. + /// + /// # Safety + /// + /// This function is only sound to call when the next variable argument: + /// + /// - has a type that is ABI-compatible with the type `T` + /// - has a value that is a properly initialized value of type `T` + /// + /// Calling this function with an incompatible type, an invalid value, or when there + /// are no more variable arguments, is unsound. + /// + /// [valid]: https://doc.rust-lang.org/nightly/nomicon/what-unsafe-does.html #[inline] pub unsafe fn arg(&mut self) -> T { // SAFETY: the caller must uphold the safety contract for `va_arg`. From a84bb32e05d7dfa069ead63e90816e1833f107c9 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 13 Sep 2025 20:49:21 +0200 Subject: [PATCH 482/682] c-variadic: test `...` with naked functions --- tests/ui/c-variadic/naked.rs | 40 ++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 tests/ui/c-variadic/naked.rs diff --git a/tests/ui/c-variadic/naked.rs b/tests/ui/c-variadic/naked.rs new file mode 100644 index 000000000000..46b59395485c --- /dev/null +++ b/tests/ui/c-variadic/naked.rs @@ -0,0 +1,40 @@ +//@ run-pass +//@ only-x86_64 +//@ only-linux +#![feature(c_variadic)] + +#[repr(C)] +#[derive(Debug, PartialEq)] +struct Data(i32, f64); + +#[unsafe(naked)] +unsafe extern "C" fn c_variadic(_: ...) -> Data { + // This assembly was generated with GCC, because clang/LLVM is unable to + // optimize out the spilling of all registers to the stack. + core::arch::naked_asm!( + " sub rsp, 96", + " mov QWORD PTR [rsp-88], rdi", + " test al, al", + " je .L7", + " movaps XMMWORD PTR [rsp-40], xmm0", + ".L7:", + " lea rax, [rsp+104]", + " mov rcx, QWORD PTR [rsp-40]", + " mov DWORD PTR [rsp-112], 0", + " mov QWORD PTR [rsp-104], rax", + " lea rax, [rsp-88]", + " mov QWORD PTR [rsp-96], rax", + " movq xmm0, rcx", + " mov eax, DWORD PTR [rsp-88]", + " mov DWORD PTR [rsp-108], 48", + " add rsp, 96", + " ret", + ) +} + +fn main() { + unsafe { + assert_eq!(c_variadic(1, 2.0), Data(1, 2.0)); + assert_eq!(c_variadic(123, 4.56), Data(123, 4.56)); + } +} From d28c31a60069d6c6019341edd2b2b26d903f081c Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 13 Sep 2025 20:50:56 +0200 Subject: [PATCH 483/682] c-variadic: check that c-variadic functions cannot be tail-called as far as I can see this was not tested, though the error message was already implemented --- tests/ui/explicit-tail-calls/c-variadic.rs | 14 ++++++++++++++ tests/ui/explicit-tail-calls/c-variadic.stderr | 8 ++++++++ 2 files changed, 22 insertions(+) create mode 100644 tests/ui/explicit-tail-calls/c-variadic.rs create mode 100644 tests/ui/explicit-tail-calls/c-variadic.stderr diff --git a/tests/ui/explicit-tail-calls/c-variadic.rs b/tests/ui/explicit-tail-calls/c-variadic.rs new file mode 100644 index 000000000000..e6eebe4228e5 --- /dev/null +++ b/tests/ui/explicit-tail-calls/c-variadic.rs @@ -0,0 +1,14 @@ +#![expect(incomplete_features)] +#![feature(c_variadic, explicit_tail_calls)] +#![allow(unused)] + +unsafe extern "C" fn foo(mut ap: ...) -> u32 { + ap.arg::() +} + +extern "C" fn bar() -> u32 { + unsafe { become foo(1, 2, 3) } + //~^ ERROR c-variadic functions can't be tail-called +} + +fn main() {} diff --git a/tests/ui/explicit-tail-calls/c-variadic.stderr b/tests/ui/explicit-tail-calls/c-variadic.stderr new file mode 100644 index 000000000000..5293339d2188 --- /dev/null +++ b/tests/ui/explicit-tail-calls/c-variadic.stderr @@ -0,0 +1,8 @@ +error: c-variadic functions can't be tail-called + --> $DIR/c-variadic.rs:10:14 + | +LL | unsafe { become foo(1, 2, 3) } + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + From a107ea18af5274c3f7e82fa917f3bda6eeb591fe Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 13 Sep 2025 20:53:42 +0200 Subject: [PATCH 484/682] c-variadic: check that inline attributes are accepted on c-variadic functions they don't do anything, because LLVM is unable to inline c-variadic functions (on most targets, anyway) --- tests/codegen-llvm/cffi/c-variadic-inline.rs | 47 ++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 tests/codegen-llvm/cffi/c-variadic-inline.rs diff --git a/tests/codegen-llvm/cffi/c-variadic-inline.rs b/tests/codegen-llvm/cffi/c-variadic-inline.rs new file mode 100644 index 000000000000..369b7e571cab --- /dev/null +++ b/tests/codegen-llvm/cffi/c-variadic-inline.rs @@ -0,0 +1,47 @@ +//@ compile-flags: -C opt-level=3 +#![feature(c_variadic)] + +// Test that the inline attributes are accepted on C-variadic functions. +// +// Currently LLVM is unable to inline C-variadic functions, but that is valid because despite +// the name even `#[inline(always)]` is just a hint. + +#[inline(always)] +unsafe extern "C" fn inline_always(mut ap: ...) -> u32 { + ap.arg::() +} + +#[inline] +unsafe extern "C" fn inline(mut ap: ...) -> u32 { + ap.arg::() +} + +#[inline(never)] +unsafe extern "C" fn inline_never(mut ap: ...) -> u32 { + ap.arg::() +} + +#[cold] +unsafe extern "C" fn cold(mut ap: ...) -> u32 { + ap.arg::() +} + +#[unsafe(no_mangle)] +#[inline(never)] +fn helper() { + // CHECK-LABEL: helper + // CHECK-LABEL: call c_variadic_inline::inline_always + // CHECK-LABEL: call c_variadic_inline::inline + // CHECK-LABEL: call c_variadic_inline::inline_never + // CHECK-LABEL: call c_variadic_inline::cold + unsafe { + inline_always(1); + inline(2); + inline_never(3); + cold(4); + } +} + +fn main() { + helper() +} From 1ebf69d1b1a94e99c01680514571c41d0b864c15 Mon Sep 17 00:00:00 2001 From: Jo Bates <29763794+jbatez@users.noreply.github.com> Date: Wed, 4 Jun 2025 19:49:13 -0700 Subject: [PATCH 485/682] initial implementation of the darwin_objc unstable feature --- compiler/rustc_attr_parsing/messages.ftl | 8 + .../src/attributes/codegen_attrs.rs | 69 +++++- compiler/rustc_attr_parsing/src/context.rs | 6 +- .../src/session_diagnostics.rs | 28 +++ compiler/rustc_codegen_llvm/src/base.rs | 24 +- compiler/rustc_codegen_llvm/src/common.rs | 14 ++ compiler/rustc_codegen_llvm/src/consts.rs | 226 +++++++++++++++++- compiler/rustc_codegen_llvm/src/context.rs | 81 ++++++- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 1 + compiler/rustc_codegen_llvm/src/llvm/mod.rs | 4 + .../rustc_codegen_ssa/src/codegen_attrs.rs | 6 + compiler/rustc_feature/src/builtin_attrs.rs | 8 + .../rustc_hir/src/attrs/data_structures.rs | 6 + .../rustc_hir/src/attrs/encode_cross_crate.rs | 2 + .../src/middle/codegen_fn_attrs.rs | 6 + compiler/rustc_passes/src/check_attr.rs | 2 + compiler/rustc_span/src/symbol.rs | 2 + library/core/src/lib.rs | 1 + library/core/src/os/darwin/mod.rs | 19 ++ library/core/src/os/darwin/objc.rs | 113 +++++++++ library/core/src/os/mod.rs | 24 ++ library/std/src/os/darwin/mod.rs | 2 + library/std/src/os/darwin/objc.rs | 13 + src/tools/tidy/src/pal.rs | 1 + .../codegen-llvm/auxiliary/darwin_objc_aux.rs | 27 +++ tests/codegen-llvm/darwin-no-objc.rs | 52 ++++ tests/codegen-llvm/darwin-objc-abi-v1.rs | 100 ++++++++ tests/codegen-llvm/darwin-objc-abi-v2.rs | 185 ++++++++++++++ tests/codegen-llvm/darwin-objc-cross-crate.rs | 58 +++++ tests/ui/README.md | 6 + tests/ui/darwin-objc/darwin-objc-bad-arg.rs | 36 +++ .../ui/darwin-objc/darwin-objc-bad-arg.stderr | 50 ++++ tests/ui/darwin-objc/darwin-objc-bad-const.rs | 17 ++ .../darwin-objc/darwin-objc-bad-const.stderr | 19 ++ tests/ui/darwin-objc/darwin-objc-bad-ref.rs | 31 +++ .../ui/darwin-objc/darwin-objc-bad-ref.stderr | 39 +++ .../darwin-objc/darwin-objc-class-selector.rs | 31 +++ tests/ui/darwin-objc/darwin-objc-class.rs | 39 +++ tests/ui/darwin-objc/darwin-objc-selector.rs | 36 +++ 39 files changed, 1381 insertions(+), 11 deletions(-) create mode 100644 library/core/src/os/darwin/mod.rs create mode 100644 library/core/src/os/darwin/objc.rs create mode 100644 library/core/src/os/mod.rs create mode 100644 library/std/src/os/darwin/objc.rs create mode 100644 tests/codegen-llvm/auxiliary/darwin_objc_aux.rs create mode 100644 tests/codegen-llvm/darwin-no-objc.rs create mode 100644 tests/codegen-llvm/darwin-objc-abi-v1.rs create mode 100644 tests/codegen-llvm/darwin-objc-abi-v2.rs create mode 100644 tests/codegen-llvm/darwin-objc-cross-crate.rs create mode 100644 tests/ui/darwin-objc/darwin-objc-bad-arg.rs create mode 100644 tests/ui/darwin-objc/darwin-objc-bad-arg.stderr create mode 100644 tests/ui/darwin-objc/darwin-objc-bad-const.rs create mode 100644 tests/ui/darwin-objc/darwin-objc-bad-const.stderr create mode 100644 tests/ui/darwin-objc/darwin-objc-bad-ref.rs create mode 100644 tests/ui/darwin-objc/darwin-objc-bad-ref.stderr create mode 100644 tests/ui/darwin-objc/darwin-objc-class-selector.rs create mode 100644 tests/ui/darwin-objc/darwin-objc-class.rs create mode 100644 tests/ui/darwin-objc/darwin-objc-selector.rs diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index 839a5d23c3b4..81ec17077c13 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -122,6 +122,14 @@ attr_parsing_null_on_export = `export_name` may not contain null characters attr_parsing_null_on_link_section = `link_section` may not contain null characters +attr_parsing_null_on_objc_class = `objc::class!` may not contain null characters + +attr_parsing_null_on_objc_selector = `objc::selector!` may not contain null characters + +attr_parsing_objc_class_expected_string_literal = `objc::class!` expected a string literal + +attr_parsing_objc_selector_expected_string_literal = `objc::selector!` expected a string literal + attr_parsing_repr_ident = meta item in `repr` must be an identifier diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index d5d51f2e79ab..262b8213977b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -2,7 +2,10 @@ use rustc_hir::attrs::{CoverageAttrKind, OptimizeAttr, SanitizerSet, UsedBy}; use rustc_session::parse::feature_err; use super::prelude::*; -use crate::session_diagnostics::{NakedFunctionIncompatibleAttribute, NullOnExport}; +use crate::session_diagnostics::{ + NakedFunctionIncompatibleAttribute, NullOnExport, NullOnObjcClass, NullOnObjcSelector, + ObjcClassExpectedStringLiteral, ObjcSelectorExpectedStringLiteral, +}; pub(crate) struct OptimizeParser; @@ -150,6 +153,70 @@ impl SingleAttributeParser for ExportNameParser { } } +pub(crate) struct ObjcClassParser; + +impl SingleAttributeParser for ObjcClassParser { + const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_class]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = + AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]); + const TEMPLATE: AttributeTemplate = template!(NameValueStr: "ClassName"); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + let Some(nv) = args.name_value() else { + cx.expected_name_value(cx.attr_span, None); + return None; + }; + let Some(classname) = nv.value_as_str() else { + // `#[rustc_objc_class = ...]` is expected to be used as an implementatioin detail + // inside a standard library macro, but `cx.expected_string_literal` exposes too much. + // Use a custom error message instead. + cx.emit_err(ObjcClassExpectedStringLiteral { span: nv.value_span }); + return None; + }; + if classname.as_str().contains('\0') { + // `#[rustc_objc_class = ...]` will be converted to a null-terminated string, + // so it may not contain any null characters. + cx.emit_err(NullOnObjcClass { span: nv.value_span }); + return None; + } + Some(AttributeKind::ObjcClass { classname, span: cx.attr_span }) + } +} + +pub(crate) struct ObjcSelectorParser; + +impl SingleAttributeParser for ObjcSelectorParser { + const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_selector]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = + AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]); + const TEMPLATE: AttributeTemplate = template!(NameValueStr: "methodName"); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + let Some(nv) = args.name_value() else { + cx.expected_name_value(cx.attr_span, None); + return None; + }; + let Some(methname) = nv.value_as_str() else { + // `#[rustc_objc_selector = ...]` is expected to be used as an implementatioin detail + // inside a standard library macro, but `cx.expected_string_literal` exposes too much. + // Use a custom error message instead. + cx.emit_err(ObjcSelectorExpectedStringLiteral { span: nv.value_span }); + return None; + }; + if methname.as_str().contains('\0') { + // `#[rustc_objc_selector = ...]` will be converted to a null-terminated string, + // so it may not contain any null characters. + cx.emit_err(NullOnObjcSelector { span: nv.value_span }); + return None; + } + Some(AttributeKind::ObjcSelector { methname, span: cx.attr_span }) + } +} + #[derive(Default)] pub(crate) struct NakedParser { span: Option, diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index b3ab1d3edd6d..6c940e3dcaf9 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -20,8 +20,8 @@ use crate::attributes::allow_unstable::{ use crate::attributes::body::CoroutineParser; use crate::attributes::codegen_attrs::{ ColdParser, CoverageParser, ExportNameParser, ForceTargetFeatureParser, NakedParser, - NoMangleParser, OptimizeParser, SanitizeParser, TargetFeatureParser, TrackCallerParser, - UsedParser, + NoMangleParser, ObjcClassParser, ObjcSelectorParser, OptimizeParser, SanitizeParser, + TargetFeatureParser, TrackCallerParser, UsedParser, }; use crate::attributes::confusables::ConfusablesParser; use crate::attributes::crate_level::{ @@ -187,6 +187,8 @@ attribute_parsers!( Single, Single, Single, + Single, + Single, Single, Single, Single, diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 32ea9005a97d..2c2b14c8a68b 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -459,6 +459,34 @@ pub(crate) struct NullOnLinkSection { pub span: Span, } +#[derive(Diagnostic)] +#[diag(attr_parsing_null_on_objc_class)] +pub(crate) struct NullOnObjcClass { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_null_on_objc_selector)] +pub(crate) struct NullOnObjcSelector { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_objc_class_expected_string_literal)] +pub(crate) struct ObjcClassExpectedStringLiteral { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_objc_selector_expected_string_literal)] +pub(crate) struct ObjcSelectorExpectedStringLiteral { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(attr_parsing_stability_outside_std, code = E0734)] pub(crate) struct StabilityOutsideStd { diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index 9cc5d8dbc21c..978134cc32b1 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -109,18 +109,36 @@ pub(crate) fn compile_codegen_unit( attributes::apply_to_llfn(entry, llvm::AttributePlace::Function, &attrs); } + // Define Objective-C module info and module flags. Note, the module info will + // also be added to the `llvm.compiler.used` variable, created later. + // + // These are only necessary when we need the linker to do its Objective-C-specific + // magic. We could theoretically do it unconditionally, but at a slight cost to linker + // performance in the common case where it's unnecessary. + if !cx.objc_classrefs.borrow().is_empty() || !cx.objc_selrefs.borrow().is_empty() { + if cx.objc_abi_version() == 1 { + cx.define_objc_module_info(); + } + cx.add_objc_module_flags(); + } + // Finalize code coverage by injecting the coverage map. Note, the coverage map will // also be added to the `llvm.compiler.used` variable, created next. if cx.sess().instrument_coverage() { cx.coverageinfo_finalize(); } - // Create the llvm.used and llvm.compiler.used variables. + // Create the llvm.used variable. if !cx.used_statics.is_empty() { cx.create_used_variable_impl(c"llvm.used", &cx.used_statics); } - if !cx.compiler_used_statics.is_empty() { - cx.create_used_variable_impl(c"llvm.compiler.used", &cx.compiler_used_statics); + + // Create the llvm.compiler.used variable. + { + let compiler_used_statics = cx.compiler_used_statics.borrow(); + if !compiler_used_statics.is_empty() { + cx.create_used_variable_impl(c"llvm.compiler.used", &compiler_used_statics); + } } // Run replace-all-uses-with for statics that need it. This must diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 11b79a7fe68c..aa2df46329f2 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -108,6 +108,10 @@ impl<'ll, CX: Borrow>> GenericCx<'ll, CX> { bytes_in_context(self.llcx(), bytes) } + pub(crate) fn null_terminate_const_bytes(&self, bytes: &[u8]) -> &'ll Value { + null_terminate_bytes_in_context(self.llcx(), bytes) + } + pub(crate) fn const_get_elt(&self, v: &'ll Value, idx: u64) -> &'ll Value { unsafe { let idx = c_uint::try_from(idx).expect("LLVMGetAggregateElement index overflow"); @@ -381,6 +385,16 @@ pub(crate) fn bytes_in_context<'ll>(llcx: &'ll llvm::Context, bytes: &[u8]) -> & } } +pub(crate) fn null_terminate_bytes_in_context<'ll>( + llcx: &'ll llvm::Context, + bytes: &[u8], +) -> &'ll Value { + unsafe { + let ptr = bytes.as_ptr() as *const c_char; + llvm::LLVMConstStringInContext2(llcx, ptr, bytes.len(), FALSE) + } +} + pub(crate) fn named_struct<'ll>(ty: &'ll Type, elts: &[&'ll Value]) -> &'ll Value { let len = c_uint::try_from(elts.len()).expect("LLVMConstStructInContext elements len overflow"); unsafe { llvm::LLVMConstNamedStruct(ty, elts.as_ptr(), len) } diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index dc9bb743560d..a110ecbb75d9 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -16,6 +16,7 @@ use rustc_middle::mir::mono::MonoItem; use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf}; use rustc_middle::ty::{self, Instance}; use rustc_middle::{bug, span_bug}; +use rustc_span::Symbol; use tracing::{debug, instrument, trace}; use crate::common::CodegenCx; @@ -331,6 +332,10 @@ impl<'ll> CodegenCx<'ll, '_> { } g + } else if let Some(classname) = fn_attrs.objc_class { + self.get_objc_classref(classname) + } else if let Some(methname) = fn_attrs.objc_selector { + self.get_objc_selref(methname) } else { check_and_apply_linkage(self, fn_attrs, llty, sym, def_id) }; @@ -543,8 +548,225 @@ impl<'ll> CodegenCx<'ll, '_> { /// Add a global value to a list to be stored in the `llvm.compiler.used` variable, /// an array of ptr. - pub(crate) fn add_compiler_used_global(&mut self, global: &'ll Value) { - self.compiler_used_statics.push(global); + pub(crate) fn add_compiler_used_global(&self, global: &'ll Value) { + self.compiler_used_statics.borrow_mut().push(global); + } + + // We do our best here to match what Clang does when compiling Objective-C natively. + // See Clang's `CGObjCCommonMac::CreateCStringLiteral`: + // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L4134 + fn define_objc_classname(&self, classname: &str) -> &'ll Value { + assert_eq!(self.objc_abi_version(), 1); + + let llval = self.null_terminate_const_bytes(classname.as_bytes()); + let llty = self.val_ty(llval); + let sym = self.generate_local_symbol_name("OBJC_CLASS_NAME_"); + let g = self.define_global(&sym, llty).unwrap_or_else(|| { + bug!("symbol `{}` is already defined", sym); + }); + set_global_alignment(self, g, self.tcx.data_layout.i8_align.abi); + llvm::set_initializer(g, llval); + llvm::set_linkage(g, llvm::Linkage::PrivateLinkage); + llvm::set_section(g, c"__TEXT,__cstring,cstring_literals"); + llvm::LLVMSetGlobalConstant(g, llvm::TRUE); + llvm::LLVMSetUnnamedAddress(g, llvm::UnnamedAddr::Global); + self.add_compiler_used_global(g); + + g + } + + // We do our best here to match what Clang does when compiling Objective-C natively. + // See Clang's `ObjCNonFragileABITypesHelper`: + // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L6052 + fn get_objc_class_t(&self) -> &'ll Type { + if let Some(class_t) = self.objc_class_t.get() { + return class_t; + } + + assert_eq!(self.objc_abi_version(), 2); + + // struct _class_t { + // struct _class_t* isa; + // struct _class_t* const superclass; + // void* cache; + // IMP* vtable; + // struct class_ro_t* ro; + // } + + let class_t = self.type_named_struct("struct._class_t"); + let els = [self.type_ptr(); 5]; + let packed = false; + self.set_struct_body(class_t, &els, packed); + + self.objc_class_t.set(Some(class_t)); + class_t + } + + // We do our best here to match what Clang does when compiling Objective-C natively. We + // deduplicate references within a CGU, but we need a reference definition in each referencing + // CGU. All attempts at using external references to a single reference definition result in + // linker errors. + fn get_objc_classref(&self, classname: Symbol) -> &'ll Value { + let mut classrefs = self.objc_classrefs.borrow_mut(); + if let Some(classref) = classrefs.get(&classname).copied() { + return classref; + } + + let g = match self.objc_abi_version() { + 1 => { + // See Clang's `CGObjCMac::EmitClassRefFromId`: + // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L5205 + let llval = self.define_objc_classname(classname.as_str()); + let llty = self.type_ptr(); + let sym = self.generate_local_symbol_name("OBJC_CLASS_REFERENCES_"); + let g = self.define_global(&sym, llty).unwrap_or_else(|| { + bug!("symbol `{}` is already defined", sym); + }); + set_global_alignment(self, g, self.tcx.data_layout.pointer_align().abi); + llvm::set_initializer(g, llval); + llvm::set_linkage(g, llvm::Linkage::PrivateLinkage); + llvm::set_section(g, c"__OBJC,__cls_refs,literal_pointers,no_dead_strip"); + self.add_compiler_used_global(g); + g + } + 2 => { + // See Clang's `CGObjCNonFragileABIMac::EmitClassRefFromId`: + // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L7423 + let llval = { + let extern_sym = format!("OBJC_CLASS_$_{}", classname.as_str()); + let extern_llty = self.get_objc_class_t(); + self.declare_global(&extern_sym, extern_llty) + }; + let llty = self.type_ptr(); + let sym = self.generate_local_symbol_name("OBJC_CLASSLIST_REFERENCES_$_"); + let g = self.define_global(&sym, llty).unwrap_or_else(|| { + bug!("symbol `{}` is already defined", sym); + }); + set_global_alignment(self, g, self.tcx.data_layout.pointer_align().abi); + llvm::set_initializer(g, llval); + llvm::set_linkage(g, llvm::Linkage::InternalLinkage); + llvm::set_section(g, c"__DATA,__objc_classrefs,regular,no_dead_strip"); + self.add_compiler_used_global(g); + g + } + _ => unreachable!(), + }; + + classrefs.insert(classname, g); + g + } + + // We do our best here to match what Clang does when compiling Objective-C natively. We + // deduplicate references within a CGU, but we need a reference definition in each referencing + // CGU. All attempts at using external references to a single reference definition result in + // linker errors. + // + // Newer versions of Apple Clang generate calls to `@"objc_msgSend$methname"` selector stub + // functions. We don't currently do that. The code we generate is closer to what Apple Clang + // generates with the `-fno-objc-msgsend-selector-stubs` option. + fn get_objc_selref(&self, methname: Symbol) -> &'ll Value { + let mut selrefs = self.objc_selrefs.borrow_mut(); + if let Some(selref) = selrefs.get(&methname).copied() { + return selref; + } + + let abi_version = self.objc_abi_version(); + + // See Clang's `CGObjCCommonMac::CreateCStringLiteral`: + // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L4134 + let methname_llval = self.null_terminate_const_bytes(methname.as_str().as_bytes()); + let methname_llty = self.val_ty(methname_llval); + let methname_sym = self.generate_local_symbol_name("OBJC_METH_VAR_NAME_"); + let methname_g = self.define_global(&methname_sym, methname_llty).unwrap_or_else(|| { + bug!("symbol `{}` is already defined", methname_sym); + }); + set_global_alignment(self, methname_g, self.tcx.data_layout.i8_align.abi); + llvm::set_initializer(methname_g, methname_llval); + llvm::set_linkage(methname_g, llvm::Linkage::PrivateLinkage); + llvm::set_section( + methname_g, + match abi_version { + 1 => c"__TEXT,__cstring,cstring_literals", + 2 => c"__TEXT,__objc_methname,cstring_literals", + _ => unreachable!(), + }, + ); + llvm::LLVMSetGlobalConstant(methname_g, llvm::TRUE); + llvm::LLVMSetUnnamedAddress(methname_g, llvm::UnnamedAddr::Global); + self.add_compiler_used_global(methname_g); + + // See Clang's `CGObjCMac::EmitSelectorAddr`: + // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L5243 + // And Clang's `CGObjCNonFragileABIMac::EmitSelectorAddr`: + // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L7586 + let selref_llval = methname_g; + let selref_llty = self.type_ptr(); + let selref_sym = self.generate_local_symbol_name("OBJC_SELECTOR_REFERENCES_"); + let selref_g = self.define_global(&selref_sym, selref_llty).unwrap_or_else(|| { + bug!("symbol `{}` is already defined", selref_sym); + }); + set_global_alignment(self, selref_g, self.tcx.data_layout.pointer_align().abi); + llvm::set_initializer(selref_g, selref_llval); + llvm::set_externally_initialized(selref_g, true); + llvm::set_linkage( + selref_g, + match abi_version { + 1 => llvm::Linkage::PrivateLinkage, + 2 => llvm::Linkage::InternalLinkage, + _ => unreachable!(), + }, + ); + llvm::set_section( + selref_g, + match abi_version { + 1 => c"__OBJC,__message_refs,literal_pointers,no_dead_strip", + 2 => c"__DATA,__objc_selrefs,literal_pointers,no_dead_strip", + _ => unreachable!(), + }, + ); + self.add_compiler_used_global(selref_g); + + selrefs.insert(methname, selref_g); + selref_g + } + + // We do our best here to match what Clang does when compiling Objective-C natively. + // See Clang's `ObjCTypesHelper`: + // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L5936 + // And Clang's `CGObjCMac::EmitModuleInfo`: + // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L5151 + pub(crate) fn define_objc_module_info(&mut self) { + assert_eq!(self.objc_abi_version(), 1); + + // struct _objc_module { + // long version; // Hardcoded to 7 in Clang. + // long size; // sizeof(struct _objc_module) + // char* name; // Hardcoded to classname "" in Clang. + // struct _objc_symtab* symtab; // Null without class or category definitions. + // } + + let llty = self.type_named_struct("struct._objc_module"); + let i32_llty = self.type_i32(); + let ptr_llty = self.type_ptr(); + let packed = false; + self.set_struct_body(llty, &[i32_llty, i32_llty, ptr_llty, ptr_llty], packed); + + let version = self.const_uint(i32_llty, 7); + let size = self.const_uint(i32_llty, 16); + let name = self.define_objc_classname(""); + let symtab = self.const_null(ptr_llty); + let llval = crate::common::named_struct(llty, &[version, size, name, symtab]); + + let sym = "OBJC_MODULES"; + let g = self.define_global(&sym, llty).unwrap_or_else(|| { + bug!("symbol `{}` is already defined", sym); + }); + set_global_alignment(self, g, self.tcx.data_layout.pointer_align().abi); + llvm::set_initializer(g, llval); + llvm::set_linkage(g, llvm::Linkage::PrivateLinkage); + llvm::set_section(g, c"__OBJC,__module_info,regular,no_dead_strip"); + + self.add_compiler_used_global(g); } } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index a69fa54a54a4..2f309d4694ce 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -26,7 +26,7 @@ use rustc_session::config::{ BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, FunctionReturn, PAuthKey, PacRet, }; use rustc_span::source_map::Spanned; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Span, Symbol}; use rustc_symbol_mangling::mangle_internal_symbol; use rustc_target::spec::{HasTargetSpec, RelocModel, SmallDataThresholdSupport, Target, TlsModel}; use smallvec::SmallVec; @@ -119,7 +119,7 @@ pub(crate) struct FullCx<'ll, 'tcx> { /// Statics that will be placed in the llvm.compiler.used variable /// See for details - pub compiler_used_statics: Vec<&'ll Value>, + pub compiler_used_statics: RefCell>, /// Mapping of non-scalar types to llvm types. pub type_lowering: RefCell, Option), &'ll Type>>, @@ -146,6 +146,15 @@ pub(crate) struct FullCx<'ll, 'tcx> { /// `global_asm!` needs to be able to find this new global so that it can /// compute the correct mangled symbol name to insert into the asm. pub renamed_statics: RefCell>, + + /// Cached Objective-C class type + pub objc_class_t: Cell>, + + /// Cache of Objective-C class references + pub objc_classrefs: RefCell>, + + /// Cache of Objective-C selector references + pub objc_selrefs: RefCell>, } fn to_llvm_tls_model(tls_model: TlsModel) -> llvm::ThreadLocalMode { @@ -644,7 +653,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { const_globals: Default::default(), statics_to_rauw: RefCell::new(Vec::new()), used_statics: Vec::new(), - compiler_used_statics: Vec::new(), + compiler_used_statics: Default::default(), type_lowering: Default::default(), scalar_lltypes: Default::default(), coverage_cx, @@ -655,6 +664,9 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { intrinsics: Default::default(), local_gen_sym_counter: Cell::new(0), renamed_statics: Default::default(), + objc_class_t: Cell::new(None), + objc_classrefs: Default::default(), + objc_selrefs: Default::default(), }, PhantomData, ) @@ -679,6 +691,69 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { llvm::set_linkage(g, llvm::Linkage::AppendingLinkage); llvm::set_section(g, c"llvm.metadata"); } + + /// The Objective-C ABI that is used. + /// + /// This corresponds to the `-fobjc-abi-version=` flag in Clang / GCC. + pub(crate) fn objc_abi_version(&self) -> u32 { + assert!(self.tcx.sess.target.is_like_darwin); + if self.tcx.sess.target.arch == "x86" && self.tcx.sess.target.os == "macos" { + // 32-bit x86 macOS uses ABI version 1 (a.k.a. the "fragile ABI"). + 1 + } else { + // All other Darwin-like targets we support use ABI version 2 + // (a.k.a the "non-fragile ABI"). + 2 + } + } + + // We do our best here to match what Clang does when compiling Objective-C natively. + // See Clang's `CGObjCCommonMac::EmitImageInfo`: + // https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L5085 + pub(crate) fn add_objc_module_flags(&self) { + let abi_version = self.objc_abi_version(); + + llvm::add_module_flag_u32( + self.llmod, + llvm::ModuleFlagMergeBehavior::Error, + "Objective-C Version", + abi_version, + ); + + llvm::add_module_flag_u32( + self.llmod, + llvm::ModuleFlagMergeBehavior::Error, + "Objective-C Image Info Version", + 0, + ); + + llvm::add_module_flag_str( + self.llmod, + llvm::ModuleFlagMergeBehavior::Error, + "Objective-C Image Info Section", + match abi_version { + 1 => "__OBJC,__image_info,regular", + 2 => "__DATA,__objc_imageinfo,regular,no_dead_strip", + _ => unreachable!(), + }, + ); + + if self.tcx.sess.target.env == "sim" { + llvm::add_module_flag_u32( + self.llmod, + llvm::ModuleFlagMergeBehavior::Error, + "Objective-C Is Simulated", + 1 << 5, + ); + } + + llvm::add_module_flag_u32( + self.llmod, + llvm::ModuleFlagMergeBehavior::Error, + "Objective-C Class Properties", + 1 << 6, + ); + } } impl<'ll> SimpleCx<'ll> { pub(crate) fn get_type_of_global(&self, val: &'ll Value) -> &'ll Type { diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 0679f55ab7f0..e125aba6afc7 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1237,6 +1237,7 @@ unsafe extern "C" { pub(crate) safe fn LLVMSetGlobalConstant(GlobalVar: &Value, IsConstant: Bool); pub(crate) safe fn LLVMSetTailCall(CallInst: &Value, IsTailCall: Bool); pub(crate) safe fn LLVMSetTailCallKind(CallInst: &Value, kind: TailCallKind); + pub(crate) safe fn LLVMSetExternallyInitialized(GlobalVar: &Value, IsExtInit: Bool); // Operations on attributes pub(crate) fn LLVMCreateStringAttribute( diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index d6974e22c85c..1115d82fa85d 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -258,6 +258,10 @@ pub(crate) fn set_alignment(llglobal: &Value, align: Align) { } } +pub(crate) fn set_externally_initialized(llglobal: &Value, is_ext_init: bool) { + LLVMSetExternallyInitialized(llglobal, is_ext_init.to_llvm_bool()); +} + /// Get the `name`d comdat from `llmod` and assign it to `llglobal`. /// /// Inserts the comdat into `llmod` if it does not exist. diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index dc500c363f4a..32ae810ecc89 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -296,6 +296,12 @@ fn process_builtin_attrs( AttributeKind::Sanitize { span, .. } => { interesting_spans.sanitize = Some(*span); } + AttributeKind::ObjcClass { classname, .. } => { + codegen_fn_attrs.objc_class = Some(*classname); + } + AttributeKind::ObjcSelector { methname, .. } => { + codegen_fn_attrs.objc_selector = Some(*methname); + } _ => {} } } diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 129ab7eccb57..99d6e93faa9e 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -1057,6 +1057,14 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_std_internal_symbol, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, ), + rustc_attr!( + rustc_objc_class, Normal, template!(NameValueStr: "ClassName"), ErrorPreceding, + EncodeCrossCrate::No, + ), + rustc_attr!( + rustc_objc_selector, Normal, template!(NameValueStr: "methodName"), ErrorPreceding, + EncodeCrossCrate::No, + ), // ========================================================================== // Internal attributes, Macro related: diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index ea11a99efbc3..04b144abd03d 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -594,6 +594,12 @@ pub enum AttributeKind { /// Represents `#[non_exhaustive]` NonExhaustive(Span), + /// Represents `#[rustc_objc_class]` + ObjcClass { classname: Symbol, span: Span }, + + /// Represents `#[rustc_objc_selector]` + ObjcSelector { methname: Symbol, span: Span }, + /// Represents `#[optimize(size|speed)]` Optimize(OptimizeAttr, Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 55521c158540..cb4feeb05f1a 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -69,6 +69,8 @@ impl AttributeKind { NoMangle(..) => Yes, // Needed for rustdoc NoStd(..) => No, NonExhaustive(..) => Yes, // Needed for rustdoc + ObjcClass { .. } => No, + ObjcSelector { .. } => No, Optimize(..) => No, ParenSugar(..) => No, PassByValue(..) => Yes, diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 8b4503073b0d..f0d96c6ac898 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -69,6 +69,10 @@ pub struct CodegenFnAttrs { /// The `#[patchable_function_entry(...)]` attribute. Indicates how many nops should be around /// the function entry. pub patchable_function_entry: Option, + /// The `#[rustc_objc_class = "..."]` attribute. + pub objc_class: Option, + /// The `#[rustc_objc_selector = "..."]` attribute. + pub objc_selector: Option, } #[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable, PartialEq, Eq)] @@ -185,6 +189,8 @@ impl CodegenFnAttrs { instruction_set: None, alignment: None, patchable_function_entry: None, + objc_class: None, + objc_selector: None, } } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 2562d2e0b83c..60f575cb8441 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -276,6 +276,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::PatternComplexityLimit { .. } | AttributeKind::NoCore { .. } | AttributeKind::NoStd { .. } + | AttributeKind::ObjcClass { .. } + | AttributeKind::ObjcSelector { .. } ) => { /* do nothing */ } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index cdb0b5b58da6..668e80a26b44 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1917,6 +1917,8 @@ symbols! { rustc_no_mir_inline, rustc_nonnull_optimization_guaranteed, rustc_nounwind, + rustc_objc_class, + rustc_objc_selector, rustc_object_lifetime_default, rustc_on_unimplemented, rustc_outlives, diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 86a68e18b0af..5d52bfb1b125 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -313,6 +313,7 @@ pub mod io; pub mod iter; pub mod net; pub mod option; +pub mod os; pub mod panic; pub mod panicking; #[unstable(feature = "pattern_type_macro", issue = "123646")] diff --git a/library/core/src/os/darwin/mod.rs b/library/core/src/os/darwin/mod.rs new file mode 100644 index 000000000000..8426d82b8ce3 --- /dev/null +++ b/library/core/src/os/darwin/mod.rs @@ -0,0 +1,19 @@ +//! Platform-specific extensions to `core` for Darwin / Apple platforms. +//! +//! This is available on the following operating systems: +//! - macOS +//! - iOS +//! - tvOS +//! - watchOS +//! - visionOS +//! +//! Note: This module is called "Darwin" as that's the name of the underlying +//! core OS of the above operating systems, but it should not be confused with +//! the `-darwin` suffix in the `x86_64-apple-darwin` and +//! `aarch64-apple-darwin` target names, which are mostly named that way for +//! legacy reasons. + +#![unstable(feature = "darwin_objc", issue = "145496")] +#![doc(cfg(target_vendor = "apple"))] + +pub mod objc; diff --git a/library/core/src/os/darwin/objc.rs b/library/core/src/os/darwin/objc.rs new file mode 100644 index 000000000000..928cb54e82c7 --- /dev/null +++ b/library/core/src/os/darwin/objc.rs @@ -0,0 +1,113 @@ +//! Defines types and macros for Objective-C interoperability. + +#![unstable(feature = "darwin_objc", issue = "145496")] +#![allow(nonstandard_style)] + +use crate::fmt; + +/// Equivalent to Objective-C’s `struct objc_class` type. +#[cfg_attr(not(doc), repr(u8))] // An implementation detail we don't want to show up in rustdoc +pub enum objc_class { + #[unstable( + feature = "objc_class_variant", + reason = "temporary implementation detail", + issue = "none" + )] + #[doc(hidden)] + __variant1, + #[unstable( + feature = "objc_class_variant", + reason = "temporary implementation detail", + issue = "none" + )] + #[doc(hidden)] + __variant2, +} + +impl fmt::Debug for objc_class { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("objc_class").finish() + } +} + +/// Equivalent to Objective-C’s `struct objc_selector` type. +#[cfg_attr(not(doc), repr(u8))] // An implementation detail we don't want to show up in rustdoc +pub enum objc_selector { + #[unstable( + feature = "objc_selector_variant", + reason = "temporary implementation detail", + issue = "none" + )] + #[doc(hidden)] + __variant1, + #[unstable( + feature = "objc_selector_variant", + reason = "temporary implementation detail", + issue = "none" + )] + #[doc(hidden)] + __variant2, +} + +impl fmt::Debug for objc_selector { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("objc_selector").finish() + } +} + +/// Equivalent to Objective-C’s `Class` type. +pub type Class = *mut objc_class; + +/// Equivalent to Objective-C’s `SEL` type. +pub type SEL = *mut objc_selector; + +/// Gets a reference to an Objective-C class. +/// +/// This macro will yield an expression of type [`Class`] for the given class name string literal. +/// +/// # Example +/// +/// ```no_run +/// #![feature(darwin_objc)] +/// use core::os::darwin::objc; +/// +/// let string_class = objc::class!("NSString"); +/// ``` +#[allow_internal_unstable(rustc_attrs)] +pub macro class($classname:expr) {{ + // Since static Objective-C class references actually end up with multiple definitions + // across dylib boundaries, we only expose the value of the static and don't provide a way to + // get the address of or a reference to the static. + unsafe extern "C" { + #[rustc_objc_class = $classname] + safe static VAL: $crate::os::darwin::objc::Class; + } + VAL +}} + +/// Gets a reference to an Objective-C selector. +/// +/// This macro will yield an expression of type [`SEL`] for the given method name string literal. +/// +/// It is similar to Objective-C’s `@selector` directive. +/// +/// # Examples +/// +/// ```no_run +/// #![feature(darwin_objc)] +/// use core::os::darwin::objc; +/// +/// let alloc_sel = objc::selector!("alloc"); +/// let init_sel = objc::selector!("initWithCString:encoding:"); +/// ``` +#[allow_internal_unstable(rustc_attrs)] +pub macro selector($methname:expr) {{ + // Since static Objective-C selector references actually end up with multiple definitions + // across dylib boundaries, we only expose the value of the static and don't provide a way to + // get the address of or a reference to the static. + unsafe extern "C" { + #[rustc_objc_selector = $methname] + safe static VAL: $crate::os::darwin::objc::SEL; + } + VAL +}} diff --git a/library/core/src/os/mod.rs b/library/core/src/os/mod.rs new file mode 100644 index 000000000000..897f59f530ed --- /dev/null +++ b/library/core/src/os/mod.rs @@ -0,0 +1,24 @@ +//! OS-specific functionality. + +#![unstable(feature = "darwin_objc", issue = "145496")] + +#[cfg(all( + doc, + any( + all(target_arch = "wasm32", not(target_os = "wasi")), + all(target_vendor = "fortanix", target_env = "sgx") + ) +))] +#[unstable(issue = "none", feature = "std_internals")] +pub mod darwin {} + +// darwin +#[cfg(not(all( + doc, + any( + all(target_arch = "wasm32", not(target_os = "wasi")), + all(target_vendor = "fortanix", target_env = "sgx") + ) +)))] +#[cfg(any(target_vendor = "apple", doc))] +pub mod darwin; diff --git a/library/std/src/os/darwin/mod.rs b/library/std/src/os/darwin/mod.rs index 3b1bd974fa31..ff184f477fb0 100644 --- a/library/std/src/os/darwin/mod.rs +++ b/library/std/src/os/darwin/mod.rs @@ -17,6 +17,8 @@ #![doc(cfg(target_vendor = "apple"))] pub mod fs; +pub mod objc; + // deprecated, but used for public reexport under `std::os::unix::raw`, as // well as `std::os::macos`/`std::os::ios`, because those modules precede the // decision to remove these. diff --git a/library/std/src/os/darwin/objc.rs b/library/std/src/os/darwin/objc.rs new file mode 100644 index 000000000000..a4b31fee7c58 --- /dev/null +++ b/library/std/src/os/darwin/objc.rs @@ -0,0 +1,13 @@ +//! Defines types and macros for Objective-C interoperability. +//! +//! This module re-exports all the items in [`core::os::darwin::objc`]. +//! +//! [`core::os::darwin::objc`]: ../../../../core/os/darwin/objc/index.html "mod core::os::darwin::objc" + +#![unstable(feature = "darwin_objc", issue = "145496")] + +// We can't generate an intra-doc link for this automatically since `core::os::darwin` isn't +// compiled into `core` on every platform even though it's documented on every platform. +// We just link to it directly in the module documentation above instead. +#[cfg(not(doc))] +pub use core::os::darwin::objc::*; diff --git a/src/tools/tidy/src/pal.rs b/src/tools/tidy/src/pal.rs index 5b8b44429bbc..991ad55809cc 100644 --- a/src/tools/tidy/src/pal.rs +++ b/src/tools/tidy/src/pal.rs @@ -53,6 +53,7 @@ const EXCEPTION_PATHS: &[&str] = &[ // core::ffi contains platform-specific type and linkage configuration "library/core/src/ffi/mod.rs", "library/core/src/ffi/primitives.rs", + "library/core/src/os", // Platform-specific public interfaces "library/std/src/sys", // Platform-specific code for std lives here. "library/std/src/os", // Platform-specific public interfaces // Temporary `std` exceptions diff --git a/tests/codegen-llvm/auxiliary/darwin_objc_aux.rs b/tests/codegen-llvm/auxiliary/darwin_objc_aux.rs new file mode 100644 index 000000000000..3c35d003c8af --- /dev/null +++ b/tests/codegen-llvm/auxiliary/darwin_objc_aux.rs @@ -0,0 +1,27 @@ +#![crate_type = "lib"] +#![feature(darwin_objc)] + +use std::os::darwin::objc; + +#[link(name = "Foundation", kind = "framework")] +unsafe extern "C" {} + +#[inline(always)] +pub fn inline_get_object_class() -> objc::Class { + objc::class!("NSObject") +} + +#[inline(always)] +pub fn inline_get_alloc_selector() -> objc::SEL { + objc::selector!("alloc") +} + +#[inline(never)] +pub fn never_inline_get_string_class() -> objc::Class { + objc::class!("NSString") +} + +#[inline(never)] +pub fn never_inline_get_init_selector() -> objc::SEL { + objc::selector!("init") +} diff --git a/tests/codegen-llvm/darwin-no-objc.rs b/tests/codegen-llvm/darwin-no-objc.rs new file mode 100644 index 000000000000..fda3671fb6d8 --- /dev/null +++ b/tests/codegen-llvm/darwin-no-objc.rs @@ -0,0 +1,52 @@ +// Test that we don't generate Objective-C definitions or image info unnecessarily. + +//@ add-core-stubs +//@ revisions: i686_apple_darwin +//@ [i686_apple_darwin] compile-flags: --target i686-apple-darwin +//@ [i686_apple_darwin] needs-llvm-components: x86 +//@ revisions: x86_64_macos +//@ [x86_64_macos] compile-flags: --target x86_64-apple-darwin +//@ [x86_64_macos] needs-llvm-components: x86 +//@ revisions: aarch64_macos +//@ [aarch64_macos] compile-flags: --target aarch64-apple-darwin +//@ [aarch64_macos] needs-llvm-components: aarch64 +//@ revisions: i386_ios +//@ [i386_ios] compile-flags: --target i386-apple-ios +//@ [i386_ios] needs-llvm-components: x86 +//@ revisions: x86_64_ios +//@ [x86_64_ios] compile-flags: --target x86_64-apple-ios +//@ [x86_64_ios] needs-llvm-components: x86 +//@ revisions: armv7s_ios +//@ [armv7s_ios] compile-flags: --target armv7s-apple-ios +//@ [armv7s_ios] needs-llvm-components: arm +//@ revisions: aarch64_ios +//@ [aarch64_ios] compile-flags: --target aarch64-apple-ios +//@ [aarch64_ios] needs-llvm-components: aarch64 +//@ revisions: aarch64_ios_sim +//@ [aarch64_ios_sim] compile-flags: --target aarch64-apple-ios-sim +//@ [aarch64_ios_sim] needs-llvm-components: aarch64 + +#![crate_type = "lib"] +#![feature(no_core, lang_items)] +#![no_core] + +extern crate minicore; +use minicore::*; + +#[no_mangle] +pub fn foo() {} + +// CHECK-NOT: %struct._class_t +// CHECK-NOT: %struct._objc_module +// CHECK-NOT: @OBJC_CLASS_NAME_ +// CHECK-NOT: @"OBJC_CLASS_$_{{[0-9A-Z_a-z]+}}" +// CHECK-NOT: @"OBJC_CLASSLIST_REFERENCES_$_.{{[0-9]+}}" +// CHECK-NOT: @OBJC_METH_VAR_NAME_ +// CHECK-NOT: @OBJC_SELECTOR_REFERENCES_ +// CHECK-NOT: @OBJC_MODULES + +// CHECK-NOT: !"Objective-C Version" +// CHECK-NOT: !"Objective-C Image Info Version" +// CHECK-NOT: !"Objective-C Image Info Section" +// CHECK-NOT: !"Objective-C Is Simulated" +// CHECK-NOT: !"Objective-C Class Properties" diff --git a/tests/codegen-llvm/darwin-objc-abi-v1.rs b/tests/codegen-llvm/darwin-objc-abi-v1.rs new file mode 100644 index 000000000000..0fc1de9332ac --- /dev/null +++ b/tests/codegen-llvm/darwin-objc-abi-v1.rs @@ -0,0 +1,100 @@ +// ignore-tidy-linelength +//@ add-core-stubs +//@ revisions: i686_apple_darwin +//@ [i686_apple_darwin] compile-flags: --target i686-apple-darwin +//@ [i686_apple_darwin] needs-llvm-components: x86 + +#![crate_type = "lib"] +#![feature(no_core, lang_items, rustc_attrs)] +#![no_core] + +extern crate minicore; +use minicore::*; + +#[no_mangle] +pub fn get_class() -> *mut () { + unsafe extern "C" { + #[rustc_objc_class = "MyClass"] + safe static VAL: *mut (); + } + VAL +} + +#[no_mangle] +pub fn get_class_again() -> *mut () { + // Codegen should de-duplicate this class with the one from get_class above. + unsafe extern "C" { + #[rustc_objc_class = "MyClass"] + safe static VAL: *mut (); + } + VAL +} + +#[no_mangle] +pub fn get_selector() -> *mut () { + unsafe extern "C" { + #[rustc_objc_selector = "myMethod"] + safe static VAL: *mut (); + } + VAL +} + +#[no_mangle] +pub fn get_selector_again() -> *mut () { + // Codegen should de-duplicate this selector with the one from get_selector above. + unsafe extern "C" { + #[rustc_objc_selector = "myMethod"] + safe static VAL: *mut (); + } + VAL +} + +#[no_mangle] +pub fn get_other_class() -> *mut () { + unsafe extern "C" { + #[rustc_objc_class = "OtherClass"] + safe static VAL: *mut (); + } + VAL +} + +#[no_mangle] +pub fn get_other_selector() -> *mut () { + unsafe extern "C" { + #[rustc_objc_selector = "otherMethod"] + safe static VAL: *mut (); + } + VAL +} + +// CHECK: %struct._objc_module = type { i32, i32, ptr, ptr } + +// CHECK: @OBJC_CLASS_NAME_.{{[0-9]+}} = private unnamed_addr constant [8 x i8] c"MyClass\00", section "__TEXT,__cstring,cstring_literals", align 1 +// CHECK: @OBJC_CLASS_REFERENCES_.{{[0-9]+}} = private global ptr @OBJC_CLASS_NAME_.{{[0-9]+}}, section "__OBJC,__cls_refs,literal_pointers,no_dead_strip", align 4 +// CHECK-NOT: @OBJC_CLASS_NAME_ +// CHECK-NOT: @OBJC_CLASS_REFERENCES_ + +// CHECK: @OBJC_METH_VAR_NAME_.{{[0-9]+}} = private unnamed_addr constant [9 x i8] c"myMethod\00", section "__TEXT,__cstring,cstring_literals", align 1 +// CHECK: @OBJC_SELECTOR_REFERENCES_.{{[0-9]+}} = private externally_initialized global ptr @OBJC_METH_VAR_NAME_.{{[0-9]+}}, section "__OBJC,__message_refs,literal_pointers,no_dead_strip", align 4 +// CHECK-NOT: @OBJC_METH_VAR_NAME_ +// CHECK-NOT: @OBJC_SELECTOR_REFERENCES_ + +// CHECK: @OBJC_CLASS_NAME_.{{[0-9]+}} = private unnamed_addr constant [11 x i8] c"OtherClass\00", section "__TEXT,__cstring,cstring_literals", align 1 +// CHECK: @OBJC_CLASS_REFERENCES_.{{[0-9]+}} = private global ptr @OBJC_CLASS_NAME_.{{[0-9]+}}, section "__OBJC,__cls_refs,literal_pointers,no_dead_strip", align 4 + +// CHECK: @OBJC_METH_VAR_NAME_.{{[0-9]+}} = private unnamed_addr constant [12 x i8] c"otherMethod\00", section "__TEXT,__cstring,cstring_literals", align 1 +// CHECK: @OBJC_SELECTOR_REFERENCES_.{{[0-9]+}} = private externally_initialized global ptr @OBJC_METH_VAR_NAME_.{{[0-9]+}}, section "__OBJC,__message_refs,literal_pointers,no_dead_strip", align 4 + +// CHECK: @OBJC_CLASS_NAME_.{{[0-9]+}} = private unnamed_addr constant [1 x i8] zeroinitializer, section "__TEXT,__cstring,cstring_literals", align 1 +// CHECK: @OBJC_MODULES = private global %struct._objc_module { i32 7, i32 16, ptr @OBJC_CLASS_NAME_.{{[0-9]+}}, ptr null }, section "__OBJC,__module_info,regular,no_dead_strip", align 4 + +// CHECK: load ptr, ptr @OBJC_CLASS_REFERENCES_.{{[0-9]+}}, align 4 +// CHECK: load ptr, ptr @OBJC_SELECTOR_REFERENCES_.{{[0-9]+}}, align 4 +// CHECK: load ptr, ptr @OBJC_CLASS_REFERENCES_.{{[0-9]+}}, align 4 +// CHECK: load ptr, ptr @OBJC_SELECTOR_REFERENCES_.{{[0-9]+}}, align 4 + +// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Version", i32 1} +// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Image Info Version", i32 0} +// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Image Info Section", !"__OBJC,__image_info,regular"} +// CHECK-NOT: !{{[0-9]+}} = !{i32 1, !"Objective-C Is Simulated", i32 32} +// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Class Properties", i32 64} diff --git a/tests/codegen-llvm/darwin-objc-abi-v2.rs b/tests/codegen-llvm/darwin-objc-abi-v2.rs new file mode 100644 index 000000000000..f142371d5825 --- /dev/null +++ b/tests/codegen-llvm/darwin-objc-abi-v2.rs @@ -0,0 +1,185 @@ +// ignore-tidy-linelength +//@ add-core-stubs +//@ revisions: x86_64_macos +//@ [x86_64_macos] compile-flags: --target x86_64-apple-darwin +//@ [x86_64_macos] needs-llvm-components: x86 +//@ revisions: aarch64_macos +//@ [aarch64_macos] compile-flags: --target aarch64-apple-darwin +//@ [aarch64_macos] needs-llvm-components: aarch64 +//@ revisions: i386_ios +//@ [i386_ios] compile-flags: --target i386-apple-ios +//@ [i386_ios] needs-llvm-components: x86 +//@ revisions: x86_64_ios +//@ [x86_64_ios] compile-flags: --target x86_64-apple-ios +//@ [x86_64_ios] needs-llvm-components: x86 +//@ revisions: armv7s_ios +//@ [armv7s_ios] compile-flags: --target armv7s-apple-ios +//@ [armv7s_ios] needs-llvm-components: arm +//@ revisions: aarch64_ios +//@ [aarch64_ios] compile-flags: --target aarch64-apple-ios +//@ [aarch64_ios] needs-llvm-components: aarch64 +//@ revisions: aarch64_ios_sim +//@ [aarch64_ios_sim] compile-flags: --target aarch64-apple-ios-sim +//@ [aarch64_ios_sim] needs-llvm-components: aarch64 + +#![crate_type = "lib"] +#![feature(no_core, lang_items, rustc_attrs)] +#![no_core] + +extern crate minicore; +use minicore::*; + +#[no_mangle] +pub fn get_class() -> *mut () { + unsafe extern "C" { + #[rustc_objc_class = "MyClass"] + safe static VAL: *mut (); + } + VAL +} + +#[no_mangle] +pub fn get_class_again() -> *mut () { + // Codegen should de-duplicate this class with the one from get_class above. + unsafe extern "C" { + #[rustc_objc_class = "MyClass"] + safe static VAL: *mut (); + } + VAL +} + +#[no_mangle] +pub fn get_selector() -> *mut () { + unsafe extern "C" { + #[rustc_objc_selector = "myMethod"] + safe static VAL: *mut (); + } + VAL +} + +#[no_mangle] +pub fn get_selector_again() -> *mut () { + // Codegen should de-duplicate this selector with the one from get_selector above. + unsafe extern "C" { + #[rustc_objc_selector = "myMethod"] + safe static VAL: *mut (); + } + VAL +} + +#[no_mangle] +pub fn get_other_class() -> *mut () { + unsafe extern "C" { + #[rustc_objc_class = "OtherClass"] + safe static VAL: *mut (); + } + VAL +} + +#[no_mangle] +pub fn get_other_selector() -> *mut () { + unsafe extern "C" { + #[rustc_objc_selector = "otherMethod"] + safe static VAL: *mut (); + } + VAL +} + +// CHECK: %struct._class_t = type { ptr, ptr, ptr, ptr, ptr } + +// CHECK: @"OBJC_CLASS_$_MyClass" = external global %struct._class_t +// CHECK: @"OBJC_CLASSLIST_REFERENCES_$_.{{[0-9]+}}" = internal global ptr @"OBJC_CLASS_$_MyClass", section "__DATA,__objc_classrefs,regular,no_dead_strip", +// x86_64_macos-SAME: align 8 +// aarch64_macos-SAME: align 8 +// i386_ios-SAME: align 4 +// x86_64_ios-SAME: align 8 +// armv7s_ios-SAME: align 4 +// aarch64_ios-SAME: align 8 +// aarch64_ios_sim-SAME: align 8 +// CHECK-NOT: @"OBJC_CLASS_$_MyClass" +// CHECK-NOT: @"OBJC_CLASSLIST_REFERENCES_$_ + +// CHECK: @OBJC_METH_VAR_NAME_.{{[0-9]+}} = private unnamed_addr constant [9 x i8] c"myMethod\00", section "__TEXT,__objc_methname,cstring_literals", align 1 +// CHECK: @OBJC_SELECTOR_REFERENCES_.{{[0-9]+}} = internal externally_initialized global ptr @OBJC_METH_VAR_NAME_.{{[0-9]+}}, section "__DATA,__objc_selrefs,literal_pointers,no_dead_strip", +// x86_64_macos-SAME: align 8 +// aarch64_macos-SAME: align 8 +// i386_ios-SAME: align 4 +// x86_64_ios-SAME: align 8 +// armv7s_ios-SAME: align 4 +// aarch64_ios-SAME: align 8 +// aarch64_ios_sim-SAME: align 8 +// CHECK-NOT: @OBJC_METH_VAR_NAME_ +// CHECK-NOT: @OBJC_SELECTOR_REFERENCES_ + +// CHECK: @"OBJC_CLASS_$_OtherClass" = external global %struct._class_t +// CHECK: @"OBJC_CLASSLIST_REFERENCES_$_.{{[0-9]+}}" = internal global ptr @"OBJC_CLASS_$_OtherClass", section "__DATA,__objc_classrefs,regular,no_dead_strip", +// x86_64_macos-SAME: align 8 +// aarch64_macos-SAME: align 8 +// i386_ios-SAME: align 4 +// x86_64_ios-SAME: align 8 +// armv7s_ios-SAME: align 4 +// aarch64_ios-SAME: align 8 +// aarch64_ios_sim-SAME: align 8 + +// CHECK: @OBJC_METH_VAR_NAME_.{{[0-9]+}} = private unnamed_addr constant [12 x i8] c"otherMethod\00", section "__TEXT,__objc_methname,cstring_literals", align 1 +// CHECK: @OBJC_SELECTOR_REFERENCES_.{{[0-9]+}} = internal externally_initialized global ptr @OBJC_METH_VAR_NAME_.{{[0-9]+}}, section "__DATA,__objc_selrefs,literal_pointers,no_dead_strip", +// x86_64_macos-SAME: align 8 +// aarch64_macos-SAME: align 8 +// i386_ios-SAME: align 4 +// x86_64_ios-SAME: align 8 +// armv7s_ios-SAME: align 4 +// aarch64_ios-SAME: align 8 +// aarch64_ios_sim-SAME: align 8 + +// CHECK-NOT: @OBJC_CLASS_NAME_ +// CHECK-NOT: @OBJC_MODULES + +// CHECK: load ptr, ptr @"OBJC_CLASSLIST_REFERENCES_$_.{{[0-9]+}}", +// x86_64_macos-SAME: align 8 +// aarch64_macos-SAME: align 8 +// i386_ios-SAME: align 4 +// x86_64_ios-SAME: align 8 +// armv7s_ios-SAME: align 4 +// aarch64_ios-SAME: align 8 +// aarch64_ios_sim-SAME: align 8 + +// CHECK: load ptr, ptr @OBJC_SELECTOR_REFERENCES_.{{[0-9]+}}, +// x86_64_macos-SAME: align 8 +// aarch64_macos-SAME: align 8 +// i386_ios-SAME: align 4 +// x86_64_ios-SAME: align 8 +// armv7s_ios-SAME: align 4 +// aarch64_ios-SAME: align 8 +// aarch64_ios_sim-SAME: align 8 + +// CHECK: load ptr, ptr @"OBJC_CLASSLIST_REFERENCES_$_.{{[0-9]+}}", +// x86_64_macos-SAME: align 8 +// aarch64_macos-SAME: align 8 +// i386_ios-SAME: align 4 +// x86_64_ios-SAME: align 8 +// armv7s_ios-SAME: align 4 +// aarch64_ios-SAME: align 8 +// aarch64_ios_sim-SAME: align 8 + +// CHECK: load ptr, ptr @OBJC_SELECTOR_REFERENCES_.{{[0-9]+}}, +// x86_64_macos-SAME: align 8 +// aarch64_macos-SAME: align 8 +// i386_ios-SAME: align 4 +// x86_64_ios-SAME: align 8 +// armv7s_ios-SAME: align 4 +// aarch64_ios-SAME: align 8 +// aarch64_ios_sim-SAME: align 8 + +// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Version", i32 2} +// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Image Info Version", i32 0} +// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Image Info Section", !"__DATA,__objc_imageinfo,regular,no_dead_strip"} + +// x86_64_macos-NOT: !{{[0-9]+}} = !{i32 1, !"Objective-C Is Simulated", i32 32} +// aarch64_macos-NOT: !{{[0-9]+}} = !{i32 1, !"Objective-C Is Simulated", i32 32} +// i386_ios: !{{[0-9]+}} = !{i32 1, !"Objective-C Is Simulated", i32 32} +// x86_64_ios: !{{[0-9]+}} = !{i32 1, !"Objective-C Is Simulated", i32 32} +// armv7s_ios-NOT: !{{[0-9]+}} = !{i32 1, !"Objective-C Is Simulated", i32 32} +// aarch64_ios-NOT: !{{[0-9]+}} = !{i32 1, !"Objective-C Is Simulated", i32 32} +// aarch64_ios_sim: !{{[0-9]+}} = !{i32 1, !"Objective-C Is Simulated", i32 32} + +// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Class Properties", i32 64} diff --git a/tests/codegen-llvm/darwin-objc-cross-crate.rs b/tests/codegen-llvm/darwin-objc-cross-crate.rs new file mode 100644 index 000000000000..74ad9a27346d --- /dev/null +++ b/tests/codegen-llvm/darwin-objc-cross-crate.rs @@ -0,0 +1,58 @@ +// Test that Objective-C class and selector references inlined across crates +// get defined in this CGU but non-inline references don't. + +// ignore-tidy-linelength +//@ aux-build: darwin_objc_aux.rs +//@ revisions: x86_64_macos aarch64_macos +//@ [x86_64_macos] only-x86_64-apple-darwin +//@ [aarch64_macos] only-aarch64-apple-darwin + +#![crate_type = "lib"] +#![feature(darwin_objc)] + +use std::os::darwin::objc; + +extern crate darwin_objc_aux as aux; + +#[no_mangle] +pub fn get_object_class() -> objc::Class { + aux::inline_get_object_class() +} + +#[no_mangle] +pub fn get_alloc_selector() -> objc::SEL { + aux::inline_get_alloc_selector() +} + +#[no_mangle] +pub fn get_string_class() -> objc::Class { + aux::never_inline_get_string_class() +} + +#[no_mangle] +pub fn get_init_selector() -> objc::SEL { + aux::never_inline_get_init_selector() +} + +// CHECK: %struct._class_t = type { ptr, ptr, ptr, ptr, ptr } + +// CHECK: @"OBJC_CLASS_$_NSObject" = external global %struct._class_t +// CHECK: @"OBJC_CLASSLIST_REFERENCES_$_.{{[0-9]+}}" = internal global ptr @"OBJC_CLASS_$_NSObject", section "__DATA,__objc_classrefs,regular,no_dead_strip", align 8 + +// CHECK: @OBJC_METH_VAR_NAME_.{{[0-9]+}} = private unnamed_addr constant [6 x i8] c"alloc\00", section "__TEXT,__objc_methname,cstring_literals", align 1 +// CHECK: @OBJC_SELECTOR_REFERENCES_.{{[0-9]+}} = internal externally_initialized global ptr @OBJC_METH_VAR_NAME_.{{[0-9]+}}, section "__DATA,__objc_selrefs,literal_pointers,no_dead_strip", align 8 + +// CHECK-NOT: @"OBJC_CLASS_$_NSString" = external global %struct._class_t +// CHECK-NOT: @"OBJC_CLASSLIST_REFERENCES_$_.{{[0-9]+}}" = internal global ptr @"OBJC_CLASS_$_NSString" + +// CHECK-NOT: @OBJC_METH_VAR_NAME_.{{[0-9]+}} = private unnamed_addr constant [5 x i8] c"init\00" +// CHECK-NOT: @OBJC_SELECTOR_REFERENCES_.{{[0-9]+}} = internal externally_initialized global ptr @OBJC_METH_VAR_NAME_.{{[0-9]+}} + +// CHECK: load ptr, ptr @"OBJC_CLASSLIST_REFERENCES_$_.{{[0-9]+}}", align 8 +// CHECK: load ptr, ptr @OBJC_SELECTOR_REFERENCES_.{{[0-9]+}}, align 8 + +// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Version", i32 2} +// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Image Info Version", i32 0} +// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Image Info Section", !"__DATA,__objc_imageinfo,regular,no_dead_strip"} +// CHECK-NOT: !{{[0-9]+}} = !{i32 1, !"Objective-C Is Simulated", i32 32} +// CHECK: !{{[0-9]+}} = !{i32 1, !"Objective-C Class Properties", i32 64} diff --git a/tests/ui/README.md b/tests/ui/README.md index 66c1bb905a79..3b28ef694dd3 100644 --- a/tests/ui/README.md +++ b/tests/ui/README.md @@ -350,6 +350,12 @@ Tests for FFI with C varargs (`va_list`). Tests for detection and handling of cyclic trait dependencies. +## `tests/ui/darwin-objc/`: Darwin Objective-C + +Tests exercising `#![feature(darwin_objc)]`. + +See [Tracking Issue for `darwin_objc` #145496](https://github.com/rust-lang/rust/issues/145496). + ## `tests/ui/dataflow_const_prop/` Contains a single regression test for const prop in `SwitchInt` pass crashing when `ptr2int` transmute is involved. diff --git a/tests/ui/darwin-objc/darwin-objc-bad-arg.rs b/tests/ui/darwin-objc/darwin-objc-bad-arg.rs new file mode 100644 index 000000000000..70eb83aa052d --- /dev/null +++ b/tests/ui/darwin-objc/darwin-objc-bad-arg.rs @@ -0,0 +1,36 @@ +// Test that `objc::class!` and `objc::selector!` only take string literals. + +//@ edition: 2024 +//@ only-apple + +#![feature(darwin_objc)] + +use std::os::darwin::objc; + +pub fn main() { + let s = "NSObject"; + objc::class!(s); + //~^ ERROR attribute value must be a literal + + objc::class!(NSObject); + //~^ ERROR attribute value must be a literal + + objc::class!(123); + //~^ ERROR `objc::class!` expected a string literal + + objc::class!("NSObject\0"); + //~^ ERROR `objc::class!` may not contain null characters + + let s = "alloc"; + objc::selector!(s); + //~^ ERROR attribute value must be a literal + + objc::selector!(alloc); + //~^ ERROR attribute value must be a literal + + objc::selector!(123); + //~^ ERROR `objc::selector!` expected a string literal + + objc::selector!("alloc\0"); + //~^ ERROR `objc::selector!` may not contain null characters +} diff --git a/tests/ui/darwin-objc/darwin-objc-bad-arg.stderr b/tests/ui/darwin-objc/darwin-objc-bad-arg.stderr new file mode 100644 index 000000000000..99eeb27e30c1 --- /dev/null +++ b/tests/ui/darwin-objc/darwin-objc-bad-arg.stderr @@ -0,0 +1,50 @@ +error: attribute value must be a literal + --> $DIR/darwin-objc-bad-arg.rs:12:18 + | +LL | objc::class!(s); + | ^ + +error: attribute value must be a literal + --> $DIR/darwin-objc-bad-arg.rs:15:18 + | +LL | objc::class!(NSObject); + | ^^^^^^^^ + +error: `objc::class!` expected a string literal + --> $DIR/darwin-objc-bad-arg.rs:18:18 + | +LL | objc::class!(123); + | ^^^ + +error: `objc::class!` may not contain null characters + --> $DIR/darwin-objc-bad-arg.rs:21:18 + | +LL | objc::class!("NSObject\0"); + | ^^^^^^^^^^^^ + +error: attribute value must be a literal + --> $DIR/darwin-objc-bad-arg.rs:25:21 + | +LL | objc::selector!(s); + | ^ + +error: attribute value must be a literal + --> $DIR/darwin-objc-bad-arg.rs:28:21 + | +LL | objc::selector!(alloc); + | ^^^^^ + +error: `objc::selector!` expected a string literal + --> $DIR/darwin-objc-bad-arg.rs:31:21 + | +LL | objc::selector!(123); + | ^^^ + +error: `objc::selector!` may not contain null characters + --> $DIR/darwin-objc-bad-arg.rs:34:21 + | +LL | objc::selector!("alloc\0"); + | ^^^^^^^^^ + +error: aborting due to 8 previous errors + diff --git a/tests/ui/darwin-objc/darwin-objc-bad-const.rs b/tests/ui/darwin-objc/darwin-objc-bad-const.rs new file mode 100644 index 000000000000..ccf28697b482 --- /dev/null +++ b/tests/ui/darwin-objc/darwin-objc-bad-const.rs @@ -0,0 +1,17 @@ +// Test that `objc::class!` and `objc::selector!` aren't `const` expressions. +// The system gives them their final values at dynamic load time. + +//@ edition: 2024 +//@ only-apple + +#![feature(darwin_objc)] + +use std::os::darwin::objc; + +pub const CLASS: objc::Class = objc::class!("NSObject"); +//~^ ERROR cannot access extern static `CLASS::VAL` [E0080] + +pub const SELECTOR: objc::SEL = objc::selector!("alloc"); +//~^ ERROR cannot access extern static `SELECTOR::VAL` [E0080] + +pub fn main() {} diff --git a/tests/ui/darwin-objc/darwin-objc-bad-const.stderr b/tests/ui/darwin-objc/darwin-objc-bad-const.stderr new file mode 100644 index 000000000000..43d0b7d761f1 --- /dev/null +++ b/tests/ui/darwin-objc/darwin-objc-bad-const.stderr @@ -0,0 +1,19 @@ +error[E0080]: cannot access extern static `CLASS::VAL` + --> $DIR/darwin-objc-bad-const.rs:11:32 + | +LL | pub const CLASS: objc::Class = objc::class!("NSObject"); + | ^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `CLASS` failed here + | + = note: this error originates in the macro `objc::class` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0080]: cannot access extern static `SELECTOR::VAL` + --> $DIR/darwin-objc-bad-const.rs:14:33 + | +LL | pub const SELECTOR: objc::SEL = objc::selector!("alloc"); + | ^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `SELECTOR` failed here + | + = note: this error originates in the macro `objc::selector` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/darwin-objc/darwin-objc-bad-ref.rs b/tests/ui/darwin-objc/darwin-objc-bad-ref.rs new file mode 100644 index 000000000000..fb1437366dd4 --- /dev/null +++ b/tests/ui/darwin-objc/darwin-objc-bad-ref.rs @@ -0,0 +1,31 @@ +// Test that `objc::class!` and `objc::selector!` can't be returned by reference. +// A single instance may have multiple addresses (e.g. across dylib boundaries). + +//@ edition: 2024 +//@ only-apple + +#![feature(darwin_objc)] + +use std::os::darwin::objc; + +pub fn class_ref<'a>() -> &'a objc::Class { + &objc::class!("NSObject") + //~^ ERROR cannot return reference to temporary value [E0515] +} + +pub fn class_ref_static() -> &'static objc::Class { + &objc::class!("NSObject") + //~^ ERROR cannot return reference to temporary value [E0515] +} + +pub fn selector_ref<'a>() -> &'a objc::SEL { + &objc::selector!("alloc") + //~^ ERROR cannot return reference to temporary value [E0515] +} + +pub fn selector_ref_static() -> &'static objc::SEL { + &objc::selector!("alloc") + //~^ ERROR cannot return reference to temporary value [E0515] +} + +pub fn main() {} diff --git a/tests/ui/darwin-objc/darwin-objc-bad-ref.stderr b/tests/ui/darwin-objc/darwin-objc-bad-ref.stderr new file mode 100644 index 000000000000..b09e6a75c844 --- /dev/null +++ b/tests/ui/darwin-objc/darwin-objc-bad-ref.stderr @@ -0,0 +1,39 @@ +error[E0515]: cannot return reference to temporary value + --> $DIR/darwin-objc-bad-ref.rs:12:5 + | +LL | &objc::class!("NSObject") + | ^------------------------ + | || + | |temporary value created here + | returns a reference to data owned by the current function + +error[E0515]: cannot return reference to temporary value + --> $DIR/darwin-objc-bad-ref.rs:17:5 + | +LL | &objc::class!("NSObject") + | ^------------------------ + | || + | |temporary value created here + | returns a reference to data owned by the current function + +error[E0515]: cannot return reference to temporary value + --> $DIR/darwin-objc-bad-ref.rs:22:5 + | +LL | &objc::selector!("alloc") + | ^------------------------ + | || + | |temporary value created here + | returns a reference to data owned by the current function + +error[E0515]: cannot return reference to temporary value + --> $DIR/darwin-objc-bad-ref.rs:27:5 + | +LL | &objc::selector!("alloc") + | ^------------------------ + | || + | |temporary value created here + | returns a reference to data owned by the current function + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0515`. diff --git a/tests/ui/darwin-objc/darwin-objc-class-selector.rs b/tests/ui/darwin-objc/darwin-objc-class-selector.rs new file mode 100644 index 000000000000..b9a2fc3634f4 --- /dev/null +++ b/tests/ui/darwin-objc/darwin-objc-class-selector.rs @@ -0,0 +1,31 @@ +// Call `[NSObject class]` using `objc::class!` and `objc::selector!`. + +//@ edition: 2024 +//@ only-apple +//@ run-pass + +#![feature(darwin_objc)] + +use std::mem::transmute; +use std::os::darwin::objc; + +#[link(name = "Foundation", kind = "framework")] +unsafe extern "C" {} + +#[link(name = "objc", kind = "dylib")] +unsafe extern "C" { + unsafe fn objc_msgSend(); +} + +fn main() { + let msg_send_fn = unsafe { + transmute::< + unsafe extern "C" fn(), + unsafe extern "C" fn(objc::Class, objc::SEL) -> objc::Class, + >(objc_msgSend) + }; + let static_sel = objc::selector!("class"); + let static_class = objc::class!("NSObject"); + let runtime_class = unsafe { msg_send_fn(static_class, static_sel) }; + assert_eq!(static_class, runtime_class); +} diff --git a/tests/ui/darwin-objc/darwin-objc-class.rs b/tests/ui/darwin-objc/darwin-objc-class.rs new file mode 100644 index 000000000000..851149d87268 --- /dev/null +++ b/tests/ui/darwin-objc/darwin-objc-class.rs @@ -0,0 +1,39 @@ +// Test that `objc::class!` returns the same thing as `objc_lookUpClass`. + +//@ edition: 2024 +//@ only-apple +//@ run-pass + +#![feature(darwin_objc)] + +use std::ffi::c_char; +use std::os::darwin::objc; + +#[link(name = "Foundation", kind = "framework")] +unsafe extern "C" {} + +#[link(name = "objc")] +unsafe extern "C" { + fn objc_lookUpClass(methname: *const c_char) -> objc::Class; +} + +fn get_object_class() -> objc::Class { + objc::class!("NSObject") +} + +fn lookup_object_class() -> objc::Class { + unsafe { objc_lookUpClass(c"NSObject".as_ptr()) } +} + +fn get_string_class() -> objc::Class { + objc::class!("NSString") +} + +fn lookup_string_class() -> objc::Class { + unsafe { objc_lookUpClass(c"NSString".as_ptr()) } +} + +fn main() { + assert_eq!(get_object_class(), lookup_object_class()); + assert_eq!(get_string_class(), lookup_string_class()); +} diff --git a/tests/ui/darwin-objc/darwin-objc-selector.rs b/tests/ui/darwin-objc/darwin-objc-selector.rs new file mode 100644 index 000000000000..008ae4c4ca45 --- /dev/null +++ b/tests/ui/darwin-objc/darwin-objc-selector.rs @@ -0,0 +1,36 @@ +// Test that `objc::selector!` returns the same thing as `sel_registerName`. + +//@ edition: 2024 +//@ only-apple +//@ run-pass + +#![feature(darwin_objc)] + +use std::ffi::c_char; +use std::os::darwin::objc; + +#[link(name = "objc")] +unsafe extern "C" { + fn sel_registerName(methname: *const c_char) -> objc::SEL; +} + +fn get_alloc_selector() -> objc::SEL { + objc::selector!("alloc") +} + +fn register_alloc_selector() -> objc::SEL { + unsafe { sel_registerName(c"alloc".as_ptr()) } +} + +fn get_init_selector() -> objc::SEL { + objc::selector!("initWithCString:encoding:") +} + +fn register_init_selector() -> objc::SEL { + unsafe { sel_registerName(c"initWithCString:encoding:".as_ptr()) } +} + +fn main() { + assert_eq!(get_alloc_selector(), register_alloc_selector()); + assert_eq!(get_init_selector(), register_init_selector()); +} From 16c720645cac29e71cc897374bb462fab42a8682 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 14 Sep 2025 14:37:10 +1000 Subject: [PATCH 486/682] Note some previous attempts to change the Default impl for `[T; 0]` --- library/core/src/array/mod.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index d14419a23a1d..d713e575b58d 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -472,6 +472,11 @@ impl SpecArrayClone for T { // The Default impls cannot be done with const generics because `[T; 0]` doesn't // require Default to be implemented, and having different impl blocks for // different numbers isn't supported yet. +// +// Trying to improve the `[T; 0]` situation has proven to be difficult. +// Please see these issues for more context on past attempts and crater runs: +// - https://github.com/rust-lang/rust/issues/61415 +// - https://github.com/rust-lang/rust/pull/145457 macro_rules! array_impl_default { {$n:expr, $t:ident $($ts:ident)*} => { From 3a784d9d1f292707000cdfce9072c876480471e4 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Sun, 14 Sep 2025 04:52:56 +0000 Subject: [PATCH 487/682] Prepare for merging from rust-lang/rust This updates the rust-version file to a015919e54c60b1b2bec7a98dec478cfc4a48f4e. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index e5f0be9ac955..8315eb30f943 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -4ba1cf9ade4c8e2fa10676a50ee34594eb161837 +a015919e54c60b1b2bec7a98dec478cfc4a48f4e From 8df078a3f0071b311be2449407ae523f89ca6a33 Mon Sep 17 00:00:00 2001 From: Tsukasa OI Date: Sat, 13 Sep 2025 21:59:39 +0000 Subject: [PATCH 488/682] RISC-V: Improvements of inline assembly uses This commit performs various improvements (better register allocation, less register clobbering on the worst case and better readability) of RISC-V inline assembly use cases. Note that it does not change the `p` module (which defines the "P" extension draft instructions but very likely to change). 1. Use `lateout` as possible. Unlike `out(reg)` and `in(reg)` pair, `lateout(reg)` and `in(reg)` can share the same register because they state that the late-output register is written after all the reads are performed. It can improve register allocation. 2. Add `preserves_flags` option as possible. While RISC-V doesn't have _regular_ condition codes, RISC-V inline assembly in the Rust language assumes that some registers (mainly vector state registers) may be overwritten by default. By adding `preserves_flags` to the intrinsics corresponding instructions without overwriting them, it can minimize register clobbering on the worst case. 3. Use trailing semicolon. As `asm!` declares an action and it doesn't return a value by itself, it would be better to have trailing semicolon to denote that an `asm!` call is effectively a statement. 4. Make most of `asm!` calls multi-lined. `rustfmt` makes some simple (yet long) `asm!` calls multi-lined but it does not perform formatting of complex `asm!` calls with inputs and/or outputs. To keep consistency, it makes most of the `asm!` calls multi-lined. --- .../crates/core_arch/src/riscv64/mod.rs | 21 +- .../crates/core_arch/src/riscv_shared/mod.rs | 245 ++++++++++++++---- 2 files changed, 215 insertions(+), 51 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/riscv64/mod.rs b/library/stdarch/crates/core_arch/src/riscv64/mod.rs index 0e860f6f2ad2..a7efc0c7f58a 100644 --- a/library/stdarch/crates/core_arch/src/riscv64/mod.rs +++ b/library/stdarch/crates/core_arch/src/riscv64/mod.rs @@ -20,7 +20,12 @@ pub use zk::*; #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hlv_wu(src: *const u32) -> u32 { let value: u32; - asm!(".insn i 0x73, 0x4, {}, {}, 0x681", out(reg) value, in(reg) src, options(readonly, nostack)); + asm!( + ".insn i 0x73, 0x4, {}, {}, 0x681", + lateout(reg) value, + in(reg) src, + options(readonly, nostack, preserves_flags) + ); value } @@ -38,7 +43,12 @@ pub unsafe fn hlv_wu(src: *const u32) -> u32 { #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hlv_d(src: *const i64) -> i64 { let value: i64; - asm!(".insn i 0x73, 0x4, {}, {}, 0x6C0", out(reg) value, in(reg) src, options(readonly, nostack)); + asm!( + ".insn i 0x73, 0x4, {}, {}, 0x6C0", + lateout(reg) value, + in(reg) src, + options(readonly, nostack, preserves_flags) + ); value } @@ -53,5 +63,10 @@ pub unsafe fn hlv_d(src: *const i64) -> i64 { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hsv_d(dst: *mut i64, src: i64) { - asm!(".insn r 0x73, 0x4, 0x37, x0, {}, {}", in(reg) dst, in(reg) src, options(nostack)); + asm!( + ".insn r 0x73, 0x4, 0x37, x0, {}, {}", + in(reg) dst, + in(reg) src, + options(nostack, preserves_flags) + ); } diff --git a/library/stdarch/crates/core_arch/src/riscv_shared/mod.rs b/library/stdarch/crates/core_arch/src/riscv_shared/mod.rs index 3ce24324de2e..1bd147a64808 100644 --- a/library/stdarch/crates/core_arch/src/riscv_shared/mod.rs +++ b/library/stdarch/crates/core_arch/src/riscv_shared/mod.rs @@ -44,7 +44,12 @@ use crate::arch::asm; #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub fn pause() { - unsafe { asm!(".insn i 0x0F, 0, x0, x0, 0x010", options(nomem, nostack)) } + unsafe { + asm!( + ".insn i 0x0F, 0, x0, x0, 0x010", + options(nomem, nostack, preserves_flags) + ); + } } /// Generates the `NOP` instruction @@ -54,7 +59,9 @@ pub fn pause() { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub fn nop() { - unsafe { asm!("nop", options(nomem, nostack)) } + unsafe { + asm!("nop", options(nomem, nostack, preserves_flags)); + } } /// Generates the `WFI` instruction @@ -65,7 +72,7 @@ pub fn nop() { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn wfi() { - asm!("wfi", options(nomem, nostack)) + asm!("wfi", options(nomem, nostack, preserves_flags)); } /// Generates the `FENCE.I` instruction @@ -78,7 +85,7 @@ pub unsafe fn wfi() { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn fence_i() { - asm!("fence.i", options(nostack)) + asm!("fence.i", options(nostack, preserves_flags)); } /// Supervisor memory management fence for given virtual address and address space @@ -92,7 +99,7 @@ pub unsafe fn fence_i() { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn sfence_vma(vaddr: usize, asid: usize) { - asm!("sfence.vma {}, {}", in(reg) vaddr, in(reg) asid, options(nostack)) + asm!("sfence.vma {}, {}", in(reg) vaddr, in(reg) asid, options(nostack, preserves_flags)); } /// Supervisor memory management fence for given virtual address @@ -104,7 +111,7 @@ pub unsafe fn sfence_vma(vaddr: usize, asid: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn sfence_vma_vaddr(vaddr: usize) { - asm!("sfence.vma {}, x0", in(reg) vaddr, options(nostack)) + asm!("sfence.vma {}, x0", in(reg) vaddr, options(nostack, preserves_flags)); } /// Supervisor memory management fence for given address space @@ -118,7 +125,7 @@ pub unsafe fn sfence_vma_vaddr(vaddr: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn sfence_vma_asid(asid: usize) { - asm!("sfence.vma x0, {}", in(reg) asid, options(nostack)) + asm!("sfence.vma x0, {}", in(reg) asid, options(nostack, preserves_flags)); } /// Supervisor memory management fence for all address spaces and virtual addresses @@ -129,7 +136,7 @@ pub unsafe fn sfence_vma_asid(asid: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn sfence_vma_all() { - asm!("sfence.vma", options(nostack)) + asm!("sfence.vma", options(nostack, preserves_flags)); } /// Invalidate supervisor translation cache for given virtual address and address space @@ -139,8 +146,13 @@ pub unsafe fn sfence_vma_all() { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn sinval_vma(vaddr: usize, asid: usize) { - // asm!("sinval.vma {}, {}", in(reg) vaddr, in(reg) asid, options(nostack)) - asm!(".insn r 0x73, 0, 0x0B, x0, {}, {}", in(reg) vaddr, in(reg) asid, options(nostack)) + // asm!("sinval.vma {}, {}", in(reg) vaddr, in(reg) asid, options(nostack, preserves_flags)); + asm!( + ".insn r 0x73, 0, 0x0B, x0, {}, {}", + in(reg) vaddr, + in(reg) asid, + options(nostack, preserves_flags) + ); } /// Invalidate supervisor translation cache for given virtual address @@ -150,7 +162,11 @@ pub unsafe fn sinval_vma(vaddr: usize, asid: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn sinval_vma_vaddr(vaddr: usize) { - asm!(".insn r 0x73, 0, 0x0B, x0, {}, x0", in(reg) vaddr, options(nostack)) + asm!( + ".insn r 0x73, 0, 0x0B, x0, {}, x0", + in(reg) vaddr, + options(nostack, preserves_flags) + ); } /// Invalidate supervisor translation cache for given address space @@ -160,7 +176,11 @@ pub unsafe fn sinval_vma_vaddr(vaddr: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn sinval_vma_asid(asid: usize) { - asm!(".insn r 0x73, 0, 0x0B, x0, x0, {}", in(reg) asid, options(nostack)) + asm!( + ".insn r 0x73, 0, 0x0B, x0, x0, {}", + in(reg) asid, + options(nostack, preserves_flags) + ); } /// Invalidate supervisor translation cache for all address spaces and virtual addresses @@ -170,7 +190,10 @@ pub unsafe fn sinval_vma_asid(asid: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn sinval_vma_all() { - asm!(".insn r 0x73, 0, 0x0B, x0, x0, x0", options(nostack)) + asm!( + ".insn r 0x73, 0, 0x0B, x0, x0, x0", + options(nostack, preserves_flags) + ); } /// Generates the `SFENCE.W.INVAL` instruction @@ -180,8 +203,11 @@ pub unsafe fn sinval_vma_all() { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn sfence_w_inval() { - // asm!("sfence.w.inval", options(nostack)) - asm!(".insn i 0x73, 0, x0, x0, 0x180", options(nostack)) + // asm!("sfence.w.inval", options(nostack, preserves_flags)); + asm!( + ".insn i 0x73, 0, x0, x0, 0x180", + options(nostack, preserves_flags) + ); } /// Generates the `SFENCE.INVAL.IR` instruction @@ -191,8 +217,11 @@ pub unsafe fn sfence_w_inval() { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn sfence_inval_ir() { - // asm!("sfence.inval.ir", options(nostack)) - asm!(".insn i 0x73, 0, x0, x0, 0x181", options(nostack)) + // asm!("sfence.inval.ir", options(nostack, preserves_flags)); + asm!( + ".insn i 0x73, 0, x0, x0, 0x181", + options(nostack, preserves_flags) + ); } /// Loads virtual machine memory by signed byte integer @@ -207,7 +236,12 @@ pub unsafe fn sfence_inval_ir() { #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hlv_b(src: *const i8) -> i8 { let value: i8; - asm!(".insn i 0x73, 0x4, {}, {}, 0x600", out(reg) value, in(reg) src, options(readonly, nostack)); + asm!( + ".insn i 0x73, 0x4, {}, {}, 0x600", + lateout(reg) value, + in(reg) src, + options(readonly, nostack, preserves_flags) + ); value } @@ -223,7 +257,12 @@ pub unsafe fn hlv_b(src: *const i8) -> i8 { #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hlv_bu(src: *const u8) -> u8 { let value: u8; - asm!(".insn i 0x73, 0x4, {}, {}, 0x601", out(reg) value, in(reg) src, options(readonly, nostack)); + asm!( + ".insn i 0x73, 0x4, {}, {}, 0x601", + lateout(reg) value, + in(reg) src, + options(readonly, nostack, preserves_flags) + ); value } @@ -239,7 +278,12 @@ pub unsafe fn hlv_bu(src: *const u8) -> u8 { #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hlv_h(src: *const i16) -> i16 { let value: i16; - asm!(".insn i 0x73, 0x4, {}, {}, 0x640", out(reg) value, in(reg) src, options(readonly, nostack)); + asm!( + ".insn i 0x73, 0x4, {}, {}, 0x640", + lateout(reg) value, + in(reg) src, + options(readonly, nostack, preserves_flags) + ); value } @@ -255,7 +299,12 @@ pub unsafe fn hlv_h(src: *const i16) -> i16 { #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hlv_hu(src: *const u16) -> u16 { let value: u16; - asm!(".insn i 0x73, 0x4, {}, {}, 0x641", out(reg) value, in(reg) src, options(readonly, nostack)); + asm!( + ".insn i 0x73, 0x4, {}, {}, 0x641", + lateout(reg) value, + in(reg) src, + options(readonly, nostack, preserves_flags) + ); value } @@ -271,7 +320,12 @@ pub unsafe fn hlv_hu(src: *const u16) -> u16 { #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hlvx_hu(src: *const u16) -> u16 { let insn: u16; - asm!(".insn i 0x73, 0x4, {}, {}, 0x643", out(reg) insn, in(reg) src, options(readonly, nostack)); + asm!( + ".insn i 0x73, 0x4, {}, {}, 0x643", + lateout(reg) insn, + in(reg) src, + options(readonly, nostack, preserves_flags) + ); insn } @@ -287,7 +341,12 @@ pub unsafe fn hlvx_hu(src: *const u16) -> u16 { #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hlv_w(src: *const i32) -> i32 { let value: i32; - asm!(".insn i 0x73, 0x4, {}, {}, 0x680", out(reg) value, in(reg) src, options(readonly, nostack)); + asm!( + ".insn i 0x73, 0x4, {}, {}, 0x680", + lateout(reg) value, + in(reg) src, + options(readonly, nostack, preserves_flags) + ); value } @@ -303,7 +362,12 @@ pub unsafe fn hlv_w(src: *const i32) -> i32 { #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hlvx_wu(src: *const u32) -> u32 { let insn: u32; - asm!(".insn i 0x73, 0x4, {}, {}, 0x683", out(reg) insn, in(reg) src, options(readonly, nostack)); + asm!( + ".insn i 0x73, 0x4, {}, {}, 0x683", + lateout(reg) insn, + in(reg) src, + options(readonly, nostack, preserves_flags) + ); insn } @@ -318,7 +382,12 @@ pub unsafe fn hlvx_wu(src: *const u32) -> u32 { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hsv_b(dst: *mut i8, src: i8) { - asm!(".insn r 0x73, 0x4, 0x31, x0, {}, {}", in(reg) dst, in(reg) src, options(nostack)); + asm!( + ".insn r 0x73, 0x4, 0x31, x0, {}, {}", + in(reg) dst, + in(reg) src, + options(nostack, preserves_flags) + ); } /// Stores virtual machine memory by half integer @@ -332,7 +401,12 @@ pub unsafe fn hsv_b(dst: *mut i8, src: i8) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hsv_h(dst: *mut i16, src: i16) { - asm!(".insn r 0x73, 0x4, 0x33, x0, {}, {}", in(reg) dst, in(reg) src, options(nostack)); + asm!( + ".insn r 0x73, 0x4, 0x33, x0, {}, {}", + in(reg) dst, + in(reg) src, + options(nostack, preserves_flags) + ); } /// Stores virtual machine memory by word integer @@ -346,7 +420,12 @@ pub unsafe fn hsv_h(dst: *mut i16, src: i16) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hsv_w(dst: *mut i32, src: i32) { - asm!(".insn r 0x73, 0x4, 0x35, x0, {}, {}", in(reg) dst, in(reg) src, options(nostack)); + asm!( + ".insn r 0x73, 0x4, 0x35, x0, {}, {}", + in(reg) dst, + in(reg) src, + options(nostack, preserves_flags) + ); } /// Hypervisor memory management fence for given guest virtual address and guest address space @@ -360,8 +439,13 @@ pub unsafe fn hsv_w(dst: *mut i32, src: i32) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hfence_vvma(vaddr: usize, asid: usize) { - // asm!("hfence.vvma {}, {}", in(reg) vaddr, in(reg) asid) - asm!(".insn r 0x73, 0, 0x11, x0, {}, {}", in(reg) vaddr, in(reg) asid, options(nostack)) + // asm!("hfence.vvma {}, {}", in(reg) vaddr, in(reg) asid, options(nostack, preserves_flags)); + asm!( + ".insn r 0x73, 0, 0x11, x0, {}, {}", + in(reg) vaddr, + in(reg) asid, + options(nostack, preserves_flags) + ); } /// Hypervisor memory management fence for given guest virtual address @@ -375,7 +459,11 @@ pub unsafe fn hfence_vvma(vaddr: usize, asid: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hfence_vvma_vaddr(vaddr: usize) { - asm!(".insn r 0x73, 0, 0x11, x0, {}, x0", in(reg) vaddr, options(nostack)) + asm!( + ".insn r 0x73, 0, 0x11, x0, {}, x0", + in(reg) vaddr, + options(nostack, preserves_flags) + ); } /// Hypervisor memory management fence for given guest address space @@ -389,7 +477,11 @@ pub unsafe fn hfence_vvma_vaddr(vaddr: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hfence_vvma_asid(asid: usize) { - asm!(".insn r 0x73, 0, 0x11, x0, x0, {}", in(reg) asid, options(nostack)) + asm!( + ".insn r 0x73, 0, 0x11, x0, x0, {}", + in(reg) asid, + options(nostack, preserves_flags) + ); } /// Hypervisor memory management fence for all guest address spaces and guest virtual addresses @@ -403,7 +495,10 @@ pub unsafe fn hfence_vvma_asid(asid: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hfence_vvma_all() { - asm!(".insn r 0x73, 0, 0x11, x0, x0, x0", options(nostack)) + asm!( + ".insn r 0x73, 0, 0x11, x0, x0, x0", + options(nostack, preserves_flags) + ); } /// Hypervisor memory management fence for guest physical address and virtual machine @@ -416,8 +511,13 @@ pub unsafe fn hfence_vvma_all() { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hfence_gvma(gaddr: usize, vmid: usize) { - // asm!("hfence.gvma {}, {}", in(reg) gaddr, in(reg) vmid, options(nostack)) - asm!(".insn r 0x73, 0, 0x31, x0, {}, {}", in(reg) gaddr, in(reg) vmid, options(nostack)) + // asm!("hfence.gvma {}, {}", in(reg) gaddr, in(reg) vmid, options(nostack, preserves_flags)); + asm!( + ".insn r 0x73, 0, 0x31, x0, {}, {}", + in(reg) gaddr, + in(reg) vmid, + options(nostack, preserves_flags) + ); } /// Hypervisor memory management fence for guest physical address @@ -429,7 +529,11 @@ pub unsafe fn hfence_gvma(gaddr: usize, vmid: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hfence_gvma_gaddr(gaddr: usize) { - asm!(".insn r 0x73, 0, 0x31, x0, {}, x0", in(reg) gaddr, options(nostack)) + asm!( + ".insn r 0x73, 0, 0x31, x0, {}, x0", + in(reg) gaddr, + options(nostack, preserves_flags) + ); } /// Hypervisor memory management fence for given virtual machine @@ -441,7 +545,11 @@ pub unsafe fn hfence_gvma_gaddr(gaddr: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hfence_gvma_vmid(vmid: usize) { - asm!(".insn r 0x73, 0, 0x31, x0, x0, {}", in(reg) vmid, options(nostack)) + asm!( + ".insn r 0x73, 0, 0x31, x0, x0, {}", + in(reg) vmid, + options(nostack, preserves_flags) + ); } /// Hypervisor memory management fence for all virtual machines and guest physical addresses @@ -453,7 +561,10 @@ pub unsafe fn hfence_gvma_vmid(vmid: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hfence_gvma_all() { - asm!(".insn r 0x73, 0, 0x31, x0, x0, x0", options(nostack)) + asm!( + ".insn r 0x73, 0, 0x31, x0, x0, x0", + options(nostack, preserves_flags) + ); } /// Invalidate hypervisor translation cache for given guest virtual address and guest address space @@ -465,8 +576,13 @@ pub unsafe fn hfence_gvma_all() { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hinval_vvma(vaddr: usize, asid: usize) { - // asm!("hinval.vvma {}, {}", in(reg) vaddr, in(reg) asid, options(nostack)) - asm!(".insn r 0x73, 0, 0x13, x0, {}, {}", in(reg) vaddr, in(reg) asid, options(nostack)) + // asm!("hinval.vvma {}, {}", in(reg) vaddr, in(reg) asid, options(nostack, preserves_flags)); + asm!( + ".insn r 0x73, 0, 0x13, x0, {}, {}", + in(reg) vaddr, + in(reg) asid, + options(nostack, preserves_flags) + ); } /// Invalidate hypervisor translation cache for given guest virtual address @@ -478,7 +594,11 @@ pub unsafe fn hinval_vvma(vaddr: usize, asid: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hinval_vvma_vaddr(vaddr: usize) { - asm!(".insn r 0x73, 0, 0x13, x0, {}, x0", in(reg) vaddr, options(nostack)) + asm!( + ".insn r 0x73, 0, 0x13, x0, {}, x0", + in(reg) vaddr, + options(nostack, preserves_flags) + ); } /// Invalidate hypervisor translation cache for given guest address space @@ -490,7 +610,11 @@ pub unsafe fn hinval_vvma_vaddr(vaddr: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hinval_vvma_asid(asid: usize) { - asm!(".insn r 0x73, 0, 0x13, x0, x0, {}", in(reg) asid, options(nostack)) + asm!( + ".insn r 0x73, 0, 0x13, x0, x0, {}", + in(reg) asid, + options(nostack, preserves_flags) + ); } /// Invalidate hypervisor translation cache for all guest address spaces and guest virtual addresses @@ -502,7 +626,10 @@ pub unsafe fn hinval_vvma_asid(asid: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hinval_vvma_all() { - asm!(".insn r 0x73, 0, 0x13, x0, x0, x0", options(nostack)) + asm!( + ".insn r 0x73, 0, 0x13, x0, x0, x0", + options(nostack, preserves_flags) + ); } /// Invalidate hypervisor translation cache for guest physical address and virtual machine @@ -515,8 +642,13 @@ pub unsafe fn hinval_vvma_all() { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hinval_gvma(gaddr: usize, vmid: usize) { - // asm!("hinval.gvma {}, {}", in(reg) gaddr, in(reg) vmid, options(nostack)) - asm!(".insn r 0x73, 0, 0x33, x0, {}, {}", in(reg) gaddr, in(reg) vmid, options(nostack)) + // asm!("hinval.gvma {}, {}", in(reg) gaddr, in(reg) vmid, options(nostack, preserves_flags)); + asm!( + ".insn r 0x73, 0, 0x33, x0, {}, {}", + in(reg) gaddr, + in(reg) vmid, + options(nostack, preserves_flags) + ); } /// Invalidate hypervisor translation cache for guest physical address @@ -528,7 +660,11 @@ pub unsafe fn hinval_gvma(gaddr: usize, vmid: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hinval_gvma_gaddr(gaddr: usize) { - asm!(".insn r 0x73, 0, 0x33, x0, {}, x0", in(reg) gaddr, options(nostack)) + asm!( + ".insn r 0x73, 0, 0x33, x0, {}, x0", + in(reg) gaddr, + options(nostack, preserves_flags) + ); } /// Invalidate hypervisor translation cache for given virtual machine @@ -540,7 +676,11 @@ pub unsafe fn hinval_gvma_gaddr(gaddr: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hinval_gvma_vmid(vmid: usize) { - asm!(".insn r 0x73, 0, 0x33, x0, x0, {}", in(reg) vmid, options(nostack)) + asm!( + ".insn r 0x73, 0, 0x33, x0, x0, {}", + in(reg) vmid, + options(nostack, preserves_flags) + ); } /// Invalidate hypervisor translation cache for all virtual machines and guest physical addresses @@ -552,7 +692,10 @@ pub unsafe fn hinval_gvma_vmid(vmid: usize) { #[inline] #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub unsafe fn hinval_gvma_all() { - asm!(".insn r 0x73, 0, 0x33, x0, x0, x0", options(nostack)) + asm!( + ".insn r 0x73, 0, 0x33, x0, x0, x0", + options(nostack, preserves_flags) + ); } /// Reads the floating-point rounding mode register `frm` @@ -574,6 +717,12 @@ pub unsafe fn hinval_gvma_all() { #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] pub fn frrm() -> u32 { let value: u32; - unsafe { asm!("frrm {}", out(reg) value, options(nomem, nostack)) }; + unsafe { + asm!( + "frrm {}", + out(reg) value, + options(nomem, nostack, preserves_flags) + ); + } value } From f56eb060479e6dcb386f7a67fa9f13ae3c31e6f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Wed, 10 Sep 2025 11:01:34 -0700 Subject: [PATCH 489/682] merge crate-level into ALLOWED_TARGETS --- .../src/attributes/crate_level.rs | 40 ++++--------------- .../rustc_attr_parsing/src/attributes/mod.rs | 18 +-------- .../src/attributes/prelude.rs | 2 +- .../src/attributes/traits.rs | 5 +-- compiler/rustc_attr_parsing/src/context.rs | 4 +- compiler/rustc_attr_parsing/src/interface.rs | 1 - .../rustc_attr_parsing/src/target_checking.rs | 15 +++++-- 7 files changed, 23 insertions(+), 62 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index 0b2c05482bfa..4611de444599 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -43,11 +43,7 @@ impl SingleAttributeParser for CrateNameParser { const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name"); - const TYPE: AttributeType = AttributeType::CrateLevel; - - // because it's a crate-level attribute, we already warn about it. - // Putting target limitations here would give duplicate warnings - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { let ArgParser::NameValue(n) = args else { @@ -76,11 +72,7 @@ impl SingleAttributeParser for RecursionLimitParser { const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N", "https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute"); - const TYPE: AttributeType = AttributeType::CrateLevel; - - // because it's a crate-level attribute, we already warn about it. - // Putting target limitations here would give duplicate warnings - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { let ArgParser::NameValue(nv) = args else { @@ -103,11 +95,7 @@ impl SingleAttributeParser for MoveSizeLimitParser { const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N"); - const TYPE: AttributeType = AttributeType::CrateLevel; - - // because it's a crate-level attribute, we already warn about it. - // Putting target limitations here would give duplicate warnings - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { let ArgParser::NameValue(nv) = args else { @@ -130,11 +118,7 @@ impl SingleAttributeParser for TypeLengthLimitParser { const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N"); - const TYPE: AttributeType = AttributeType::CrateLevel; - - // because it's a crate-level attribute, we already warn about it. - // Putting target limitations here would give duplicate warnings - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { let ArgParser::NameValue(nv) = args else { @@ -157,11 +141,7 @@ impl SingleAttributeParser for PatternComplexityLimitParser { const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N"); - const TYPE: AttributeType = AttributeType::CrateLevel; - - // because it's a crate-level attribute, we already warn about it. - // Putting target limitations here would give duplicate warnings - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { let ArgParser::NameValue(nv) = args else { @@ -182,11 +162,8 @@ pub(crate) struct NoCoreParser; impl NoArgsAttributeParser for NoCoreParser { const PATH: &[Symbol] = &[sym::no_core]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - // because it's a crate-level attribute, we already warn about it. - // Putting target limitations here would give duplicate warnings - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoCore; - const TYPE: AttributeType = AttributeType::CrateLevel; } pub(crate) struct NoStdParser; @@ -194,9 +171,6 @@ pub(crate) struct NoStdParser; impl NoArgsAttributeParser for NoStdParser { const PATH: &[Symbol] = &[sym::no_std]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - // because it's a crate-level attribute, we already warn about it. - // Putting target limitations here would give duplicate warnings - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoStd; - const TYPE: AttributeType = AttributeType::CrateLevel; } diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 043bc925eac9..4ed13d239b9d 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -12,15 +12,11 @@ //! - [`CombineAttributeParser`](crate::attributes::CombineAttributeParser): makes it easy to implement an attribute which should combine the //! contents of attributes, if an attribute appear multiple times in a list //! -//! By default, attributes are allowed anywhere. When adding an attribute that should only be used -//! at the crate root, consider setting the `TYPE` in the parser trait to -//! [`AttributeType::CrateLevel`](rustc_feature::AttributeType::CrateLevel). -//! //! Attributes should be added to `crate::context::ATTRIBUTE_PARSERS` to be parsed. use std::marker::PhantomData; -use rustc_feature::{AttributeTemplate, AttributeType, template}; +use rustc_feature::{AttributeTemplate, template}; use rustc_hir::attrs::AttributeKind; use rustc_span::{Span, Symbol}; use thin_vec::ThinVec; @@ -89,11 +85,8 @@ pub(crate) trait AttributeParser: Default + 'static { /// /// If an attribute has this symbol, the `accept` function will be called on it. const ATTRIBUTES: AcceptMapping; - const ALLOWED_TARGETS: AllowedTargets; - const TYPE: AttributeType = AttributeType::Normal; - /// The parser has gotten a chance to accept the attributes on an item, /// here it can produce an attribute. /// @@ -135,8 +128,6 @@ pub(crate) trait SingleAttributeParser: 'static { /// The template this attribute parser should implement. Used for diagnostics. const TEMPLATE: AttributeTemplate; - const TYPE: AttributeType = AttributeType::Normal; - /// Converts a single syntactical attribute to a single semantic attribute, or [`AttributeKind`] fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option; } @@ -183,8 +174,6 @@ impl, S: Stage> AttributeParser for Single )]; const ALLOWED_TARGETS: AllowedTargets = T::ALLOWED_TARGETS; - const TYPE: AttributeType = T::TYPE; - fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option { Some(self.1?.0) } @@ -269,7 +258,6 @@ pub(crate) trait NoArgsAttributeParser: 'static { const PATH: &[Symbol]; const ON_DUPLICATE: OnDuplicate; const ALLOWED_TARGETS: AllowedTargets; - const TYPE: AttributeType = AttributeType::Normal; /// Create the [`AttributeKind`] given attribute's [`Span`]. const CREATE: fn(Span) -> AttributeKind; @@ -289,7 +277,6 @@ impl, S: Stage> SingleAttributeParser for Without const ON_DUPLICATE: OnDuplicate = T::ON_DUPLICATE; const ALLOWED_TARGETS: AllowedTargets = T::ALLOWED_TARGETS; const TEMPLATE: AttributeTemplate = template!(Word); - const TYPE: AttributeType = T::TYPE; fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { if let Err(span) = args.no_args() { @@ -323,8 +310,6 @@ pub(crate) trait CombineAttributeParser: 'static { /// The template this attribute parser should implement. Used for diagnostics. const TEMPLATE: AttributeTemplate; - const TYPE: AttributeType = AttributeType::Normal; - /// Converts a single syntactical attribute to a number of elements of the semantic attribute, or [`AttributeKind`] fn extend<'c>( cx: &'c mut AcceptContext<'_, '_, S>, @@ -360,7 +345,6 @@ impl, S: Stage> AttributeParser for Combine) -> Option { if let Some(first_span) = self.first_span { diff --git a/compiler/rustc_attr_parsing/src/attributes/prelude.rs b/compiler/rustc_attr_parsing/src/attributes/prelude.rs index 8f040ffb9d41..980366b5c372 100644 --- a/compiler/rustc_attr_parsing/src/attributes/prelude.rs +++ b/compiler/rustc_attr_parsing/src/attributes/prelude.rs @@ -1,6 +1,6 @@ // data structures #[doc(hidden)] -pub(super) use rustc_feature::{AttributeTemplate, AttributeType, template}; +pub(super) use rustc_feature::{AttributeTemplate, template}; #[doc(hidden)] pub(super) use rustc_hir::attrs::AttributeKind; #[doc(hidden)] diff --git a/compiler/rustc_attr_parsing/src/attributes/traits.rs b/compiler/rustc_attr_parsing/src/attributes/traits.rs index fbd9a480fbb6..c756bce96e28 100644 --- a/compiler/rustc_attr_parsing/src/attributes/traits.rs +++ b/compiler/rustc_attr_parsing/src/attributes/traits.rs @@ -1,7 +1,5 @@ use std::mem; -use rustc_feature::AttributeType; - use super::prelude::*; use crate::attributes::{ AttributeOrder, NoArgsAttributeParser, OnDuplicate, SingleAttributeParser, @@ -155,8 +153,7 @@ pub(crate) struct CoherenceIsCoreParser; impl NoArgsAttributeParser for CoherenceIsCoreParser { const PATH: &[Symbol] = &[sym::rustc_coherence_is_core]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); - const TYPE: AttributeType = AttributeType::CrateLevel; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::CoherenceIsCore; } diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index b3ab1d3edd6d..d7998048be5a 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -6,7 +6,7 @@ use std::sync::LazyLock; use private::Sealed; use rustc_ast::{AttrStyle, CRATE_NODE_ID, MetaItemLit, NodeId}; use rustc_errors::{Diag, Diagnostic, Level}; -use rustc_feature::{AttributeTemplate, AttributeType}; +use rustc_feature::AttributeTemplate; use rustc_hir::attrs::AttributeKind; use rustc_hir::lints::{AttributeLint, AttributeLintKind}; use rustc_hir::{AttrPath, CRATE_HIR_ID, HirId}; @@ -83,7 +83,6 @@ pub(super) struct GroupTypeInnerAccept { pub(super) template: AttributeTemplate, pub(super) accept_fn: AcceptFn, pub(super) allowed_targets: AllowedTargets, - pub(super) attribute_type: AttributeType, } type AcceptFn = @@ -133,7 +132,6 @@ macro_rules! attribute_parsers { }) }), allowed_targets: <$names as crate::attributes::AttributeParser<$stage>>::ALLOWED_TARGETS, - attribute_type: <$names as crate::attributes::AttributeParser<$stage>>::TYPE, }); } diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index 0fe3c209421f..8f2de4af14e0 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -272,7 +272,6 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { (accept.accept_fn)(&mut cx, args); if !matches!(cx.stage.should_emit(), ShouldEmit::Nothing) { - Self::check_type(accept.attribute_type, target, &mut cx); Self::check_target(&accept.allowed_targets, target, &mut cx); } } diff --git a/compiler/rustc_attr_parsing/src/target_checking.rs b/compiler/rustc_attr_parsing/src/target_checking.rs index edc496b460cf..c52253699b51 100644 --- a/compiler/rustc_attr_parsing/src/target_checking.rs +++ b/compiler/rustc_attr_parsing/src/target_checking.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use rustc_ast::AttrStyle; use rustc_errors::DiagArgValue; -use rustc_feature::{AttributeType, Features}; +use rustc_feature::Features; use rustc_hir::lints::AttributeLintKind; use rustc_hir::{MethodKind, Target}; @@ -14,6 +14,11 @@ use crate::session_diagnostics::InvalidTarget; pub(crate) enum AllowedTargets { AllowList(&'static [Policy]), AllowListWarnRest(&'static [Policy]), + /// Special, and not the same as `AllowList(&[Allow(Target::Crate)])`. + /// For crate-level attributes we emit a specific set of lints to warn + /// people about accidentally not using them on the crate. + /// Only use this for attributes that are *exclusively* valid at the crate level. + CrateLevel, } pub(crate) enum AllowedResult { @@ -43,6 +48,7 @@ impl AllowedTargets { AllowedResult::Warn } } + AllowedTargets::CrateLevel => AllowedResult::Allowed, } } @@ -50,6 +56,7 @@ impl AllowedTargets { match self { AllowedTargets::AllowList(list) => list, AllowedTargets::AllowListWarnRest(list) => list, + AllowedTargets::CrateLevel => ALL_TARGETS, } .iter() .filter_map(|target| match target { @@ -74,6 +81,8 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { target: Target, cx: &mut AcceptContext<'_, 'sess, S>, ) { + Self::check_type(matches!(allowed_targets, AllowedTargets::CrateLevel), target, cx); + match allowed_targets.is_allowed(target) { AllowedResult::Allowed => {} AllowedResult::Warn => { @@ -109,7 +118,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { } pub(crate) fn check_type( - attribute_type: AttributeType, + crate_level: bool, target: Target, cx: &mut AcceptContext<'_, 'sess, S>, ) { @@ -119,7 +128,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { return; } - if attribute_type != AttributeType::CrateLevel { + if !crate_level { return; } From 074bff027e09e35b929a09ba834c68546e4a1194 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sun, 14 Sep 2025 08:56:40 +0200 Subject: [PATCH 490/682] Remove unnecessary `#![allow]` in test --- tests/ui/never_loop_fixable.fixed | 2 +- tests/ui/never_loop_fixable.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/never_loop_fixable.fixed b/tests/ui/never_loop_fixable.fixed index 00c2af93a28f..5bc9ff1bb4df 100644 --- a/tests/ui/never_loop_fixable.fixed +++ b/tests/ui/never_loop_fixable.fixed @@ -1,4 +1,4 @@ -#![allow(clippy::iter_next_slice, clippy::needless_return, clippy::redundant_pattern_matching)] +#![allow(clippy::iter_next_slice, clippy::needless_return)] fn no_break_or_continue_loop() { if let Some(i) = [1, 2, 3].iter().next() { diff --git a/tests/ui/never_loop_fixable.rs b/tests/ui/never_loop_fixable.rs index de85599f094d..9782bc107e9a 100644 --- a/tests/ui/never_loop_fixable.rs +++ b/tests/ui/never_loop_fixable.rs @@ -1,4 +1,4 @@ -#![allow(clippy::iter_next_slice, clippy::needless_return, clippy::redundant_pattern_matching)] +#![allow(clippy::iter_next_slice, clippy::needless_return)] fn no_break_or_continue_loop() { for i in [1, 2, 3].iter() { From 619a6964c757e9e94253fa6618fb03519ad2b55e Mon Sep 17 00:00:00 2001 From: omskscream Date: Sat, 13 Sep 2025 22:41:43 +0300 Subject: [PATCH 491/682] clean up issue-2284 (core marker trait name shadowing) --- tests/ui/issues/issue-2284.rs | 13 ------------ .../core-marker-name-shadowing-issue-2284.rs | 21 +++++++++++++++++++ 2 files changed, 21 insertions(+), 13 deletions(-) delete mode 100644 tests/ui/issues/issue-2284.rs create mode 100644 tests/ui/traits/core-marker-name-shadowing-issue-2284.rs diff --git a/tests/ui/issues/issue-2284.rs b/tests/ui/issues/issue-2284.rs deleted file mode 100644 index 358331ecd9a4..000000000000 --- a/tests/ui/issues/issue-2284.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@ run-pass -#![allow(dead_code)] - -trait Send { - fn f(&self); -} - -fn f(t: T) { - t.f(); -} - -pub fn main() { -} diff --git a/tests/ui/traits/core-marker-name-shadowing-issue-2284.rs b/tests/ui/traits/core-marker-name-shadowing-issue-2284.rs new file mode 100644 index 000000000000..e5d083ac8c3f --- /dev/null +++ b/tests/ui/traits/core-marker-name-shadowing-issue-2284.rs @@ -0,0 +1,21 @@ +//@ run-pass +#![allow(dead_code)] + +//! Tests that user-defined trait is prioritized in compile time over +//! the core::marker trait with the same name, allowing shadowing core traits. +//! +//! # Context +//! Original issue: https://github.com/rust-lang/rust/issues/2284 +//! Original fix pull request: https://github.com/rust-lang/rust/pull/3792 + + +trait Send { + fn f(&self); +} + +fn f(t: T) { + t.f(); +} + +pub fn main() { +} From 05a5c7d0a104d3c3efbccb0aaf01b51495ad3080 Mon Sep 17 00:00:00 2001 From: omskscream Date: Sat, 13 Sep 2025 23:43:20 +0300 Subject: [PATCH 492/682] clean up issue-19479 (assoc type from another trait) --- .../assoc-type-via-another-trait-issue-19479.rs} | 6 ++++++ 1 file changed, 6 insertions(+) rename tests/ui/{issues/issue-19479.rs => traits/associated_type_bound/assoc-type-via-another-trait-issue-19479.rs} (50%) diff --git a/tests/ui/issues/issue-19479.rs b/tests/ui/traits/associated_type_bound/assoc-type-via-another-trait-issue-19479.rs similarity index 50% rename from tests/ui/issues/issue-19479.rs rename to tests/ui/traits/associated_type_bound/assoc-type-via-another-trait-issue-19479.rs index ed586b765502..f17a89bcb5f4 100644 --- a/tests/ui/issues/issue-19479.rs +++ b/tests/ui/traits/associated_type_bound/assoc-type-via-another-trait-issue-19479.rs @@ -1,5 +1,11 @@ //@ check-pass +//! Tests that it's possible to define an associated type in a trait +//! using an associated type from type parameter bound trait in a blanket implementation. +//! +//! # Context +//! Original issue: https://github.com/rust-lang/rust/issues/19479 + trait Base { fn dummy(&self) { } } From 8ee3a08b871f8b24075f67eda06330f7005cd435 Mon Sep 17 00:00:00 2001 From: omskscream Date: Sat, 13 Sep 2025 23:56:55 +0300 Subject: [PATCH 493/682] clean up issue-18088 (operator from supertrait) --- tests/ui/issues/issue-18088.rs | 8 -------- .../inheritance/supertrait-operator-issue-18088.rs | 13 +++++++++++++ 2 files changed, 13 insertions(+), 8 deletions(-) delete mode 100644 tests/ui/issues/issue-18088.rs create mode 100644 tests/ui/traits/inheritance/supertrait-operator-issue-18088.rs diff --git a/tests/ui/issues/issue-18088.rs b/tests/ui/issues/issue-18088.rs deleted file mode 100644 index ba198884c639..000000000000 --- a/tests/ui/issues/issue-18088.rs +++ /dev/null @@ -1,8 +0,0 @@ -//@ check-pass - -pub trait Indexable: std::ops::Index { - fn index2(&self, i: usize) -> &T { - &self[i] - } -} -fn main() {} diff --git a/tests/ui/traits/inheritance/supertrait-operator-issue-18088.rs b/tests/ui/traits/inheritance/supertrait-operator-issue-18088.rs new file mode 100644 index 000000000000..8bd45cd54a44 --- /dev/null +++ b/tests/ui/traits/inheritance/supertrait-operator-issue-18088.rs @@ -0,0 +1,13 @@ +//@ check-pass + +//! Tests that operators from supertrait are available directly on `self` for an inheritor trait. +//! +//! # Context +//! Original issue: https://github.com/rust-lang/rust/issues/18088 + +pub trait Indexable: std::ops::Index { + fn index2(&self, i: usize) -> &T { + &self[i] + } +} +fn main() {} From 22aecd3001038d0ac00ecd06985e2b0abc57e6dc Mon Sep 17 00:00:00 2001 From: omskscream Date: Sun, 14 Sep 2025 01:18:18 +0300 Subject: [PATCH 494/682] clean up issue-21950 (dyn trait cast without assoc type at the cast) --- tests/ui/issues/issue-21950.rs | 12 ------------ ...-as-dyn-trait-wo-assoc-type-issue-21950.rs | 19 +++++++++++++++++++ ...yn-trait-wo-assoc-type-issue-21950.stderr} | 2 +- 3 files changed, 20 insertions(+), 13 deletions(-) delete mode 100644 tests/ui/issues/issue-21950.rs create mode 100644 tests/ui/traits/cast-as-dyn-trait-wo-assoc-type-issue-21950.rs rename tests/ui/{issues/issue-21950.stderr => traits/cast-as-dyn-trait-wo-assoc-type-issue-21950.stderr} (85%) diff --git a/tests/ui/issues/issue-21950.rs b/tests/ui/issues/issue-21950.rs deleted file mode 100644 index 7a85ac91bca0..000000000000 --- a/tests/ui/issues/issue-21950.rs +++ /dev/null @@ -1,12 +0,0 @@ -trait Add { - type Output; -} - -impl Add for i32 { - type Output = i32; -} - -fn main() { - let x = &10 as &dyn Add; - //~^ ERROR E0191 -} diff --git a/tests/ui/traits/cast-as-dyn-trait-wo-assoc-type-issue-21950.rs b/tests/ui/traits/cast-as-dyn-trait-wo-assoc-type-issue-21950.rs new file mode 100644 index 000000000000..3c3815054502 --- /dev/null +++ b/tests/ui/traits/cast-as-dyn-trait-wo-assoc-type-issue-21950.rs @@ -0,0 +1,19 @@ +//! Tests that compiler yields error E0191 when value with existing trait implementation +//! is cast as same `dyn` trait without specifying associated type at the cast. +//! +//! # Context +//! Original issue: https://github.com/rust-lang/rust/issues/21950 + +trait Add { + type Output; +} + +impl Add for i32 { + type Output = i32; +} + +fn main() { + let x = &10 as &dyn Add; //OK + let x = &10 as &dyn Add; + //~^ ERROR E0191 +} diff --git a/tests/ui/issues/issue-21950.stderr b/tests/ui/traits/cast-as-dyn-trait-wo-assoc-type-issue-21950.stderr similarity index 85% rename from tests/ui/issues/issue-21950.stderr rename to tests/ui/traits/cast-as-dyn-trait-wo-assoc-type-issue-21950.stderr index 24230cfe17f3..5f4974e6f237 100644 --- a/tests/ui/issues/issue-21950.stderr +++ b/tests/ui/traits/cast-as-dyn-trait-wo-assoc-type-issue-21950.stderr @@ -1,5 +1,5 @@ error[E0191]: the value of the associated type `Output` in `Add` must be specified - --> $DIR/issue-21950.rs:10:25 + --> $DIR/cast-as-dyn-trait-wo-assoc-type-issue-21950.rs:17:25 | LL | type Output; | ----------- `Output` defined here From 25881bfc3400005b047c8eb553fb7bb2fab25950 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 12 Sep 2025 18:56:52 +0200 Subject: [PATCH 495/682] fix(elidable_lifetime_names): avoid overlapping spans in suggestions --- clippy_lints/src/lifetimes.rs | 93 +++++++++++---- tests/ui/crashes/ice-15666.fixed | 6 + tests/ui/crashes/ice-15666.rs | 6 + tests/ui/crashes/ice-15666.stderr | 16 +++ tests/ui/elidable_lifetime_names.fixed | 82 +++++++++++++ tests/ui/elidable_lifetime_names.rs | 82 +++++++++++++ tests/ui/elidable_lifetime_names.stderr | 146 +++++++++++++++++++++++- 7 files changed, 410 insertions(+), 21 deletions(-) create mode 100644 tests/ui/crashes/ice-15666.fixed create mode 100644 tests/ui/crashes/ice-15666.rs create mode 100644 tests/ui/crashes/ice-15666.stderr diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index aac5d08fd37c..d8b186b6787d 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -856,36 +856,89 @@ fn elision_suggestions( .filter(|param| !param.is_elided_lifetime() && !param.is_impl_trait()) .collect::>(); - let mut suggestions = if elidable_lts.len() == explicit_params.len() { + if !elidable_lts + .iter() + .all(|lt| explicit_params.iter().any(|param| param.def_id == *lt)) + { + return None; + } + + let mut suggestions = if elidable_lts.is_empty() { + vec![] + } else if elidable_lts.len() == explicit_params.len() { // if all the params are elided remove the whole generic block // // fn x<'a>() {} // ^^^^ vec![(generics.span, String::new())] } else { - elidable_lts - .iter() - .map(|&id| { - let pos = explicit_params.iter().position(|param| param.def_id == id)?; - let param = explicit_params.get(pos)?; - - let span = if let Some(next) = explicit_params.get(pos + 1) { - // fn x<'prev, 'a, 'next>() {} - // ^^^^ - param.span.until(next.span) + match &explicit_params[..] { + // no params, nothing to elide + [] => unreachable!("handled by `elidable_lts.is_empty()`"), + [param] => { + if elidable_lts.contains(¶m.def_id) { + unreachable!("handled by `elidable_lts.len() == explicit_params.len()`") } else { - // `pos` should be at least 1 here, because the param in position 0 would either have a `next` - // param or would have taken the `elidable_lts.len() == explicit_params.len()` branch. - let prev = explicit_params.get(pos - 1)?; + unreachable!("handled by `elidable_lts.is_empty()`") + } + }, + [_, _, ..] => { + // Given a list like `<'a, 'b, 'c, 'd, ..>`, + // + // If there is a cluster of elidable lifetimes at the beginning, say `'a` and `'b`, we should + // suggest removing them _and_ the trailing comma. The span for that is `a.span.until(c.span)`: + // <'a, 'b, 'c, 'd, ..> => <'a, 'b, 'c, 'd, ..> + // ^^ ^^ ^^^^^^^^ + // + // And since we know that `'c` isn't elidable--otherwise it would've been in the cluster--we can go + // over all the lifetimes after it, and for each elidable one, add a suggestion spanning the + // lifetime itself and the comma before, because each individual suggestion is guaranteed to leave + // the list valid: + // <.., 'c, 'd, 'e, 'f, 'g, ..> => <.., 'c, 'd, 'e, 'f, 'g, ..> + // ^^ ^^ ^^ ^^^^ ^^^^^^^^ + // + // In case there is no such starting cluster, we only need to do the second part of the algorithm: + // <'a, 'b, 'c, 'd, 'e, 'f, 'g, ..> => <'a, 'b , 'c, 'd, 'e, 'f, 'g, ..> + // ^^ ^^ ^^ ^^ ^^^^^^^^^ ^^^^^^^^ - // fn x<'prev, 'a>() {} - // ^^^^ - param.span.with_lo(prev.span.hi()) + // Split off the starting cluster + // TODO: use `slice::split_once` once stabilized (github.com/rust-lang/rust/issues/112811): + // ``` + // let Some(split) = explicit_params.split_once(|param| !elidable_lts.contains(¶m.def_id)) else { + // // there were no lifetime param that couldn't be elided + // unreachable!("handled by `elidable_lts.len() == explicit_params.len()`") + // }; + // match split { /* .. */ } + // ``` + let Some(split_pos) = explicit_params + .iter() + .position(|param| !elidable_lts.contains(¶m.def_id)) + else { + // there were no lifetime param that couldn't be elided + unreachable!("handled by `elidable_lts.len() == explicit_params.len()`") }; + let split = explicit_params + .split_at_checked(split_pos) + .expect("got `split_pos` from `position` on the same Vec"); - Some((span, String::new())) - }) - .collect::>>()? + match split { + ([..], []) => unreachable!("handled by `elidable_lts.len() == explicit_params.len()`"), + ([], [_]) => unreachable!("handled by `explicit_params.len() == 1`"), + (cluster, rest @ [rest_first, ..]) => { + // the span for the cluster + (cluster.first().map(|fw| fw.span.until(rest_first.span)).into_iter()) + // the span for the remaining lifetimes (calculations independent of the cluster) + .chain( + rest.array_windows() + .filter(|[_, curr]| elidable_lts.contains(&curr.def_id)) + .map(|[prev, curr]| curr.span.with_lo(prev.span.hi())), + ) + .map(|sp| (sp, String::new())) + .collect() + }, + } + }, + } }; suggestions.extend(usages.iter().map(|&usage| { diff --git a/tests/ui/crashes/ice-15666.fixed b/tests/ui/crashes/ice-15666.fixed new file mode 100644 index 000000000000..53b618765c0f --- /dev/null +++ b/tests/ui/crashes/ice-15666.fixed @@ -0,0 +1,6 @@ +#![warn(clippy::elidable_lifetime_names)] + +struct UnitVariantAccess<'a, 'b, 's>(&'a &'b &'s ()); +trait Trait<'de> {} +impl<'de> Trait<'de> for UnitVariantAccess<'_, 'de, '_> {} +//~^ elidable_lifetime_names diff --git a/tests/ui/crashes/ice-15666.rs b/tests/ui/crashes/ice-15666.rs new file mode 100644 index 000000000000..1414b3d2035e --- /dev/null +++ b/tests/ui/crashes/ice-15666.rs @@ -0,0 +1,6 @@ +#![warn(clippy::elidable_lifetime_names)] + +struct UnitVariantAccess<'a, 'b, 's>(&'a &'b &'s ()); +trait Trait<'de> {} +impl<'de, 'a, 's> Trait<'de> for UnitVariantAccess<'a, 'de, 's> {} +//~^ elidable_lifetime_names diff --git a/tests/ui/crashes/ice-15666.stderr b/tests/ui/crashes/ice-15666.stderr new file mode 100644 index 000000000000..b417c09b5c65 --- /dev/null +++ b/tests/ui/crashes/ice-15666.stderr @@ -0,0 +1,16 @@ +error: the following explicit lifetimes could be elided: 'a, 's + --> tests/ui/crashes/ice-15666.rs:5:11 + | +LL | impl<'de, 'a, 's> Trait<'de> for UnitVariantAccess<'a, 'de, 's> {} + | ^^ ^^ ^^ ^^ + | + = note: `-D clippy::elidable-lifetime-names` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::elidable_lifetime_names)]` +help: elide the lifetimes + | +LL - impl<'de, 'a, 's> Trait<'de> for UnitVariantAccess<'a, 'de, 's> {} +LL + impl<'de> Trait<'de> for UnitVariantAccess<'_, 'de, '_> {} + | + +error: aborting due to 1 previous error + diff --git a/tests/ui/elidable_lifetime_names.fixed b/tests/ui/elidable_lifetime_names.fixed index abeee5c4cef3..a6c4cb7a36a8 100644 --- a/tests/ui/elidable_lifetime_names.fixed +++ b/tests/ui/elidable_lifetime_names.fixed @@ -192,3 +192,85 @@ mod issue13923 { x.b } } + +fn issue15666_original() { + struct UnitVariantAccess<'a, 'b, 's>(&'a &'b &'s ()); + + trait Trait<'de> {} + + //~v elidable_lifetime_names + impl<'de> Trait<'de> for UnitVariantAccess<'_, 'de, '_> {} + // ^^ ^^ ^^ ^^ +} + +#[allow(clippy::upper_case_acronyms)] +fn issue15666() { + struct S1<'a>(&'a ()); + struct S2<'a, 'b>(&'a &'b ()); + struct S3<'a, 'b, 'c>(&'a &'b &'c ()); + + trait T {} + trait TA<'a> {} + trait TB<'b> {} + trait TC<'c> {} + trait TAB<'a, 'b> {} + trait TAC<'a, 'c> {} + trait TBC<'b, 'c> {} + trait TABC<'a, 'b, 'c> {} + + // 1 lifetime + + impl<'a> TA<'a> for S1<'a> {} + + //~v elidable_lifetime_names + impl T for S1<'_> {} + // ^^ + + // 2 lifetimes + + impl<'a, 'b> TAB<'a, 'b> for S2<'a, 'b> {} + + //~v elidable_lifetime_names + impl<'a> TA<'a> for S2<'a, '_> {} + // ^^ + + //~v elidable_lifetime_names + impl<'b> TB<'b> for S2<'_, 'b> {} + // ^^ + + //~v elidable_lifetime_names + impl T for S2<'_, '_> {} + // ^^ ^^ + + // 3 lifetimes + + impl<'a, 'b, 'c> TABC<'a, 'b, 'c> for S3<'a, 'b, 'c> {} + + //~v elidable_lifetime_names + impl<'a, 'b> TAB<'a, 'b> for S3<'a, 'b, '_> {} + // ^^ + + //~v elidable_lifetime_names + impl<'a, 'c> TAC<'a, 'c> for S3<'a, '_, 'c> {} + // ^^ + + //~v elidable_lifetime_names + impl<'a> TA<'a> for S3<'a, '_, '_> {} + // ^^ ^^ + + //~v elidable_lifetime_names + impl<'b, 'c> TBC<'b, 'c> for S3<'_, 'b, 'c> {} + // ^^ + + //~v elidable_lifetime_names + impl<'b> TB<'b> for S3<'_, 'b, '_> {} + // ^^ ^^ + + //~v elidable_lifetime_names + impl<'c> TC<'c> for S3<'_, '_, 'c> {} + // ^^ ^^ + + //~v elidable_lifetime_names + impl T for S3<'_, '_, '_> {} + // ^^ ^^ ^^ +} diff --git a/tests/ui/elidable_lifetime_names.rs b/tests/ui/elidable_lifetime_names.rs index fae3577a8e96..e08056b2fb56 100644 --- a/tests/ui/elidable_lifetime_names.rs +++ b/tests/ui/elidable_lifetime_names.rs @@ -192,3 +192,85 @@ mod issue13923 { x.b } } + +fn issue15666_original() { + struct UnitVariantAccess<'a, 'b, 's>(&'a &'b &'s ()); + + trait Trait<'de> {} + + //~v elidable_lifetime_names + impl<'de, 'a, 's> Trait<'de> for UnitVariantAccess<'a, 'de, 's> {} + // ^^ ^^ ^^ ^^ +} + +#[allow(clippy::upper_case_acronyms)] +fn issue15666() { + struct S1<'a>(&'a ()); + struct S2<'a, 'b>(&'a &'b ()); + struct S3<'a, 'b, 'c>(&'a &'b &'c ()); + + trait T {} + trait TA<'a> {} + trait TB<'b> {} + trait TC<'c> {} + trait TAB<'a, 'b> {} + trait TAC<'a, 'c> {} + trait TBC<'b, 'c> {} + trait TABC<'a, 'b, 'c> {} + + // 1 lifetime + + impl<'a> TA<'a> for S1<'a> {} + + //~v elidable_lifetime_names + impl<'a> T for S1<'a> {} + // ^^ + + // 2 lifetimes + + impl<'a, 'b> TAB<'a, 'b> for S2<'a, 'b> {} + + //~v elidable_lifetime_names + impl<'a, 'b> TA<'a> for S2<'a, 'b> {} + // ^^ + + //~v elidable_lifetime_names + impl<'a, 'b> TB<'b> for S2<'a, 'b> {} + // ^^ + + //~v elidable_lifetime_names + impl<'a, 'b> T for S2<'a, 'b> {} + // ^^ ^^ + + // 3 lifetimes + + impl<'a, 'b, 'c> TABC<'a, 'b, 'c> for S3<'a, 'b, 'c> {} + + //~v elidable_lifetime_names + impl<'a, 'b, 'c> TAB<'a, 'b> for S3<'a, 'b, 'c> {} + // ^^ + + //~v elidable_lifetime_names + impl<'a, 'b, 'c> TAC<'a, 'c> for S3<'a, 'b, 'c> {} + // ^^ + + //~v elidable_lifetime_names + impl<'a, 'b, 'c> TA<'a> for S3<'a, 'b, 'c> {} + // ^^ ^^ + + //~v elidable_lifetime_names + impl<'a, 'b, 'c> TBC<'b, 'c> for S3<'a, 'b, 'c> {} + // ^^ + + //~v elidable_lifetime_names + impl<'a, 'b, 'c> TB<'b> for S3<'a, 'b, 'c> {} + // ^^ ^^ + + //~v elidable_lifetime_names + impl<'a, 'b, 'c> TC<'c> for S3<'a, 'b, 'c> {} + // ^^ ^^ + + //~v elidable_lifetime_names + impl<'a, 'b, 'c> T for S3<'a, 'b, 'c> {} + // ^^ ^^ ^^ +} diff --git a/tests/ui/elidable_lifetime_names.stderr b/tests/ui/elidable_lifetime_names.stderr index a60dfc697564..03fe383b8f67 100644 --- a/tests/ui/elidable_lifetime_names.stderr +++ b/tests/ui/elidable_lifetime_names.stderr @@ -158,5 +158,149 @@ LL | o: &'t str, LL ~ ) -> Content<'t, '_> { | -error: aborting due to 12 previous errors +error: the following explicit lifetimes could be elided: 'a, 's + --> tests/ui/elidable_lifetime_names.rs:202:15 + | +LL | impl<'de, 'a, 's> Trait<'de> for UnitVariantAccess<'a, 'de, 's> {} + | ^^ ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'de, 'a, 's> Trait<'de> for UnitVariantAccess<'a, 'de, 's> {} +LL + impl<'de> Trait<'de> for UnitVariantAccess<'_, 'de, '_> {} + | + +error: the following explicit lifetimes could be elided: 'a + --> tests/ui/elidable_lifetime_names.rs:226:10 + | +LL | impl<'a> T for S1<'a> {} + | ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a> T for S1<'a> {} +LL + impl T for S1<'_> {} + | + +error: the following explicit lifetimes could be elided: 'b + --> tests/ui/elidable_lifetime_names.rs:234:14 + | +LL | impl<'a, 'b> TA<'a> for S2<'a, 'b> {} + | ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b> TA<'a> for S2<'a, 'b> {} +LL + impl<'a> TA<'a> for S2<'a, '_> {} + | + +error: the following explicit lifetimes could be elided: 'a + --> tests/ui/elidable_lifetime_names.rs:238:10 + | +LL | impl<'a, 'b> TB<'b> for S2<'a, 'b> {} + | ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b> TB<'b> for S2<'a, 'b> {} +LL + impl<'b> TB<'b> for S2<'_, 'b> {} + | + +error: the following explicit lifetimes could be elided: 'a, 'b + --> tests/ui/elidable_lifetime_names.rs:242:10 + | +LL | impl<'a, 'b> T for S2<'a, 'b> {} + | ^^ ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b> T for S2<'a, 'b> {} +LL + impl T for S2<'_, '_> {} + | + +error: the following explicit lifetimes could be elided: 'c + --> tests/ui/elidable_lifetime_names.rs:250:18 + | +LL | impl<'a, 'b, 'c> TAB<'a, 'b> for S3<'a, 'b, 'c> {} + | ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b, 'c> TAB<'a, 'b> for S3<'a, 'b, 'c> {} +LL + impl<'a, 'b> TAB<'a, 'b> for S3<'a, 'b, '_> {} + | + +error: the following explicit lifetimes could be elided: 'b + --> tests/ui/elidable_lifetime_names.rs:254:14 + | +LL | impl<'a, 'b, 'c> TAC<'a, 'c> for S3<'a, 'b, 'c> {} + | ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b, 'c> TAC<'a, 'c> for S3<'a, 'b, 'c> {} +LL + impl<'a, 'c> TAC<'a, 'c> for S3<'a, '_, 'c> {} + | + +error: the following explicit lifetimes could be elided: 'b, 'c + --> tests/ui/elidable_lifetime_names.rs:258:14 + | +LL | impl<'a, 'b, 'c> TA<'a> for S3<'a, 'b, 'c> {} + | ^^ ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b, 'c> TA<'a> for S3<'a, 'b, 'c> {} +LL + impl<'a> TA<'a> for S3<'a, '_, '_> {} + | + +error: the following explicit lifetimes could be elided: 'a + --> tests/ui/elidable_lifetime_names.rs:262:10 + | +LL | impl<'a, 'b, 'c> TBC<'b, 'c> for S3<'a, 'b, 'c> {} + | ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b, 'c> TBC<'b, 'c> for S3<'a, 'b, 'c> {} +LL + impl<'b, 'c> TBC<'b, 'c> for S3<'_, 'b, 'c> {} + | + +error: the following explicit lifetimes could be elided: 'a, 'c + --> tests/ui/elidable_lifetime_names.rs:266:10 + | +LL | impl<'a, 'b, 'c> TB<'b> for S3<'a, 'b, 'c> {} + | ^^ ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b, 'c> TB<'b> for S3<'a, 'b, 'c> {} +LL + impl<'b> TB<'b> for S3<'_, 'b, '_> {} + | + +error: the following explicit lifetimes could be elided: 'a, 'b + --> tests/ui/elidable_lifetime_names.rs:270:10 + | +LL | impl<'a, 'b, 'c> TC<'c> for S3<'a, 'b, 'c> {} + | ^^ ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b, 'c> TC<'c> for S3<'a, 'b, 'c> {} +LL + impl<'c> TC<'c> for S3<'_, '_, 'c> {} + | + +error: the following explicit lifetimes could be elided: 'a, 'b, 'c + --> tests/ui/elidable_lifetime_names.rs:274:10 + | +LL | impl<'a, 'b, 'c> T for S3<'a, 'b, 'c> {} + | ^^ ^^ ^^ ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - impl<'a, 'b, 'c> T for S3<'a, 'b, 'c> {} +LL + impl T for S3<'_, '_, '_> {} + | + +error: aborting due to 24 previous errors From 7791178805c1bccd2696c123ba5e5e4c896cf7d6 Mon Sep 17 00:00:00 2001 From: Lucas Baumann Date: Sun, 14 Sep 2025 12:11:58 +0200 Subject: [PATCH 496/682] fix 404 link --- src/doc/rustc/src/target-tier-policy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc/src/target-tier-policy.md b/src/doc/rustc/src/target-tier-policy.md index 28d3dc32a63e..f6b78eed24fc 100644 --- a/src/doc/rustc/src/target-tier-policy.md +++ b/src/doc/rustc/src/target-tier-policy.md @@ -701,4 +701,4 @@ RFC process, with approval by the compiler and infra teams. Any such proposal will be communicated widely to the Rust community, both when initially proposed and before being dropped from a stable release. -[MCP]: https://forge.rust-lang.org/compiler/mcp.html +[MCP]: https://forge.rust-lang.org/compiler/proposals-and-stabilization.html#how-do-i-submit-an-mcp From 2e816736efaebf1f4666efac1817bcccd78a3e52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Tue, 26 Aug 2025 14:24:22 +0200 Subject: [PATCH 497/682] Move more early buffered lints to dyn lint diagnostics (1/N) --- compiler/rustc_builtin_macros/messages.ftl | 11 ++++ compiler/rustc_builtin_macros/src/asm.rs | 5 +- compiler/rustc_builtin_macros/src/errors.rs | 22 ++++++- .../rustc_builtin_macros/src/source_util.rs | 3 +- .../rustc_builtin_macros/src/test_harness.rs | 3 +- compiler/rustc_builtin_macros/src/util.rs | 3 +- compiler/rustc_expand/messages.ftl | 14 ++++- compiler/rustc_expand/src/config.rs | 3 +- compiler/rustc_expand/src/errors.rs | 42 ++++++++++++- compiler/rustc_expand/src/mbe/macro_check.rs | 45 +++++++++----- compiler/rustc_expand/src/mbe/transcribe.rs | 6 +- compiler/rustc_lint/messages.ftl | 31 ---------- compiler/rustc_lint/src/early/diagnostics.rs | 41 ------------ compiler/rustc_lint/src/lints.rs | 62 +------------------ compiler/rustc_lint_defs/src/lib.rs | 18 +----- compiler/rustc_resolve/messages.ftl | 6 ++ compiler/rustc_resolve/src/diagnostics.rs | 2 +- compiler/rustc_resolve/src/errors.rs | 18 +++++- compiler/rustc_resolve/src/ident.rs | 3 +- .../issue-61053-duplicate-binder.stderr | 4 +- 20 files changed, 150 insertions(+), 192 deletions(-) diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 358c0d3db460..7c1a5f44e165 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -64,6 +64,11 @@ builtin_macros_autodiff_ty_activity = {$act} can not be used for this type builtin_macros_autodiff_unknown_activity = did not recognize Activity: `{$act}` builtin_macros_autodiff_width = autodiff width must fit u32, but is {$width} + +builtin_macros_avoid_att_syntax = avoid using `.att_syntax`, prefer using `options(att_syntax)` instead + +builtin_macros_avoid_intel_syntax = avoid using `.intel_syntax`, Intel syntax is the default + builtin_macros_bad_derive_target = `derive` may only be applied to `struct`s, `enum`s and `union`s .label = not applicable here .label2 = not a `struct`, `enum` or `union` @@ -138,6 +143,8 @@ builtin_macros_derive_path_args_list = traits in `#[derive(...)]` don't accept a builtin_macros_derive_path_args_value = traits in `#[derive(...)]` don't accept values .suggestion = remove the value +builtin_macros_duplicate_macro_attribute = duplicated attribute + builtin_macros_env_not_defined = environment variable `{$var}` not defined at compile time .cargo = Cargo sets build script variables at run time. Use `std::env::var({$var_expr})` instead .custom = use `std::env::var({$var_expr})` to read the variable at run time @@ -231,6 +238,8 @@ builtin_macros_derive_from_wrong_field_count = `#[derive(From)]` used on a struc builtin_macros_derive_from_usage_note = `#[derive(From)]` can only be used on structs with exactly one field +builtin_macros_incomplete_include = include macro expected single expression in source + builtin_macros_multiple_default_attrs = multiple `#[default]` attributes .note = only one `#[default]` attribute is needed .label = `#[default]` used here @@ -294,3 +303,5 @@ builtin_macros_unexpected_lit = expected path to a trait, found literal .label = not a trait .str_lit = try using `#[derive({$sym})]` .other = for example, write `#[derive(Debug)]` for `Debug` + +builtin_macros_unnameable_test_items = cannot test inner items diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 86b8e1ff8dbb..ae62b5ea2a09 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -1,4 +1,3 @@ -use lint::BuiltinLintDiag; use rustc_ast::tokenstream::TokenStream; use rustc_ast::{AsmMacro, token}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; @@ -352,7 +351,7 @@ fn expand_preparsed_asm( lint::builtin::BAD_ASM_STYLE, find_span(".intel_syntax"), ecx.current_expansion.lint_node_id, - BuiltinLintDiag::AvoidUsingIntelSyntax, + errors::AvoidIntelSyntax, ); } if template_str.contains(".att_syntax") { @@ -360,7 +359,7 @@ fn expand_preparsed_asm( lint::builtin::BAD_ASM_STYLE, find_span(".att_syntax"), ecx.current_expansion.lint_node_id, - BuiltinLintDiag::AvoidUsingAttSyntax, + errors::AvoidAttSyntax, ); } } diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index 54e8f7503377..0993fdc5be45 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -3,9 +3,29 @@ use rustc_errors::{ Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, MultiSpan, SingleLabelManySpans, Subdiagnostic, }; -use rustc_macros::{Diagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_span::{Ident, Span, Symbol}; +#[derive(LintDiagnostic)] +#[diag(builtin_macros_avoid_intel_syntax)] +pub(crate) struct AvoidIntelSyntax; + +#[derive(LintDiagnostic)] +#[diag(builtin_macros_avoid_att_syntax)] +pub(crate) struct AvoidAttSyntax; + +#[derive(LintDiagnostic)] +#[diag(builtin_macros_incomplete_include)] +pub(crate) struct IncompleteInclude; + +#[derive(LintDiagnostic)] +#[diag(builtin_macros_unnameable_test_items)] +pub(crate) struct UnnameableTestItems; + +#[derive(LintDiagnostic)] +#[diag(builtin_macros_duplicate_macro_attribute)] +pub(crate) struct DuplicateMacroAttribute; + #[derive(Diagnostic)] #[diag(builtin_macros_requires_cfg_pattern)] pub(crate) struct RequiresCfgPattern { diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index 11b868f81a97..16adaab15c52 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -12,7 +12,6 @@ use rustc_expand::base::{ DummyResult, ExpandResult, ExtCtxt, MacEager, MacResult, MacroExpanderResult, resolve_path, }; use rustc_expand::module::DirOwnership; -use rustc_lint_defs::BuiltinLintDiag; use rustc_parse::lexer::StripTokens; use rustc_parse::parser::ForceCollect; use rustc_parse::{new_parser_from_file, unwrap_or_emit_fatal, utf8_error}; @@ -159,7 +158,7 @@ pub(crate) fn expand_include<'cx>( INCOMPLETE_INCLUDE, p.token.span, self.node_id, - BuiltinLintDiag::IncompleteInclude, + errors::IncompleteInclude, ); } Some(expr) diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index a9d91f77560a..51089e5a1d3f 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -11,7 +11,6 @@ use rustc_errors::DiagCtxtHandle; use rustc_expand::base::{ExtCtxt, ResolverExpand}; use rustc_expand::expand::{AstFragment, ExpansionConfig}; use rustc_feature::Features; -use rustc_lint_defs::BuiltinLintDiag; use rustc_session::Session; use rustc_session::lint::builtin::UNNAMEABLE_TEST_ITEMS; use rustc_span::hygiene::{AstPass, SyntaxContext, Transparency}; @@ -165,7 +164,7 @@ impl<'a> Visitor<'a> for InnerItemLinter<'_> { UNNAMEABLE_TEST_ITEMS, attr.span, i.id, - BuiltinLintDiag::UnnameableTestItems, + errors::UnnameableTestItems, ); } } diff --git a/compiler/rustc_builtin_macros/src/util.rs b/compiler/rustc_builtin_macros/src/util.rs index 3a4585d5be9d..e26f31dce67b 100644 --- a/compiler/rustc_builtin_macros/src/util.rs +++ b/compiler/rustc_builtin_macros/src/util.rs @@ -5,7 +5,6 @@ use rustc_errors::{Applicability, Diag, ErrorGuaranteed}; use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt}; use rustc_expand::expand::AstFragment; use rustc_feature::AttributeTemplate; -use rustc_lint_defs::BuiltinLintDiag; use rustc_lint_defs::builtin::DUPLICATE_MACRO_ATTRIBUTES; use rustc_parse::{exp, parser}; use rustc_session::errors::report_lit_error; @@ -49,7 +48,7 @@ pub(crate) fn warn_on_duplicate_attribute(ecx: &ExtCtxt<'_>, item: &Annotatable, DUPLICATE_MACRO_ATTRIBUTES, attr.span, ecx.current_expansion.lint_node_id, - BuiltinLintDiag::DuplicateMacroAttribute, + errors::DuplicateMacroAttribute, ); } } diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl index 61ba716d082b..fc50ac6e3bfe 100644 --- a/compiler/rustc_expand/messages.ftl +++ b/compiler/rustc_expand/messages.ftl @@ -3,6 +3,8 @@ expand_attributes_on_expressions_experimental = .help_outer_doc = `///` is used for outer documentation comments; for a plain comment, use `//` .help_inner_doc = `//!` is used for inner documentation comments; for a plain comment, use `//` by removing the `!` or inserting a space in between them: `// !` +expand_cfg_attr_no_attributes = `#[cfg_attr]` does not expand to any attributes + expand_collapse_debuginfo_illegal = illegal value for attribute #[collapse_debuginfo(no|external|yes)] @@ -89,6 +91,13 @@ expand_malformed_feature_attribute = malformed `feature` attribute input .expected = expected just one word +expand_metavar_still_repeating = variable `{$ident}` is still repeating at this depth + .label = expected repetition + +expand_metavariable_wrong_operator = meta-variable repeats with different Kleene operator + .binder_label = expected repetition + .occurrence_label = conflicting repetition + expand_meta_var_dif_seq_matchers = {$msg} expand_missing_fragment_specifier = missing fragment specifier @@ -176,11 +185,10 @@ expand_resolve_relative_path = expand_trace_macro = trace_macro +expand_unknown_macro_variable = unknown macro variable `{$name}` + expand_unsupported_key_value = key-value macro attributes are not supported -expand_var_still_repeating = - variable `{$ident}` is still repeating at this depth - expand_wrong_fragment_kind = non-{$kind} macro in {$kind} position: {$name} diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 15419ab7423f..2925e337071c 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -21,7 +21,6 @@ use rustc_feature::{ ACCEPTED_LANG_FEATURES, AttributeSafety, EnabledLangFeature, EnabledLibFeature, Features, REMOVED_LANG_FEATURES, UNSTABLE_LANG_FEATURES, }; -use rustc_lint_defs::BuiltinLintDiag; use rustc_session::Session; use rustc_session::parse::feature_err; use rustc_span::{STDLIB_STABLE_CRATES, Span, Symbol, sym}; @@ -315,7 +314,7 @@ impl<'a> StripUnconfigured<'a> { rustc_lint_defs::builtin::UNUSED_ATTRIBUTES, cfg_attr.span, ast::CRATE_NODE_ID, - BuiltinLintDiag::CfgAttrNoAttributes, + crate::errors::CfgAttrNoAttributes, ); } diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index 6eb5cd65846e..a939dadaf2fc 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -3,9 +3,13 @@ use std::borrow::Cow; use rustc_ast::ast; use rustc_errors::codes::*; use rustc_hir::limit::Limit; -use rustc_macros::{Diagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol}; +#[derive(LintDiagnostic)] +#[diag(expand_cfg_attr_no_attributes)] +pub(crate) struct CfgAttrNoAttributes; + #[derive(Diagnostic)] #[diag(expand_expr_repeat_no_syntax_vars)] pub(crate) struct NoSyntaxVarsExprRepeat { @@ -28,13 +32,30 @@ pub(crate) struct CountRepetitionMisplaced { } #[derive(Diagnostic)] -#[diag(expand_var_still_repeating)] -pub(crate) struct VarStillRepeating { +#[diag(expand_metavar_still_repeating)] +pub(crate) struct MacroVarStillRepeating { #[primary_span] pub span: Span, pub ident: MacroRulesNormalizedIdent, } +#[derive(LintDiagnostic)] +#[diag(expand_metavar_still_repeating)] +pub(crate) struct MetaVarStillRepeatingLint { + #[label] + pub label: Span, + pub ident: MacroRulesNormalizedIdent, +} + +#[derive(LintDiagnostic)] +#[diag(expand_metavariable_wrong_operator)] +pub(crate) struct MetaVariableWrongOperator { + #[label(expand_binder_label)] + pub binder: Span, + #[label(expand_occurrence_label)] + pub occurrence: Span, +} + #[derive(Diagnostic)] #[diag(expand_meta_var_dif_seq_matchers)] pub(crate) struct MetaVarsDifSeqMatchers { @@ -43,6 +64,12 @@ pub(crate) struct MetaVarsDifSeqMatchers { pub msg: String, } +#[derive(LintDiagnostic)] +#[diag(expand_unknown_macro_variable)] +pub(crate) struct UnknownMacroVariable { + pub name: MacroRulesNormalizedIdent, +} + #[derive(Diagnostic)] #[diag(expand_resolve_relative_path)] pub(crate) struct ResolveRelativePath { @@ -345,6 +372,15 @@ pub(crate) struct DuplicateMatcherBinding { pub prev: Span, } +#[derive(LintDiagnostic)] +#[diag(expand_duplicate_matcher_binding)] +pub(crate) struct DuplicateMatcherBindingLint { + #[label] + pub span: Span, + #[label(expand_label2)] + pub prev: Span, +} + #[derive(Diagnostic)] #[diag(expand_missing_fragment_specifier)] #[note] diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs index faeae1f494e6..ebd6e887f7d2 100644 --- a/compiler/rustc_expand/src/mbe/macro_check.rs +++ b/compiler/rustc_expand/src/mbe/macro_check.rs @@ -108,8 +108,7 @@ use rustc_ast::token::{Delimiter, IdentIsRaw, Token, TokenKind}; use rustc_ast::{DUMMY_NODE_ID, NodeId}; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::MultiSpan; -use rustc_lint_defs::BuiltinLintDiag; +use rustc_errors::DecorateDiagCompat; use rustc_session::lint::builtin::META_VARIABLE_MISUSE; use rustc_session::parse::ParseSess; use rustc_span::{ErrorGuaranteed, MacroRulesNormalizedIdent, Span, kw}; @@ -245,9 +244,12 @@ fn check_binders( // There are 3 possibilities: if let Some(prev_info) = binders.get(&name) { // 1. The meta-variable is already bound in the current LHS: This is an error. - let mut span = MultiSpan::from_span(span); - span.push_span_label(prev_info.span, "previous declaration"); - buffer_lint(psess, span, node_id, BuiltinLintDiag::DuplicateMatcherBinding); + buffer_lint( + psess, + span, + node_id, + errors::DuplicateMatcherBindingLint { span, prev: prev_info.span }, + ); } else if get_binder_info(macros, binders, name).is_none() { // 2. The meta-variable is free: This is a binder. binders.insert(name, BinderInfo { span, ops: ops.into() }); @@ -579,7 +581,7 @@ fn check_ops_is_prefix( return; } } - buffer_lint(psess, span.into(), node_id, BuiltinLintDiag::UnknownMacroVariable(name)); + buffer_lint(psess, span, node_id, errors::UnknownMacroVariable { name }); } /// Returns whether `binder_ops` is a prefix of `occurrence_ops`. @@ -604,29 +606,42 @@ fn ops_is_prefix( psess: &ParseSess, node_id: NodeId, span: Span, - name: MacroRulesNormalizedIdent, + ident: MacroRulesNormalizedIdent, binder_ops: &[KleeneToken], occurrence_ops: &[KleeneToken], ) { for (i, binder) in binder_ops.iter().enumerate() { if i >= occurrence_ops.len() { - let mut span = MultiSpan::from_span(span); - span.push_span_label(binder.span, "expected repetition"); - buffer_lint(psess, span, node_id, BuiltinLintDiag::MetaVariableStillRepeating(name)); + buffer_lint( + psess, + span, + node_id, + errors::MetaVarStillRepeatingLint { label: binder.span, ident }, + ); return; } let occurrence = &occurrence_ops[i]; if occurrence.op != binder.op { - let mut span = MultiSpan::from_span(span); - span.push_span_label(binder.span, "expected repetition"); - span.push_span_label(occurrence.span, "conflicting repetition"); - buffer_lint(psess, span, node_id, BuiltinLintDiag::MetaVariableWrongOperator); + buffer_lint( + psess, + span, + node_id, + errors::MetaVariableWrongOperator { + binder: binder.span, + occurrence: occurrence.span, + }, + ); return; } } } -fn buffer_lint(psess: &ParseSess, span: MultiSpan, node_id: NodeId, diag: BuiltinLintDiag) { +fn buffer_lint( + psess: &ParseSess, + span: Span, + node_id: NodeId, + diag: impl Into, +) { // Macros loaded from other crates have dummy node ids. if node_id != DUMMY_NODE_ID { psess.buffer_lint(META_VARIABLE_MISUSE, span, node_id, diag); diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index ed8aa71d59dd..6a3f1f62c91e 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -17,8 +17,8 @@ use rustc_span::{ use smallvec::{SmallVec, smallvec}; use crate::errors::{ - CountRepetitionMisplaced, MetaVarsDifSeqMatchers, MustRepeatOnce, MveUnrecognizedVar, - NoSyntaxVarsExprRepeat, VarStillRepeating, + CountRepetitionMisplaced, MacroVarStillRepeating, MetaVarsDifSeqMatchers, MustRepeatOnce, + MveUnrecognizedVar, NoSyntaxVarsExprRepeat, }; use crate::mbe::macro_parser::NamedMatch; use crate::mbe::macro_parser::NamedMatch::*; @@ -483,7 +483,7 @@ fn transcribe_metavar<'tx>( } MatchedSeq(..) => { // We were unable to descend far enough. This is an error. - return Err(dcx.create_err(VarStillRepeating { span: sp, ident })); + return Err(dcx.create_err(MacroVarStillRepeating { span: sp, ident })); } }; diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 417e5a97069c..5a60a4f48d9d 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -40,12 +40,6 @@ lint_atomic_ordering_load = atomic loads cannot have `Release` or `AcqRel` order lint_atomic_ordering_store = atomic stores cannot have `Acquire` or `AcqRel` ordering .help = consider using ordering modes `Release`, `SeqCst` or `Relaxed` -lint_avoid_att_syntax = - avoid using `.att_syntax`, prefer using `options(att_syntax)` instead - -lint_avoid_intel_syntax = - avoid using `.intel_syntax`, Intel syntax is the default - lint_bad_attribute_argument = bad attribute argument lint_bad_opt_access = {$msg} @@ -190,9 +184,6 @@ lint_builtin_while_true = denote infinite loops with `loop {"{"} ... {"}"}` lint_byte_slice_in_packed_struct_with_derive = {$ty} slice in a packed struct that derives a built-in trait .help = consider implementing the trait by hand, or remove the `packed` attribute -lint_cfg_attr_no_attributes = - `#[cfg_attr]` does not expand to any attributes - lint_check_name_unknown_tool = unknown lint tool: `{$tool_name}` lint_closure_returning_async_block = closure returning async block can be made into an async closure @@ -249,11 +240,6 @@ lint_dropping_copy_types = calls to `std::mem::drop` with a value that implement lint_dropping_references = calls to `std::mem::drop` with a reference instead of an owned value does nothing .label = argument has type `{$arg_ty}` -lint_duplicate_macro_attribute = - duplicated attribute - -lint_duplicate_matcher_binding = duplicate matcher binding - 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 @@ -409,9 +395,6 @@ lint_improper_ctypes_union_layout_help = consider adding a `#[repr(C)]` or `#[re lint_improper_ctypes_union_layout_reason = this union has unspecified layout lint_improper_ctypes_union_non_exhaustive = this union is non-exhaustive -lint_incomplete_include = - include macro expected single expression in source - lint_invalid_asm_label_binary = avoid using labels containing only the digits `0` and `1` in inline assembly .label = use a different label that doesn't start with `0` or `1` .help = start numbering with `2` instead @@ -476,9 +459,6 @@ lint_legacy_derive_helpers = derive helper attribute is used before it is introd lint_lintpass_by_hand = implementing `LintPass` by hand .help = try using `declare_lint_pass!` or `impl_lint_pass!` instead -lint_macro_expanded_macro_exports_accessed_by_absolute_paths = macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths - .note = the macro is defined here - lint_macro_expr_fragment_specifier_2024_migration = the `expr` fragment specifier will accept more expressions in the 2024 edition .suggestion = to keep the existing behavior, use the `expr_2021` fragment specifier @@ -499,10 +479,6 @@ lint_map_unit_fn = `Iterator::map` call that discard the iterator's values .map_label = after this call to map, the resulting iterator is `impl Iterator`, which means the only information carried by the iterator is the number of items .suggestion = you might have meant to use `Iterator::for_each` -lint_metavariable_still_repeating = variable `{$name}` is still repeating at this depth - -lint_metavariable_wrong_operator = meta-variable repeats with different Kleene operator - lint_mismatched_lifetime_syntaxes_eliding_while_named = eliding a lifetime that's named elsewhere is confusing @@ -719,9 +695,6 @@ lint_pattern_in_foreign = patterns aren't allowed in foreign function declaratio lint_private_extern_crate_reexport = extern crate `{$ident}` is private and cannot be re-exported .suggestion = consider making the `extern crate` item publicly accessible -lint_proc_macro_derive_resolution_fallback = cannot find {$ns_descr} `{$ident}` in this scope - .label = names from parent modules are not accessible without an explicit import - lint_query_instability = using `{$query}` can result in unstable query results .note = if you believe this case to be fine, allow this lint and add a comment explaining your rationale @@ -935,13 +908,9 @@ lint_unknown_lint = *[false] did you mean: `{$replace}` } -lint_unknown_macro_variable = unknown macro variable `{$name}` - lint_unknown_tool_in_scoped_lint = unknown tool name `{$tool_name}` found in scoped lint: `{$tool_name}::{$lint_name}` .help = add `#![register_tool({$tool_name})]` to the crate root -lint_unnameable_test_items = cannot test inner items - lint_unnecessary_qualification = unnecessary qualification .suggestion = remove the unnecessary path segments diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 7300490b838b..4463586515ec 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -64,17 +64,6 @@ pub fn decorate_builtin_lint( } .decorate_lint(diag); } - BuiltinLintDiag::ProcMacroDeriveResolutionFallback { - span: macro_span, - ns_descr, - ident, - } => lints::ProcMacroDeriveResolutionFallback { span: macro_span, ns_descr, ident } - .decorate_lint(diag), - BuiltinLintDiag::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def) => { - lints::MacroExpandedMacroExportsAccessedByAbsolutePaths { definition: span_def } - .decorate_lint(diag) - } - BuiltinLintDiag::ElidedLifetimesInPaths(n, path_span, incl_angl_brckt, insertion_span) => { lints::ElidedLifetimesInPaths { subdiag: elided_lifetime_in_path_suggestion( @@ -398,36 +387,6 @@ pub fn decorate_builtin_lint( BuiltinLintDiag::UnstableFeature(msg) => { lints::UnstableFeature { msg }.decorate_lint(diag); } - BuiltinLintDiag::AvoidUsingIntelSyntax => { - lints::AvoidIntelSyntax.decorate_lint(diag); - } - BuiltinLintDiag::AvoidUsingAttSyntax => { - lints::AvoidAttSyntax.decorate_lint(diag); - } - BuiltinLintDiag::IncompleteInclude => { - lints::IncompleteInclude.decorate_lint(diag); - } - BuiltinLintDiag::UnnameableTestItems => { - lints::UnnameableTestItems.decorate_lint(diag); - } - BuiltinLintDiag::DuplicateMacroAttribute => { - lints::DuplicateMacroAttribute.decorate_lint(diag); - } - BuiltinLintDiag::CfgAttrNoAttributes => { - lints::CfgAttrNoAttributes.decorate_lint(diag); - } - BuiltinLintDiag::MetaVariableStillRepeating(name) => { - lints::MetaVariableStillRepeating { name }.decorate_lint(diag); - } - BuiltinLintDiag::MetaVariableWrongOperator => { - lints::MetaVariableWrongOperator.decorate_lint(diag); - } - BuiltinLintDiag::DuplicateMatcherBinding => { - lints::DuplicateMatcherBinding.decorate_lint(diag); - } - BuiltinLintDiag::UnknownMacroVariable(name) => { - lints::UnknownMacroVariable { name }.decorate_lint(diag); - } BuiltinLintDiag::UnusedCrateDependency { extern_crate, local_crate } => { lints::UnusedCrateDependency { extern_crate, local_crate }.decorate_lint(diag) } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 56d65ed08f9e..1c13fadeef96 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -17,7 +17,7 @@ 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, sym}; +use rustc_span::{Ident, Span, Symbol, sym}; use crate::builtin::{InitError, ShorthandAssocTyCollector, TypeAliasBounds}; use crate::errors::{OverruledAttributeSub, RequestedLevel}; @@ -2585,50 +2585,6 @@ impl<'a> LintDiagnostic<'a, ()> for UnstableFeature { } } -#[derive(LintDiagnostic)] -#[diag(lint_avoid_intel_syntax)] -pub(crate) struct AvoidIntelSyntax; - -#[derive(LintDiagnostic)] -#[diag(lint_avoid_att_syntax)] -pub(crate) struct AvoidAttSyntax; - -#[derive(LintDiagnostic)] -#[diag(lint_incomplete_include)] -pub(crate) struct IncompleteInclude; - -#[derive(LintDiagnostic)] -#[diag(lint_unnameable_test_items)] -pub(crate) struct UnnameableTestItems; - -#[derive(LintDiagnostic)] -#[diag(lint_duplicate_macro_attribute)] -pub(crate) struct DuplicateMacroAttribute; - -#[derive(LintDiagnostic)] -#[diag(lint_cfg_attr_no_attributes)] -pub(crate) struct CfgAttrNoAttributes; - -#[derive(LintDiagnostic)] -#[diag(lint_metavariable_still_repeating)] -pub(crate) struct MetaVariableStillRepeating { - pub name: MacroRulesNormalizedIdent, -} - -#[derive(LintDiagnostic)] -#[diag(lint_metavariable_wrong_operator)] -pub(crate) struct MetaVariableWrongOperator; - -#[derive(LintDiagnostic)] -#[diag(lint_duplicate_matcher_binding)] -pub(crate) struct DuplicateMatcherBinding; - -#[derive(LintDiagnostic)] -#[diag(lint_unknown_macro_variable)] -pub(crate) struct UnknownMacroVariable { - pub name: MacroRulesNormalizedIdent, -} - #[derive(LintDiagnostic)] #[diag(lint_unused_crate_dependency)] #[help] @@ -2714,22 +2670,6 @@ pub(crate) struct AbsPathWithModuleSugg { pub replacement: String, } -#[derive(LintDiagnostic)] -#[diag(lint_proc_macro_derive_resolution_fallback)] -pub(crate) struct ProcMacroDeriveResolutionFallback { - #[label] - pub span: Span, - pub ns_descr: &'static str, - pub ident: Ident, -} - -#[derive(LintDiagnostic)] -#[diag(lint_macro_expanded_macro_exports_accessed_by_absolute_paths)] -pub(crate) struct MacroExpandedMacroExportsAccessedByAbsolutePaths { - #[note] - pub definition: Span, -} - #[derive(LintDiagnostic)] #[diag(lint_hidden_lifetime_parameters)] pub(crate) struct ElidedLifetimesInPaths { diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 2e84233e5a56..77c17c6c1e39 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -11,7 +11,7 @@ use rustc_hir_id::{HashStableContext, HirId, ItemLocalId}; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::def_id::DefPathHash; pub use rustc_span::edition::Edition; -use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol, sym}; +use rustc_span::{Ident, Span, Symbol, sym}; use serde::{Deserialize, Serialize}; pub use self::Level::*; @@ -620,12 +620,6 @@ pub enum DeprecatedSinceKind { #[derive(Debug)] pub enum BuiltinLintDiag { AbsPathWithModule(Span), - ProcMacroDeriveResolutionFallback { - span: Span, - ns_descr: &'static str, - ident: Ident, - }, - MacroExpandedMacroExportsAccessedByAbsolutePaths(Span), ElidedLifetimesInPaths(usize, Span, bool, Span), UnknownCrateTypes { span: Span, @@ -774,16 +768,6 @@ pub enum BuiltinLintDiag { UnusedMacroDefinition(Symbol), MacroRuleNeverUsed(usize, Symbol), UnstableFeature(DiagMessage), - AvoidUsingIntelSyntax, - AvoidUsingAttSyntax, - IncompleteInclude, - UnnameableTestItems, - DuplicateMacroAttribute, - CfgAttrNoAttributes, - MetaVariableStillRepeating(MacroRulesNormalizedIdent), - MetaVariableWrongOperator, - DuplicateMatcherBinding, - UnknownMacroVariable(MacroRulesNormalizedIdent), UnusedCrateDependency { extern_crate: Symbol, local_crate: Symbol, diff --git a/compiler/rustc_resolve/messages.ftl b/compiler/rustc_resolve/messages.ftl index 0e566e20a124..1c0e5ad65b3a 100644 --- a/compiler/rustc_resolve/messages.ftl +++ b/compiler/rustc_resolve/messages.ftl @@ -260,6 +260,9 @@ resolve_macro_defined_later = resolve_macro_expanded_extern_crate_cannot_shadow_extern_arguments = macro-expanded `extern crate` items cannot shadow names passed with `--extern` +resolve_macro_expanded_macro_exports_accessed_by_absolute_paths = macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths + .note = the macro is defined here + resolve_macro_expected_found = expected {$expected}, found {$found} `{$macro_path}` .label = not {$article} {$expected} @@ -339,6 +342,9 @@ resolve_param_in_ty_of_const_param = resolve_pattern_doesnt_bind_name = pattern doesn't bind `{$name}` +resolve_proc_macro_derive_resolution_fallback = cannot find {$ns_descr} `{$ident}` in this scope + .label = names from parent modules are not accessible without an explicit import + resolve_proc_macro_same_crate = can't use a procedural macro from the same crate that defines it .help = you can define integration tests in a directory named `tests` diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 689e713ef46d..236ab1f09d35 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -138,7 +138,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, CRATE_NODE_ID, span_use, - BuiltinLintDiag::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def), + errors::MacroExpandedMacroExportsAccessedByAbsolutePaths { definition: span_def }, ); } diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index 72be94e58db9..fb77b9ca1d31 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -3,7 +3,7 @@ use rustc_errors::{ Applicability, Diag, ElidedLifetimeInPathSubdiag, EmissionGuarantee, IntoDiagArg, MultiSpan, Subdiagnostic, }; -use rustc_macros::{Diagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_span::{Ident, Span, Symbol}; use crate::late::PatternSource; @@ -566,6 +566,22 @@ pub(crate) struct ProcMacroSameCrate { pub(crate) is_test: bool, } +#[derive(LintDiagnostic)] +#[diag(resolve_proc_macro_derive_resolution_fallback)] +pub(crate) struct ProcMacroDeriveResolutionFallback { + #[label] + pub span: Span, + pub ns_descr: &'static str, + pub ident: Ident, +} + +#[derive(LintDiagnostic)] +#[diag(resolve_macro_expanded_macro_exports_accessed_by_absolute_paths)] +pub(crate) struct MacroExpandedMacroExportsAccessedByAbsolutePaths { + #[note] + pub definition: Span, +} + #[derive(Diagnostic)] #[diag(resolve_imported_crate)] pub(crate) struct CrateImported { diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index bc06a2275711..35051675fd8d 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -4,7 +4,6 @@ use rustc_ast::{self as ast, NodeId}; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::{DefKind, MacroKinds, Namespace, NonMacroAttrKind, PartialRes, PerNS}; use rustc_middle::bug; -use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::PROC_MACRO_DERIVE_RESOLUTION_FALLBACK; use rustc_session::parse::feature_err; use rustc_span::hygiene::{ExpnId, ExpnKind, LocalExpnId, MacroKind, SyntaxContext}; @@ -520,7 +519,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, lint_id, orig_ident.span, - BuiltinLintDiag::ProcMacroDeriveResolutionFallback { + errors::ProcMacroDeriveResolutionFallback { span: orig_ident.span, ns_descr: ns.descr(), ident, diff --git a/tests/ui/macros/issue-61053-duplicate-binder.stderr b/tests/ui/macros/issue-61053-duplicate-binder.stderr index 7c7cb26b4071..1ecbc3f86d0d 100644 --- a/tests/ui/macros/issue-61053-duplicate-binder.stderr +++ b/tests/ui/macros/issue-61053-duplicate-binder.stderr @@ -2,9 +2,9 @@ error: duplicate matcher binding --> $DIR/issue-61053-duplicate-binder.rs:7:20 | LL | ($x:tt $x:tt) => { $x }; - | -- ^^ + | -- ^^ duplicate binding | | - | previous declaration + | previous binding | note: the lint level is defined here --> $DIR/issue-61053-duplicate-binder.rs:1:9 From 31c0d96cb604ead17afeb09062d10cc019de9560 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 27 Aug 2025 14:11:32 +0200 Subject: [PATCH 498/682] Move more early buffered lints to dyn lint diagnostics (2/N) --- compiler/rustc_ast_passes/messages.ftl | 4 +++ .../rustc_ast_passes/src/ast_validation.rs | 2 +- compiler/rustc_ast_passes/src/errors.rs | 7 +++++ compiler/rustc_expand/messages.ftl | 4 +++ compiler/rustc_expand/src/errors.rs | 8 +++++ compiler/rustc_expand/src/expand.rs | 2 +- compiler/rustc_lint/messages.ftl | 14 --------- compiler/rustc_lint/src/early/diagnostics.rs | 13 -------- compiler/rustc_lint/src/lints.rs | 31 ------------------- compiler/rustc_lint_defs/src/lib.rs | 9 ------ compiler/rustc_resolve/messages.ftl | 7 +++++ compiler/rustc_resolve/src/check_unused.rs | 2 +- compiler/rustc_resolve/src/errors.rs | 16 ++++++++++ compiler/rustc_resolve/src/macros.rs | 2 +- 14 files changed, 50 insertions(+), 71 deletions(-) diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 8dcf3e3aa388..e5405a7ad910 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -216,6 +216,10 @@ ast_passes_match_arm_with_no_body = .suggestion = add a body after the pattern ast_passes_missing_unsafe_on_extern = extern blocks must be unsafe + .suggestion = needs `unsafe` before the extern keyword + +ast_passes_missing_unsafe_on_extern_lint = extern blocks should be unsafe + .suggestion = needs `unsafe` before the extern keyword ast_passes_module_nonascii = trying to load file for module `{$name}` with non-ascii identifier name .help = consider using the `#[path]` attribute to specify filesystem path diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index a6ef89b553db..dc221c2fb1ab 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1131,7 +1131,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { MISSING_UNSAFE_ON_EXTERN, item.id, item.span, - BuiltinLintDiag::MissingUnsafeOnExtern { + errors::MissingUnsafeOnExternLint { suggestion: item.span.shrink_to_lo(), }, ); diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index ae805042c549..e09ca5b81c80 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -523,6 +523,13 @@ pub(crate) struct MissingUnsafeOnExtern { pub span: Span, } +#[derive(LintDiagnostic)] +#[diag(ast_passes_missing_unsafe_on_extern_lint)] +pub(crate) struct MissingUnsafeOnExternLint { + #[suggestion(code = "unsafe ", applicability = "machine-applicable")] + pub suggestion: Span, +} + #[derive(Diagnostic)] #[diag(ast_passes_fieldless_union)] pub(crate) struct FieldlessUnion { diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl index fc50ac6e3bfe..f82ef96e9c75 100644 --- a/compiler/rustc_expand/messages.ftl +++ b/compiler/rustc_expand/messages.ftl @@ -80,6 +80,10 @@ expand_macro_body_stability = .label = invalid body stability attribute .label2 = body stability attribute affects this macro +expand_macro_call_unused_doc_comment = unused doc comment + .label = rustdoc does not generate documentation for macro invocations + .help = to document an item produced by a macro, the macro must produce the documentation as part of its expansion + expand_macro_const_stability = macros cannot have const stability attributes .label = invalid const stability attribute diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index a939dadaf2fc..06c029482dfe 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -537,3 +537,11 @@ pub(crate) struct MacroArgsBadDelimSugg { #[suggestion_part(code = ")")] pub close: Span, } + +#[derive(LintDiagnostic)] +#[diag(expand_macro_call_unused_doc_comment)] +#[help] +pub(crate) struct MacroCallUnusedDocComment { + #[label] + pub span: Span, +} diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 38e057d2776e..cda38cd7ae61 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -2183,7 +2183,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { UNUSED_DOC_COMMENTS, current_span, self.cx.current_expansion.lint_node_id, - BuiltinLintDiag::UnusedDocComment(attr.span), + crate::errors::MacroCallUnusedDocComment { span: attr.span }, ); } else if rustc_attr_parsing::is_builtin_attr(attr) && !AttributeParser::::is_parsed_attribute(&attr.path()) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 5a60a4f48d9d..68bef1772c0e 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -453,9 +453,6 @@ lint_int_to_ptr_transmutes = transmuting an integer to a pointer creates a point .suggestion_with_exposed_provenance = use `std::ptr::with_exposed_provenance{$suffix}` instead to use a previously exposed provenance .suggestion_without_provenance_mut = if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut` -lint_legacy_derive_helpers = derive helper attribute is used before it is introduced - .label = the attribute is introduced here - lint_lintpass_by_hand = implementing `LintPass` by hand .help = try using `declare_lint_pass!` or `impl_lint_pass!` instead @@ -524,9 +521,6 @@ lint_mismatched_lifetime_syntaxes_suggestion_mixed = lint_mismatched_lifetime_syntaxes_suggestion_mixed_only_paths = use `'_` for type paths -lint_missing_unsafe_on_extern = extern blocks should be unsafe - .suggestion = needs `unsafe` before the extern keyword - lint_mixed_script_confusables = the usage of Script Group `{$set}` in this crate consists solely of mixed script confusables .includes_note = the usage includes {$includes} @@ -962,14 +956,6 @@ lint_unused_def = unused {$pre}`{$def}`{$post} that must be used lint_unused_delim = unnecessary {$delim} around {$item} .suggestion = remove these {$delim} -lint_unused_doc_comment = unused doc comment - .label = rustdoc does not generate documentation for macro invocations - .help = to document an item produced by a macro, the macro must produce the documentation as part of its expansion - -lint_unused_extern_crate = unused extern crate - .label = unused - .suggestion = remove the unused `extern crate` - lint_unused_import_braces = braces around {$node} is unnecessary lint_unused_imports = {$num_snippets -> diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 4463586515ec..eb5c89660e1f 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -135,9 +135,6 @@ pub fn decorate_builtin_lint( stability::Deprecated { sub, kind: "macro".to_owned(), path, note, since_kind } .decorate_lint(diag); } - BuiltinLintDiag::UnusedDocComment(attr_span) => { - lints::UnusedDocComment { span: attr_span }.decorate_lint(diag); - } BuiltinLintDiag::PatternsInFnsWithoutBody { span: remove_span, ident, is_foreign } => { let sub = lints::PatternsInFnsWithoutBodySub { ident, span: remove_span }; if is_foreign { @@ -147,9 +144,6 @@ pub fn decorate_builtin_lint( } .decorate_lint(diag); } - BuiltinLintDiag::LegacyDeriveHelpers(label_span) => { - lints::LegacyDeriveHelpers { span: label_span }.decorate_lint(diag); - } BuiltinLintDiag::OrPatternsBackCompat(suggestion_span, suggestion) => { lints::OrPatternsBackCompat { span: suggestion_span, suggestion }.decorate_lint(diag); } @@ -210,9 +204,6 @@ pub fn decorate_builtin_lint( }; lints::DeprecatedWhereClauseLocation { suggestion }.decorate_lint(diag); } - BuiltinLintDiag::MissingUnsafeOnExtern { suggestion } => { - lints::MissingUnsafeOnExtern { suggestion }.decorate_lint(diag); - } BuiltinLintDiag::SingleUseLifetime { param_span, use_span: Some((use_span, elide)), @@ -242,7 +233,6 @@ pub fn decorate_builtin_lint( .decorate_lint(diag); } BuiltinLintDiag::SingleUseLifetime { use_span: None, deletion_span, ident, .. } => { - debug!(?deletion_span); lints::UnusedLifetime { deletion_span, ident }.decorate_lint(diag); } BuiltinLintDiag::NamedArgumentUsedPositionally { @@ -283,9 +273,6 @@ pub fn decorate_builtin_lint( BuiltinLintDiag::ByteSliceInPackedStructWithDerive { ty } => { lints::ByteSliceInPackedStructWithDerive { ty }.decorate_lint(diag); } - BuiltinLintDiag::UnusedExternCrate { span, removal_span } => { - lints::UnusedExternCrate { span, removal_span }.decorate_lint(diag); - } BuiltinLintDiag::ExternCrateNotIdiomatic { vis_span, ident_span } => { let suggestion_span = vis_span.between(ident_span); let code = if vis_span.is_empty() { "use " } else { " use " }; diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 1c13fadeef96..c084e8605f78 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2749,14 +2749,6 @@ pub(crate) enum RedundantImportSub { DefinedPrelude(#[primary_span] Span), } -#[derive(LintDiagnostic)] -#[diag(lint_unused_doc_comment)] -#[help] -pub(crate) struct UnusedDocComment { - #[label] - pub span: Span, -} - #[derive(LintDiagnostic)] pub(crate) enum PatternsInFnsWithoutBody { #[diag(lint_pattern_in_foreign)] @@ -2780,13 +2772,6 @@ pub(crate) struct PatternsInFnsWithoutBodySub { pub ident: Ident, } -#[derive(LintDiagnostic)] -#[diag(lint_legacy_derive_helpers)] -pub(crate) struct LegacyDeriveHelpers { - #[label] - pub span: Span, -} - #[derive(LintDiagnostic)] #[diag(lint_or_patterns_back_compat)] pub(crate) struct OrPatternsBackCompat { @@ -2878,13 +2863,6 @@ pub(crate) enum DeprecatedWhereClauseLocationSugg { }, } -#[derive(LintDiagnostic)] -#[diag(lint_missing_unsafe_on_extern)] -pub(crate) struct MissingUnsafeOnExtern { - #[suggestion(code = "unsafe ", applicability = "machine-applicable")] - pub suggestion: Span, -} - #[derive(LintDiagnostic)] #[diag(lint_single_use_lifetime)] pub(crate) struct SingleUseLifetime { @@ -2940,15 +2918,6 @@ pub(crate) struct ByteSliceInPackedStructWithDerive { pub ty: String, } -#[derive(LintDiagnostic)] -#[diag(lint_unused_extern_crate)] -pub(crate) struct UnusedExternCrate { - #[label] - pub span: Span, - #[suggestion(code = "", applicability = "machine-applicable", style = "verbose")] - pub removal_span: Span, -} - #[derive(LintDiagnostic)] #[diag(lint_extern_crate_not_idiomatic)] pub(crate) struct ExternCrateNotIdiomatic { diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 77c17c6c1e39..50c3bcf0434b 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -640,7 +640,6 @@ pub enum BuiltinLintDiag { path: String, since_kind: DeprecatedSinceKind, }, - UnusedDocComment(Span), UnusedBuiltinAttribute { attr_name: Symbol, macro_name: String, @@ -652,7 +651,6 @@ pub enum BuiltinLintDiag { ident: Ident, is_foreign: bool, }, - LegacyDeriveHelpers(Span), OrPatternsBackCompat(Span, String), ReservedPrefix(Span, String), /// `'r#` in edition < 2021. @@ -668,9 +666,6 @@ pub enum BuiltinLintDiag { UnexpectedCfgName((Symbol, Span), Option<(Symbol, Span)>), UnexpectedCfgValue((Symbol, Span), Option<(Symbol, Span)>), DeprecatedWhereclauseLocation(Span, Option<(Span, String)>), - MissingUnsafeOnExtern { - suggestion: Span, - }, SingleUseLifetime { /// Span of the parameter which declares this lifetime. param_span: Span, @@ -699,10 +694,6 @@ pub enum BuiltinLintDiag { // FIXME: enum of byte/string ty: String, }, - UnusedExternCrate { - span: Span, - removal_span: Span, - }, ExternCrateNotIdiomatic { vis_span: Span, ident_span: Span, diff --git a/compiler/rustc_resolve/messages.ftl b/compiler/rustc_resolve/messages.ftl index 1c0e5ad65b3a..32833f5e8d9b 100644 --- a/compiler/rustc_resolve/messages.ftl +++ b/compiler/rustc_resolve/messages.ftl @@ -231,6 +231,9 @@ resolve_item_was_cfg_out = the item is gated here resolve_label_with_similar_name_reachable = a label with a similar name is reachable +resolve_legacy_derive_helpers = derive helper attribute is used before it is introduced + .label = the attribute is introduced here + resolve_lending_iterator_report_error = associated type `Iterator::Item` is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type .note = you can't create an `Iterator` that borrows each `Item` from itself, but you can instead create a new type that borrows your existing type and implement `Iterator` for that new type @@ -471,6 +474,10 @@ resolve_unreachable_label_suggestion_use_similarly_named = resolve_unreachable_label_with_similar_name_exists = a label with a similar name exists but is unreachable +resolve_unused_extern_crate = unused extern crate + .label = unused + .suggestion = remove the unused `extern crate` + resolve_variable_bound_with_different_mode = variable `{$variable_name}` is bound inconsistently across alternatives separated by `|` .label = bound in different ways diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 50a1ad23a546..a80cd7540afa 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -169,7 +169,7 @@ impl<'a, 'ra, 'tcx> UnusedImportCheckVisitor<'a, 'ra, 'tcx> { UNUSED_EXTERN_CRATES, extern_crate.id, span, - BuiltinLintDiag::UnusedExternCrate { + crate::errors::UnusedExternCrate { span: extern_crate.span, removal_span: extern_crate.span_with_attributes, }, diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index fb77b9ca1d31..6b36906c8653 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -1292,3 +1292,19 @@ pub(crate) struct TraitImplMismatch { #[label(resolve_trait_impl_mismatch_label_item)] pub(crate) trait_item_span: Span, } + +#[derive(LintDiagnostic)] +#[diag(resolve_legacy_derive_helpers)] +pub(crate) struct LegacyDeriveHelpers { + #[label] + pub span: Span, +} + +#[derive(LintDiagnostic)] +#[diag(resolve_unused_extern_crate)] +pub(crate) struct UnusedExternCrate { + #[label] + pub span: Span, + #[suggestion(code = "", applicability = "machine-applicable", style = "verbose")] + pub removal_span: Span, +} diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 5fa87b4cd9b6..d3e98ef839b0 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -979,7 +979,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { LEGACY_DERIVE_HELPERS, node_id, ident.span, - BuiltinLintDiag::LegacyDeriveHelpers(binding.span), + errors::LegacyDeriveHelpers { span: binding.span }, ); } } From 379b181fe698108d61ac05bad328e14567739304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 27 Aug 2025 14:11:41 +0200 Subject: [PATCH 499/682] Remove a dead early lint Dropped in favor a hard error in RUST-127907. --- compiler/rustc_lint/messages.ftl | 3 --- compiler/rustc_lint/src/early/diagnostics.rs | 3 --- compiler/rustc_lint/src/lints.rs | 8 -------- compiler/rustc_lint_defs/src/lib.rs | 4 ---- 4 files changed, 18 deletions(-) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 68bef1772c0e..e348ae2c5745 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -181,9 +181,6 @@ lint_builtin_unused_doc_comment = unused doc comment lint_builtin_while_true = denote infinite loops with `loop {"{"} ... {"}"}` .suggestion = use `loop` -lint_byte_slice_in_packed_struct_with_derive = {$ty} slice in a packed struct that derives a built-in trait - .help = consider implementing the trait by hand, or remove the `packed` attribute - lint_check_name_unknown_tool = unknown lint tool: `{$tool_name}` lint_closure_returning_async_block = closure returning async block can be made into an async closure diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index eb5c89660e1f..403988899386 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -270,9 +270,6 @@ pub fn decorate_builtin_lint( } .decorate_lint(diag); } - BuiltinLintDiag::ByteSliceInPackedStructWithDerive { ty } => { - lints::ByteSliceInPackedStructWithDerive { ty }.decorate_lint(diag); - } BuiltinLintDiag::ExternCrateNotIdiomatic { vis_span, ident_span } => { let suggestion_span = vis_span.between(ident_span); let code = if vis_span.is_empty() { "use " } else { " use " }; diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index c084e8605f78..64875ee4e8a1 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2910,14 +2910,6 @@ pub(crate) struct NamedArgumentUsedPositionally { pub named_arg_name: String, } -#[derive(LintDiagnostic)] -#[diag(lint_byte_slice_in_packed_struct_with_derive)] -#[help] -pub(crate) struct ByteSliceInPackedStructWithDerive { - // FIXME: make this translatable - pub ty: String, -} - #[derive(LintDiagnostic)] #[diag(lint_extern_crate_not_idiomatic)] pub(crate) struct ExternCrateNotIdiomatic { diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 50c3bcf0434b..0b4b5015aca5 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -690,10 +690,6 @@ pub enum BuiltinLintDiag { /// Indicates if the named argument is used as a width/precision for formatting is_formatting_arg: bool, }, - ByteSliceInPackedStructWithDerive { - // FIXME: enum of byte/string - ty: String, - }, ExternCrateNotIdiomatic { vis_span: Span, ident_span: Span, From 27a180a31a10bad8d5116146459a49a16b4f3866 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 27 Aug 2025 15:07:14 +0200 Subject: [PATCH 500/682] Move more early buffered lints to dyn lint diagnostics (3/N) --- compiler/rustc_lint/messages.ftl | 11 ---------- compiler/rustc_lint/src/early/diagnostics.rs | 8 -------- compiler/rustc_lint/src/lints.rs | 21 -------------------- compiler/rustc_lint_defs/src/lib.rs | 8 -------- compiler/rustc_resolve/messages.ftl | 11 ++++++++++ compiler/rustc_resolve/src/check_unused.rs | 4 ++-- compiler/rustc_resolve/src/errors.rs | 21 ++++++++++++++++++++ compiler/rustc_resolve/src/imports.rs | 6 +++--- compiler/rustc_resolve/src/late.rs | 4 ++-- 9 files changed, 39 insertions(+), 55 deletions(-) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index e348ae2c5745..c11e59ca0588 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -460,10 +460,6 @@ lint_macro_is_private = macro `{$ident}` is private lint_macro_rule_never_used = rule #{$n} of macro `{$name}` is never used -lint_macro_use_deprecated = - applying the `#[macro_use]` attribute to an `extern crate` item is deprecated - .help = remove it and import macros at use sites with a `use` item instead - lint_malformed_attribute = malformed lint attribute input lint_map_unit_fn = `Iterator::map` call that discard the iterator's values @@ -726,9 +722,6 @@ lint_redundant_semicolons_suggestion = remove {$multiple_semicolons -> *[false] this semicolon } -lint_reexport_private_dependency = - {$kind} `{$name}` from private dependency '{$krate}' is re-exported - lint_remove_mut_from_pattern = remove `mut` from the parameter lint_removed_lint = lint `{$name}` has been removed: {$reason} @@ -966,15 +959,11 @@ lint_unused_imports = {$num_snippets -> } .help = if this is a test module, consider adding a `#[cfg(test)]` to the containing module -lint_unused_label = unused label - lint_unused_lifetime = lifetime parameter `{$ident}` never used .suggestion = elide the unused lifetime lint_unused_macro_definition = unused macro definition: `{$name}` -lint_unused_macro_use = unused `#[macro_use]` import - lint_unused_op = unused {$op} that must be used .label = the {$op} produces a value .suggestion = use `let _ = ...` to ignore the resulting value diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 403988899386..e8daf3e46b87 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -308,9 +308,6 @@ pub fn decorate_builtin_lint( } .decorate_lint(diag); } - BuiltinLintDiag::ReexportPrivateDependency { name, kind, krate } => { - lints::ReexportPrivateDependency { name, kind, krate }.decorate_lint(diag); - } BuiltinLintDiag::UnusedQualifications { removal_span } => { lints::UnusedQualifications { removal_span }.decorate_lint(diag); } @@ -350,15 +347,10 @@ pub fn decorate_builtin_lint( }); lints::UnknownDiagnosticAttribute { typo }.decorate_lint(diag); } - BuiltinLintDiag::MacroUseDeprecated => { - lints::MacroUseDeprecated.decorate_lint(diag); - } - BuiltinLintDiag::UnusedMacroUse => lints::UnusedMacroUse.decorate_lint(diag), BuiltinLintDiag::PrivateExternCrateReexport { source: ident, extern_crate_span } => { lints::PrivateExternCrateReexport { ident, sugg: extern_crate_span.shrink_to_lo() } .decorate_lint(diag); } - BuiltinLintDiag::UnusedLabel => lints::UnusedLabel.decorate_lint(diag), BuiltinLintDiag::MacroIsPrivate(ident) => { lints::MacroIsPrivate { ident }.decorate_lint(diag); } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 64875ee4e8a1..7c375a98f9e9 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2535,15 +2535,6 @@ pub(crate) mod unexpected_cfg_value { } } -#[derive(LintDiagnostic)] -#[diag(lint_macro_use_deprecated)] -#[help] -pub(crate) struct MacroUseDeprecated; - -#[derive(LintDiagnostic)] -#[diag(lint_unused_macro_use)] -pub(crate) struct UnusedMacroUse; - #[derive(LintDiagnostic)] #[diag(lint_private_extern_crate_reexport, code = E0365)] pub(crate) struct PrivateExternCrateReexport { @@ -2552,10 +2543,6 @@ pub(crate) struct PrivateExternCrateReexport { pub sugg: Span, } -#[derive(LintDiagnostic)] -#[diag(lint_unused_label)] -pub(crate) struct UnusedLabel; - #[derive(LintDiagnostic)] #[diag(lint_macro_is_private)] pub(crate) struct MacroIsPrivate { @@ -2957,14 +2944,6 @@ pub(crate) struct HiddenGlobReexports { pub namespace: String, } -#[derive(LintDiagnostic)] -#[diag(lint_reexport_private_dependency)] -pub(crate) struct ReexportPrivateDependency { - pub name: String, - pub kind: String, - pub krate: Symbol, -} - #[derive(LintDiagnostic)] #[diag(lint_unnecessary_qualification)] pub(crate) struct UnusedQualifications { diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 0b4b5015aca5..239b03edfaf0 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -717,11 +717,6 @@ pub enum BuiltinLintDiag { /// The local binding that shadows the glob reexport. private_item_span: Span, }, - ReexportPrivateDependency { - name: String, - kind: String, - krate: Symbol, - }, UnusedQualifications { /// The span of the unnecessarily-qualified path to remove. removal_span: Span, @@ -744,13 +739,10 @@ pub enum BuiltinLintDiag { span: Span, typo_name: Option, }, - MacroUseDeprecated, - UnusedMacroUse, PrivateExternCrateReexport { source: Ident, extern_crate_span: Span, }, - UnusedLabel, MacroIsPrivate(Ident), UnusedMacroDefinition(Symbol), MacroRuleNeverUsed(usize, Symbol), diff --git a/compiler/rustc_resolve/messages.ftl b/compiler/rustc_resolve/messages.ftl index 32833f5e8d9b..cc8fbc18b737 100644 --- a/compiler/rustc_resolve/messages.ftl +++ b/compiler/rustc_resolve/messages.ftl @@ -274,6 +274,10 @@ resolve_macro_extern_deprecated = `#[macro_escape]` is a deprecated synonym for `#[macro_use]` .help = try an outer attribute: `#[macro_use]` +resolve_macro_use_deprecated = + applying the `#[macro_use]` attribute to an `extern crate` item is deprecated + .help = remove it and import macros at use sites with a `use` item instead + resolve_macro_use_extern_crate_self = `#[macro_use]` is not supported on `extern crate self` resolve_macro_use_name_already_in_use = @@ -357,6 +361,9 @@ resolve_reexport_of_crate_public = resolve_reexport_of_private = re-export of private `{$ident}` +resolve_reexport_private_dependency = + {$kind} `{$name}` from private dependency '{$krate}' is re-exported + resolve_relative_2018 = relative paths are not supported in visibilities in 2018 edition or later .suggestion = try @@ -478,6 +485,10 @@ resolve_unused_extern_crate = unused extern crate .label = unused .suggestion = remove the unused `extern crate` +resolve_unused_label = unused label + +resolve_unused_macro_use = unused `#[macro_use]` import + resolve_variable_bound_with_different_mode = variable `{$variable_name}` is bound inconsistently across alternatives separated by `|` .label = bound in different ways diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index a80cd7540afa..95f979a3fed3 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -406,7 +406,7 @@ impl Resolver<'_, '_> { MACRO_USE_EXTERN_CRATE, import.root_id, import.span, - BuiltinLintDiag::MacroUseDeprecated, + crate::errors::MacroUseDeprecated, ); } } @@ -427,7 +427,7 @@ impl Resolver<'_, '_> { UNUSED_IMPORTS, import.root_id, import.span, - BuiltinLintDiag::UnusedMacroUse, + crate::errors::UnusedMacroUse, ); } _ => {} diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index 6b36906c8653..f0ea97ba8a0c 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -1308,3 +1308,24 @@ pub(crate) struct UnusedExternCrate { #[suggestion(code = "", applicability = "machine-applicable", style = "verbose")] pub removal_span: Span, } + +#[derive(LintDiagnostic)] +#[diag(resolve_reexport_private_dependency)] +pub(crate) struct ReexportPrivateDependency { + pub name: Symbol, + pub kind: &'static str, + pub krate: Symbol, +} + +#[derive(LintDiagnostic)] +#[diag(resolve_unused_label)] +pub(crate) struct UnusedLabel; + +#[derive(LintDiagnostic)] +#[diag(resolve_unused_macro_use)] +pub(crate) struct UnusedMacroUse; + +#[derive(LintDiagnostic)] +#[diag(resolve_macro_use_deprecated)] +#[help] +pub(crate) struct MacroUseDeprecated; diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index d7fc028287ff..33c2c7436d1b 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -720,9 +720,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { EXPORTED_PRIVATE_DEPENDENCIES, binding_id, binding.span, - BuiltinLintDiag::ReexportPrivateDependency { - kind: binding.res().descr().to_string(), - name: key.ident.name.to_string(), + crate::errors::ReexportPrivateDependency { + name: key.ident.name, + kind: binding.res().descr(), krate: self.tcx.crate_name(reexported_def_id.krate), }, ); diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 521fef2b9d43..4d4acc60ca80 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -29,7 +29,7 @@ use rustc_middle::middle::resolve_bound_vars::Set1; use rustc_middle::ty::{DelegationFnSig, Visibility}; use rustc_middle::{bug, span_bug}; use rustc_session::config::{CrateType, ResolveDocLinks}; -use rustc_session::lint::{self, BuiltinLintDiag}; +use rustc_session::lint; use rustc_session::parse::feature_err; use rustc_span::source_map::{Spanned, respan}; use rustc_span::{BytePos, Ident, Span, Symbol, SyntaxContext, kw, sym}; @@ -5225,7 +5225,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { lint::builtin::UNUSED_LABELS, *id, *span, - BuiltinLintDiag::UnusedLabel, + errors::UnusedLabel, ); } } From ec7ad59789138871418362d9592b1e848bab4053 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sun, 14 Sep 2025 06:03:22 +0200 Subject: [PATCH 501/682] Move more early buffered lints to dyn lint diagnostics (4/N) --- compiler/rustc_expand/messages.ftl | 11 +++++ compiler/rustc_expand/src/errors.rs | 28 +++++++++++++ compiler/rustc_expand/src/expand.rs | 3 +- compiler/rustc_expand/src/mbe/macro_rules.rs | 5 +-- compiler/rustc_interface/messages.ftl | 3 ++ compiler/rustc_interface/src/errors.rs | 17 +++++++- compiler/rustc_interface/src/util.rs | 8 ++-- compiler/rustc_lint/messages.ftl | 14 ------- compiler/rustc_lint/src/early/diagnostics.rs | 19 --------- compiler/rustc_lint/src/lints.rs | 44 -------------------- compiler/rustc_lint_defs/src/lib.rs | 12 ------ 11 files changed, 66 insertions(+), 98 deletions(-) diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl index f82ef96e9c75..47c00bff5c95 100644 --- a/compiler/rustc_expand/messages.ftl +++ b/compiler/rustc_expand/messages.ftl @@ -160,6 +160,9 @@ expand_mve_unrecognized_var = expand_non_inline_modules_in_proc_macro_input_are_unstable = non-inline modules in proc macro input are unstable +expand_or_patterns_back_compat = the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro + .suggestion = use pat_param to preserve semantics + expand_proc_macro_back_compat = using an old version of `{$crate_name}` .note = older versions of the `{$crate_name}` crate no longer compile; please update to `{$crate_name}` v{$fixed_version}, or switch to one of the `{$crate_name}` alternatives @@ -189,8 +192,16 @@ expand_resolve_relative_path = expand_trace_macro = trace_macro +expand_trailing_semi_macro = trailing semicolon in macro used in expression position + .note1 = macro invocations at the end of a block are treated as expressions + .note2 = to ignore the value produced by the macro, add a semicolon after the invocation of `{$name}` + expand_unknown_macro_variable = unknown macro variable `{$name}` +expand_unused_builtin_attribute = unused attribute `{$attr_name}` + .note = the built-in attribute `{$attr_name}` will be ignored, since it's applied to the macro invocation `{$macro_name}` + .suggestion = remove the attribute + expand_unsupported_key_value = key-value macro attributes are not supported diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index 06c029482dfe..c37c2d88d9cd 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -545,3 +545,31 @@ pub(crate) struct MacroCallUnusedDocComment { #[label] pub span: Span, } + +#[derive(LintDiagnostic)] +#[diag(expand_or_patterns_back_compat)] +pub(crate) struct OrPatternsBackCompat { + #[suggestion(code = "{suggestion}", applicability = "machine-applicable")] + pub span: Span, + pub suggestion: String, +} + +#[derive(LintDiagnostic)] +#[diag(expand_trailing_semi_macro)] +pub(crate) struct TrailingMacro { + #[note(expand_note1)] + #[note(expand_note2)] + pub is_trailing: bool, + pub name: Ident, +} + +#[derive(LintDiagnostic)] +#[diag(expand_unused_builtin_attribute)] +pub(crate) struct UnusedBuiltinAttribute { + #[note] + pub invoc_span: Span, + pub attr_name: Symbol, + pub macro_name: String, + #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")] + pub attr_span: Span, +} diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index cda38cd7ae61..4c0e0bbfe26d 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -25,7 +25,6 @@ use rustc_parse::parser::{ token_descr, }; use rustc_session::Session; -use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::{UNUSED_ATTRIBUTES, UNUSED_DOC_COMMENTS}; use rustc_session::parse::feature_err; use rustc_span::hygiene::SyntaxContext; @@ -2196,7 +2195,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { UNUSED_ATTRIBUTES, attr.span, self.cx.current_expansion.lint_node_id, - BuiltinLintDiag::UnusedBuiltinAttribute { + crate::errors::UnusedBuiltinAttribute { attr_name, macro_name: pprust::path_to_string(&call.path), invoc_span: call.path.span, diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 8b43f852b263..946265eba8bd 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -17,7 +17,6 @@ use rustc_hir as hir; use rustc_hir::attrs::AttributeKind; use rustc_hir::def::MacroKinds; use rustc_hir::find_attr; -use rustc_lint_defs::BuiltinLintDiag; use rustc_lint_defs::builtin::{ RUST_2021_INCOMPATIBLE_OR_PATTERNS, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS, }; @@ -90,7 +89,7 @@ impl<'a> ParserAnyMacro<'a> { SEMICOLON_IN_EXPRESSIONS_FROM_MACROS, parser.token.span, lint_node_id, - BuiltinLintDiag::TrailingMacro(is_trailing_mac, macro_ident), + errors::TrailingMacro { is_trailing: is_trailing_mac, name: macro_ident }, ); } parser.bump(); @@ -1425,7 +1424,7 @@ fn check_matcher_core<'tt>( RUST_2021_INCOMPATIBLE_OR_PATTERNS, span, ast::CRATE_NODE_ID, - BuiltinLintDiag::OrPatternsBackCompat(span, suggestion), + errors::OrPatternsBackCompat { span, suggestion }, ); } match is_in_follow(next_token, kind) { diff --git a/compiler/rustc_interface/messages.ftl b/compiler/rustc_interface/messages.ftl index d83b18cbc2df..6f6666f8c76f 100644 --- a/compiler/rustc_interface/messages.ftl +++ b/compiler/rustc_interface/messages.ftl @@ -30,6 +30,9 @@ interface_ignoring_out_dir = ignoring --out-dir flag due to -o flag interface_input_file_would_be_overwritten = the input file "{$path}" would be overwritten by the generated executable +interface_invalid_crate_type_value = invalid `crate_type` value + .suggestion = did you mean + interface_mixed_bin_crate = cannot mix `bin` crate type with others diff --git a/compiler/rustc_interface/src/errors.rs b/compiler/rustc_interface/src/errors.rs index d1082eaf6173..727e09c7562b 100644 --- a/compiler/rustc_interface/src/errors.rs +++ b/compiler/rustc_interface/src/errors.rs @@ -1,7 +1,7 @@ use std::io; use std::path::Path; -use rustc_macros::Diagnostic; +use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; #[derive(Diagnostic)] @@ -108,3 +108,18 @@ pub(crate) struct AbiRequiredTargetFeature<'a> { pub feature: &'a str, pub enabled: &'a str, } + +#[derive(LintDiagnostic)] +#[diag(interface_invalid_crate_type_value)] +pub(crate) struct UnknownCrateTypes { + #[subdiagnostic] + pub sugg: Option, +} + +#[derive(Subdiagnostic)] +#[suggestion(interface_suggestion, code = r#""{snippet}""#, applicability = "maybe-incorrect")] +pub(crate) struct UnknownCrateTypesSub { + #[primary_span] + pub span: Span, + pub snippet: Symbol, +} diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 04006f3e446c..061c764e619a 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -13,9 +13,8 @@ use rustc_errors::LintBuffer; use rustc_metadata::{DylibError, load_symbol_from_dylib}; use rustc_middle::ty::CurrentGcx; use rustc_session::config::{Cfg, OutFileName, OutputFilenames, OutputTypes, Sysroot, host_tuple}; -use rustc_session::lint::{self, BuiltinLintDiag}; use rustc_session::output::{CRATE_TYPES, categorize_crate_type}; -use rustc_session::{EarlyDiagCtxt, Session, filesearch}; +use rustc_session::{EarlyDiagCtxt, Session, filesearch, lint}; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edition::Edition; use rustc_span::source_map::SourceMapInputs; @@ -468,7 +467,10 @@ pub(crate) fn check_attr_crate_type( lint::builtin::UNKNOWN_CRATE_TYPES, ast::CRATE_NODE_ID, span, - BuiltinLintDiag::UnknownCrateTypes { span, candidate }, + errors::UnknownCrateTypes { + sugg: candidate + .map(|cand| errors::UnknownCrateTypesSub { span, snippet: cand }), + }, ); } } else { diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index c11e59ca0588..5b98c9fafaad 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -407,9 +407,6 @@ lint_invalid_asm_label_named = avoid using named labels in inline assembly .note = see the asm section of Rust By Example for more information lint_invalid_asm_label_no_span = the label may be declared in the expansion of a macro -lint_invalid_crate_type_value = invalid `crate_type` value - .suggestion = did you mean - # FIXME: we should ordinalize $valid_up_to when we add support for doing so lint_invalid_from_utf8_checked = calls to `{$method}` with an invalid literal always return an error .label = the literal was valid UTF-8 up to the {$valid_up_to} bytes @@ -637,9 +634,6 @@ lint_opaque_hidden_inferred_bound = opaque type `{$ty}` does not satisfy its ass lint_opaque_hidden_inferred_bound_sugg = add this bound -lint_or_patterns_back_compat = the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro - .suggestion = use pat_param to preserve semantics - lint_out_of_scope_macro_calls = cannot find macro `{$path}` in the current scope when looking from {$location} .label = not found from {$location} .help = import `macro_rules` with `use` to make it callable above its definition @@ -783,10 +777,6 @@ lint_symbol_intern_string_literal = using `Symbol::intern` on a string literal lint_too_large_char_cast = value exceeds maximum `char` value .note = maximum valid `char` value is `0x10FFFF` -lint_trailing_semi_macro = trailing semicolon in macro used in expression position - .note1 = macro invocations at the end of a block are treated as expressions - .note2 = to ignore the value produced by the macro, add a semicolon after the invocation of `{$name}` - lint_ty_qualified = usage of qualified `ty::{$ty}` .suggestion = try importing it and using it unqualified @@ -917,10 +907,6 @@ lint_untranslatable_diag = diagnostics should be created using translatable mess lint_unused_allocation = unnecessary allocation, use `&` instead lint_unused_allocation_mut = unnecessary allocation, use `&mut` instead -lint_unused_builtin_attribute = unused attribute `{$attr_name}` - .note = the built-in attribute `{$attr_name}` will be ignored, since it's applied to the macro invocation `{$macro_name}` - .suggestion = remove the attribute - lint_unused_closure = unused {$pre}{$count -> [one] closure diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index e8daf3e46b87..9029ee044711 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -76,10 +76,6 @@ pub fn decorate_builtin_lint( } .decorate_lint(diag); } - BuiltinLintDiag::UnknownCrateTypes { span, candidate } => { - let sugg = candidate.map(|candidate| lints::UnknownCrateTypesSub { span, candidate }); - lints::UnknownCrateTypes { sugg }.decorate_lint(diag); - } BuiltinLintDiag::UnusedImports { remove_whole_use, num_to_remove, @@ -144,9 +140,6 @@ pub fn decorate_builtin_lint( } .decorate_lint(diag); } - BuiltinLintDiag::OrPatternsBackCompat(suggestion_span, suggestion) => { - lints::OrPatternsBackCompat { span: suggestion_span, suggestion }.decorate_lint(diag); - } BuiltinLintDiag::ReservedPrefix(label_span, prefix) => { lints::ReservedPrefix { label: label_span, @@ -166,18 +159,6 @@ pub fn decorate_builtin_lint( lints::ReservedMultihash { suggestion }.decorate_lint(diag); } } - BuiltinLintDiag::UnusedBuiltinAttribute { - attr_name, - macro_name, - invoc_span, - attr_span, - } => { - lints::UnusedBuiltinAttribute { invoc_span, attr_name, macro_name, attr_span } - .decorate_lint(diag); - } - BuiltinLintDiag::TrailingMacro(is_trailing, name) => { - lints::TrailingMacro { is_trailing, name }.decorate_lint(diag); - } BuiltinLintDiag::BreakWithLabelAndLoop(sugg_span) => { lints::BreakWithLabelAndLoop { sub: lints::BreakWithLabelAndLoopSub { diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 7c375a98f9e9..b7312484de5c 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2664,21 +2664,6 @@ pub(crate) struct ElidedLifetimesInPaths { pub subdiag: ElidedLifetimeInPathSubdiag, } -#[derive(LintDiagnostic)] -#[diag(lint_invalid_crate_type_value)] -pub(crate) struct UnknownCrateTypes { - #[subdiagnostic] - pub sugg: Option, -} - -#[derive(Subdiagnostic)] -#[suggestion(lint_suggestion, code = r#""{candidate}""#, applicability = "maybe-incorrect")] -pub(crate) struct UnknownCrateTypesSub { - #[primary_span] - pub span: Span, - pub candidate: Symbol, -} - #[derive(LintDiagnostic)] #[diag(lint_unused_imports)] pub(crate) struct UnusedImports { @@ -2759,14 +2744,6 @@ pub(crate) struct PatternsInFnsWithoutBodySub { pub ident: Ident, } -#[derive(LintDiagnostic)] -#[diag(lint_or_patterns_back_compat)] -pub(crate) struct OrPatternsBackCompat { - #[suggestion(code = "{suggestion}", applicability = "machine-applicable")] - pub span: Span, - pub suggestion: String, -} - #[derive(LintDiagnostic)] #[diag(lint_reserved_prefix)] pub(crate) struct ReservedPrefix { @@ -2787,27 +2764,6 @@ pub(crate) struct RawPrefix { pub suggestion: Span, } -#[derive(LintDiagnostic)] -#[diag(lint_unused_builtin_attribute)] -pub(crate) struct UnusedBuiltinAttribute { - #[note] - pub invoc_span: Span, - pub attr_name: Symbol, - pub macro_name: String, - #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")] - pub attr_span: Span, -} - -#[derive(LintDiagnostic)] -#[diag(lint_trailing_semi_macro)] -pub(crate) struct TrailingMacro { - #[note(lint_note1)] - #[note(lint_note2)] - pub is_trailing: bool, - - pub name: Ident, -} - #[derive(LintDiagnostic)] #[diag(lint_break_with_label_and_loop)] pub(crate) struct BreakWithLabelAndLoop { diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 239b03edfaf0..40a818a3c9dc 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -621,10 +621,6 @@ pub enum DeprecatedSinceKind { pub enum BuiltinLintDiag { AbsPathWithModule(Span), ElidedLifetimesInPaths(usize, Span, bool, Span), - UnknownCrateTypes { - span: Span, - candidate: Option, - }, UnusedImports { remove_whole_use: bool, num_to_remove: usize, @@ -640,18 +636,11 @@ pub enum BuiltinLintDiag { path: String, since_kind: DeprecatedSinceKind, }, - UnusedBuiltinAttribute { - attr_name: Symbol, - macro_name: String, - invoc_span: Span, - attr_span: Span, - }, PatternsInFnsWithoutBody { span: Span, ident: Ident, is_foreign: bool, }, - OrPatternsBackCompat(Span, String), ReservedPrefix(Span, String), /// `'r#` in edition < 2021. RawPrefix(Span), @@ -660,7 +649,6 @@ pub enum BuiltinLintDiag { is_string: bool, suggestion: Span, }, - TrailingMacro(bool, Ident), BreakWithLabelAndLoop(Span), UnicodeTextFlow(Span, String), UnexpectedCfgName((Symbol, Span), Option<(Symbol, Span)>), From 9f850ceb33b2f55084362dddd2396db8469779bd Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 12 Sep 2025 01:59:39 +0900 Subject: [PATCH 502/682] fix: Infinite loop while elaborting predicates --- .../crates/hir-ty/src/dyn_compatibility.rs | 3 ++ .../hir-ty/src/dyn_compatibility/tests.rs | 3 +- .../crates/hir-ty/src/lower_nextsolver.rs | 3 ++ .../crates/hir-ty/src/next_solver/interner.rs | 23 +++++++++++++- .../hir-ty/src/tests/regression/new_solver.rs | 31 +++++++++++++++++++ 5 files changed, 61 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index d4b3751cf566..b87c99821774 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -138,6 +138,9 @@ pub fn generics_require_sized_self(db: &dyn HirDatabase, def: GenericDefId) -> b let interner = DbInterner::new_with(db, Some(krate), None); let predicates = db.generic_predicates_ns(def); + // FIXME: We should use `explicit_predicates_of` here, which hasn't been implemented to + // rust-analyzer yet + // https://github.com/rust-lang/rust/blob/ddaf12390d3ffb7d5ba74491a48f3cd528e5d777/compiler/rustc_hir_analysis/src/collect/predicates_of.rs#L490 elaborate::elaborate(interner, predicates.iter().copied()).any(|pred| { match pred.kind().skip_binder() { ClauseKind::Trait(trait_pred) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility/tests.rs index 4ffa45508497..04a9ba79921a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility/tests.rs @@ -253,7 +253,8 @@ trait Bar { trait Baz : Bar { } "#, - [("Bar", vec![]), ("Baz", vec![SizedSelf, SelfReferential])], + // FIXME: We should also report `SizedSelf` here + [("Bar", vec![]), ("Baz", vec![SelfReferential])], ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs index c6a8fa81edff..5c29befe123c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -1358,6 +1358,9 @@ where } } + // FIXME: rustc gathers more predicates by recursing through resulting trait predicates. + // See https://github.com/rust-lang/rust/blob/76c5ed2847cdb26ef2822a3a165d710f6b772217/compiler/rustc_hir_analysis/src/collect/predicates_of.rs#L689-L715 + ( GenericPredicates(predicates.is_empty().not().then(|| predicates.into())), create_diagnostics(ctx.diagnostics), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 0f512fdaf868..7b6e8a1073d7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -5,7 +5,7 @@ use base_db::Crate; use chalk_ir::{ProgramClauseImplication, SeparatorTraitRef, Variances}; use hir_def::lang_item::LangItem; use hir_def::signatures::{FieldData, FnFlags, ImplFlags, StructFlags, TraitFlags}; -use hir_def::{AdtId, BlockId, TypeAliasId, VariantId}; +use hir_def::{AdtId, BlockId, GenericDefId, TypeAliasId, VariantId}; use hir_def::{AttrDefId, Lookup}; use hir_def::{CallableDefId, EnumVariantId, ItemContainerId, StructId, UnionId}; use intern::sym::non_exhaustive; @@ -1334,6 +1334,13 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { .db() .generic_predicates_ns(def_id.0.into()) .iter() + .filter(|p| match p.kind().skip_binder() { + rustc_type_ir::ClauseKind::Trait(tr) => match tr.self_ty().kind() { + rustc_type_ir::TyKind::Param(param) => param.index == 0, + _ => false, + }, + _ => true, + }) .cloned() .map(|p| (p, Span::dummy())) .collect(); @@ -1345,10 +1352,24 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { self, def_id: Self::DefId, ) -> EarlyBinder> { + fn is_self_or_assoc(ty: Ty<'_>) -> bool { + match ty.kind() { + rustc_type_ir::TyKind::Param(param) => param.index == 0, + rustc_type_ir::TyKind::Alias(rustc_type_ir::AliasTyKind::Projection, alias) => { + is_self_or_assoc(alias.self_ty()) + } + _ => false, + } + } + let predicates: Vec<(Clause<'db>, Span)> = self .db() .generic_predicates_ns(def_id.try_into().unwrap()) .iter() + .filter(|p| match p.kind().skip_binder() { + rustc_type_ir::ClauseKind::Trait(tr) => is_self_or_assoc(tr.self_ty()), + _ => true, + }) .cloned() .map(|p| (p, Span::dummy())) .collect(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs index 4df788638a31..595f285bd930 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -116,3 +116,34 @@ fn main() { "#]], ); } + +#[test] +fn no_infinite_loop_on_super_predicates_elaboration() { + check_infer( + r#" +//- minicore: sized +trait DimMax { + type Output: Dimension; +} + +trait Dimension: DimMax<:: Smaller, Output = Self> { + type Smaller: Dimension; +} + +fn test(t: T) +where + T: DimMax, + U: Dimension, +{ + let t: >::Output = loop {}; +} +"#, + expect![[r#" + 182..183 't': T + 230..280 '{ ... {}; }': () + 240..241 't': >::Output + 270..277 'loop {}': ! + 275..277 '{}': () + "#]], + ) +} From df04be8cf744b500b5972fdc76898a96067d5517 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sun, 14 Sep 2025 13:18:19 +0000 Subject: [PATCH 503/682] Comment. --- compiler/rustc_mir_transform/src/gvn.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index f8b3e5c6f879..fc0a03b1aab5 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -154,19 +154,25 @@ impl<'tcx> crate::MirPass<'tcx> for GVN { } newtype_index! { + /// This represents a `Value` in the symbolic execution. #[debug_format = "_v{}"] struct VnIndex {} } +/// Marker type to forbid hashing and comparing opaque values. +/// This struct should only be constructed by `ValueSet::insert_unique` to ensure we use that +/// method to create non-unifiable values. It will ICE if used in `ValueSet::insert`. #[derive(Copy, Clone, Debug, Eq)] struct VnOpaque; impl PartialEq for VnOpaque { fn eq(&self, _: &VnOpaque) -> bool { + // ICE if we try to compare unique values unreachable!() } } impl Hash for VnOpaque { fn hash(&self, _: &mut T) { + // ICE if we try to hash unique values unreachable!() } } @@ -191,6 +197,8 @@ enum Value<'tcx> { // `disambiguator` is `None` iff the constant is deterministic. disambiguator: Option, }, + + // Aggregates. /// An aggregate value, either tuple/closure/struct/enum. /// This does not contain unions, as we cannot reason with the value. Aggregate(VariantIdx, Vec), @@ -252,6 +260,7 @@ impl<'tcx> ValueSet<'tcx> { } /// Insert a `(Value, Ty)` pair without hashing or deduplication. + /// This always creates a new `VnIndex`. #[inline] fn insert_unique( &mut self, From 8811344f22e0c015aaa45367b6462e3ecdcb22b5 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sun, 14 Sep 2025 13:04:53 +0000 Subject: [PATCH 504/682] Elaborate comment. --- compiler/rustc_mir_transform/src/dest_prop.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index 7e6f39881b8b..b9ab0a9feee6 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -590,8 +590,12 @@ fn save_as_intervals<'tcx>( twostep = TwoStepIndex::from_u32(twostep.as_u32() + 1); debug_assert_eq!(twostep, two_step_loc(loc, Effect::After)); append_at(&mut values, &state, twostep); - // Ensure we have a non-zero live range even for dead stores. This is done by marking - // all the written-to locals as live in the second half of the statement. + // Like terminators, ensure we have a non-zero live range even for dead stores. + // Some rvalues interleave reads and writes, for instance `Rvalue::Aggregate`, see + // https://github.com/rust-lang/rust/issues/146383. By precaution, treat statements + // as behaving so by default. + // We make an exception for simple assignments `_a.stuff = {copy|move} _b.stuff`, + // as marking `_b` live here would prevent unification. let is_simple_assignment = matches!(stmt.kind, StatementKind::Assign(box (_, Rvalue::Use(_)))); VisitPlacesWith(|place: Place<'tcx>, ctxt| { From f981739fcfdbe3e69d5fd630c4390a33ffc41035 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 4 Sep 2025 22:55:50 +0200 Subject: [PATCH 505/682] refactor `ptr_offset_with_cast` --- clippy_lints/src/declared_lints.rs | 2 +- clippy_lints/src/lib.rs | 2 - clippy_lints/src/methods/mod.rs | 49 +++++- .../src/methods/ptr_offset_with_cast.rs | 82 ++++++++++ clippy_lints/src/ptr_offset_with_cast.rs | 151 ------------------ clippy_utils/src/msrvs.rs | 2 +- tests/ui/ptr_offset_with_cast.fixed | 22 ++- tests/ui/ptr_offset_with_cast.rs | 22 ++- tests/ui/ptr_offset_with_cast.stderr | 41 ++++- 9 files changed, 209 insertions(+), 164 deletions(-) create mode 100644 clippy_lints/src/methods/ptr_offset_with_cast.rs delete mode 100644 clippy_lints/src/ptr_offset_with_cast.rs diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index d0c7443a4a4b..8d576c2263f0 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -448,6 +448,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::OR_THEN_UNWRAP_INFO, crate::methods::PATH_BUF_PUSH_OVERWRITE_INFO, crate::methods::PATH_ENDS_WITH_EXT_INFO, + crate::methods::PTR_OFFSET_WITH_CAST_INFO, crate::methods::RANGE_ZIP_WITH_LEN_INFO, crate::methods::READONLY_WRITE_LOCK_INFO, crate::methods::READ_LINE_WITHOUT_TRIM_INFO, @@ -625,7 +626,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::ptr::MUT_FROM_REF_INFO, crate::ptr::PTR_ARG_INFO, crate::ptr::PTR_EQ_INFO, - crate::ptr_offset_with_cast::PTR_OFFSET_WITH_CAST_INFO, crate::pub_underscore_fields::PUB_UNDERSCORE_FIELDS_INFO, crate::pub_use::PUB_USE_INFO, crate::question_mark::QUESTION_MARK_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 0d7931aae765..d15208285179 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -302,7 +302,6 @@ mod permissions_set_readonly_false; mod pointers_in_nomem_asm_block; mod precedence; mod ptr; -mod ptr_offset_with_cast; mod pub_underscore_fields; mod pub_use; mod question_mark; @@ -592,7 +591,6 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(unwrap::Unwrap)); store.register_late_pass(move |_| Box::new(indexing_slicing::IndexingSlicing::new(conf))); store.register_late_pass(move |tcx| Box::new(non_copy_const::NonCopyConst::new(tcx, conf))); - store.register_late_pass(|_| Box::new(ptr_offset_with_cast::PtrOffsetWithCast)); store.register_late_pass(|_| Box::new(redundant_clone::RedundantClone)); store.register_late_pass(|_| Box::new(slow_vector_initialization::SlowVectorInit)); store.register_late_pass(move |_| Box::new(unnecessary_wraps::UnnecessaryWraps::new(conf))); diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 49ca81dafc28..d06b0ed64bdc 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -91,6 +91,7 @@ mod or_fun_call; mod or_then_unwrap; mod path_buf_push_overwrite; mod path_ends_with_ext; +mod ptr_offset_with_cast; mod range_zip_with_len; mod read_line_without_trim; mod readonly_write_lock; @@ -1725,6 +1726,43 @@ declare_clippy_lint! { "Check for offset calculations on raw pointers to zero-sized types" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of the `offset` pointer method with a `usize` casted to an + /// `isize`. + /// + /// ### Why is this bad? + /// If we’re always increasing the pointer address, we can avoid the numeric + /// cast by using the `add` method instead. + /// + /// ### Example + /// ```no_run + /// let vec = vec![b'a', b'b', b'c']; + /// let ptr = vec.as_ptr(); + /// let offset = 1_usize; + /// + /// unsafe { + /// ptr.offset(offset as isize); + /// } + /// ``` + /// + /// Could be written: + /// + /// ```no_run + /// let vec = vec![b'a', b'b', b'c']; + /// let ptr = vec.as_ptr(); + /// let offset = 1_usize; + /// + /// unsafe { + /// ptr.add(offset); + /// } + /// ``` + #[clippy::version = "1.30.0"] + pub PTR_OFFSET_WITH_CAST, + complexity, + "unneeded pointer offset cast" +} + declare_clippy_lint! { /// ### What it does /// Checks for `FileType::is_file()`. @@ -4665,6 +4703,7 @@ impl_lint_pass!(Methods => [ UNINIT_ASSUMED_INIT, MANUAL_SATURATING_ARITHMETIC, ZST_OFFSET, + PTR_OFFSET_WITH_CAST, FILETYPE_IS_FILE, OPTION_AS_REF_DEREF, UNNECESSARY_LAZY_EVALUATIONS, @@ -4960,10 +4999,7 @@ impl Methods { // Handle method calls whose receiver and arguments may not come from expansion if let Some((name, recv, args, span, call_span)) = method_call(expr) { match (name, args) { - ( - sym::add | sym::offset | sym::sub | sym::wrapping_offset | sym::wrapping_add | sym::wrapping_sub, - [_arg], - ) => { + (sym::add | sym::sub | sym::wrapping_add | sym::wrapping_sub, [_arg]) => { zst_offset::check(cx, expr, recv); }, (sym::all, [arg]) => { @@ -5334,6 +5370,11 @@ impl Methods { }, _ => iter_nth_zero::check(cx, expr, recv, n_arg), }, + (sym::offset | sym::wrapping_offset, [arg]) => { + zst_offset::check(cx, expr, recv); + + ptr_offset_with_cast::check(cx, name, expr, recv, arg, self.msrv); + }, (sym::ok_or_else, [arg]) => { unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"); }, diff --git a/clippy_lints/src/methods/ptr_offset_with_cast.rs b/clippy_lints/src/methods/ptr_offset_with_cast.rs new file mode 100644 index 000000000000..d19d3b8eb89d --- /dev/null +++ b/clippy_lints/src/methods/ptr_offset_with_cast.rs @@ -0,0 +1,82 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::sym; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_span::Symbol; +use std::fmt; + +use super::PTR_OFFSET_WITH_CAST; + +pub(super) fn check( + cx: &LateContext<'_>, + method: Symbol, + expr: &Expr<'_>, + recv: &Expr<'_>, + arg: &Expr<'_>, + msrv: Msrv, +) { + // `pointer::add` and `pointer::wrapping_add` are only stable since 1.26.0. These functions + // became const-stable in 1.61.0, the same version that `pointer::offset` became const-stable. + if !msrv.meets(cx, msrvs::POINTER_ADD_SUB_METHODS) { + return; + } + + let method = match method { + sym::offset => Method::Offset, + sym::wrapping_offset => Method::WrappingOffset, + _ => return, + }; + + if !cx.typeck_results().expr_ty_adjusted(recv).is_raw_ptr() { + return; + } + + // Check if the argument to the method call is a cast from usize. + let cast_lhs_expr = match arg.kind { + ExprKind::Cast(lhs, _) if cx.typeck_results().expr_ty(lhs).is_usize() => lhs, + _ => return, + }; + + let ExprKind::MethodCall(method_name, _, _, _) = expr.kind else { + return; + }; + + let msg = format!("use of `{method}` with a `usize` casted to an `isize`"); + span_lint_and_then(cx, PTR_OFFSET_WITH_CAST, expr.span, msg, |diag| { + diag.multipart_suggestion( + format!("use `{}` instead", method.suggestion()), + vec![ + (method_name.ident.span, method.suggestion().to_string()), + (arg.span.with_lo(cast_lhs_expr.span.hi()), String::new()), + ], + Applicability::MachineApplicable, + ); + }); +} + +#[derive(Copy, Clone)] +enum Method { + Offset, + WrappingOffset, +} + +impl Method { + #[must_use] + fn suggestion(self) -> &'static str { + match self { + Self::Offset => "add", + Self::WrappingOffset => "wrapping_add", + } + } +} + +impl fmt::Display for Method { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Offset => write!(f, "offset"), + Self::WrappingOffset => write!(f, "wrapping_offset"), + } + } +} diff --git a/clippy_lints/src/ptr_offset_with_cast.rs b/clippy_lints/src/ptr_offset_with_cast.rs deleted file mode 100644 index d8d813f9846d..000000000000 --- a/clippy_lints/src/ptr_offset_with_cast.rs +++ /dev/null @@ -1,151 +0,0 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; -use clippy_utils::source::SpanRangeExt; -use clippy_utils::sym; -use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; -use std::fmt; - -declare_clippy_lint! { - /// ### What it does - /// Checks for usage of the `offset` pointer method with a `usize` casted to an - /// `isize`. - /// - /// ### Why is this bad? - /// If we’re always increasing the pointer address, we can avoid the numeric - /// cast by using the `add` method instead. - /// - /// ### Example - /// ```no_run - /// let vec = vec![b'a', b'b', b'c']; - /// let ptr = vec.as_ptr(); - /// let offset = 1_usize; - /// - /// unsafe { - /// ptr.offset(offset as isize); - /// } - /// ``` - /// - /// Could be written: - /// - /// ```no_run - /// let vec = vec![b'a', b'b', b'c']; - /// let ptr = vec.as_ptr(); - /// let offset = 1_usize; - /// - /// unsafe { - /// ptr.add(offset); - /// } - /// ``` - #[clippy::version = "1.30.0"] - pub PTR_OFFSET_WITH_CAST, - complexity, - "unneeded pointer offset cast" -} - -declare_lint_pass!(PtrOffsetWithCast => [PTR_OFFSET_WITH_CAST]); - -impl<'tcx> LateLintPass<'tcx> for PtrOffsetWithCast { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - // Check if the expressions is a ptr.offset or ptr.wrapping_offset method call - let Some((receiver_expr, arg_expr, method)) = expr_as_ptr_offset_call(cx, expr) else { - return; - }; - - // Check if the argument to the method call is a cast from usize - let Some(cast_lhs_expr) = expr_as_cast_from_usize(cx, arg_expr) else { - return; - }; - - let msg = format!("use of `{method}` with a `usize` casted to an `isize`"); - if let Some(sugg) = build_suggestion(cx, method, receiver_expr, cast_lhs_expr) { - span_lint_and_sugg( - cx, - PTR_OFFSET_WITH_CAST, - expr.span, - msg, - "try", - sugg, - Applicability::MachineApplicable, - ); - } else { - span_lint(cx, PTR_OFFSET_WITH_CAST, expr.span, msg); - } - } -} - -// If the given expression is a cast from a usize, return the lhs of the cast -fn expr_as_cast_from_usize<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { - if let ExprKind::Cast(cast_lhs_expr, _) = expr.kind - && is_expr_ty_usize(cx, cast_lhs_expr) - { - return Some(cast_lhs_expr); - } - None -} - -// If the given expression is a ptr::offset or ptr::wrapping_offset method call, return the -// receiver, the arg of the method call, and the method. -fn expr_as_ptr_offset_call<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, -) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Method)> { - if let ExprKind::MethodCall(path_segment, arg_0, [arg_1], _) = &expr.kind - && is_expr_ty_raw_ptr(cx, arg_0) - { - if path_segment.ident.name == sym::offset { - return Some((arg_0, arg_1, Method::Offset)); - } - if path_segment.ident.name == sym::wrapping_offset { - return Some((arg_0, arg_1, Method::WrappingOffset)); - } - } - None -} - -// Is the type of the expression a usize? -fn is_expr_ty_usize(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - cx.typeck_results().expr_ty(expr) == cx.tcx.types.usize -} - -// Is the type of the expression a raw pointer? -fn is_expr_ty_raw_ptr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - cx.typeck_results().expr_ty(expr).is_raw_ptr() -} - -fn build_suggestion( - cx: &LateContext<'_>, - method: Method, - receiver_expr: &Expr<'_>, - cast_lhs_expr: &Expr<'_>, -) -> Option { - let receiver = receiver_expr.span.get_source_text(cx)?; - let cast_lhs = cast_lhs_expr.span.get_source_text(cx)?; - Some(format!("{receiver}.{}({cast_lhs})", method.suggestion())) -} - -#[derive(Copy, Clone)] -enum Method { - Offset, - WrappingOffset, -} - -impl Method { - #[must_use] - fn suggestion(self) -> &'static str { - match self { - Self::Offset => "add", - Self::WrappingOffset => "wrapping_add", - } - } -} - -impl fmt::Display for Method { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Offset => write!(f, "offset"), - Self::WrappingOffset => write!(f, "wrapping_offset"), - } - } -} diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 896d607fbcdd..21dff28d5650 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -73,7 +73,7 @@ msrv_aliases! { 1,29,0 { ITER_FLATTEN } 1,28,0 { FROM_BOOL, REPEAT_WITH, SLICE_FROM_REF } 1,27,0 { ITERATOR_TRY_FOLD } - 1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN } + 1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN, POINTER_ADD_SUB_METHODS } 1,24,0 { IS_ASCII_DIGIT, PTR_NULL } 1,18,0 { HASH_MAP_RETAIN, HASH_SET_RETAIN } 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR } diff --git a/tests/ui/ptr_offset_with_cast.fixed b/tests/ui/ptr_offset_with_cast.fixed index 4fe9dcf46c35..42d1abeaa05a 100644 --- a/tests/ui/ptr_offset_with_cast.fixed +++ b/tests/ui/ptr_offset_with_cast.fixed @@ -1,4 +1,4 @@ -#![allow(clippy::unnecessary_cast, clippy::useless_vec)] +#![expect(clippy::unnecessary_cast, clippy::useless_vec, clippy::needless_borrow)] fn main() { let vec = vec![b'a', b'b', b'c']; @@ -18,5 +18,25 @@ fn main() { //~^ ptr_offset_with_cast let _ = ptr.wrapping_offset(offset_isize as isize); let _ = ptr.wrapping_offset(offset_u8 as isize); + + let _ = S.offset(offset_usize as isize); + let _ = S.wrapping_offset(offset_usize as isize); + + let _ = (&ptr).add(offset_usize); + //~^ ptr_offset_with_cast + let _ = (&ptr).wrapping_add(offset_usize); + //~^ ptr_offset_with_cast + } +} + +#[derive(Clone, Copy)] +struct S; + +impl S { + fn offset(self, _: isize) -> Self { + self + } + fn wrapping_offset(self, _: isize) -> Self { + self } } diff --git a/tests/ui/ptr_offset_with_cast.rs b/tests/ui/ptr_offset_with_cast.rs index a1fb892733d3..6d06a6af1fa2 100644 --- a/tests/ui/ptr_offset_with_cast.rs +++ b/tests/ui/ptr_offset_with_cast.rs @@ -1,4 +1,4 @@ -#![allow(clippy::unnecessary_cast, clippy::useless_vec)] +#![expect(clippy::unnecessary_cast, clippy::useless_vec, clippy::needless_borrow)] fn main() { let vec = vec![b'a', b'b', b'c']; @@ -18,5 +18,25 @@ fn main() { //~^ ptr_offset_with_cast let _ = ptr.wrapping_offset(offset_isize as isize); let _ = ptr.wrapping_offset(offset_u8 as isize); + + let _ = S.offset(offset_usize as isize); + let _ = S.wrapping_offset(offset_usize as isize); + + let _ = (&ptr).offset(offset_usize as isize); + //~^ ptr_offset_with_cast + let _ = (&ptr).wrapping_offset(offset_usize as isize); + //~^ ptr_offset_with_cast + } +} + +#[derive(Clone, Copy)] +struct S; + +impl S { + fn offset(self, _: isize) -> Self { + self + } + fn wrapping_offset(self, _: isize) -> Self { + self } } diff --git a/tests/ui/ptr_offset_with_cast.stderr b/tests/ui/ptr_offset_with_cast.stderr index dcd5e027d182..022b3286c93b 100644 --- a/tests/ui/ptr_offset_with_cast.stderr +++ b/tests/ui/ptr_offset_with_cast.stderr @@ -2,16 +2,51 @@ error: use of `offset` with a `usize` casted to an `isize` --> tests/ui/ptr_offset_with_cast.rs:12:17 | LL | let _ = ptr.offset(offset_usize as isize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.add(offset_usize)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::ptr-offset-with-cast` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::ptr_offset_with_cast)]` +help: use `add` instead + | +LL - let _ = ptr.offset(offset_usize as isize); +LL + let _ = ptr.add(offset_usize); + | error: use of `wrapping_offset` with a `usize` casted to an `isize` --> tests/ui/ptr_offset_with_cast.rs:17:17 | LL | let _ = ptr.wrapping_offset(offset_usize as isize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.wrapping_add(offset_usize)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `wrapping_add` instead + | +LL - let _ = ptr.wrapping_offset(offset_usize as isize); +LL + let _ = ptr.wrapping_add(offset_usize); + | -error: aborting due to 2 previous errors +error: use of `offset` with a `usize` casted to an `isize` + --> tests/ui/ptr_offset_with_cast.rs:25:17 + | +LL | let _ = (&ptr).offset(offset_usize as isize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `add` instead + | +LL - let _ = (&ptr).offset(offset_usize as isize); +LL + let _ = (&ptr).add(offset_usize); + | + +error: use of `wrapping_offset` with a `usize` casted to an `isize` + --> tests/ui/ptr_offset_with_cast.rs:27:17 + | +LL | let _ = (&ptr).wrapping_offset(offset_usize as isize); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `wrapping_add` instead + | +LL - let _ = (&ptr).wrapping_offset(offset_usize as isize); +LL + let _ = (&ptr).wrapping_add(offset_usize); + | + +error: aborting due to 4 previous errors From ce859d7713dbca1b2a3dff17ccc4d4a0598bffbf Mon Sep 17 00:00:00 2001 From: Sidney Cammeresi Date: Sun, 14 Sep 2025 08:06:11 -0700 Subject: [PATCH 506/682] Switch `std::vec::PeekMut::pop` from self to this parameter. Since PeekMut implements Deref, it shouldn't have any methods of its own. See also: `std::collections::binary_heap::PeekMut::pop` --- .mailmap | 1 + library/alloc/src/vec/peek_mut.rs | 4 ++-- library/alloctests/tests/vec.rs | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.mailmap b/.mailmap index 6e3eed1226e0..0f7bc5e38bd1 100644 --- a/.mailmap +++ b/.mailmap @@ -609,6 +609,7 @@ Shohei Wada Shotaro Yamada Shotaro Yamada Shyam Sundar B +Sidney Cammeresi Simon Barber-Dueck Simon BD Simon Sapin Simonas Kazlauskas Simonas Kazlauskas diff --git a/library/alloc/src/vec/peek_mut.rs b/library/alloc/src/vec/peek_mut.rs index c0dd941ed393..caeaf2799d73 100644 --- a/library/alloc/src/vec/peek_mut.rs +++ b/library/alloc/src/vec/peek_mut.rs @@ -29,9 +29,9 @@ impl<'a, T> PeekMut<'a, T> { /// Removes the peeked value from the vector and returns it. #[unstable(feature = "vec_peek_mut", issue = "122742")] - pub fn pop(self) -> T { + pub fn pop(this: Self) -> T { // SAFETY: PeekMut is only constructed if the vec is non-empty - unsafe { self.vec.pop().unwrap_unchecked() } + unsafe { this.vec.pop().unwrap_unchecked() } } } diff --git a/library/alloctests/tests/vec.rs b/library/alloctests/tests/vec.rs index 00f640cd17ea..404eb49e1ea9 100644 --- a/library/alloctests/tests/vec.rs +++ b/library/alloctests/tests/vec.rs @@ -15,7 +15,7 @@ use std::ops::Bound::*; use std::panic::{AssertUnwindSafe, catch_unwind}; use std::rc::Rc; use std::sync::atomic::{AtomicU32, Ordering}; -use std::vec::{Drain, IntoIter}; +use std::vec::{Drain, IntoIter, PeekMut}; use crate::testing::macros::struct_with_counted_drop; @@ -2647,7 +2647,7 @@ fn test_peek_mut() { assert_eq!(*p, 2); *p = 0; assert_eq!(*p, 0); - p.pop(); + PeekMut::pop(p); assert_eq!(vec.len(), 1); } else { unreachable!() From 8af66386660b17ec85a34705f8a475fd0874ac70 Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Sat, 13 Sep 2025 14:01:24 +0500 Subject: [PATCH 507/682] Changelog for Clippy 1.90 --- CHANGELOG.md | 147 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 146 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb2a76a81836..4a9be0214372 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,152 @@ document. ## Unreleased / Beta / In Rust Nightly -[4ef75291...master](https://github.com/rust-lang/rust-clippy/compare/4ef75291...master) +[e9b7045...master](https://github.com/rust-lang/rust-clippy/compare/e9b7045...master) + +## Rust 1.90 + +Current stable, released 2025-09-18 + +[View all 118 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2025-06-13T15%3A55%3A04Z..2025-07-25T13%3A24%3A00Z+base%3Amaster) + +Note: This Clippy release does not introduce many new lints and is focused entirely on bug fixes — see +[#15086](https://github.com/rust-lang/rust-clippy/issues/15086) for more details. + +## New Lints + +* Added [`manual_is_multiple_of`] to `complexity` [#14292](https://github.com/rust-lang/rust-clippy/pull/14292) +* Added [`doc_broken_link`] to `pedantic` [#13696](https://github.com/rust-lang/rust-clippy/pull/13696) + +### Moves and Deprecations + +* Move [`uninlined_format_args`] to `pedantic` (from `style`, now allow-by-default) [#15287](https://github.com/rust-lang/rust-clippy/pull/15287) + +### Enhancements + +* [`or_fun_call`] now lints `Option::get_or_insert`, `Result::map_or`, `Option/Result::and` methods + [#15071](https://github.com/rust-lang/rust-clippy/pull/15071) + [#15073](https://github.com/rust-lang/rust-clippy/pull/15073) + [#15074](https://github.com/rust-lang/rust-clippy/pull/15074) +* [`incompatible_msrv`] now recognizes types exceeding MSRV + [#15296](https://github.com/rust-lang/rust-clippy/pull/15296) +* [`incompatible_msrv`] now checks the right MSRV when in a `const` context + [#15297](https://github.com/rust-lang/rust-clippy/pull/15297) +* [`zero_ptr`] now lints in `const` context as well + [#15152](https://github.com/rust-lang/rust-clippy/pull/15152) +* [`map_identity`],[`flat_map_identity`] now recognizes `|[x, y]| [x, y]` as an identity function + [#15229](https://github.com/rust-lang/rust-clippy/pull/15229) +* [`exit`] no longer fails on the main function when using `--test` or `--all-targets` flag + [#15222](https://github.com/rust-lang/rust-clippy/pull/15222) + +### False Positive Fixes + +* [`if_then_some_else_none`] fixed FP when require type coercion + [#15267](https://github.com/rust-lang/rust-clippy/pull/15267) +* [`module_name_repetitions`] fixed FP on exported macros + [#15319](https://github.com/rust-lang/rust-clippy/pull/15319) +* [`unused_async`] fixed FP on function with `todo!` + [#15308](https://github.com/rust-lang/rust-clippy/pull/15308) +* [`useless_attribute`] fixed FP when using `#[expect(redundant_imports)]` and similar lint attributes + on `use` statements + [#15318](https://github.com/rust-lang/rust-clippy/pull/15318) +* [`pattern_type_mismatch`] fixed FP in external macro + [#15306](https://github.com/rust-lang/rust-clippy/pull/15306) +* [`large_enum_variant`] fixed FP by not suggesting `Box` in `no_std` mode + [#15241](https://github.com/rust-lang/rust-clippy/pull/15241) +* [`missing_inline_in_public_items`] fixed FP on functions with `extern` + [#15313](https://github.com/rust-lang/rust-clippy/pull/15313) +* [`needless_range_loop`] fixed FP on array literals + [#15314](https://github.com/rust-lang/rust-clippy/pull/15314) +* [`ptr_as_ptr`] fixed wrong suggestions with turbo fish + [#15289](https://github.com/rust-lang/rust-clippy/pull/15289) +* [`range_plus_one`], [`range_minus_one`] fixed FP by restricting lint to cases where it is safe + to switch the range type + [#14432](https://github.com/rust-lang/rust-clippy/pull/14432) +* [`ptr_arg`] fixed FP with underscore binding to `&T` or `&mut T` argument + [#15105](https://github.com/rust-lang/rust-clippy/pull/15105) +* [`unsafe_derive_deserialize`] fixed FP caused by the standard library's `pin!()` macro + [#15137](https://github.com/rust-lang/rust-clippy/pull/15137) +* [`unused_trait_names`] fixed FP in macros + [#14947](https://github.com/rust-lang/rust-clippy/pull/14947) +* [`expect_fun_call`] fixed invalid suggestions + [#15122](https://github.com/rust-lang/rust-clippy/pull/15122) +* [`manual_is_multiple_of`] fixed various false positives + [#15205](https://github.com/rust-lang/rust-clippy/pull/15205) +* [`manual_assert`] fixed wrong suggestions for macros + [#15264](https://github.com/rust-lang/rust-clippy/pull/15264) +* [`expect_used`] fixed false negative when meeting `Option::ok().expect` + [#15253](https://github.com/rust-lang/rust-clippy/pull/15253) +* [`manual_abs_diff`] fixed wrong suggestions behind refs + [#15265](https://github.com/rust-lang/rust-clippy/pull/15265) +* [`approx_constant`] fixed FP with overly precise literals and non trivially formatted numerals + [#15236](https://github.com/rust-lang/rust-clippy/pull/15236) +* [`arithmetic_side_effects`] fixed FP on `NonZeroU*.get() - 1` + [#15238](https://github.com/rust-lang/rust-clippy/pull/15238) +* [`legacy_numeric_constants`] fixed suggestion when call is inside parentheses + [#15191](https://github.com/rust-lang/rust-clippy/pull/15191) +* [`manual_is_variant_and`] fixed inverted suggestions that could lead to code with different semantics + [#15206](https://github.com/rust-lang/rust-clippy/pull/15206) +* [`unnecessary_map_or`] fixed FP by not proposing to replace `map_or` call when types wouldn't be correct + [#15181](https://github.com/rust-lang/rust-clippy/pull/15181) +* [`return_and_then`] fixed FP in case of a partially used expression + [#15115](https://github.com/rust-lang/rust-clippy/pull/15115) +* [`manual_let_else`] fixed FP with binding subpattern with unused name + [#15118](https://github.com/rust-lang/rust-clippy/pull/15118) +* [`suboptimal_flops`] fixed FP with ambiguous float types in mul_add suggestions + [#15133](https://github.com/rust-lang/rust-clippy/pull/15133) +* [`borrow_as_ptr`] fixed FP by not removing cast to trait object pointer + [#15145](https://github.com/rust-lang/rust-clippy/pull/15145) +* [`unnecessary_operation`] fixed FP by not removing casts if they are useful to type the expression + [#15182](https://github.com/rust-lang/rust-clippy/pull/15182) +* [`empty_loop`] fixed FP on intrinsic function declaration + [#15201](https://github.com/rust-lang/rust-clippy/pull/15201) +* [`doc_nested_refdefs`] fixed FP where task lists are reported as refdefs + [#15146](https://github.com/rust-lang/rust-clippy/pull/15146) +* [`std_instead_of_core`] fixed FP when not all items come from the new crate + [#15165](https://github.com/rust-lang/rust-clippy/pull/15165) +* [`swap_with_temporary`] fixed FP leading to different semantics being suggested + [#15172](https://github.com/rust-lang/rust-clippy/pull/15172) +* [`cast_possible_truncation`] fixed FP by not giving suggestions inside const context + [#15164](https://github.com/rust-lang/rust-clippy/pull/15164) +* [`missing_panics_doc`] fixed FP by allowing unwrap() and expect()s in const-only contexts + [#15170](https://github.com/rust-lang/rust-clippy/pull/15170) +* [`disallowed_script_idents`] fixed FP on identifiers with `_` + [#15123](https://github.com/rust-lang/rust-clippy/pull/15123) +* [`wildcard_enum_match_arm`] fixed wrong suggestions with raw identifiers + [#15093](https://github.com/rust-lang/rust-clippy/pull/15093) +* [`borrow_deref_ref`] fixed FP by not proposing replacing a reborrow when the reborrow is itself mutably borrowed + [#14967](https://github.com/rust-lang/rust-clippy/pull/14967) +* [`manual_ok_err`] fixed wrong suggestions with references + [#15053](https://github.com/rust-lang/rust-clippy/pull/15053) +* [`identity_op`] fixed FP when encountering `Default::default()` + [#15028](https://github.com/rust-lang/rust-clippy/pull/15028) +* [`exhaustive_structs`] fixed FP on structs with default valued field + [#15022](https://github.com/rust-lang/rust-clippy/pull/15022) +* [`collapsible_else_if`] fixed FP on conditionally compiled stmt + [#14906](https://github.com/rust-lang/rust-clippy/pull/14906) +* [`missing_const_for_fn`] fixed FP by checking MSRV before emitting lint on function containing + non-`Sized` trait bounds + [#15080](https://github.com/rust-lang/rust-clippy/pull/15080) +* [`question_mark`] fixed FP when else branch of let-else contains `#[cfg]` + [#15082](https://github.com/rust-lang/rust-clippy/pull/15082) + +### ICE Fixes + +* [`single_match`] fixed ICE with deref patterns and string literals + [#15124](https://github.com/rust-lang/rust-clippy/pull/15124) +* [`needless_doctest_main`] fixed panic when doctest is invalid + [#15052](https://github.com/rust-lang/rust-clippy/pull/15052) +* [`zero_sized_map_values`] do not attempt to compute size of a type with escaping lifetimes + [#15434](https://github.com/rust-lang/rust-clippy/pull/15434) + +### Documentation Improvements + +* [`manual_is_variant_and`] improved documentation to include equality comparison patterns + [#15239](https://github.com/rust-lang/rust-clippy/pull/15239) +* [`uninlined_format_args`] improved documentation with example of how to fix a `{:?}` parameter + [#15228](https://github.com/rust-lang/rust-clippy/pull/15228) +* [`undocumented_unsafe_blocks`] improved documentation wording + [#15213](https://github.com/rust-lang/rust-clippy/pull/15213) ## Rust 1.89 From 572b42346482472736723fe433b417fe32a01800 Mon Sep 17 00:00:00 2001 From: Alan Somers Date: Sun, 14 Sep 2025 11:33:11 -0600 Subject: [PATCH 508/682] On FreeBSD, use readdir instead of readdir_r readdir_r has the same problems on FreeBSD as it does on other platforms: it assumes a fixed NAME_MAX. And readdir has the same thread-safety guarantee as it does on other platforms: it's safe as long as only one thread tries to read from the directory stream at a given time. Furthermore, readdir_r is likely to be removed for FreeBSD 16, so we should stop using it now. --- library/std/src/sys/fs/unix.rs | 148 +++++++++++++++++---------------- 1 file changed, 77 insertions(+), 71 deletions(-) diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index dfd6ce56a768..33a1e7ff5e40 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -21,29 +21,31 @@ use libc::fstatat as fstatat64; #[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "hurd"))] use libc::fstatat64; #[cfg(any( - target_os = "android", - target_os = "solaris", - target_os = "fuchsia", - target_os = "redox", - target_os = "illumos", target_os = "aix", + target_os = "android", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "illumos", target_os = "nto", + target_os = "redox", + target_os = "solaris", target_os = "vita", all(target_os = "linux", target_env = "musl"), ))] use libc::readdir as readdir64; #[cfg(not(any( + target_os = "aix", target_os = "android", - target_os = "linux", - target_os = "solaris", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "hurd", target_os = "illumos", target_os = "l4re", - target_os = "fuchsia", - target_os = "redox", - target_os = "aix", + target_os = "linux", target_os = "nto", + target_os = "redox", + target_os = "solaris", target_os = "vita", - target_os = "hurd", )))] use libc::readdir_r as readdir64_r; #[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "hurd"))] @@ -271,16 +273,17 @@ unsafe impl Send for Dir {} unsafe impl Sync for Dir {} #[cfg(any( - target_os = "android", - target_os = "linux", - target_os = "solaris", - target_os = "illumos", - target_os = "fuchsia", - target_os = "redox", target_os = "aix", - target_os = "nto", - target_os = "vita", + target_os = "android", + target_os = "freebsd", + target_os = "fuchsia", target_os = "hurd", + target_os = "illumos", + target_os = "linux", + target_os = "nto", + target_os = "redox", + target_os = "solaris", + target_os = "vita", ))] pub struct DirEntry { dir: Arc, @@ -295,16 +298,17 @@ pub struct DirEntry { // we're not using the immediate `d_name` on these targets. Keeping this as an // `entry` field in `DirEntry` helps reduce the `cfg` boilerplate elsewhere. #[cfg(any( - target_os = "android", - target_os = "linux", - target_os = "solaris", - target_os = "illumos", - target_os = "fuchsia", - target_os = "redox", target_os = "aix", - target_os = "nto", - target_os = "vita", + target_os = "android", + target_os = "freebsd", + target_os = "fuchsia", target_os = "hurd", + target_os = "illumos", + target_os = "linux", + target_os = "nto", + target_os = "redox", + target_os = "solaris", + target_os = "vita", ))] struct dirent64_min { d_ino: u64, @@ -319,16 +323,17 @@ struct dirent64_min { } #[cfg(not(any( - target_os = "android", - target_os = "linux", - target_os = "solaris", - target_os = "illumos", - target_os = "fuchsia", - target_os = "redox", target_os = "aix", - target_os = "nto", - target_os = "vita", + target_os = "android", + target_os = "freebsd", + target_os = "fuchsia", target_os = "hurd", + target_os = "illumos", + target_os = "linux", + target_os = "nto", + target_os = "redox", + target_os = "solaris", + target_os = "vita", )))] pub struct DirEntry { dir: Arc, @@ -698,16 +703,17 @@ impl Iterator for ReadDir { type Item = io::Result; #[cfg(any( - target_os = "android", - target_os = "linux", - target_os = "solaris", - target_os = "fuchsia", - target_os = "redox", - target_os = "illumos", target_os = "aix", - target_os = "nto", - target_os = "vita", + target_os = "android", + target_os = "freebsd", + target_os = "fuchsia", target_os = "hurd", + target_os = "illumos", + target_os = "linux", + target_os = "nto", + target_os = "redox", + target_os = "solaris", + target_os = "vita", ))] fn next(&mut self) -> Option> { use crate::sys::os::{errno, set_errno}; @@ -768,6 +774,9 @@ impl Iterator for ReadDir { // only access those bytes. #[cfg(not(target_os = "vita"))] let entry = dirent64_min { + #[cfg(target_os = "freebsd")] + d_ino: (*entry_ptr).d_fileno, + #[cfg(not(target_os = "freebsd"))] d_ino: (*entry_ptr).d_ino as u64, #[cfg(not(any( target_os = "solaris", @@ -791,16 +800,17 @@ impl Iterator for ReadDir { } #[cfg(not(any( - target_os = "android", - target_os = "linux", - target_os = "solaris", - target_os = "fuchsia", - target_os = "redox", - target_os = "illumos", target_os = "aix", - target_os = "nto", - target_os = "vita", + target_os = "android", + target_os = "freebsd", + target_os = "fuchsia", target_os = "hurd", + target_os = "illumos", + target_os = "linux", + target_os = "nto", + target_os = "redox", + target_os = "solaris", + target_os = "vita", )))] fn next(&mut self) -> Option> { if self.end_of_stream { @@ -970,36 +980,32 @@ impl DirEntry { } #[cfg(any( - target_os = "linux", + target_os = "aix", + target_os = "android", target_os = "cygwin", target_os = "emscripten", - target_os = "android", - target_os = "solaris", - target_os = "illumos", - target_os = "haiku", - target_os = "l4re", - target_os = "fuchsia", - target_os = "redox", - target_os = "vxworks", target_os = "espidf", + target_os = "freebsd", + target_os = "fuchsia", + target_os = "haiku", target_os = "horizon", - target_os = "vita", - target_os = "aix", - target_os = "nto", target_os = "hurd", + target_os = "illumos", + target_os = "l4re", + target_os = "linux", + target_os = "nto", + target_os = "redox", target_os = "rtems", + target_os = "solaris", + target_os = "vita", + target_os = "vxworks", target_vendor = "apple", ))] pub fn ino(&self) -> u64 { self.entry.d_ino as u64 } - #[cfg(any( - target_os = "freebsd", - target_os = "openbsd", - target_os = "netbsd", - target_os = "dragonfly" - ))] + #[cfg(any(target_os = "openbsd", target_os = "netbsd", target_os = "dragonfly"))] pub fn ino(&self) -> u64 { self.entry.d_fileno as u64 } @@ -1014,7 +1020,6 @@ impl DirEntry { #[cfg(any( target_os = "netbsd", target_os = "openbsd", - target_os = "freebsd", target_os = "dragonfly", target_vendor = "apple", ))] @@ -1030,7 +1035,6 @@ impl DirEntry { #[cfg(not(any( target_os = "netbsd", target_os = "openbsd", - target_os = "freebsd", target_os = "dragonfly", target_vendor = "apple", )))] @@ -1040,6 +1044,7 @@ impl DirEntry { #[cfg(not(any( target_os = "android", + target_os = "freebsd", target_os = "linux", target_os = "solaris", target_os = "illumos", @@ -1055,6 +1060,7 @@ impl DirEntry { } #[cfg(any( target_os = "android", + target_os = "freebsd", target_os = "linux", target_os = "solaris", target_os = "illumos", From 5025bfa1466488a879bd83f8a022362970182ce4 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 10 Sep 2025 20:34:02 +0300 Subject: [PATCH 509/682] Improve `rust-analyzer diagnostics` In my experience the `processing ` messages make it harder to search for the actual diagnostics, so remove them and instead print the filename only if there is a diagnostic. Also allow choosing the minimum severity. --- .../rust-analyzer/src/cli/diagnostics.rs | 42 +++++++++++++------ .../crates/rust-analyzer/src/cli/flags.rs | 24 +++++++++++ 2 files changed, 54 insertions(+), 12 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs index 7b12cb14009f..82590c8e707f 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs @@ -9,7 +9,7 @@ use ide::{AnalysisHost, AssistResolveStrategy, Diagnostic, DiagnosticsConfig, Se use ide_db::{LineIndexDatabase, base_db::SourceDatabase}; use load_cargo::{LoadCargoConfig, ProcMacroServerChoice, load_workspace_at}; -use crate::cli::flags; +use crate::cli::{flags, progress_report::ProgressReport}; impl flags::Diagnostics { pub fn run(self) -> anyhow::Result<()> { @@ -50,23 +50,26 @@ impl flags::Diagnostics { let mut found_error = false; let mut visited_files = FxHashSet::default(); + let min_severity = self.severity.unwrap_or(flags::Severity::Weak); - let work = all_modules(db).into_iter().filter(|module| { - let file_id = module.definition_source_file_id(db).original_file(db); - let source_root = db.file_source_root(file_id.file_id(db)).source_root_id(db); - let source_root = db.source_root(source_root).source_root(db); - !source_root.is_library - }); + let work = all_modules(db) + .into_iter() + .filter(|module| { + let file_id = module.definition_source_file_id(db).original_file(db); + let source_root = db.file_source_root(file_id.file_id(db)).source_root_id(db); + let source_root = db.source_root(source_root).source_root(db); + !source_root.is_library + }) + .collect::>(); + let mut bar = ProgressReport::new(work.len()); for module in work { let file_id = module.definition_source_file_id(db).original_file(db); if !visited_files.contains(&file_id) { + let message = format!("processing {}", _vfs.file_path(file_id.file_id(db))); + bar.set_message(move || message.clone()); let crate_name = module.krate().display_name(db).as_deref().unwrap_or(&sym::unknown).to_owned(); - println!( - "processing crate: {crate_name}, module: {}", - _vfs.file_path(file_id.file_id(db)) - ); for diagnostic in analysis .full_diagnostics( &DiagnosticsConfig::test_sample(), @@ -75,6 +78,16 @@ impl flags::Diagnostics { ) .unwrap() { + let severity = match diagnostic.severity { + Severity::Error => flags::Severity::Error, + Severity::Warning => flags::Severity::Warning, + Severity::WeakWarning => flags::Severity::Weak, + Severity::Allow => continue, + }; + if severity < min_severity { + continue; + } + if matches!(diagnostic.severity, Severity::Error) { found_error = true; } @@ -83,12 +96,17 @@ impl flags::Diagnostics { let line_index = db.line_index(range.file_id); let start = line_index.line_col(range.range.start()); let end = line_index.line_col(range.range.end()); - println!("{severity:?} {code:?} from {start:?} to {end:?}: {message}"); + bar.println(format!( + "at crate {crate_name}, file {}: {severity:?} {code:?} from {start:?} to {end:?}: {message}", + _vfs.file_path(file_id.file_id(db)) + )); } visited_files.insert(file_id); } + bar.inc(1); } + bar.finish_and_clear(); println!(); println!("diagnostic scan complete"); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs index 16f351272b69..75030bedfca3 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs @@ -124,6 +124,9 @@ xflags::xflags! { optional --disable-proc-macros /// Run the proc-macro-srv binary at the specified path. optional --proc-macro-srv path: PathBuf + + /// The minimum severity. + optional --severity severity: Severity } /// Report unresolved references @@ -281,6 +284,7 @@ pub struct Diagnostics { pub disable_build_scripts: bool, pub disable_proc_macros: bool, pub proc_macro_srv: Option, + pub severity: Option, } #[derive(Debug)] @@ -376,3 +380,23 @@ impl FromStr for OutputFormat { } } } + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum Severity { + Weak, + Warning, + Error, +} + +impl FromStr for Severity { + type Err = String; + + fn from_str(s: &str) -> Result { + match &*s.to_ascii_lowercase() { + "weak" => Ok(Self::Weak), + "warning" => Ok(Self::Warning), + "error" => Ok(Self::Error), + _ => Err(format!("unknown severity `{s}`")), + } + } +} From 927c4c03194da284404985747c2cad25fa1f5066 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Sun, 14 Sep 2025 17:55:33 -0400 Subject: [PATCH 510/682] Fix typo in error message --- compiler/rustc_middle/messages.ftl | 2 +- tests/ui/consts/const_refs_to_static_fail.stderr | 2 +- tests/ui/consts/const_refs_to_static_fail_invalid.stderr | 4 ++-- .../ui/consts/miri_unleashed/const_refers_to_static.stderr | 2 +- .../const_refers_to_static_cross_crate.stderr | 6 +++--- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_middle/messages.ftl b/compiler/rustc_middle/messages.ftl index 69adb2fe3919..279ab9a9d8fb 100644 --- a/compiler/rustc_middle/messages.ftl +++ b/compiler/rustc_middle/messages.ftl @@ -84,7 +84,7 @@ middle_failed_writing_file = # Note: We only mention patterns here since the error can only occur with references, and those # are forbidden in const generics. middle_invalid_const_in_valtree = constant {$global_const_id} cannot be used as pattern - .note = constants that reference mutable or external memory cannot be used as pattern + .note = constants that reference mutable or external memory cannot be used as patterns middle_layout_cycle = a cycle occurred during layout computation diff --git a/tests/ui/consts/const_refs_to_static_fail.stderr b/tests/ui/consts/const_refs_to_static_fail.stderr index c567b3e0ce1f..2bb6d2b8fef7 100644 --- a/tests/ui/consts/const_refs_to_static_fail.stderr +++ b/tests/ui/consts/const_refs_to_static_fail.stderr @@ -16,7 +16,7 @@ error: constant BAD_PATTERN cannot be used as pattern LL | BAD_PATTERN => {}, | ^^^^^^^^^^^ | - = note: constants that reference mutable or external memory cannot be used as pattern + = note: constants that reference mutable or external memory cannot be used as patterns error: aborting due to 3 previous errors diff --git a/tests/ui/consts/const_refs_to_static_fail_invalid.stderr b/tests/ui/consts/const_refs_to_static_fail_invalid.stderr index f9088c318a62..226a9de285dc 100644 --- a/tests/ui/consts/const_refs_to_static_fail_invalid.stderr +++ b/tests/ui/consts/const_refs_to_static_fail_invalid.stderr @@ -15,7 +15,7 @@ error: constant extern_::C cannot be used as pattern LL | C => {} | ^ | - = note: constants that reference mutable or external memory cannot be used as pattern + = note: constants that reference mutable or external memory cannot be used as patterns error: constant mutable::C cannot be used as pattern --> $DIR/const_refs_to_static_fail_invalid.rs:42:9 @@ -23,7 +23,7 @@ error: constant mutable::C cannot be used as pattern LL | C => {} | ^ | - = note: constants that reference mutable or external memory cannot be used as pattern + = note: constants that reference mutable or external memory cannot be used as patterns error: aborting due to 3 previous errors diff --git a/tests/ui/consts/miri_unleashed/const_refers_to_static.stderr b/tests/ui/consts/miri_unleashed/const_refers_to_static.stderr index 6b70a211a72c..5b8797c51162 100644 --- a/tests/ui/consts/miri_unleashed/const_refers_to_static.stderr +++ b/tests/ui/consts/miri_unleashed/const_refers_to_static.stderr @@ -22,7 +22,7 @@ error: constant REF_INTERIOR_MUT cannot be used as pattern LL | REF_INTERIOR_MUT => {}, | ^^^^^^^^^^^^^^^^ | - = note: constants that reference mutable or external memory cannot be used as pattern + = note: constants that reference mutable or external memory cannot be used as patterns warning: skipping const checks | diff --git a/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr b/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr index d753506cc94e..c2b730375f2c 100644 --- a/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr +++ b/tests/ui/consts/miri_unleashed/const_refers_to_static_cross_crate.stderr @@ -10,7 +10,7 @@ error: constant SLICE_MUT cannot be used as pattern LL | SLICE_MUT => true, | ^^^^^^^^^ | - = note: constants that reference mutable or external memory cannot be used as pattern + = note: constants that reference mutable or external memory cannot be used as patterns error: constant U8_MUT cannot be used as pattern --> $DIR/const_refers_to_static_cross_crate.rs:44:9 @@ -18,7 +18,7 @@ error: constant U8_MUT cannot be used as pattern LL | U8_MUT => true, | ^^^^^^ | - = note: constants that reference mutable or external memory cannot be used as pattern + = note: constants that reference mutable or external memory cannot be used as patterns error: constant U8_MUT2 cannot be used as pattern --> $DIR/const_refers_to_static_cross_crate.rs:53:9 @@ -26,7 +26,7 @@ error: constant U8_MUT2 cannot be used as pattern LL | U8_MUT2 => true, | ^^^^^^^ | - = note: constants that reference mutable or external memory cannot be used as pattern + = note: constants that reference mutable or external memory cannot be used as patterns error: aborting due to 4 previous errors From 37d058fa048acd6bf385677f7cdb1e020ceb0769 Mon Sep 17 00:00:00 2001 From: clubby789 Date: Sun, 14 Sep 2025 23:03:56 +0100 Subject: [PATCH 511/682] tidy: Remove `WorkspaceInfo` constructor --- src/tools/tidy/src/deps.rs | 150 ++++++++++++++++++++++--------------- 1 file changed, 90 insertions(+), 60 deletions(-) diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 290f0ea31341..8092a8000cf5 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -63,71 +63,101 @@ pub(crate) struct WorkspaceInfo<'a> { pub(crate) submodules: &'a [&'a str], } -impl<'a> WorkspaceInfo<'a> { - const fn new( - path: &'a str, - exceptions: ExceptionList, - crates_and_deps: Option<(&'a [&str], &'a [&str], ListLocation)>, - submodules: &'a [&str], - ) -> Self { - Self { path, exceptions, crates_and_deps, submodules } - } -} - /// The workspaces to check for licensing and optionally permitted dependencies. // FIXME auto detect all cargo workspaces pub(crate) const WORKSPACES: &[WorkspaceInfo<'static>] = &[ // The root workspace has to be first for check_rustfix to work. - WorkspaceInfo::new( - ".", - EXCEPTIONS, - Some((&["rustc-main"], PERMITTED_RUSTC_DEPENDENCIES, PERMITTED_RUSTC_DEPS_LOCATION)), - &[], - ), - WorkspaceInfo::new( - "library", - EXCEPTIONS_STDLIB, - Some((&["sysroot"], PERMITTED_STDLIB_DEPENDENCIES, PERMITTED_STDLIB_DEPS_LOCATION)), - &[], - ), - // Outside of the alphabetical section because rustfmt formats it using multiple lines. - WorkspaceInfo::new( - "compiler/rustc_codegen_cranelift", - EXCEPTIONS_CRANELIFT, - Some(( - &["rustc_codegen_cranelift"], - PERMITTED_CRANELIFT_DEPENDENCIES, - PERMITTED_CRANELIFT_DEPS_LOCATION, + WorkspaceInfo { + path: ".", + exceptions: EXCEPTIONS, + crates_and_deps: Some(( + &["rustc-main"], + PERMITTED_RUSTC_DEPENDENCIES, + PERMITTED_RUSTC_DEPS_LOCATION, )), - &[], - ), - // tidy-alphabetical-start - WorkspaceInfo::new("compiler/rustc_codegen_gcc", EXCEPTIONS_GCC, None, &[]), - WorkspaceInfo::new("src/bootstrap", EXCEPTIONS_BOOTSTRAP, None, &[]), - WorkspaceInfo::new("src/tools/cargo", EXCEPTIONS_CARGO, None, &["src/tools/cargo"]), - //WorkspaceInfo::new("src/tools/miri/test-cargo-miri", &[], None), // FIXME uncomment once all deps are vendored - //WorkspaceInfo::new("src/tools/miri/test_dependencies", &[], None), // FIXME uncomment once all deps are vendored - WorkspaceInfo::new("src/tools/rust-analyzer", EXCEPTIONS_RUST_ANALYZER, None, &[]), - WorkspaceInfo::new( - "src/tools/rustbook", - EXCEPTIONS_RUSTBOOK, - None, - &["src/doc/book", "src/doc/reference"], - ), - WorkspaceInfo::new( - "src/tools/rustc-perf", - EXCEPTIONS_RUSTC_PERF, - None, - &["src/tools/rustc-perf"], - ), - WorkspaceInfo::new("src/tools/test-float-parse", EXCEPTIONS, None, &[]), - WorkspaceInfo::new( - "tests/run-make-cargo/uefi-qemu/uefi_qemu_test", - EXCEPTIONS_UEFI_QEMU_TEST, - None, - &[], - ), - // tidy-alphabetical-end + submodules: &[], + }, + WorkspaceInfo { + path: "library", + exceptions: EXCEPTIONS_STDLIB, + crates_and_deps: Some(( + &["sysroot"], + PERMITTED_STDLIB_DEPENDENCIES, + PERMITTED_STDLIB_DEPS_LOCATION, + )), + submodules: &[], + }, + { + WorkspaceInfo { + path: "compiler/rustc_codegen_cranelift", + exceptions: EXCEPTIONS_CRANELIFT, + crates_and_deps: Some(( + &["rustc_codegen_cranelift"], + PERMITTED_CRANELIFT_DEPENDENCIES, + PERMITTED_CRANELIFT_DEPS_LOCATION, + )), + submodules: &[], + } + }, + WorkspaceInfo { + path: "compiler/rustc_codegen_gcc", + exceptions: EXCEPTIONS_GCC, + crates_and_deps: None, + submodules: &[], + }, + WorkspaceInfo { + path: "src/bootstrap", + exceptions: EXCEPTIONS_BOOTSTRAP, + crates_and_deps: None, + submodules: &[], + }, + WorkspaceInfo { + path: "src/tools/cargo", + exceptions: EXCEPTIONS_CARGO, + crates_and_deps: None, + submodules: &["src/tools/cargo"], + }, + // FIXME uncomment once all deps are vendored + // WorkspaceInfo { + // path: "src/tools/miri/test-cargo-miri", + // crates_and_deps: None + // submodules: &[], + // }, + // WorkspaceInfo { + // path: "src/tools/miri/test_dependencies", + // crates_and_deps: None, + // submodules: &[], + // } + WorkspaceInfo { + path: "src/tools/rust-analyzer", + exceptions: EXCEPTIONS_RUST_ANALYZER, + crates_and_deps: None, + submodules: &[], + }, + WorkspaceInfo { + path: "src/tools/rustbook", + exceptions: EXCEPTIONS_RUSTBOOK, + crates_and_deps: None, + submodules: &["src/doc/book", "src/doc/reference"], + }, + WorkspaceInfo { + path: "src/tools/rustc-perf", + exceptions: EXCEPTIONS_RUSTC_PERF, + crates_and_deps: None, + submodules: &["src/tools/rustc-perf"], + }, + WorkspaceInfo { + path: "src/tools/test-float-parse", + exceptions: EXCEPTIONS, + crates_and_deps: None, + submodules: &[], + }, + WorkspaceInfo { + path: "tests/run-make-cargo/uefi-qemu/uefi_qemu_test", + exceptions: EXCEPTIONS_UEFI_QEMU_TEST, + crates_and_deps: None, + submodules: &[], + }, ]; /// These are exceptions to Rust's permissive licensing policy, and From b92ad97f1d8d458de880daea3826d8db30717a0e Mon Sep 17 00:00:00 2001 From: binarycat Date: Sun, 14 Sep 2025 18:26:29 -0500 Subject: [PATCH 512/682] bootstrap.py: disable incremental build for bootstrap in CI --- src/bootstrap/bootstrap.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 2ece53eb0cc9..e017cfe6aefb 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -1039,6 +1039,9 @@ class RustBuild(object): # See also: . if "CARGO_BUILD_TARGET" in env: del env["CARGO_BUILD_TARGET"] + # if in CI, don't use incremental build when building bootstrap. + if "GITHUB_ACTIONS" in env: + env["CARGO_INCREMENTAL"] = "0" env["CARGO_TARGET_DIR"] = build_dir env["RUSTC"] = self.rustc() env["LD_LIBRARY_PATH"] = ( From 5ebdec5ac2908b0bae42adbe451beeadbe8fa5de Mon Sep 17 00:00:00 2001 From: Tsukasa OI Date: Mon, 15 Sep 2025 02:16:34 +0000 Subject: [PATCH 513/682] rustc_codegen_llvm: Adjust RISC-V inline assembly's clobber list Despite that the `fflags` register (representing floating point exception flags) is stated as a flag register in the reference, it's not in the default clobber list of the RISC-V inline assembly and it would be better to fix it. --- compiler/rustc_codegen_llvm/src/asm.rs | 1 + tests/codegen-llvm/asm/riscv-clobbers.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 38c1d3b53e87..b79176e90981 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -240,6 +240,7 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { constraints.extend_from_slice(&[ + "~{fflags}".to_string(), "~{vtype}".to_string(), "~{vl}".to_string(), "~{vxsat}".to_string(), diff --git a/tests/codegen-llvm/asm/riscv-clobbers.rs b/tests/codegen-llvm/asm/riscv-clobbers.rs index e55b6731098e..0f235ddcdcc8 100644 --- a/tests/codegen-llvm/asm/riscv-clobbers.rs +++ b/tests/codegen-llvm/asm/riscv-clobbers.rs @@ -17,7 +17,7 @@ extern crate minicore; use minicore::*; // CHECK-LABEL: @flags_clobber -// CHECK: call void asm sideeffect "", "~{vtype},~{vl},~{vxsat},~{vxrm}"() +// CHECK: call void asm sideeffect "", "~{fflags},~{vtype},~{vl},~{vxsat},~{vxrm}"() #[no_mangle] pub unsafe fn flags_clobber() { asm!("", options(nostack, nomem)); From 55b8d4e6ffc220db78b928aacc274423631134f7 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 15 Sep 2025 07:56:35 +0300 Subject: [PATCH 514/682] Don't mark unknown type as implementing every notable trait Because the new solver, will return "yes" for them (which is a good thing). --- .../rust-analyzer/crates/ide/src/hover.rs | 6 +++++ .../crates/ide/src/hover/tests.rs | 23 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index 3f52303d74e4..03b9b3677511 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -483,6 +483,12 @@ fn notable_traits<'db>( db: &'db RootDatabase, ty: &hir::Type<'db>, ) -> Vec<(hir::Trait, Vec<(Option>, hir::Name)>)> { + if ty.is_unknown() { + // The trait solver returns "yes" to the question whether the error type + // impls any trait, and we don't want to show it as having any notable trait. + return Vec::new(); + } + db.notable_traits_in_deps(ty.krate(db).into()) .iter() .flat_map(|it| &**it) diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 58d8a7edbe04..a88c0c9866e5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -11097,3 +11097,26 @@ impl Enum<'_, Borrowed> { "#]], ); } + +#[test] +fn unknown_should_not_implement_notable_traits() { + check( + r#" +//- minicore: future, iterator +fn foo() { + let x$0; +} + "#, + expect![[r#" + *x* + + ```rust + let x: {unknown} + ``` + + --- + + no Drop + "#]], + ); +} From 91f32ed1803b7164446f386c5844718e963aeb0c Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 15 Sep 2025 08:25:17 +0300 Subject: [PATCH 515/682] Add regression tests to some S-blocked-on-new-solver issues That were fixed by the migration. --- .../hir-ty/src/tests/regression/new_solver.rs | 126 ++++++++++++++++++ .../crates/test-utils/src/minicore.rs | 12 +- 2 files changed, 137 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs index 595f285bd930..f559230fee39 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -2,6 +2,132 @@ use expect_test::expect; use crate::tests::{check_infer, check_no_mismatches}; +#[test] +fn regression_20365() { + check_infer( + r#" +//- minicore: iterator +struct Vec(T); +struct IntoIter(T); +impl IntoIterator for Vec { + type IntoIter = IntoIter; + type Item = T; +} +impl Iterator for IntoIter { + type Item = T; +} + +fn f(a: Vec) { + let iter = a.into_iter(); +} + +pub trait Space: IntoIterator { + type Ty: Space; +} +impl Space for [u8; 1] { + type Ty = Self; +} + "#, + expect![[r#" + 201..202 'a': Vec + 213..246 '{ ...r(); }': () + 223..227 'iter': IntoIter + 230..231 'a': Vec + 230..243 'a.into_iter()': IntoIter + "#]], + ); +} + +#[test] +fn regression_19971() { + check_infer( + r#" +//- minicore: pointee +fn make(_thin: *const (), _meta: core::ptr::DynMetadata) -> *const T +where + T: core::ptr::Pointee> + ?Sized, +{ + loop {} +} +trait Foo { + fn foo(&self) -> i32 { + loop {} + } +} + +fn test() -> i32 { + struct F {} + impl Foo for F {} + let meta = core::ptr::metadata(0 as *const F as *const dyn Foo); + + let f = F {}; + let fat_ptr = make(&f as *const F as *const (), meta); // <-- infers type as `*const {unknown}` + + let fat_ref = unsafe { &*fat_ptr }; // <-- infers type as `&{unknown}` + fat_ref.foo() // cannot 'go to definition' on `foo` +} + + "#, + expect![[r#" + 11..16 '_thin': *const () + 29..34 '_meta': DynMetadata + 155..170 '{ loop {} }': *const T + 161..168 'loop {}': ! + 166..168 '{}': () + 195..199 'self': &'? Self + 208..231 '{ ... }': i32 + 218..225 'loop {}': ! + 223..225 '{}': () + 252..613 '{ ...foo` }': i32 + 300..304 'meta': DynMetadata + 307..326 'core::...tadata': fn metadata(*const (dyn Foo + '?)) -> ::Metadata + 307..359 'core::...n Foo)': DynMetadata + 327..328 '0': usize + 327..340 '0 as *const F': *const F + 327..358 '0 as *...yn Foo': *const (dyn Foo + '?) + 370..371 'f': F + 374..378 'F {}': F + 388..395 'fat_ptr': *const (dyn Foo + '?) + 398..402 'make': fn make(*const (), DynMetadata) -> *const (dyn Foo + '?) + 398..437 'make(&... meta)': *const (dyn Foo + '?) + 403..405 '&f': &'? F + 403..417 '&f as *const F': *const F + 403..430 '&f as ...nst ()': *const () + 404..405 'f': F + 432..436 'meta': DynMetadata + 489..496 'fat_ref': &'? (dyn Foo + '?) + 499..519 'unsafe..._ptr }': &'? (dyn Foo + '?) + 508..517 '&*fat_ptr': &'? (dyn Foo + '?) + 509..517 '*fat_ptr': dyn Foo + '? + 510..517 'fat_ptr': *const (dyn Foo + '?) + 560..567 'fat_ref': &'? (dyn Foo + '?) + 560..573 'fat_ref.foo()': i32 + "#]], + ); +} + +#[test] +fn regression_19752() { + check_no_mismatches( + r#" +//- minicore: sized, copy +trait T1: Sized + Copy { + fn a(self, other: Self) -> Self { + other + } + + fn b(&mut self, other: Self) { + *self = self.a(other); + } +} + +trait T2: Sized { + type T1: T1; +} + "#, + ); +} + #[test] fn opaque_generics() { check_infer( diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index 26e0f46705b9..7c3e7fea1bde 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -55,7 +55,7 @@ //! panic: fmt //! phantom_data: //! pin: -//! pointee: copy, send, sync, ord, hash, unpin +//! pointee: copy, send, sync, ord, hash, unpin, phantom_data //! range: //! receiver: deref //! result: @@ -504,6 +504,16 @@ pub mod ptr { #[lang = "metadata_type"] type Metadata: Copy + Send + Sync + Ord + Hash + Unpin; } + + #[lang = "dyn_metadata"] + pub struct DynMetadata { + _phantom: crate::marker::PhantomData, + } + + pub const fn metadata(ptr: *const T) -> ::Metadata { + loop {} + } + // endregion:pointee // region:non_null #[rustc_layout_scalar_valid_range_start(1)] From f40e71c9de3d5d1075fa9fd94fccdd6126ae1ad4 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 15 Sep 2025 08:50:08 +0300 Subject: [PATCH 516/682] Add Regression Test For The One And The Only Issue #5514 --- .../hir-ty/src/tests/regression/new_solver.rs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs index f559230fee39..82d670cef2b0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -128,6 +128,40 @@ trait T2: Sized { ); } +#[test] +fn regression_type_checker_does_not_eagerly_select_predicates_from_where_clauses() { + // This was a very long standing issue (#5514) with a lot of duplicates, that was + // fixed by the switch to the new trait solver, so it deserves a long name and a + // honorable mention. + check_infer( + r#" +//- minicore: from + +struct Foo; +impl Foo { + fn method(self) -> i32 { 0 } +} + +fn f>(u: T) { + let x = u.into(); + x.method(); +} + "#, + expect![[r#" + 38..42 'self': Foo + 51..56 '{ 0 }': i32 + 53..54 '0': i32 + 79..80 'u': T + 85..126 '{ ...d(); }': () + 95..96 'x': Foo + 99..100 'u': T + 99..107 'u.into()': Foo + 113..114 'x': Foo + 113..123 'x.method()': i32 + "#]], + ); +} + #[test] fn opaque_generics() { check_infer( From 96f8dbe1b33691a949b23a005581b66c6f7e4cbf Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 15 Sep 2025 16:36:00 +1000 Subject: [PATCH 517/682] compiletest: Enable new-output-capture by default --- src/tools/compiletest/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index f647f96a9bf3..17a60ace8046 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -472,7 +472,7 @@ pub fn parse_config(args: Vec) -> Config { let value = matches .opt_str("new-output-capture") .or_else(|| env::var("COMPILETEST_NEW_OUTPUT_CAPTURE").ok()) - .unwrap_or_else(|| "off".to_owned()); + .unwrap_or_else(|| "on".to_owned()); parse_bool_option(&value) .unwrap_or_else(|| panic!("unknown `--new-output-capture` value `{value}` given")) }, From f6a7da8673de4a87e8e882ff533904274396726b Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 15 Sep 2025 10:48:26 +0300 Subject: [PATCH 518/682] Add a testing guide --- .../rust-analyzer/docs/book/src/SUMMARY.md | 1 + .../docs/book/src/contributing/testing.md | 106 ++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 src/tools/rust-analyzer/docs/book/src/contributing/testing.md diff --git a/src/tools/rust-analyzer/docs/book/src/SUMMARY.md b/src/tools/rust-analyzer/docs/book/src/SUMMARY.md index dffdae94a6e8..3fb46d59a4a5 100644 --- a/src/tools/rust-analyzer/docs/book/src/SUMMARY.md +++ b/src/tools/rust-analyzer/docs/book/src/SUMMARY.md @@ -23,3 +23,4 @@ - [Setup](contributing/setup.md) - [Style](contributing/style.md) - [Syntax](contributing/syntax.md) + - [Writing Tests](contributing/testing.md) diff --git a/src/tools/rust-analyzer/docs/book/src/contributing/testing.md b/src/tools/rust-analyzer/docs/book/src/contributing/testing.md new file mode 100644 index 000000000000..ccee9b847b6e --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/contributing/testing.md @@ -0,0 +1,106 @@ +rust-analyzer's testing is based on *snapshot tests*: a test is a piece of input text, usually a Rust code, and some output text. There is then some testing helper that runs the feature on the input text and compares the result to the output text. + +rust-analyzer uses a combination of the crate [`expect-test`](https://docs.rs/expect-test) and a custom testing framework. + +This all may sound too abstract, so let's demonstrate with an example. + +Type inference tests are located at `crates/hir-ty/src/tests`. There are various test helpers you can use. One of the simplest is `check_no_mismatches()`: it is given a piece of Rust code (we'll talk more about Rust code in tests later) and asserts that there are no type mismatches in it, that is, one type was expected but another was found (for example, `let x: () = 1` is a type mismatch). Note that we determine type mismatches via rust-analyzer's own analysis, not via the compiler (this is what we are testing, after all), which means there are often missed mismatches and sometimes bogus ones as well. + +For example, the following test will fail: +```rust +#[test] +fn this_will_fail() { + check_no_mismatches( + r#" +fn main() { + let x: () = 1; +} + "#, + ); +} +``` + +Sometimes we want to check more that there are no type mismatches. For that we use other helpers. For example, often we want to assert that the type of some expression is some specific type. For that we use the `check_types()` function. It takes a Rust code string with custom annotation, that are common in our test suite. The general scheme of annotation is: + + - `$0` marks a position. What to do with it is determined by the testing helper. Commonly it denotes the cursor position in IDE tests (for example, hover). + - `$0...$0` marks a range, commonly a selection in IDE tests. + - `^...^`, commonly seen in a comment (`// ^^^^`), labels the line above. For example, the following will attach the label `hey` to the range of the variable name `cool`: + + ```rust + let cool; + // ^^^^ hey + ``` + +`check_types()` uses labels to assert type: when you attach a label to a range, `check_types()` assert that the type of this range will be what written in the label. + +It's all too abstract without an example: +```rust +#[test] +fn my_test() { + check_types( + r#" +fn main() { + let x = 1; + // ^ i32 +} + "#, + ); +} +``` +Here, we assert that the type of the variable `x` is `i32`. Which is true, of course, so the test will pass. + +Oftentimes it is convenient to assert the types of all of the expressions at once, and that brings us to the last kind of test. It uses `expect-test` to match an output text: +```rust +#[test] +fn my_test() { + check_infer( + r#" +fn main() { + let x = 1; +} + "#, + expect![[r#" + 10..28 '{ ...= 1; }': () + 20..21 'x': i32 + 24..25 '1': i32 + "#]], + ); +} +``` +The text inside the `expect![[]]` is determined by the helper, `check_infer()` in this case. For `check_infer()`, each line is a range in the source code (the range is counted in bytes and the source is trimmed, indentation is stripped), next to it there is the text in that range, or some part of it with `...` if it's too long, and finally comes the type of that range. + +The important feature of `expect-test` is that it allows easy update of the expectation. Say you changed something in the code, maybe fixed a bug, and the output in `expect![[]]` needs to change. Or maybe you are writing it from scratch. Writing it by hand is very tedious and prone to mistakes. But `expect-trait` has a magic. You can set the environment variable `UPDATE_EXPECT=1`, then run the test, and it will update automatically! Some editors (e.g. VSCode) make it even more convenient: on them, on the top of every test that uses `expect-test`, next to the usual `Run | Debug` buttons, rust-analyzer also shows an `Update Expect` button. Clicking it will run that test in updating mode. + +## Rust code in the tests + +The first thing that you probably already noticed is that the Rust code in the tests is syntax highlighted! In fact, it even uses semantic highlighting. rust-analyzer highlights strings "as if" they contain Rust code if they are passed to a parameter marked `#[rust_analyzer::rust_fixture]`, and rust-analyzer test helpers do that (in fact, this was designed for them). + +The syntax highlighting is very important, not just because it's nice to the eye: it's very easy to make mistakes in test code, and debugging that can be very hard. Often the test will just fail, printing an `{unknown}` type, and you'll have no clue what's going wrong. The syntax is the clue; if something isn't highlighted correctly, that probably means there is an error (there is one exception to this, which we'll discuss later). You can even set the semantic highlighting tag `unresolved_reference` to e.g. red, so you will see such things clearly. + +Still, often you won't know what's going wrong. Why you can't fix the test, or worse, you expect it to fail but it doesn't. You can try the code on a real IDE to be sure it works. Later we'll give some tips to fix the test. + +### The fixture + +The Rust code in a test is not, a fact, a single Rust file. It has a mini-language that allows you to express multiple files, multiple crates, different configs, and more. All options are documented in `crates/test-utils/src/fixture.rs`, but here are some of the common ones: + + - `//- minicore: flag1, flag2, ...`. This is by far the most common flag. Tests in rust-analyzer don't have access by default to any other type - not `Option`, not `Iterator`, not even `Sized`. This flag allows you to include parts of the `crates/test-utils/src/minicore.rs` file, which mimics `core`. All possible flags are listed at the top of `minicore` along with the flags they imply, then later you can see by `// region:flag` and `// endregion:flag` what code each flag enables. + - `// /path/to/file.rs crate:crate deps:dep_a,dep_b`. The first component is the filename of the code that follows (until the next file). It is required, but only if you supply this line. Other components in this line are optional. They include `crate:crate_name`, to start a new crate, or `deps:dep_a,dep_b`, to declare dependencies between crates. You can also declare modules as usual in Rust - just name your paths `/foo.rs` or `/foo/mod.rs`, declare `mod foo` and that's it! + +So the following snippet: +```rust +//- minicore: sized, fn +// /lib.rs crate:foo +pub mod bar; +// /bar.rs +pub struct Bar; +// /main.rs crate:main deps:foo +use foo::Bar; +``` +Declares two crates `foo` and `main` where `main` depends on `foo`, with dependency in `Sized` and the `FnX` traits from `core`, and a module of `foo` called `bar`. + +And as promised, here are some tips to make your test work: + + - If you use some type/trait, you must *always* include it in `minicore`. Note - not all types from core/std are available there, you can add new (under flags) if you need. And import them if they are not in the prelude. + - If you use unsized types (`dyn Trait`/slices), you may want to include some or all of the following `minicore` flags: `sized`, `unsize`, `coerce_unsized`, `dispatch_from_dyn`. + - If you use closures, consider including the `fn` minicore flag. Async closures need the `async_fn` flag. + - `sized` is commonly needed, consider adding it if you're stuck. From 8265748da82be9909909930b191ead719cc760eb Mon Sep 17 00:00:00 2001 From: Maksim Bondarenkov Date: Mon, 15 Sep 2025 11:07:07 +0300 Subject: [PATCH 519/682] opt-dist: don't set `RUST_LOG=collector=debug` see [Zulip discussion](https://rust-lang.zulipchat.com/#narrow/channel/122651-general/topic/opt-dist.3A.20do.20not.20set.20RUST_LOG.3Dcollector.3Ddebug.20forcefully) --- src/tools/opt-dist/src/training.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tools/opt-dist/src/training.rs b/src/tools/opt-dist/src/training.rs index 4f9352d11b12..b3e95e087e3f 100644 --- a/src/tools/opt-dist/src/training.rs +++ b/src/tools/opt-dist/src/training.rs @@ -39,7 +39,6 @@ fn init_compiler_benchmarks( "--exact-match", crates.join(",").as_str(), ]) - .env("RUST_LOG", "collector=debug") .env("RUSTC", env.rustc_stage_0().as_str()) .env("RUSTC_BOOTSTRAP", "1") .workdir(&env.rustc_perf_dir()); From b919a5f5183afb876b5206b3b23b249183cb313c Mon Sep 17 00:00:00 2001 From: tiif Date: Mon, 15 Sep 2025 08:57:22 +0000 Subject: [PATCH 520/682] Remove UnsizedConstParamTy trait and make it into an unstable impl --- .../src/deriving/bounds.rs | 39 ------------------- compiler/rustc_builtin_macros/src/lib.rs | 1 - compiler/rustc_hir/src/lang_items.rs | 1 - .../rustc_hir_analysis/src/check/wfcheck.rs | 13 +------ .../src/coherence/builtin.rs | 15 ++----- library/core/src/marker.rs | 33 +++------------- library/core/src/mem/transmutability.rs | 6 +-- library/core/src/tuple.rs | 10 +---- 8 files changed, 14 insertions(+), 104 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/deriving/bounds.rs b/compiler/rustc_builtin_macros/src/deriving/bounds.rs index dd8f0e46a0e0..63342880b094 100644 --- a/compiler/rustc_builtin_macros/src/deriving/bounds.rs +++ b/compiler/rustc_builtin_macros/src/deriving/bounds.rs @@ -51,43 +51,4 @@ pub(crate) fn expand_deriving_const_param_ty( }; trait_def.expand(cx, mitem, item, push); - - let trait_def = TraitDef { - span, - path: path_std!(marker::UnsizedConstParamTy), - skip_path_as_bound: false, - needs_copy_as_bound_if_packed: false, - additional_bounds: vec![ty::Ty::Path(path_std!(cmp::Eq))], - supports_unions: false, - methods: Vec::new(), - associated_types: Vec::new(), - is_const, - is_staged_api_crate: cx.ecfg.features.staged_api(), - }; - - trait_def.expand(cx, mitem, item, push); -} - -pub(crate) fn expand_deriving_unsized_const_param_ty( - cx: &ExtCtxt<'_>, - span: Span, - mitem: &MetaItem, - item: &Annotatable, - push: &mut dyn FnMut(Annotatable), - is_const: bool, -) { - let trait_def = TraitDef { - span, - path: path_std!(marker::UnsizedConstParamTy), - skip_path_as_bound: false, - needs_copy_as_bound_if_packed: false, - additional_bounds: vec![ty::Ty::Path(path_std!(cmp::Eq))], - supports_unions: false, - methods: Vec::new(), - associated_types: Vec::new(), - is_const, - is_staged_api_crate: cx.ecfg.features.staged_api(), - }; - - trait_def.expand(cx, mitem, item, push); } diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 1bcea95fbb7b..4541e2cd3b41 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -129,7 +129,6 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { Clone: clone::expand_deriving_clone, Copy: bounds::expand_deriving_copy, ConstParamTy: bounds::expand_deriving_const_param_ty, - UnsizedConstParamTy: bounds::expand_deriving_unsized_const_param_ty, Debug: debug::expand_deriving_debug, Default: default::expand_deriving_default, Eq: eq::expand_deriving_eq, diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 67d2f15d4147..2e099a97b65b 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -370,7 +370,6 @@ language_item_table! { CoercePointeeValidated, sym::coerce_pointee_validated, coerce_pointee_validated_trait, Target::Trait, GenericRequirement::Exact(0); ConstParamTy, sym::const_param_ty, const_param_ty_trait, Target::Trait, GenericRequirement::Exact(0); - UnsizedConstParamTy, sym::unsized_const_param_ty, unsized_const_param_ty_trait, Target::Trait, GenericRequirement::Exact(0); Poll, sym::Poll, poll, Target::Enum, GenericRequirement::None; PollReady, sym::Ready, poll_ready_variant, Target::Variant, GenericRequirement::None; diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 22a9446fd4cf..6270fb24ba70 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -819,17 +819,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &ty::GenericParamDef) -> Result<(), Er let span = tcx.def_span(param.def_id); let def_id = param.def_id.expect_local(); - if tcx.features().unsized_const_params() { - enter_wf_checking_ctxt(tcx, tcx.local_parent(def_id), |wfcx| { - wfcx.register_bound( - ObligationCause::new(span, def_id, ObligationCauseCode::ConstParam(ty)), - wfcx.param_env, - ty, - tcx.require_lang_item(LangItem::UnsizedConstParamTy, span), - ); - Ok(()) - }) - } else if tcx.features().adt_const_params() { + if tcx.features().adt_const_params() { enter_wf_checking_ctxt(tcx, tcx.local_parent(def_id), |wfcx| { wfcx.register_bound( ObligationCause::new(span, def_id, ObligationCauseCode::ConstParam(ty)), @@ -880,7 +870,6 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &ty::GenericParamDef) -> Result<(), Er tcx, tcx.param_env(param.def_id), ty, - LangItem::ConstParamTy, cause, ) { // Can never implement `ConstParamTy`, don't suggest anything. diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 32b175611ceb..0b9a01d6042f 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -1,7 +1,6 @@ //! Check properties that are required by built-in traits and set //! up data structures required by type-checking/codegen. -use std::assert_matches::assert_matches; use std::collections::BTreeMap; use rustc_data_structures::fx::FxHashSet; @@ -40,10 +39,7 @@ pub(super) fn check_trait<'tcx>( checker.check(lang_items.async_drop_trait(), visit_implementation_of_drop)?; checker.check(lang_items.copy_trait(), visit_implementation_of_copy)?; checker.check(lang_items.const_param_ty_trait(), |checker| { - visit_implementation_of_const_param_ty(checker, LangItem::ConstParamTy) - })?; - checker.check(lang_items.unsized_const_param_ty_trait(), |checker| { - visit_implementation_of_const_param_ty(checker, LangItem::UnsizedConstParamTy) + visit_implementation_of_const_param_ty(checker) })?; checker.check(lang_items.coerce_unsized_trait(), visit_implementation_of_coerce_unsized)?; checker @@ -138,12 +134,7 @@ fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaran } } -fn visit_implementation_of_const_param_ty( - checker: &Checker<'_>, - kind: LangItem, -) -> Result<(), ErrorGuaranteed> { - assert_matches!(kind, LangItem::ConstParamTy | LangItem::UnsizedConstParamTy); - +fn visit_implementation_of_const_param_ty(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { let tcx = checker.tcx; let header = checker.impl_header; let impl_did = checker.impl_def_id; @@ -157,7 +148,7 @@ fn visit_implementation_of_const_param_ty( } let cause = traits::ObligationCause::misc(DUMMY_SP, impl_did); - match type_allowed_to_implement_const_param_ty(tcx, param_env, self_type, kind, cause) { + match type_allowed_to_implement_const_param_ty(tcx, param_env, self_type, cause) { Ok(()) => Ok(()), Err(ConstParamTyImplementationError::InfrigingFields(fields)) => { let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span; diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index d03d7a43469a..1c100312a9a3 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -1083,7 +1083,7 @@ pub trait Tuple {} // We name this differently than the derive macro so that the `adt_const_params` can // be used independently of `unsized_const_params` without requiring a full path // to the derive macro every time it is used. This should be renamed on stabilization. -pub trait ConstParamTy_: UnsizedConstParamTy + StructuralPartialEq + Eq {} +pub trait ConstParamTy_: StructuralPartialEq + Eq {} /// Derive macro generating an impl of the trait `ConstParamTy`. #[rustc_builtin_macro] @@ -1093,23 +1093,6 @@ pub macro ConstParamTy($item:item) { /* compiler built-in */ } -#[lang = "unsized_const_param_ty"] -#[unstable(feature = "unsized_const_params", issue = "95174")] -#[diagnostic::on_unimplemented(message = "`{Self}` can't be used as a const parameter type")] -/// A marker for types which can be used as types of `const` generic parameters. -/// -/// Equivalent to [`ConstParamTy_`] except that this is used by -/// the `unsized_const_params` to allow for fake unstable impls. -pub trait UnsizedConstParamTy: StructuralPartialEq + Eq {} - -/// Derive macro generating an impl of the trait `ConstParamTy`. -#[rustc_builtin_macro] -#[allow_internal_unstable(unsized_const_params)] -#[unstable(feature = "unsized_const_params", issue = "95174")] -pub macro UnsizedConstParamTy($item:item) { - /* compiler built-in */ -} - // FIXME(adt_const_params): handle `ty::FnDef`/`ty::Closure` marker_impls! { #[unstable(feature = "adt_const_params", issue = "95174")] @@ -1124,17 +1107,11 @@ marker_impls! { marker_impls! { #[unstable(feature = "unsized_const_params", issue = "95174")] - UnsizedConstParamTy for - usize, u8, u16, u32, u64, u128, - isize, i8, i16, i32, i64, i128, - bool, - char, - (), - {T: UnsizedConstParamTy, const N: usize} [T; N], - + #[unstable_feature_bound(unsized_const_params)] + ConstParamTy_ for str, - {T: UnsizedConstParamTy} [T], - {T: UnsizedConstParamTy + ?Sized} &T, + {T: ConstParamTy_} [T], + {T: ConstParamTy_ + ?Sized} &T, } /// A common trait implemented by all function pointers. diff --git a/library/core/src/mem/transmutability.rs b/library/core/src/mem/transmutability.rs index 782b826448a3..f36cb8cddb83 100644 --- a/library/core/src/mem/transmutability.rs +++ b/library/core/src/mem/transmutability.rs @@ -1,4 +1,4 @@ -use crate::marker::{ConstParamTy_, UnsizedConstParamTy}; +use crate::marker::ConstParamTy_; /// Marks that `Src` is transmutable into `Self`. /// @@ -83,6 +83,7 @@ use crate::marker::{ConstParamTy_, UnsizedConstParamTy}; /// Furthermore, stability does not imply portability. For example, the size of /// `usize` is stable, but not portable. #[unstable(feature = "transmutability", issue = "99571")] +#[unstable_feature_bound(transmutability)] #[lang = "transmute_trait"] #[rustc_deny_explicit_impl] #[rustc_do_not_implement_via_object] @@ -288,9 +289,8 @@ pub struct Assume { } #[unstable(feature = "transmutability", issue = "99571")] +#[unstable_feature_bound(transmutability)] impl ConstParamTy_ for Assume {} -#[unstable(feature = "transmutability", issue = "99571")] -impl UnsizedConstParamTy for Assume {} impl Assume { /// With this, [`TransmuteFrom`] does not assume you have ensured any safety diff --git a/library/core/src/tuple.rs b/library/core/src/tuple.rs index 3892f8310769..c57a8d81ade1 100644 --- a/library/core/src/tuple.rs +++ b/library/core/src/tuple.rs @@ -1,7 +1,7 @@ // See core/src/primitive_docs.rs for documentation. use crate::cmp::Ordering::{self, *}; -use crate::marker::{ConstParamTy_, StructuralPartialEq, UnsizedConstParamTy}; +use crate::marker::{ConstParamTy_, StructuralPartialEq}; use crate::ops::ControlFlow::{self, Break, Continue}; // Recursive macro for implementing n-ary tuple functions and operations @@ -47,17 +47,11 @@ macro_rules! tuple_impls { maybe_tuple_doc! { $($T)+ @ #[unstable(feature = "adt_const_params", issue = "95174")] + #[unstable_feature_bound(unsized_const_params)] impl<$($T: ConstParamTy_),+> ConstParamTy_ for ($($T,)+) {} } - maybe_tuple_doc! { - $($T)+ @ - #[unstable(feature = "unsized_const_params", issue = "95174")] - impl<$($T: UnsizedConstParamTy),+> UnsizedConstParamTy for ($($T,)+) - {} - } - maybe_tuple_doc! { $($T)+ @ #[unstable(feature = "structural_match", issue = "31434")] From 1a02cd531d6b5ff154078208daf7816b1657f389 Mon Sep 17 00:00:00 2001 From: tiif Date: Mon, 15 Sep 2025 08:59:19 +0000 Subject: [PATCH 521/682] Add check to make sure ConstParamTy impls of certain types are gated with #[unstable_feature_bound(unsized_const_params)] --- .../rustc_trait_selection/src/traits/misc.rs | 38 +++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index 393f458bea27..4c25882daa92 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -1,15 +1,14 @@ //! Miscellaneous type-system utilities that are too small to deserve their own modules. -use std::assert_matches::assert_matches; - use hir::LangItem; use rustc_ast::Mutability; use rustc_hir as hir; use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt}; use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeVisitableExt, TypingMode}; +use rustc_span::sym; use crate::regions::InferCtxtRegionExt; -use crate::traits::{self, FulfillmentError, ObligationCause}; +use crate::traits::{self, FulfillmentError, Obligation, ObligationCause}; pub enum CopyImplementationError<'tcx> { InfringingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>), @@ -98,10 +97,9 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, self_type: Ty<'tcx>, - lang_item: LangItem, parent_cause: ObligationCause<'tcx>, ) -> Result<(), ConstParamTyImplementationError<'tcx>> { - assert_matches!(lang_item, LangItem::ConstParamTy | LangItem::UnsizedConstParamTy); + let mut need_unstable_feature_bound = false; let inner_tys: Vec<_> = match *self_type.kind() { // Trivially okay as these types are all: @@ -112,18 +110,14 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>( // Handle types gated under `feature(unsized_const_params)` // FIXME(unsized_const_params): Make `const N: [u8]` work then forbid references - ty::Slice(inner_ty) | ty::Ref(_, inner_ty, Mutability::Not) - if lang_item == LangItem::UnsizedConstParamTy => - { + ty::Slice(inner_ty) | ty::Ref(_, inner_ty, Mutability::Not) => { + need_unstable_feature_bound = true; vec![inner_ty] } - ty::Str if lang_item == LangItem::UnsizedConstParamTy => { + ty::Str => { + need_unstable_feature_bound = true; vec![Ty::new_slice(tcx, tcx.types.u8)] } - ty::Str | ty::Slice(..) | ty::Ref(_, _, Mutability::Not) => { - return Err(ConstParamTyImplementationError::UnsizedConstParamsFeatureRequired); - } - ty::Array(inner_ty, _) => vec![inner_ty], // `str` morally acts like a newtype around `[u8]` @@ -137,7 +131,7 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>( adt, args, parent_cause.clone(), - lang_item, + LangItem::ConstParamTy, ) .map_err(ConstParamTyImplementationError::InfrigingFields)?; @@ -153,11 +147,25 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>( let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); let ocx = traits::ObligationCtxt::new_with_diagnostics(&infcx); + // Make sure impls certain types are gated with #[unstable_feature_bound(unsized_const_params)] + if need_unstable_feature_bound { + ocx.register_obligation(Obligation::new( + tcx, + parent_cause.clone(), + param_env, + ty::ClauseKind::UnstableFeature(sym::unsized_const_params), + )); + + if !ocx.select_all_or_error().is_empty() { + return Err(ConstParamTyImplementationError::UnsizedConstParamsFeatureRequired); + } + } + ocx.register_bound( parent_cause.clone(), param_env, inner_ty, - tcx.require_lang_item(lang_item, parent_cause.span), + tcx.require_lang_item(LangItem::ConstParamTy, parent_cause.span), ); let errors = ocx.select_all_or_error(); From 0bd2ee3a0c260894eb92f5f0f5f8dd56182cda3f Mon Sep 17 00:00:00 2001 From: tiif Date: Mon, 15 Sep 2025 09:01:22 +0000 Subject: [PATCH 522/682] Fix the testcases to not use UnsizedConstParamTy --- .../auxiliary/unsized_const_param.rs | 2 +- .../adt_const_params/const_param_ty_bad.rs | 2 +- .../const_param_ty_bad.stderr | 42 ++++++------- .../const_param_ty_dyn_compatibility.rs | 5 +- .../const_param_ty_dyn_compatibility.stderr | 19 +----- ...nst_param_ty_generic_bounds_do_not_hold.rs | 2 +- ...param_ty_generic_bounds_do_not_hold.stderr | 24 +++---- .../adt_const_params/const_param_ty_good.rs | 8 +-- .../const_param_ty_impl_bad_field.rs | 4 +- .../const_param_ty_impl_bad_field.stderr | 10 +-- .../const_param_ty_impl_no_structural_eq.rs | 8 +-- ...onst_param_ty_impl_no_structural_eq.stderr | 28 ++++----- .../const_param_ty_impl_union.rs | 4 +- .../const_param_ty_impl_union.stderr | 10 +-- .../nested_bad_const_param_ty.rs | 3 - .../nested_bad_const_param_ty.stderr | 63 +++---------------- .../non_valtreeable_const_arg-2.stderr | 6 +- .../reference_pointee_is_const_param-1.rs | 4 +- .../reference_pointee_is_const_param-1.stderr | 6 +- .../reference_pointee_is_const_param-2.rs | 4 +- .../reference_pointee_is_const_param-2.stderr | 6 +- .../trait_objects_as_a_const_generic.rs | 4 +- .../trait_objects_as_a_const_generic.stderr | 6 +- .../adt_const_params/unsized_field-1.rs | 5 +- .../adt_const_params/unsized_field-1.stderr | 28 +++++++-- .../adt_const_params/unsized_field-2.rs | 14 ----- .../adt_const_params/unsized_field-2.stderr | 27 -------- .../unsizing-wfcheck-issue-126272.rs | 1 - .../unsizing-wfcheck-issue-126272.stderr | 51 +++++++-------- ...const-param-with-additional-obligations.rs | 4 +- .../no_const_param_ty_bound.stderr | 2 +- tests/ui/const-generics/issue-66451.rs | 6 +- .../issues/issue-63322-forbid-dyn.full.stderr | 2 +- ...nst-param-mismatch.adt_const_params.stderr | 22 ++++--- tests/ui/const-generics/slice-const-param.rs | 2 +- ...m-static-reference.adt_const_params.stderr | 12 ++-- .../transmute-const-param-static-reference.rs | 2 +- .../symbol_mangling_v0_str.rs | 4 +- .../feature-gate-unsized-const-params.rs | 10 +-- .../feature-gate-unsized-const-params.stderr | 19 +++--- .../ui/layout/thaw-transmute-invalid-enum.rs | 1 + .../layout/thaw-transmute-invalid-enum.stderr | 19 ++++-- .../const-generics-structural-demangling.rs | 8 +-- 43 files changed, 218 insertions(+), 291 deletions(-) delete mode 100644 tests/ui/const-generics/adt_const_params/unsized_field-2.rs delete mode 100644 tests/ui/const-generics/adt_const_params/unsized_field-2.stderr diff --git a/tests/ui/const-generics/adt_const_params/auxiliary/unsized_const_param.rs b/tests/ui/const-generics/adt_const_params/auxiliary/unsized_const_param.rs index e2ba459f8dd3..1bfd7c346561 100644 --- a/tests/ui/const-generics/adt_const_params/auxiliary/unsized_const_param.rs +++ b/tests/ui/const-generics/adt_const_params/auxiliary/unsized_const_param.rs @@ -1,6 +1,6 @@ #![feature(adt_const_params, unsized_const_params)] -#[derive(std::marker::UnsizedConstParamTy, Eq, PartialEq)] +#[derive(std::marker::ConstParamTy, Eq, PartialEq)] pub struct Foo([u8]); #[derive(std::marker::ConstParamTy, Eq, PartialEq)] diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_bad.rs b/tests/ui/const-generics/adt_const_params/const_param_ty_bad.rs index 35539193a278..d482e7fad06d 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_bad.rs +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_bad.rs @@ -1,7 +1,7 @@ #![allow(incomplete_features)] #![feature(adt_const_params, unsized_const_params)] -fn check(_: impl std::marker::UnsizedConstParamTy) {} +fn check(_: impl std::marker::ConstParamTy_) {} fn main() { check(main); //~ error: `fn() {main}` can't be used as a const parameter type diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_bad.stderr b/tests/ui/const-generics/adt_const_params/const_param_ty_bad.stderr index c05584ef909a..ff514f5608f1 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_bad.stderr +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_bad.stderr @@ -2,15 +2,15 @@ error[E0277]: `fn() {main}` can't be used as a const parameter type --> $DIR/const_param_ty_bad.rs:7:11 | LL | check(main); - | ----- ^^^^ the trait `UnsizedConstParamTy` is not implemented for fn item `fn() {main}` + | ----- ^^^^ the trait `ConstParamTy_` is not implemented for fn item `fn() {main}` | | | required by a bound introduced by this call | note: required by a bound in `check` --> $DIR/const_param_ty_bad.rs:4:18 | -LL | fn check(_: impl std::marker::UnsizedConstParamTy) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` +LL | fn check(_: impl std::marker::ConstParamTy_) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` help: use parentheses to call this function | LL | check(main()); @@ -24,12 +24,12 @@ LL | check(|| {}); | | | required by a bound introduced by this call | - = help: the trait `UnsizedConstParamTy` is not implemented for closure `{closure@$DIR/const_param_ty_bad.rs:8:11: 8:13}` + = help: the trait `ConstParamTy_` is not implemented for closure `{closure@$DIR/const_param_ty_bad.rs:8:11: 8:13}` note: required by a bound in `check` --> $DIR/const_param_ty_bad.rs:4:18 | -LL | fn check(_: impl std::marker::UnsizedConstParamTy) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` +LL | fn check(_: impl std::marker::ConstParamTy_) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` help: use parentheses to call this closure | LL - check(|| {}); @@ -40,15 +40,15 @@ error[E0277]: `fn()` can't be used as a const parameter type --> $DIR/const_param_ty_bad.rs:9:11 | LL | check(main as fn()); - | ----- ^^^^^^^^^^^^ the trait `UnsizedConstParamTy` is not implemented for `fn()` + | ----- ^^^^^^^^^^^^ the trait `ConstParamTy_` is not implemented for `fn()` | | | required by a bound introduced by this call | note: required by a bound in `check` --> $DIR/const_param_ty_bad.rs:4:18 | -LL | fn check(_: impl std::marker::UnsizedConstParamTy) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` +LL | fn check(_: impl std::marker::ConstParamTy_) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` help: use parentheses to call this function pointer | LL | check(main as fn()()); @@ -58,16 +58,16 @@ error[E0277]: `&mut ()` can't be used as a const parameter type --> $DIR/const_param_ty_bad.rs:10:11 | LL | check(&mut ()); - | ----- ^^^^^^^ the trait `UnsizedConstParamTy` is not implemented for `&mut ()` + | ----- ^^^^^^^ the trait `ConstParamTy_` is not implemented for `&mut ()` | | | required by a bound introduced by this call | - = note: `UnsizedConstParamTy` is implemented for `&()`, but not for `&mut ()` + = note: `ConstParamTy_` is implemented for `&()`, but not for `&mut ()` note: required by a bound in `check` --> $DIR/const_param_ty_bad.rs:4:18 | -LL | fn check(_: impl std::marker::UnsizedConstParamTy) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` +LL | fn check(_: impl std::marker::ConstParamTy_) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` help: consider removing the leading `&`-reference | LL - check(&mut ()); @@ -78,31 +78,31 @@ error[E0277]: `*mut ()` can't be used as a const parameter type --> $DIR/const_param_ty_bad.rs:11:11 | LL | check(&mut () as *mut ()); - | ----- ^^^^^^^^^^^^^^^^^^ the trait `UnsizedConstParamTy` is not implemented for `*mut ()` + | ----- ^^^^^^^^^^^^^^^^^^ the trait `ConstParamTy_` is not implemented for `*mut ()` | | | required by a bound introduced by this call | - = help: the trait `UnsizedConstParamTy` is implemented for `()` + = help: the trait `ConstParamTy_` is implemented for `()` note: required by a bound in `check` --> $DIR/const_param_ty_bad.rs:4:18 | -LL | fn check(_: impl std::marker::UnsizedConstParamTy) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` +LL | fn check(_: impl std::marker::ConstParamTy_) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` error[E0277]: `*const ()` can't be used as a const parameter type --> $DIR/const_param_ty_bad.rs:12:11 | LL | check(&() as *const ()); - | ----- ^^^^^^^^^^^^^^^^ the trait `UnsizedConstParamTy` is not implemented for `*const ()` + | ----- ^^^^^^^^^^^^^^^^ the trait `ConstParamTy_` is not implemented for `*const ()` | | | required by a bound introduced by this call | - = help: the trait `UnsizedConstParamTy` is implemented for `()` + = help: the trait `ConstParamTy_` is implemented for `()` note: required by a bound in `check` --> $DIR/const_param_ty_bad.rs:4:18 | -LL | fn check(_: impl std::marker::UnsizedConstParamTy) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` +LL | fn check(_: impl std::marker::ConstParamTy_) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` error: aborting due to 6 previous errors diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_dyn_compatibility.rs b/tests/ui/const-generics/adt_const_params/const_param_ty_dyn_compatibility.rs index 6a553c2e0854..2fcf872c99aa 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_dyn_compatibility.rs +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_dyn_compatibility.rs @@ -1,12 +1,9 @@ #![feature(adt_const_params, unsized_const_params)] #![allow(incomplete_features)] -use std::marker::{ConstParamTy_, UnsizedConstParamTy}; +use std::marker::ConstParamTy_; fn foo(a: &dyn ConstParamTy_) {} //~^ ERROR: the trait `ConstParamTy_` -fn bar(a: &dyn UnsizedConstParamTy) {} -//~^ ERROR: the trait `UnsizedConstParamTy` - fn main() {} diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_dyn_compatibility.stderr b/tests/ui/const-generics/adt_const_params/const_param_ty_dyn_compatibility.stderr index c71ec24dda33..ce695ff66d58 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_dyn_compatibility.stderr +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_dyn_compatibility.stderr @@ -15,23 +15,6 @@ LL - fn foo(a: &dyn ConstParamTy_) {} LL + fn foo(a: &impl ConstParamTy_) {} | -error[E0038]: the trait `UnsizedConstParamTy` is not dyn compatible - --> $DIR/const_param_ty_dyn_compatibility.rs:9:16 - | -LL | fn bar(a: &dyn UnsizedConstParamTy) {} - | ^^^^^^^^^^^^^^^^^^^ `UnsizedConstParamTy` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $SRC_DIR/core/src/cmp.rs:LL:COL - | - = note: the trait is not dyn compatible because it uses `Self` as a type parameter -help: consider using an opaque type instead - | -LL - fn bar(a: &dyn UnsizedConstParamTy) {} -LL + fn bar(a: &impl UnsizedConstParamTy) {} - | - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_generic_bounds_do_not_hold.rs b/tests/ui/const-generics/adt_const_params/const_param_ty_generic_bounds_do_not_hold.rs index 7ffdafa33e93..a86d74275de8 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_generic_bounds_do_not_hold.rs +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_generic_bounds_do_not_hold.rs @@ -4,7 +4,7 @@ #[derive(PartialEq, Eq)] struct NotParam; -fn check() {} +fn check() {} fn main() { check::<&NotParam>(); //~ error: `NotParam` can't be used as a const parameter type diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_generic_bounds_do_not_hold.stderr b/tests/ui/const-generics/adt_const_params/const_param_ty_generic_bounds_do_not_hold.stderr index 158e76630f3a..ca2aa3adcb7a 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_generic_bounds_do_not_hold.stderr +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_generic_bounds_do_not_hold.stderr @@ -4,17 +4,17 @@ error[E0277]: `NotParam` can't be used as a const parameter type LL | check::<&NotParam>(); | ^^^^^^^^^ unsatisfied trait bound | -help: the trait `UnsizedConstParamTy` is not implemented for `NotParam` +help: the trait `ConstParamTy_` is not implemented for `NotParam` --> $DIR/const_param_ty_generic_bounds_do_not_hold.rs:5:1 | LL | struct NotParam; | ^^^^^^^^^^^^^^^ - = note: required for `&NotParam` to implement `UnsizedConstParamTy` + = note: required for `&NotParam` to implement `ConstParamTy_` note: required by a bound in `check` --> $DIR/const_param_ty_generic_bounds_do_not_hold.rs:7:13 | -LL | fn check() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` +LL | fn check() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` error[E0277]: `NotParam` can't be used as a const parameter type --> $DIR/const_param_ty_generic_bounds_do_not_hold.rs:11:13 @@ -22,17 +22,17 @@ error[E0277]: `NotParam` can't be used as a const parameter type LL | check::<[NotParam]>(); | ^^^^^^^^^^ unsatisfied trait bound | -help: the trait `UnsizedConstParamTy` is not implemented for `NotParam` +help: the trait `ConstParamTy_` is not implemented for `NotParam` --> $DIR/const_param_ty_generic_bounds_do_not_hold.rs:5:1 | LL | struct NotParam; | ^^^^^^^^^^^^^^^ - = note: required for `[NotParam]` to implement `UnsizedConstParamTy` + = note: required for `[NotParam]` to implement `ConstParamTy_` note: required by a bound in `check` --> $DIR/const_param_ty_generic_bounds_do_not_hold.rs:7:13 | -LL | fn check() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` +LL | fn check() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` error[E0277]: `NotParam` can't be used as a const parameter type --> $DIR/const_param_ty_generic_bounds_do_not_hold.rs:12:13 @@ -40,17 +40,17 @@ error[E0277]: `NotParam` can't be used as a const parameter type LL | check::<[NotParam; 17]>(); | ^^^^^^^^^^^^^^ unsatisfied trait bound | -help: the trait `UnsizedConstParamTy` is not implemented for `NotParam` +help: the trait `ConstParamTy_` is not implemented for `NotParam` --> $DIR/const_param_ty_generic_bounds_do_not_hold.rs:5:1 | LL | struct NotParam; | ^^^^^^^^^^^^^^^ - = note: required for `[NotParam; 17]` to implement `UnsizedConstParamTy` + = note: required for `[NotParam; 17]` to implement `ConstParamTy_` note: required by a bound in `check` --> $DIR/const_param_ty_generic_bounds_do_not_hold.rs:7:13 | -LL | fn check() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` +LL | fn check() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check` error: aborting due to 3 previous errors diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_good.rs b/tests/ui/const-generics/adt_const_params/const_param_ty_good.rs index 98a8eb6ee950..24bbc5a9a238 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_good.rs +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_good.rs @@ -3,7 +3,7 @@ #![feature(adt_const_params, unsized_const_params)] #![allow(incomplete_features)] -use std::marker::UnsizedConstParamTy; +use std::marker::{ConstParamTy, ConstParamTy_}; #[derive(PartialEq, Eq)] struct S { @@ -11,15 +11,15 @@ struct S { gen: T, } -impl UnsizedConstParamTy for S {} +impl ConstParamTy_ for S {} -#[derive(PartialEq, Eq, UnsizedConstParamTy)] +#[derive(PartialEq, Eq, ConstParamTy)] struct D { field: u8, gen: T, } -fn check() {} +fn check() {} fn main() { check::(); diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_field.rs b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_field.rs index 8b3d0546010d..0614ea97b1ad 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_field.rs +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_field.rs @@ -7,10 +7,10 @@ struct NotParam; #[derive(PartialEq, Eq)] struct CantParam(NotParam); -impl std::marker::UnsizedConstParamTy for CantParam {} +impl std::marker::ConstParamTy_ for CantParam {} //~^ error: the trait `ConstParamTy_` cannot be implemented for this type -#[derive(std::marker::UnsizedConstParamTy, Eq, PartialEq)] +#[derive(std::marker::ConstParamTy, Eq, PartialEq)] //~^ error: the trait `ConstParamTy_` cannot be implemented for this type struct CantParamDerive(NotParam); diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_field.stderr b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_field.stderr index a4e5736d8342..fd1836802c4a 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_field.stderr +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_bad_field.stderr @@ -1,17 +1,17 @@ error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type - --> $DIR/const_param_ty_impl_bad_field.rs:10:43 + --> $DIR/const_param_ty_impl_bad_field.rs:10:37 | LL | struct CantParam(NotParam); | -------- this field does not implement `ConstParamTy_` LL | -LL | impl std::marker::UnsizedConstParamTy for CantParam {} - | ^^^^^^^^^ +LL | impl std::marker::ConstParamTy_ for CantParam {} + | ^^^^^^^^^ error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type --> $DIR/const_param_ty_impl_bad_field.rs:13:10 | -LL | #[derive(std::marker::UnsizedConstParamTy, Eq, PartialEq)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[derive(std::marker::ConstParamTy, Eq, PartialEq)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | struct CantParamDerive(NotParam); | -------- this field does not implement `ConstParamTy_` diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.rs b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.rs index e743b78fd134..a1c8eccfb095 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.rs +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.rs @@ -3,20 +3,20 @@ #[derive(PartialEq, Eq)] struct ImplementsConstParamTy; -impl std::marker::UnsizedConstParamTy for ImplementsConstParamTy {} +impl std::marker::ConstParamTy_ for ImplementsConstParamTy {} struct CantParam(ImplementsConstParamTy); -impl std::marker::UnsizedConstParamTy for CantParam {} +impl std::marker::ConstParamTy_ for CantParam {} //~^ error: the type `CantParam` does not `#[derive(PartialEq)]` //~| ERROR the trait bound `CantParam: Eq` is not satisfied -#[derive(std::marker::UnsizedConstParamTy)] +#[derive(std::marker::ConstParamTy)] //~^ error: the type `CantParamDerive` does not `#[derive(PartialEq)]` //~| ERROR the trait bound `CantParamDerive: Eq` is not satisfied struct CantParamDerive(ImplementsConstParamTy); -fn check() {} +fn check() {} fn main() { check::(); diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.stderr b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.stderr index d3141381db8c..c6b791ed9674 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.stderr +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_no_structural_eq.stderr @@ -1,10 +1,10 @@ error[E0277]: the trait bound `CantParam: Eq` is not satisfied - --> $DIR/const_param_ty_impl_no_structural_eq.rs:10:43 + --> $DIR/const_param_ty_impl_no_structural_eq.rs:10:37 | -LL | impl std::marker::UnsizedConstParamTy for CantParam {} - | ^^^^^^^^^ the trait `Eq` is not implemented for `CantParam` +LL | impl std::marker::ConstParamTy_ for CantParam {} + | ^^^^^^^^^ the trait `Eq` is not implemented for `CantParam` | -note: required by a bound in `UnsizedConstParamTy` +note: required by a bound in `ConstParamTy_` --> $SRC_DIR/core/src/marker.rs:LL:COL help: consider annotating `CantParam` with `#[derive(Eq)]` | @@ -13,26 +13,26 @@ LL | struct CantParam(ImplementsConstParamTy); | error[E0277]: the type `CantParam` does not `#[derive(PartialEq)]` - --> $DIR/const_param_ty_impl_no_structural_eq.rs:10:43 + --> $DIR/const_param_ty_impl_no_structural_eq.rs:10:37 | -LL | impl std::marker::UnsizedConstParamTy for CantParam {} - | ^^^^^^^^^ unsatisfied trait bound +LL | impl std::marker::ConstParamTy_ for CantParam {} + | ^^^^^^^^^ unsatisfied trait bound | help: the trait `StructuralPartialEq` is not implemented for `CantParam` --> $DIR/const_param_ty_impl_no_structural_eq.rs:8:1 | LL | struct CantParam(ImplementsConstParamTy); | ^^^^^^^^^^^^^^^^ -note: required by a bound in `UnsizedConstParamTy` +note: required by a bound in `ConstParamTy_` --> $SRC_DIR/core/src/marker.rs:LL:COL error[E0277]: the trait bound `CantParamDerive: Eq` is not satisfied --> $DIR/const_param_ty_impl_no_structural_eq.rs:14:10 | -LL | #[derive(std::marker::UnsizedConstParamTy)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Eq` is not implemented for `CantParamDerive` +LL | #[derive(std::marker::ConstParamTy)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Eq` is not implemented for `CantParamDerive` | -note: required by a bound in `UnsizedConstParamTy` +note: required by a bound in `ConstParamTy_` --> $SRC_DIR/core/src/marker.rs:LL:COL help: consider annotating `CantParamDerive` with `#[derive(Eq)]` | @@ -43,15 +43,15 @@ LL | struct CantParamDerive(ImplementsConstParamTy); error[E0277]: the type `CantParamDerive` does not `#[derive(PartialEq)]` --> $DIR/const_param_ty_impl_no_structural_eq.rs:14:10 | -LL | #[derive(std::marker::UnsizedConstParamTy)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound +LL | #[derive(std::marker::ConstParamTy)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound | help: the trait `StructuralPartialEq` is not implemented for `CantParamDerive` --> $DIR/const_param_ty_impl_no_structural_eq.rs:17:1 | LL | struct CantParamDerive(ImplementsConstParamTy); | ^^^^^^^^^^^^^^^^^^^^^^ -note: required by a bound in `UnsizedConstParamTy` +note: required by a bound in `ConstParamTy_` --> $SRC_DIR/core/src/marker.rs:LL:COL error: aborting due to 4 previous errors diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_union.rs b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_union.rs index 236b3bc162aa..0c9b12805f75 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_union.rs +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_union.rs @@ -12,10 +12,10 @@ impl PartialEq for Union { } impl Eq for Union {} -impl std::marker::UnsizedConstParamTy for Union {} +impl std::marker::ConstParamTy_ for Union {} //~^ ERROR the trait `ConstParamTy` may not be implemented for this type -#[derive(std::marker::UnsizedConstParamTy)] +#[derive(std::marker::ConstParamTy)] //~^ ERROR this trait cannot be derived for unions union UnionDerive { a: u8, diff --git a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_union.stderr b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_union.stderr index 837c289c9248..cc2147b49aea 100644 --- a/tests/ui/const-generics/adt_const_params/const_param_ty_impl_union.stderr +++ b/tests/ui/const-generics/adt_const_params/const_param_ty_impl_union.stderr @@ -1,14 +1,14 @@ error: this trait cannot be derived for unions --> $DIR/const_param_ty_impl_union.rs:18:10 | -LL | #[derive(std::marker::UnsizedConstParamTy)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[derive(std::marker::ConstParamTy)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: the trait `ConstParamTy` may not be implemented for this type - --> $DIR/const_param_ty_impl_union.rs:15:43 + --> $DIR/const_param_ty_impl_union.rs:15:37 | -LL | impl std::marker::UnsizedConstParamTy for Union {} - | ^^^^^ type is not a structure or enumeration +LL | impl std::marker::ConstParamTy_ for Union {} + | ^^^^^ type is not a structure or enumeration error: aborting due to 2 previous errors diff --git a/tests/ui/const-generics/adt_const_params/nested_bad_const_param_ty.rs b/tests/ui/const-generics/adt_const_params/nested_bad_const_param_ty.rs index 34ea143d2546..d42ef90e1b18 100644 --- a/tests/ui/const-generics/adt_const_params/nested_bad_const_param_ty.rs +++ b/tests/ui/const-generics/adt_const_params/nested_bad_const_param_ty.rs @@ -5,17 +5,14 @@ use std::marker::ConstParamTy; #[derive(ConstParamTy)] //~^ ERROR the trait `ConstParamTy_` cannot be implemented for this ty -//~| ERROR the trait `ConstParamTy_` cannot be implemented for this ty struct Foo([*const u8; 1]); #[derive(ConstParamTy)] //~^ ERROR the trait `ConstParamTy_` cannot be implemented for this ty -//~| ERROR the trait `ConstParamTy_` cannot be implemented for this ty struct Foo2([*mut u8; 1]); #[derive(ConstParamTy)] //~^ ERROR the trait `ConstParamTy_` cannot be implemented for this ty -//~| ERROR the trait `ConstParamTy_` cannot be implemented for this ty struct Foo3([fn(); 1]); fn main() {} diff --git a/tests/ui/const-generics/adt_const_params/nested_bad_const_param_ty.stderr b/tests/ui/const-generics/adt_const_params/nested_bad_const_param_ty.stderr index 6b8d2394a861..442ec6b96cef 100644 --- a/tests/ui/const-generics/adt_const_params/nested_bad_const_param_ty.stderr +++ b/tests/ui/const-generics/adt_const_params/nested_bad_const_param_ty.stderr @@ -3,91 +3,46 @@ error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type | LL | #[derive(ConstParamTy)] | ^^^^^^^^^^^^ -... +LL | LL | struct Foo([*const u8; 1]); | -------------- this field does not implement `ConstParamTy_` | note: the `ConstParamTy_` impl for `[*const u8; 1]` requires that `*const u8: ConstParamTy_` - --> $DIR/nested_bad_const_param_ty.rs:9:12 + --> $DIR/nested_bad_const_param_ty.rs:8:12 | LL | struct Foo([*const u8; 1]); | ^^^^^^^^^^^^^^ error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type - --> $DIR/nested_bad_const_param_ty.rs:11:10 + --> $DIR/nested_bad_const_param_ty.rs:10:10 | LL | #[derive(ConstParamTy)] | ^^^^^^^^^^^^ -... +LL | LL | struct Foo2([*mut u8; 1]); | ------------ this field does not implement `ConstParamTy_` | note: the `ConstParamTy_` impl for `[*mut u8; 1]` requires that `*mut u8: ConstParamTy_` - --> $DIR/nested_bad_const_param_ty.rs:14:13 + --> $DIR/nested_bad_const_param_ty.rs:12:13 | LL | struct Foo2([*mut u8; 1]); | ^^^^^^^^^^^^ error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type - --> $DIR/nested_bad_const_param_ty.rs:16:10 + --> $DIR/nested_bad_const_param_ty.rs:14:10 | LL | #[derive(ConstParamTy)] | ^^^^^^^^^^^^ -... +LL | LL | struct Foo3([fn(); 1]); | --------- this field does not implement `ConstParamTy_` | note: the `ConstParamTy_` impl for `[fn(); 1]` requires that `fn(): ConstParamTy_` - --> $DIR/nested_bad_const_param_ty.rs:19:13 + --> $DIR/nested_bad_const_param_ty.rs:16:13 | LL | struct Foo3([fn(); 1]); | ^^^^^^^^^ -error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type - --> $DIR/nested_bad_const_param_ty.rs:6:10 - | -LL | #[derive(ConstParamTy)] - | ^^^^^^^^^^^^ -... -LL | struct Foo([*const u8; 1]); - | -------------- this field does not implement `ConstParamTy_` - | -note: the `ConstParamTy_` impl for `[*const u8; 1]` requires that `*const u8: UnsizedConstParamTy` - --> $DIR/nested_bad_const_param_ty.rs:9:12 - | -LL | struct Foo([*const u8; 1]); - | ^^^^^^^^^^^^^^ - -error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type - --> $DIR/nested_bad_const_param_ty.rs:11:10 - | -LL | #[derive(ConstParamTy)] - | ^^^^^^^^^^^^ -... -LL | struct Foo2([*mut u8; 1]); - | ------------ this field does not implement `ConstParamTy_` - | -note: the `ConstParamTy_` impl for `[*mut u8; 1]` requires that `*mut u8: UnsizedConstParamTy` - --> $DIR/nested_bad_const_param_ty.rs:14:13 - | -LL | struct Foo2([*mut u8; 1]); - | ^^^^^^^^^^^^ - -error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type - --> $DIR/nested_bad_const_param_ty.rs:16:10 - | -LL | #[derive(ConstParamTy)] - | ^^^^^^^^^^^^ -... -LL | struct Foo3([fn(); 1]); - | --------- this field does not implement `ConstParamTy_` - | -note: the `ConstParamTy_` impl for `[fn(); 1]` requires that `fn(): UnsizedConstParamTy` - --> $DIR/nested_bad_const_param_ty.rs:19:13 - | -LL | struct Foo3([fn(); 1]); - | ^^^^^^^^^ - -error: aborting due to 6 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0204`. diff --git a/tests/ui/const-generics/adt_const_params/non_valtreeable_const_arg-2.stderr b/tests/ui/const-generics/adt_const_params/non_valtreeable_const_arg-2.stderr index b13f76eabadb..72dfda50ea5c 100644 --- a/tests/ui/const-generics/adt_const_params/non_valtreeable_const_arg-2.stderr +++ b/tests/ui/const-generics/adt_const_params/non_valtreeable_const_arg-2.stderr @@ -9,11 +9,13 @@ help: you might be missing a const parameter LL | impl Wrapper<{ bar() }> { | +++++++++++++++++++++++ -error[E0741]: using function pointers as const generic parameters is forbidden +error: using function pointers as const generic parameters is forbidden --> $DIR/non_valtreeable_const_arg-2.rs:8:25 | LL | struct Wrapper; | ^^^^ + | + = note: the only supported types are integers, `bool`, and `char` error[E0599]: the function or associated item `call` exists for struct `Wrapper`, but its trait bounds were not satisfied --> $DIR/non_valtreeable_const_arg-2.rs:17:26 @@ -35,5 +37,5 @@ note: the trait `Fn` must be implemented error: aborting due to 3 previous errors -Some errors have detailed explanations: E0425, E0599, E0741. +Some errors have detailed explanations: E0425, E0599. For more information about an error, try `rustc --explain E0425`. diff --git a/tests/ui/const-generics/adt_const_params/reference_pointee_is_const_param-1.rs b/tests/ui/const-generics/adt_const_params/reference_pointee_is_const_param-1.rs index a1ee1c4cdd58..937acf2e6bb6 100644 --- a/tests/ui/const-generics/adt_const_params/reference_pointee_is_const_param-1.rs +++ b/tests/ui/const-generics/adt_const_params/reference_pointee_is_const_param-1.rs @@ -1,11 +1,11 @@ #![feature(adt_const_params, unsized_const_params)] #![allow(incomplete_features)] -use std::marker::UnsizedConstParamTy; +use std::marker::ConstParamTy_; struct Foo; -impl UnsizedConstParamTy for &'static Foo {} +impl ConstParamTy_ for &'static Foo {} //~^ ERROR: the trait `ConstParamTy_` cannot be implemented for this type fn main() {} diff --git a/tests/ui/const-generics/adt_const_params/reference_pointee_is_const_param-1.stderr b/tests/ui/const-generics/adt_const_params/reference_pointee_is_const_param-1.stderr index 5ca8e6c75167..b977cf5d44e0 100644 --- a/tests/ui/const-generics/adt_const_params/reference_pointee_is_const_param-1.stderr +++ b/tests/ui/const-generics/adt_const_params/reference_pointee_is_const_param-1.stderr @@ -1,8 +1,8 @@ error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type - --> $DIR/reference_pointee_is_const_param-1.rs:8:30 + --> $DIR/reference_pointee_is_const_param-1.rs:8:24 | -LL | impl UnsizedConstParamTy for &'static Foo {} - | ^^^^^^^^^^^^ this field does not implement `ConstParamTy_` +LL | impl ConstParamTy_ for &'static Foo {} + | ^^^^^^^^^^^^ this field does not implement `ConstParamTy_` error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/adt_const_params/reference_pointee_is_const_param-2.rs b/tests/ui/const-generics/adt_const_params/reference_pointee_is_const_param-2.rs index ac1b522f469e..605fed26c9c3 100644 --- a/tests/ui/const-generics/adt_const_params/reference_pointee_is_const_param-2.rs +++ b/tests/ui/const-generics/adt_const_params/reference_pointee_is_const_param-2.rs @@ -3,12 +3,12 @@ // Regression test for #119299 -use std::marker::UnsizedConstParamTy; +use std::marker::ConstParamTy_; #[derive(Eq, PartialEq)] struct ConstStrU(*const u8, usize); -impl UnsizedConstParamTy for &'static ConstStrU {} +impl ConstParamTy_ for &'static ConstStrU {} //~^ ERROR: the trait `ConstParamTy_` cannot be implemented for this type impl ConstStrU { diff --git a/tests/ui/const-generics/adt_const_params/reference_pointee_is_const_param-2.stderr b/tests/ui/const-generics/adt_const_params/reference_pointee_is_const_param-2.stderr index 5e5f6cc642d3..0fe92f253a05 100644 --- a/tests/ui/const-generics/adt_const_params/reference_pointee_is_const_param-2.stderr +++ b/tests/ui/const-generics/adt_const_params/reference_pointee_is_const_param-2.stderr @@ -1,8 +1,8 @@ error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type - --> $DIR/reference_pointee_is_const_param-2.rs:11:30 + --> $DIR/reference_pointee_is_const_param-2.rs:11:24 | -LL | impl UnsizedConstParamTy for &'static ConstStrU {} - | ^^^^^^^^^^^^^^^^^^ this field does not implement `ConstParamTy_` +LL | impl ConstParamTy_ for &'static ConstStrU {} + | ^^^^^^^^^^^^^^^^^^ this field does not implement `ConstParamTy_` error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/adt_const_params/trait_objects_as_a_const_generic.rs b/tests/ui/const-generics/adt_const_params/trait_objects_as_a_const_generic.rs index b0934508399d..0fe86adb291d 100644 --- a/tests/ui/const-generics/adt_const_params/trait_objects_as_a_const_generic.rs +++ b/tests/ui/const-generics/adt_const_params/trait_objects_as_a_const_generic.rs @@ -1,11 +1,11 @@ #![feature(adt_const_params, unsized_const_params)] #![allow(incomplete_features)] -use std::marker::UnsizedConstParamTy; +use std::marker::ConstParamTy_; trait Trait {} -impl UnsizedConstParamTy for dyn Trait {} +impl ConstParamTy_ for dyn Trait {} //~^ ERROR: the trait `ConstParamTy` may not be implemented for this type fn foo() {} diff --git a/tests/ui/const-generics/adt_const_params/trait_objects_as_a_const_generic.stderr b/tests/ui/const-generics/adt_const_params/trait_objects_as_a_const_generic.stderr index 9933ba6e335b..67c6314e2975 100644 --- a/tests/ui/const-generics/adt_const_params/trait_objects_as_a_const_generic.stderr +++ b/tests/ui/const-generics/adt_const_params/trait_objects_as_a_const_generic.stderr @@ -1,8 +1,8 @@ error: the trait `ConstParamTy` may not be implemented for this type - --> $DIR/trait_objects_as_a_const_generic.rs:8:30 + --> $DIR/trait_objects_as_a_const_generic.rs:8:24 | -LL | impl UnsizedConstParamTy for dyn Trait {} - | ^^^^^^^^^ type is not a structure or enumeration +LL | impl ConstParamTy_ for dyn Trait {} + | ^^^^^^^^^ type is not a structure or enumeration error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/adt_const_params/unsized_field-1.rs b/tests/ui/const-generics/adt_const_params/unsized_field-1.rs index f6e5bd6e355d..5db031cb900f 100644 --- a/tests/ui/const-generics/adt_const_params/unsized_field-1.rs +++ b/tests/ui/const-generics/adt_const_params/unsized_field-1.rs @@ -14,7 +14,10 @@ struct A([u8]); struct B(&'static [u8]); #[derive(ConstParamTy, Eq, PartialEq)] -//~^ ERROR: the trait `ConstParamTy_` cannot be implemented for this type struct C(unsized_const_param::Foo); +#[derive(std::marker::ConstParamTy, Eq, PartialEq)] +//~^ ERROR: the trait `ConstParamTy_` cannot be implemented for this type +struct D(unsized_const_param::GenericNotUnsizedParam<&'static [u8]>); + fn main() {} diff --git a/tests/ui/const-generics/adt_const_params/unsized_field-1.stderr b/tests/ui/const-generics/adt_const_params/unsized_field-1.stderr index 3089b30bd76d..a5ae5c726da8 100644 --- a/tests/ui/const-generics/adt_const_params/unsized_field-1.stderr +++ b/tests/ui/const-generics/adt_const_params/unsized_field-1.stderr @@ -6,6 +6,12 @@ LL | #[derive(ConstParamTy, Eq, PartialEq)] LL | LL | struct A([u8]); | ---- this field does not implement `ConstParamTy_` + | +note: the `ConstParamTy_` impl for `[u8]` requires that `unstable feature: `unsized_const_params`` + --> $DIR/unsized_field-1.rs:10:10 + | +LL | struct A([u8]); + | ^^^^ error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type --> $DIR/unsized_field-1.rs:12:10 @@ -15,15 +21,27 @@ LL | #[derive(ConstParamTy, Eq, PartialEq)] LL | LL | struct B(&'static [u8]); | ------------- this field does not implement `ConstParamTy_` + | +note: the `ConstParamTy_` impl for `&'static [u8]` requires that `unstable feature: `unsized_const_params`` + --> $DIR/unsized_field-1.rs:14:10 + | +LL | struct B(&'static [u8]); + | ^^^^^^^^^^^^^ error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type - --> $DIR/unsized_field-1.rs:16:10 + --> $DIR/unsized_field-1.rs:19:10 | -LL | #[derive(ConstParamTy, Eq, PartialEq)] - | ^^^^^^^^^^^^ +LL | #[derive(std::marker::ConstParamTy, Eq, PartialEq)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ LL | -LL | struct C(unsized_const_param::Foo); - | ------------------------ this field does not implement `ConstParamTy_` +LL | struct D(unsized_const_param::GenericNotUnsizedParam<&'static [u8]>); + | ---------------------------------------------------------- this field does not implement `ConstParamTy_` + | +note: the `ConstParamTy_` impl for `GenericNotUnsizedParam<&'static [u8]>` requires that `unstable feature: `unsized_const_params`` + --> $DIR/unsized_field-1.rs:21:10 + | +LL | struct D(unsized_const_param::GenericNotUnsizedParam<&'static [u8]>); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 3 previous errors diff --git a/tests/ui/const-generics/adt_const_params/unsized_field-2.rs b/tests/ui/const-generics/adt_const_params/unsized_field-2.rs deleted file mode 100644 index e4a3a481b4e3..000000000000 --- a/tests/ui/const-generics/adt_const_params/unsized_field-2.rs +++ /dev/null @@ -1,14 +0,0 @@ -//@ aux-build:unsized_const_param.rs -#![feature(adt_const_params, unsized_const_params)] -//~^ WARN: the feature `unsized_const_params` is incomplete - -extern crate unsized_const_param; - -#[derive(std::marker::ConstParamTy, Eq, PartialEq)] -//~^ ERROR: the trait `ConstParamTy_` cannot be implemented for this type -struct A(unsized_const_param::GenericNotUnsizedParam<&'static [u8]>); - -#[derive(std::marker::UnsizedConstParamTy, Eq, PartialEq)] -struct B(unsized_const_param::GenericNotUnsizedParam<&'static [u8]>); - -fn main() {} diff --git a/tests/ui/const-generics/adt_const_params/unsized_field-2.stderr b/tests/ui/const-generics/adt_const_params/unsized_field-2.stderr deleted file mode 100644 index cef70ca0463b..000000000000 --- a/tests/ui/const-generics/adt_const_params/unsized_field-2.stderr +++ /dev/null @@ -1,27 +0,0 @@ -warning: the feature `unsized_const_params` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/unsized_field-2.rs:2:30 - | -LL | #![feature(adt_const_params, unsized_const_params)] - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #95174 for more information - = note: `#[warn(incomplete_features)]` on by default - -error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type - --> $DIR/unsized_field-2.rs:7:10 - | -LL | #[derive(std::marker::ConstParamTy, Eq, PartialEq)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ -LL | -LL | struct A(unsized_const_param::GenericNotUnsizedParam<&'static [u8]>); - | ---------------------------------------------------------- this field does not implement `ConstParamTy_` - | -note: the `ConstParamTy_` impl for `GenericNotUnsizedParam<&'static [u8]>` requires that `&'static [u8]: ConstParamTy_` - --> $DIR/unsized_field-2.rs:9:10 - | -LL | struct A(unsized_const_param::GenericNotUnsizedParam<&'static [u8]>); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error; 1 warning emitted - -For more information about this error, try `rustc --explain E0204`. diff --git a/tests/ui/const-generics/adt_const_params/unsizing-wfcheck-issue-126272.rs b/tests/ui/const-generics/adt_const_params/unsizing-wfcheck-issue-126272.rs index 311f507d3c7f..500e8e22b0e3 100644 --- a/tests/ui/const-generics/adt_const_params/unsizing-wfcheck-issue-126272.rs +++ b/tests/ui/const-generics/adt_const_params/unsizing-wfcheck-issue-126272.rs @@ -7,7 +7,6 @@ use std::marker::ConstParamTy; #[derive(Debug, PartialEq, Eq, ConstParamTy)] //~^ ERROR the trait `ConstParamTy_` -//~| ERROR the trait `ConstParamTy_` struct Foo { nested: &'static Bar, //~^ ERROR the size for values diff --git a/tests/ui/const-generics/adt_const_params/unsizing-wfcheck-issue-126272.stderr b/tests/ui/const-generics/adt_const_params/unsizing-wfcheck-issue-126272.stderr index 992a27c1c0e9..d4aeb91999c6 100644 --- a/tests/ui/const-generics/adt_const_params/unsizing-wfcheck-issue-126272.stderr +++ b/tests/ui/const-generics/adt_const_params/unsizing-wfcheck-issue-126272.stderr @@ -1,17 +1,17 @@ error[E0277]: the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time - --> $DIR/unsizing-wfcheck-issue-126272.rs:12:13 + --> $DIR/unsizing-wfcheck-issue-126272.rs:11:13 | LL | nested: &'static Bar, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `(dyn Debug + 'static)` note: required by an implicit `Sized` bound in `Bar` - --> $DIR/unsizing-wfcheck-issue-126272.rs:21:12 + --> $DIR/unsizing-wfcheck-issue-126272.rs:20:12 | LL | struct Bar(T); | ^ required by the implicit `Sized` requirement on this type parameter in `Bar` help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box` - --> $DIR/unsizing-wfcheck-issue-126272.rs:21:12 + --> $DIR/unsizing-wfcheck-issue-126272.rs:20:12 | LL | struct Bar(T); | ^ - ...if indirection were used here: `Box` @@ -26,34 +26,25 @@ LL | #[derive(Debug, PartialEq, Eq, ConstParamTy)] ... LL | nested: &'static Bar, | ----------------------------------------- this field does not implement `ConstParamTy_` - -error[E0204]: the trait `ConstParamTy_` cannot be implemented for this type - --> $DIR/unsizing-wfcheck-issue-126272.rs:8:32 | -LL | #[derive(Debug, PartialEq, Eq, ConstParamTy)] - | ^^^^^^^^^^^^ -... +note: the `ConstParamTy_` impl for `&'static Bar<(dyn Debug + 'static)>` requires that `(dyn Debug + 'static): ConstParamTy_` + --> $DIR/unsizing-wfcheck-issue-126272.rs:11:13 + | LL | nested: &'static Bar, - | ----------------------------------------- this field does not implement `ConstParamTy_` - | + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: the `ConstParamTy_` impl for `&'static Bar<(dyn Debug + 'static)>` requires that `(dyn Debug + 'static): Eq` - --> $DIR/unsizing-wfcheck-issue-126272.rs:12:13 + --> $DIR/unsizing-wfcheck-issue-126272.rs:11:13 | LL | nested: &'static Bar, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: the `ConstParamTy_` impl for `&'static Bar<(dyn Debug + 'static)>` requires that `(dyn Debug + 'static): Sized` - --> $DIR/unsizing-wfcheck-issue-126272.rs:12:13 - | -LL | nested: &'static Bar, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: the `ConstParamTy_` impl for `&'static Bar<(dyn Debug + 'static)>` requires that `(dyn Debug + 'static): UnsizedConstParamTy` - --> $DIR/unsizing-wfcheck-issue-126272.rs:12:13 + --> $DIR/unsizing-wfcheck-issue-126272.rs:11:13 | LL | nested: &'static Bar, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time - --> $DIR/unsizing-wfcheck-issue-126272.rs:12:5 + --> $DIR/unsizing-wfcheck-issue-126272.rs:11:5 | LL | #[derive(Debug, PartialEq, Eq, ConstParamTy)] | ----- in this derive macro expansion @@ -64,7 +55,7 @@ LL | nested: &'static Bar, = help: the trait `Sized` is not implemented for `(dyn Debug + 'static)` = help: the trait `Debug` is implemented for `Bar` note: required for `Bar<(dyn Debug + 'static)>` to implement `Debug` - --> $DIR/unsizing-wfcheck-issue-126272.rs:20:10 + --> $DIR/unsizing-wfcheck-issue-126272.rs:19:10 | LL | #[derive(Debug, PartialEq, Eq, ConstParamTy)] | ^^^^^ @@ -75,7 +66,7 @@ LL | struct Bar(T); = note: required for the cast from `&&&'static Bar<(dyn Debug + 'static)>` to `&dyn Debug` error[E0369]: binary operation `==` cannot be applied to type `&Bar` - --> $DIR/unsizing-wfcheck-issue-126272.rs:12:5 + --> $DIR/unsizing-wfcheck-issue-126272.rs:11:5 | LL | #[derive(Debug, PartialEq, Eq, ConstParamTy)] | --------- in this derive macro expansion @@ -84,7 +75,7 @@ LL | nested: &'static Bar, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `dyn Debug: Eq` is not satisfied - --> $DIR/unsizing-wfcheck-issue-126272.rs:12:5 + --> $DIR/unsizing-wfcheck-issue-126272.rs:11:5 | LL | #[derive(Debug, PartialEq, Eq, ConstParamTy)] | -- in this derive macro expansion @@ -94,7 +85,7 @@ LL | nested: &'static Bar, | = help: the trait `Eq` is implemented for `Bar` note: required for `Bar` to implement `Eq` - --> $DIR/unsizing-wfcheck-issue-126272.rs:20:28 + --> $DIR/unsizing-wfcheck-issue-126272.rs:19:28 | LL | #[derive(Debug, PartialEq, Eq, ConstParamTy)] | ^^ unsatisfied trait bound introduced in this `derive` macro @@ -104,7 +95,7 @@ note: required by a bound in `AssertParamIsEq` --> $SRC_DIR/core/src/cmp.rs:LL:COL error[E0277]: the size for values of type `dyn Debug` cannot be known at compilation time - --> $DIR/unsizing-wfcheck-issue-126272.rs:12:5 + --> $DIR/unsizing-wfcheck-issue-126272.rs:11:5 | LL | #[derive(Debug, PartialEq, Eq, ConstParamTy)] | -- in this derive macro expansion @@ -114,12 +105,12 @@ LL | nested: &'static Bar, | = help: the trait `Sized` is not implemented for `dyn Debug` note: required by an implicit `Sized` bound in `Bar` - --> $DIR/unsizing-wfcheck-issue-126272.rs:21:12 + --> $DIR/unsizing-wfcheck-issue-126272.rs:20:12 | LL | struct Bar(T); | ^ required by the implicit `Sized` requirement on this type parameter in `Bar` help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box` - --> $DIR/unsizing-wfcheck-issue-126272.rs:21:12 + --> $DIR/unsizing-wfcheck-issue-126272.rs:20:12 | LL | struct Bar(T); | ^ - ...if indirection were used here: `Box` @@ -127,26 +118,26 @@ LL | struct Bar(T); | this could be changed to `T: ?Sized`... error[E0277]: the size for values of type `(dyn Debug + 'static)` cannot be known at compilation time - --> $DIR/unsizing-wfcheck-issue-126272.rs:26:33 + --> $DIR/unsizing-wfcheck-issue-126272.rs:25:33 | LL | let x: Test<{ Foo { nested: &Bar(4) } }> = Test; | ^^^^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `(dyn Debug + 'static)` note: required by an implicit `Sized` bound in `Bar` - --> $DIR/unsizing-wfcheck-issue-126272.rs:21:12 + --> $DIR/unsizing-wfcheck-issue-126272.rs:20:12 | LL | struct Bar(T); | ^ required by the implicit `Sized` requirement on this type parameter in `Bar` help: you could relax the implicit `Sized` bound on `T` if it were used through indirection like `&T` or `Box` - --> $DIR/unsizing-wfcheck-issue-126272.rs:21:12 + --> $DIR/unsizing-wfcheck-issue-126272.rs:20:12 | LL | struct Bar(T); | ^ - ...if indirection were used here: `Box` | | | this could be changed to `T: ?Sized`... -error: aborting due to 8 previous errors +error: aborting due to 7 previous errors Some errors have detailed explanations: E0204, E0277, E0369. For more information about an error, try `rustc --explain E0204`. diff --git a/tests/ui/const-generics/const-param-with-additional-obligations.rs b/tests/ui/const-generics/const-param-with-additional-obligations.rs index 98097e86c7df..5110f95d5bf9 100644 --- a/tests/ui/const-generics/const-param-with-additional-obligations.rs +++ b/tests/ui/const-generics/const-param-with-additional-obligations.rs @@ -1,14 +1,14 @@ #![feature(adt_const_params, unsized_const_params)] #![allow(incomplete_features)] -use std::marker::UnsizedConstParamTy; +use std::marker::ConstParamTy_; #[derive(Eq, PartialEq)] struct Foo(T); trait Other {} -impl UnsizedConstParamTy for Foo where T: Other + UnsizedConstParamTy {} +impl ConstParamTy_ for Foo where T: Other + ConstParamTy_ {} fn foo>() {} //~^ ERROR `Foo` must implement `ConstParamTy` to be used as the type of a const generic parameter diff --git a/tests/ui/const-generics/generic_const_parameter_types/no_const_param_ty_bound.stderr b/tests/ui/const-generics/generic_const_parameter_types/no_const_param_ty_bound.stderr index 4ea323f4a5c8..1eacc28b2625 100644 --- a/tests/ui/const-generics/generic_const_parameter_types/no_const_param_ty_bound.stderr +++ b/tests/ui/const-generics/generic_const_parameter_types/no_const_param_ty_bound.stderr @@ -4,7 +4,7 @@ error[E0741]: `[T; N]` can't be used as a const parameter type LL | struct UsesType(PhantomData); | ^^^^^^ | - = note: `T` must implement `UnsizedConstParamTy`, but it does not + = note: `T` must implement `ConstParamTy_`, but it does not error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/issue-66451.rs b/tests/ui/const-generics/issue-66451.rs index 0b8693e0e675..9ce7658586ad 100644 --- a/tests/ui/const-generics/issue-66451.rs +++ b/tests/ui/const-generics/issue-66451.rs @@ -1,15 +1,15 @@ #![feature(adt_const_params, unsized_const_params)] #![allow(incomplete_features)] -use std::marker::UnsizedConstParamTy; +use std::marker::ConstParamTy; -#[derive(Debug, PartialEq, Eq, UnsizedConstParamTy)] +#[derive(Debug, PartialEq, Eq, ConstParamTy)] struct Foo { value: i32, nested: &'static Bar, } -#[derive(Debug, PartialEq, Eq, UnsizedConstParamTy)] +#[derive(Debug, PartialEq, Eq, ConstParamTy)] struct Bar(T); struct Test; diff --git a/tests/ui/const-generics/issues/issue-63322-forbid-dyn.full.stderr b/tests/ui/const-generics/issues/issue-63322-forbid-dyn.full.stderr index 8ea96428deb3..1301ca92f015 100644 --- a/tests/ui/const-generics/issues/issue-63322-forbid-dyn.full.stderr +++ b/tests/ui/const-generics/issues/issue-63322-forbid-dyn.full.stderr @@ -4,7 +4,7 @@ error[E0741]: `&'static (dyn A + 'static)` can't be used as a const parameter ty LL | fn test() { | ^^^^^^^^^^^^^^ | - = note: `(dyn A + 'static)` must implement `UnsizedConstParamTy`, but it does not + = note: `(dyn A + 'static)` must implement `ConstParamTy_`, but it does not error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/slice-const-param-mismatch.adt_const_params.stderr b/tests/ui/const-generics/slice-const-param-mismatch.adt_const_params.stderr index bcb2bd255dab..d970b12df6a1 100644 --- a/tests/ui/const-generics/slice-const-param-mismatch.adt_const_params.stderr +++ b/tests/ui/const-generics/slice-const-param-mismatch.adt_const_params.stderr @@ -1,14 +1,22 @@ -error[E0741]: `&'static str` can't be used as a const parameter type - --> $DIR/slice-const-param-mismatch.rs:8:29 +error[E0658]: use of unstable library feature `unsized_const_params` + --> $DIR/slice-const-param-mismatch.rs:8:20 | LL | struct ConstString; - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(unsized_const_params)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: required for `&'static str` to implement `ConstParamTy_` -error[E0741]: `&'static [u8]` can't be used as a const parameter type - --> $DIR/slice-const-param-mismatch.rs:11:28 +error[E0658]: use of unstable library feature `unsized_const_params` + --> $DIR/slice-const-param-mismatch.rs:11:19 | LL | struct ConstBytes; - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(unsized_const_params)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: required for `&'static [u8]` to implement `ConstParamTy_` error[E0308]: mismatched types --> $DIR/slice-const-param-mismatch.rs:17:35 @@ -45,5 +53,5 @@ LL | let _: ConstBytes = ConstBytes::; error: aborting due to 5 previous errors -Some errors have detailed explanations: E0308, E0741. +Some errors have detailed explanations: E0308, E0658. For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/const-generics/slice-const-param.rs b/tests/ui/const-generics/slice-const-param.rs index 1c5088b52835..8b5e934149e4 100644 --- a/tests/ui/const-generics/slice-const-param.rs +++ b/tests/ui/const-generics/slice-const-param.rs @@ -12,7 +12,7 @@ pub fn function_with_bytes() -> &'static [u8] { } // Also check the codepaths for custom DST -#[derive(std::marker::UnsizedConstParamTy, PartialEq, Eq)] +#[derive(std::marker::ConstParamTy, PartialEq, Eq)] struct MyStr(str); fn function_with_my_str() -> &'static MyStr { diff --git a/tests/ui/const-generics/transmute-const-param-static-reference.adt_const_params.stderr b/tests/ui/const-generics/transmute-const-param-static-reference.adt_const_params.stderr index 7a936ced0305..c2351c707d7b 100644 --- a/tests/ui/const-generics/transmute-const-param-static-reference.adt_const_params.stderr +++ b/tests/ui/const-generics/transmute-const-param-static-reference.adt_const_params.stderr @@ -1,9 +1,13 @@ -error[E0741]: `&'static ()` can't be used as a const parameter type - --> $DIR/transmute-const-param-static-reference.rs:9:23 +error[E0658]: use of unstable library feature `unsized_const_params` + --> $DIR/transmute-const-param-static-reference.rs:9:14 | LL | struct Const; - | ^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(unsized_const_params)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: required for `&'static ()` to implement `ConstParamTy_` error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0741`. +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/const-generics/transmute-const-param-static-reference.rs b/tests/ui/const-generics/transmute-const-param-static-reference.rs index 0b47fd31eaf6..bf164bdadb0e 100644 --- a/tests/ui/const-generics/transmute-const-param-static-reference.rs +++ b/tests/ui/const-generics/transmute-const-param-static-reference.rs @@ -8,7 +8,7 @@ struct Const; //[min]~^ ERROR `&'static ()` is forbidden as the type of a const generic parameter -//[adt_const_params]~^^ ERROR `&'static ()` can't be used as a const parameter type +//[adt_const_params]~^^ ERROR use of unstable library feature `unsized_const_params` fn main() { const A: &'static () = unsafe { std::mem::transmute(10 as *const ()) }; diff --git a/tests/ui/const-generics/unsized_const_params/symbol_mangling_v0_str.rs b/tests/ui/const-generics/unsized_const_params/symbol_mangling_v0_str.rs index 359126f12516..429c401af1f5 100644 --- a/tests/ui/const-generics/unsized_const_params/symbol_mangling_v0_str.rs +++ b/tests/ui/const-generics/unsized_const_params/symbol_mangling_v0_str.rs @@ -1,13 +1,13 @@ //@ check-pass //@ compile-flags: -Csymbol-mangling-version=v0 #![allow(incomplete_features)] -#![feature(unsized_const_params)] +#![feature(adt_const_params, unsized_const_params)] // Regression test for #116303 #[derive(PartialEq, Eq)] struct MyStr(str); -impl std::marker::UnsizedConstParamTy for MyStr {} +impl std::marker::ConstParamTy_ for MyStr {} fn function_with_my_str() -> &'static MyStr { S diff --git a/tests/ui/feature-gates/feature-gate-unsized-const-params.rs b/tests/ui/feature-gates/feature-gate-unsized-const-params.rs index d088d382377c..56eebbd53c91 100644 --- a/tests/ui/feature-gates/feature-gate-unsized-const-params.rs +++ b/tests/ui/feature-gates/feature-gate-unsized-const-params.rs @@ -1,6 +1,8 @@ -struct Foo; -//~^ ERROR: `[u8]` is forbidden as the type of a const generic parameter -//~| HELP: add `#![feature(adt_const_params)]` to the crate -//~| HELP: add `#![feature(unsized_const_params)]` to the crate +#![feature(adt_const_params)] + +struct Bar(u8); + +struct Foo; +//~^ ERROR: `Bar` must implement `ConstParamTy` to be used as the type of a const generic parameter fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-unsized-const-params.stderr b/tests/ui/feature-gates/feature-gate-unsized-const-params.stderr index 85ca2f59cb63..d4f7d45ea323 100644 --- a/tests/ui/feature-gates/feature-gate-unsized-const-params.stderr +++ b/tests/ui/feature-gates/feature-gate-unsized-const-params.stderr @@ -1,18 +1,15 @@ -error: `[u8]` is forbidden as the type of a const generic parameter - --> $DIR/feature-gate-unsized-const-params.rs:1:21 +error[E0741]: `Bar` must implement `ConstParamTy` to be used as the type of a const generic parameter + --> $DIR/feature-gate-unsized-const-params.rs:5:21 | -LL | struct Foo; - | ^^^^ +LL | struct Foo; + | ^^^ | - = note: the only supported types are integers, `bool`, and `char` -help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types +help: add `#[derive(ConstParamTy, PartialEq, Eq)]` to the struct | -LL + #![feature(adt_const_params)] - | -help: add `#![feature(unsized_const_params)]` to the crate attributes to enable references to implement the `ConstParamTy` trait - | -LL + #![feature(unsized_const_params)] +LL - struct Bar(u8); +LL + #[derive(ConstParamTy, PartialEq, Eq)] | error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0741`. diff --git a/tests/ui/layout/thaw-transmute-invalid-enum.rs b/tests/ui/layout/thaw-transmute-invalid-enum.rs index a7c2e1a86de7..20fc8d463594 100644 --- a/tests/ui/layout/thaw-transmute-invalid-enum.rs +++ b/tests/ui/layout/thaw-transmute-invalid-enum.rs @@ -9,6 +9,7 @@ mod assert { where Dst: TransmuteFrom, //~^ ERROR: use of unstable library feature `transmutability` + //~^^ ERROR: use of unstable library feature `transmutability` { } } diff --git a/tests/ui/layout/thaw-transmute-invalid-enum.stderr b/tests/ui/layout/thaw-transmute-invalid-enum.stderr index d12fc4694e0a..2b89159c2633 100644 --- a/tests/ui/layout/thaw-transmute-invalid-enum.stderr +++ b/tests/ui/layout/thaw-transmute-invalid-enum.stderr @@ -1,5 +1,5 @@ error[E0412]: cannot find type `Subset` in this scope - --> $DIR/thaw-transmute-invalid-enum.rs:34:41 + --> $DIR/thaw-transmute-invalid-enum.rs:35:41 | LL | assert::is_transmutable::(); | ^^^^^^ not found in this scope @@ -10,7 +10,7 @@ LL | fn test() { | ++++++++ error[E0517]: attribute should be applied to a struct or union - --> $DIR/thaw-transmute-invalid-enum.rs:21:11 + --> $DIR/thaw-transmute-invalid-enum.rs:22:11 | LL | #[repr(C, packed(2))] | ^^^^^^^^^ @@ -50,8 +50,19 @@ LL | Dst: TransmuteFrom, = help: add `#![feature(transmutability)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +error[E0658]: use of unstable library feature `transmutability` + --> $DIR/thaw-transmute-invalid-enum.rs:10:14 + | +LL | Dst: TransmuteFrom, + | ^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(transmutability)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +note: required by a bound in `TransmuteFrom` + --> $SRC_DIR/core/src/mem/transmutability.rs:LL:COL + error[E0740]: field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union - --> $DIR/thaw-transmute-invalid-enum.rs:29:9 + --> $DIR/thaw-transmute-invalid-enum.rs:30:9 | LL | a: Ox00, | ^^^^^^^ @@ -62,7 +73,7 @@ help: wrap the field type in `ManuallyDrop<...>` LL | a: std::mem::ManuallyDrop, | +++++++++++++++++++++++ + -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors Some errors have detailed explanations: E0412, E0517, E0658, E0740. For more information about an error, try `rustc --explain E0412`. diff --git a/tests/ui/symbol-names/const-generics-structural-demangling.rs b/tests/ui/symbol-names/const-generics-structural-demangling.rs index 06e3ce51fa6a..0b4af61f9910 100644 --- a/tests/ui/symbol-names/const-generics-structural-demangling.rs +++ b/tests/ui/symbol-names/const-generics-structural-demangling.rs @@ -6,7 +6,7 @@ #![feature(adt_const_params, unsized_const_params, decl_macro, rustc_attrs)] #![allow(incomplete_features)] -use std::marker::UnsizedConstParamTy; +use std::marker::ConstParamTy; pub struct RefByte; @@ -42,7 +42,7 @@ pub struct TupleByteBool; //~| ERROR demangling-alt(>) impl TupleByteBool<{ (1, false) }> {} -#[derive(PartialEq, Eq, UnsizedConstParamTy)] +#[derive(PartialEq, Eq, ConstParamTy)] pub enum MyOption { Some(T), None, @@ -66,7 +66,7 @@ impl OptionUsize<{ MyOption::None }> {} //~| ERROR demangling-alt(::Some(0)}>>) impl OptionUsize<{ MyOption::Some(0) }> {} -#[derive(PartialEq, Eq, UnsizedConstParamTy)] +#[derive(PartialEq, Eq, ConstParamTy)] pub struct Foo { s: &'static str, ch: char, @@ -83,7 +83,7 @@ impl Foo_<{ Foo { s: "abc", ch: 'x', slice: &[1, 2, 3] } }> {} // NOTE(eddyb) this tests specifically the use of disambiguators in field names, // using macros 2.0 hygiene to create a `struct` with conflicting field names. macro duplicate_field_name_test($x:ident) { - #[derive(PartialEq, Eq, UnsizedConstParamTy)] + #[derive(PartialEq, Eq, ConstParamTy)] pub struct Bar { $x: u8, x: u16, From 1c30399ceae2400e266660a8f98a7504c284fda1 Mon Sep 17 00:00:00 2001 From: tiif Date: Mon, 15 Sep 2025 09:02:12 +0000 Subject: [PATCH 523/682] Add documentation for select_where_possible and select_all_or_error --- compiler/rustc_infer/src/traits/engine.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_infer/src/traits/engine.rs b/compiler/rustc_infer/src/traits/engine.rs index 9a66bd0574c9..4a252719694d 100644 --- a/compiler/rustc_infer/src/traits/engine.rs +++ b/compiler/rustc_infer/src/traits/engine.rs @@ -72,12 +72,27 @@ pub trait TraitEngine<'tcx, E: 'tcx>: 'tcx { self.register_predicate_obligation(infcx, obligation); } } - + /// Go over the list of pending obligations and try to evaluate them. + /// + /// For each result: + /// Ok: remove the obligation from the list + /// Ambiguous: leave the obligation in the list to be evaluated later + /// Err: remove the obligation from the list and return an error + /// + /// Returns a list of errors from obligations that evaluated to Err. #[must_use] fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec; fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec; + /// Evaluate all pending obligations, return error if they can't be evaluated. + /// + /// For each result: + /// Ok: remove the obligation from the list + /// Ambiguous: remove the obligation from the list and return an error + /// Err: remove the obligation from the list and return an error + /// + /// Returns a list of errors from obligations that evaluated to Ambiguous or Err. #[must_use] fn select_all_or_error(&mut self, infcx: &InferCtxt<'tcx>) -> Vec { let errors = self.select_where_possible(infcx); From 9405e76431374e25077b374ed0cd9c920a1c0f4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 15 Sep 2025 02:53:29 -0700 Subject: [PATCH 524/682] Detect attempt to use var-args in closure ``` error: unexpected `...` --> $DIR/varargs-in-closure-isnt-supported.rs:5:20 | LL | let mut lol = |...| (); | ^^^ not a valid pattern | = note: C-variadic type `...` is not allowed here ``` --- compiler/rustc_parse/messages.ftl | 1 + compiler/rustc_parse/src/errors.rs | 4 ++- compiler/rustc_parse/src/parser/pat.rs | 27 +++++++++++++------ .../varargs-in-closure-isnt-supported.rs | 11 ++++++++ .../varargs-in-closure-isnt-supported.stderr | 22 +++++++++++++++ 5 files changed, 56 insertions(+), 9 deletions(-) create mode 100644 tests/ui/closures/varargs-in-closure-isnt-supported.rs create mode 100644 tests/ui/closures/varargs-in-closure-isnt-supported.stderr diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 72cd75f6d894..6d9521c7d2be 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -189,6 +189,7 @@ parse_dotdotdot = unexpected token: `...` parse_dotdotdot_rest_pattern = unexpected `...` .label = not a valid pattern .suggestion = for a rest pattern, use `..` instead of `...` + .note = C-variadic type `...` is not allowed here parse_double_colon_in_bound = expected `:` followed by trait or lifetime .suggestion = use single colon diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 00ca5acd84d5..2b107fe35d59 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2723,7 +2723,9 @@ pub(crate) struct DotDotDotRestPattern { #[label] pub span: Span, #[suggestion(style = "verbose", code = "", applicability = "machine-applicable")] - pub suggestion: Span, + pub suggestion: Option, + #[note] + pub var_args: Option<()>, } #[derive(Diagnostic)] diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index c4d30b3d3283..fda19d62bc77 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -756,7 +756,7 @@ impl<'a> Parser<'a> { self.bump(); // `..` PatKind::Rest } else if self.check(exp!(DotDotDot)) && !self.is_pat_range_end_start(1) { - self.recover_dotdotdot_rest_pat(lo) + self.recover_dotdotdot_rest_pat(lo, expected) } else if let Some(form) = self.parse_range_end() { self.parse_pat_range_to(form)? // `..=X`, `...X`, or `..X`. } else if self.eat(exp!(Bang)) { @@ -886,16 +886,27 @@ impl<'a> Parser<'a> { /// Recover from a typoed `...` pattern that was encountered /// Ref: Issue #70388 - fn recover_dotdotdot_rest_pat(&mut self, lo: Span) -> PatKind { + fn recover_dotdotdot_rest_pat(&mut self, lo: Span, expected: Option) -> PatKind { // A typoed rest pattern `...`. self.bump(); // `...` - // The user probably mistook `...` for a rest pattern `..`. - self.dcx().emit_err(DotDotDotRestPattern { - span: lo, - suggestion: lo.with_lo(lo.hi() - BytePos(1)), - }); - PatKind::Rest + if let Some(Expected::ParameterName) = expected { + // We have `...` in a closure argument, likely meant to be var-arg, which aren't + // supported in closures (#146489). + PatKind::Err(self.dcx().emit_err(DotDotDotRestPattern { + span: lo, + suggestion: None, + var_args: Some(()), + })) + } else { + // The user probably mistook `...` for a rest pattern `..`. + self.dcx().emit_err(DotDotDotRestPattern { + span: lo, + suggestion: Some(lo.with_lo(lo.hi() - BytePos(1))), + var_args: None, + }); + PatKind::Rest + } } /// Try to recover the more general form `intersect ::= $pat_lhs @ $pat_rhs`. diff --git a/tests/ui/closures/varargs-in-closure-isnt-supported.rs b/tests/ui/closures/varargs-in-closure-isnt-supported.rs new file mode 100644 index 000000000000..5dff69194f90 --- /dev/null +++ b/tests/ui/closures/varargs-in-closure-isnt-supported.rs @@ -0,0 +1,11 @@ +// var-args are not supported in closures, ensure we don't misdirect people (#146489) +#![feature(c_variadic)] + +unsafe extern "C" fn thats_not_a_pattern(mut ap: ...) -> u32 { + let mut lol = |...| (); //~ ERROR: unexpected `...` + unsafe { ap.arg::() } //~^ NOTE: C-variadic type `...` is not allowed here + //~| ERROR: type annotations needed + //~| NOTE: not a valid pattern +} + +fn main() {} diff --git a/tests/ui/closures/varargs-in-closure-isnt-supported.stderr b/tests/ui/closures/varargs-in-closure-isnt-supported.stderr new file mode 100644 index 000000000000..4f66ff59af12 --- /dev/null +++ b/tests/ui/closures/varargs-in-closure-isnt-supported.stderr @@ -0,0 +1,22 @@ +error: unexpected `...` + --> $DIR/varargs-in-closure-isnt-supported.rs:5:20 + | +LL | let mut lol = |...| (); + | ^^^ not a valid pattern + | + = note: C-variadic type `...` is not allowed here + +error[E0282]: type annotations needed + --> $DIR/varargs-in-closure-isnt-supported.rs:5:20 + | +LL | let mut lol = |...| (); + | ^^^ + | +help: consider giving this closure parameter an explicit type + | +LL | let mut lol = |...: /* Type */| (); + | ++++++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0282`. From 0e290e4228e90720b4b3e263f95250db7046cdd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 15 Sep 2025 03:06:16 -0700 Subject: [PATCH 525/682] Silence inference error on `PatKind::Err` --- .../src/error_reporting/infer/need_type_info.rs | 10 ++++++++-- .../closures/varargs-in-closure-isnt-supported.rs | 1 - .../varargs-in-closure-isnt-supported.stderr | 14 +------------- 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs index edab530590b2..94772be16be2 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs @@ -4,7 +4,7 @@ use std::path::PathBuf; use rustc_errors::codes::*; use rustc_errors::{Diag, IntoDiagArg}; -use rustc_hir as hir; +use rustc_hir::{self as hir, PatKind}; use rustc_hir::def::{CtorOf, DefKind, Namespace, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; @@ -512,7 +512,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { type_name: ty_to_string(self, ty, def_id), }); } - InferSourceKind::ClosureArg { insert_span, ty } => { + InferSourceKind::ClosureArg { insert_span, ty, .. } => { infer_subdiags.push(SourceKindSubdiag::LetLike { span: insert_span, name: String::new(), @@ -652,6 +652,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }), }; *err.long_ty_path() = long_ty_path; + if let InferSourceKind::ClosureArg { kind: PatKind::Err(_), .. } = kind { + // We will have already emitted an error about this pattern. + err.downgrade_to_delayed_bug(); + } err } } @@ -673,6 +677,7 @@ enum InferSourceKind<'tcx> { ClosureArg { insert_span: Span, ty: Ty<'tcx>, + kind: PatKind<'tcx>, }, GenericArg { insert_span: Span, @@ -1197,6 +1202,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { kind: InferSourceKind::ClosureArg { insert_span: param.pat.span.shrink_to_hi(), ty: param_ty, + kind: param.pat.kind, }, }) } diff --git a/tests/ui/closures/varargs-in-closure-isnt-supported.rs b/tests/ui/closures/varargs-in-closure-isnt-supported.rs index 5dff69194f90..4de78bef14d3 100644 --- a/tests/ui/closures/varargs-in-closure-isnt-supported.rs +++ b/tests/ui/closures/varargs-in-closure-isnt-supported.rs @@ -4,7 +4,6 @@ unsafe extern "C" fn thats_not_a_pattern(mut ap: ...) -> u32 { let mut lol = |...| (); //~ ERROR: unexpected `...` unsafe { ap.arg::() } //~^ NOTE: C-variadic type `...` is not allowed here - //~| ERROR: type annotations needed //~| NOTE: not a valid pattern } diff --git a/tests/ui/closures/varargs-in-closure-isnt-supported.stderr b/tests/ui/closures/varargs-in-closure-isnt-supported.stderr index 4f66ff59af12..a645741a5277 100644 --- a/tests/ui/closures/varargs-in-closure-isnt-supported.stderr +++ b/tests/ui/closures/varargs-in-closure-isnt-supported.stderr @@ -6,17 +6,5 @@ LL | let mut lol = |...| (); | = note: C-variadic type `...` is not allowed here -error[E0282]: type annotations needed - --> $DIR/varargs-in-closure-isnt-supported.rs:5:20 - | -LL | let mut lol = |...| (); - | ^^^ - | -help: consider giving this closure parameter an explicit type - | -LL | let mut lol = |...: /* Type */| (); - | ++++++++++++ +error: aborting due to 1 previous error -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0282`. From a84c8deb1f61e020e026e1fa53bc613426da8fdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 15 Sep 2025 03:10:27 -0700 Subject: [PATCH 526/682] Fix existing test --- tests/ui/c-variadic/no-closure.rs | 3 +-- tests/ui/c-variadic/no-closure.stderr | 18 +++--------------- 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/tests/ui/c-variadic/no-closure.rs b/tests/ui/c-variadic/no-closure.rs index c0b77786e8b4..a5b791fbca80 100644 --- a/tests/ui/c-variadic/no-closure.rs +++ b/tests/ui/c-variadic/no-closure.rs @@ -8,8 +8,7 @@ const F: extern "C" fn(...) = |_: ...| {}; fn foo() { let f = |...| {}; - //~^ ERROR: `..` patterns are not allowed here - //~| ERROR: unexpected `...` + //~^ ERROR: unexpected `...` let f = |_: ...| {}; //~^ ERROR C-variadic type `...` may not be nested inside another type diff --git a/tests/ui/c-variadic/no-closure.stderr b/tests/ui/c-variadic/no-closure.stderr index 77bd106bb951..ad635a29ab48 100644 --- a/tests/ui/c-variadic/no-closure.stderr +++ b/tests/ui/c-variadic/no-closure.stderr @@ -10,26 +10,14 @@ error: unexpected `...` LL | let f = |...| {}; | ^^^ not a valid pattern | -help: for a rest pattern, use `..` instead of `...` - | -LL - let f = |...| {}; -LL + let f = |..| {}; - | + = note: C-variadic type `...` is not allowed here error[E0743]: C-variadic type `...` may not be nested inside another type - --> $DIR/no-closure.rs:14:17 + --> $DIR/no-closure.rs:13:17 | LL | let f = |_: ...| {}; | ^^^ -error: `..` patterns are not allowed here - --> $DIR/no-closure.rs:10:14 - | -LL | let f = |...| {}; - | ^^^ - | - = note: only allowed in tuple, tuple struct, and slice patterns - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0743`. From dcc62e7e58083dabec40464e4b1bcc9788b70ecb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 15 Sep 2025 12:32:03 +0200 Subject: [PATCH 527/682] Only run Cranelift dist test on nightly --- tests/ui/dist/cranelift-x86_64-unknown-linux-gnu-dist.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/ui/dist/cranelift-x86_64-unknown-linux-gnu-dist.rs b/tests/ui/dist/cranelift-x86_64-unknown-linux-gnu-dist.rs index 198f8d1bc10c..6d12ab1e4cf6 100644 --- a/tests/ui/dist/cranelift-x86_64-unknown-linux-gnu-dist.rs +++ b/tests/ui/dist/cranelift-x86_64-unknown-linux-gnu-dist.rs @@ -2,6 +2,7 @@ // dist artifacts. //@ only-dist +//@ only-nightly (cranelift is not stable yet) //@ only-x86_64-unknown-linux-gnu //@ compile-flags: -Z codegen-backend=cranelift //@ run-pass From c92dee244dcca49c1eed99a768749e433f17e112 Mon Sep 17 00:00:00 2001 From: Boxy Uwu Date: Wed, 10 Sep 2025 11:27:36 +0100 Subject: [PATCH 528/682] Add relnotes 1.90 --- RELEASES.md | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index 33abe45ce462..2b65d070d5fd 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,129 @@ +Version 1.90 (2025-09-18) +========================== + +
+ +Language +-------- +- [Split up the `unknown_or_malformed_diagnostic_attributes` lint](https://github.com/rust-lang/rust/pull/140717). This lint has been split up into four finer-grained lints, with `unknown_or_malformed_diagnostic_attributes` now being the lint group that contains these lints: + 1. `unknown_diagnostic_attributes`: unknown to the current compiler + 2. `misplaced_diagnostic_attributes`: placed on the wrong item + 3. `malformed_diagnostic_attributes`: malformed attribute syntax or options + 4. `malformed_diagnostic_format_literals`: malformed format string literal +- [Allow constants whose final value has references to mutable/external memory, but reject such constants as patterns](https://github.com/rust-lang/rust/pull/140942) +- [Allow volatile access to non-Rust memory, including address 0](https://github.com/rust-lang/rust/pull/141260) + + + + +Compiler +-------- +- [Use `lld` by default on `x86_64-unknown-linux-gnu`](https://github.com/rust-lang/rust/pull/140525). +- [Tier 3 `musl` targets now link dynamically by default](https://github.com/rust-lang/rust/pull/144410). Affected targets: + - `mips64-unknown-linux-muslabi64` + - `powerpc64-unknown-linux-musl` + - `powerpc-unknown-linux-musl` + - `powerpc-unknown-linux-muslspe` + - `riscv32gc-unknown-linux-musl` + - `s390x-unknown-linux-musl` + - `thumbv7neon-unknown-linux-musleabihf` + + + + +Platform Support +---------------- +- [Demote `x86_64-apple-darwin` to Tier 2 with host tools](https://github.com/rust-lang/rust/pull/145252) + + +Refer to Rust's [platform support page][platform-support-doc] +for more information on Rust's tiered platform support. + +[platform-support-doc]: https://doc.rust-lang.org/rustc/platform-support.html + + + +Libraries +--------- +- [Stabilize `u*::{checked,overflowing,saturating,wrapping}_sub_signed`](https://github.com/rust-lang/rust/issues/126043) +- [Allow comparisons between `CStr`, `CString`, and `Cow`](https://github.com/rust-lang/rust/pull/137268) +- [Remove some unsized tuple impls since unsized tuples can't be constructed](https://github.com/rust-lang/rust/pull/138340) +- [Set `MSG_NOSIGNAL` for `UnixStream`](https://github.com/rust-lang/rust/pull/140005) +- [`proc_macro::Ident::new` now supports `$crate`.](https://github.com/rust-lang/rust/pull/141996) +- [Guarantee the pointer returned from `Thread::into_raw` has at least 8 bytes of alignment](https://github.com/rust-lang/rust/pull/143859) + + + + +Stabilized APIs +--------------- + +- [`u{n}::checked_sub_signed`](https://doc.rust-lang.org/stable/std/primitive.usize.html#method.checked_sub_signed) +- [`u{n}::overflowing_sub_signed`](https://doc.rust-lang.org/stable/std/primitive.usize.html#method.overflowing_sub_signed) +- [`u{n}::saturating_sub_signed`](https://doc.rust-lang.org/stable/std/primitive.usize.html#method.saturating_sub_signed) +- [`u{n}::wrapping_sub_signed`](https://doc.rust-lang.org/stable/std/primitive.usize.html#method.wrapping_sub_signed) +- [`impl Copy for IntErrorKind`](https://doc.rust-lang.org/stable/std/num/enum.IntErrorKind.html#impl-Copy-for-IntErrorKind) +- [`impl Hash for IntErrorKind`](https://doc.rust-lang.org/stable/std/num/enum.IntErrorKind.html#impl-Hash-for-IntErrorKind) +- [`impl PartialEq<&CStr> for CStr`](https://doc.rust-lang.org/stable/std/ffi/struct.CStr.html#impl-PartialEq%3C%26CStr%3E-for-CStr) +- [`impl PartialEq for CStr`](https://doc.rust-lang.org/stable/std/ffi/struct.CStr.html#impl-PartialEq%3CCString%3E-for-CStr) +- [`impl PartialEq> for CStr`](https://doc.rust-lang.org/stable/std/ffi/struct.CStr.html#impl-PartialEq%3CCow%3C'_,+CStr%3E%3E-for-CStr) +- [`impl PartialEq<&CStr> for CString`](https://doc.rust-lang.org/stable/std/ffi/struct.CString.html#impl-PartialEq%3C%26CStr%3E-for-CString) +- [`impl PartialEq for CString`](https://doc.rust-lang.org/stable/std/ffi/struct.CString.html#impl-PartialEq%3CCStr%3E-for-CString) +- [`impl PartialEq> for CString`](https://doc.rust-lang.org/stable/std/ffi/struct.CString.html#impl-PartialEq%3CCow%3C'_,+CStr%3E%3E-for-CString) +- [`impl PartialEq<&CStr> for Cow`](https://doc.rust-lang.org/stable/std/borrow/enum.Cow.html#impl-PartialEq%3C%26CStr%3E-for-Cow%3C'_,+CStr%3E) +- [`impl PartialEq for Cow`](https://doc.rust-lang.org/stable/std/borrow/enum.Cow.html#impl-PartialEq%3CCStr%3E-for-Cow%3C'_,+CStr%3E) +- [`impl PartialEq for Cow`](https://doc.rust-lang.org/stable/std/borrow/enum.Cow.html#impl-PartialEq%3CCString%3E-for-Cow%3C'_,+CStr%3E) + + +These previously stable APIs are now stable in const contexts: + +- [`<[T]>::reverse`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.reverse) +- [`f32::floor`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.floor) +- [`f32::ceil`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.ceil) +- [`f32::trunc`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.trunc) +- [`f32::fract`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.fract) +- [`f32::round`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.round) +- [`f32::round_ties_even`](https://doc.rust-lang.org/stable/std/primitive.f32.html#method.round_ties_even) +- [`f64::floor`](https://doc.rust-lang.org/stable/std/primitive.f64.html#method.floor) +- [`f64::ceil`](https://doc.rust-lang.org/stable/std/primitive.f64.html#method.ceil) +- [`f64::trunc`](https://doc.rust-lang.org/stable/std/primitive.f64.html#method.trunc) +- [`f64::fract`](https://doc.rust-lang.org/stable/std/primitive.f64.html#method.fract) +- [`f64::round`](https://doc.rust-lang.org/stable/std/primitive.f64.html#method.round) +- [`f64::round_ties_even`](https://doc.rust-lang.org/stable/std/primitive.f64.html#method.round_ties_even) + + + + +Cargo +----- +- [Add `http.proxy-cainfo` config for proxy certs](https://github.com/rust-lang/cargo/pull/15374/) +- [Use `gix` for `cargo package`](https://github.com/rust-lang/cargo/pull/15534/) +- [feat(publish): Stabilize multi-package publishing](https://github.com/rust-lang/cargo/pull/15636/) + + + +Rustdoc +----- +- [Add ways to collapse all impl blocks](https://github.com/rust-lang/rust/pull/141663). Previously the "Summary" button and "-" keyboard shortcut would never collapse `impl` blocks, now they do when shift is held +- [Display unsafe attributes with `unsafe()` wrappers](https://github.com/rust-lang/rust/pull/143662) + + + + +Compatibility Notes +------------------- +- [Use `lld` by default on `x86_64-unknown-linux-gnu`](https://github.com/rust-lang/rust/pull/140525). + See also . +- [Make `core::iter::Fuse`'s `Default` impl construct `I::default()` internally as promised in the docs instead of always being empty](https://github.com/rust-lang/rust/pull/140985) +- [Set `MSG_NOSIGNAL` for `UnixStream`](https://github.com/rust-lang/rust/pull/140005) + This may change program behavior but results in the same behavior as other primitives (e.g., stdout, network sockets). + Programs relying on signals to terminate them should update handling of sockets to handle errors on write by exiting. +- [On Unix `std::env::home_dir` will use the fallback if the `HOME` environment variable is empty](https://github.com/rust-lang/rust/pull/141840) +- We now [reject unsupported `extern "{abi}"`s consistently in all positions](https://github.com/rust-lang/rust/pull/142134). This primarily affects the use of implementing traits on an `extern "{abi}"` function pointer, like `extern "stdcall" fn()`, on a platform that doesn't support that, like aarch64-unknown-linux-gnu. Direct usage of these unsupported ABI strings by declaring or defining functions was already rejected, so this is only a change for consistency. +- [const-eval: error when initializing a static writes to that static](https://github.com/rust-lang/rust/pull/143084) +- [Check that the `proc_macro_derive` macro has correct arguments when applied to the crate root](https://github.com/rust-lang/rust/pull/143607) + + Version 1.89.0 (2025-08-07) ========================== From 53b58b3afdc1d3b069c8b73230abf68878196f75 Mon Sep 17 00:00:00 2001 From: Jens Reidel Date: Mon, 15 Sep 2025 14:31:43 +0200 Subject: [PATCH 529/682] tests/run-make: Update list of statically linked musl targets All of the tier 3 targets in the list now link dynamically by default (except mips64el-unknown-linux-muslabi64, I overlooked that one). Adjust the list of targets expected to link statically accordingly. Signed-off-by: Jens Reidel --- tests/run-make/musl-default-linking/rmake.rs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/tests/run-make/musl-default-linking/rmake.rs b/tests/run-make/musl-default-linking/rmake.rs index 7bb54e2739c9..1b30c538b5e3 100644 --- a/tests/run-make/musl-default-linking/rmake.rs +++ b/tests/run-make/musl-default-linking/rmake.rs @@ -4,7 +4,7 @@ use run_make_support::{rustc, serde_json}; // Per https://github.com/rust-lang/compiler-team/issues/422, // we should be trying to move these targets to dynamically link // musl libc by default. -//@ needs-llvm-components: aarch64 arm mips powerpc riscv systemz x86 +//@ needs-llvm-components: aarch64 arm mips powerpc x86 static LEGACY_STATIC_LINKING_TARGETS: &[&'static str] = &[ "aarch64-unknown-linux-musl", "arm-unknown-linux-musleabi", @@ -14,16 +14,8 @@ static LEGACY_STATIC_LINKING_TARGETS: &[&'static str] = &[ "armv7-unknown-linux-musleabihf", "i586-unknown-linux-musl", "i686-unknown-linux-musl", - "mips64-unknown-linux-musl", - "mips64-unknown-linux-muslabi64", "mips64el-unknown-linux-muslabi64", - "powerpc-unknown-linux-musl", - "powerpc-unknown-linux-muslspe", - "powerpc64-unknown-linux-musl", "powerpc64le-unknown-linux-musl", - "riscv32gc-unknown-linux-musl", - "s390x-unknown-linux-musl", - "thumbv7neon-unknown-linux-musleabihf", "x86_64-unknown-linux-musl", ]; From 7299e8fc4db506bb5ec3990e22b611c45df2f13d Mon Sep 17 00:00:00 2001 From: tiif Date: Mon, 15 Sep 2025 14:15:29 +0000 Subject: [PATCH 530/682] Fix feature gate tests --- .../feature-gate-adt_const_params.rs | 6 +++++- .../feature-gate-adt_const_params.stderr | 12 ++++-------- .../feature-gate-unsized-const-params.rs | 10 ++++------ .../feature-gate-unsized-const-params.stderr | 19 +++++++++++-------- 4 files changed, 24 insertions(+), 23 deletions(-) diff --git a/tests/ui/feature-gates/feature-gate-adt_const_params.rs b/tests/ui/feature-gates/feature-gate-adt_const_params.rs index 8a3bcf25963f..7efa529c5579 100644 --- a/tests/ui/feature-gates/feature-gate-adt_const_params.rs +++ b/tests/ui/feature-gates/feature-gate-adt_const_params.rs @@ -1,2 +1,6 @@ -struct Foo; //~ ERROR `&'static str` is forbidden +struct Bar(u8); + +struct Foo; +//~^ ERROR: `Bar` is forbidden as the type of a const generic parameter + fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-adt_const_params.stderr b/tests/ui/feature-gates/feature-gate-adt_const_params.stderr index 18d514f8cb5f..7ea91a8f4c2b 100644 --- a/tests/ui/feature-gates/feature-gate-adt_const_params.stderr +++ b/tests/ui/feature-gates/feature-gate-adt_const_params.stderr @@ -1,18 +1,14 @@ -error: `&'static str` is forbidden as the type of a const generic parameter - --> $DIR/feature-gate-adt_const_params.rs:1:24 +error: `Bar` is forbidden as the type of a const generic parameter + --> $DIR/feature-gate-adt_const_params.rs:3:21 | -LL | struct Foo; - | ^^^^^^^^^^^^ +LL | struct Foo; + | ^^^ | = note: the only supported types are integers, `bool`, and `char` help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | LL + #![feature(adt_const_params)] | -help: add `#![feature(unsized_const_params)]` to the crate attributes to enable references to implement the `ConstParamTy` trait - | -LL + #![feature(unsized_const_params)] - | error: aborting due to 1 previous error diff --git a/tests/ui/feature-gates/feature-gate-unsized-const-params.rs b/tests/ui/feature-gates/feature-gate-unsized-const-params.rs index 56eebbd53c91..d088d382377c 100644 --- a/tests/ui/feature-gates/feature-gate-unsized-const-params.rs +++ b/tests/ui/feature-gates/feature-gate-unsized-const-params.rs @@ -1,8 +1,6 @@ -#![feature(adt_const_params)] - -struct Bar(u8); - -struct Foo; -//~^ ERROR: `Bar` must implement `ConstParamTy` to be used as the type of a const generic parameter +struct Foo; +//~^ ERROR: `[u8]` is forbidden as the type of a const generic parameter +//~| HELP: add `#![feature(adt_const_params)]` to the crate +//~| HELP: add `#![feature(unsized_const_params)]` to the crate fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-unsized-const-params.stderr b/tests/ui/feature-gates/feature-gate-unsized-const-params.stderr index d4f7d45ea323..85ca2f59cb63 100644 --- a/tests/ui/feature-gates/feature-gate-unsized-const-params.stderr +++ b/tests/ui/feature-gates/feature-gate-unsized-const-params.stderr @@ -1,15 +1,18 @@ -error[E0741]: `Bar` must implement `ConstParamTy` to be used as the type of a const generic parameter - --> $DIR/feature-gate-unsized-const-params.rs:5:21 +error: `[u8]` is forbidden as the type of a const generic parameter + --> $DIR/feature-gate-unsized-const-params.rs:1:21 | -LL | struct Foo; - | ^^^ +LL | struct Foo; + | ^^^^ | -help: add `#[derive(ConstParamTy, PartialEq, Eq)]` to the struct + = note: the only supported types are integers, `bool`, and `char` +help: add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types | -LL - struct Bar(u8); -LL + #[derive(ConstParamTy, PartialEq, Eq)] +LL + #![feature(adt_const_params)] + | +help: add `#![feature(unsized_const_params)]` to the crate attributes to enable references to implement the `ConstParamTy` trait + | +LL + #![feature(unsized_const_params)] | error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0741`. From 9cbf1a5502b81399d522a4224cb012df7e1166a6 Mon Sep 17 00:00:00 2001 From: Sasha Pourcelot Date: Sat, 23 Aug 2025 19:20:38 +0200 Subject: [PATCH 531/682] Split `FnCtxt::report_args_error` into subfunctions --- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 2404 ++++++++++------- 1 file changed, 1380 insertions(+), 1024 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 94b635c41b4b..22bd7e1d23e1 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1,3 +1,4 @@ +use std::ops::Deref; use std::{fmt, iter, mem}; use itertools::Itertools; @@ -7,7 +8,7 @@ use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, a_or_an, lis use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; -use rustc_hir::{ExprKind, HirId, LangItem, Node, QPath}; +use rustc_hir::{Expr, ExprKind, HirId, LangItem, Node, QPath}; use rustc_hir_analysis::check::potentially_plural_count; use rustc_hir_analysis::hir_ty_lowering::{HirTyLowerer, PermitVariants}; use rustc_index::IndexVec; @@ -565,358 +566,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tuple_arguments: TupleArgumentsFlag, ) -> ErrorGuaranteed { // Next, let's construct the error - let (error_span, call_ident, full_call_span, call_name, is_method) = match &call_expr.kind { - hir::ExprKind::Call( - hir::Expr { hir_id, span, kind: hir::ExprKind::Path(qpath), .. }, - _, - ) => { - if let Res::Def(DefKind::Ctor(of, _), _) = - self.typeck_results.borrow().qpath_res(qpath, *hir_id) - { - let name = match of { - CtorOf::Struct => "struct", - CtorOf::Variant => "enum variant", - }; - (call_span, None, *span, name, false) - } else { - (call_span, None, *span, "function", false) - } - } - hir::ExprKind::Call(hir::Expr { span, .. }, _) => { - (call_span, None, *span, "function", false) - } - hir::ExprKind::MethodCall(path_segment, _, _, span) => { - let ident_span = path_segment.ident.span; - let ident_span = if let Some(args) = path_segment.args { - ident_span.with_hi(args.span_ext.hi()) - } else { - ident_span - }; - (*span, Some(path_segment.ident), ident_span, "method", true) - } - k => span_bug!(call_span, "checking argument types on a non-call: `{:?}`", k), - }; - let args_span = error_span.trim_start(full_call_span).unwrap_or(error_span); - // Don't print if it has error types or is just plain `_` - fn has_error_or_infer<'tcx>(tys: impl IntoIterator>) -> bool { - tys.into_iter().any(|ty| ty.references_error() || ty.is_ty_var()) - } - - let tcx = self.tcx; - - // Get the argument span in the context of the call span so that - // suggestions and labels are (more) correct when an arg is a - // macro invocation. - let normalize_span = |span: Span| -> Span { - let normalized_span = span.find_ancestor_inside_same_ctxt(error_span).unwrap_or(span); - // Sometimes macros mess up the spans, so do not normalize the - // arg span to equal the error span, because that's less useful - // than pointing out the arg expr in the wrong context. - if normalized_span.source_equal(error_span) { span } else { normalized_span } - }; - - // Precompute the provided types and spans, since that's all we typically need for below - let provided_arg_tys: IndexVec, Span)> = provided_args - .iter() - .map(|expr| { - let ty = self - .typeck_results - .borrow() - .expr_ty_adjusted_opt(*expr) - .unwrap_or_else(|| Ty::new_misc_error(tcx)); - (self.resolve_vars_if_possible(ty), normalize_span(expr.span)) - }) - .collect(); - let callee_expr = match &call_expr.peel_blocks().kind { - hir::ExprKind::Call(callee, _) => Some(*callee), - hir::ExprKind::MethodCall(_, receiver, ..) => { - if let Some((DefKind::AssocFn, def_id)) = - self.typeck_results.borrow().type_dependent_def(call_expr.hir_id) - && let Some(assoc) = tcx.opt_associated_item(def_id) - && assoc.is_method() - { - Some(*receiver) - } else { - None - } - } - _ => None, - }; - let callee_ty = callee_expr - .and_then(|callee_expr| self.typeck_results.borrow().expr_ty_adjusted_opt(callee_expr)); - - // Obtain another method on `Self` that have similar name. - let similar_assoc = |call_name: Ident| -> Option<(ty::AssocItem, ty::FnSig<'_>)> { - if let Some(callee_ty) = callee_ty - && let Ok(Some(assoc)) = self.probe_op( - call_name.span, - MethodCall, - Some(call_name), - None, - IsSuggestion(true), - callee_ty.peel_refs(), - callee_expr.unwrap().hir_id, - TraitsInScope, - |mut ctxt| ctxt.probe_for_similar_candidate(), - ) - && assoc.is_method() - { - let args = self.infcx.fresh_args_for_item(call_name.span, assoc.def_id); - let fn_sig = tcx.fn_sig(assoc.def_id).instantiate(tcx, args); - - self.instantiate_binder_with_fresh_vars( - call_name.span, - BoundRegionConversionTime::FnCall, - fn_sig, - ); - } - None - }; - - let suggest_confusable = |err: &mut Diag<'_>| { - let Some(call_name) = call_ident else { - return; - }; - let Some(callee_ty) = callee_ty else { - return; - }; - let input_types: Vec> = provided_arg_tys.iter().map(|(ty, _)| *ty).collect(); - // Check for other methods in the following order - // - methods marked as `rustc_confusables` with the provided arguments - // - methods with the same argument type/count and short levenshtein distance - // - methods marked as `rustc_confusables` (done) - // - methods with short levenshtein distance - - // Look for commonly confusable method names considering arguments. - if let Some(_name) = self.confusable_method_name( - err, - callee_ty.peel_refs(), - call_name, - Some(input_types.clone()), - ) { - return; - } - // Look for method names with short levenshtein distance, considering arguments. - if let Some((assoc, fn_sig)) = similar_assoc(call_name) - && fn_sig.inputs()[1..] - .iter() - .zip(input_types.iter()) - .all(|(expected, found)| self.may_coerce(*expected, *found)) - && fn_sig.inputs()[1..].len() == input_types.len() - { - let assoc_name = assoc.name(); - err.span_suggestion_verbose( - call_name.span, - format!("you might have meant to use `{}`", assoc_name), - assoc_name, - Applicability::MaybeIncorrect, - ); - return; - } - // Look for commonly confusable method names disregarding arguments. - if let Some(_name) = - self.confusable_method_name(err, callee_ty.peel_refs(), call_name, None) - { - return; - } - // Look for similarly named methods with levenshtein distance with the right - // number of arguments. - if let Some((assoc, fn_sig)) = similar_assoc(call_name) - && fn_sig.inputs()[1..].len() == input_types.len() - { - err.span_note( - tcx.def_span(assoc.def_id), - format!( - "there's is a method with similar name `{}`, but the arguments don't match", - assoc.name(), - ), - ); - return; - } - // Fallthrough: look for similarly named methods with levenshtein distance. - if let Some((assoc, _)) = similar_assoc(call_name) { - err.span_note( - tcx.def_span(assoc.def_id), - format!( - "there's is a method with similar name `{}`, but their argument count \ - doesn't match", - assoc.name(), - ), - ); - return; - } - }; - // A "softer" version of the `demand_compatible`, which checks types without persisting them, - // and treats error types differently - // This will allow us to "probe" for other argument orders that would likely have been correct - let check_compatible = |provided_idx: ProvidedIdx, expected_idx: ExpectedIdx| { - if provided_idx.as_usize() == expected_idx.as_usize() { - return compatibility_diagonal[provided_idx].clone(); - } - - let (formal_input_ty, expected_input_ty) = formal_and_expected_inputs[expected_idx]; - // If either is an error type, we defy the usual convention and consider them to *not* be - // coercible. This prevents our error message heuristic from trying to pass errors into - // every argument. - if (formal_input_ty, expected_input_ty).references_error() { - return Compatibility::Incompatible(None); - } - - let (arg_ty, arg_span) = provided_arg_tys[provided_idx]; - - let expectation = Expectation::rvalue_hint(self, expected_input_ty); - let coerced_ty = expectation.only_has_type(self).unwrap_or(formal_input_ty); - let can_coerce = self.may_coerce(arg_ty, coerced_ty); - if !can_coerce { - return Compatibility::Incompatible(Some(ty::error::TypeError::Sorts( - ty::error::ExpectedFound::new(coerced_ty, arg_ty), - ))); - } - - // Using probe here, since we don't want this subtyping to affect inference. - let subtyping_error = self.probe(|_| { - self.at(&self.misc(arg_span), self.param_env) - .sup(DefineOpaqueTypes::Yes, formal_input_ty, coerced_ty) - .err() - }); - - // Same as above: if either the coerce type or the checked type is an error type, - // consider them *not* compatible. - let references_error = (coerced_ty, arg_ty).references_error(); - match (references_error, subtyping_error) { - (false, None) => Compatibility::Compatible, - (_, subtyping_error) => Compatibility::Incompatible(subtyping_error), - } - }; - - let mk_trace = |span, (formal_ty, expected_ty), provided_ty| { - let mismatched_ty = if expected_ty == provided_ty { - // If expected == provided, then we must have failed to sup - // the formal type. Avoid printing out "expected Ty, found Ty" - // in that case. - formal_ty - } else { - expected_ty - }; - TypeTrace::types(&self.misc(span), mismatched_ty, provided_ty) - }; - - // The algorithm here is inspired by levenshtein distance and longest common subsequence. - // We'll try to detect 4 different types of mistakes: - // - An extra parameter has been provided that doesn't satisfy *any* of the other inputs - // - An input is missing, which isn't satisfied by *any* of the other arguments - // - Some number of arguments have been provided in the wrong order - // - A type is straight up invalid - - // First, let's find the errors - let (mut errors, matched_inputs) = - ArgMatrix::new(provided_args.len(), formal_and_expected_inputs.len(), check_compatible) - .find_errors(); + let mut fn_call_diag_ctxt = FnCallDiagCtxt::new( + self, + compatibility_diagonal, + formal_and_expected_inputs, + provided_args, + c_variadic, + err_code, + fn_def_id, + call_span, + call_expr, + tuple_arguments, + ); // First, check if we just need to wrap some arguments in a tuple. - if let Some((mismatch_idx, terr)) = - compatibility_diagonal.iter_enumerated().find_map(|(i, c)| { - if let Compatibility::Incompatible(Some(terr)) = c { - Some((i, *terr)) - } else { - None - } - }) - { - // Is the first bad expected argument a tuple? - // Do we have as many extra provided arguments as the tuple's length? - // If so, we might have just forgotten to wrap some args in a tuple. - if let Some(ty::Tuple(tys)) = - formal_and_expected_inputs.get(mismatch_idx.to_expected_idx()).map(|tys| tys.1.kind()) - // If the tuple is unit, we're not actually wrapping any arguments. - && !tys.is_empty() - && provided_arg_tys.len() == formal_and_expected_inputs.len() - 1 + tys.len() - { - // Wrap up the N provided arguments starting at this position in a tuple. - let provided_args_to_tuple = &provided_arg_tys[mismatch_idx..]; - let (provided_args_to_tuple, provided_args_after_tuple) = - provided_args_to_tuple.split_at(tys.len()); - let provided_as_tuple = - Ty::new_tup_from_iter(tcx, provided_args_to_tuple.iter().map(|&(ty, _)| ty)); + if let Some(err) = fn_call_diag_ctxt.check_wrap_args_in_tuple() { + return err; + } - let mut satisfied = true; - // Check if the newly wrapped tuple + rest of the arguments are compatible. - for ((_, expected_ty), provided_ty) in std::iter::zip( - formal_and_expected_inputs[mismatch_idx.to_expected_idx()..].iter(), - [provided_as_tuple] - .into_iter() - .chain(provided_args_after_tuple.iter().map(|&(ty, _)| ty)), - ) { - if !self.may_coerce(provided_ty, *expected_ty) { - satisfied = false; - break; - } - } - - // If they're compatible, suggest wrapping in an arg, and we're done! - // Take some care with spans, so we don't suggest wrapping a macro's - // innards in parenthesis, for example. - if satisfied - && let &[(_, hi @ lo)] | &[(_, lo), .., (_, hi)] = provided_args_to_tuple - { - let mut err; - if tys.len() == 1 { - // A tuple wrap suggestion actually occurs within, - // so don't do anything special here. - err = self.err_ctxt().report_and_explain_type_error( - mk_trace( - lo, - formal_and_expected_inputs[mismatch_idx.to_expected_idx()], - provided_arg_tys[mismatch_idx].0, - ), - self.param_env, - terr, - ); - err.span_label( - full_call_span, - format!("arguments to this {call_name} are incorrect"), - ); - } else { - err = self.dcx().struct_span_err( - full_call_span, - format!( - "{call_name} takes {}{} but {} {} supplied", - if c_variadic { "at least " } else { "" }, - potentially_plural_count( - formal_and_expected_inputs.len(), - "argument" - ), - potentially_plural_count(provided_args.len(), "argument"), - pluralize!("was", provided_args.len()) - ), - ); - err.code(err_code.to_owned()); - err.multipart_suggestion_verbose( - "wrap these arguments in parentheses to construct a tuple", - vec![ - (lo.shrink_to_lo(), "(".to_string()), - (hi.shrink_to_hi(), ")".to_string()), - ], - Applicability::MachineApplicable, - ); - }; - self.label_fn_like( - &mut err, - fn_def_id, - callee_ty, - call_expr, - None, - Some(mismatch_idx.as_usize()), - &matched_inputs, - &formal_and_expected_inputs, - is_method, - tuple_arguments, - ); - suggest_confusable(&mut err); - return err.emit(); - } - } + if let Some(fallback_error) = fn_call_diag_ctxt.ensure_has_errors() { + return fallback_error; } // Okay, so here's where it gets complicated in regards to what errors @@ -926,608 +596,47 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 2) Valid but incorrect arguments // 3) Invalid arguments // - Currently I think this only comes up with `CyclicTy` - // + // We first need to go through, remove those from (3) and emit those // as their own error, particularly since they're error code and // message is special. From what I can tell, we *must* emit these // here (vs somewhere prior to this function) since the arguments // become invalid *because* of how they get used in the function. // It is what it is. - - if errors.is_empty() { - if cfg!(debug_assertions) { - span_bug!(error_span, "expected errors from argument matrix"); - } else { - let mut err = - self.dcx().create_err(errors::ArgMismatchIndeterminate { span: error_span }); - suggest_confusable(&mut err); - return err.emit(); - } - } - - let detect_dotdot = |err: &mut Diag<'_>, ty: Ty<'_>, expr: &hir::Expr<'_>| { - if let ty::Adt(adt, _) = ty.kind() - && self.tcx().is_lang_item(adt.did(), hir::LangItem::RangeFull) - && let hir::ExprKind::Struct( - hir::QPath::LangItem(hir::LangItem::RangeFull, _), - [], - _, - ) = expr.kind - { - // We have `Foo(a, .., c)`, where the user might be trying to use the "rest" syntax - // from default field values, which is not supported on tuples. - let explanation = if self.tcx.features().default_field_values() { - "this is only supported on non-tuple struct literals" - } else if self.tcx.sess.is_nightly_build() { - "this is only supported on non-tuple struct literals when \ - `#![feature(default_field_values)]` is enabled" - } else { - "this is not supported" - }; - let msg = format!( - "you might have meant to use `..` to skip providing a value for \ - expected fields, but {explanation}; it is instead interpreted as a \ - `std::ops::RangeFull` literal", - ); - err.span_help(expr.span, msg); - } - }; - - let mut reported = None; - errors.retain(|error| { - let Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(e))) = - error - else { - return true; - }; - let (provided_ty, provided_span) = provided_arg_tys[*provided_idx]; - let trace = - mk_trace(provided_span, formal_and_expected_inputs[*expected_idx], provided_ty); - if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308) { - let mut err = - self.err_ctxt().report_and_explain_type_error(trace, self.param_env, *e); - suggest_confusable(&mut err); - reported = Some(err.emit()); - return false; - } - true - }); - - // We're done if we found errors, but we already emitted them. - if let Some(reported) = reported - && errors.is_empty() + if let Some(err) = fn_call_diag_ctxt.filter_out_invalid_arguments() + && fn_call_diag_ctxt.errors.is_empty() { - return reported; + // We're done if we found errors, but we already emitted them. + return err; + } + + assert!(!fn_call_diag_ctxt.errors.is_empty()); + + // Last special case: if there is only one "Incompatible" error, just emit that + if let Some(err) = fn_call_diag_ctxt.check_single_incompatible() { + return err; } - assert!(!errors.is_empty()); // Okay, now that we've emitted the special errors separately, we // are only left missing/extra/swapped and mismatched arguments, both // can be collated pretty easily if needed. - // Next special case: if there is only one "Incompatible" error, just emit that - if let &[ - Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(err))), - ] = &errors[..] - { - let (formal_ty, expected_ty) = formal_and_expected_inputs[expected_idx]; - let (provided_ty, provided_arg_span) = provided_arg_tys[provided_idx]; - let trace = mk_trace(provided_arg_span, (formal_ty, expected_ty), provided_ty); - let mut err = self.err_ctxt().report_and_explain_type_error(trace, self.param_env, err); - self.emit_coerce_suggestions( - &mut err, - provided_args[provided_idx], - provided_ty, - Expectation::rvalue_hint(self, expected_ty) - .only_has_type(self) - .unwrap_or(formal_ty), - None, - None, - ); - err.span_label(full_call_span, format!("arguments to this {call_name} are incorrect")); - - self.label_generic_mismatches( - &mut err, - fn_def_id, - &matched_inputs, - &provided_arg_tys, - &formal_and_expected_inputs, - is_method, - ); - - if let hir::ExprKind::MethodCall(_, rcvr, _, _) = call_expr.kind - && provided_idx.as_usize() == expected_idx.as_usize() - { - self.note_source_of_type_mismatch_constraint( - &mut err, - rcvr, - crate::demand::TypeMismatchSource::Arg { - call_expr, - incompatible_arg: provided_idx.as_usize(), - }, - ); - } - - self.suggest_ptr_null_mut( - expected_ty, - provided_ty, - provided_args[provided_idx], - &mut err, - ); - - self.suggest_deref_unwrap_or( - &mut err, - callee_ty, - call_ident, - expected_ty, - provided_ty, - provided_args[provided_idx], - is_method, - ); - - // Call out where the function is defined - self.label_fn_like( - &mut err, - fn_def_id, - callee_ty, - call_expr, - Some(expected_ty), - Some(expected_idx.as_usize()), - &matched_inputs, - &formal_and_expected_inputs, - is_method, - tuple_arguments, - ); - suggest_confusable(&mut err); - detect_dotdot(&mut err, provided_ty, provided_args[provided_idx]); - return err.emit(); - } - // Special case, we found an extra argument is provided, which is very common in practice. // but there is a obviously better removing suggestion compared to the current one, // try to find the argument with Error type, if we removed it all the types will become good, // then we will replace the current suggestion. - if let [Error::Extra(provided_idx)] = &errors[..] { - let remove_idx_is_perfect = |idx: usize| -> bool { - let removed_arg_tys = provided_arg_tys - .iter() - .enumerate() - .filter_map(|(j, arg)| if idx == j { None } else { Some(arg) }) - .collect::>(); - std::iter::zip(formal_and_expected_inputs.iter(), removed_arg_tys.iter()).all( - |((expected_ty, _), (provided_ty, _))| { - !provided_ty.references_error() - && self.may_coerce(*provided_ty, *expected_ty) - }, - ) - }; + fn_call_diag_ctxt.maybe_optimize_extra_arg_suggestion(); - if !remove_idx_is_perfect(provided_idx.as_usize()) { - if let Some(i) = (0..provided_args.len()).find(|&i| remove_idx_is_perfect(i)) { - errors = vec![Error::Extra(ProvidedIdx::from_usize(i))]; - } - } - } + let mut err = fn_call_diag_ctxt.initial_final_diagnostic(); + fn_call_diag_ctxt.suggest_confusable(&mut err); - let mut err = if formal_and_expected_inputs.len() == provided_args.len() { - struct_span_code_err!( - self.dcx(), - full_call_span, - E0308, - "arguments to this {} are incorrect", - call_name, - ) - } else { - self.dcx() - .struct_span_err( - full_call_span, - format!( - "this {} takes {}{} but {} {} supplied", - call_name, - if c_variadic { "at least " } else { "" }, - potentially_plural_count(formal_and_expected_inputs.len(), "argument"), - potentially_plural_count(provided_args.len(), "argument"), - pluralize!("was", provided_args.len()) - ), - ) - .with_code(err_code.to_owned()) - }; + // As we encounter issues, keep track of what we want to provide for the suggestion. - suggest_confusable(&mut err); - // As we encounter issues, keep track of what we want to provide for the suggestion - let mut labels = vec![]; - // If there is a single error, we give a specific suggestion; otherwise, we change to - // "did you mean" with the suggested function call - enum SuggestionText { - None, - Provide(bool), - Remove(bool), - Swap, - Reorder, - DidYouMean, - } - let mut suggestion_text = SuggestionText::None; + let (mut suggestions, labels, suggestion_text) = + fn_call_diag_ctxt.labels_and_suggestion_text(&mut err); - let ty_to_snippet = |ty: Ty<'tcx>, expected_idx: ExpectedIdx| { - if ty.is_unit() { - "()".to_string() - } else if ty.is_suggestable(tcx, false) { - format!("/* {ty} */") - } else if let Some(fn_def_id) = fn_def_id - && self.tcx.def_kind(fn_def_id).is_fn_like() - && let self_implicit = - matches!(call_expr.kind, hir::ExprKind::MethodCall(..)) as usize - && let Some(Some(arg)) = - self.tcx.fn_arg_idents(fn_def_id).get(expected_idx.as_usize() + self_implicit) - && arg.name != kw::SelfLower - { - format!("/* {} */", arg.name) - } else { - "/* value */".to_string() - } - }; - - let mut errors = errors.into_iter().peekable(); - let mut only_extras_so_far = errors - .peek() - .is_some_and(|first| matches!(first, Error::Extra(arg_idx) if arg_idx.index() == 0)); - let mut prev_extra_idx = None; - let mut suggestions = vec![]; - while let Some(error) = errors.next() { - only_extras_so_far &= matches!(error, Error::Extra(_)); - - match error { - Error::Invalid(provided_idx, expected_idx, compatibility) => { - let (formal_ty, expected_ty) = formal_and_expected_inputs[expected_idx]; - let (provided_ty, provided_span) = provided_arg_tys[provided_idx]; - if let Compatibility::Incompatible(error) = compatibility { - let trace = mk_trace(provided_span, (formal_ty, expected_ty), provided_ty); - if let Some(e) = error { - self.err_ctxt().note_type_err( - &mut err, - &trace.cause, - None, - Some(self.param_env.and(trace.values)), - e, - true, - None, - ); - } - } - - self.emit_coerce_suggestions( - &mut err, - provided_args[provided_idx], - provided_ty, - Expectation::rvalue_hint(self, expected_ty) - .only_has_type(self) - .unwrap_or(formal_ty), - None, - None, - ); - detect_dotdot(&mut err, provided_ty, provided_args[provided_idx]); - } - Error::Extra(arg_idx) => { - let (provided_ty, provided_span) = provided_arg_tys[arg_idx]; - let provided_ty_name = if !has_error_or_infer([provided_ty]) { - // FIXME: not suggestable, use something else - format!(" of type `{provided_ty}`") - } else { - "".to_string() - }; - let idx = if provided_arg_tys.len() == 1 { - "".to_string() - } else { - format!(" #{}", arg_idx.as_usize() + 1) - }; - labels.push(( - provided_span, - format!("unexpected argument{idx}{provided_ty_name}"), - )); - let mut span = provided_span; - if span.can_be_used_for_suggestions() - && error_span.can_be_used_for_suggestions() - { - if arg_idx.index() > 0 - && let Some((_, prev)) = - provided_arg_tys.get(ProvidedIdx::from_usize(arg_idx.index() - 1)) - { - // Include previous comma - span = prev.shrink_to_hi().to(span); - } - - // Is last argument for deletion in a row starting from the 0-th argument? - // Then delete the next comma, so we are not left with `f(, ...)` - // - // fn f() {} - // - f(0, 1,) - // + f() - let trim_next_comma = match errors.peek() { - Some(Error::Extra(provided_idx)) - if only_extras_so_far - && provided_idx.index() > arg_idx.index() + 1 => - // If the next Error::Extra ("next") doesn't next to current ("current"), - // fn foo(_: (), _: u32) {} - // - foo("current", (), 1u32, "next") - // + foo((), 1u32) - // If the previous error is not a `Error::Extra`, then do not trim the next comma - // - foo((), "current", 42u32, "next") - // + foo((), 42u32) - { - prev_extra_idx.is_none_or(|prev_extra_idx| { - prev_extra_idx + 1 == arg_idx.index() - }) - } - // If no error left, we need to delete the next comma - None if only_extras_so_far => true, - // Not sure if other error type need to be handled as well - _ => false, - }; - - if trim_next_comma { - let next = provided_arg_tys - .get(arg_idx + 1) - .map(|&(_, sp)| sp) - .unwrap_or_else(|| { - // Try to move before `)`. Note that `)` here is not necessarily - // the latin right paren, it could be a Unicode-confusable that - // looks like a `)`, so we must not use `- BytePos(1)` - // manipulations here. - self.tcx().sess.source_map().end_point(call_expr.span) - }); - - // Include next comma - span = span.until(next); - } - - suggestions.push((span, String::new())); - - suggestion_text = match suggestion_text { - SuggestionText::None => SuggestionText::Remove(false), - SuggestionText::Remove(_) => SuggestionText::Remove(true), - _ => SuggestionText::DidYouMean, - }; - prev_extra_idx = Some(arg_idx.index()) - } - detect_dotdot(&mut err, provided_ty, provided_args[arg_idx]); - } - Error::Missing(expected_idx) => { - // If there are multiple missing arguments adjacent to each other, - // then we can provide a single error. - - let mut missing_idxs = vec![expected_idx]; - while let Some(e) = errors.next_if(|e| { - matches!(e, Error::Missing(next_expected_idx) - if *next_expected_idx == *missing_idxs.last().unwrap() + 1) - }) { - match e { - Error::Missing(expected_idx) => missing_idxs.push(expected_idx), - _ => unreachable!( - "control flow ensures that we should always get an `Error::Missing`" - ), - } - } - - // NOTE: Because we might be re-arranging arguments, might have extra - // arguments, etc. it's hard to *really* know where we should provide - // this error label, so as a heuristic, we point to the provided arg, or - // to the call if the missing inputs pass the provided args. - match &missing_idxs[..] { - &[expected_idx] => { - let (_, input_ty) = formal_and_expected_inputs[expected_idx]; - let span = if let Some((_, arg_span)) = - provided_arg_tys.get(expected_idx.to_provided_idx()) - { - *arg_span - } else { - args_span - }; - let rendered = if !has_error_or_infer([input_ty]) { - format!(" of type `{input_ty}`") - } else { - "".to_string() - }; - labels.push(( - span, - format!( - "argument #{}{rendered} is missing", - expected_idx.as_usize() + 1 - ), - )); - - suggestion_text = match suggestion_text { - SuggestionText::None => SuggestionText::Provide(false), - SuggestionText::Provide(_) => SuggestionText::Provide(true), - _ => SuggestionText::DidYouMean, - }; - } - &[first_idx, second_idx] => { - let (_, first_expected_ty) = formal_and_expected_inputs[first_idx]; - let (_, second_expected_ty) = formal_and_expected_inputs[second_idx]; - let span = if let (Some((_, first_span)), Some((_, second_span))) = ( - provided_arg_tys.get(first_idx.to_provided_idx()), - provided_arg_tys.get(second_idx.to_provided_idx()), - ) { - first_span.to(*second_span) - } else { - args_span - }; - let rendered = - if !has_error_or_infer([first_expected_ty, second_expected_ty]) { - format!( - " of type `{first_expected_ty}` and `{second_expected_ty}`" - ) - } else { - "".to_string() - }; - labels.push((span, format!("two arguments{rendered} are missing"))); - suggestion_text = match suggestion_text { - SuggestionText::None | SuggestionText::Provide(_) => { - SuggestionText::Provide(true) - } - _ => SuggestionText::DidYouMean, - }; - } - &[first_idx, second_idx, third_idx] => { - let (_, first_expected_ty) = formal_and_expected_inputs[first_idx]; - let (_, second_expected_ty) = formal_and_expected_inputs[second_idx]; - let (_, third_expected_ty) = formal_and_expected_inputs[third_idx]; - let span = if let (Some((_, first_span)), Some((_, third_span))) = ( - provided_arg_tys.get(first_idx.to_provided_idx()), - provided_arg_tys.get(third_idx.to_provided_idx()), - ) { - first_span.to(*third_span) - } else { - args_span - }; - let rendered = if !has_error_or_infer([ - first_expected_ty, - second_expected_ty, - third_expected_ty, - ]) { - format!( - " of type `{first_expected_ty}`, `{second_expected_ty}`, and `{third_expected_ty}`" - ) - } else { - "".to_string() - }; - labels.push((span, format!("three arguments{rendered} are missing"))); - suggestion_text = match suggestion_text { - SuggestionText::None | SuggestionText::Provide(_) => { - SuggestionText::Provide(true) - } - _ => SuggestionText::DidYouMean, - }; - } - missing_idxs => { - let first_idx = *missing_idxs.first().unwrap(); - let last_idx = *missing_idxs.last().unwrap(); - // NOTE: Because we might be re-arranging arguments, might have extra arguments, etc. - // It's hard to *really* know where we should provide this error label, so this is a - // decent heuristic - let span = if let (Some((_, first_span)), Some((_, last_span))) = ( - provided_arg_tys.get(first_idx.to_provided_idx()), - provided_arg_tys.get(last_idx.to_provided_idx()), - ) { - first_span.to(*last_span) - } else { - args_span - }; - labels.push((span, "multiple arguments are missing".to_string())); - suggestion_text = match suggestion_text { - SuggestionText::None | SuggestionText::Provide(_) => { - SuggestionText::Provide(true) - } - _ => SuggestionText::DidYouMean, - }; - } - } - } - Error::Swap( - first_provided_idx, - second_provided_idx, - first_expected_idx, - second_expected_idx, - ) => { - let (first_provided_ty, first_span) = provided_arg_tys[first_provided_idx]; - let (_, first_expected_ty) = formal_and_expected_inputs[first_expected_idx]; - let first_provided_ty_name = if !has_error_or_infer([first_provided_ty]) { - format!(", found `{first_provided_ty}`") - } else { - String::new() - }; - labels.push(( - first_span, - format!("expected `{first_expected_ty}`{first_provided_ty_name}"), - )); - - let (second_provided_ty, second_span) = provided_arg_tys[second_provided_idx]; - let (_, second_expected_ty) = formal_and_expected_inputs[second_expected_idx]; - let second_provided_ty_name = if !has_error_or_infer([second_provided_ty]) { - format!(", found `{second_provided_ty}`") - } else { - String::new() - }; - labels.push(( - second_span, - format!("expected `{second_expected_ty}`{second_provided_ty_name}"), - )); - - suggestion_text = match suggestion_text { - SuggestionText::None => SuggestionText::Swap, - _ => SuggestionText::DidYouMean, - }; - } - Error::Permutation(args) => { - for (dst_arg, dest_input) in args { - let (_, expected_ty) = formal_and_expected_inputs[dst_arg]; - let (provided_ty, provided_span) = provided_arg_tys[dest_input]; - let provided_ty_name = if !has_error_or_infer([provided_ty]) { - format!(", found `{provided_ty}`") - } else { - String::new() - }; - labels.push(( - provided_span, - format!("expected `{expected_ty}`{provided_ty_name}"), - )); - } - - suggestion_text = match suggestion_text { - SuggestionText::None => SuggestionText::Reorder, - _ => SuggestionText::DidYouMean, - }; - } - } - } - - self.label_generic_mismatches( - &mut err, - fn_def_id, - &matched_inputs, - &provided_arg_tys, - &formal_and_expected_inputs, - is_method, - ); - - // Incorporate the argument changes in the removal suggestion. - // When a type is *missing*, and the rest are additional, we want to suggest these with a - // multipart suggestion, but in order to do so we need to figure out *where* the arg that - // was provided but had the wrong type should go, because when looking at `expected_idx` - // that is the position in the argument list in the definition, while `provided_idx` will - // not be present. So we have to look at what the *last* provided position was, and point - // one after to suggest the replacement. FIXME(estebank): This is hacky, and there's - // probably a better more involved change we can make to make this work. - // For example, if we have - // ``` - // fn foo(i32, &'static str) {} - // foo((), (), ()); - // ``` - // what should be suggested is - // ``` - // foo(/* i32 */, /* &str */); - // ``` - // which includes the replacement of the first two `()` for the correct type, and the - // removal of the last `()`. - let mut prev = -1; - for (expected_idx, provided_idx) in matched_inputs.iter_enumerated() { - // We want to point not at the *current* argument expression index, but rather at the - // index position where it *should have been*, which is *after* the previous one. - if let Some(provided_idx) = provided_idx { - prev = provided_idx.index() as i64; - continue; - } - let idx = ProvidedIdx::from_usize((prev + 1) as usize); - if let Some((_, arg_span)) = provided_arg_tys.get(idx) { - prev += 1; - // There is a type that was *not* found anywhere, so it isn't a move, but a - // replacement and we look at what type it should have been. This will allow us - // To suggest a multipart suggestion when encountering `foo(1, "")` where the def - // was `fn foo(())`. - let (_, expected_ty) = formal_and_expected_inputs[expected_idx]; - suggestions.push((*arg_span, ty_to_snippet(expected_ty, expected_idx))); - } - } + fn_call_diag_ctxt.label_generic_mismatches(&mut err); + fn_call_diag_ctxt.append_arguments_changes(&mut suggestions); // If we have less than 5 things to say, it would be useful to call out exactly what's wrong if labels.len() <= 5 { @@ -1537,113 +646,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // Call out where the function is defined - self.label_fn_like( + fn_call_diag_ctxt.label_fn_like( &mut err, fn_def_id, - callee_ty, + fn_call_diag_ctxt.callee_ty, call_expr, None, None, - &matched_inputs, - &formal_and_expected_inputs, - is_method, + &fn_call_diag_ctxt.matched_inputs, + &fn_call_diag_ctxt.formal_and_expected_inputs, + fn_call_diag_ctxt.call_metadata.is_method, tuple_arguments, ); // And add a suggestion block for all of the parameters - let suggestion_text = match suggestion_text { - SuggestionText::None => None, - SuggestionText::Provide(plural) => { - Some(format!("provide the argument{}", if plural { "s" } else { "" })) - } - SuggestionText::Remove(plural) => { - err.multipart_suggestion_verbose( - format!("remove the extra argument{}", if plural { "s" } else { "" }), - suggestions, - Applicability::HasPlaceholders, - ); - None - } - SuggestionText::Swap => Some("swap these arguments".to_string()), - SuggestionText::Reorder => Some("reorder these arguments".to_string()), - SuggestionText::DidYouMean => Some("did you mean".to_string()), - }; - if let Some(suggestion_text) = suggestion_text - && !full_call_span.in_external_macro(self.sess().source_map()) + if let Some(suggestion_message) = + FnCallDiagCtxt::format_suggestion_text(&mut err, suggestions, suggestion_text) + && !fn_call_diag_ctxt.call_is_in_macro() { - let source_map = self.sess().source_map(); - let suggestion_span = if let Some(args_span) = error_span.trim_start(full_call_span) { - // Span of the braces, e.g. `(a, b, c)`. - args_span - } else { - // The arg span of a function call that wasn't even given braces - // like what might happen with delegation reuse. - // e.g. `reuse HasSelf::method;` should suggest `reuse HasSelf::method($args);`. - full_call_span.shrink_to_hi() - }; + let (suggestion_span, suggestion_code) = fn_call_diag_ctxt.suggestion_code(); - // Controls how the arguments should be listed in the suggestion. - enum ArgumentsFormatting { - SingleLine, - Multiline { fallback_indent: String, brace_indent: String }, - } - let arguments_formatting = { - let mut provided_inputs = matched_inputs.iter().filter_map(|a| *a); - if let Some(brace_indent) = source_map.indentation_before(suggestion_span) - && let Some(first_idx) = provided_inputs.by_ref().next() - && let Some(last_idx) = provided_inputs.by_ref().next() - && let (_, first_span) = provided_arg_tys[first_idx] - && let (_, last_span) = provided_arg_tys[last_idx] - && source_map.is_multiline(first_span.to(last_span)) - && let Some(fallback_indent) = source_map.indentation_before(first_span) - { - ArgumentsFormatting::Multiline { fallback_indent, brace_indent } - } else { - ArgumentsFormatting::SingleLine - } - }; - - let mut suggestion = "(".to_owned(); - let mut needs_comma = false; - for (expected_idx, provided_idx) in matched_inputs.iter_enumerated() { - if needs_comma { - suggestion += ","; - } - match &arguments_formatting { - ArgumentsFormatting::SingleLine if needs_comma => suggestion += " ", - ArgumentsFormatting::SingleLine => {} - ArgumentsFormatting::Multiline { .. } => suggestion += "\n", - } - needs_comma = true; - let (suggestion_span, suggestion_text) = if let Some(provided_idx) = provided_idx - && let (_, provided_span) = provided_arg_tys[*provided_idx] - && let Ok(arg_text) = source_map.span_to_snippet(provided_span) - { - (Some(provided_span), arg_text) - } else { - // Propose a placeholder of the correct type - let (_, expected_ty) = formal_and_expected_inputs[expected_idx]; - (None, ty_to_snippet(expected_ty, expected_idx)) - }; - if let ArgumentsFormatting::Multiline { fallback_indent, .. } = - &arguments_formatting - { - let indent = suggestion_span - .and_then(|span| source_map.indentation_before(span)) - .unwrap_or_else(|| fallback_indent.clone()); - suggestion += &indent; - } - suggestion += &suggestion_text; - } - if let ArgumentsFormatting::Multiline { brace_indent, .. } = arguments_formatting { - suggestion += ",\n"; - suggestion += &brace_indent; - } - suggestion += ")"; err.span_suggestion_verbose( suggestion_span, - suggestion_text, - suggestion, + suggestion_message, + suggestion_code, Applicability::HasPlaceholders, ); } @@ -2836,3 +1862,1333 @@ impl FnParam<'_> { D(*self, idx) } } + +struct FnCallDiagCtxt<'a, 'b, 'tcx> { + arg_matching_ctxt: ArgMatchingCtxt<'a, 'b, 'tcx>, + errors: Vec>, + matched_inputs: IndexVec>, +} + +impl<'a, 'b, 'tcx> Deref for FnCallDiagCtxt<'a, 'b, 'tcx> { + type Target = ArgMatchingCtxt<'a, 'b, 'tcx>; + + fn deref(&self) -> &Self::Target { + &self.arg_matching_ctxt + } +} + +// Controls how the arguments should be listed in the suggestion. +enum ArgumentsFormatting { + SingleLine, + Multiline { fallback_indent: String, brace_indent: String }, +} + +impl<'a, 'b, 'tcx> FnCallDiagCtxt<'a, 'b, 'tcx> { + fn new( + arg: &'a FnCtxt<'b, 'tcx>, + compatibility_diagonal: IndexVec>, + formal_and_expected_inputs: IndexVec, Ty<'tcx>)>, + provided_args: IndexVec>, + c_variadic: bool, + err_code: ErrCode, + fn_def_id: Option, + call_span: Span, + call_expr: &'tcx Expr<'tcx>, + tuple_arguments: TupleArgumentsFlag, + ) -> Self { + let arg_matching_ctxt = ArgMatchingCtxt::new( + arg, + compatibility_diagonal, + formal_and_expected_inputs, + provided_args, + c_variadic, + err_code, + fn_def_id, + call_span, + call_expr, + tuple_arguments, + ); + + // The algorithm here is inspired by levenshtein distance and longest common subsequence. + // We'll try to detect 4 different types of mistakes: + // - An extra parameter has been provided that doesn't satisfy *any* of the other inputs + // - An input is missing, which isn't satisfied by *any* of the other arguments + // - Some number of arguments have been provided in the wrong order + // - A type is straight up invalid + let (errors, matched_inputs) = ArgMatrix::new( + arg_matching_ctxt.provided_args.len(), + arg_matching_ctxt.formal_and_expected_inputs.len(), + |provided, expected| arg_matching_ctxt.check_compatible(provided, expected), + ) + .find_errors(); + + FnCallDiagCtxt { arg_matching_ctxt, errors, matched_inputs } + } + + fn check_wrap_args_in_tuple(&self) -> Option { + if let Some((mismatch_idx, terr)) = self.first_incompatible_error() { + // Is the first bad expected argument a tuple? + // Do we have as many extra provided arguments as the tuple's length? + // If so, we might have just forgotten to wrap some args in a tuple. + if let Some(ty::Tuple(tys)) = + self.formal_and_expected_inputs.get(mismatch_idx.to_expected_idx()).map(|tys| tys.1.kind()) + // If the tuple is unit, we're not actually wrapping any arguments. + && !tys.is_empty() + && self.provided_arg_tys.len() == self.formal_and_expected_inputs.len() - 1 + tys.len() + { + // Wrap up the N provided arguments starting at this position in a tuple. + let provided_args_to_tuple = &self.provided_arg_tys[mismatch_idx..]; + let (provided_args_to_tuple, provided_args_after_tuple) = + provided_args_to_tuple.split_at(tys.len()); + let provided_as_tuple = Ty::new_tup_from_iter( + self.tcx, + provided_args_to_tuple.iter().map(|&(ty, _)| ty), + ); + + let mut satisfied = true; + // Check if the newly wrapped tuple + rest of the arguments are compatible. + for ((_, expected_ty), provided_ty) in std::iter::zip( + self.formal_and_expected_inputs[mismatch_idx.to_expected_idx()..].iter(), + [provided_as_tuple] + .into_iter() + .chain(provided_args_after_tuple.iter().map(|&(ty, _)| ty)), + ) { + if !self.may_coerce(provided_ty, *expected_ty) { + satisfied = false; + break; + } + } + + // If they're compatible, suggest wrapping in an arg, and we're done! + // Take some care with spans, so we don't suggest wrapping a macro's + // innards in parenthesis, for example. + if satisfied + && let &[(_, hi @ lo)] | &[(_, lo), .., (_, hi)] = provided_args_to_tuple + { + let mut err; + if tys.len() == 1 { + // A tuple wrap suggestion actually occurs within, + // so don't do anything special here. + err = self.err_ctxt().report_and_explain_type_error( + self.arg_matching_ctxt.args_ctxt.call_ctxt.mk_trace( + lo, + self.formal_and_expected_inputs[mismatch_idx.to_expected_idx()], + self.provided_arg_tys[mismatch_idx].0, + ), + self.param_env, + terr, + ); + let call_name = self.call_metadata.call_name; + err.span_label( + self.call_metadata.full_call_span, + format!("arguments to this {call_name} are incorrect"), + ); + } else { + let call_name = self.call_metadata.call_name; + err = self.dcx().struct_span_err( + self.arg_matching_ctxt.args_ctxt.call_metadata.full_call_span, + format!( + "{call_name} takes {}{} but {} {} supplied", + if self.c_variadic { "at least " } else { "" }, + potentially_plural_count( + self.formal_and_expected_inputs.len(), + "argument" + ), + potentially_plural_count(self.provided_args.len(), "argument"), + pluralize!("was", self.provided_args.len()) + ), + ); + err.code(self.err_code.to_owned()); + err.multipart_suggestion_verbose( + "wrap these arguments in parentheses to construct a tuple", + vec![ + (lo.shrink_to_lo(), "(".to_string()), + (hi.shrink_to_hi(), ")".to_string()), + ], + Applicability::MachineApplicable, + ); + }; + self.arg_matching_ctxt.args_ctxt.call_ctxt.fn_ctxt.label_fn_like( + &mut err, + self.fn_def_id, + self.callee_ty, + self.call_expr, + None, + Some(mismatch_idx.as_usize()), + &self.matched_inputs, + &self.formal_and_expected_inputs, + self.call_metadata.is_method, + self.tuple_arguments, + ); + self.suggest_confusable(&mut err); + Some(err.emit()) + } else { + None + } + } else { + None + } + } else { + None + } + } + + fn ensure_has_errors(&self) -> Option { + if self.errors.is_empty() { + if cfg!(debug_assertions) { + span_bug!(self.call_metadata.error_span, "expected errors from argument matrix"); + } else { + let mut err = self.dcx().create_err(errors::ArgMismatchIndeterminate { + span: self.call_metadata.error_span, + }); + self.arg_matching_ctxt.suggest_confusable(&mut err); + return Some(err.emit()); + } + } + + None + } + + fn detect_dotdot(&self, err: &mut Diag<'_>, ty: Ty<'tcx>, expr: &hir::Expr<'tcx>) { + if let ty::Adt(adt, _) = ty.kind() + && self.tcx().is_lang_item(adt.did(), hir::LangItem::RangeFull) + && let hir::ExprKind::Struct(hir::QPath::LangItem(hir::LangItem::RangeFull, _), [], _) = + expr.kind + { + // We have `Foo(a, .., c)`, where the user might be trying to use the "rest" syntax + // from default field values, which is not supported on tuples. + let explanation = if self.tcx.features().default_field_values() { + "this is only supported on non-tuple struct literals" + } else if self.tcx.sess.is_nightly_build() { + "this is only supported on non-tuple struct literals when \ + `#![feature(default_field_values)]` is enabled" + } else { + "this is not supported" + }; + let msg = format!( + "you might have meant to use `..` to skip providing a value for \ + expected fields, but {explanation}; it is instead interpreted as a \ + `std::ops::RangeFull` literal", + ); + err.span_help(expr.span, msg); + } + } + + fn filter_out_invalid_arguments(&mut self) -> Option { + let mut reported = None; + + self.errors.retain(|error| { + let Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(e))) = + error + else { + return true; + }; + let (provided_ty, provided_span) = + self.arg_matching_ctxt.provided_arg_tys[*provided_idx]; + let trace = self.arg_matching_ctxt.mk_trace( + provided_span, + self.arg_matching_ctxt.formal_and_expected_inputs[*expected_idx], + provided_ty, + ); + if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308) { + let mut err = self.arg_matching_ctxt.err_ctxt().report_and_explain_type_error( + trace, + self.arg_matching_ctxt.param_env, + *e, + ); + self.arg_matching_ctxt.suggest_confusable(&mut err); + reported = Some(err.emit()); + return false; + } + true + }); + + reported + } + + fn check_single_incompatible(&self) -> Option { + if let &[ + Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(err))), + ] = &self.errors[..] + { + let (formal_ty, expected_ty) = self.formal_and_expected_inputs[expected_idx]; + let (provided_ty, provided_arg_span) = self.provided_arg_tys[provided_idx]; + let trace = self.mk_trace(provided_arg_span, (formal_ty, expected_ty), provided_ty); + let mut err = self.err_ctxt().report_and_explain_type_error(trace, self.param_env, err); + self.emit_coerce_suggestions( + &mut err, + self.provided_args[provided_idx], + provided_ty, + Expectation::rvalue_hint(self.fn_ctxt, expected_ty) + .only_has_type(self.fn_ctxt) + .unwrap_or(formal_ty), + None, + None, + ); + let call_name = self.call_metadata.call_name; + err.span_label( + self.call_metadata.full_call_span, + format!("arguments to this {call_name} are incorrect"), + ); + + self.fn_ctxt.label_generic_mismatches( + &mut err, + self.fn_def_id, + &self.matched_inputs, + &self.provided_arg_tys, + &self.formal_and_expected_inputs, + self.call_metadata.is_method, + ); + + if let hir::ExprKind::MethodCall(_, rcvr, _, _) = + self.arg_matching_ctxt.args_ctxt.call_ctxt.call_expr.kind + && provided_idx.as_usize() == expected_idx.as_usize() + { + self.note_source_of_type_mismatch_constraint( + &mut err, + rcvr, + crate::demand::TypeMismatchSource::Arg { + call_expr: self.call_expr, + incompatible_arg: provided_idx.as_usize(), + }, + ); + } + + self.suggest_ptr_null_mut( + expected_ty, + provided_ty, + self.provided_args[provided_idx], + &mut err, + ); + + self.suggest_deref_unwrap_or( + &mut err, + self.callee_ty, + self.call_metadata.call_ident, + expected_ty, + provided_ty, + self.provided_args[provided_idx], + self.call_metadata.is_method, + ); + + // Call out where the function is defined + self.label_fn_like( + &mut err, + self.fn_def_id, + self.callee_ty, + self.call_expr, + Some(expected_ty), + Some(expected_idx.as_usize()), + &self.matched_inputs, + &self.formal_and_expected_inputs, + self.call_metadata.is_method, + self.tuple_arguments, + ); + self.arg_matching_ctxt.suggest_confusable(&mut err); + self.detect_dotdot(&mut err, provided_ty, self.provided_args[provided_idx]); + return Some(err.emit()); + } + + None + } + + fn maybe_optimize_extra_arg_suggestion(&mut self) { + if let [Error::Extra(provided_idx)] = &self.errors[..] { + if !self.remove_idx_is_perfect(provided_idx.as_usize()) { + if let Some(i) = (0..self.args_ctxt.call_ctxt.provided_args.len()) + .find(|&i| self.remove_idx_is_perfect(i)) + { + self.errors = vec![Error::Extra(ProvidedIdx::from_usize(i))]; + } + } + } + } + + fn initial_final_diagnostic(&self) -> Diag<'_> { + if self.formal_and_expected_inputs.len() == self.provided_args.len() { + struct_span_code_err!( + self.dcx(), + self.call_metadata.full_call_span, + E0308, + "arguments to this {} are incorrect", + self.call_metadata.call_name, + ) + } else { + self.arg_matching_ctxt + .dcx() + .struct_span_err( + self.call_metadata.full_call_span, + format!( + "this {} takes {}{} but {} {} supplied", + self.call_metadata.call_name, + if self.c_variadic { "at least " } else { "" }, + potentially_plural_count(self.formal_and_expected_inputs.len(), "argument"), + potentially_plural_count(self.provided_args.len(), "argument"), + pluralize!("was", self.provided_args.len()) + ), + ) + .with_code(self.err_code.to_owned()) + } + } + + fn labels_and_suggestion_text( + &self, + err: &mut Diag<'_>, + ) -> (Vec<(Span, String)>, Vec<(Span, String)>, SuggestionText) { + // Don't print if it has error types or is just plain `_` + fn has_error_or_infer<'tcx>(tys: impl IntoIterator>) -> bool { + tys.into_iter().any(|ty| ty.references_error() || ty.is_ty_var()) + } + + let mut labels = Vec::new(); + let mut suggestion_text = SuggestionText::None; + + let mut errors = self.errors.iter().peekable(); + let mut only_extras_so_far = errors + .peek() + .is_some_and(|first| matches!(first, Error::Extra(arg_idx) if arg_idx.index() == 0)); + let mut prev_extra_idx = None; + let mut suggestions = vec![]; + while let Some(error) = errors.next() { + only_extras_so_far &= matches!(error, Error::Extra(_)); + + match error { + Error::Invalid(provided_idx, expected_idx, compatibility) => { + let (formal_ty, expected_ty) = + self.arg_matching_ctxt.args_ctxt.call_ctxt.formal_and_expected_inputs + [*expected_idx]; + let (provided_ty, provided_span) = + self.arg_matching_ctxt.provided_arg_tys[*provided_idx]; + if let Compatibility::Incompatible(error) = compatibility { + let trace = self.arg_matching_ctxt.args_ctxt.call_ctxt.mk_trace( + provided_span, + (formal_ty, expected_ty), + provided_ty, + ); + if let Some(e) = error { + self.err_ctxt().note_type_err( + err, + &trace.cause, + None, + Some(self.param_env.and(trace.values)), + *e, + true, + None, + ); + } + } + + self.emit_coerce_suggestions( + err, + self.provided_args[*provided_idx], + provided_ty, + Expectation::rvalue_hint(self.fn_ctxt, expected_ty) + .only_has_type(self.fn_ctxt) + .unwrap_or(formal_ty), + None, + None, + ); + self.detect_dotdot(err, provided_ty, self.provided_args[*provided_idx]); + } + Error::Extra(arg_idx) => { + let (provided_ty, provided_span) = self.provided_arg_tys[*arg_idx]; + let provided_ty_name = if !has_error_or_infer([provided_ty]) { + // FIXME: not suggestable, use something else + format!(" of type `{provided_ty}`") + } else { + "".to_string() + }; + let idx = if self.provided_arg_tys.len() == 1 { + "".to_string() + } else { + format!(" #{}", arg_idx.as_usize() + 1) + }; + labels.push(( + provided_span, + format!("unexpected argument{idx}{provided_ty_name}"), + )); + let mut span = provided_span; + if span.can_be_used_for_suggestions() + && self.call_metadata.error_span.can_be_used_for_suggestions() + { + if arg_idx.index() > 0 + && let Some((_, prev)) = self + .provided_arg_tys + .get(ProvidedIdx::from_usize(arg_idx.index() - 1)) + { + // Include previous comma + span = prev.shrink_to_hi().to(span); + } + + // Is last argument for deletion in a row starting from the 0-th argument? + // Then delete the next comma, so we are not left with `f(, ...)` + // + // fn f() {} + // - f(0, 1,) + // + f() + let trim_next_comma = match errors.peek() { + Some(Error::Extra(provided_idx)) + if only_extras_so_far + && provided_idx.index() > arg_idx.index() + 1 => + // If the next Error::Extra ("next") doesn't next to current ("current"), + // fn foo(_: (), _: u32) {} + // - foo("current", (), 1u32, "next") + // + foo((), 1u32) + // If the previous error is not a `Error::Extra`, then do not trim the next comma + // - foo((), "current", 42u32, "next") + // + foo((), 42u32) + { + prev_extra_idx.is_none_or(|prev_extra_idx| { + prev_extra_idx + 1 == arg_idx.index() + }) + } + // If no error left, we need to delete the next comma + None if only_extras_so_far => true, + // Not sure if other error type need to be handled as well + _ => false, + }; + + if trim_next_comma { + let next = self + .provided_arg_tys + .get(*arg_idx + 1) + .map(|&(_, sp)| sp) + .unwrap_or_else(|| { + // Try to move before `)`. Note that `)` here is not necessarily + // the latin right paren, it could be a Unicode-confusable that + // looks like a `)`, so we must not use `- BytePos(1)` + // manipulations here. + self.arg_matching_ctxt + .tcx() + .sess + .source_map() + .end_point(self.call_expr.span) + }); + + // Include next comma + span = span.until(next); + } + + suggestions.push((span, String::new())); + + suggestion_text = match suggestion_text { + SuggestionText::None => SuggestionText::Remove(false), + SuggestionText::Remove(_) => SuggestionText::Remove(true), + _ => SuggestionText::DidYouMean, + }; + prev_extra_idx = Some(arg_idx.index()) + } + self.detect_dotdot(err, provided_ty, self.provided_args[*arg_idx]); + } + Error::Missing(expected_idx) => { + // If there are multiple missing arguments adjacent to each other, + // then we can provide a single error. + + let mut missing_idxs = vec![*expected_idx]; + while let Some(e) = errors.next_if(|e| { + matches!(e, Error::Missing(next_expected_idx) + if *next_expected_idx == *missing_idxs.last().unwrap() + 1) + }) { + match e { + Error::Missing(expected_idx) => missing_idxs.push(*expected_idx), + _ => unreachable!( + "control flow ensures that we should always get an `Error::Missing`" + ), + } + } + + // NOTE: Because we might be re-arranging arguments, might have extra + // arguments, etc. it's hard to *really* know where we should provide + // this error label, so as a heuristic, we point to the provided arg, or + // to the call if the missing inputs pass the provided args. + match &missing_idxs[..] { + &[expected_idx] => { + let (_, input_ty) = self.formal_and_expected_inputs[expected_idx]; + let span = if let Some((_, arg_span)) = + self.provided_arg_tys.get(expected_idx.to_provided_idx()) + { + *arg_span + } else { + self.args_span + }; + let rendered = if !has_error_or_infer([input_ty]) { + format!(" of type `{input_ty}`") + } else { + "".to_string() + }; + labels.push(( + span, + format!( + "argument #{}{rendered} is missing", + expected_idx.as_usize() + 1 + ), + )); + + suggestion_text = match suggestion_text { + SuggestionText::None => SuggestionText::Provide(false), + SuggestionText::Provide(_) => SuggestionText::Provide(true), + _ => SuggestionText::DidYouMean, + }; + } + &[first_idx, second_idx] => { + let (_, first_expected_ty) = self.formal_and_expected_inputs[first_idx]; + let (_, second_expected_ty) = + self.formal_and_expected_inputs[second_idx]; + let span = if let (Some((_, first_span)), Some((_, second_span))) = ( + self.provided_arg_tys.get(first_idx.to_provided_idx()), + self.provided_arg_tys.get(second_idx.to_provided_idx()), + ) { + first_span.to(*second_span) + } else { + self.args_span + }; + let rendered = + if !has_error_or_infer([first_expected_ty, second_expected_ty]) { + format!( + " of type `{first_expected_ty}` and `{second_expected_ty}`" + ) + } else { + "".to_string() + }; + labels.push((span, format!("two arguments{rendered} are missing"))); + suggestion_text = match suggestion_text { + SuggestionText::None | SuggestionText::Provide(_) => { + SuggestionText::Provide(true) + } + _ => SuggestionText::DidYouMean, + }; + } + &[first_idx, second_idx, third_idx] => { + let (_, first_expected_ty) = self.formal_and_expected_inputs[first_idx]; + let (_, second_expected_ty) = + self.formal_and_expected_inputs[second_idx]; + let (_, third_expected_ty) = self.formal_and_expected_inputs[third_idx]; + let span = if let (Some((_, first_span)), Some((_, third_span))) = ( + self.provided_arg_tys.get(first_idx.to_provided_idx()), + self.provided_arg_tys.get(third_idx.to_provided_idx()), + ) { + first_span.to(*third_span) + } else { + self.args_span + }; + let rendered = if !has_error_or_infer([ + first_expected_ty, + second_expected_ty, + third_expected_ty, + ]) { + format!( + " of type `{first_expected_ty}`, `{second_expected_ty}`, and `{third_expected_ty}`" + ) + } else { + "".to_string() + }; + labels.push((span, format!("three arguments{rendered} are missing"))); + suggestion_text = match suggestion_text { + SuggestionText::None | SuggestionText::Provide(_) => { + SuggestionText::Provide(true) + } + _ => SuggestionText::DidYouMean, + }; + } + missing_idxs => { + let first_idx = *missing_idxs.first().unwrap(); + let last_idx = *missing_idxs.last().unwrap(); + // NOTE: Because we might be re-arranging arguments, might have extra arguments, etc. + // It's hard to *really* know where we should provide this error label, so this is a + // decent heuristic + let span = if let (Some((_, first_span)), Some((_, last_span))) = ( + self.provided_arg_tys.get(first_idx.to_provided_idx()), + self.provided_arg_tys.get(last_idx.to_provided_idx()), + ) { + first_span.to(*last_span) + } else { + self.args_span + }; + labels.push((span, "multiple arguments are missing".to_string())); + suggestion_text = match suggestion_text { + SuggestionText::None | SuggestionText::Provide(_) => { + SuggestionText::Provide(true) + } + _ => SuggestionText::DidYouMean, + }; + } + } + } + Error::Swap( + first_provided_idx, + second_provided_idx, + first_expected_idx, + second_expected_idx, + ) => { + let (first_provided_ty, first_span) = + self.provided_arg_tys[*first_provided_idx]; + let (_, first_expected_ty) = + self.formal_and_expected_inputs[*first_expected_idx]; + let first_provided_ty_name = if !has_error_or_infer([first_provided_ty]) { + format!(", found `{first_provided_ty}`") + } else { + String::new() + }; + labels.push(( + first_span, + format!("expected `{first_expected_ty}`{first_provided_ty_name}"), + )); + + let (second_provided_ty, second_span) = + self.provided_arg_tys[*second_provided_idx]; + let (_, second_expected_ty) = + self.formal_and_expected_inputs[*second_expected_idx]; + let second_provided_ty_name = if !has_error_or_infer([second_provided_ty]) { + format!(", found `{second_provided_ty}`") + } else { + String::new() + }; + labels.push(( + second_span, + format!("expected `{second_expected_ty}`{second_provided_ty_name}"), + )); + + suggestion_text = match suggestion_text { + SuggestionText::None => SuggestionText::Swap, + _ => SuggestionText::DidYouMean, + }; + } + Error::Permutation(args) => { + for (dst_arg, dest_input) in args { + let (_, expected_ty) = self.formal_and_expected_inputs[*dst_arg]; + let (provided_ty, provided_span) = self.provided_arg_tys[*dest_input]; + let provided_ty_name = if !has_error_or_infer([provided_ty]) { + format!(", found `{provided_ty}`") + } else { + String::new() + }; + labels.push(( + provided_span, + format!("expected `{expected_ty}`{provided_ty_name}"), + )); + } + + suggestion_text = match suggestion_text { + SuggestionText::None => SuggestionText::Reorder, + _ => SuggestionText::DidYouMean, + }; + } + } + } + + (suggestions, labels, suggestion_text) + } + + fn label_generic_mismatches(&self, err: &mut Diag<'b>) { + self.fn_ctxt.label_generic_mismatches( + err, + self.fn_def_id, + &self.matched_inputs, + &self.provided_arg_tys, + &self.formal_and_expected_inputs, + self.call_metadata.is_method, + ); + } + + /// Incorporate the argument changes in the removal suggestion. + /// + /// When a type is *missing*, and the rest are additional, we want to suggest these with a + /// multipart suggestion, but in order to do so we need to figure out *where* the arg that + /// was provided but had the wrong type should go, because when looking at `expected_idx` + /// that is the position in the argument list in the definition, while `provided_idx` will + /// not be present. So we have to look at what the *last* provided position was, and point + /// one after to suggest the replacement. + fn append_arguments_changes(&self, suggestions: &mut Vec<(Span, String)>) { + // FIXME(estebank): This is hacky, and there's + // probably a better more involved change we can make to make this work. + // For example, if we have + // ``` + // fn foo(i32, &'static str) {} + // foo((), (), ()); + // ``` + // what should be suggested is + // ``` + // foo(/* i32 */, /* &str */); + // ``` + // which includes the replacement of the first two `()` for the correct type, and the + // removal of the last `()`. + + let mut prev = -1; + for (expected_idx, provided_idx) in self.matched_inputs.iter_enumerated() { + // We want to point not at the *current* argument expression index, but rather at the + // index position where it *should have been*, which is *after* the previous one. + if let Some(provided_idx) = provided_idx { + prev = provided_idx.index() as i64; + continue; + } + let idx = ProvidedIdx::from_usize((prev + 1) as usize); + if let Some((_, arg_span)) = self.provided_arg_tys.get(idx) { + prev += 1; + // There is a type that was *not* found anywhere, so it isn't a move, but a + // replacement and we look at what type it should have been. This will allow us + // To suggest a multipart suggestion when encountering `foo(1, "")` where the def + // was `fn foo(())`. + let (_, expected_ty) = self.formal_and_expected_inputs[expected_idx]; + suggestions.push((*arg_span, self.ty_to_snippet(expected_ty, expected_idx))); + } + } + } + + fn format_suggestion_text( + err: &mut Diag<'_>, + suggestions: Vec<(Span, String)>, + suggestion_text: SuggestionText, + ) -> Option { + let suggestion_text = match suggestion_text { + SuggestionText::None => None, + SuggestionText::Provide(plural) => { + Some(format!("provide the argument{}", if plural { "s" } else { "" })) + } + SuggestionText::Remove(plural) => { + err.multipart_suggestion_verbose( + format!("remove the extra argument{}", if plural { "s" } else { "" }), + suggestions, + Applicability::HasPlaceholders, + ); + None + } + SuggestionText::Swap => Some("swap these arguments".to_string()), + SuggestionText::Reorder => Some("reorder these arguments".to_string()), + SuggestionText::DidYouMean => Some("did you mean".to_string()), + }; + suggestion_text + } + + fn arguments_formatting(&self, suggestion_span: Span) -> ArgumentsFormatting { + let source_map = self.sess().source_map(); + let mut provided_inputs = self.matched_inputs.iter().filter_map(|a| *a); + if let Some(brace_indent) = source_map.indentation_before(suggestion_span) + && let Some(first_idx) = provided_inputs.by_ref().next() + && let Some(last_idx) = provided_inputs.by_ref().next() + && let (_, first_span) = self.provided_arg_tys[first_idx] + && let (_, last_span) = self.provided_arg_tys[last_idx] + && source_map.is_multiline(first_span.to(last_span)) + && let Some(fallback_indent) = source_map.indentation_before(first_span) + { + ArgumentsFormatting::Multiline { fallback_indent, brace_indent } + } else { + ArgumentsFormatting::SingleLine + } + } + + fn suggestion_code(&self) -> (Span, String) { + let source_map = self.sess().source_map(); + let suggestion_span = if let Some(args_span) = + self.call_metadata.error_span.trim_start(self.call_metadata.full_call_span) + { + // Span of the braces, e.g. `(a, b, c)`. + args_span + } else { + // The arg span of a function call that wasn't even given braces + // like what might happen with delegation reuse. + // e.g. `reuse HasSelf::method;` should suggest `reuse HasSelf::method($args);`. + self.call_metadata.full_call_span.shrink_to_hi() + }; + + let arguments_formatting = self.arguments_formatting(suggestion_span); + + let mut suggestion = "(".to_owned(); + let mut needs_comma = false; + for (expected_idx, provided_idx) in self.matched_inputs.iter_enumerated() { + if needs_comma { + suggestion += ","; + } + match &arguments_formatting { + ArgumentsFormatting::SingleLine if needs_comma => suggestion += " ", + ArgumentsFormatting::SingleLine => {} + ArgumentsFormatting::Multiline { .. } => suggestion += "\n", + } + needs_comma = true; + let (suggestion_span, suggestion_text) = if let Some(provided_idx) = provided_idx + && let (_, provided_span) = self.provided_arg_tys[*provided_idx] + && let Ok(arg_text) = source_map.span_to_snippet(provided_span) + { + (Some(provided_span), arg_text) + } else { + // Propose a placeholder of the correct type + let (_, expected_ty) = self.formal_and_expected_inputs[expected_idx]; + (None, self.ty_to_snippet(expected_ty, expected_idx)) + }; + if let ArgumentsFormatting::Multiline { fallback_indent, .. } = &arguments_formatting { + let indent = suggestion_span + .and_then(|span| source_map.indentation_before(span)) + .unwrap_or_else(|| fallback_indent.clone()); + suggestion += &indent; + } + suggestion += &suggestion_text; + } + if let ArgumentsFormatting::Multiline { brace_indent, .. } = arguments_formatting { + suggestion += ",\n"; + suggestion += &brace_indent; + } + suggestion += ")"; + + (suggestion_span, suggestion) + } +} + +struct ArgMatchingCtxt<'a, 'b, 'tcx> { + args_ctxt: ArgsCtxt<'a, 'b, 'tcx>, + provided_arg_tys: IndexVec, Span)>, +} + +impl<'a, 'b, 'tcx> Deref for ArgMatchingCtxt<'a, 'b, 'tcx> { + type Target = ArgsCtxt<'a, 'b, 'tcx>; + + fn deref(&self) -> &Self::Target { + &self.args_ctxt + } +} + +impl<'a, 'b, 'tcx> ArgMatchingCtxt<'a, 'b, 'tcx> { + fn new( + arg: &'a FnCtxt<'b, 'tcx>, + compatibility_diagonal: IndexVec>, + formal_and_expected_inputs: IndexVec, Ty<'tcx>)>, + provided_args: IndexVec>, + c_variadic: bool, + err_code: ErrCode, + fn_def_id: Option, + call_span: Span, + call_expr: &'tcx Expr<'tcx>, + tuple_arguments: TupleArgumentsFlag, + ) -> Self { + let args_ctxt = ArgsCtxt::new( + arg, + compatibility_diagonal, + formal_and_expected_inputs, + provided_args, + c_variadic, + err_code, + fn_def_id, + call_span, + call_expr, + tuple_arguments, + ); + let provided_arg_tys = args_ctxt.provided_arg_tys(); + + ArgMatchingCtxt { args_ctxt, provided_arg_tys } + } + + fn suggest_confusable(&self, err: &mut Diag<'_>) { + let Some(call_name) = self.call_metadata.call_ident else { + return; + }; + let Some(callee_ty) = self.callee_ty else { + return; + }; + let input_types: Vec> = self.provided_arg_tys.iter().map(|(ty, _)| *ty).collect(); + + // Check for other methods in the following order + // - methods marked as `rustc_confusables` with the provided arguments + // - methods with the same argument type/count and short levenshtein distance + // - methods marked as `rustc_confusables` (done) + // - methods with short levenshtein distance + + // Look for commonly confusable method names considering arguments. + if let Some(_name) = self.confusable_method_name( + err, + callee_ty.peel_refs(), + call_name, + Some(input_types.clone()), + ) { + return; + } + // Look for method names with short levenshtein distance, considering arguments. + if let Some((assoc, fn_sig)) = self.similar_assoc(call_name) + && fn_sig.inputs()[1..] + .iter() + .zip(input_types.iter()) + .all(|(expected, found)| self.may_coerce(*expected, *found)) + && fn_sig.inputs()[1..].len() == input_types.len() + { + let assoc_name = assoc.name(); + err.span_suggestion_verbose( + call_name.span, + format!("you might have meant to use `{}`", assoc_name), + assoc_name, + Applicability::MaybeIncorrect, + ); + return; + } + } + + /// A "softer" version of the `demand_compatible`, which checks types without persisting them, + /// and treats error types differently + /// This will allow us to "probe" for other argument orders that would likely have been correct + fn check_compatible( + &self, + provided_idx: ProvidedIdx, + expected_idx: ExpectedIdx, + ) -> Compatibility<'tcx> { + if provided_idx.as_usize() == expected_idx.as_usize() { + return self.compatibility_diagonal[provided_idx].clone(); + } + + let (formal_input_ty, expected_input_ty) = self.formal_and_expected_inputs[expected_idx]; + // If either is an error type, we defy the usual convention and consider them to *not* be + // coercible. This prevents our error message heuristic from trying to pass errors into + // every argument. + if (formal_input_ty, expected_input_ty).references_error() { + return Compatibility::Incompatible(None); + } + + let (arg_ty, arg_span) = self.provided_arg_tys[provided_idx]; + + let expectation = Expectation::rvalue_hint(self.fn_ctxt, expected_input_ty); + let coerced_ty = expectation.only_has_type(self.fn_ctxt).unwrap_or(formal_input_ty); + let can_coerce = self.may_coerce(arg_ty, coerced_ty); + if !can_coerce { + return Compatibility::Incompatible(Some(ty::error::TypeError::Sorts( + ty::error::ExpectedFound::new(coerced_ty, arg_ty), + ))); + } + + // Using probe here, since we don't want this subtyping to affect inference. + let subtyping_error = self.probe(|_| { + self.at(&self.misc(arg_span), self.param_env) + .sup(DefineOpaqueTypes::Yes, formal_input_ty, coerced_ty) + .err() + }); + + // Same as above: if either the coerce type or the checked type is an error type, + // consider them *not* compatible. + let references_error = (coerced_ty, arg_ty).references_error(); + match (references_error, subtyping_error) { + (false, None) => Compatibility::Compatible, + (_, subtyping_error) => Compatibility::Incompatible(subtyping_error), + } + } + + fn remove_idx_is_perfect(&self, idx: usize) -> bool { + let removed_arg_tys = self + .provided_arg_tys + .iter() + .enumerate() + .filter_map(|(j, arg)| if idx == j { None } else { Some(arg) }) + .collect::>(); + std::iter::zip(self.formal_and_expected_inputs.iter(), removed_arg_tys.iter()).all( + |((expected_ty, _), (provided_ty, _))| { + !provided_ty.references_error() && self.may_coerce(*provided_ty, *expected_ty) + }, + ) + } +} + +struct ArgsCtxt<'a, 'b, 'tcx> { + call_ctxt: CallCtxt<'a, 'b, 'tcx>, + call_metadata: CallMetadata, + args_span: Span, +} + +impl<'a, 'b, 'tcx> Deref for ArgsCtxt<'a, 'b, 'tcx> { + type Target = CallCtxt<'a, 'b, 'tcx>; + + fn deref(&self) -> &Self::Target { + &self.call_ctxt + } +} + +impl<'a, 'b, 'tcx> ArgsCtxt<'a, 'b, 'tcx> { + fn new( + arg: &'a FnCtxt<'b, 'tcx>, + compatibility_diagonal: IndexVec>, + formal_and_expected_inputs: IndexVec, Ty<'tcx>)>, + provided_args: IndexVec>, + c_variadic: bool, + err_code: ErrCode, + fn_def_id: Option, + call_span: Span, + call_expr: &'tcx Expr<'tcx>, + tuple_arguments: TupleArgumentsFlag, + ) -> Self { + let call_ctxt: CallCtxt<'_, '_, '_> = CallCtxt::new( + arg, + compatibility_diagonal, + formal_and_expected_inputs, + provided_args, + c_variadic, + err_code, + fn_def_id, + call_span, + call_expr, + tuple_arguments, + ); + + let call_metadata = call_ctxt.call_metadata(); + let args_span = call_metadata + .error_span + .trim_start(call_metadata.full_call_span) + .unwrap_or(call_metadata.error_span); + + ArgsCtxt { args_span, call_metadata, call_ctxt } + } + + /// Get the argument span in the context of the call span so that + /// suggestions and labels are (more) correct when an arg is a + /// macro invocation. + fn normalize_span(&self, span: Span) -> Span { + let normalized_span = + span.find_ancestor_inside_same_ctxt(self.call_metadata.error_span).unwrap_or(span); + // Sometimes macros mess up the spans, so do not normalize the + // arg span to equal the error span, because that's less useful + // than pointing out the arg expr in the wrong context. + if normalized_span.source_equal(self.call_metadata.error_span) { + span + } else { + normalized_span + } + } + + /// Computes the provided types and spans. + fn provided_arg_tys(&self) -> IndexVec, Span)> { + self.call_ctxt + .provided_args + .iter() + .map(|expr| { + let ty = self + .call_ctxt + .fn_ctxt + .typeck_results + .borrow() + .expr_ty_adjusted_opt(*expr) + .unwrap_or_else(|| Ty::new_misc_error(self.call_ctxt.fn_ctxt.tcx)); + ( + self.call_ctxt.fn_ctxt.resolve_vars_if_possible(ty), + self.normalize_span(expr.span), + ) + }) + .collect() + } + + // Obtain another method on `Self` that have similar name. + fn similar_assoc(&self, call_name: Ident) -> Option<(ty::AssocItem, ty::FnSig<'tcx>)> { + if let Some(callee_ty) = self.call_ctxt.callee_ty + && let Ok(Some(assoc)) = self.call_ctxt.fn_ctxt.probe_op( + call_name.span, + MethodCall, + Some(call_name), + None, + IsSuggestion(true), + callee_ty.peel_refs(), + self.call_ctxt.callee_expr.unwrap().hir_id, + TraitsInScope, + |mut ctxt| ctxt.probe_for_similar_candidate(), + ) + && assoc.is_method() + { + let args = + self.call_ctxt.fn_ctxt.infcx.fresh_args_for_item(call_name.span, assoc.def_id); + let fn_sig = self + .call_ctxt + .fn_ctxt + .tcx + .fn_sig(assoc.def_id) + .instantiate(self.call_ctxt.fn_ctxt.tcx, args); + + self.call_ctxt.fn_ctxt.instantiate_binder_with_fresh_vars( + call_name.span, + BoundRegionConversionTime::FnCall, + fn_sig, + ); + } + None + } + + fn call_is_in_macro(&self) -> bool { + self.call_metadata.full_call_span.in_external_macro(self.sess().source_map()) + } +} + +struct CallMetadata { + error_span: Span, + call_ident: Option, + full_call_span: Span, + call_name: &'static str, + is_method: bool, +} + +struct CallCtxt<'a, 'b, 'tcx> { + fn_ctxt: &'a FnCtxt<'b, 'tcx>, + compatibility_diagonal: IndexVec>, + formal_and_expected_inputs: IndexVec, Ty<'tcx>)>, + provided_args: IndexVec>, + c_variadic: bool, + err_code: ErrCode, + fn_def_id: Option, + call_span: Span, + call_expr: &'tcx hir::Expr<'tcx>, + tuple_arguments: TupleArgumentsFlag, + callee_expr: Option<&'tcx Expr<'tcx>>, + callee_ty: Option>, +} + +impl<'a, 'b, 'tcx> Deref for CallCtxt<'a, 'b, 'tcx> { + type Target = &'a FnCtxt<'b, 'tcx>; + + fn deref(&self) -> &Self::Target { + &self.fn_ctxt + } +} + +impl<'a, 'b, 'tcx> CallCtxt<'a, 'b, 'tcx> { + fn new( + fn_ctxt: &'a FnCtxt<'b, 'tcx>, + compatibility_diagonal: IndexVec>, + formal_and_expected_inputs: IndexVec, Ty<'tcx>)>, + provided_args: IndexVec>, + c_variadic: bool, + err_code: ErrCode, + fn_def_id: Option, + call_span: Span, + call_expr: &'tcx hir::Expr<'tcx>, + tuple_arguments: TupleArgumentsFlag, + ) -> CallCtxt<'a, 'b, 'tcx> { + let callee_expr = match &call_expr.peel_blocks().kind { + hir::ExprKind::Call(callee, _) => Some(*callee), + hir::ExprKind::MethodCall(_, receiver, ..) => { + if let Some((DefKind::AssocFn, def_id)) = + fn_ctxt.typeck_results.borrow().type_dependent_def(call_expr.hir_id) + && let Some(assoc) = fn_ctxt.tcx.opt_associated_item(def_id) + && assoc.is_method() + { + Some(*receiver) + } else { + None + } + } + _ => None, + }; + + let callee_ty = callee_expr.and_then(|callee_expr| { + fn_ctxt.typeck_results.borrow().expr_ty_adjusted_opt(callee_expr) + }); + + CallCtxt { + fn_ctxt, + compatibility_diagonal, + formal_and_expected_inputs, + provided_args, + c_variadic, + err_code, + fn_def_id, + call_span, + call_expr, + tuple_arguments, + callee_expr, + callee_ty, + } + } + + fn call_metadata(&self) -> CallMetadata { + match &self.call_expr.kind { + hir::ExprKind::Call( + hir::Expr { hir_id, span, kind: hir::ExprKind::Path(qpath), .. }, + _, + ) => { + if let Res::Def(DefKind::Ctor(of, _), _) = + self.typeck_results.borrow().qpath_res(qpath, *hir_id) + { + let name = match of { + CtorOf::Struct => "struct", + CtorOf::Variant => "enum variant", + }; + CallMetadata { + error_span: self.call_span, + call_ident: None, + full_call_span: *span, + call_name: name, + is_method: false, + } + } else { + CallMetadata { + error_span: self.call_span, + call_ident: None, + full_call_span: *span, + call_name: "function", + is_method: false, + } + } + } + hir::ExprKind::Call(hir::Expr { span, .. }, _) => CallMetadata { + error_span: self.call_span, + call_ident: None, + full_call_span: *span, + call_name: "function", + is_method: false, + }, + hir::ExprKind::MethodCall(path_segment, _, _, span) => { + let ident_span = path_segment.ident.span; + let ident_span = if let Some(args) = path_segment.args { + ident_span.with_hi(args.span_ext.hi()) + } else { + ident_span + }; + CallMetadata { + error_span: *span, + call_ident: Some(path_segment.ident), + full_call_span: ident_span, + call_name: "method", + is_method: true, + } + } + k => span_bug!(self.call_span, "checking argument types on a non-call: `{:?}`", k), + } + } + + fn mk_trace( + &self, + span: Span, + (formal_ty, expected_ty): (Ty<'tcx>, Ty<'tcx>), + provided_ty: Ty<'tcx>, + ) -> TypeTrace<'tcx> { + let mismatched_ty = if expected_ty == provided_ty { + // If expected == provided, then we must have failed to sup + // the formal type. Avoid printing out "expected Ty, found Ty" + // in that case. + formal_ty + } else { + expected_ty + }; + TypeTrace::types(&self.misc(span), mismatched_ty, provided_ty) + } + + fn ty_to_snippet(&self, ty: Ty<'tcx>, expected_idx: ExpectedIdx) -> String { + if ty.is_unit() { + "()".to_string() + } else if ty.is_suggestable(self.tcx, false) { + format!("/* {ty} */") + } else if let Some(fn_def_id) = self.fn_def_id + && self.tcx.def_kind(fn_def_id).is_fn_like() + && let self_implicit = + matches!(self.call_expr.kind, hir::ExprKind::MethodCall(..)) as usize + && let Some(Some(arg)) = + self.tcx.fn_arg_idents(fn_def_id).get(expected_idx.as_usize() + self_implicit) + && arg.name != kw::SelfLower + { + format!("/* {} */", arg.name) + } else { + "/* value */".to_string() + } + } + + fn first_incompatible_error(&self) -> Option<(ProvidedIdx, TypeError<'tcx>)> { + self.compatibility_diagonal.iter_enumerated().find_map(|(i, c)| { + if let Compatibility::Incompatible(Some(terr)) = c { Some((i, *terr)) } else { None } + }) + } +} + +enum SuggestionText { + None, + Provide(bool), + Remove(bool), + Swap, + Reorder, + DidYouMean, +} From 1991779bd4a0a8d8905b6644b06aa317dde353ac Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Mon, 15 Sep 2025 15:31:56 +0000 Subject: [PATCH 532/682] Make llvm_enzyme a regular cargo feature This makes it clearer that it is set by the build system rather than by the rustc that compiles the current rustc. It also avoids bootstrap needing to pass --check-cfg llvm_enzyme to rustc. --- compiler/rustc/Cargo.toml | 1 + compiler/rustc_builtin_macros/Cargo.toml | 5 +++++ compiler/rustc_builtin_macros/src/autodiff.rs | 3 ++- compiler/rustc_codegen_llvm/Cargo.toml | 1 + compiler/rustc_codegen_llvm/src/back/lto.rs | 2 +- compiler/rustc_codegen_llvm/src/back/write.rs | 6 ++++-- compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs | 8 ++++---- compiler/rustc_driver_impl/Cargo.toml | 1 + compiler/rustc_interface/Cargo.toml | 1 + src/bootstrap/src/core/build_steps/compile.rs | 4 ---- src/bootstrap/src/lib.rs | 6 +++--- 11 files changed, 23 insertions(+), 15 deletions(-) diff --git a/compiler/rustc/Cargo.toml b/compiler/rustc/Cargo.toml index e3214d1ab9ce..9ef8fa75062a 100644 --- a/compiler/rustc/Cargo.toml +++ b/compiler/rustc/Cargo.toml @@ -30,6 +30,7 @@ features = ['unprefixed_malloc_on_supported_platforms'] check_only = ['rustc_driver_impl/check_only'] jemalloc = ['dep:tikv-jemalloc-sys'] llvm = ['rustc_driver_impl/llvm'] +llvm_enzyme = ['rustc_driver_impl/llvm_enzyme'] max_level_info = ['rustc_driver_impl/max_level_info'] rustc_randomized_layouts = ['rustc_driver_impl/rustc_randomized_layouts'] # tidy-alphabetical-end diff --git a/compiler/rustc_builtin_macros/Cargo.toml b/compiler/rustc_builtin_macros/Cargo.toml index e56b9e641a10..ce9a3ce3f248 100644 --- a/compiler/rustc_builtin_macros/Cargo.toml +++ b/compiler/rustc_builtin_macros/Cargo.toml @@ -33,3 +33,8 @@ smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } thin-vec = "0.2.12" tracing = "0.1" # tidy-alphabetical-end + +[features] +# tidy-alphabetical-start +llvm_enzyme = [] +# tidy-alphabetical-end diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs index 48d0795af5ee..f4a923797e2d 100644 --- a/compiler/rustc_builtin_macros/src/autodiff.rs +++ b/compiler/rustc_builtin_macros/src/autodiff.rs @@ -209,7 +209,8 @@ mod llvm_enzyme { mut item: Annotatable, mode: DiffMode, ) -> Vec { - if cfg!(not(llvm_enzyme)) { + // FIXME(bjorn3) maybe have the backend directly tell if autodiff is supported? + if cfg!(not(feature = "llvm_enzyme")) { ecx.sess.dcx().emit_err(errors::AutoDiffSupportNotBuild { span: meta_item.span }); return vec![item]; } diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index 2d11628250cd..67bd1e59bb0c 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -46,5 +46,6 @@ tracing = "0.1" [features] # tidy-alphabetical-start check_only = ["rustc_llvm/check_only"] +llvm_enzyme = [] # tidy-alphabetical-end diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index f571716d9dd9..78107d95e5a7 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -617,7 +617,7 @@ pub(crate) fn run_pass_manager( crate::builder::gpu_offload::handle_gpu_code(cgcx, &cx); } - if cfg!(llvm_enzyme) && enable_ad && !thin { + if cfg!(feature = "llvm_enzyme") && enable_ad && !thin { let opt_stage = llvm::OptStage::FatLTO; let stage = write::AutodiffStage::PostAD; if !config.autodiff.contains(&config::AutoDiff::NoPostopt) { diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 423f0da48781..bda81fbd19e8 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -574,7 +574,8 @@ pub(crate) unsafe fn llvm_optimize( // FIXME(ZuseZ4): In a future update we could figure out how to only optimize individual functions getting // differentiated. - let consider_ad = cfg!(llvm_enzyme) && config.autodiff.contains(&config::AutoDiff::Enable); + let consider_ad = + cfg!(feature = "llvm_enzyme") && config.autodiff.contains(&config::AutoDiff::Enable); let run_enzyme = autodiff_stage == AutodiffStage::DuringAD; let print_before_enzyme = config.autodiff.contains(&config::AutoDiff::PrintModBefore); let print_after_enzyme = config.autodiff.contains(&config::AutoDiff::PrintModAfter); @@ -740,7 +741,8 @@ pub(crate) fn optimize( // If we know that we will later run AD, then we disable vectorization and loop unrolling. // Otherwise we pretend AD is already done and run the normal opt pipeline (=PostAD). - let consider_ad = cfg!(llvm_enzyme) && config.autodiff.contains(&config::AutoDiff::Enable); + let consider_ad = + cfg!(feature = "llvm_enzyme") && config.autodiff.contains(&config::AutoDiff::Enable); let autodiff_stage = if consider_ad { AutodiffStage::PreAD } else { AutodiffStage::PostAD }; // The embedded bitcode is used to run LTO/ThinLTO. // The bitcode obtained during the `codegen` phase is no longer suitable for performing LTO. diff --git a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs index 56d756e52cce..695435eb6daa 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/enzyme_ffi.rs @@ -59,10 +59,10 @@ pub(crate) enum LLVMRustVerifierFailureAction { LLVMReturnStatusAction = 2, } -#[cfg(llvm_enzyme)] +#[cfg(feature = "llvm_enzyme")] pub(crate) use self::Enzyme_AD::*; -#[cfg(llvm_enzyme)] +#[cfg(feature = "llvm_enzyme")] pub(crate) mod Enzyme_AD { use std::ffi::{CString, c_char}; @@ -134,10 +134,10 @@ pub(crate) mod Enzyme_AD { } } -#[cfg(not(llvm_enzyme))] +#[cfg(not(feature = "llvm_enzyme"))] pub(crate) use self::Fallback_AD::*; -#[cfg(not(llvm_enzyme))] +#[cfg(not(feature = "llvm_enzyme"))] pub(crate) mod Fallback_AD { #![allow(unused_variables)] diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index ae1dbd2cf514..46efa50cff36 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -74,6 +74,7 @@ ctrlc = "3.4.4" # tidy-alphabetical-start check_only = ['rustc_interface/check_only'] llvm = ['rustc_interface/llvm'] +llvm_enzyme = ['rustc_interface/llvm_enzyme'] max_level_info = ['rustc_log/max_level_info'] rustc_randomized_layouts = [ 'rustc_index/rustc_randomized_layouts', diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml index 473ac5e0cea4..f0836c47740a 100644 --- a/compiler/rustc_interface/Cargo.toml +++ b/compiler/rustc_interface/Cargo.toml @@ -58,4 +58,5 @@ rustc_abi = { path = "../rustc_abi" } # tidy-alphabetical-start check_only = ['rustc_codegen_llvm?/check_only'] llvm = ['dep:rustc_codegen_llvm'] +llvm_enzyme = ['rustc_builtin_macros/llvm_enzyme', 'rustc_codegen_llvm/llvm_enzyme'] # tidy-alphabetical-end diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 1458b0beefa8..14104d7d1d78 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1357,10 +1357,6 @@ pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetS cargo.env("RUSTC_VERIFY_LLVM_IR", "1"); } - if builder.config.llvm_enzyme { - cargo.rustflag("--cfg=llvm_enzyme"); - } - // These conditionals represent a tension between three forces: // - For non-check builds, we need to define some LLVM-related environment // variables, requiring LLVM to have been built. diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index a2aeed20948e..e953fe2945e4 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -87,9 +87,6 @@ const EXTRA_CHECK_CFGS: &[(Option, &str, Option<&[&'static str]>)] = &[ (Some(Mode::Codegen), "bootstrap", None), (Some(Mode::ToolRustcPrivate), "bootstrap", None), (Some(Mode::ToolStd), "bootstrap", None), - (Some(Mode::Rustc), "llvm_enzyme", None), - (Some(Mode::Codegen), "llvm_enzyme", None), - (Some(Mode::ToolRustcPrivate), "llvm_enzyme", None), (Some(Mode::ToolRustcPrivate), "rust_analyzer", None), (Some(Mode::ToolStd), "rust_analyzer", None), // Any library specific cfgs like `target_os`, `target_arch` should be put in @@ -869,6 +866,9 @@ impl Build { if (self.config.llvm_enabled(target) || kind == Kind::Check) && check("llvm") { features.push("llvm"); } + if self.config.llvm_enzyme { + features.push("llvm_enzyme"); + } // keep in sync with `bootstrap/compile.rs:rustc_cargo_env` if self.config.rust_randomize_layout && check("rustc_randomized_layouts") { features.push("rustc_randomized_layouts"); From 2d76bb1f23be5da37199e632ad2473f245db4a5f Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Fri, 12 Sep 2025 18:30:02 -0400 Subject: [PATCH 533/682] replace some `#[const_trait]` with `const trait` --- library/core/src/cmp.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 94536f25b419..95896ab14418 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -334,9 +334,8 @@ pub macro PartialEq($item:item) { #[doc(alias = "!=")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "Eq"] -#[const_trait] #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] -pub trait Eq: [const] PartialEq + PointeeSized { +pub const trait Eq: [const] PartialEq + PointeeSized { // this method is used solely by `impl Eq or #[derive(Eq)]` to assert that every component of a // type implements `Eq` itself. The current deriving infrastructure means doing this assertion // without using a method on this trait is nearly impossible. @@ -966,9 +965,8 @@ impl Clone for Reverse { #[doc(alias = ">=")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "Ord"] -#[const_trait] #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] -pub trait Ord: [const] Eq + [const] PartialOrd + PointeeSized { +pub const trait Ord: [const] Eq + [const] PartialOrd + PointeeSized { /// This method returns an [`Ordering`] between `self` and `other`. /// /// By convention, `self.cmp(&other)` returns the ordering matching the expression @@ -1352,9 +1350,8 @@ pub macro Ord($item:item) { )] #[rustc_diagnostic_item = "PartialOrd"] #[allow(multiple_supertrait_upcastable)] // FIXME(sized_hierarchy): remove this -#[const_trait] #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] -pub trait PartialOrd: PartialEq + PointeeSized { +pub const trait PartialOrd: PartialEq + PointeeSized { /// This method returns an ordering between `self` and `other` values if one exists. /// /// # Examples From 4e286122d9a9dfcc1ae33c3959a3e8fd325f324b Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 15 Sep 2025 05:21:30 +0300 Subject: [PATCH 534/682] Port a bunch of stuff from rustc and fix a bunch of type mismatches/diagnostics This started from porting coercion, but ended with porting much more. --- .../crates/hir-def/src/signatures.rs | 19 + .../crates/hir-ty/src/autoderef.rs | 405 +-- .../crates/hir-ty/src/builder.rs | 47 +- .../crates/hir-ty/src/chalk_ext.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/db.rs | 8 + .../hir-ty/src/diagnostics/unsafe_check.rs | 5 +- .../crates/hir-ty/src/display.rs | 45 +- .../rust-analyzer/crates/hir-ty/src/drop.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/infer.rs | 194 +- .../crates/hir-ty/src/infer/autoderef.rs | 54 + .../crates/hir-ty/src/infer/cast.rs | 156 +- .../crates/hir-ty/src/infer/closure.rs | 2305 +++++------------ .../hir-ty/src/infer/closure/analysis.rs | 1298 ++++++++++ .../crates/hir-ty/src/infer/coerce.rs | 2040 ++++++++++----- .../crates/hir-ty/src/infer/expr.rs | 685 +++-- .../crates/hir-ty/src/infer/pat.rs | 31 +- .../crates/hir-ty/src/infer/unify.rs | 616 +++-- .../rust-analyzer/crates/hir-ty/src/layout.rs | 7 +- .../rust-analyzer/crates/hir-ty/src/lib.rs | 6 +- .../crates/hir-ty/src/lower_nextsolver.rs | 106 + .../crates/hir-ty/src/method_resolution.rs | 47 +- .../crates/hir-ty/src/mir/borrowck.rs | 9 +- .../crates/hir-ty/src/mir/eval.rs | 6 +- .../crates/hir-ty/src/mir/eval/shim.rs | 15 +- .../crates/hir-ty/src/mir/eval/tests.rs | 142 +- .../crates/hir-ty/src/mir/lower.rs | 2 +- .../crates/hir-ty/src/next_solver.rs | 8 +- .../crates/hir-ty/src/next_solver/consts.rs | 47 +- .../crates/hir-ty/src/next_solver/def_id.rs | 60 +- .../crates/hir-ty/src/next_solver/fulfill.rs | 149 +- .../hir-ty/src/next_solver/fulfill/errors.rs | 1332 ++++++++++ .../hir-ty/src/next_solver/generic_arg.rs | 45 +- .../crates/hir-ty/src/next_solver/infer/at.rs | 32 +- .../hir-ty/src/next_solver/infer/mod.rs | 6 + .../infer/region_constraints/mod.rs | 53 +- .../next_solver/infer/relate/generalize.rs | 2 +- .../src/next_solver/infer/relate/lattice.rs | 269 ++ .../src/next_solver/infer/relate/mod.rs | 1 + .../hir-ty/src/next_solver/infer/select.rs | 334 +++ .../src/next_solver/infer/snapshot/fudge.rs | 263 ++ .../src/next_solver/infer/snapshot/mod.rs | 1 + .../next_solver/infer/snapshot/undo_log.rs | 4 + .../hir-ty/src/next_solver/infer/traits.rs | 108 +- .../src/next_solver/infer/type_variable.rs | 15 +- .../crates/hir-ty/src/next_solver/inspect.rs | 501 ++++ .../crates/hir-ty/src/next_solver/interner.rs | 88 +- .../crates/hir-ty/src/next_solver/mapping.rs | 37 + .../solve_normalize.rs => normalize.rs} | 66 +- .../hir-ty/src/next_solver/obligation_ctxt.rs | 203 ++ .../hir-ty/src/next_solver/predicate.rs | 52 +- .../crates/hir-ty/src/next_solver/project.rs | 3 - .../crates/hir-ty/src/next_solver/solver.rs | 16 +- .../src/next_solver/structural_normalize.rs | 57 + .../crates/hir-ty/src/next_solver/ty.rs | 87 +- .../crates/hir-ty/src/next_solver/util.rs | 6 +- .../crates/hir-ty/src/target_feature.rs | 2 +- .../crates/hir-ty/src/tests/coercion.rs | 42 +- .../crates/hir-ty/src/tests/diagnostics.rs | 1 - .../crates/hir-ty/src/tests/incremental.rs | 8 +- .../crates/hir-ty/src/tests/macros.rs | 34 +- .../hir-ty/src/tests/method_resolution.rs | 63 +- .../crates/hir-ty/src/tests/never_type.rs | 24 +- .../crates/hir-ty/src/tests/opaque_types.rs | 1 + .../crates/hir-ty/src/tests/patterns.rs | 38 +- .../crates/hir-ty/src/tests/regression.rs | 242 +- .../hir-ty/src/tests/regression/new_solver.rs | 111 + .../crates/hir-ty/src/tests/simple.rs | 98 +- .../crates/hir-ty/src/tests/traits.rs | 357 +-- .../rust-analyzer/crates/hir-ty/src/traits.rs | 5 - .../rust-analyzer/crates/hir-ty/src/utils.rs | 108 +- .../crates/hir-ty/src/variance.rs | 57 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 8 +- .../src/handlers/convert_closure_to_fn.rs | 7 +- .../src/handlers/extract_function.rs | 2 +- .../ide-completion/src/completions/dot.rs | 4 +- .../ide-completion/src/tests/expression.rs | 3 +- .../src/handlers/typed_hole.rs | 2 +- .../src/handlers/unresolved_method.rs | 24 +- .../crates/ide/src/hover/tests.rs | 14 +- .../crates/ide/src/inlay_hints/bind_pat.rs | 16 +- .../crates/intern/src/symbol/symbols.rs | 1 + .../rust-analyzer/tests/slow-tests/main.rs | 4 +- .../crates/test-utils/src/minicore.rs | 28 +- src/tools/rust-analyzer/xtask/src/tidy.rs | 9 + 84 files changed, 9412 insertions(+), 3998 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/infer/autoderef.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill/errors.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/lattice.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/select.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/fudge.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs rename src/tools/rust-analyzer/crates/hir-ty/src/next_solver/{project/solve_normalize.rs => normalize.rs} (87%) create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/obligation_ctxt.rs delete mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project.rs create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/next_solver/structural_normalize.rs diff --git a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs index bf72fafeae72..47638610ed73 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs @@ -489,6 +489,7 @@ bitflags! { const HAS_TARGET_FEATURE = 1 << 9; const DEPRECATED_SAFE_2024 = 1 << 10; const EXPLICIT_SAFE = 1 << 11; + const RUSTC_INTRINSIC = 1 << 12; } } @@ -522,6 +523,9 @@ impl FunctionSignature { if attrs.by_key(sym::target_feature).exists() { flags.insert(FnFlags::HAS_TARGET_FEATURE); } + if attrs.by_key(sym::rustc_intrinsic).exists() { + flags.insert(FnFlags::RUSTC_INTRINSIC); + } let legacy_const_generics_indices = attrs.rustc_legacy_const_generics(); let source = loc.source(db); @@ -617,6 +621,21 @@ impl FunctionSignature { pub fn has_target_feature(&self) -> bool { self.flags.contains(FnFlags::HAS_TARGET_FEATURE) } + + pub fn is_intrinsic(db: &dyn DefDatabase, id: FunctionId) -> bool { + let data = db.function_signature(id); + data.flags.contains(FnFlags::RUSTC_INTRINSIC) + // Keep this around for a bit until extern "rustc-intrinsic" abis are no longer used + || match &data.abi { + Some(abi) => *abi == sym::rust_dash_intrinsic, + None => match id.lookup(db).container { + ItemContainerId::ExternBlockId(block) => { + block.abi(db) == Some(sym::rust_dash_intrinsic) + } + _ => false, + }, + } + } } bitflags! { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs index 82696a5c94da..fd60ffcf24b0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs @@ -3,27 +3,28 @@ //! reference to a type with the field `bar`. This is an approximation of the //! logic in rustc (which lives in rustc_hir_analysis/check/autoderef.rs). -use std::mem; +use std::fmt; -use chalk_ir::cast::Cast; -use hir_def::lang_item::LangItem; -use hir_expand::name::Name; -use intern::sym; +use hir_def::{TypeAliasId, lang_item::LangItem}; +use rustc_type_ir::inherent::{IntoKind, Ty as _}; +use tracing::debug; use triomphe::Arc; +use crate::next_solver::infer::InferOk; use crate::{ - Canonical, Goal, Interner, ProjectionTyExt, TraitEnvironment, Ty, TyBuilder, TyKind, - db::HirDatabase, infer::unify::InferenceTable, next_solver::mapping::ChalkToNextSolver, + TraitEnvironment, + db::HirDatabase, + infer::unify::InferenceTable, + next_solver::{ + Ty, TyKind, + infer::traits::{ObligationCause, PredicateObligations}, + mapping::{ChalkToNextSolver, NextSolverToChalk}, + obligation_ctxt::ObligationCtxt, + }, }; const AUTODEREF_RECURSION_LIMIT: usize = 20; -#[derive(Debug)] -pub(crate) enum AutoderefKind { - Builtin, - Overloaded, -} - /// Returns types that `ty` transitively dereferences to. This function is only meant to be used /// outside `hir-ty`. /// @@ -34,16 +35,17 @@ pub(crate) enum AutoderefKind { pub fn autoderef( db: &dyn HirDatabase, env: Arc, - ty: Canonical, -) -> impl Iterator { + ty: crate::Canonical, +) -> impl Iterator { let mut table = InferenceTable::new(db, env); + let interner = table.interner; let ty = table.instantiate_canonical(ty); - let mut autoderef = Autoderef::new_no_tracking(&mut table, ty, false, false); + let mut autoderef = Autoderef::new_no_tracking(&mut table, ty.to_nextsolver(interner)); let mut v = Vec::new(); while let Some((ty, _steps)) = autoderef.next() { // `ty` may contain unresolved inference variables. Since there's no chance they would be // resolved, just replace with fallback type. - let resolved = autoderef.table.resolve_completely(ty); + let resolved = autoderef.table.resolve_completely(ty.to_chalk(interner)); // If the deref chain contains a cycle (e.g. `A` derefs to `B` and `B` derefs to `A`), we // would revisit some already visited types. Stop here to avoid duplication. @@ -59,178 +61,267 @@ pub fn autoderef( v.into_iter() } -trait TrackAutoderefSteps { +pub(crate) trait TrackAutoderefSteps<'db>: Default + fmt::Debug { fn len(&self) -> usize; - fn push(&mut self, kind: AutoderefKind, ty: &Ty); + fn push(&mut self, ty: Ty<'db>, kind: AutoderefKind); } -impl TrackAutoderefSteps for usize { +impl<'db> TrackAutoderefSteps<'db> for usize { fn len(&self) -> usize { *self } - fn push(&mut self, _: AutoderefKind, _: &Ty) { + fn push(&mut self, _: Ty<'db>, _: AutoderefKind) { *self += 1; } } -impl TrackAutoderefSteps for Vec<(AutoderefKind, Ty)> { +impl<'db> TrackAutoderefSteps<'db> for Vec<(Ty<'db>, AutoderefKind)> { fn len(&self) -> usize { self.len() } - fn push(&mut self, kind: AutoderefKind, ty: &Ty) { - self.push((kind, ty.clone())); + fn push(&mut self, ty: Ty<'db>, kind: AutoderefKind) { + self.push((ty, kind)); } } -#[derive(Debug)] -pub(crate) struct Autoderef<'table, 'db, T = Vec<(AutoderefKind, Ty)>> { - pub(crate) table: &'table mut InferenceTable<'db>, - ty: Ty, +#[derive(Copy, Clone, Debug)] +pub(crate) enum AutoderefKind { + /// A true pointer type, such as `&T` and `*mut T`. + Builtin, + /// A type which must dispatch to a `Deref` implementation. + Overloaded, +} + +struct AutoderefSnapshot<'db, Steps> { at_start: bool, - steps: T, - explicit: bool, + reached_recursion_limit: bool, + steps: Steps, + cur_ty: Ty<'db>, + obligations: PredicateObligations<'db>, +} + +#[derive(Clone, Copy)] +struct AutoderefTraits { + trait_target: TypeAliasId, +} + +/// Recursively dereference a type, considering both built-in +/// dereferences (`*`) and the `Deref` trait. +/// Although called `Autoderef` it can be configured to use the +/// `Receiver` trait instead of the `Deref` trait. +pub(crate) struct Autoderef<'a, 'db, Steps = Vec<(Ty<'db>, AutoderefKind)>> { + // Meta infos: + pub(crate) table: &'a mut InferenceTable<'db>, + traits: Option, + + // Current state: + state: AutoderefSnapshot<'db, Steps>, + + // Configurations: + include_raw_pointers: bool, use_receiver_trait: bool, } -impl<'table, 'db> Autoderef<'table, 'db> { - pub(crate) fn new( - table: &'table mut InferenceTable<'db>, - ty: Ty, - explicit: bool, - use_receiver_trait: bool, - ) -> Self { - let ty = table.structurally_resolve_type(&ty); - Autoderef { table, ty, at_start: true, steps: Vec::new(), explicit, use_receiver_trait } - } +impl<'a, 'db, Steps: TrackAutoderefSteps<'db>> Iterator for Autoderef<'a, 'db, Steps> { + type Item = (Ty<'db>, usize); - pub(crate) fn steps(&self) -> &[(AutoderefKind, Ty)] { - &self.steps - } -} - -impl<'table, 'db> Autoderef<'table, 'db, usize> { - pub(crate) fn new_no_tracking( - table: &'table mut InferenceTable<'db>, - ty: Ty, - explicit: bool, - use_receiver_trait: bool, - ) -> Self { - let ty = table.structurally_resolve_type(&ty); - Autoderef { table, ty, at_start: true, steps: 0, explicit, use_receiver_trait } - } -} - -#[allow(private_bounds)] -impl Autoderef<'_, '_, T> { - pub(crate) fn step_count(&self) -> usize { - self.steps.len() - } - - pub(crate) fn final_ty(&self) -> Ty { - self.ty.clone() - } -} - -impl Iterator for Autoderef<'_, '_, T> { - type Item = (Ty, usize); - - #[tracing::instrument(skip_all)] fn next(&mut self) -> Option { - if mem::take(&mut self.at_start) { - return Some((self.ty.clone(), 0)); + debug!("autoderef: steps={:?}, cur_ty={:?}", self.state.steps, self.state.cur_ty); + if self.state.at_start { + self.state.at_start = false; + debug!("autoderef stage #0 is {:?}", self.state.cur_ty); + return Some((self.state.cur_ty, 0)); } - if self.steps.len() > AUTODEREF_RECURSION_LIMIT { + // If we have reached the recursion limit, error gracefully. + if self.state.steps.len() >= AUTODEREF_RECURSION_LIMIT { + self.state.reached_recursion_limit = true; return None; } - let (kind, new_ty) = - autoderef_step(self.table, self.ty.clone(), self.explicit, self.use_receiver_trait)?; - - self.steps.push(kind, &self.ty); - self.ty = new_ty; - - Some((self.ty.clone(), self.step_count())) - } -} - -pub(crate) fn autoderef_step( - table: &mut InferenceTable<'_>, - ty: Ty, - explicit: bool, - use_receiver_trait: bool, -) -> Option<(AutoderefKind, Ty)> { - if let Some(derefed) = builtin_deref(table.db, &ty, explicit) { - Some((AutoderefKind::Builtin, table.structurally_resolve_type(derefed))) - } else { - Some((AutoderefKind::Overloaded, deref_by_trait(table, ty, use_receiver_trait)?)) - } -} - -pub(crate) fn builtin_deref<'ty>( - db: &dyn HirDatabase, - ty: &'ty Ty, - explicit: bool, -) -> Option<&'ty Ty> { - match ty.kind(Interner) { - TyKind::Ref(.., ty) => Some(ty), - TyKind::Raw(.., ty) if explicit => Some(ty), - &TyKind::Adt(chalk_ir::AdtId(adt), ref substs) if crate::lang_items::is_box(db, adt) => { - substs.at(Interner, 0).ty(Interner) + if self.state.cur_ty.is_ty_var() { + return None; } - _ => None, - } -} -pub(crate) fn deref_by_trait( - table @ &mut InferenceTable { db, .. }: &mut InferenceTable<'_>, - ty: Ty, - use_receiver_trait: bool, -) -> Option { - let _p = tracing::info_span!("deref_by_trait").entered(); - if table.structurally_resolve_type(&ty).inference_var(Interner).is_some() { - // don't try to deref unknown variables - return None; - } - - let trait_id = || { - // FIXME: Remove the `false` once `Receiver` needs to be stabilized, doing so will - // effectively bump the MSRV of rust-analyzer to 1.84 due to 1.83 and below lacking the - // blanked impl on `Deref`. - #[expect(clippy::overly_complex_bool_expr)] - if use_receiver_trait - && false - && let Some(receiver) = LangItem::Receiver.resolve_trait(db, table.trait_env.krate) + // Otherwise, deref if type is derefable: + // NOTE: in the case of self.use_receiver_trait = true, you might think it would + // be better to skip this clause and use the Overloaded case only, since &T + // and &mut T implement Receiver. But built-in derefs apply equally to Receiver + // and Deref, and this has benefits for const and the emitted MIR. + let (kind, new_ty) = if let Some(ty) = + self.state.cur_ty.builtin_deref(self.table.db, self.include_raw_pointers) { - return Some(receiver); - } - // Old rustc versions might not have `Receiver` trait. - // Fallback to `Deref` if they don't - LangItem::Deref.resolve_trait(db, table.trait_env.krate) - }; - let trait_id = trait_id()?; - let target = - trait_id.trait_items(db).associated_type_by_name(&Name::new_symbol_root(sym::Target))?; - - let projection = { - let b = TyBuilder::subst_for_def(db, trait_id, None); - if b.remaining() != 1 { - // the Target type + Deref trait should only have one generic parameter, - // namely Deref's Self type + debug_assert_eq!(ty, self.table.infer_ctxt.resolve_vars_if_possible(ty)); + // NOTE: we may still need to normalize the built-in deref in case + // we have some type like `&::Assoc`, since users of + // autoderef expect this type to have been structurally normalized. + if let TyKind::Alias(..) = ty.kind() { + let (normalized_ty, obligations) = structurally_normalize_ty(self.table, ty)?; + self.state.obligations.extend(obligations); + (AutoderefKind::Builtin, normalized_ty) + } else { + (AutoderefKind::Builtin, ty) + } + } else if let Some(ty) = self.overloaded_deref_ty(self.state.cur_ty) { + // The overloaded deref check already normalizes the pointee type. + (AutoderefKind::Overloaded, ty) + } else { return None; - } - let deref_subst = b.push(ty).build(); - TyBuilder::assoc_type_projection(db, target, Some(deref_subst)).build() - }; + }; - // Check that the type implements Deref at all - let trait_ref = projection.trait_ref(db); - let implements_goal: Goal = trait_ref.cast(Interner); - if table.try_obligation(implements_goal.clone()).no_solution() { - return None; + self.state.steps.push(self.state.cur_ty, kind); + debug!( + "autoderef stage #{:?} is {:?} from {:?}", + self.step_count(), + new_ty, + (self.state.cur_ty, kind) + ); + self.state.cur_ty = new_ty; + + Some((self.state.cur_ty, self.step_count())) + } +} + +impl<'a, 'db> Autoderef<'a, 'db> { + pub(crate) fn new(table: &'a mut InferenceTable<'db>, base_ty: Ty<'db>) -> Self { + Self::new_impl(table, base_ty) + } +} + +impl<'a, 'db> Autoderef<'a, 'db, usize> { + pub(crate) fn new_no_tracking(table: &'a mut InferenceTable<'db>, base_ty: Ty<'db>) -> Self { + Self::new_impl(table, base_ty) + } +} + +impl<'a, 'db, Steps: TrackAutoderefSteps<'db>> Autoderef<'a, 'db, Steps> { + fn new_impl(table: &'a mut InferenceTable<'db>, base_ty: Ty<'db>) -> Self { + Autoderef { + state: AutoderefSnapshot { + steps: Steps::default(), + cur_ty: table.infer_ctxt.resolve_vars_if_possible(base_ty), + obligations: PredicateObligations::new(), + at_start: true, + reached_recursion_limit: false, + }, + table, + traits: None, + include_raw_pointers: false, + use_receiver_trait: false, + } } - table.register_obligation(implements_goal.to_nextsolver(table.interner)); + fn autoderef_traits(&mut self) -> Option { + match &mut self.traits { + Some(it) => Some(*it), + None => { + let traits = if self.use_receiver_trait { + AutoderefTraits { + trait_target: LangItem::ReceiverTarget + .resolve_type_alias(self.table.db, self.table.trait_env.krate) + .or_else(|| { + LangItem::DerefTarget + .resolve_type_alias(self.table.db, self.table.trait_env.krate) + })?, + } + } else { + AutoderefTraits { + trait_target: LangItem::DerefTarget + .resolve_type_alias(self.table.db, self.table.trait_env.krate)?, + } + }; + Some(*self.traits.insert(traits)) + } + } + } - let result = table.normalize_projection_ty(projection); - Some(table.structurally_resolve_type(&result)) + fn overloaded_deref_ty(&mut self, ty: Ty<'db>) -> Option> { + debug!("overloaded_deref_ty({:?})", ty); + let interner = self.table.interner; + + // , or whatever the equivalent trait is that we've been asked to walk. + let AutoderefTraits { trait_target } = self.autoderef_traits()?; + + let (normalized_ty, obligations) = structurally_normalize_ty( + self.table, + Ty::new_projection(interner, trait_target.into(), [ty]), + )?; + debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations); + self.state.obligations.extend(obligations); + + Some(self.table.infer_ctxt.resolve_vars_if_possible(normalized_ty)) + } + + /// Returns the final type we ended up with, which may be an unresolved + /// inference variable. + pub(crate) fn final_ty(&self) -> Ty<'db> { + self.state.cur_ty + } + + pub(crate) fn step_count(&self) -> usize { + self.state.steps.len() + } + + pub(crate) fn take_obligations(&mut self) -> PredicateObligations<'db> { + std::mem::take(&mut self.state.obligations) + } + + pub(crate) fn steps(&self) -> &Steps { + &self.state.steps + } + + #[expect(dead_code)] + pub(crate) fn reached_recursion_limit(&self) -> bool { + self.state.reached_recursion_limit + } + + /// also dereference through raw pointer types + /// e.g., assuming ptr_to_Foo is the type `*const Foo` + /// fcx.autoderef(span, ptr_to_Foo) => [*const Foo] + /// fcx.autoderef(span, ptr_to_Foo).include_raw_ptrs() => [*const Foo, Foo] + pub(crate) fn include_raw_pointers(mut self) -> Self { + self.include_raw_pointers = true; + self + } + + /// Use `core::ops::Receiver` and `core::ops::Receiver::Target` as + /// the trait and associated type to iterate, instead of + /// `core::ops::Deref` and `core::ops::Deref::Target` + pub(crate) fn use_receiver_trait(mut self) -> Self { + self.use_receiver_trait = true; + self + } +} + +fn structurally_normalize_ty<'db>( + table: &InferenceTable<'db>, + ty: Ty<'db>, +) -> Option<(Ty<'db>, PredicateObligations<'db>)> { + let mut ocx = ObligationCtxt::new(&table.infer_ctxt); + let Ok(normalized_ty) = + ocx.structurally_normalize_ty(&ObligationCause::misc(), table.param_env, ty) + else { + // We shouldn't have errors here in the old solver, except for + // evaluate/fulfill mismatches, but that's not a reason for an ICE. + return None; + }; + let errors = ocx.select_where_possible(); + if !errors.is_empty() { + unreachable!(); + } + + Some((normalized_ty, ocx.into_pending_obligations())) +} + +pub(crate) fn overloaded_deref_ty<'db>( + table: &InferenceTable<'db>, + ty: Ty<'db>, +) -> Option>> { + let interner = table.interner; + + let trait_target = LangItem::DerefTarget.resolve_type_alias(table.db, table.trait_env.krate)?; + + let (normalized_ty, obligations) = + structurally_normalize_ty(table, Ty::new_projection(interner, trait_target.into(), [ty]))?; + + Some(InferOk { value: normalized_ty, obligations }) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs index 8af8fb73f344..3755175cf516 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs @@ -1,16 +1,12 @@ //! `TyBuilder`, a helper for building instances of `Ty` and related types. -use std::iter; - use chalk_ir::{ AdtId, DebruijnIndex, Scalar, cast::{Cast, CastTo, Caster}, fold::TypeFoldable, interner::HasInterner, }; -use hir_def::{ - DefWithBodyId, GenericDefId, GenericParamId, TraitId, TypeAliasId, builtin_type::BuiltinType, -}; +use hir_def::{GenericDefId, GenericParamId, TraitId, TypeAliasId, builtin_type::BuiltinType}; use smallvec::SmallVec; use crate::{ @@ -246,47 +242,6 @@ impl TyBuilder<()> { TyBuilder::new((), params, parent_subst) } - /// Creates a `TyBuilder` to build `Substitution` for a coroutine defined in `parent`. - /// - /// A coroutine's substitution consists of: - /// - resume type of coroutine - /// - yield type of coroutine ([`Coroutine::Yield`](std::ops::Coroutine::Yield)) - /// - return type of coroutine ([`Coroutine::Return`](std::ops::Coroutine::Return)) - /// - generic parameters in scope on `parent` - /// - /// in this order. - /// - /// This method prepopulates the builder with placeholder substitution of `parent`, so you - /// should only push exactly 3 `GenericArg`s before building. - pub fn subst_for_coroutine(db: &dyn HirDatabase, parent: DefWithBodyId) -> TyBuilder<()> { - let parent_subst = - parent.as_generic_def_id(db).map(|p| generics(db, p).placeholder_subst(db)); - // These represent resume type, yield type, and return type of coroutine. - let params = std::iter::repeat_n(ParamKind::Type, 3).collect(); - TyBuilder::new((), params, parent_subst) - } - - pub fn subst_for_closure( - db: &dyn HirDatabase, - parent: DefWithBodyId, - sig_ty: Ty, - ) -> Substitution { - let sig_ty = sig_ty.cast(Interner); - let self_subst = iter::once(&sig_ty); - let Some(parent) = parent.as_generic_def_id(db) else { - return Substitution::from_iter(Interner, self_subst); - }; - Substitution::from_iter( - Interner, - generics(db, parent) - .placeholder_subst(db) - .iter(Interner) - .chain(self_subst) - .cloned() - .collect::>(), - ) - } - pub fn build(self) -> Substitution { let ((), subst) = self.build_internal(); subst diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs index 9f0ea14a8063..1faf9f66dc54 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs @@ -210,7 +210,7 @@ impl TyExt for Ty { match self.kind(Interner) { TyKind::Function(fn_ptr) => Some(CallableSig::from_fn_ptr(fn_ptr)), TyKind::FnDef(def, parameters) => Some(CallableSig::from_def(db, *def, parameters)), - TyKind::Closure(.., substs) => ClosureSubst(substs).sig_ty().callable_sig(db), + TyKind::Closure(.., substs) => ClosureSubst(substs).sig_ty(db).callable_sig(db), _ => None, } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 0aec2b9dec7b..448fc4aede03 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -283,6 +283,14 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { def: TyDefId, ) -> crate::next_solver::EarlyBinder<'db, crate::next_solver::Ty<'db>>; + /// Returns the type of the value of the given constant, or `None` if the `ValueTyDefId` is + /// a `StructId` or `EnumVariantId` with a record constructor. + #[salsa::invoke(crate::lower_nextsolver::value_ty_query)] + fn value_ty_ns<'db>( + &'db self, + def: ValueTyDefId, + ) -> Option>>; + #[salsa::invoke(crate::lower_nextsolver::type_for_type_alias_with_diagnostics_query)] #[salsa::cycle(cycle_result = crate::lower_nextsolver::type_for_type_alias_with_diagnostics_cycle_result)] fn type_for_type_alias_with_diagnostics_ns<'db>( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs index 64c4cdeaddf3..3f04b72c2fc6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs @@ -14,6 +14,7 @@ use hir_def::{ }; use span::Edition; +use crate::utils::TargetFeatureIsSafeInTarget; use crate::{ InferenceResult, Interner, TargetFeatures, TyExt, TyKind, db::HirDatabase, @@ -147,7 +148,7 @@ struct UnsafeVisitor<'db> { edition: Edition, /// On some targets (WASM), calling safe functions with `#[target_feature]` is always safe, even when /// the target feature is not enabled. This flag encodes that. - target_feature_is_safe: bool, + target_feature_is_safe: TargetFeatureIsSafeInTarget, } impl<'db> UnsafeVisitor<'db> { @@ -167,7 +168,7 @@ impl<'db> UnsafeVisitor<'db> { let edition = krate.data(db).edition; let target_feature_is_safe = match &krate.workspace_data(db).target { Ok(target) => target_feature_is_safe_in_target(target), - Err(_) => false, + Err(_) => TargetFeatureIsSafeInTarget::No, }; Self { db, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index ea8bdf8bcbae..7418e3bbeb49 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -38,14 +38,16 @@ use rustc_apfloat::{ }; use rustc_hash::FxHashSet; use rustc_type_ir::{ - AliasTyKind, RegionKind, - inherent::{AdtDef, IntoKind, SliceLike}, + AliasTyKind, CoroutineArgsParts, RegionKind, + inherent::{AdtDef, GenericArgs as _, IntoKind, SliceLike}, }; use smallvec::SmallVec; use span::Edition; use stdx::never; use triomphe::Arc; +use crate::next_solver::infer::DbInternerInferExt; +use crate::next_solver::infer::traits::ObligationCause; use crate::{ AliasEq, AliasTy, Binders, CallableDefId, CallableSig, ConcreteConst, Const, ConstScalar, ConstValue, DomainGoal, FnAbi, GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData, @@ -789,11 +791,11 @@ fn render_const_scalar_ns( ) -> Result<(), HirDisplayError> { let trait_env = TraitEnvironment::empty(f.krate()); let interner = DbInterner::new_with(f.db, Some(trait_env.krate), trait_env.block); - let ty = crate::next_solver::project::solve_normalize::normalize( - interner, - trait_env.env.to_nextsolver(interner), - ty, - ); + let infcx = interner.infer_ctxt().build(rustc_type_ir::TypingMode::PostAnalysis); + let ty = infcx + .at(&ObligationCause::new(), trait_env.env.to_nextsolver(interner)) + .deeply_normalize(ty) + .unwrap_or(ty); render_const_scalar_inner(f, b, memory_map, ty, trait_env) } @@ -1556,7 +1558,7 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { } _ => (), } - let sig = ClosureSubst(&substs).sig_ty().callable_sig(db); + let sig = ClosureSubst(&substs).sig_ty(db).callable_sig(db); if let Some(sig) = sig { let InternedClosure(def, _) = db.lookup_intern_closure(id); let infer = db.infer(def); @@ -1696,26 +1698,17 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { DisplaySourceCodeError::Coroutine, )); } - let subst = convert_args_for_result(interner, subst.as_slice()); - let subst = subst.as_slice(Interner); - let a: Option> = subst - .get(subst.len() - 3..) - .and_then(|args| args.iter().map(|arg| arg.ty(Interner)).collect()); + let CoroutineArgsParts { resume_ty, yield_ty, return_ty, .. } = + subst.split_coroutine_args(); + write!(f, "|")?; + resume_ty.hir_fmt(f)?; + write!(f, "|")?; - if let Some([resume_ty, yield_ty, ret_ty]) = a.as_deref() { - write!(f, "|")?; - resume_ty.hir_fmt(f)?; - write!(f, "|")?; + write!(f, " yields ")?; + yield_ty.hir_fmt(f)?; - write!(f, " yields ")?; - yield_ty.hir_fmt(f)?; - - write!(f, " -> ")?; - ret_ty.hir_fmt(f)?; - } else { - // This *should* be unreachable, but fallback just in case. - write!(f, "{{coroutine}}")?; - } + write!(f, " -> ")?; + return_ty.hir_fmt(f)?; } TyKind::CoroutineWitness(..) => write!(f, "{{coroutine witness}}")?, TyKind::Pat(_, _) => write!(f, "{{pat}}")?, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs b/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs index a7e942d92442..f5c2f41069ea 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs @@ -120,7 +120,7 @@ pub(crate) fn has_drop_glue(db: &dyn HirDatabase, ty: Ty, env: Arc Arc { @@ -159,7 +164,7 @@ pub(crate) fn normalize(db: &dyn HirDatabase, trait_env: Arc, let mut table = unify::InferenceTable::new(db, trait_env); let ty_with_vars = table.normalize_associated_types_in(ty); - table.resolve_obligations_as_possible(); + table.select_obligations_where_possible(); table.propagate_diverging_flag(); table.resolve_completely(ty_with_vars) } @@ -183,18 +188,14 @@ impl BindingMode { } } +// FIXME: Remove this `InferOk`, switch all code to the second one, that uses `Obligation` instead of `Goal`. #[derive(Debug)] pub(crate) struct InferOk<'db, T> { + #[allow(dead_code)] value: T, goals: Vec>>, } -impl<'db, T> InferOk<'db, T> { - fn map(self, f: impl FnOnce(T) -> U) -> InferOk<'db, U> { - InferOk { value: f(self.value), goals: self.goals } - } -} - #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum InferenceTyDiagnosticSource { /// Diagnostics that come from types in the body. @@ -378,6 +379,26 @@ impl Adjustment { } } +/// At least for initial deployment, we want to limit two-phase borrows to +/// only a few specific cases. Right now, those are mostly "things that desugar" +/// into method calls: +/// - using `x.some_method()` syntax, where some_method takes `&mut self`, +/// - using `Foo::some_method(&mut x, ...)` syntax, +/// - binary assignment operators (`+=`, `-=`, `*=`, etc.). +/// +/// Anything else should be rejected until generalized two-phase borrow support +/// is implemented. Right now, dataflow can't handle the general case where there +/// is more than one use of a mutable borrow, and we don't want to accept too much +/// new code via two-phase borrows, so we try to limit where we create two-phase +/// capable mutable borrows. +/// See #49434 for tracking. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub(crate) enum AllowTwoPhase { + // FIXME: We should use this when appropriate. + Yes, + No, +} + #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum Adjust { /// Go from ! to any type. @@ -393,8 +414,6 @@ pub enum Adjust { /// call, with the signature `&'a T -> &'a U` or `&'a mut T -> &'a mut U`. /// The target type is `U` in both cases, with the region and mutability /// being those shared by both the receiver and the returned reference. -/// -/// Mutability is `None` when we are not sure. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct OverloadedDeref(pub Option); @@ -656,6 +675,7 @@ pub(crate) struct InferenceContext<'db> { /// Generally you should not resolve things via this resolver. Instead create a TyLoweringContext /// and resolve the path via its methods. This will ensure proper error reporting. pub(crate) resolver: Resolver<'db>, + target_features: OnceCell<(TargetFeatures, TargetFeatureIsSafeInTarget)>, generic_def: GenericDefId, generics: OnceCell, table: unify::InferenceTable<'db>, @@ -673,11 +693,11 @@ pub(crate) struct InferenceContext<'db> { /// If `Some`, this stores coercion information for returned /// expressions. If `None`, this is in a context where return is /// inappropriate, such as a const expression. - return_coercion: Option, + return_coercion: Option>, /// The resume type and the yield type, respectively, of the coroutine being inferred. resume_yield_tys: Option<(Ty, Ty)>, diverges: Diverges, - breakables: Vec, + breakables: Vec>, /// Whether we are inside the pattern of a destructuring assignment. inside_assignment: bool, @@ -692,21 +712,21 @@ pub(crate) struct InferenceContext<'db> { /// We do that because sometimes we truncate projections (when a closure captures /// both `a.b` and `a.b.c`), and we want to provide accurate spans in this case. current_capture_span_stack: Vec, - current_closure: Option, + current_closure: Option, /// Stores the list of closure ids that need to be analyzed before this closure. See the /// comment on `InferenceContext::sort_closures` - closure_dependencies: FxHashMap>, - deferred_closures: FxHashMap, ExprId)>>, + closure_dependencies: FxHashMap>, + deferred_closures: FxHashMap, ExprId)>>, diagnostics: Diagnostics, } #[derive(Clone, Debug)] -struct BreakableContext { +struct BreakableContext<'db> { /// Whether this context contains at least one break expression. may_break: bool, /// The coercion target of the context. - coerce: Option, + coerce: Option>, /// The optional label of the context. label: Option, kind: BreakableKind, @@ -721,10 +741,10 @@ enum BreakableKind { Border, } -fn find_breakable( - ctxs: &mut [BreakableContext], +fn find_breakable<'a, 'db>( + ctxs: &'a mut [BreakableContext<'db>], label: Option, -) -> Option<&mut BreakableContext> { +) -> Option<&'a mut BreakableContext<'db>> { let mut ctxs = ctxs .iter_mut() .rev() @@ -735,10 +755,10 @@ fn find_breakable( } } -fn find_continuable( - ctxs: &mut [BreakableContext], +fn find_continuable<'a, 'db>( + ctxs: &'a mut [BreakableContext<'db>], label: Option, -) -> Option<&mut BreakableContext> { +) -> Option<&'a mut BreakableContext<'db>> { match label { Some(_) => find_breakable(ctxs, label).filter(|it| matches!(it.kind, BreakableKind::Loop)), None => find_breakable(ctxs, label), @@ -759,6 +779,7 @@ impl<'db> InferenceContext<'db> { ) -> Self { let trait_env = db.trait_environment_for_body(owner); InferenceContext { + target_features: OnceCell::new(), generics: OnceCell::new(), result: InferenceResult::default(), table: unify::InferenceTable::new(db, trait_env), @@ -794,18 +815,56 @@ impl<'db> InferenceContext<'db> { self.generics.get_or_init(|| crate::generics::generics(self.db, self.generic_def)) } + #[inline] + fn krate(&self) -> Crate { + self.resolver.krate() + } + + fn target_features<'a>( + db: &dyn HirDatabase, + target_features: &'a OnceCell<(TargetFeatures, TargetFeatureIsSafeInTarget)>, + owner: DefWithBodyId, + krate: Crate, + ) -> (&'a TargetFeatures, TargetFeatureIsSafeInTarget) { + let (target_features, target_feature_is_safe) = target_features.get_or_init(|| { + let target_features = match owner { + DefWithBodyId::FunctionId(id) => TargetFeatures::from_attrs(&db.attrs(id.into())), + _ => TargetFeatures::default(), + }; + let target_feature_is_safe = match &krate.workspace_data(db).target { + Ok(target) => crate::utils::target_feature_is_safe_in_target(target), + Err(_) => TargetFeatureIsSafeInTarget::No, + }; + (target_features, target_feature_is_safe) + }); + (target_features, *target_feature_is_safe) + } + + #[inline] + pub(crate) fn set_tainted_by_errors(&mut self) { + self.result.has_errors = true; + } + // FIXME: This function should be private in module. It is currently only used in the consteval, since we need // `InferenceResult` in the middle of inference. See the fixme comment in `consteval::eval_to_const`. If you // used this function for another workaround, mention it here. If you really need this function and believe that // there is no problem in it being `pub(crate)`, remove this comment. - pub(crate) fn resolve_all(self) -> InferenceResult { + pub(crate) fn resolve_all(mut self) -> InferenceResult { + self.table.select_obligations_where_possible(); + self.table.fallback_if_possible(); + + // Comment from rustc: + // Even though coercion casts provide type hints, we check casts after fallback for + // backwards compatibility. This makes fallback a stronger type hint than a cast coercion. + let cast_checks = std::mem::take(&mut self.deferred_cast_checks); + for mut cast in cast_checks.into_iter() { + if let Err(diag) = cast.check(&mut self) { + self.diagnostics.push(diag); + } + } + let InferenceContext { - mut table, - mut result, - mut deferred_cast_checks, - tuple_field_accesses_rev, - diagnostics, - .. + mut table, mut result, tuple_field_accesses_rev, diagnostics, .. } = self; let mut diagnostics = diagnostics.finish(); // Destructure every single field so whenever new fields are added to `InferenceResult` we @@ -831,31 +890,12 @@ impl<'db> InferenceContext<'db> { closure_info: _, mutated_bindings_in_closure: _, tuple_field_access_types: _, - coercion_casts, + coercion_casts: _, diagnostics: _, } = &mut result; - table.resolve_obligations_as_possible(); - table.fallback_if_possible(); - - // Comment from rustc: - // Even though coercion casts provide type hints, we check casts after fallback for - // backwards compatibility. This makes fallback a stronger type hint than a cast coercion. - let mut apply_adjustments = |expr, adj: Vec<_>| { - expr_adjustments.insert(expr, adj.into_boxed_slice()); - }; - let mut set_coercion_cast = |expr| { - coercion_casts.insert(expr); - }; - for cast in deferred_cast_checks.iter_mut() { - if let Err(diag) = - cast.check(&mut table, &mut apply_adjustments, &mut set_coercion_cast) - { - diagnostics.push(diag); - } - } // FIXME resolve obligations as well (use Guidance if necessary) - table.resolve_obligations_as_possible(); + table.select_obligations_where_possible(); // make sure diverging type variables are marked as such table.propagate_diverging_flag(); @@ -1081,7 +1121,8 @@ impl<'db> InferenceContext<'db> { }; self.return_ty = self.process_user_written_ty(return_ty); - self.return_coercion = Some(CoerceMany::new(self.return_ty.clone())); + self.return_coercion = + Some(CoerceMany::new(self.return_ty.to_nextsolver(self.table.interner))); // Functions might be defining usage sites of TAITs. // To define an TAITs, that TAIT must appear in the function's signatures. @@ -1117,8 +1158,12 @@ impl<'db> InferenceContext<'db> { fold_tys( t, |ty, _| { + let ty = self.table.structurally_resolve_type(&ty); let opaque_ty_id = match ty.kind(Interner) { - TyKind::OpaqueType(opaque_ty_id, _) => *opaque_ty_id, + TyKind::OpaqueType(opaque_ty_id, _) + | TyKind::Alias(AliasTy::Opaque(crate::OpaqueTy { opaque_ty_id, .. })) => { + *opaque_ty_id + } _ => return ty, }; let (impl_traits, idx) = @@ -1214,9 +1259,11 @@ impl<'db> InferenceContext<'db> { ty: &chalk_ir::Ty, outer_binder: DebruijnIndex, ) -> std::ops::ControlFlow { - let ty = self.table.resolve_ty_shallow(ty); + let ty = self.table.structurally_resolve_type(ty); - if let TyKind::OpaqueType(id, _) = ty.kind(Interner) + if let TyKind::OpaqueType(id, _) + | TyKind::Alias(AliasTy::Opaque(crate::OpaqueTy { opaque_ty_id: id, .. })) = + ty.kind(Interner) && let ImplTraitId::TypeAliasImplTrait(alias_id, _) = self.db.lookup_intern_impl_trait_id((*id).into()) { @@ -1361,6 +1408,13 @@ impl<'db> InferenceContext<'db> { } } + fn write_pat_adj(&mut self, pat: PatId, adjustments: Box<[Ty]>) { + if adjustments.is_empty() { + return; + } + self.result.pat_adjustments.entry(pat).or_default().extend(adjustments); + } + fn write_method_resolution(&mut self, expr: ExprId, func: FunctionId, subst: Substitution) { self.result.method_resolutions.insert(expr, (func, subst)); } @@ -1587,24 +1641,14 @@ impl<'db> InferenceContext<'db> { self.table.process_remote_user_written_ty(ty) } - /// Recurses through the given type, normalizing associated types mentioned - /// in it by replacing them by type variables and registering obligations to - /// resolve later. This should be done once for every type we get from some - /// type annotation (e.g. from a let type annotation, field type or function - /// call). `make_ty` handles this already, but e.g. for field types we need - /// to do it as well. - fn normalize_associated_types_in(&mut self, ty: T) -> T - where - T: HasInterner + TypeFoldable + ChalkToNextSolver<'db, U>, - U: NextSolverToChalk<'db, T> + rustc_type_ir::TypeFoldable>, - { - self.table.normalize_associated_types_in(ty) - } - fn resolve_ty_shallow(&mut self, ty: &Ty) -> Ty { self.table.resolve_ty_shallow(ty) } + fn shallow_resolve(&self, ty: crate::next_solver::Ty<'db>) -> crate::next_solver::Ty<'db> { + self.table.shallow_resolve(ty) + } + fn resolve_associated_type(&mut self, inner_ty: Ty, assoc_ty: Option) -> Ty { self.resolve_associated_type_with_params(inner_ty, assoc_ty, &[]) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/autoderef.rs new file mode 100644 index 000000000000..77b1ae6a94a4 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/autoderef.rs @@ -0,0 +1,54 @@ +//! Autoderef helpers for inference. + +use std::iter; + +use crate::{ + Adjust, Adjustment, OverloadedDeref, + autoderef::{Autoderef, AutoderefKind}, + infer::unify::InferenceTable, + next_solver::{ + Ty, + infer::{InferOk, traits::PredicateObligations}, + mapping::NextSolverToChalk, + }, +}; + +impl<'db> InferenceTable<'db> { + pub(crate) fn autoderef(&mut self, base_ty: Ty<'db>) -> Autoderef<'_, 'db> { + Autoderef::new(self, base_ty) + } +} + +impl<'db> Autoderef<'_, 'db> { + /// Returns the adjustment steps. + pub(crate) fn adjust_steps(mut self) -> Vec { + let infer_ok = self.adjust_steps_as_infer_ok(); + self.table.register_infer_ok(infer_ok) + } + + pub(crate) fn adjust_steps_as_infer_ok(&mut self) -> InferOk<'db, Vec> { + let steps = self.steps(); + if steps.is_empty() { + return InferOk { obligations: PredicateObligations::new(), value: vec![] }; + } + + let targets = steps.iter().skip(1).map(|&(ty, _)| ty).chain(iter::once(self.final_ty())); + let steps: Vec<_> = steps + .iter() + .map(|&(_source, kind)| { + if let AutoderefKind::Overloaded = kind { + Some(OverloadedDeref(Some(chalk_ir::Mutability::Not))) + } else { + None + } + }) + .zip(targets) + .map(|(autoderef, target)| Adjustment { + kind: Adjust::Deref(autoderef), + target: target.to_chalk(self.table.interner), + }) + .collect(); + + InferOk { obligations: self.take_obligations(), value: steps } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs index bc3ee3c4c54d..4cd6144a14cb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs @@ -4,12 +4,14 @@ use chalk_ir::{Mutability, Scalar, TyVariableKind, UintTy}; use hir_def::{AdtId, hir::ExprId, signatures::TraitFlags}; use stdx::never; +use crate::infer::coerce::CoerceNever; use crate::{ - Adjustment, Binders, DynTy, InferenceDiagnostic, Interner, PlaceholderIndex, - QuantifiedWhereClauses, Ty, TyExt, TyKind, TypeFlags, WhereClause, + Binders, DynTy, InferenceDiagnostic, Interner, PlaceholderIndex, QuantifiedWhereClauses, Ty, + TyExt, TyKind, TypeFlags, WhereClause, db::HirDatabase, from_chalk_trait_id, - infer::{coerce::CoerceNever, unify::InferenceTable}, + infer::{AllowTwoPhase, InferenceContext}, + next_solver::mapping::ChalkToNextSolver, }; #[derive(Debug)] @@ -93,23 +95,25 @@ impl CastCheck { Self { expr, source_expr, expr_ty, cast_ty } } - pub(super) fn check( + pub(super) fn check( &mut self, - table: &mut InferenceTable<'_>, - apply_adjustments: &mut F, - set_coercion_cast: &mut G, - ) -> Result<(), InferenceDiagnostic> - where - F: FnMut(ExprId, Vec), - G: FnMut(ExprId), - { - self.expr_ty = table.eagerly_normalize_and_resolve_shallow_in(self.expr_ty.clone()); - self.cast_ty = table.eagerly_normalize_and_resolve_shallow_in(self.cast_ty.clone()); + ctx: &mut InferenceContext<'_>, + ) -> Result<(), InferenceDiagnostic> { + self.expr_ty = ctx.table.eagerly_normalize_and_resolve_shallow_in(self.expr_ty.clone()); + self.cast_ty = ctx.table.eagerly_normalize_and_resolve_shallow_in(self.cast_ty.clone()); // This should always come first so that we apply the coercion, which impacts infer vars. - if let Ok((adj, _)) = table.coerce(&self.expr_ty, &self.cast_ty, CoerceNever::Yes) { - apply_adjustments(self.source_expr, adj); - set_coercion_cast(self.source_expr); + if ctx + .coerce( + self.source_expr.into(), + self.expr_ty.to_nextsolver(ctx.table.interner), + self.cast_ty.to_nextsolver(ctx.table.interner), + AllowTwoPhase::No, + CoerceNever::Yes, + ) + .is_ok() + { + ctx.result.coercion_casts.insert(self.source_expr); return Ok(()); } @@ -118,7 +122,7 @@ impl CastCheck { } if !self.cast_ty.data(Interner).flags.contains(TypeFlags::HAS_TY_INFER) - && !table.is_sized(&self.cast_ty) + && !ctx.table.is_sized(&self.cast_ty) { return Err(InferenceDiagnostic::CastToUnsized { expr: self.expr, @@ -133,30 +137,31 @@ impl CastCheck { return Ok(()); } - self.do_check(table, apply_adjustments) + self.do_check(ctx) .map_err(|e| e.into_diagnostic(self.expr, self.expr_ty.clone(), self.cast_ty.clone())) } - fn do_check( - &self, - table: &mut InferenceTable<'_>, - apply_adjustments: &mut F, - ) -> Result<(), CastError> - where - F: FnMut(ExprId, Vec), - { + fn do_check(&self, ctx: &mut InferenceContext<'_>) -> Result<(), CastError> { let (t_from, t_cast) = match ( - CastTy::from_ty(table.db, &self.expr_ty), - CastTy::from_ty(table.db, &self.cast_ty), + CastTy::from_ty(ctx.db, &self.expr_ty), + CastTy::from_ty(ctx.db, &self.cast_ty), ) { (Some(t_from), Some(t_cast)) => (t_from, t_cast), (None, Some(t_cast)) => match self.expr_ty.kind(Interner) { TyKind::FnDef(..) => { - let sig = self.expr_ty.callable_sig(table.db).expect("FnDef had no sig"); - let sig = table.eagerly_normalize_and_resolve_shallow_in(sig); + let sig = self.expr_ty.callable_sig(ctx.db).expect("FnDef had no sig"); + let sig = ctx.table.eagerly_normalize_and_resolve_shallow_in(sig); let fn_ptr = TyKind::Function(sig.to_fn_ptr()).intern(Interner); - if let Ok((adj, _)) = table.coerce(&self.expr_ty, &fn_ptr, CoerceNever::Yes) { - apply_adjustments(self.source_expr, adj); + if ctx + .coerce( + self.source_expr.into(), + self.expr_ty.to_nextsolver(ctx.table.interner), + fn_ptr.to_nextsolver(ctx.table.interner), + AllowTwoPhase::No, + CoerceNever::Yes, + ) + .is_ok() + { } else { return Err(CastError::IllegalCast); } @@ -176,11 +181,11 @@ impl CastCheck { }, // array-ptr-cast CastTy::Ptr(t, m) => { - let t = table.eagerly_normalize_and_resolve_shallow_in(t); - if !table.is_sized(&t) { + let t = ctx.table.eagerly_normalize_and_resolve_shallow_in(t); + if !ctx.table.is_sized(&t) { return Err(CastError::IllegalCast); } - self.check_ref_cast(table, inner_ty, *mutbl, &t, m, apply_adjustments) + self.check_ref_cast(ctx, inner_ty, *mutbl, &t, m) } _ => Err(CastError::NonScalar), }; @@ -202,12 +207,10 @@ impl CastCheck { } (CastTy::Int(Int::Bool | Int::CEnum | Int::Char) | CastTy::Float, CastTy::Ptr(..)) | (CastTy::Ptr(..) | CastTy::FnPtr, CastTy::Float) => Err(CastError::IllegalCast), - (CastTy::Ptr(src, _), CastTy::Ptr(dst, _)) => { - self.check_ptr_ptr_cast(table, &src, &dst) - } - (CastTy::Ptr(src, _), CastTy::Int(_)) => self.check_ptr_addr_cast(table, &src), - (CastTy::Int(_), CastTy::Ptr(dst, _)) => self.check_addr_ptr_cast(table, &dst), - (CastTy::FnPtr, CastTy::Ptr(dst, _)) => self.check_fptr_ptr_cast(table, &dst), + (CastTy::Ptr(src, _), CastTy::Ptr(dst, _)) => self.check_ptr_ptr_cast(ctx, &src, &dst), + (CastTy::Ptr(src, _), CastTy::Int(_)) => self.check_ptr_addr_cast(ctx, &src), + (CastTy::Int(_), CastTy::Ptr(dst, _)) => self.check_addr_ptr_cast(ctx, &dst), + (CastTy::FnPtr, CastTy::Ptr(dst, _)) => self.check_fptr_ptr_cast(ctx, &dst), (CastTy::Int(Int::CEnum), CastTy::Int(_)) => Ok(()), (CastTy::Int(Int::Char | Int::Bool), CastTy::Int(_)) => Ok(()), (CastTy::Int(_) | CastTy::Float, CastTy::Int(_) | CastTy::Float) => Ok(()), @@ -215,26 +218,30 @@ impl CastCheck { } } - fn check_ref_cast( + fn check_ref_cast( &self, - table: &mut InferenceTable<'_>, + ctx: &mut InferenceContext<'_>, t_expr: &Ty, m_expr: Mutability, t_cast: &Ty, m_cast: Mutability, - apply_adjustments: &mut F, - ) -> Result<(), CastError> - where - F: FnMut(ExprId, Vec), - { + ) -> Result<(), CastError> { // Mutability order is opposite to rustc. `Mut < Not` if m_expr <= m_cast && let TyKind::Array(ety, _) = t_expr.kind(Interner) { // Coerce to a raw pointer so that we generate RawPtr in MIR. let array_ptr_type = TyKind::Raw(m_expr, t_expr.clone()).intern(Interner); - if let Ok((adj, _)) = table.coerce(&self.expr_ty, &array_ptr_type, CoerceNever::Yes) { - apply_adjustments(self.source_expr, adj); + if ctx + .coerce( + self.source_expr.into(), + self.expr_ty.to_nextsolver(ctx.table.interner), + array_ptr_type.to_nextsolver(ctx.table.interner), + AllowTwoPhase::No, + CoerceNever::Yes, + ) + .is_ok() + { } else { never!( "could not cast from reference to array to pointer to array ({:?} to {:?})", @@ -245,7 +252,16 @@ impl CastCheck { // This is a less strict condition than rustc's `demand_eqtype`, // but false negative is better than false positive - if table.coerce(ety, t_cast, CoerceNever::Yes).is_ok() { + if ctx + .coerce( + self.source_expr.into(), + ety.to_nextsolver(ctx.table.interner), + t_cast.to_nextsolver(ctx.table.interner), + AllowTwoPhase::No, + CoerceNever::Yes, + ) + .is_ok() + { return Ok(()); } } @@ -255,12 +271,12 @@ impl CastCheck { fn check_ptr_ptr_cast( &self, - table: &mut InferenceTable<'_>, + ctx: &mut InferenceContext<'_>, src: &Ty, dst: &Ty, ) -> Result<(), CastError> { - let src_kind = pointer_kind(src, table).map_err(|_| CastError::Unknown)?; - let dst_kind = pointer_kind(dst, table).map_err(|_| CastError::Unknown)?; + let src_kind = pointer_kind(src, ctx).map_err(|_| CastError::Unknown)?; + let dst_kind = pointer_kind(dst, ctx).map_err(|_| CastError::Unknown)?; match (src_kind, dst_kind) { (Some(PointerKind::Error), _) | (_, Some(PointerKind::Error)) => Ok(()), @@ -285,9 +301,9 @@ impl CastCheck { return Ok(()); } let src_principal = - table.db.trait_signature(from_chalk_trait_id(src_principal)); + ctx.db.trait_signature(from_chalk_trait_id(src_principal)); let dst_principal = - table.db.trait_signature(from_chalk_trait_id(dst_principal)); + ctx.db.trait_signature(from_chalk_trait_id(dst_principal)); if src_principal.flags.contains(TraitFlags::AUTO) && dst_principal.flags.contains(TraitFlags::AUTO) { @@ -306,10 +322,10 @@ impl CastCheck { fn check_ptr_addr_cast( &self, - table: &mut InferenceTable<'_>, + ctx: &mut InferenceContext<'_>, expr_ty: &Ty, ) -> Result<(), CastError> { - match pointer_kind(expr_ty, table).map_err(|_| CastError::Unknown)? { + match pointer_kind(expr_ty, ctx).map_err(|_| CastError::Unknown)? { // None => Err(CastError::UnknownExprPtrKind), None => Ok(()), Some(PointerKind::Error) => Ok(()), @@ -320,10 +336,10 @@ impl CastCheck { fn check_addr_ptr_cast( &self, - table: &mut InferenceTable<'_>, + ctx: &mut InferenceContext<'_>, cast_ty: &Ty, ) -> Result<(), CastError> { - match pointer_kind(cast_ty, table).map_err(|_| CastError::Unknown)? { + match pointer_kind(cast_ty, ctx).map_err(|_| CastError::Unknown)? { // None => Err(CastError::UnknownCastPtrKind), None => Ok(()), Some(PointerKind::Error) => Ok(()), @@ -336,10 +352,10 @@ impl CastCheck { fn check_fptr_ptr_cast( &self, - table: &mut InferenceTable<'_>, + ctx: &mut InferenceContext<'_>, cast_ty: &Ty, ) -> Result<(), CastError> { - match pointer_kind(cast_ty, table).map_err(|_| CastError::Unknown)? { + match pointer_kind(cast_ty, ctx).map_err(|_| CastError::Unknown)? { // None => Err(CastError::UnknownCastPtrKind), None => Ok(()), Some(PointerKind::Error) => Ok(()), @@ -362,10 +378,10 @@ enum PointerKind { Error, } -fn pointer_kind(ty: &Ty, table: &mut InferenceTable<'_>) -> Result, ()> { - let ty = table.eagerly_normalize_and_resolve_shallow_in(ty.clone()); +fn pointer_kind(ty: &Ty, ctx: &mut InferenceContext<'_>) -> Result, ()> { + let ty = ctx.table.eagerly_normalize_and_resolve_shallow_in(ty.clone()); - if table.is_sized(&ty) { + if ctx.table.is_sized(&ty) { return Ok(Some(PointerKind::Thin)); } @@ -378,11 +394,11 @@ fn pointer_kind(ty: &Ty, table: &mut InferenceTable<'_>) -> Result) -> Result { match subst.iter(Interner).last().and_then(|arg| arg.ty(Interner)) { None => Ok(Some(PointerKind::Thin)), - Some(ty) => pointer_kind(ty, table), + Some(ty) => pointer_kind(ty, ctx), } } TyKind::Foreign(_) => Ok(Some(PointerKind::Thin)), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index fd7e5a6a0e12..1d5d8dd13edd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -1,143 +1,168 @@ //! Inference of closure parameter types based on the closure's expected type. -use std::{cmp, convert::Infallible, mem, ops::ControlFlow}; +pub(crate) mod analysis; + +use std::ops::ControlFlow; +use std::{iter, mem}; -use chalk_ir::{ - BoundVar, DebruijnIndex, FnSubst, GenericArg, Mutability, TyKind, - cast::Cast, - fold::{FallibleTypeFolder, Shift, TypeFoldable}, - visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, -}; -use either::Either; use hir_def::{ - DefWithBodyId, FieldId, HasModule, TupleFieldId, TupleId, VariantId, - expr_store::path::Path, - hir::{ - Array, AsmOperand, BinaryOp, BindingId, CaptureBy, ClosureKind, Expr, ExprId, ExprOrPatId, - Pat, PatId, Statement, UnaryOp, - }, - item_tree::FieldsShape, + TraitId, + hir::{ClosureKind, ExprId, PatId}, lang_item::LangItem, - resolver::ValueNs, type_ref::TypeRefId, }; -use hir_def::{ItemContainerId, Lookup, TraitId}; -use hir_expand::name::Name; -use intern::sym; -use rustc_hash::{FxHashMap, FxHashSet}; -use smallvec::{SmallVec, smallvec}; -use stdx::{format_to, never}; -use syntax::utils::is_raw_identifier; +use rustc_type_ir::{ + ClosureArgs, ClosureArgsParts, CoroutineArgs, CoroutineArgsParts, Interner, TypeSuperVisitable, + TypeVisitable, TypeVisitableExt, TypeVisitor, + inherent::{BoundExistentialPredicates, GenericArgs as _, IntoKind, SliceLike, Ty as _}, +}; +use tracing::debug; +use crate::traits::FnTrait; use crate::{ - Adjust, Adjustment, AliasEq, AliasTy, Binders, BindingMode, ClosureId, DynTy, DynTyExt, FnAbi, - FnPointer, FnSig, Interner, OpaqueTy, ProjectionTy, ProjectionTyExt, Substitution, Ty, - TyBuilder, TyExt, WhereClause, - db::{HirDatabase, InternedClosure, InternedCoroutine}, - error_lifetime, from_assoc_type_id, from_chalk_trait_id, from_placeholder_idx, - generics::Generics, - infer::{BreakableKind, CoerceMany, Diverges, coerce::CoerceNever}, - make_binders, - mir::{BorrowKind, MirSpan, MutBorrowKind, ProjectionElem}, - next_solver::mapping::ChalkToNextSolver, - to_assoc_type_id, - traits::FnTrait, - utils::{self, elaborate_clause_supertraits}, + FnAbi, + db::{InternedClosure, InternedCoroutine}, + infer::{BreakableKind, Diverges, coerce::CoerceMany}, + next_solver::{ + AliasTy, Binder, ClauseKind, DbInterner, ErrorGuaranteed, FnSig, GenericArgs, PolyFnSig, + PolyProjectionPredicate, Predicate, PredicateKind, SolverDefId, Ty, TyKind, + abi::Safety, + infer::{ + BoundRegionConversionTime, DefineOpaqueTypes, InferOk, InferResult, + traits::{ObligationCause, PredicateObligations}, + }, + mapping::{ChalkToNextSolver, NextSolverToChalk}, + util::explicit_item_bounds, + }, }; use super::{Expectation, InferenceContext}; #[derive(Debug)] -pub(super) struct ClosureSignature { - pub(super) ret_ty: Ty, - pub(super) expected_sig: FnPointer, +struct ClosureSignatures<'tcx> { + /// The signature users of the closure see. + bound_sig: PolyFnSig<'tcx>, + /// The signature within the function body. + /// This mostly differs in the sense that lifetimes are now early bound and any + /// opaque types from the signature expectation are overridden in case there are + /// explicit hidden types written by the user in the closure signature. + liberated_sig: FnSig<'tcx>, } impl<'db> InferenceContext<'db> { pub(super) fn infer_closure( &mut self, - body: &ExprId, + body: ExprId, args: &[PatId], - ret_type: &Option, + ret_type: Option, arg_types: &[Option], closure_kind: ClosureKind, tgt_expr: ExprId, expected: &Expectation, - ) -> Ty { + ) -> crate::Ty { assert_eq!(args.len(), arg_types.len()); + let interner = self.table.interner; let (expected_sig, expected_kind) = match expected.to_option(&mut self.table) { - Some(expected_ty) => self.deduce_closure_signature(&expected_ty, closure_kind), + Some(expected_ty) => { + self.deduce_closure_signature(expected_ty.to_nextsolver(interner), closure_kind) + } None => (None, None), }; - let ClosureSignature { expected_sig: mut bound_sig, ret_ty: body_ret_ty } = - self.sig_of_closure(body, ret_type, arg_types, closure_kind, expected_sig); - bound_sig.substitution.0 = self - .normalize_associated_types_in::<_, crate::next_solver::GenericArgs<'db>>( - bound_sig.substitution.0, - ); - let bound_sig = bound_sig; - let sig_ty = TyKind::Function(bound_sig.clone()).intern(Interner); + let ClosureSignatures { bound_sig, liberated_sig } = + self.sig_of_closure(arg_types, ret_type, expected_sig); + let body_ret_ty = bound_sig.output().skip_binder(); + let sig_ty = Ty::new_fn_ptr(interner, bound_sig); + let parent_args = GenericArgs::identity_for_item(interner, self.generic_def.into()); let (id, ty, resume_yield_tys) = match closure_kind { ClosureKind::Coroutine(_) => { - let sig_tys = bound_sig.substitution.0.as_slice(Interner); - // FIXME: report error when there are more than 1 parameter. - let resume_ty = match sig_tys.first() { - // When `sig_tys.len() == 1` the first type is the return type, not the - // first parameter type. - Some(ty) if sig_tys.len() > 1 => ty.assert_ty_ref(Interner).clone(), - _ => self.result.standard_types.unit.clone(), - }; - let yield_ty = self.table.new_type_var(); + let yield_ty = self.table.next_ty_var(); + let resume_ty = liberated_sig + .inputs() + .get(0) + .unwrap_or(self.result.standard_types.unit.to_nextsolver(interner)); - let subst = TyBuilder::subst_for_coroutine(self.db, self.owner) - .push(resume_ty.clone()) - .push(yield_ty.clone()) - .push(body_ret_ty.clone()) - .build(); + // FIXME: Infer the upvars later. + let parts = CoroutineArgsParts { + parent_args, + kind_ty: Ty::new_unit(interner), + resume_ty, + yield_ty, + return_ty: body_ret_ty, + tupled_upvars_ty: Ty::new_unit(interner), + }; let coroutine_id = self.db.intern_coroutine(InternedCoroutine(self.owner, tgt_expr)).into(); - let coroutine_ty = TyKind::Coroutine(coroutine_id, subst).intern(Interner); + let coroutine_ty = Ty::new_coroutine( + interner, + coroutine_id, + CoroutineArgs::new(interner, parts).args, + ); - (None, coroutine_ty, Some((resume_ty, yield_ty))) - } - ClosureKind::Closure | ClosureKind::Async => { - let closure_id = - self.db.intern_closure(InternedClosure(self.owner, tgt_expr)).into(); - let closure_ty = TyKind::Closure( - closure_id, - TyBuilder::subst_for_closure(self.db, self.owner, sig_ty.clone()), + ( + None, + coroutine_ty, + Some((resume_ty.to_chalk(interner), yield_ty.to_chalk(interner))), ) - .intern(Interner); + } + // FIXME(next-solver): `ClosureKind::Async` should really be a separate arm that creates a `CoroutineClosure`. + // But for now we treat it as a closure. + ClosureKind::Closure | ClosureKind::Async => { + let closure_id = self.db.intern_closure(InternedClosure(self.owner, tgt_expr)); + match expected_kind { + Some(kind) => { + self.result.closure_info.insert( + closure_id.into(), + ( + Vec::new(), + match kind { + rustc_type_ir::ClosureKind::Fn => FnTrait::Fn, + rustc_type_ir::ClosureKind::FnMut => FnTrait::FnMut, + rustc_type_ir::ClosureKind::FnOnce => FnTrait::FnOnce, + }, + ), + ); + } + None => {} + }; + // FIXME: Infer the kind and the upvars later when needed. + let parts = ClosureArgsParts { + parent_args, + closure_kind_ty: Ty::from_closure_kind( + interner, + expected_kind.unwrap_or(rustc_type_ir::ClosureKind::Fn), + ), + closure_sig_as_fn_ptr_ty: sig_ty, + tupled_upvars_ty: Ty::new_unit(interner), + }; + let closure_ty = Ty::new_closure( + interner, + closure_id.into(), + ClosureArgs::new(interner, parts).args, + ); self.deferred_closures.entry(closure_id).or_default(); self.add_current_closure_dependency(closure_id); (Some(closure_id), closure_ty, None) } }; - // Eagerly try to relate the closure type with the expected - // type, otherwise we often won't have enough information to - // infer the body. - self.deduce_closure_type_from_expectations(tgt_expr, &ty, &sig_ty, expected, expected_kind); - // Now go through the argument patterns - for (arg_pat, arg_ty) in args.iter().zip(bound_sig.substitution.0.as_slice(Interner).iter()) - { - self.infer_top_pat(*arg_pat, arg_ty.assert_ty_ref(Interner), None); + for (arg_pat, arg_ty) in args.iter().zip(bound_sig.skip_binder().inputs()) { + self.infer_top_pat(*arg_pat, &arg_ty.to_chalk(interner), None); } // FIXME: lift these out into a struct let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); let prev_closure = mem::replace(&mut self.current_closure, id); - let prev_ret_ty = mem::replace(&mut self.return_ty, body_ret_ty.clone()); + let prev_ret_ty = mem::replace(&mut self.return_ty, body_ret_ty.to_chalk(interner)); let prev_ret_coercion = self.return_coercion.replace(CoerceMany::new(body_ret_ty)); let prev_resume_yield_tys = mem::replace(&mut self.resume_yield_tys, resume_yield_tys); self.with_breakable_ctx(BreakableKind::Border, None, None, |this| { - this.infer_return(*body); + this.infer_return(body); }); self.diverges = prev_diverges; @@ -146,176 +171,203 @@ impl<'db> InferenceContext<'db> { self.current_closure = prev_closure; self.resume_yield_tys = prev_resume_yield_tys; - self.table.normalize_associated_types_in(ty) + ty.to_chalk(interner) } - // This function handles both closures and coroutines. - pub(super) fn deduce_closure_type_from_expectations( - &mut self, - closure_expr: ExprId, - closure_ty: &Ty, - sig_ty: &Ty, - expectation: &Expectation, - expected_kind: Option, - ) { - let expected_ty = match expectation.to_option(&mut self.table) { - Some(ty) => ty, - None => return, - }; - - match (closure_ty.kind(Interner), expected_kind) { - (TyKind::Closure(closure_id, _), Some(closure_kind)) => { - self.result - .closure_info - .entry(*closure_id) - .or_insert_with(|| (Vec::new(), closure_kind)); - } - _ => {} - } - - // Deduction from where-clauses in scope, as well as fn-pointer coercion are handled here. - let _ = self.coerce(Some(closure_expr), closure_ty, &expected_ty, CoerceNever::Yes); - - // Coroutines are not Fn* so return early. - if matches!(closure_ty.kind(Interner), TyKind::Coroutine(..)) { - return; - } - - // Deduction based on the expected `dyn Fn` is done separately. - if let TyKind::Dyn(dyn_ty) = expected_ty.kind(Interner) - && let Some(sig) = self.deduce_sig_from_dyn_ty(dyn_ty) - { - let expected_sig_ty = TyKind::Function(sig).intern(Interner); - - self.unify(sig_ty, &expected_sig_ty); + fn fn_trait_kind_from_def_id(&self, trait_id: TraitId) -> Option { + let lang_item = self.db.lang_attr(trait_id.into())?; + match lang_item { + LangItem::Fn => Some(rustc_type_ir::ClosureKind::Fn), + LangItem::FnMut => Some(rustc_type_ir::ClosureKind::FnMut), + LangItem::FnOnce => Some(rustc_type_ir::ClosureKind::FnOnce), + _ => None, } } - // Closure kind deductions are mostly from `rustc_hir_typeck/src/closure.rs`. - // Might need to port closure sig deductions too. - pub(super) fn deduce_closure_signature( + fn async_fn_trait_kind_from_def_id( + &self, + trait_id: TraitId, + ) -> Option { + let lang_item = self.db.lang_attr(trait_id.into())?; + match lang_item { + LangItem::AsyncFn => Some(rustc_type_ir::ClosureKind::Fn), + LangItem::AsyncFnMut => Some(rustc_type_ir::ClosureKind::FnMut), + LangItem::AsyncFnOnce => Some(rustc_type_ir::ClosureKind::FnOnce), + _ => None, + } + } + + /// Given the expected type, figures out what it can about this closure we + /// are about to type check: + fn deduce_closure_signature( &mut self, - expected_ty: &Ty, + expected_ty: Ty<'db>, closure_kind: ClosureKind, - ) -> (Option>, Option) { - match expected_ty.kind(Interner) { - TyKind::Alias(AliasTy::Opaque(OpaqueTy { .. })) | TyKind::OpaqueType(..) => { - let clauses = expected_ty.impl_trait_bounds(self.db).into_iter().flatten().map( - |b: chalk_ir::Binders>| { - b.into_value_and_skipped_binders().0 - }, - ); - self.deduce_closure_kind_from_predicate_clauses(expected_ty, clauses, closure_kind) - } - TyKind::Dyn(dyn_ty) => { - let sig = - dyn_ty.bounds.skip_binders().as_slice(Interner).iter().find_map(|bound| { - if let WhereClause::AliasEq(AliasEq { - alias: AliasTy::Projection(projection_ty), - ty: projected_ty, - }) = bound.skip_binders() - && let Some(sig) = self.deduce_sig_from_projection( - closure_kind, - projection_ty, - projected_ty, - ) - { - return Some(sig); - } - None - }); - - let kind = dyn_ty.principal().and_then(|principal_trait_ref| { - self.fn_trait_kind_from_trait_id(from_chalk_trait_id( - principal_trait_ref.skip_binders().skip_binders().trait_id, - )) + ) -> (Option>, Option) { + match expected_ty.kind() { + TyKind::Alias(rustc_type_ir::Opaque, AliasTy { def_id, args, .. }) => self + .deduce_closure_signature_from_predicates( + expected_ty, + closure_kind, + explicit_item_bounds(self.table.interner, def_id) + .iter_instantiated(self.table.interner, args) + .map(|clause| clause.as_predicate()), + ), + TyKind::Dynamic(object_type, ..) => { + let sig = object_type.projection_bounds().into_iter().find_map(|pb| { + let pb = + pb.with_self_ty(self.table.interner, Ty::new_unit(self.table.interner)); + self.deduce_sig_from_projection(closure_kind, pb) }); - + let kind = object_type + .principal_def_id() + .and_then(|did| self.fn_trait_kind_from_def_id(did.0)); (sig, kind) } - TyKind::InferenceVar(ty, chalk_ir::TyVariableKind::General) => { - let clauses = self.clauses_for_self_ty(*ty); - self.deduce_closure_kind_from_predicate_clauses( - expected_ty, - clauses.into_iter(), + TyKind::Infer(rustc_type_ir::TyVar(vid)) => self + .deduce_closure_signature_from_predicates( + Ty::new_var(self.table.interner, self.table.infer_ctxt.root_var(vid)), closure_kind, - ) - } - TyKind::Function(fn_ptr) => match closure_kind { - ClosureKind::Closure => (Some(fn_ptr.substitution.clone()), Some(FnTrait::Fn)), - ClosureKind::Async | ClosureKind::Coroutine(_) => (None, None), + self.table.obligations_for_self_ty(vid).into_iter().map(|obl| obl.predicate), + ), + TyKind::FnPtr(sig_tys, hdr) => match closure_kind { + ClosureKind::Closure => { + let expected_sig = sig_tys.with(hdr); + (Some(expected_sig), Some(rustc_type_ir::ClosureKind::Fn)) + } + ClosureKind::Coroutine(_) | ClosureKind::Async => (None, None), }, _ => (None, None), } } - fn deduce_closure_kind_from_predicate_clauses( + fn deduce_closure_signature_from_predicates( &mut self, - expected_ty: &Ty, - clauses: impl DoubleEndedIterator, + expected_ty: Ty<'db>, closure_kind: ClosureKind, - ) -> (Option>, Option) { + predicates: impl DoubleEndedIterator>, + ) -> (Option>, Option) { let mut expected_sig = None; let mut expected_kind = None; - for clause in elaborate_clause_supertraits(self.db, clauses.rev()) { + for pred in rustc_type_ir::elaborate::elaborate( + self.table.interner, + // Reverse the obligations here, since `elaborate_*` uses a stack, + // and we want to keep inference generally in the same order of + // the registered obligations. + predicates.rev(), + ) + // We only care about self bounds + .filter_only_self() + { + debug!(?pred); + let bound_predicate = pred.kind(); + + // Given a Projection predicate, we can potentially infer + // the complete signature. if expected_sig.is_none() - && let WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection), ty }) = - &clause + && let PredicateKind::Clause(ClauseKind::Projection(proj_predicate)) = + bound_predicate.skip_binder() { - let inferred_sig = self.deduce_sig_from_projection(closure_kind, projection, ty); + let inferred_sig = self.deduce_sig_from_projection( + closure_kind, + bound_predicate.rebind(proj_predicate), + ); + // Make sure that we didn't infer a signature that mentions itself. // This can happen when we elaborate certain supertrait bounds that - // mention projections containing the `Self` type. See rust-lang/rust#105401. - struct MentionsTy<'a> { - expected_ty: &'a Ty, + // mention projections containing the `Self` type. See #105401. + struct MentionsTy<'db> { + expected_ty: Ty<'db>, } - impl TypeVisitor for MentionsTy<'_> { - type BreakTy = (); + impl<'db> TypeVisitor> for MentionsTy<'db> { + type Result = ControlFlow<()>; - fn interner(&self) -> Interner { - Interner - } - - fn as_dyn( - &mut self, - ) -> &mut dyn TypeVisitor - { - self - } - - fn visit_ty(&mut self, t: &Ty, db: chalk_ir::DebruijnIndex) -> ControlFlow<()> { + fn visit_ty(&mut self, t: Ty<'db>) -> Self::Result { if t == self.expected_ty { ControlFlow::Break(()) } else { - t.super_visit_with(self, db) + t.super_visit_with(self) } } } - if inferred_sig - .visit_with(&mut MentionsTy { expected_ty }, chalk_ir::DebruijnIndex::INNERMOST) - .is_continue() - { + + // Don't infer a closure signature from a goal that names the closure type as this will + // (almost always) lead to occurs check errors later in type checking. + if let Some(inferred_sig) = inferred_sig { + // In the new solver it is difficult to explicitly normalize the inferred signature as we + // would have to manually handle universes and rewriting bound vars and placeholders back + // and forth. + // + // Instead we take advantage of the fact that we relating an inference variable with an alias + // will only instantiate the variable if the alias is rigid(*not quite). Concretely we: + // - Create some new variable `?sig` + // - Equate `?sig` with the unnormalized signature, e.g. `fn( as Trait>::Assoc)` + // - Depending on whether ` as Trait>::Assoc` is rigid, ambiguous or normalizeable, + // we will either wind up with `?sig= as Trait>::Assoc/?y/ConcreteTy` respectively. + // + // *: In cases where there are ambiguous aliases in the signature that make use of bound vars + // they will wind up present in `?sig` even though they are non-rigid. + // + // This is a bit weird and means we may wind up discarding the goal due to it naming `expected_ty` + // even though the normalized form may not name `expected_ty`. However, this matches the existing + // behaviour of the old solver and would be technically a breaking change to fix. + let generalized_fnptr_sig = self.table.next_ty_var(); + let inferred_fnptr_sig = Ty::new_fn_ptr(self.table.interner, inferred_sig); + // FIXME: Report diagnostics. + _ = self + .table + .infer_ctxt + .at(&ObligationCause::new(), self.table.param_env) + .eq(DefineOpaqueTypes::Yes, inferred_fnptr_sig, generalized_fnptr_sig) + .map(|infer_ok| self.table.register_infer_ok(infer_ok)); + + let resolved_sig = + self.table.infer_ctxt.resolve_vars_if_possible(generalized_fnptr_sig); + + if resolved_sig.visit_with(&mut MentionsTy { expected_ty }).is_continue() { + expected_sig = Some(resolved_sig.fn_sig(self.table.interner)); + } + } else if inferred_sig.visit_with(&mut MentionsTy { expected_ty }).is_continue() { expected_sig = inferred_sig; } } - let trait_id = match clause { - WhereClause::AliasEq(AliasEq { - alias: AliasTy::Projection(projection), .. - }) => projection.trait_(self.db), - WhereClause::Implemented(trait_ref) => from_chalk_trait_id(trait_ref.trait_id), - _ => continue, + // Even if we can't infer the full signature, we may be able to + // infer the kind. This can occur when we elaborate a predicate + // like `F : Fn`. Note that due to subtyping we could encounter + // many viable options, so pick the most restrictive. + let trait_def_id = match bound_predicate.skip_binder() { + PredicateKind::Clause(ClauseKind::Projection(data)) => { + Some(data.projection_term.trait_def_id(self.table.interner).0) + } + PredicateKind::Clause(ClauseKind::Trait(data)) => Some(data.def_id().0), + _ => None, }; - if let Some(closure_kind) = self.fn_trait_kind_from_trait_id(trait_id) { - // always use the closure kind that is more permissive. - match (expected_kind, closure_kind) { - (None, _) => expected_kind = Some(closure_kind), - (Some(FnTrait::FnMut), FnTrait::Fn) => expected_kind = Some(FnTrait::Fn), - (Some(FnTrait::FnOnce), FnTrait::Fn | FnTrait::FnMut) => { - expected_kind = Some(closure_kind) + + if let Some(trait_def_id) = trait_def_id { + let found_kind = match closure_kind { + ClosureKind::Closure => self.fn_trait_kind_from_def_id(trait_def_id), + ClosureKind::Async => self + .async_fn_trait_kind_from_def_id(trait_def_id) + .or_else(|| self.fn_trait_kind_from_def_id(trait_def_id)), + _ => None, + }; + + if let Some(found_kind) = found_kind { + // always use the closure kind that is more permissive. + match (expected_kind, found_kind) { + (None, _) => expected_kind = Some(found_kind), + ( + Some(rustc_type_ir::ClosureKind::FnMut), + rustc_type_ir::ClosureKind::Fn, + ) => expected_kind = Some(rustc_type_ir::ClosureKind::Fn), + ( + Some(rustc_type_ir::ClosureKind::FnOnce), + rustc_type_ir::ClosureKind::Fn | rustc_type_ir::ClosureKind::FnMut, + ) => expected_kind = Some(found_kind), + _ => {} } - _ => {} } } } @@ -323,1519 +375,440 @@ impl<'db> InferenceContext<'db> { (expected_sig, expected_kind) } - fn deduce_sig_from_dyn_ty(&self, dyn_ty: &DynTy) -> Option { - // Search for a predicate like `<$self as FnX>::Output == Ret` - - let fn_traits: SmallVec<[TraitId; 3]> = - utils::fn_traits(self.db, self.owner.module(self.db).krate()).collect(); - - let self_ty = self.result.standard_types.unknown.clone(); - let bounds = dyn_ty.bounds.clone().substitute(Interner, &[self_ty.cast(Interner)]); - for bound in bounds.iter(Interner) { - // NOTE(skip_binders): the extracted types are rebound by the returned `FnPointer` - if let WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection), ty }) = - bound.skip_binders() - { - let trait_ = - match from_assoc_type_id(projection.associated_ty_id).lookup(self.db).container - { - ItemContainerId::TraitId(t) => t, - _ => panic!("associated type not in trait"), - }; - if !fn_traits.contains(&trait_) { - return None; - } - - // Skip `Self`, get the type argument. - let arg = projection.substitution.as_slice(Interner).get(1)?; - if let Some(subst) = arg.ty(Interner)?.as_tuple() { - let generic_args = subst.as_slice(Interner); - let mut sig_tys = Vec::with_capacity(generic_args.len() + 1); - for arg in generic_args { - sig_tys.push(arg.ty(Interner)?.clone()); - } - sig_tys.push(ty.clone()); - - cov_mark::hit!(dyn_fn_param_informs_call_site_closure_signature); - return Some(FnPointer { - num_binders: bound.len(Interner), - sig: FnSig { - abi: FnAbi::RustCall, - safety: chalk_ir::Safety::Safe, - variadic: false, - }, - substitution: FnSubst(Substitution::from_iter(Interner, sig_tys)), - }); - } - } - } - - None - } - + /// Given a projection like "::Result == Y", we can deduce + /// everything we need to know about a closure or coroutine. + /// + /// The `cause_span` should be the span that caused us to + /// have this expected signature, or `None` if we can't readily + /// know that. fn deduce_sig_from_projection( &mut self, closure_kind: ClosureKind, - projection_ty: &ProjectionTy, - projected_ty: &Ty, - ) -> Option> { - let container = - from_assoc_type_id(projection_ty.associated_ty_id).lookup(self.db).container; - let trait_ = match container { - hir_def::ItemContainerId::TraitId(trait_) => trait_, - _ => return None, - }; + projection: PolyProjectionPredicate<'db>, + ) -> Option> { + let SolverDefId::TypeAliasId(def_id) = projection.item_def_id() else { unreachable!() }; + let lang_item = self.db.lang_attr(def_id.into()); // For now, we only do signature deduction based off of the `Fn` and `AsyncFn` traits, // for closures and async closures, respectively. - let fn_trait_kind = self.fn_trait_kind_from_trait_id(trait_)?; - if !matches!(closure_kind, ClosureKind::Closure | ClosureKind::Async) { - return None; - } - if fn_trait_kind.is_async() { - // If the expected trait is `AsyncFn(...) -> X`, we don't know what the return type is, - // but we do know it must implement `Future`. - self.extract_async_fn_sig_from_projection(projection_ty, projected_ty) - } else { - self.extract_sig_from_projection(projection_ty, projected_ty) + match closure_kind { + ClosureKind::Closure if lang_item == Some(LangItem::FnOnceOutput) => { + self.extract_sig_from_projection(projection) + } + ClosureKind::Async if lang_item == Some(LangItem::AsyncFnOnceOutput) => { + self.extract_sig_from_projection(projection) + } + // It's possible we've passed the closure to a (somewhat out-of-fashion) + // `F: FnOnce() -> Fut, Fut: Future` style bound. Let's still + // guide inference here, since it's beneficial for the user. + ClosureKind::Async if lang_item == Some(LangItem::FnOnceOutput) => { + self.extract_sig_from_projection_and_future_bound(projection) + } + _ => None, } } + /// Given an `FnOnce::Output` or `AsyncFn::Output` projection, extract the args + /// and return type to infer a [`ty::PolyFnSig`] for the closure. fn extract_sig_from_projection( &self, - projection_ty: &ProjectionTy, - projected_ty: &Ty, - ) -> Option> { - let arg_param_ty = projection_ty.substitution.as_slice(Interner)[1].assert_ty_ref(Interner); + projection: PolyProjectionPredicate<'db>, + ) -> Option> { + let projection = self.table.infer_ctxt.resolve_vars_if_possible(projection); - let TyKind::Tuple(_, input_tys) = arg_param_ty.kind(Interner) else { + let arg_param_ty = projection.skip_binder().projection_term.args.type_at(1); + debug!(?arg_param_ty); + + let TyKind::Tuple(input_tys) = arg_param_ty.kind() else { return None; }; - let ret_param_ty = projected_ty; + // Since this is a return parameter type it is safe to unwrap. + let ret_param_ty = projection.skip_binder().term.expect_type(); + debug!(?ret_param_ty); - Some(FnSubst(Substitution::from_iter( - Interner, - input_tys.iter(Interner).map(|t| t.cast(Interner)).chain(Some(GenericArg::new( - Interner, - chalk_ir::GenericArgData::Ty(ret_param_ty.clone()), - ))), - ))) + let sig = projection.rebind(self.table.interner.mk_fn_sig( + input_tys, + ret_param_ty, + false, + Safety::Safe, + FnAbi::Rust, + )); + + Some(sig) } - fn extract_async_fn_sig_from_projection( + /// When an async closure is passed to a function that has a "two-part" `Fn` + /// and `Future` trait bound, like: + /// + /// ```rust + /// use std::future::Future; + /// + /// fn not_exactly_an_async_closure(_f: F) + /// where + /// F: FnOnce(String, u32) -> Fut, + /// Fut: Future, + /// {} + /// ``` + /// + /// The we want to be able to extract the signature to guide inference in the async + /// closure. We will have two projection predicates registered in this case. First, + /// we identify the `FnOnce` bound, and if the output type is + /// an inference variable `?Fut`, we check if that is bounded by a `Future` + /// projection. + /// + /// This function is actually best-effort with the return type; if we don't find a + /// `Future` projection, we still will return arguments that we extracted from the `FnOnce` + /// projection, and the output will be an unconstrained type variable instead. + fn extract_sig_from_projection_and_future_bound( &mut self, - projection_ty: &ProjectionTy, - projected_ty: &Ty, - ) -> Option> { - let arg_param_ty = projection_ty.substitution.as_slice(Interner)[1].assert_ty_ref(Interner); + projection: PolyProjectionPredicate<'db>, + ) -> Option> { + let projection = self.table.infer_ctxt.resolve_vars_if_possible(projection); - let TyKind::Tuple(_, input_tys) = arg_param_ty.kind(Interner) else { + let arg_param_ty = projection.skip_binder().projection_term.args.type_at(1); + debug!(?arg_param_ty); + + let TyKind::Tuple(input_tys) = arg_param_ty.kind() else { return None; }; - let ret_param_future_output = projected_ty; - let ret_param_future = self.table.new_type_var(); - let future_output = - LangItem::FutureOutput.resolve_type_alias(self.db, self.resolver.krate())?; - let future_projection = crate::AliasTy::Projection(crate::ProjectionTy { - associated_ty_id: to_assoc_type_id(future_output), - substitution: Substitution::from1(Interner, ret_param_future.clone()), - }); - let goal: crate::Goal = - crate::AliasEq { alias: future_projection, ty: ret_param_future_output.clone() } - .cast(Interner); - self.table.register_obligation(goal.to_nextsolver(self.table.interner)); - - Some(FnSubst(Substitution::from_iter( - Interner, - input_tys.iter(Interner).map(|t| t.cast(Interner)).chain(Some(GenericArg::new( - Interner, - chalk_ir::GenericArgData::Ty(ret_param_future), - ))), - ))) - } - - fn fn_trait_kind_from_trait_id(&self, trait_id: hir_def::TraitId) -> Option { - FnTrait::from_lang_item(self.db.lang_attr(trait_id.into())?) - } - - fn supplied_sig_of_closure( - &mut self, - body: &ExprId, - ret_type: &Option, - arg_types: &[Option], - closure_kind: ClosureKind, - ) -> ClosureSignature { - let mut sig_tys = Vec::with_capacity(arg_types.len() + 1); - - // collect explicitly written argument types - for arg_type in arg_types.iter() { - let arg_ty = match arg_type { - // FIXME: I think rustc actually lowers closure params with `LifetimeElisionKind::AnonymousCreateParameter` - // (but the return type with infer). - Some(type_ref) => self.make_body_ty(*type_ref), - None => self.table.new_type_var(), - }; - sig_tys.push(arg_ty); - } - - // add return type - let ret_ty = match ret_type { - Some(type_ref) => self.make_body_ty(*type_ref), - None => self.table.new_type_var(), - }; - if let ClosureKind::Async = closure_kind { - sig_tys.push(self.lower_async_block_type_impl_trait(ret_ty.clone(), *body)); - } else { - sig_tys.push(ret_ty.clone()); - } - - let expected_sig = FnPointer { - num_binders: 0, - sig: FnSig { abi: FnAbi::RustCall, safety: chalk_ir::Safety::Safe, variadic: false }, - substitution: FnSubst( - Substitution::from_iter(Interner, sig_tys.iter().cloned()).shifted_in(Interner), - ), + // If the return type is a type variable, look for bounds on it. + // We could theoretically support other kinds of return types here, + // but none of them would be useful, since async closures return + // concrete anonymous future types, and their futures are not coerced + // into any other type within the body of the async closure. + let TyKind::Infer(rustc_type_ir::TyVar(return_vid)) = + projection.skip_binder().term.expect_type().kind() + else { + return None; }; - ClosureSignature { ret_ty, expected_sig } + // FIXME: We may want to elaborate here, though I assume this will be exceedingly rare. + let mut return_ty = None; + for bound in self.table.obligations_for_self_ty(return_vid) { + if let PredicateKind::Clause(ClauseKind::Projection(ret_projection)) = + bound.predicate.kind().skip_binder() + && let ret_projection = bound.predicate.kind().rebind(ret_projection) + && let Some(ret_projection) = ret_projection.no_bound_vars() + && let SolverDefId::TypeAliasId(assoc_type) = ret_projection.def_id() + && self.db.lang_attr(assoc_type.into()) == Some(LangItem::FutureOutput) + { + return_ty = Some(ret_projection.term.expect_type()); + break; + } + } + + // SUBTLE: If we didn't find a `Future` bound for the return + // vid, we still want to attempt to provide inference guidance for the async + // closure's arguments. Instantiate a new vid to plug into the output type. + // + // You may be wondering, what if it's higher-ranked? Well, given that we + // found a type variable for the `FnOnce::Output` projection above, we know + // that the output can't mention any of the vars. + // + // Also note that we use a fresh var here for the signature since the signature + // records the output of the *future*, and `return_vid` above is the type + // variable of the future, not its output. + // + // FIXME: We probably should store this signature inference output in a way + // that does not misuse a `FnSig` type, but that can be done separately. + let return_ty = return_ty.unwrap_or_else(|| self.table.next_ty_var()); + + let sig = projection.rebind(self.table.interner.mk_fn_sig( + input_tys, + return_ty, + false, + Safety::Safe, + FnAbi::Rust, + )); + + Some(sig) } - /// The return type is the signature of the closure, and the return type - /// *as represented inside the body* (so, for async closures, the `Output` ty) - pub(super) fn sig_of_closure( + fn sig_of_closure( &mut self, - body: &ExprId, - ret_type: &Option, - arg_types: &[Option], - closure_kind: ClosureKind, - expected_sig: Option>, - ) -> ClosureSignature { + decl_inputs: &[Option], + decl_output: Option, + expected_sig: Option>, + ) -> ClosureSignatures<'db> { if let Some(e) = expected_sig { - self.sig_of_closure_with_expectation(body, ret_type, arg_types, closure_kind, e) + self.sig_of_closure_with_expectation(decl_inputs, decl_output, e) } else { - self.sig_of_closure_no_expectation(body, ret_type, arg_types, closure_kind) + self.sig_of_closure_no_expectation(decl_inputs, decl_output) } } + /// If there is no expected signature, then we will convert the + /// types that the user gave into a signature. fn sig_of_closure_no_expectation( &mut self, - body: &ExprId, - ret_type: &Option, - arg_types: &[Option], - closure_kind: ClosureKind, - ) -> ClosureSignature { - self.supplied_sig_of_closure(body, ret_type, arg_types, closure_kind) + decl_inputs: &[Option], + decl_output: Option, + ) -> ClosureSignatures<'db> { + let bound_sig = self.supplied_sig_of_closure(decl_inputs, decl_output); + + self.closure_sigs(bound_sig) } + /// Invoked to compute the signature of a closure expression. This + /// combines any user-provided type annotations (e.g., `|x: u32| + /// -> u32 { .. }`) with the expected signature. + /// + /// The approach is as follows: + /// + /// - Let `S` be the (higher-ranked) signature that we derive from the user's annotations. + /// - Let `E` be the (higher-ranked) signature that we derive from the expectations, if any. + /// - If we have no expectation `E`, then the signature of the closure is `S`. + /// - Otherwise, the signature of the closure is E. Moreover: + /// - Skolemize the late-bound regions in `E`, yielding `E'`. + /// - Instantiate all the late-bound regions bound in the closure within `S` + /// with fresh (existential) variables, yielding `S'` + /// - Require that `E' = S'` + /// - We could use some kind of subtyping relationship here, + /// I imagine, but equality is easier and works fine for + /// our purposes. + /// + /// The key intuition here is that the user's types must be valid + /// from "the inside" of the closure, but the expectation + /// ultimately drives the overall signature. + /// + /// # Examples + /// + /// ```ignore (illustrative) + /// fn with_closure(_: F) + /// where F: Fn(&u32) -> &u32 { .. } + /// + /// with_closure(|x: &u32| { ... }) + /// ``` + /// + /// Here: + /// - E would be `fn(&u32) -> &u32`. + /// - S would be `fn(&u32) -> ?T` + /// - E' is `&'!0 u32 -> &'!0 u32` + /// - S' is `&'?0 u32 -> ?T` + /// + /// S' can be unified with E' with `['?0 = '!0, ?T = &'!10 u32]`. + /// + /// # Arguments + /// + /// - `expr_def_id`: the `LocalDefId` of the closure expression + /// - `decl`: the HIR declaration of the closure + /// - `body`: the body of the closure + /// - `expected_sig`: the expected signature (if any). Note that + /// this is missing a binder: that is, there may be late-bound + /// regions with depth 1, which are bound then by the closure. fn sig_of_closure_with_expectation( &mut self, - body: &ExprId, - ret_type: &Option, - arg_types: &[Option], - closure_kind: ClosureKind, - expected_sig: FnSubst, - ) -> ClosureSignature { - let expected_sig = FnPointer { - num_binders: 0, - sig: FnSig { abi: FnAbi::RustCall, safety: chalk_ir::Safety::Safe, variadic: false }, - substitution: expected_sig, - }; - - // If the expected signature does not match the actual arg types, - // then just return the expected signature - if expected_sig.substitution.0.len(Interner) != arg_types.len() + 1 { - let ret_ty = match ret_type { - Some(type_ref) => self.make_body_ty(*type_ref), - None => self.table.new_type_var(), - }; - return ClosureSignature { expected_sig, ret_ty }; + decl_inputs: &[Option], + decl_output: Option, + expected_sig: PolyFnSig<'db>, + ) -> ClosureSignatures<'db> { + // Watch out for some surprises and just ignore the + // expectation if things don't see to match up with what we + // expect. + if expected_sig.c_variadic() { + return self.sig_of_closure_no_expectation(decl_inputs, decl_output); + } else if expected_sig.skip_binder().inputs_and_output.len() != decl_inputs.len() + 1 { + return self + .sig_of_closure_with_mismatched_number_of_arguments(decl_inputs, decl_output); } - self.merge_supplied_sig_with_expectation( - body, - ret_type, - arg_types, - closure_kind, - expected_sig, - ) + // Create a `PolyFnSig`. Note the oddity that late bound + // regions appearing free in `expected_sig` are now bound up + // in this binder we are creating. + assert!(!expected_sig.skip_binder().has_vars_bound_above(rustc_type_ir::INNERMOST)); + let bound_sig = expected_sig.map_bound(|sig| { + self.table.interner.mk_fn_sig( + sig.inputs(), + sig.output(), + sig.c_variadic, + Safety::Safe, + FnAbi::RustCall, + ) + }); + + // `deduce_expectations_from_expected_type` introduces + // late-bound lifetimes defined elsewhere, which we now + // anonymize away, so as not to confuse the user. + let bound_sig = self.table.interner.anonymize_bound_vars(bound_sig); + + let closure_sigs = self.closure_sigs(bound_sig); + + // Up till this point, we have ignored the annotations that the user + // gave. This function will check that they unify successfully. + // Along the way, it also writes out entries for types that the user + // wrote into our typeck results, which are then later used by the privacy + // check. + match self.merge_supplied_sig_with_expectation(decl_inputs, decl_output, closure_sigs) { + Ok(infer_ok) => self.table.register_infer_ok(infer_ok), + Err(_) => self.sig_of_closure_no_expectation(decl_inputs, decl_output), + } } + fn sig_of_closure_with_mismatched_number_of_arguments( + &mut self, + decl_inputs: &[Option], + decl_output: Option, + ) -> ClosureSignatures<'db> { + let error_sig = self.error_sig_of_closure(decl_inputs, decl_output); + + self.closure_sigs(error_sig) + } + + /// Enforce the user's types against the expectation. See + /// `sig_of_closure_with_expectation` for details on the overall + /// strategy. fn merge_supplied_sig_with_expectation( &mut self, - body: &ExprId, - ret_type: &Option, - arg_types: &[Option], - closure_kind: ClosureKind, - expected_sig: FnPointer, - ) -> ClosureSignature { - let supplied_sig = self.supplied_sig_of_closure(body, ret_type, arg_types, closure_kind); + decl_inputs: &[Option], + decl_output: Option, + mut expected_sigs: ClosureSignatures<'db>, + ) -> InferResult<'db, ClosureSignatures<'db>> { + // Get the signature S that the user gave. + // + // (See comment on `sig_of_closure_with_expectation` for the + // meaning of these letters.) + let supplied_sig = self.supplied_sig_of_closure(decl_inputs, decl_output); - let snapshot = self.table.snapshot(); - if !self.table.unify::<_, crate::next_solver::GenericArgs<'_>>( - &expected_sig.substitution.0, - &supplied_sig.expected_sig.substitution.0, - ) { - self.table.rollback_to(snapshot); - } + debug!(?supplied_sig); - supplied_sig - } -} - -// The below functions handle capture and closure kind (Fn, FnMut, ..) - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub(crate) struct HirPlace { - pub(crate) local: BindingId, - pub(crate) projections: Vec>, -} - -impl HirPlace { - fn ty(&self, ctx: &mut InferenceContext<'_>) -> Ty { - let mut ty = ctx.table.resolve_completely(ctx.result[self.local].clone()); - for p in &self.projections { - ty = p.projected_ty( - ty, - ctx.db, - |_, _, _| { - unreachable!("Closure field only happens in MIR"); - }, - ctx.owner.module(ctx.db).krate(), + // FIXME(#45727): As discussed in [this comment][c1], naively + // forcing equality here actually results in suboptimal error + // messages in some cases. For now, if there would have been + // an obvious error, we fallback to declaring the type of the + // closure to be the one the user gave, which allows other + // error message code to trigger. + // + // However, I think [there is potential to do even better + // here][c2], since in *this* code we have the precise span of + // the type parameter in question in hand when we report the + // error. + // + // [c1]: https://github.com/rust-lang/rust/pull/45072#issuecomment-341089706 + // [c2]: https://github.com/rust-lang/rust/pull/45072#issuecomment-341096796 + self.table.commit_if_ok(|table| { + let mut all_obligations = PredicateObligations::new(); + let supplied_sig = table.infer_ctxt.instantiate_binder_with_fresh_vars( + BoundRegionConversionTime::FnCall, + supplied_sig, ); - } - ty - } - fn capture_kind_of_truncated_place( - &self, - mut current_capture: CaptureKind, - len: usize, - ) -> CaptureKind { - if let CaptureKind::ByRef(BorrowKind::Mut { - kind: MutBorrowKind::Default | MutBorrowKind::TwoPhasedBorrow, - }) = current_capture - && self.projections[len..].contains(&ProjectionElem::Deref) - { - current_capture = - CaptureKind::ByRef(BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }); - } - current_capture - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub enum CaptureKind { - ByRef(BorrowKind), - ByValue, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct CapturedItem { - pub(crate) place: HirPlace, - pub(crate) kind: CaptureKind, - /// The inner vec is the stacks; the outer vec is for each capture reference. - /// - /// Even though we always report only the last span (i.e. the most inclusive span), - /// we need to keep them all, since when a closure occurs inside a closure, we - /// copy all captures of the inner closure to the outer closure, and then we may - /// truncate them, and we want the correct span to be reported. - span_stacks: SmallVec<[SmallVec<[MirSpan; 3]>; 3]>, - pub(crate) ty: Binders, -} - -impl CapturedItem { - pub fn local(&self) -> BindingId { - self.place.local - } - - /// Returns whether this place has any field (aka. non-deref) projections. - pub fn has_field_projections(&self) -> bool { - self.place.projections.iter().any(|it| !matches!(it, ProjectionElem::Deref)) - } - - pub fn ty(&self, subst: &Substitution) -> Ty { - self.ty.clone().substitute(Interner, utils::ClosureSubst(subst).parent_subst()) - } - - pub fn kind(&self) -> CaptureKind { - self.kind - } - - pub fn spans(&self) -> SmallVec<[MirSpan; 3]> { - self.span_stacks.iter().map(|stack| *stack.last().expect("empty span stack")).collect() - } - - /// Converts the place to a name that can be inserted into source code. - pub fn place_to_name(&self, owner: DefWithBodyId, db: &dyn HirDatabase) -> String { - let body = db.body(owner); - let mut result = body[self.place.local].name.as_str().to_owned(); - for proj in &self.place.projections { - match proj { - ProjectionElem::Deref => {} - ProjectionElem::Field(Either::Left(f)) => { - let variant_data = f.parent.fields(db); - match variant_data.shape { - FieldsShape::Record => { - result.push('_'); - result.push_str(variant_data.fields()[f.local_id].name.as_str()) - } - FieldsShape::Tuple => { - let index = - variant_data.fields().iter().position(|it| it.0 == f.local_id); - if let Some(index) = index { - format_to!(result, "_{index}"); - } - } - FieldsShape::Unit => {} - } - } - ProjectionElem::Field(Either::Right(f)) => format_to!(result, "_{}", f.index), - &ProjectionElem::ClosureField(field) => format_to!(result, "_{field}"), - ProjectionElem::Index(_) - | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subslice { .. } - | ProjectionElem::OpaqueCast(_) => { - never!("Not happen in closure capture"); - continue; - } + // The liberated version of this signature should be a subtype + // of the liberated form of the expectation. + for (supplied_ty, expected_ty) in + iter::zip(supplied_sig.inputs(), expected_sigs.liberated_sig.inputs()) + { + // Check that E' = S'. + let cause = ObligationCause::new(); + let InferOk { value: (), obligations } = table + .infer_ctxt + .at(&cause, table.param_env) + .eq(DefineOpaqueTypes::Yes, expected_ty, supplied_ty)?; + all_obligations.extend(obligations); } - } - if is_raw_identifier(&result, owner.module(db).krate().data(db).edition) { - result.insert_str(0, "r#"); - } - result - } - pub fn display_place_source_code(&self, owner: DefWithBodyId, db: &dyn HirDatabase) -> String { - let body = db.body(owner); - let krate = owner.krate(db); - let edition = krate.data(db).edition; - let mut result = body[self.place.local].name.display(db, edition).to_string(); - for proj in &self.place.projections { - match proj { - // In source code autoderef kicks in. - ProjectionElem::Deref => {} - ProjectionElem::Field(Either::Left(f)) => { - let variant_data = f.parent.fields(db); - match variant_data.shape { - FieldsShape::Record => format_to!( - result, - ".{}", - variant_data.fields()[f.local_id].name.display(db, edition) - ), - FieldsShape::Tuple => format_to!( - result, - ".{}", - variant_data - .fields() - .iter() - .position(|it| it.0 == f.local_id) - .unwrap_or_default() - ), - FieldsShape::Unit => {} - } - } - ProjectionElem::Field(Either::Right(f)) => { - let field = f.index; - format_to!(result, ".{field}"); - } - &ProjectionElem::ClosureField(field) => { - format_to!(result, ".{field}"); - } - ProjectionElem::Index(_) - | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subslice { .. } - | ProjectionElem::OpaqueCast(_) => { - never!("Not happen in closure capture"); - continue; - } - } - } - let final_derefs_count = self - .place - .projections - .iter() - .rev() - .take_while(|proj| matches!(proj, ProjectionElem::Deref)) - .count(); - result.insert_str(0, &"*".repeat(final_derefs_count)); - result - } + let supplied_output_ty = supplied_sig.output(); + let cause = ObligationCause::new(); + let InferOk { value: (), obligations } = + table.infer_ctxt.at(&cause, table.param_env).eq( + DefineOpaqueTypes::Yes, + expected_sigs.liberated_sig.output(), + supplied_output_ty, + )?; + all_obligations.extend(obligations); - pub fn display_place(&self, owner: DefWithBodyId, db: &dyn HirDatabase) -> String { - let body = db.body(owner); - let krate = owner.krate(db); - let edition = krate.data(db).edition; - let mut result = body[self.place.local].name.display(db, edition).to_string(); - let mut field_need_paren = false; - for proj in &self.place.projections { - match proj { - ProjectionElem::Deref => { - result = format!("*{result}"); - field_need_paren = true; - } - ProjectionElem::Field(Either::Left(f)) => { - if field_need_paren { - result = format!("({result})"); - } - let variant_data = f.parent.fields(db); - let field = match variant_data.shape { - FieldsShape::Record => { - variant_data.fields()[f.local_id].name.as_str().to_owned() - } - FieldsShape::Tuple => variant_data - .fields() - .iter() - .position(|it| it.0 == f.local_id) - .unwrap_or_default() - .to_string(), - FieldsShape::Unit => "[missing field]".to_owned(), - }; - result = format!("{result}.{field}"); - field_need_paren = false; - } - ProjectionElem::Field(Either::Right(f)) => { - let field = f.index; - if field_need_paren { - result = format!("({result})"); - } - result = format!("{result}.{field}"); - field_need_paren = false; - } - &ProjectionElem::ClosureField(field) => { - if field_need_paren { - result = format!("({result})"); - } - result = format!("{result}.{field}"); - field_need_paren = false; - } - ProjectionElem::Index(_) - | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subslice { .. } - | ProjectionElem::OpaqueCast(_) => { - never!("Not happen in closure capture"); - continue; - } - } - } - result - } -} + let inputs = supplied_sig + .inputs() + .into_iter() + .map(|ty| table.infer_ctxt.resolve_vars_if_possible(ty)); -#[derive(Debug, Clone, PartialEq, Eq)] -pub(crate) struct CapturedItemWithoutTy { - pub(crate) place: HirPlace, - pub(crate) kind: CaptureKind, - /// The inner vec is the stacks; the outer vec is for each capture reference. - pub(crate) span_stacks: SmallVec<[SmallVec<[MirSpan; 3]>; 3]>, -} + expected_sigs.liberated_sig = table.interner.mk_fn_sig( + inputs, + supplied_output_ty, + expected_sigs.liberated_sig.c_variadic, + Safety::Safe, + FnAbi::RustCall, + ); -impl CapturedItemWithoutTy { - fn with_ty(self, ctx: &mut InferenceContext<'_>) -> CapturedItem { - let ty = self.place.ty(ctx); - let ty = match &self.kind { - CaptureKind::ByValue => ty, - CaptureKind::ByRef(bk) => { - let m = match bk { - BorrowKind::Mut { .. } => Mutability::Mut, - _ => Mutability::Not, - }; - TyKind::Ref(m, error_lifetime(), ty).intern(Interner) - } - }; - return CapturedItem { - place: self.place, - kind: self.kind, - span_stacks: self.span_stacks, - ty: replace_placeholder_with_binder(ctx, ty), - }; - - fn replace_placeholder_with_binder(ctx: &mut InferenceContext<'_>, ty: Ty) -> Binders { - struct Filler<'a> { - db: &'a dyn HirDatabase, - generics: &'a Generics, - } - impl FallibleTypeFolder for Filler<'_> { - type Error = (); - - fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder { - self - } - - fn interner(&self) -> Interner { - Interner - } - - fn try_fold_free_placeholder_const( - &mut self, - ty: chalk_ir::Ty, - idx: chalk_ir::PlaceholderIndex, - outer_binder: DebruijnIndex, - ) -> Result, Self::Error> { - let x = from_placeholder_idx(self.db, idx).0; - let Some(idx) = self.generics.type_or_const_param_idx(x) else { - return Err(()); - }; - Ok(BoundVar::new(outer_binder, idx).to_const(Interner, ty)) - } - - fn try_fold_free_placeholder_ty( - &mut self, - idx: chalk_ir::PlaceholderIndex, - outer_binder: DebruijnIndex, - ) -> std::result::Result { - let x = from_placeholder_idx(self.db, idx).0; - let Some(idx) = self.generics.type_or_const_param_idx(x) else { - return Err(()); - }; - Ok(BoundVar::new(outer_binder, idx).to_ty(Interner)) - } - } - let filler = &mut Filler { db: ctx.db, generics: ctx.generics() }; - let result = ty.clone().try_fold_with(filler, DebruijnIndex::INNERMOST).unwrap_or(ty); - make_binders(ctx.db, filler.generics, result) - } - } -} - -impl InferenceContext<'_> { - fn place_of_expr(&mut self, tgt_expr: ExprId) -> Option { - let r = self.place_of_expr_without_adjust(tgt_expr)?; - let adjustments = - self.result.expr_adjustments.get(&tgt_expr).map(|it| &**it).unwrap_or_default(); - apply_adjusts_to_place(&mut self.current_capture_span_stack, r, adjustments) - } - - /// Pushes the span into `current_capture_span_stack`, *without clearing it first*. - fn path_place(&mut self, path: &Path, id: ExprOrPatId) -> Option { - if path.type_anchor().is_some() { - return None; - } - let hygiene = self.body.expr_or_pat_path_hygiene(id); - self.resolver.resolve_path_in_value_ns_fully(self.db, path, hygiene).and_then(|result| { - match result { - ValueNs::LocalBinding(binding) => { - let mir_span = match id { - ExprOrPatId::ExprId(id) => MirSpan::ExprId(id), - ExprOrPatId::PatId(id) => MirSpan::PatId(id), - }; - self.current_capture_span_stack.push(mir_span); - Some(HirPlace { local: binding, projections: Vec::new() }) - } - _ => None, - } + Ok(InferOk { value: expected_sigs, obligations: all_obligations }) }) } - /// Changes `current_capture_span_stack` to contain the stack of spans for this expr. - fn place_of_expr_without_adjust(&mut self, tgt_expr: ExprId) -> Option { - self.current_capture_span_stack.clear(); - match &self.body[tgt_expr] { - Expr::Path(p) => { - let resolver_guard = - self.resolver.update_to_inner_scope(self.db, self.owner, tgt_expr); - let result = self.path_place(p, tgt_expr.into()); - self.resolver.reset_to_guard(resolver_guard); - return result; - } - Expr::Field { expr, name: _ } => { - let mut place = self.place_of_expr(*expr)?; - let field = self.result.field_resolution(tgt_expr)?; - self.current_capture_span_stack.push(MirSpan::ExprId(tgt_expr)); - place.projections.push(ProjectionElem::Field(field)); - return Some(place); - } - Expr::UnaryOp { expr, op: UnaryOp::Deref } => { - if matches!( - self.expr_ty_after_adjustments(*expr).kind(Interner), - TyKind::Ref(..) | TyKind::Raw(..) - ) { - let mut place = self.place_of_expr(*expr)?; - self.current_capture_span_stack.push(MirSpan::ExprId(tgt_expr)); - place.projections.push(ProjectionElem::Deref); - return Some(place); - } - } - _ => (), - } - None - } - - fn push_capture(&mut self, place: HirPlace, kind: CaptureKind) { - self.current_captures.push(CapturedItemWithoutTy { - place, - kind, - span_stacks: smallvec![self.current_capture_span_stack.iter().copied().collect()], - }); - } - - fn truncate_capture_spans(&self, capture: &mut CapturedItemWithoutTy, mut truncate_to: usize) { - // The first span is the identifier, and it must always remain. - truncate_to += 1; - for span_stack in &mut capture.span_stacks { - let mut remained = truncate_to; - let mut actual_truncate_to = 0; - for &span in &*span_stack { - actual_truncate_to += 1; - if !span.is_ref_span(self.body) { - remained -= 1; - if remained == 0 { - break; - } - } - } - if actual_truncate_to < span_stack.len() - && span_stack[actual_truncate_to].is_ref_span(self.body) - { - // Include the ref operator if there is one, we will fix it later (in `strip_captures_ref_span()`) if it's incorrect. - actual_truncate_to += 1; - } - span_stack.truncate(actual_truncate_to); - } - } - - fn ref_expr(&mut self, expr: ExprId, place: Option) { - if let Some(place) = place { - self.add_capture(place, CaptureKind::ByRef(BorrowKind::Shared)); - } - self.walk_expr(expr); - } - - fn add_capture(&mut self, place: HirPlace, kind: CaptureKind) { - if self.is_upvar(&place) { - self.push_capture(place, kind); - } - } - - fn mutate_path_pat(&mut self, path: &Path, id: PatId) { - if let Some(place) = self.path_place(path, id.into()) { - self.add_capture( - place, - CaptureKind::ByRef(BorrowKind::Mut { kind: MutBorrowKind::Default }), - ); - self.current_capture_span_stack.pop(); // Remove the pattern span. - } - } - - fn mutate_expr(&mut self, expr: ExprId, place: Option) { - if let Some(place) = place { - self.add_capture( - place, - CaptureKind::ByRef(BorrowKind::Mut { kind: MutBorrowKind::Default }), - ); - } - self.walk_expr(expr); - } - - fn consume_expr(&mut self, expr: ExprId) { - if let Some(place) = self.place_of_expr(expr) { - self.consume_place(place); - } - self.walk_expr(expr); - } - - fn consume_place(&mut self, place: HirPlace) { - if self.is_upvar(&place) { - let ty = place.ty(self); - let kind = if self.is_ty_copy(ty) { - CaptureKind::ByRef(BorrowKind::Shared) - } else { - CaptureKind::ByValue - }; - self.push_capture(place, kind); - } - } - - fn walk_expr_with_adjust(&mut self, tgt_expr: ExprId, adjustment: &[Adjustment]) { - if let Some((last, rest)) = adjustment.split_last() { - match &last.kind { - Adjust::NeverToAny | Adjust::Deref(None) | Adjust::Pointer(_) => { - self.walk_expr_with_adjust(tgt_expr, rest) - } - Adjust::Deref(Some(m)) => match m.0 { - Some(m) => { - self.ref_capture_with_adjusts(m, tgt_expr, rest); - } - None => unreachable!(), - }, - Adjust::Borrow(b) => { - self.ref_capture_with_adjusts(b.mutability(), tgt_expr, rest); - } - } - } else { - self.walk_expr_without_adjust(tgt_expr); - } - } - - fn ref_capture_with_adjusts(&mut self, m: Mutability, tgt_expr: ExprId, rest: &[Adjustment]) { - let capture_kind = match m { - Mutability::Mut => CaptureKind::ByRef(BorrowKind::Mut { kind: MutBorrowKind::Default }), - Mutability::Not => CaptureKind::ByRef(BorrowKind::Shared), - }; - if let Some(place) = self.place_of_expr_without_adjust(tgt_expr) - && let Some(place) = - apply_adjusts_to_place(&mut self.current_capture_span_stack, place, rest) - { - self.add_capture(place, capture_kind); - } - self.walk_expr_with_adjust(tgt_expr, rest); - } - - fn walk_expr(&mut self, tgt_expr: ExprId) { - if let Some(it) = self.result.expr_adjustments.get_mut(&tgt_expr) { - // FIXME: this take is completely unneeded, and just is here to make borrow checker - // happy. Remove it if you can. - let x_taken = mem::take(it); - self.walk_expr_with_adjust(tgt_expr, &x_taken); - *self.result.expr_adjustments.get_mut(&tgt_expr).unwrap() = x_taken; - } else { - self.walk_expr_without_adjust(tgt_expr); - } - } - - fn walk_expr_without_adjust(&mut self, tgt_expr: ExprId) { - match &self.body[tgt_expr] { - Expr::OffsetOf(_) => (), - Expr::InlineAsm(e) => e.operands.iter().for_each(|(_, op)| match op { - AsmOperand::In { expr, .. } - | AsmOperand::Out { expr: Some(expr), .. } - | AsmOperand::InOut { expr, .. } => self.walk_expr_without_adjust(*expr), - AsmOperand::SplitInOut { in_expr, out_expr, .. } => { - self.walk_expr_without_adjust(*in_expr); - if let Some(out_expr) = out_expr { - self.walk_expr_without_adjust(*out_expr); - } - } - AsmOperand::Out { expr: None, .. } - | AsmOperand::Const(_) - | AsmOperand::Label(_) - | AsmOperand::Sym(_) => (), - }), - Expr::If { condition, then_branch, else_branch } => { - self.consume_expr(*condition); - self.consume_expr(*then_branch); - if let &Some(expr) = else_branch { - self.consume_expr(expr); - } - } - Expr::Async { statements, tail, .. } - | Expr::Unsafe { statements, tail, .. } - | Expr::Block { statements, tail, .. } => { - for s in statements.iter() { - match s { - Statement::Let { pat, type_ref: _, initializer, else_branch } => { - if let Some(else_branch) = else_branch { - self.consume_expr(*else_branch); - } - if let Some(initializer) = initializer { - if else_branch.is_some() { - self.consume_expr(*initializer); - } else { - self.walk_expr(*initializer); - } - if let Some(place) = self.place_of_expr(*initializer) { - self.consume_with_pat(place, *pat); - } - } - } - Statement::Expr { expr, has_semi: _ } => { - self.consume_expr(*expr); - } - Statement::Item(_) => (), - } - } - if let Some(tail) = tail { - self.consume_expr(*tail); - } - } - Expr::Call { callee, args } => { - self.consume_expr(*callee); - self.consume_exprs(args.iter().copied()); - } - Expr::MethodCall { receiver, args, .. } => { - self.consume_expr(*receiver); - self.consume_exprs(args.iter().copied()); - } - Expr::Match { expr, arms } => { - for arm in arms.iter() { - self.consume_expr(arm.expr); - if let Some(guard) = arm.guard { - self.consume_expr(guard); - } - } - self.walk_expr(*expr); - if let Some(discr_place) = self.place_of_expr(*expr) - && self.is_upvar(&discr_place) - { - let mut capture_mode = None; - for arm in arms.iter() { - self.walk_pat(&mut capture_mode, arm.pat); - } - if let Some(c) = capture_mode { - self.push_capture(discr_place, c); - } - } - } - Expr::Break { expr, label: _ } - | Expr::Return { expr } - | Expr::Yield { expr } - | Expr::Yeet { expr } => { - if let &Some(expr) = expr { - self.consume_expr(expr); - } - } - &Expr::Become { expr } => { - self.consume_expr(expr); - } - Expr::RecordLit { fields, spread, .. } => { - if let &Some(expr) = spread { - self.consume_expr(expr); - } - self.consume_exprs(fields.iter().map(|it| it.expr)); - } - Expr::Field { expr, name: _ } => self.select_from_expr(*expr), - Expr::UnaryOp { expr, op: UnaryOp::Deref } => { - if matches!( - self.expr_ty_after_adjustments(*expr).kind(Interner), - TyKind::Ref(..) | TyKind::Raw(..) - ) { - self.select_from_expr(*expr); - } else if let Some((f, _)) = self.result.method_resolution(tgt_expr) { - let mutability = 'b: { - if let Some(deref_trait) = - self.resolve_lang_item(LangItem::DerefMut).and_then(|it| it.as_trait()) - && let Some(deref_fn) = deref_trait - .trait_items(self.db) - .method_by_name(&Name::new_symbol_root(sym::deref_mut)) - { - break 'b deref_fn == f; - } - false - }; - let place = self.place_of_expr(*expr); - if mutability { - self.mutate_expr(*expr, place); - } else { - self.ref_expr(*expr, place); - } - } else { - self.select_from_expr(*expr); - } - } - Expr::Let { pat, expr } => { - self.walk_expr(*expr); - if let Some(place) = self.place_of_expr(*expr) { - self.consume_with_pat(place, *pat); - } - } - Expr::UnaryOp { expr, op: _ } - | Expr::Array(Array::Repeat { initializer: expr, repeat: _ }) - | Expr::Await { expr } - | Expr::Loop { body: expr, label: _ } - | Expr::Box { expr } - | Expr::Cast { expr, type_ref: _ } => { - self.consume_expr(*expr); - } - Expr::Ref { expr, rawness: _, mutability } => { - // We need to do this before we push the span so the order will be correct. - let place = self.place_of_expr(*expr); - self.current_capture_span_stack.push(MirSpan::ExprId(tgt_expr)); - match mutability { - hir_def::type_ref::Mutability::Shared => self.ref_expr(*expr, place), - hir_def::type_ref::Mutability::Mut => self.mutate_expr(*expr, place), - } - } - Expr::BinaryOp { lhs, rhs, op } => { - let Some(op) = op else { - return; - }; - if matches!(op, BinaryOp::Assignment { .. }) { - let place = self.place_of_expr(*lhs); - self.mutate_expr(*lhs, place); - self.consume_expr(*rhs); - return; - } - self.consume_expr(*lhs); - self.consume_expr(*rhs); - } - Expr::Range { lhs, rhs, range_type: _ } => { - if let &Some(expr) = lhs { - self.consume_expr(expr); - } - if let &Some(expr) = rhs { - self.consume_expr(expr); - } - } - Expr::Index { base, index } => { - self.select_from_expr(*base); - self.consume_expr(*index); - } - Expr::Closure { .. } => { - let ty = self.expr_ty(tgt_expr); - let TyKind::Closure(id, _) = ty.kind(Interner) else { - never!("closure type is always closure"); - return; - }; - let (captures, _) = - self.result.closure_info.get(id).expect( - "We sort closures, so we should always have data for inner closures", - ); - let mut cc = mem::take(&mut self.current_captures); - cc.extend(captures.iter().filter(|it| self.is_upvar(&it.place)).map(|it| { - CapturedItemWithoutTy { - place: it.place.clone(), - kind: it.kind, - span_stacks: it.span_stacks.clone(), - } - })); - self.current_captures = cc; - } - Expr::Array(Array::ElementList { elements: exprs }) | Expr::Tuple { exprs } => { - self.consume_exprs(exprs.iter().copied()) - } - &Expr::Assignment { target, value } => { - self.walk_expr(value); - let resolver_guard = - self.resolver.update_to_inner_scope(self.db, self.owner, tgt_expr); - match self.place_of_expr(value) { - Some(rhs_place) => { - self.inside_assignment = true; - self.consume_with_pat(rhs_place, target); - self.inside_assignment = false; - } - None => self.body.walk_pats(target, &mut |pat| match &self.body[pat] { - Pat::Path(path) => self.mutate_path_pat(path, pat), - &Pat::Expr(expr) => { - let place = self.place_of_expr(expr); - self.mutate_expr(expr, place); - } - _ => {} - }), - } - self.resolver.reset_to_guard(resolver_guard); - } - - Expr::Missing - | Expr::Continue { .. } - | Expr::Path(_) - | Expr::Literal(_) - | Expr::Const(_) - | Expr::Underscore => (), - } - } - - fn walk_pat(&mut self, result: &mut Option, pat: PatId) { - let mut update_result = |ck: CaptureKind| match result { - Some(r) => { - *r = cmp::max(*r, ck); - } - None => *result = Some(ck), - }; - - self.walk_pat_inner( - pat, - &mut update_result, - BorrowKind::Mut { kind: MutBorrowKind::Default }, - ); - } - - fn walk_pat_inner( - &mut self, - p: PatId, - update_result: &mut impl FnMut(CaptureKind), - mut for_mut: BorrowKind, - ) { - match &self.body[p] { - Pat::Ref { .. } - | Pat::Box { .. } - | Pat::Missing - | Pat::Wild - | Pat::Tuple { .. } - | Pat::Expr(_) - | Pat::Or(_) => (), - Pat::TupleStruct { .. } | Pat::Record { .. } => { - if let Some(variant) = self.result.variant_resolution_for_pat(p) { - let adt = variant.adt_id(self.db); - let is_multivariant = match adt { - hir_def::AdtId::EnumId(e) => e.enum_variants(self.db).variants.len() != 1, - _ => false, - }; - if is_multivariant { - update_result(CaptureKind::ByRef(BorrowKind::Shared)); - } - } - } - Pat::Slice { .. } - | Pat::ConstBlock(_) - | Pat::Path(_) - | Pat::Lit(_) - | Pat::Range { .. } => { - update_result(CaptureKind::ByRef(BorrowKind::Shared)); - } - Pat::Bind { id, .. } => match self.result.binding_modes[p] { - crate::BindingMode::Move => { - if self.is_ty_copy(self.result.type_of_binding[*id].clone()) { - update_result(CaptureKind::ByRef(BorrowKind::Shared)); - } else { - update_result(CaptureKind::ByValue); - } - } - crate::BindingMode::Ref(r) => match r { - Mutability::Mut => update_result(CaptureKind::ByRef(for_mut)), - Mutability::Not => update_result(CaptureKind::ByRef(BorrowKind::Shared)), - }, - }, - } - if self.result.pat_adjustments.get(&p).is_some_and(|it| !it.is_empty()) { - for_mut = BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }; - } - self.body.walk_pats_shallow(p, |p| self.walk_pat_inner(p, update_result, for_mut)); - } - - fn expr_ty(&self, expr: ExprId) -> Ty { - self.result[expr].clone() - } - - fn expr_ty_after_adjustments(&self, e: ExprId) -> Ty { - let mut ty = None; - if let Some(it) = self.result.expr_adjustments.get(&e) - && let Some(it) = it.last() - { - ty = Some(it.target.clone()); - } - ty.unwrap_or_else(|| self.expr_ty(e)) - } - - fn is_upvar(&self, place: &HirPlace) -> bool { - if let Some(c) = self.current_closure { - let InternedClosure(_, root) = self.db.lookup_intern_closure(c.into()); - return self.body.is_binding_upvar(place.local, root); - } - false - } - - fn is_ty_copy(&mut self, ty: Ty) -> bool { - if let TyKind::Closure(id, _) = ty.kind(Interner) { - // FIXME: We handle closure as a special case, since chalk consider every closure as copy. We - // should probably let chalk know which closures are copy, but I don't know how doing it - // without creating query cycles. - return self.result.closure_info.get(id).map(|it| it.1 == FnTrait::Fn).unwrap_or(true); - } - self.table.resolve_completely(ty).is_copy(self.db, self.owner) - } - - fn select_from_expr(&mut self, expr: ExprId) { - self.walk_expr(expr); - } - - fn restrict_precision_for_unsafe(&mut self) { - // FIXME: Borrow checker problems without this. - let mut current_captures = std::mem::take(&mut self.current_captures); - for capture in &mut current_captures { - let mut ty = self.table.resolve_completely(self.result[capture.place.local].clone()); - if ty.as_raw_ptr().is_some() || ty.is_union() { - capture.kind = CaptureKind::ByRef(BorrowKind::Shared); - self.truncate_capture_spans(capture, 0); - capture.place.projections.truncate(0); - continue; - } - for (i, p) in capture.place.projections.iter().enumerate() { - ty = p.projected_ty( - ty, - self.db, - |_, _, _| { - unreachable!("Closure field only happens in MIR"); - }, - self.owner.module(self.db).krate(), - ); - if ty.as_raw_ptr().is_some() || ty.is_union() { - capture.kind = CaptureKind::ByRef(BorrowKind::Shared); - self.truncate_capture_spans(capture, i + 1); - capture.place.projections.truncate(i + 1); - break; - } - } - } - self.current_captures = current_captures; - } - - fn adjust_for_move_closure(&mut self) { - // FIXME: Borrow checker won't allow without this. - let mut current_captures = std::mem::take(&mut self.current_captures); - for capture in &mut current_captures { - if let Some(first_deref) = - capture.place.projections.iter().position(|proj| *proj == ProjectionElem::Deref) - { - self.truncate_capture_spans(capture, first_deref); - capture.place.projections.truncate(first_deref); - } - capture.kind = CaptureKind::ByValue; - } - self.current_captures = current_captures; - } - - fn minimize_captures(&mut self) { - self.current_captures.sort_unstable_by_key(|it| it.place.projections.len()); - let mut hash_map = FxHashMap::::default(); - let result = mem::take(&mut self.current_captures); - for mut item in result { - let mut lookup_place = HirPlace { local: item.place.local, projections: vec![] }; - let mut it = item.place.projections.iter(); - let prev_index = loop { - if let Some(k) = hash_map.get(&lookup_place) { - break Some(*k); - } - match it.next() { - Some(it) => { - lookup_place.projections.push(it.clone()); - } - None => break None, - } - }; - match prev_index { - Some(p) => { - let prev_projections_len = self.current_captures[p].place.projections.len(); - self.truncate_capture_spans(&mut item, prev_projections_len); - self.current_captures[p].span_stacks.extend(item.span_stacks); - let len = self.current_captures[p].place.projections.len(); - let kind_after_truncate = - item.place.capture_kind_of_truncated_place(item.kind, len); - self.current_captures[p].kind = - cmp::max(kind_after_truncate, self.current_captures[p].kind); - } - None => { - hash_map.insert(item.place.clone(), self.current_captures.len()); - self.current_captures.push(item); - } - } - } - } - - fn consume_with_pat(&mut self, mut place: HirPlace, tgt_pat: PatId) { - let adjustments_count = - self.result.pat_adjustments.get(&tgt_pat).map(|it| it.len()).unwrap_or_default(); - place.projections.extend((0..adjustments_count).map(|_| ProjectionElem::Deref)); - self.current_capture_span_stack - .extend((0..adjustments_count).map(|_| MirSpan::PatId(tgt_pat))); - 'reset_span_stack: { - match &self.body[tgt_pat] { - Pat::Missing | Pat::Wild => (), - Pat::Tuple { args, ellipsis } => { - let (al, ar) = args.split_at(ellipsis.map_or(args.len(), |it| it as usize)); - let field_count = match self.result[tgt_pat].kind(Interner) { - TyKind::Tuple(_, s) => s.len(Interner), - _ => break 'reset_span_stack, - }; - let fields = 0..field_count; - let it = al.iter().zip(fields.clone()).chain(ar.iter().rev().zip(fields.rev())); - for (&arg, i) in it { - let mut p = place.clone(); - self.current_capture_span_stack.push(MirSpan::PatId(arg)); - p.projections.push(ProjectionElem::Field(Either::Right(TupleFieldId { - tuple: TupleId(!0), // dummy this, as its unused anyways - index: i as u32, - }))); - self.consume_with_pat(p, arg); - self.current_capture_span_stack.pop(); - } - } - Pat::Or(pats) => { - for pat in pats.iter() { - self.consume_with_pat(place.clone(), *pat); - } - } - Pat::Record { args, .. } => { - let Some(variant) = self.result.variant_resolution_for_pat(tgt_pat) else { - break 'reset_span_stack; - }; - match variant { - VariantId::EnumVariantId(_) | VariantId::UnionId(_) => { - self.consume_place(place) - } - VariantId::StructId(s) => { - let vd = s.fields(self.db); - for field_pat in args.iter() { - let arg = field_pat.pat; - let Some(local_id) = vd.field(&field_pat.name) else { - continue; - }; - let mut p = place.clone(); - self.current_capture_span_stack.push(MirSpan::PatId(arg)); - p.projections.push(ProjectionElem::Field(Either::Left(FieldId { - parent: variant, - local_id, - }))); - self.consume_with_pat(p, arg); - self.current_capture_span_stack.pop(); - } - } - } - } - Pat::Range { .. } | Pat::Slice { .. } | Pat::ConstBlock(_) | Pat::Lit(_) => { - self.consume_place(place) - } - Pat::Path(path) => { - if self.inside_assignment { - self.mutate_path_pat(path, tgt_pat); - } - self.consume_place(place); - } - &Pat::Bind { id, subpat: _ } => { - let mode = self.result.binding_modes[tgt_pat]; - let capture_kind = match mode { - BindingMode::Move => { - self.consume_place(place); - break 'reset_span_stack; - } - BindingMode::Ref(Mutability::Not) => BorrowKind::Shared, - BindingMode::Ref(Mutability::Mut) => { - BorrowKind::Mut { kind: MutBorrowKind::Default } - } - }; - self.current_capture_span_stack.push(MirSpan::BindingId(id)); - self.add_capture(place, CaptureKind::ByRef(capture_kind)); - self.current_capture_span_stack.pop(); - } - Pat::TupleStruct { path: _, args, ellipsis } => { - let Some(variant) = self.result.variant_resolution_for_pat(tgt_pat) else { - break 'reset_span_stack; - }; - match variant { - VariantId::EnumVariantId(_) | VariantId::UnionId(_) => { - self.consume_place(place) - } - VariantId::StructId(s) => { - let vd = s.fields(self.db); - let (al, ar) = - args.split_at(ellipsis.map_or(args.len(), |it| it as usize)); - let fields = vd.fields().iter(); - let it = al - .iter() - .zip(fields.clone()) - .chain(ar.iter().rev().zip(fields.rev())); - for (&arg, (i, _)) in it { - let mut p = place.clone(); - self.current_capture_span_stack.push(MirSpan::PatId(arg)); - p.projections.push(ProjectionElem::Field(Either::Left(FieldId { - parent: variant, - local_id: i, - }))); - self.consume_with_pat(p, arg); - self.current_capture_span_stack.pop(); - } - } - } - } - Pat::Ref { pat, mutability: _ } => { - self.current_capture_span_stack.push(MirSpan::PatId(tgt_pat)); - place.projections.push(ProjectionElem::Deref); - self.consume_with_pat(place, *pat); - self.current_capture_span_stack.pop(); - } - Pat::Box { .. } => (), // not supported - &Pat::Expr(expr) => { - self.consume_place(place); - let pat_capture_span_stack = mem::take(&mut self.current_capture_span_stack); - let old_inside_assignment = mem::replace(&mut self.inside_assignment, false); - let lhs_place = self.place_of_expr(expr); - self.mutate_expr(expr, lhs_place); - self.inside_assignment = old_inside_assignment; - self.current_capture_span_stack = pat_capture_span_stack; - } - } - } - self.current_capture_span_stack - .truncate(self.current_capture_span_stack.len() - adjustments_count); - } - - fn consume_exprs(&mut self, exprs: impl Iterator) { - for expr in exprs { - self.consume_expr(expr); - } - } - - fn closure_kind(&self) -> FnTrait { - let mut r = FnTrait::Fn; - for it in &self.current_captures { - r = cmp::min( - r, - match &it.kind { - CaptureKind::ByRef(BorrowKind::Mut { .. }) => FnTrait::FnMut, - CaptureKind::ByRef(BorrowKind::Shallow | BorrowKind::Shared) => FnTrait::Fn, - CaptureKind::ByValue => FnTrait::FnOnce, - }, - ) - } - r - } - - fn analyze_closure(&mut self, closure: ClosureId) -> FnTrait { - let InternedClosure(_, root) = self.db.lookup_intern_closure(closure.into()); - self.current_closure = Some(closure); - let Expr::Closure { body, capture_by, .. } = &self.body[root] else { - unreachable!("Closure expression id is always closure"); - }; - self.consume_expr(*body); - for item in &self.current_captures { - if matches!( - item.kind, - CaptureKind::ByRef(BorrowKind::Mut { - kind: MutBorrowKind::Default | MutBorrowKind::TwoPhasedBorrow - }) - ) && !item.place.projections.contains(&ProjectionElem::Deref) - { - // FIXME: remove the `mutated_bindings_in_closure` completely and add proper fake reads in - // MIR. I didn't do that due duplicate diagnostics. - self.result.mutated_bindings_in_closure.insert(item.place.local); - } - } - self.restrict_precision_for_unsafe(); - // `closure_kind` should be done before adjust_for_move_closure - // If there exists pre-deduced kind of a closure, use it instead of one determined by capture, as rustc does. - // rustc also does diagnostics here if the latter is not a subtype of the former. - let closure_kind = self - .result - .closure_info - .get(&closure) - .map_or_else(|| self.closure_kind(), |info| info.1); - match capture_by { - CaptureBy::Value => self.adjust_for_move_closure(), - CaptureBy::Ref => (), - } - self.minimize_captures(); - self.strip_captures_ref_span(); - let result = mem::take(&mut self.current_captures); - let captures = result.into_iter().map(|it| it.with_ty(self)).collect::>(); - self.result.closure_info.insert(closure, (captures, closure_kind)); - closure_kind - } - - fn strip_captures_ref_span(&mut self) { - // FIXME: Borrow checker won't allow without this. - let mut captures = std::mem::take(&mut self.current_captures); - for capture in &mut captures { - if matches!(capture.kind, CaptureKind::ByValue) { - for span_stack in &mut capture.span_stacks { - if span_stack[span_stack.len() - 1].is_ref_span(self.body) { - span_stack.truncate(span_stack.len() - 1); - } - } - } - } - self.current_captures = captures; - } - - pub(crate) fn infer_closures(&mut self) { - let deferred_closures = self.sort_closures(); - for (closure, exprs) in deferred_closures.into_iter().rev() { - self.current_captures = vec![]; - let kind = self.analyze_closure(closure); - - for (derefed_callee, callee_ty, params, expr) in exprs { - if let &Expr::Call { callee, .. } = &self.body[expr] { - let mut adjustments = - self.result.expr_adjustments.remove(&callee).unwrap_or_default().into_vec(); - self.write_fn_trait_method_resolution( - kind, - &derefed_callee, - &mut adjustments, - &callee_ty, - ¶ms, - expr, - ); - self.result.expr_adjustments.insert(callee, adjustments.into_boxed_slice()); - } - } - } - } - - /// We want to analyze some closures before others, to have a correct analysis: - /// * We should analyze nested closures before the parent, since the parent should capture some of - /// the things that its children captures. - /// * If a closure calls another closure, we need to analyze the callee, to find out how we should - /// capture it (e.g. by move for FnOnce) + /// If there is no expected signature, then we will convert the + /// types that the user gave into a signature. /// - /// These dependencies are collected in the main inference. We do a topological sort in this function. It - /// will consume the `deferred_closures` field and return its content in a sorted vector. - fn sort_closures(&mut self) -> Vec<(ClosureId, Vec<(Ty, Ty, Vec, ExprId)>)> { - let mut deferred_closures = mem::take(&mut self.deferred_closures); - let mut dependents_count: FxHashMap = - deferred_closures.keys().map(|it| (*it, 0)).collect(); - for deps in self.closure_dependencies.values() { - for dep in deps { - *dependents_count.entry(*dep).or_default() += 1; + /// Also, record this closure signature for later. + fn supplied_sig_of_closure( + &mut self, + decl_inputs: &[Option], + decl_output: Option, + ) -> PolyFnSig<'db> { + let interner = self.table.interner; + + let supplied_return = match decl_output { + Some(output) => { + let output = self.make_body_ty(output); + self.process_user_written_ty(output).to_nextsolver(interner) } + None => self.table.next_ty_var(), + }; + // First, convert the types that the user supplied (if any). + let supplied_arguments = decl_inputs.iter().map(|&input| match input { + Some(input) => { + let input = self.make_body_ty(input); + self.process_user_written_ty(input).to_nextsolver(interner) + } + None => self.table.next_ty_var(), + }); + + Binder::dummy(interner.mk_fn_sig( + supplied_arguments, + supplied_return, + false, + Safety::Safe, + FnAbi::RustCall, + )) + } + + /// Converts the types that the user supplied, in case that doing + /// so should yield an error, but returns back a signature where + /// all parameters are of type `ty::Error`. + fn error_sig_of_closure( + &mut self, + decl_inputs: &[Option], + decl_output: Option, + ) -> PolyFnSig<'db> { + let interner = self.table.interner; + let err_ty = Ty::new_error(interner, ErrorGuaranteed); + + if let Some(output) = decl_output { + self.make_body_ty(output); } - let mut queue: Vec<_> = - deferred_closures.keys().copied().filter(|it| dependents_count[it] == 0).collect(); - let mut result = vec![]; - while let Some(it) = queue.pop() { - if let Some(d) = deferred_closures.remove(&it) { - result.push((it, d)); + let supplied_arguments = decl_inputs.iter().map(|&input| match input { + Some(input) => { + self.make_body_ty(input); + err_ty } - for dep in self.closure_dependencies.get(&it).into_iter().flat_map(|it| it.iter()) { - let cnt = dependents_count.get_mut(dep).unwrap(); - *cnt -= 1; - if *cnt == 0 { - queue.push(*dep); - } - } - } - assert!(deferred_closures.is_empty(), "we should have analyzed all closures"); + None => err_ty, + }); + + let result = Binder::dummy(interner.mk_fn_sig( + supplied_arguments, + err_ty, + false, + Safety::Safe, + FnAbi::RustCall, + )); + + debug!("supplied_sig_of_closure: result={:?}", result); + result } - pub(super) fn add_current_closure_dependency(&mut self, dep: ClosureId) { - if let Some(c) = self.current_closure - && !dep_creates_cycle(&self.closure_dependencies, &mut FxHashSet::default(), c, dep) - { - self.closure_dependencies.entry(c).or_default().push(dep); - } - - fn dep_creates_cycle( - closure_dependencies: &FxHashMap>, - visited: &mut FxHashSet, - from: ClosureId, - to: ClosureId, - ) -> bool { - if !visited.insert(from) { - return false; - } - - if from == to { - return true; - } - - if let Some(deps) = closure_dependencies.get(&to) { - for dep in deps { - if dep_creates_cycle(closure_dependencies, visited, from, *dep) { - return true; - } - } - } - - false - } + fn closure_sigs(&self, bound_sig: PolyFnSig<'db>) -> ClosureSignatures<'db> { + let liberated_sig = bound_sig.skip_binder(); + // FIXME: When we lower HRTB we'll need to actually liberate regions here. + ClosureSignatures { bound_sig, liberated_sig } } } - -/// Call this only when the last span in the stack isn't a split. -fn apply_adjusts_to_place( - current_capture_span_stack: &mut Vec, - mut r: HirPlace, - adjustments: &[Adjustment], -) -> Option { - let span = *current_capture_span_stack.last().expect("empty capture span stack"); - for adj in adjustments { - match &adj.kind { - Adjust::Deref(None) => { - current_capture_span_stack.push(span); - r.projections.push(ProjectionElem::Deref); - } - _ => return None, - } - } - Some(r) -} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs new file mode 100644 index 000000000000..fd14b9e2de57 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs @@ -0,0 +1,1298 @@ +//! Post-inference closure analysis: captures and closure kind. + +use std::{cmp, convert::Infallible, mem}; + +use chalk_ir::{ + BoundVar, DebruijnIndex, Mutability, TyKind, + fold::{FallibleTypeFolder, TypeFoldable}, +}; +use either::Either; +use hir_def::{ + DefWithBodyId, FieldId, HasModule, TupleFieldId, TupleId, VariantId, + expr_store::path::Path, + hir::{ + Array, AsmOperand, BinaryOp, BindingId, CaptureBy, Expr, ExprId, ExprOrPatId, Pat, PatId, + Statement, UnaryOp, + }, + item_tree::FieldsShape, + lang_item::LangItem, + resolver::ValueNs, +}; +use hir_expand::name::Name; +use intern::sym; +use rustc_hash::{FxHashMap, FxHashSet}; +use smallvec::{SmallVec, smallvec}; +use stdx::{format_to, never}; +use syntax::utils::is_raw_identifier; + +use crate::db::InternedClosureId; +use crate::infer::InferenceContext; +use crate::{ + Adjust, Adjustment, Binders, BindingMode, ClosureId, Interner, Substitution, Ty, TyExt, + db::{HirDatabase, InternedClosure}, + error_lifetime, from_placeholder_idx, + generics::Generics, + make_binders, + mir::{BorrowKind, MirSpan, MutBorrowKind, ProjectionElem}, + traits::FnTrait, + utils, +}; + +// The below functions handle capture and closure kind (Fn, FnMut, ..) + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub(crate) struct HirPlace { + pub(crate) local: BindingId, + pub(crate) projections: Vec>, +} + +impl HirPlace { + fn ty(&self, ctx: &mut InferenceContext<'_>) -> Ty { + let mut ty = ctx.table.resolve_completely(ctx.result[self.local].clone()); + for p in &self.projections { + ty = p.projected_ty( + ty, + ctx.db, + |_, _, _| { + unreachable!("Closure field only happens in MIR"); + }, + ctx.owner.module(ctx.db).krate(), + ); + } + ty + } + + fn capture_kind_of_truncated_place( + &self, + mut current_capture: CaptureKind, + len: usize, + ) -> CaptureKind { + if let CaptureKind::ByRef(BorrowKind::Mut { + kind: MutBorrowKind::Default | MutBorrowKind::TwoPhasedBorrow, + }) = current_capture + && self.projections[len..].contains(&ProjectionElem::Deref) + { + current_capture = + CaptureKind::ByRef(BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }); + } + current_capture + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum CaptureKind { + ByRef(BorrowKind), + ByValue, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CapturedItem { + pub(crate) place: HirPlace, + pub(crate) kind: CaptureKind, + /// The inner vec is the stacks; the outer vec is for each capture reference. + /// + /// Even though we always report only the last span (i.e. the most inclusive span), + /// we need to keep them all, since when a closure occurs inside a closure, we + /// copy all captures of the inner closure to the outer closure, and then we may + /// truncate them, and we want the correct span to be reported. + span_stacks: SmallVec<[SmallVec<[MirSpan; 3]>; 3]>, + pub(crate) ty: Binders, +} + +impl CapturedItem { + pub fn local(&self) -> BindingId { + self.place.local + } + + /// Returns whether this place has any field (aka. non-deref) projections. + pub fn has_field_projections(&self) -> bool { + self.place.projections.iter().any(|it| !matches!(it, ProjectionElem::Deref)) + } + + pub fn ty(&self, db: &dyn HirDatabase, subst: &Substitution) -> Ty { + self.ty.clone().substitute(Interner, &utils::ClosureSubst(subst).parent_subst(db)) + } + + pub fn kind(&self) -> CaptureKind { + self.kind + } + + pub fn spans(&self) -> SmallVec<[MirSpan; 3]> { + self.span_stacks.iter().map(|stack| *stack.last().expect("empty span stack")).collect() + } + + /// Converts the place to a name that can be inserted into source code. + pub fn place_to_name(&self, owner: DefWithBodyId, db: &dyn HirDatabase) -> String { + let body = db.body(owner); + let mut result = body[self.place.local].name.as_str().to_owned(); + for proj in &self.place.projections { + match proj { + ProjectionElem::Deref => {} + ProjectionElem::Field(Either::Left(f)) => { + let variant_data = f.parent.fields(db); + match variant_data.shape { + FieldsShape::Record => { + result.push('_'); + result.push_str(variant_data.fields()[f.local_id].name.as_str()) + } + FieldsShape::Tuple => { + let index = + variant_data.fields().iter().position(|it| it.0 == f.local_id); + if let Some(index) = index { + format_to!(result, "_{index}"); + } + } + FieldsShape::Unit => {} + } + } + ProjectionElem::Field(Either::Right(f)) => format_to!(result, "_{}", f.index), + &ProjectionElem::ClosureField(field) => format_to!(result, "_{field}"), + ProjectionElem::Index(_) + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subslice { .. } + | ProjectionElem::OpaqueCast(_) => { + never!("Not happen in closure capture"); + continue; + } + } + } + if is_raw_identifier(&result, owner.module(db).krate().data(db).edition) { + result.insert_str(0, "r#"); + } + result + } + + pub fn display_place_source_code(&self, owner: DefWithBodyId, db: &dyn HirDatabase) -> String { + let body = db.body(owner); + let krate = owner.krate(db); + let edition = krate.data(db).edition; + let mut result = body[self.place.local].name.display(db, edition).to_string(); + for proj in &self.place.projections { + match proj { + // In source code autoderef kicks in. + ProjectionElem::Deref => {} + ProjectionElem::Field(Either::Left(f)) => { + let variant_data = f.parent.fields(db); + match variant_data.shape { + FieldsShape::Record => format_to!( + result, + ".{}", + variant_data.fields()[f.local_id].name.display(db, edition) + ), + FieldsShape::Tuple => format_to!( + result, + ".{}", + variant_data + .fields() + .iter() + .position(|it| it.0 == f.local_id) + .unwrap_or_default() + ), + FieldsShape::Unit => {} + } + } + ProjectionElem::Field(Either::Right(f)) => { + let field = f.index; + format_to!(result, ".{field}"); + } + &ProjectionElem::ClosureField(field) => { + format_to!(result, ".{field}"); + } + ProjectionElem::Index(_) + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subslice { .. } + | ProjectionElem::OpaqueCast(_) => { + never!("Not happen in closure capture"); + continue; + } + } + } + let final_derefs_count = self + .place + .projections + .iter() + .rev() + .take_while(|proj| matches!(proj, ProjectionElem::Deref)) + .count(); + result.insert_str(0, &"*".repeat(final_derefs_count)); + result + } + + pub fn display_place(&self, owner: DefWithBodyId, db: &dyn HirDatabase) -> String { + let body = db.body(owner); + let krate = owner.krate(db); + let edition = krate.data(db).edition; + let mut result = body[self.place.local].name.display(db, edition).to_string(); + let mut field_need_paren = false; + for proj in &self.place.projections { + match proj { + ProjectionElem::Deref => { + result = format!("*{result}"); + field_need_paren = true; + } + ProjectionElem::Field(Either::Left(f)) => { + if field_need_paren { + result = format!("({result})"); + } + let variant_data = f.parent.fields(db); + let field = match variant_data.shape { + FieldsShape::Record => { + variant_data.fields()[f.local_id].name.as_str().to_owned() + } + FieldsShape::Tuple => variant_data + .fields() + .iter() + .position(|it| it.0 == f.local_id) + .unwrap_or_default() + .to_string(), + FieldsShape::Unit => "[missing field]".to_owned(), + }; + result = format!("{result}.{field}"); + field_need_paren = false; + } + ProjectionElem::Field(Either::Right(f)) => { + let field = f.index; + if field_need_paren { + result = format!("({result})"); + } + result = format!("{result}.{field}"); + field_need_paren = false; + } + &ProjectionElem::ClosureField(field) => { + if field_need_paren { + result = format!("({result})"); + } + result = format!("{result}.{field}"); + field_need_paren = false; + } + ProjectionElem::Index(_) + | ProjectionElem::ConstantIndex { .. } + | ProjectionElem::Subslice { .. } + | ProjectionElem::OpaqueCast(_) => { + never!("Not happen in closure capture"); + continue; + } + } + } + result + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) struct CapturedItemWithoutTy { + pub(crate) place: HirPlace, + pub(crate) kind: CaptureKind, + /// The inner vec is the stacks; the outer vec is for each capture reference. + pub(crate) span_stacks: SmallVec<[SmallVec<[MirSpan; 3]>; 3]>, +} + +impl CapturedItemWithoutTy { + fn with_ty(self, ctx: &mut InferenceContext<'_>) -> CapturedItem { + let ty = self.place.ty(ctx); + let ty = match &self.kind { + CaptureKind::ByValue => ty, + CaptureKind::ByRef(bk) => { + let m = match bk { + BorrowKind::Mut { .. } => Mutability::Mut, + _ => Mutability::Not, + }; + TyKind::Ref(m, error_lifetime(), ty).intern(Interner) + } + }; + return CapturedItem { + place: self.place, + kind: self.kind, + span_stacks: self.span_stacks, + ty: replace_placeholder_with_binder(ctx, ty), + }; + + fn replace_placeholder_with_binder(ctx: &mut InferenceContext<'_>, ty: Ty) -> Binders { + struct Filler<'a> { + db: &'a dyn HirDatabase, + generics: &'a Generics, + } + impl FallibleTypeFolder for Filler<'_> { + type Error = (); + + fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder { + self + } + + fn interner(&self) -> Interner { + Interner + } + + fn try_fold_free_placeholder_const( + &mut self, + ty: chalk_ir::Ty, + idx: chalk_ir::PlaceholderIndex, + outer_binder: DebruijnIndex, + ) -> Result, Self::Error> { + let x = from_placeholder_idx(self.db, idx).0; + let Some(idx) = self.generics.type_or_const_param_idx(x) else { + return Err(()); + }; + Ok(BoundVar::new(outer_binder, idx).to_const(Interner, ty)) + } + + fn try_fold_free_placeholder_ty( + &mut self, + idx: chalk_ir::PlaceholderIndex, + outer_binder: DebruijnIndex, + ) -> std::result::Result { + let x = from_placeholder_idx(self.db, idx).0; + let Some(idx) = self.generics.type_or_const_param_idx(x) else { + return Err(()); + }; + Ok(BoundVar::new(outer_binder, idx).to_ty(Interner)) + } + } + let filler = &mut Filler { db: ctx.db, generics: ctx.generics() }; + let result = ty.clone().try_fold_with(filler, DebruijnIndex::INNERMOST).unwrap_or(ty); + make_binders(ctx.db, filler.generics, result) + } + } +} + +impl InferenceContext<'_> { + fn place_of_expr(&mut self, tgt_expr: ExprId) -> Option { + let r = self.place_of_expr_without_adjust(tgt_expr)?; + let adjustments = + self.result.expr_adjustments.get(&tgt_expr).map(|it| &**it).unwrap_or_default(); + apply_adjusts_to_place(&mut self.current_capture_span_stack, r, adjustments) + } + + /// Pushes the span into `current_capture_span_stack`, *without clearing it first*. + fn path_place(&mut self, path: &Path, id: ExprOrPatId) -> Option { + if path.type_anchor().is_some() { + return None; + } + let hygiene = self.body.expr_or_pat_path_hygiene(id); + self.resolver.resolve_path_in_value_ns_fully(self.db, path, hygiene).and_then(|result| { + match result { + ValueNs::LocalBinding(binding) => { + let mir_span = match id { + ExprOrPatId::ExprId(id) => MirSpan::ExprId(id), + ExprOrPatId::PatId(id) => MirSpan::PatId(id), + }; + self.current_capture_span_stack.push(mir_span); + Some(HirPlace { local: binding, projections: Vec::new() }) + } + _ => None, + } + }) + } + + /// Changes `current_capture_span_stack` to contain the stack of spans for this expr. + fn place_of_expr_without_adjust(&mut self, tgt_expr: ExprId) -> Option { + self.current_capture_span_stack.clear(); + match &self.body[tgt_expr] { + Expr::Path(p) => { + let resolver_guard = + self.resolver.update_to_inner_scope(self.db, self.owner, tgt_expr); + let result = self.path_place(p, tgt_expr.into()); + self.resolver.reset_to_guard(resolver_guard); + return result; + } + Expr::Field { expr, name: _ } => { + let mut place = self.place_of_expr(*expr)?; + let field = self.result.field_resolution(tgt_expr)?; + self.current_capture_span_stack.push(MirSpan::ExprId(tgt_expr)); + place.projections.push(ProjectionElem::Field(field)); + return Some(place); + } + Expr::UnaryOp { expr, op: UnaryOp::Deref } => { + if matches!( + self.expr_ty_after_adjustments(*expr).kind(Interner), + TyKind::Ref(..) | TyKind::Raw(..) + ) { + let mut place = self.place_of_expr(*expr)?; + self.current_capture_span_stack.push(MirSpan::ExprId(tgt_expr)); + place.projections.push(ProjectionElem::Deref); + return Some(place); + } + } + _ => (), + } + None + } + + fn push_capture(&mut self, place: HirPlace, kind: CaptureKind) { + self.current_captures.push(CapturedItemWithoutTy { + place, + kind, + span_stacks: smallvec![self.current_capture_span_stack.iter().copied().collect()], + }); + } + + fn truncate_capture_spans(&self, capture: &mut CapturedItemWithoutTy, mut truncate_to: usize) { + // The first span is the identifier, and it must always remain. + truncate_to += 1; + for span_stack in &mut capture.span_stacks { + let mut remained = truncate_to; + let mut actual_truncate_to = 0; + for &span in &*span_stack { + actual_truncate_to += 1; + if !span.is_ref_span(self.body) { + remained -= 1; + if remained == 0 { + break; + } + } + } + if actual_truncate_to < span_stack.len() + && span_stack[actual_truncate_to].is_ref_span(self.body) + { + // Include the ref operator if there is one, we will fix it later (in `strip_captures_ref_span()`) if it's incorrect. + actual_truncate_to += 1; + } + span_stack.truncate(actual_truncate_to); + } + } + + fn ref_expr(&mut self, expr: ExprId, place: Option) { + if let Some(place) = place { + self.add_capture(place, CaptureKind::ByRef(BorrowKind::Shared)); + } + self.walk_expr(expr); + } + + fn add_capture(&mut self, place: HirPlace, kind: CaptureKind) { + if self.is_upvar(&place) { + self.push_capture(place, kind); + } + } + + fn mutate_path_pat(&mut self, path: &Path, id: PatId) { + if let Some(place) = self.path_place(path, id.into()) { + self.add_capture( + place, + CaptureKind::ByRef(BorrowKind::Mut { kind: MutBorrowKind::Default }), + ); + self.current_capture_span_stack.pop(); // Remove the pattern span. + } + } + + fn mutate_expr(&mut self, expr: ExprId, place: Option) { + if let Some(place) = place { + self.add_capture( + place, + CaptureKind::ByRef(BorrowKind::Mut { kind: MutBorrowKind::Default }), + ); + } + self.walk_expr(expr); + } + + fn consume_expr(&mut self, expr: ExprId) { + if let Some(place) = self.place_of_expr(expr) { + self.consume_place(place); + } + self.walk_expr(expr); + } + + fn consume_place(&mut self, place: HirPlace) { + if self.is_upvar(&place) { + let ty = place.ty(self); + let kind = if self.is_ty_copy(ty) { + CaptureKind::ByRef(BorrowKind::Shared) + } else { + CaptureKind::ByValue + }; + self.push_capture(place, kind); + } + } + + fn walk_expr_with_adjust(&mut self, tgt_expr: ExprId, adjustment: &[Adjustment]) { + if let Some((last, rest)) = adjustment.split_last() { + match &last.kind { + Adjust::NeverToAny | Adjust::Deref(None) | Adjust::Pointer(_) => { + self.walk_expr_with_adjust(tgt_expr, rest) + } + Adjust::Deref(Some(m)) => match m.0 { + Some(m) => { + self.ref_capture_with_adjusts(m, tgt_expr, rest); + } + None => unreachable!(), + }, + Adjust::Borrow(b) => { + self.ref_capture_with_adjusts(b.mutability(), tgt_expr, rest); + } + } + } else { + self.walk_expr_without_adjust(tgt_expr); + } + } + + fn ref_capture_with_adjusts(&mut self, m: Mutability, tgt_expr: ExprId, rest: &[Adjustment]) { + let capture_kind = match m { + Mutability::Mut => CaptureKind::ByRef(BorrowKind::Mut { kind: MutBorrowKind::Default }), + Mutability::Not => CaptureKind::ByRef(BorrowKind::Shared), + }; + if let Some(place) = self.place_of_expr_without_adjust(tgt_expr) + && let Some(place) = + apply_adjusts_to_place(&mut self.current_capture_span_stack, place, rest) + { + self.add_capture(place, capture_kind); + } + self.walk_expr_with_adjust(tgt_expr, rest); + } + + fn walk_expr(&mut self, tgt_expr: ExprId) { + if let Some(it) = self.result.expr_adjustments.get_mut(&tgt_expr) { + // FIXME: this take is completely unneeded, and just is here to make borrow checker + // happy. Remove it if you can. + let x_taken = mem::take(it); + self.walk_expr_with_adjust(tgt_expr, &x_taken); + *self.result.expr_adjustments.get_mut(&tgt_expr).unwrap() = x_taken; + } else { + self.walk_expr_without_adjust(tgt_expr); + } + } + + fn walk_expr_without_adjust(&mut self, tgt_expr: ExprId) { + match &self.body[tgt_expr] { + Expr::OffsetOf(_) => (), + Expr::InlineAsm(e) => e.operands.iter().for_each(|(_, op)| match op { + AsmOperand::In { expr, .. } + | AsmOperand::Out { expr: Some(expr), .. } + | AsmOperand::InOut { expr, .. } => self.walk_expr_without_adjust(*expr), + AsmOperand::SplitInOut { in_expr, out_expr, .. } => { + self.walk_expr_without_adjust(*in_expr); + if let Some(out_expr) = out_expr { + self.walk_expr_without_adjust(*out_expr); + } + } + AsmOperand::Out { expr: None, .. } + | AsmOperand::Const(_) + | AsmOperand::Label(_) + | AsmOperand::Sym(_) => (), + }), + Expr::If { condition, then_branch, else_branch } => { + self.consume_expr(*condition); + self.consume_expr(*then_branch); + if let &Some(expr) = else_branch { + self.consume_expr(expr); + } + } + Expr::Async { statements, tail, .. } + | Expr::Unsafe { statements, tail, .. } + | Expr::Block { statements, tail, .. } => { + for s in statements.iter() { + match s { + Statement::Let { pat, type_ref: _, initializer, else_branch } => { + if let Some(else_branch) = else_branch { + self.consume_expr(*else_branch); + } + if let Some(initializer) = initializer { + if else_branch.is_some() { + self.consume_expr(*initializer); + } else { + self.walk_expr(*initializer); + } + if let Some(place) = self.place_of_expr(*initializer) { + self.consume_with_pat(place, *pat); + } + } + } + Statement::Expr { expr, has_semi: _ } => { + self.consume_expr(*expr); + } + Statement::Item(_) => (), + } + } + if let Some(tail) = tail { + self.consume_expr(*tail); + } + } + Expr::Call { callee, args } => { + self.consume_expr(*callee); + self.consume_exprs(args.iter().copied()); + } + Expr::MethodCall { receiver, args, .. } => { + self.consume_expr(*receiver); + self.consume_exprs(args.iter().copied()); + } + Expr::Match { expr, arms } => { + for arm in arms.iter() { + self.consume_expr(arm.expr); + if let Some(guard) = arm.guard { + self.consume_expr(guard); + } + } + self.walk_expr(*expr); + if let Some(discr_place) = self.place_of_expr(*expr) + && self.is_upvar(&discr_place) + { + let mut capture_mode = None; + for arm in arms.iter() { + self.walk_pat(&mut capture_mode, arm.pat); + } + if let Some(c) = capture_mode { + self.push_capture(discr_place, c); + } + } + } + Expr::Break { expr, label: _ } + | Expr::Return { expr } + | Expr::Yield { expr } + | Expr::Yeet { expr } => { + if let &Some(expr) = expr { + self.consume_expr(expr); + } + } + &Expr::Become { expr } => { + self.consume_expr(expr); + } + Expr::RecordLit { fields, spread, .. } => { + if let &Some(expr) = spread { + self.consume_expr(expr); + } + self.consume_exprs(fields.iter().map(|it| it.expr)); + } + Expr::Field { expr, name: _ } => self.select_from_expr(*expr), + Expr::UnaryOp { expr, op: UnaryOp::Deref } => { + if matches!( + self.expr_ty_after_adjustments(*expr).kind(Interner), + TyKind::Ref(..) | TyKind::Raw(..) + ) { + self.select_from_expr(*expr); + } else if let Some((f, _)) = self.result.method_resolution(tgt_expr) { + let mutability = 'b: { + if let Some(deref_trait) = + self.resolve_lang_item(LangItem::DerefMut).and_then(|it| it.as_trait()) + && let Some(deref_fn) = deref_trait + .trait_items(self.db) + .method_by_name(&Name::new_symbol_root(sym::deref_mut)) + { + break 'b deref_fn == f; + } + false + }; + let place = self.place_of_expr(*expr); + if mutability { + self.mutate_expr(*expr, place); + } else { + self.ref_expr(*expr, place); + } + } else { + self.select_from_expr(*expr); + } + } + Expr::Let { pat, expr } => { + self.walk_expr(*expr); + if let Some(place) = self.place_of_expr(*expr) { + self.consume_with_pat(place, *pat); + } + } + Expr::UnaryOp { expr, op: _ } + | Expr::Array(Array::Repeat { initializer: expr, repeat: _ }) + | Expr::Await { expr } + | Expr::Loop { body: expr, label: _ } + | Expr::Box { expr } + | Expr::Cast { expr, type_ref: _ } => { + self.consume_expr(*expr); + } + Expr::Ref { expr, rawness: _, mutability } => { + // We need to do this before we push the span so the order will be correct. + let place = self.place_of_expr(*expr); + self.current_capture_span_stack.push(MirSpan::ExprId(tgt_expr)); + match mutability { + hir_def::type_ref::Mutability::Shared => self.ref_expr(*expr, place), + hir_def::type_ref::Mutability::Mut => self.mutate_expr(*expr, place), + } + } + Expr::BinaryOp { lhs, rhs, op } => { + let Some(op) = op else { + return; + }; + if matches!(op, BinaryOp::Assignment { .. }) { + let place = self.place_of_expr(*lhs); + self.mutate_expr(*lhs, place); + self.consume_expr(*rhs); + return; + } + self.consume_expr(*lhs); + self.consume_expr(*rhs); + } + Expr::Range { lhs, rhs, range_type: _ } => { + if let &Some(expr) = lhs { + self.consume_expr(expr); + } + if let &Some(expr) = rhs { + self.consume_expr(expr); + } + } + Expr::Index { base, index } => { + self.select_from_expr(*base); + self.consume_expr(*index); + } + Expr::Closure { .. } => { + let ty = self.expr_ty(tgt_expr); + let TyKind::Closure(id, _) = ty.kind(Interner) else { + never!("closure type is always closure"); + return; + }; + let (captures, _) = + self.result.closure_info.get(id).expect( + "We sort closures, so we should always have data for inner closures", + ); + let mut cc = mem::take(&mut self.current_captures); + cc.extend(captures.iter().filter(|it| self.is_upvar(&it.place)).map(|it| { + CapturedItemWithoutTy { + place: it.place.clone(), + kind: it.kind, + span_stacks: it.span_stacks.clone(), + } + })); + self.current_captures = cc; + } + Expr::Array(Array::ElementList { elements: exprs }) | Expr::Tuple { exprs } => { + self.consume_exprs(exprs.iter().copied()) + } + &Expr::Assignment { target, value } => { + self.walk_expr(value); + let resolver_guard = + self.resolver.update_to_inner_scope(self.db, self.owner, tgt_expr); + match self.place_of_expr(value) { + Some(rhs_place) => { + self.inside_assignment = true; + self.consume_with_pat(rhs_place, target); + self.inside_assignment = false; + } + None => self.body.walk_pats(target, &mut |pat| match &self.body[pat] { + Pat::Path(path) => self.mutate_path_pat(path, pat), + &Pat::Expr(expr) => { + let place = self.place_of_expr(expr); + self.mutate_expr(expr, place); + } + _ => {} + }), + } + self.resolver.reset_to_guard(resolver_guard); + } + + Expr::Missing + | Expr::Continue { .. } + | Expr::Path(_) + | Expr::Literal(_) + | Expr::Const(_) + | Expr::Underscore => (), + } + } + + fn walk_pat(&mut self, result: &mut Option, pat: PatId) { + let mut update_result = |ck: CaptureKind| match result { + Some(r) => { + *r = cmp::max(*r, ck); + } + None => *result = Some(ck), + }; + + self.walk_pat_inner( + pat, + &mut update_result, + BorrowKind::Mut { kind: MutBorrowKind::Default }, + ); + } + + fn walk_pat_inner( + &mut self, + p: PatId, + update_result: &mut impl FnMut(CaptureKind), + mut for_mut: BorrowKind, + ) { + match &self.body[p] { + Pat::Ref { .. } + | Pat::Box { .. } + | Pat::Missing + | Pat::Wild + | Pat::Tuple { .. } + | Pat::Expr(_) + | Pat::Or(_) => (), + Pat::TupleStruct { .. } | Pat::Record { .. } => { + if let Some(variant) = self.result.variant_resolution_for_pat(p) { + let adt = variant.adt_id(self.db); + let is_multivariant = match adt { + hir_def::AdtId::EnumId(e) => e.enum_variants(self.db).variants.len() != 1, + _ => false, + }; + if is_multivariant { + update_result(CaptureKind::ByRef(BorrowKind::Shared)); + } + } + } + Pat::Slice { .. } + | Pat::ConstBlock(_) + | Pat::Path(_) + | Pat::Lit(_) + | Pat::Range { .. } => { + update_result(CaptureKind::ByRef(BorrowKind::Shared)); + } + Pat::Bind { id, .. } => match self.result.binding_modes[p] { + crate::BindingMode::Move => { + if self.is_ty_copy(self.result.type_of_binding[*id].clone()) { + update_result(CaptureKind::ByRef(BorrowKind::Shared)); + } else { + update_result(CaptureKind::ByValue); + } + } + crate::BindingMode::Ref(r) => match r { + Mutability::Mut => update_result(CaptureKind::ByRef(for_mut)), + Mutability::Not => update_result(CaptureKind::ByRef(BorrowKind::Shared)), + }, + }, + } + if self.result.pat_adjustments.get(&p).is_some_and(|it| !it.is_empty()) { + for_mut = BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }; + } + self.body.walk_pats_shallow(p, |p| self.walk_pat_inner(p, update_result, for_mut)); + } + + fn expr_ty(&self, expr: ExprId) -> Ty { + self.result[expr].clone() + } + + fn expr_ty_after_adjustments(&self, e: ExprId) -> Ty { + let mut ty = None; + if let Some(it) = self.result.expr_adjustments.get(&e) + && let Some(it) = it.last() + { + ty = Some(it.target.clone()); + } + ty.unwrap_or_else(|| self.expr_ty(e)) + } + + fn is_upvar(&self, place: &HirPlace) -> bool { + if let Some(c) = self.current_closure { + let InternedClosure(_, root) = self.db.lookup_intern_closure(c); + return self.body.is_binding_upvar(place.local, root); + } + false + } + + fn is_ty_copy(&mut self, ty: Ty) -> bool { + if let TyKind::Closure(id, _) = ty.kind(Interner) { + // FIXME: We handle closure as a special case, since chalk consider every closure as copy. We + // should probably let chalk know which closures are copy, but I don't know how doing it + // without creating query cycles. + return self.result.closure_info.get(id).map(|it| it.1 == FnTrait::Fn).unwrap_or(true); + } + self.table.resolve_completely(ty).is_copy(self.db, self.owner) + } + + fn select_from_expr(&mut self, expr: ExprId) { + self.walk_expr(expr); + } + + fn restrict_precision_for_unsafe(&mut self) { + // FIXME: Borrow checker problems without this. + let mut current_captures = std::mem::take(&mut self.current_captures); + for capture in &mut current_captures { + let mut ty = self.table.resolve_completely(self.result[capture.place.local].clone()); + if ty.as_raw_ptr().is_some() || ty.is_union() { + capture.kind = CaptureKind::ByRef(BorrowKind::Shared); + self.truncate_capture_spans(capture, 0); + capture.place.projections.truncate(0); + continue; + } + for (i, p) in capture.place.projections.iter().enumerate() { + ty = p.projected_ty( + ty, + self.db, + |_, _, _| { + unreachable!("Closure field only happens in MIR"); + }, + self.owner.module(self.db).krate(), + ); + if ty.as_raw_ptr().is_some() || ty.is_union() { + capture.kind = CaptureKind::ByRef(BorrowKind::Shared); + self.truncate_capture_spans(capture, i + 1); + capture.place.projections.truncate(i + 1); + break; + } + } + } + self.current_captures = current_captures; + } + + fn adjust_for_move_closure(&mut self) { + // FIXME: Borrow checker won't allow without this. + let mut current_captures = std::mem::take(&mut self.current_captures); + for capture in &mut current_captures { + if let Some(first_deref) = + capture.place.projections.iter().position(|proj| *proj == ProjectionElem::Deref) + { + self.truncate_capture_spans(capture, first_deref); + capture.place.projections.truncate(first_deref); + } + capture.kind = CaptureKind::ByValue; + } + self.current_captures = current_captures; + } + + fn minimize_captures(&mut self) { + self.current_captures.sort_unstable_by_key(|it| it.place.projections.len()); + let mut hash_map = FxHashMap::::default(); + let result = mem::take(&mut self.current_captures); + for mut item in result { + let mut lookup_place = HirPlace { local: item.place.local, projections: vec![] }; + let mut it = item.place.projections.iter(); + let prev_index = loop { + if let Some(k) = hash_map.get(&lookup_place) { + break Some(*k); + } + match it.next() { + Some(it) => { + lookup_place.projections.push(it.clone()); + } + None => break None, + } + }; + match prev_index { + Some(p) => { + let prev_projections_len = self.current_captures[p].place.projections.len(); + self.truncate_capture_spans(&mut item, prev_projections_len); + self.current_captures[p].span_stacks.extend(item.span_stacks); + let len = self.current_captures[p].place.projections.len(); + let kind_after_truncate = + item.place.capture_kind_of_truncated_place(item.kind, len); + self.current_captures[p].kind = + cmp::max(kind_after_truncate, self.current_captures[p].kind); + } + None => { + hash_map.insert(item.place.clone(), self.current_captures.len()); + self.current_captures.push(item); + } + } + } + } + + fn consume_with_pat(&mut self, mut place: HirPlace, tgt_pat: PatId) { + let adjustments_count = + self.result.pat_adjustments.get(&tgt_pat).map(|it| it.len()).unwrap_or_default(); + place.projections.extend((0..adjustments_count).map(|_| ProjectionElem::Deref)); + self.current_capture_span_stack + .extend((0..adjustments_count).map(|_| MirSpan::PatId(tgt_pat))); + 'reset_span_stack: { + match &self.body[tgt_pat] { + Pat::Missing | Pat::Wild => (), + Pat::Tuple { args, ellipsis } => { + let (al, ar) = args.split_at(ellipsis.map_or(args.len(), |it| it as usize)); + let field_count = match self.result[tgt_pat].kind(Interner) { + TyKind::Tuple(_, s) => s.len(Interner), + _ => break 'reset_span_stack, + }; + let fields = 0..field_count; + let it = al.iter().zip(fields.clone()).chain(ar.iter().rev().zip(fields.rev())); + for (&arg, i) in it { + let mut p = place.clone(); + self.current_capture_span_stack.push(MirSpan::PatId(arg)); + p.projections.push(ProjectionElem::Field(Either::Right(TupleFieldId { + tuple: TupleId(!0), // dummy this, as its unused anyways + index: i as u32, + }))); + self.consume_with_pat(p, arg); + self.current_capture_span_stack.pop(); + } + } + Pat::Or(pats) => { + for pat in pats.iter() { + self.consume_with_pat(place.clone(), *pat); + } + } + Pat::Record { args, .. } => { + let Some(variant) = self.result.variant_resolution_for_pat(tgt_pat) else { + break 'reset_span_stack; + }; + match variant { + VariantId::EnumVariantId(_) | VariantId::UnionId(_) => { + self.consume_place(place) + } + VariantId::StructId(s) => { + let vd = s.fields(self.db); + for field_pat in args.iter() { + let arg = field_pat.pat; + let Some(local_id) = vd.field(&field_pat.name) else { + continue; + }; + let mut p = place.clone(); + self.current_capture_span_stack.push(MirSpan::PatId(arg)); + p.projections.push(ProjectionElem::Field(Either::Left(FieldId { + parent: variant, + local_id, + }))); + self.consume_with_pat(p, arg); + self.current_capture_span_stack.pop(); + } + } + } + } + Pat::Range { .. } | Pat::Slice { .. } | Pat::ConstBlock(_) | Pat::Lit(_) => { + self.consume_place(place) + } + Pat::Path(path) => { + if self.inside_assignment { + self.mutate_path_pat(path, tgt_pat); + } + self.consume_place(place); + } + &Pat::Bind { id, subpat: _ } => { + let mode = self.result.binding_modes[tgt_pat]; + let capture_kind = match mode { + BindingMode::Move => { + self.consume_place(place); + break 'reset_span_stack; + } + BindingMode::Ref(Mutability::Not) => BorrowKind::Shared, + BindingMode::Ref(Mutability::Mut) => { + BorrowKind::Mut { kind: MutBorrowKind::Default } + } + }; + self.current_capture_span_stack.push(MirSpan::BindingId(id)); + self.add_capture(place, CaptureKind::ByRef(capture_kind)); + self.current_capture_span_stack.pop(); + } + Pat::TupleStruct { path: _, args, ellipsis } => { + let Some(variant) = self.result.variant_resolution_for_pat(tgt_pat) else { + break 'reset_span_stack; + }; + match variant { + VariantId::EnumVariantId(_) | VariantId::UnionId(_) => { + self.consume_place(place) + } + VariantId::StructId(s) => { + let vd = s.fields(self.db); + let (al, ar) = + args.split_at(ellipsis.map_or(args.len(), |it| it as usize)); + let fields = vd.fields().iter(); + let it = al + .iter() + .zip(fields.clone()) + .chain(ar.iter().rev().zip(fields.rev())); + for (&arg, (i, _)) in it { + let mut p = place.clone(); + self.current_capture_span_stack.push(MirSpan::PatId(arg)); + p.projections.push(ProjectionElem::Field(Either::Left(FieldId { + parent: variant, + local_id: i, + }))); + self.consume_with_pat(p, arg); + self.current_capture_span_stack.pop(); + } + } + } + } + Pat::Ref { pat, mutability: _ } => { + self.current_capture_span_stack.push(MirSpan::PatId(tgt_pat)); + place.projections.push(ProjectionElem::Deref); + self.consume_with_pat(place, *pat); + self.current_capture_span_stack.pop(); + } + Pat::Box { .. } => (), // not supported + &Pat::Expr(expr) => { + self.consume_place(place); + let pat_capture_span_stack = mem::take(&mut self.current_capture_span_stack); + let old_inside_assignment = mem::replace(&mut self.inside_assignment, false); + let lhs_place = self.place_of_expr(expr); + self.mutate_expr(expr, lhs_place); + self.inside_assignment = old_inside_assignment; + self.current_capture_span_stack = pat_capture_span_stack; + } + } + } + self.current_capture_span_stack + .truncate(self.current_capture_span_stack.len() - adjustments_count); + } + + fn consume_exprs(&mut self, exprs: impl Iterator) { + for expr in exprs { + self.consume_expr(expr); + } + } + + fn closure_kind(&self) -> FnTrait { + let mut r = FnTrait::Fn; + for it in &self.current_captures { + r = cmp::min( + r, + match &it.kind { + CaptureKind::ByRef(BorrowKind::Mut { .. }) => FnTrait::FnMut, + CaptureKind::ByRef(BorrowKind::Shallow | BorrowKind::Shared) => FnTrait::Fn, + CaptureKind::ByValue => FnTrait::FnOnce, + }, + ) + } + r + } + + fn analyze_closure(&mut self, closure: ClosureId) -> FnTrait { + let InternedClosure(_, root) = self.db.lookup_intern_closure(closure.into()); + self.current_closure = Some(closure.into()); + let Expr::Closure { body, capture_by, .. } = &self.body[root] else { + unreachable!("Closure expression id is always closure"); + }; + self.consume_expr(*body); + for item in &self.current_captures { + if matches!( + item.kind, + CaptureKind::ByRef(BorrowKind::Mut { + kind: MutBorrowKind::Default | MutBorrowKind::TwoPhasedBorrow + }) + ) && !item.place.projections.contains(&ProjectionElem::Deref) + { + // FIXME: remove the `mutated_bindings_in_closure` completely and add proper fake reads in + // MIR. I didn't do that due duplicate diagnostics. + self.result.mutated_bindings_in_closure.insert(item.place.local); + } + } + self.restrict_precision_for_unsafe(); + // `closure_kind` should be done before adjust_for_move_closure + // If there exists pre-deduced kind of a closure, use it instead of one determined by capture, as rustc does. + // rustc also does diagnostics here if the latter is not a subtype of the former. + let closure_kind = self + .result + .closure_info + .get(&closure) + .map_or_else(|| self.closure_kind(), |info| info.1); + match capture_by { + CaptureBy::Value => self.adjust_for_move_closure(), + CaptureBy::Ref => (), + } + self.minimize_captures(); + self.strip_captures_ref_span(); + let result = mem::take(&mut self.current_captures); + let captures = result.into_iter().map(|it| it.with_ty(self)).collect::>(); + self.result.closure_info.insert(closure, (captures, closure_kind)); + closure_kind + } + + fn strip_captures_ref_span(&mut self) { + // FIXME: Borrow checker won't allow without this. + let mut captures = std::mem::take(&mut self.current_captures); + for capture in &mut captures { + if matches!(capture.kind, CaptureKind::ByValue) { + for span_stack in &mut capture.span_stacks { + if span_stack[span_stack.len() - 1].is_ref_span(self.body) { + span_stack.truncate(span_stack.len() - 1); + } + } + } + } + self.current_captures = captures; + } + + pub(crate) fn infer_closures(&mut self) { + let deferred_closures = self.sort_closures(); + for (closure, exprs) in deferred_closures.into_iter().rev() { + self.current_captures = vec![]; + let kind = self.analyze_closure(closure); + + for (derefed_callee, callee_ty, params, expr) in exprs { + if let &Expr::Call { callee, .. } = &self.body[expr] { + let mut adjustments = + self.result.expr_adjustments.remove(&callee).unwrap_or_default().into_vec(); + self.write_fn_trait_method_resolution( + kind, + &derefed_callee, + &mut adjustments, + &callee_ty, + ¶ms, + expr, + ); + self.result.expr_adjustments.insert(callee, adjustments.into_boxed_slice()); + } + } + } + } + + /// We want to analyze some closures before others, to have a correct analysis: + /// * We should analyze nested closures before the parent, since the parent should capture some of + /// the things that its children captures. + /// * If a closure calls another closure, we need to analyze the callee, to find out how we should + /// capture it (e.g. by move for FnOnce) + /// + /// These dependencies are collected in the main inference. We do a topological sort in this function. It + /// will consume the `deferred_closures` field and return its content in a sorted vector. + fn sort_closures(&mut self) -> Vec<(ClosureId, Vec<(Ty, Ty, Vec, ExprId)>)> { + let mut deferred_closures = mem::take(&mut self.deferred_closures); + let mut dependents_count: FxHashMap = + deferred_closures.keys().map(|it| ((*it).into(), 0)).collect(); + for deps in self.closure_dependencies.values() { + for dep in deps { + *dependents_count.entry((*dep).into()).or_default() += 1; + } + } + let mut queue: Vec<_> = deferred_closures + .keys() + .copied() + .filter(|&it| dependents_count[&it.into()] == 0) + .collect(); + let mut result = vec![]; + while let Some(it) = queue.pop() { + if let Some(d) = deferred_closures.remove(&it) { + result.push((it.into(), d)); + } + for &dep in self.closure_dependencies.get(&it).into_iter().flat_map(|it| it.iter()) { + let cnt = dependents_count.get_mut(&dep.into()).unwrap(); + *cnt -= 1; + if *cnt == 0 { + queue.push(dep); + } + } + } + assert!(deferred_closures.is_empty(), "we should have analyzed all closures"); + result + } + + pub(crate) fn add_current_closure_dependency(&mut self, dep: InternedClosureId) { + if let Some(c) = self.current_closure + && !dep_creates_cycle(&self.closure_dependencies, &mut FxHashSet::default(), c, dep) + { + self.closure_dependencies.entry(c).or_default().push(dep); + } + + fn dep_creates_cycle( + closure_dependencies: &FxHashMap>, + visited: &mut FxHashSet, + from: InternedClosureId, + to: InternedClosureId, + ) -> bool { + if !visited.insert(from) { + return false; + } + + if from == to { + return true; + } + + if let Some(deps) = closure_dependencies.get(&to) { + for dep in deps { + if dep_creates_cycle(closure_dependencies, visited, from, *dep) { + return true; + } + } + } + + false + } + } +} + +/// Call this only when the last span in the stack isn't a split. +fn apply_adjusts_to_place( + current_capture_span_stack: &mut Vec, + mut r: HirPlace, + adjustments: &[Adjustment], +) -> Option { + let span = *current_capture_span_stack.last().expect("empty capture span stack"); + for adj in adjustments { + match &adj.kind { + Adjust::Deref(None) => { + current_capture_span_stack.push(span); + r.projections.push(ProjectionElem::Deref); + } + _ => return None, + } + } + Some(r) +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index df516662bfd3..88b10e87e531 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -1,452 +1,388 @@ -//! Coercion logic. Coercions are certain type conversions that can implicitly -//! happen in certain places, e.g. weakening `&mut` to `&` or deref coercions -//! like going from `&Vec` to `&[T]`. +//! # Type Coercion //! -//! See and -//! `rustc_hir_analysis/check/coercion.rs`. +//! Under certain circumstances we will coerce from one type to another, +//! for example by auto-borrowing. This occurs in situations where the +//! compiler has a firm 'expected type' that was supplied from the user, +//! and where the actual type is similar to that expected type in purpose +//! but not in representation (so actual subtyping is inappropriate). +//! +//! ## Reborrowing +//! +//! Note that if we are expecting a reference, we will *reborrow* +//! even if the argument provided was already a reference. This is +//! useful for freezing mut things (that is, when the expected type is &T +//! but you have &mut T) and also for avoiding the linearity +//! of mut things (when the expected is &mut T and you have &mut T). See +//! the various `tests/ui/coerce/*.rs` tests for +//! examples of where this is useful. +//! +//! ## Subtle note +//! +//! When inferring the generic arguments of functions, the argument +//! order is relevant, which can lead to the following edge case: +//! +//! ```ignore (illustrative) +//! fn foo(a: T, b: T) { +//! // ... +//! } +//! +//! foo(&7i32, &mut 7i32); +//! // This compiles, as we first infer `T` to be `&i32`, +//! // and then coerce `&mut 7i32` to `&7i32`. +//! +//! foo(&mut 7i32, &7i32); +//! // This does not compile, as we first infer `T` to be `&mut i32` +//! // and are then unable to coerce `&7i32` to `&mut i32`. +//! ``` -use std::iter; - -use chalk_ir::{BoundVar, Mutability, TyKind, TyVariableKind, cast::Cast}; -use hir_def::{hir::ExprId, lang_item::LangItem}; -use rustc_type_ir::solve::Certainty; -use stdx::always; +use chalk_ir::cast::Cast; +use hir_def::{ + CallableDefId, + hir::{ExprId, ExprOrPatId}, + lang_item::LangItem, + signatures::FunctionSignature, +}; +use intern::sym; +use rustc_ast_ir::Mutability; +use rustc_type_ir::{ + TypeAndMut, + error::TypeError, + inherent::{IntoKind, Safety, Ty as _}, +}; +use smallvec::{SmallVec, smallvec}; +use tracing::{debug, instrument}; use triomphe::Arc; use crate::{ - Canonical, FnAbi, FnPointer, FnSig, Goal, Interner, Lifetime, Substitution, TraitEnvironment, - Ty, TyBuilder, TyExt, - autoderef::{Autoderef, AutoderefKind}, - db::HirDatabase, - infer::{ - Adjust, Adjustment, AutoBorrow, InferOk, InferenceContext, OverloadedDeref, PointerCast, - TypeError, TypeMismatch, + Adjust, Adjustment, AutoBorrow, Interner, PointerCast, TargetFeatures, TraitEnvironment, + autoderef::Autoderef, + db::{HirDatabase, InternedClosureId}, + infer::{AllowTwoPhase, InferenceContext, TypeMismatch, unify::InferenceTable}, + next_solver::{ + Binder, CallableIdWrapper, ClauseKind, CoercePredicate, DbInterner, ErrorGuaranteed, + GenericArgs, PolyFnSig, PredicateKind, Region, SolverDefId, TraitRef, Ty, TyKind, + infer::{ + DefineOpaqueTypes, InferCtxt, InferOk, InferResult, + relate::RelateResult, + select::{ImplSource, SelectionError}, + traits::{Obligation, ObligationCause, PredicateObligation, PredicateObligations}, + }, + mapping::{ChalkToNextSolver, NextSolverToChalk}, + obligation_ctxt::ObligationCtxt, }, - next_solver, - utils::ClosureSubst, + utils::TargetFeatureIsSafeInTarget, }; -use super::unify::InferenceTable; - -pub(crate) type CoerceResult<'db> = Result, Ty)>, TypeError>; - -/// Do not require any adjustments, i.e. coerce `x -> x`. -fn identity(_: Ty) -> Vec { - vec![] +struct Coerce<'a, 'b, 'db> { + table: &'a mut InferenceTable<'db>, + has_errors: &'a mut bool, + target_features: &'a mut dyn FnMut() -> (&'b TargetFeatures, TargetFeatureIsSafeInTarget), + use_lub: bool, + /// Determines whether or not allow_two_phase_borrow is set on any + /// autoref adjustments we create while coercing. We don't want to + /// allow deref coercions to create two-phase borrows, at least initially, + /// but we do need two-phase borrows for function argument reborrows. + /// See rust#47489 and rust#48598 + /// See docs on the "AllowTwoPhase" type for a more detailed discussion + allow_two_phase: AllowTwoPhase, + /// Whether we allow `NeverToAny` coercions. This is unsound if we're + /// coercing a place expression without it counting as a read in the MIR. + /// This is a side-effect of HIR not really having a great distinction + /// between places and values. + coerce_never: bool, + cause: ObligationCause, } -fn simple(kind: Adjust) -> impl FnOnce(Ty) -> Vec { - move |target| vec![Adjustment { kind, target }] +type CoerceResult<'db> = InferResult<'db, (Vec, Ty<'db>)>; + +/// Coercing a mutable reference to an immutable works, while +/// coercing `&T` to `&mut T` should be forbidden. +fn coerce_mutbls<'db>(from_mutbl: Mutability, to_mutbl: Mutability) -> RelateResult<'db, ()> { + if from_mutbl >= to_mutbl { Ok(()) } else { Err(TypeError::Mutability) } } /// This always returns `Ok(...)`. fn success<'db>( adj: Vec, - target: Ty, - goals: Vec>>, + target: Ty<'db>, + obligations: PredicateObligations<'db>, ) -> CoerceResult<'db> { - Ok(InferOk { goals, value: (adj, target) }) + Ok(InferOk { value: (adj, target), obligations }) } -pub(super) enum CoercionCause { - // FIXME: Make better use of this. Right now things like return and break without a value - // use it to point to themselves, causing us to report a mismatch on those expressions even - // though technically they themselves are `!` - Expr(ExprId), -} - -#[derive(Clone, Debug)] -pub(super) struct CoerceMany { - expected_ty: Ty, - final_ty: Option, - expressions: Vec, -} - -impl CoerceMany { - pub(super) fn new(expected: Ty) -> Self { - CoerceMany { expected_ty: expected, final_ty: None, expressions: vec![] } +impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { + #[inline] + fn set_tainted_by_errors(&mut self) { + *self.has_errors = true; } - /// Returns the "expected type" with which this coercion was - /// constructed. This represents the "downward propagated" type - /// that was given to us at the start of typing whatever construct - /// we are typing (e.g., the match expression). - /// - /// Typically, this is used as the expected type when - /// type-checking each of the alternative expressions whose types - /// we are trying to merge. - pub(super) fn expected_ty(&self) -> Ty { - self.expected_ty.clone() + #[inline] + fn interner(&self) -> DbInterner<'db> { + self.table.interner } - /// Returns the current "merged type", representing our best-guess - /// at the LUB of the expressions we've seen so far (if any). This - /// isn't *final* until you call `self.complete()`, which will return - /// the merged type. - pub(super) fn merged_ty(&self) -> Ty { - self.final_ty.clone().unwrap_or_else(|| self.expected_ty.clone()) + #[inline] + fn infer_ctxt(&self) -> &InferCtxt<'db> { + &self.table.infer_ctxt } - pub(super) fn complete(self, ctx: &mut InferenceContext<'_>) -> Ty { - if let Some(final_ty) = self.final_ty { - final_ty - } else { - ctx.result.standard_types.never.clone() - } - } - - pub(super) fn coerce_forced_unit( + pub(crate) fn commit_if_ok( &mut self, - ctx: &mut InferenceContext<'_>, - cause: CoercionCause, - ) { - self.coerce(ctx, None, &ctx.result.standard_types.unit.clone(), cause) + f: impl FnOnce(&mut Self) -> Result, + ) -> Result { + let snapshot = self.table.snapshot(); + let result = f(self); + match result { + Ok(_) => {} + Err(_) => { + self.table.rollback_to(snapshot); + } + } + result } - /// Merge two types from different branches, with possible coercion. - /// - /// Mostly this means trying to coerce one to the other, but - /// - if we have two function types for different functions or closures, we need to - /// coerce both to function pointers; - /// - if we were concerned with lifetime subtyping, we'd need to look for a - /// least upper bound. - pub(super) fn coerce<'db>( - &mut self, - ctx: &mut InferenceContext<'db>, - expr: Option, - expr_ty: &Ty, - cause: CoercionCause, - ) { - let expr_ty = ctx.resolve_ty_shallow(expr_ty); - self.expected_ty = ctx.resolve_ty_shallow(&self.expected_ty); + fn unify_raw(&mut self, a: Ty<'db>, b: Ty<'db>) -> InferResult<'db, Ty<'db>> { + debug!("unify(a: {:?}, b: {:?}, use_lub: {})", a, b, self.use_lub); + self.commit_if_ok(|this| { + let at = this.infer_ctxt().at(&this.cause, this.table.param_env); - // Special case: two function types. Try to coerce both to - // pointers to have a chance at getting a match. See - // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916 - let sig = match (self.merged_ty().kind(Interner), expr_ty.kind(Interner)) { - (TyKind::FnDef(x, _), TyKind::FnDef(y, _)) - if x == y && ctx.table.unify(&self.merged_ty(), &expr_ty) => - { - None - } - (TyKind::Closure(x, _), TyKind::Closure(y, _)) if x == y => None, - (TyKind::FnDef(..) | TyKind::Closure(..), TyKind::FnDef(..) | TyKind::Closure(..)) => { - // FIXME: we're ignoring safety here. To be more correct, if we have one FnDef and one Closure, - // we should be coercing the closure to a fn pointer of the safety of the FnDef - cov_mark::hit!(coerce_fn_reification); - let sig = - self.merged_ty().callable_sig(ctx.db).expect("FnDef without callable sig"); - Some(sig) - } - _ => None, - }; - if let Some(sig) = sig { - let target_ty = TyKind::Function(sig.to_fn_ptr()).intern(Interner); - let result1 = ctx.table.coerce_inner(self.merged_ty(), &target_ty, CoerceNever::Yes); - let result2 = ctx.table.coerce_inner(expr_ty.clone(), &target_ty, CoerceNever::Yes); - if let (Ok(result1), Ok(result2)) = (result1, result2) { - ctx.table.register_infer_ok(InferOk { value: (), goals: result1.goals }); - for &e in &self.expressions { - ctx.write_expr_adj(e, result1.value.0.clone().into_boxed_slice()); - } - ctx.table.register_infer_ok(InferOk { value: (), goals: result2.goals }); - if let Some(expr) = expr { - ctx.write_expr_adj(expr, result2.value.0.into_boxed_slice()); - self.expressions.push(expr); - } - return self.final_ty = Some(target_ty); - } - } - - // It might not seem like it, but order is important here: If the expected - // type is a type variable and the new one is `!`, trying it the other - // way around first would mean we make the type variable `!`, instead of - // just marking it as possibly diverging. - // - // - [Comment from rustc](https://github.com/rust-lang/rust/blob/5ff18d0eaefd1bd9ab8ec33dab2404a44e7631ed/compiler/rustc_hir_typeck/src/coercion.rs#L1334-L1335) - // First try to coerce the new expression to the type of the previous ones, - // but only if the new expression has no coercion already applied to it. - if expr.is_none_or(|expr| !ctx.result.expr_adjustments.contains_key(&expr)) - && let Ok(res) = ctx.coerce(expr, &expr_ty, &self.merged_ty(), CoerceNever::Yes) - { - self.final_ty = Some(res); - if let Some(expr) = expr { - self.expressions.push(expr); - } - return; - } - - if let Ok((adjustments, res)) = - ctx.coerce_inner(&self.merged_ty(), &expr_ty, CoerceNever::Yes) - { - self.final_ty = Some(res); - for &e in &self.expressions { - ctx.write_expr_adj(e, adjustments.clone().into_boxed_slice()); - } - } else { - match cause { - CoercionCause::Expr(id) => { - ctx.result.type_mismatches.insert( - id.into(), - TypeMismatch { expected: self.merged_ty(), actual: expr_ty.clone() }, - ); - } - } - cov_mark::hit!(coerce_merge_fail_fallback); - } - if let Some(expr) = expr { - self.expressions.push(expr); - } - } -} - -pub fn could_coerce( - db: &dyn HirDatabase, - env: Arc, - tys: &Canonical<(Ty, Ty)>, -) -> bool { - coerce(db, env, tys).is_ok() -} - -pub(crate) fn coerce( - db: &dyn HirDatabase, - env: Arc, - tys: &Canonical<(Ty, Ty)>, -) -> Result<(Vec, Ty), TypeError> { - let mut table = InferenceTable::new(db, env); - let vars = table.fresh_subst(tys.binders.as_slice(Interner)); - let ty1_with_vars = vars.apply(tys.value.0.clone(), Interner); - let ty2_with_vars = vars.apply(tys.value.1.clone(), Interner); - let (adjustments, ty) = table.coerce(&ty1_with_vars, &ty2_with_vars, CoerceNever::Yes)?; - // default any type vars that weren't unified back to their original bound vars - // (kind of hacky) - let find_var = |iv| { - vars.iter(Interner).position(|v| match v.interned() { - chalk_ir::GenericArgData::Ty(ty) => ty.inference_var(Interner), - chalk_ir::GenericArgData::Lifetime(lt) => lt.inference_var(Interner), - chalk_ir::GenericArgData::Const(c) => c.inference_var(Interner), - } == Some(iv)) - }; - let fallback = |iv, kind, default, binder| match kind { - chalk_ir::VariableKind::Ty(_ty_kind) => find_var(iv) - .map_or(default, |i| BoundVar::new(binder, i).to_ty(Interner).cast(Interner)), - chalk_ir::VariableKind::Lifetime => find_var(iv) - .map_or(default, |i| BoundVar::new(binder, i).to_lifetime(Interner).cast(Interner)), - chalk_ir::VariableKind::Const(ty) => find_var(iv) - .map_or(default, |i| BoundVar::new(binder, i).to_const(Interner, ty).cast(Interner)), - }; - // FIXME also map the types in the adjustments - Ok((adjustments, table.resolve_with_fallback(ty, &fallback))) -} - -#[derive(Clone, Copy, PartialEq, Eq)] -pub(crate) enum CoerceNever { - Yes, - No, -} - -impl InferenceContext<'_> { - /// Unify two types, but may coerce the first one to the second one - /// using "implicit coercion rules" if needed. - pub(super) fn coerce( - &mut self, - expr: Option, - from_ty: &Ty, - to_ty: &Ty, - // [Comment from rustc](https://github.com/rust-lang/rust/blob/4cc494bbfe9911d24f3ee521f98d5c6bb7e3ffe8/compiler/rustc_hir_typeck/src/coercion.rs#L85-L89) - // Whether we allow `NeverToAny` coercions. This is unsound if we're - // coercing a place expression without it counting as a read in the MIR. - // This is a side-effect of HIR not really having a great distinction - // between places and values. - coerce_never: CoerceNever, - ) -> Result { - let (adjustments, ty) = self.coerce_inner(from_ty, to_ty, coerce_never)?; - if let Some(expr) = expr { - self.write_expr_adj(expr, adjustments.into_boxed_slice()); - } - Ok(ty) - } - - fn coerce_inner( - &mut self, - from_ty: &Ty, - to_ty: &Ty, - coerce_never: CoerceNever, - ) -> Result<(Vec, Ty), TypeError> { - let from_ty = self.resolve_ty_shallow(from_ty); - let to_ty = self.resolve_ty_shallow(to_ty); - self.table.coerce(&from_ty, &to_ty, coerce_never) - } -} - -impl<'db> InferenceTable<'db> { - /// Unify two types, but may coerce the first one to the second one - /// using "implicit coercion rules" if needed. - pub(crate) fn coerce( - &mut self, - from_ty: &Ty, - to_ty: &Ty, - coerce_never: CoerceNever, - ) -> Result<(Vec, Ty), TypeError> { - let from_ty = self.structurally_resolve_type(from_ty); - let to_ty = self.structurally_resolve_type(to_ty); - match self.coerce_inner(from_ty, &to_ty, coerce_never) { - Ok(InferOk { value: (adjustments, ty), goals }) => { - self.register_infer_ok(InferOk { value: (), goals }); - Ok((adjustments, ty)) - } - Err(e) => { - // FIXME deal with error - Err(e) - } - } - } - - fn coerce_inner( - &mut self, - from_ty: Ty, - to_ty: &Ty, - coerce_never: CoerceNever, - ) -> CoerceResult<'db> { - if from_ty.is_never() { - if let TyKind::InferenceVar(tv, TyVariableKind::General) = to_ty.kind(Interner) { - self.set_diverging(*tv, TyVariableKind::General); - } - if coerce_never == CoerceNever::Yes { - // Subtle: If we are coercing from `!` to `?T`, where `?T` is an unbound - // type variable, we want `?T` to fallback to `!` if not - // otherwise constrained. An example where this arises: - // - // let _: Option = Some({ return; }); - // - // here, we would coerce from `!` to `?T`. - return success(simple(Adjust::NeverToAny)(to_ty.clone()), to_ty.clone(), vec![]); + let res = if this.use_lub { + at.lub(b, a) } else { - return self.unify_and(&from_ty, to_ty, identity); + at.sup(DefineOpaqueTypes::Yes, b, a) + .map(|InferOk { value: (), obligations }| InferOk { value: b, obligations }) + }; + + // In the new solver, lazy norm may allow us to shallowly equate + // more types, but we emit possibly impossible-to-satisfy obligations. + // Filter these cases out to make sure our coercion is more accurate. + match res { + Ok(InferOk { value, obligations }) => { + let mut ocx = ObligationCtxt::new(this.infer_ctxt()); + ocx.register_obligations(obligations); + if ocx.select_where_possible().is_empty() { + Ok(InferOk { value, obligations: ocx.into_pending_obligations() }) + } else { + Err(TypeError::Mismatch) + } + } + res => res, + } + }) + } + + /// Unify two types (using sub or lub). + fn unify(&mut self, a: Ty<'db>, b: Ty<'db>) -> CoerceResult<'db> { + self.unify_raw(a, b) + .and_then(|InferOk { value: ty, obligations }| success(vec![], ty, obligations)) + } + + /// Unify two types (using sub or lub) and produce a specific coercion. + fn unify_and( + &mut self, + a: Ty<'db>, + b: Ty<'db>, + adjustments: impl IntoIterator, + final_adjustment: Adjust, + ) -> CoerceResult<'db> { + self.unify_raw(a, b).and_then(|InferOk { value: ty, obligations }| { + success( + adjustments + .into_iter() + .chain(std::iter::once(Adjustment { + target: ty.to_chalk(self.interner()), + kind: final_adjustment, + })) + .collect(), + ty, + obligations, + ) + }) + } + + #[instrument(skip(self))] + fn coerce(&mut self, a: Ty<'db>, b: Ty<'db>) -> CoerceResult<'db> { + // First, remove any resolved type variables (at the top level, at least): + let a = self.table.shallow_resolve(a); + let b = self.table.shallow_resolve(b); + debug!("Coerce.tys({:?} => {:?})", a, b); + + // Coercing from `!` to any type is allowed: + if a.is_never() { + // If we're coercing into an inference var, mark it as possibly diverging. + // FIXME: rustc does this differently. + if let TyKind::Infer(rustc_type_ir::TyVar(b)) = b.kind() { + self.table.set_diverging(b.as_u32().into(), chalk_ir::TyVariableKind::General); + } + + if self.coerce_never { + return success( + vec![Adjustment { + kind: Adjust::NeverToAny, + target: b.to_chalk(self.interner()), + }], + b, + PredicateObligations::new(), + ); + } else { + // Otherwise the only coercion we can do is unification. + return self.unify(a, b); } } // If we are coercing into a TAIT, coerce into its proxy inference var, instead. - let mut to_ty = to_ty; - let _to; - if let Some(tait_table) = &self.tait_coercion_table - && let TyKind::OpaqueType(opaque_ty_id, _) = to_ty.kind(Interner) - && !matches!(from_ty.kind(Interner), TyKind::InferenceVar(..) | TyKind::OpaqueType(..)) - && let Some(ty) = tait_table.get(opaque_ty_id) + // FIXME(next-solver): This should not be here. This is not how rustc does thing, and it also not allows us + // to normalize opaques defined in our scopes. Instead, we should properly register + // `TypingMode::Analysis::defining_opaque_types_and_generators`, and rely on the solver to reveal + // them for us (we'll also need some global-like registry for the values, something we cannot + // really implement, therefore we can really support only RPITs and ITIAT or the new `#[define_opaque]` + // TAIT, not the old global TAIT). + let mut b = b; + if let Some(tait_table) = &self.table.tait_coercion_table + && let TyKind::Alias(rustc_type_ir::Opaque, opaque_ty) = b.kind() + && let SolverDefId::InternedOpaqueTyId(opaque_ty_id) = opaque_ty.def_id + && !matches!(a.kind(), TyKind::Infer(..) | TyKind::Alias(rustc_type_ir::Opaque, _)) + && let Some(ty) = tait_table.get(&opaque_ty_id.into()) { - _to = ty.clone(); - to_ty = &_to; + b = ty.to_nextsolver(self.interner()); + b = self.table.shallow_resolve(b); + } + let b = b; + + // Coercing *from* an unresolved inference variable means that + // we have no information about the source type. This will always + // ultimately fall back to some form of subtyping. + if a.is_infer() { + return self.coerce_from_inference_variable(a, b); } // Consider coercing the subtype to a DST - if let Ok(ret) = self.try_coerce_unsized(&from_ty, to_ty) { - return Ok(ret); + // + // NOTE: this is wrapped in a `commit_if_ok` because it creates + // a "spurious" type variable, and we don't want to have that + // type variable in memory if the coercion fails. + let unsize = self.commit_if_ok(|this| this.coerce_unsized(a, b)); + match unsize { + Ok(_) => { + debug!("coerce: unsize successful"); + return unsize; + } + Err(error) => { + debug!(?error, "coerce: unsize failed"); + } } - // Examine the supertype and consider auto-borrowing. - match to_ty.kind(Interner) { - TyKind::Raw(mt, _) => return self.coerce_ptr(from_ty, to_ty, *mt), - TyKind::Ref(mt, lt, _) => return self.coerce_ref(from_ty, to_ty, *mt, lt), + // Examine the supertype and consider type-specific coercions, such + // as auto-borrowing, coercing pointer mutability, a `dyn*` coercion, + // or pin-ergonomics. + match b.kind() { + TyKind::RawPtr(_, b_mutbl) => { + return self.coerce_raw_ptr(a, b, b_mutbl); + } + TyKind::Ref(r_b, _, mutbl_b) => { + return self.coerce_borrowed_pointer(a, b, r_b, mutbl_b); + } _ => {} } - match from_ty.kind(Interner) { + match a.kind() { TyKind::FnDef(..) => { // Function items are coercible to any closure // type; function pointers are not (that would // require double indirection). // Additionally, we permit coercion of function // items to drop the unsafe qualifier. - self.coerce_from_fn_item(from_ty, to_ty) + self.coerce_from_fn_item(a, b) } - TyKind::Function(from_fn_ptr) => { + TyKind::FnPtr(a_sig_tys, a_hdr) => { // We permit coercion of fn pointers to drop the // unsafe qualifier. - self.coerce_from_fn_pointer(from_ty.clone(), from_fn_ptr, to_ty) + self.coerce_from_fn_pointer(a_sig_tys.with(a_hdr), b) } - TyKind::Closure(_, from_substs) => { + TyKind::Closure(closure_def_id_a, args_a) => { // Non-capturing closures are coercible to // function pointers or unsafe function pointers. // It cannot convert closures that require unsafe. - self.coerce_closure_to_fn(from_ty.clone(), from_substs, to_ty) + self.coerce_closure_to_fn(a, closure_def_id_a.0, args_a, b) } _ => { // Otherwise, just use unification rules. - self.unify_and(&from_ty, to_ty, identity) + self.unify(a, b) } } } - /// Unify two types (using sub or lub) and produce a specific coercion. - fn unify_and(&mut self, t1: &Ty, t2: &Ty, f: F) -> CoerceResult<'db> - where - F: FnOnce(Ty) -> Vec, - { - self.try_unify(t1, t2) - .and_then(|InferOk { goals, .. }| success(f(t1.clone()), t1.clone(), goals)) - } + /// Coercing *from* an inference variable. In this case, we have no information + /// about the source type, so we can't really do a true coercion and we always + /// fall back to subtyping (`unify_and`). + fn coerce_from_inference_variable(&mut self, a: Ty<'db>, b: Ty<'db>) -> CoerceResult<'db> { + debug!("coerce_from_inference_variable(a={:?}, b={:?})", a, b); + debug_assert!(a.is_infer() && self.table.shallow_resolve(a) == a); + debug_assert!(self.table.shallow_resolve(b) == b); - fn coerce_ptr(&mut self, from_ty: Ty, to_ty: &Ty, to_mt: Mutability) -> CoerceResult<'db> { - let (is_ref, from_mt, from_inner) = match from_ty.kind(Interner) { - TyKind::Ref(mt, _, ty) => (true, mt, ty), - TyKind::Raw(mt, ty) => (false, mt, ty), - _ => return self.unify_and(&from_ty, to_ty, identity), - }; + if b.is_infer() { + // Two unresolved type variables: create a `Coerce` predicate. + let target_ty = if self.use_lub { self.table.next_ty_var() } else { b }; - coerce_mutabilities(*from_mt, to_mt)?; + let mut obligations = PredicateObligations::with_capacity(2); + for &source_ty in &[a, b] { + if source_ty != target_ty { + obligations.push(Obligation::new( + self.interner(), + self.cause.clone(), + self.table.param_env, + Binder::dummy(PredicateKind::Coerce(CoercePredicate { + a: source_ty, + b: target_ty, + })), + )); + } + } - // Check that the types which they point at are compatible. - let from_raw = TyKind::Raw(to_mt, from_inner.clone()).intern(Interner); - - // Although references and raw ptrs have the same - // representation, we still register an Adjust::DerefRef so that - // regionck knows that the region for `a` must be valid here. - if is_ref { - self.unify_and(&from_raw, to_ty, |target| { - vec![ - Adjustment { kind: Adjust::Deref(None), target: from_inner.clone() }, - Adjustment { kind: Adjust::Borrow(AutoBorrow::RawPtr(to_mt)), target }, - ] - }) - } else if *from_mt != to_mt { - self.unify_and( - &from_raw, - to_ty, - simple(Adjust::Pointer(PointerCast::MutToConstPointer)), - ) + debug!( + "coerce_from_inference_variable: two inference variables, target_ty={:?}, obligations={:?}", + target_ty, obligations + ); + success(vec![], target_ty, obligations) } else { - self.unify_and(&from_raw, to_ty, identity) + // One unresolved type variable: just apply subtyping, we may be able + // to do something useful. + self.unify(a, b) } } /// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`. /// To match `A` with `B`, autoderef will be performed, /// calling `deref`/`deref_mut` where necessary. - fn coerce_ref( + fn coerce_borrowed_pointer( &mut self, - from_ty: Ty, - to_ty: &Ty, - to_mt: Mutability, - to_lt: &Lifetime, + a: Ty<'db>, + b: Ty<'db>, + r_b: Region<'db>, + mutbl_b: Mutability, ) -> CoerceResult<'db> { - let (_from_lt, from_mt) = match from_ty.kind(Interner) { - TyKind::Ref(mt, lt, _) => { - coerce_mutabilities(*mt, to_mt)?; - (lt.clone(), *mt) // clone is probably not good? + debug!("coerce_borrowed_pointer(a={:?}, b={:?})", a, b); + debug_assert!(self.table.shallow_resolve(a) == a); + debug_assert!(self.table.shallow_resolve(b) == b); + + // If we have a parameter of type `&M T_a` and the value + // provided is `expr`, we will be adding an implicit borrow, + // meaning that we convert `f(expr)` to `f(&M *expr)`. Therefore, + // to type check, we will construct the type that `&M*expr` would + // yield. + + let (r_a, mt_a) = match a.kind() { + TyKind::Ref(r_a, ty, mutbl) => { + let mt_a = TypeAndMut::> { ty, mutbl }; + coerce_mutbls(mt_a.mutbl, mutbl_b)?; + (r_a, mt_a) } - _ => return self.unify_and(&from_ty, to_ty, identity), + _ => return self.unify(a, b), }; - // NOTE: this code is mostly copied and adapted from rustc, and - // currently more complicated than necessary, carrying errors around - // etc.. This complication will become necessary when we actually track - // details of coercion errors though, so I think it's useful to leave - // the structure like it is. - - let snapshot = self.snapshot(); - - let mut autoderef = Autoderef::new(self, from_ty.clone(), false, false); let mut first_error = None; + let mut r_borrow_var = None; + let mut autoderef = Autoderef::new(self.table, a); let mut found = None; while let Some((referent_ty, autoderefs)) = autoderef.next() { @@ -456,7 +392,7 @@ impl<'db> InferenceTable<'db> { continue; } - // At this point, we have deref'd `a` to `referent_ty`. So + // At this point, we have deref'd `a` to `referent_ty`. So // imagine we are coercing from `&'a mut Vec` to `&'b mut [T]`. // In the autoderef loop for `&'a mut Vec`, we would get // three callbacks: @@ -478,11 +414,85 @@ impl<'db> InferenceTable<'db> { // compare those. Note that this means we use the target // mutability [1], since it may be that we are coercing // from `&mut T` to `&U`. - let lt = to_lt; // FIXME: Involve rustc LUB and SUB flag checks - let derefd_from_ty = TyKind::Ref(to_mt, lt.clone(), referent_ty).intern(Interner); - match autoderef.table.try_unify(&derefd_from_ty, to_ty) { - Ok(result) => { - found = Some(result.map(|()| derefd_from_ty)); + // + // One fine point concerns the region that we use. We + // choose the region such that the region of the final + // type that results from `unify` will be the region we + // want for the autoref: + // + // - if in sub mode, that means we want to use `'b` (the + // region from the target reference) for both + // pointers [2]. This is because sub mode (somewhat + // arbitrarily) returns the subtype region. In the case + // where we are coercing to a target type, we know we + // want to use that target type region (`'b`) because -- + // for the program to type-check -- it must be the + // smaller of the two. + // - One fine point. It may be surprising that we can + // use `'b` without relating `'a` and `'b`. The reason + // that this is ok is that what we produce is + // effectively a `&'b *x` expression (if you could + // annotate the region of a borrow), and regionck has + // code that adds edges from the region of a borrow + // (`'b`, here) into the regions in the borrowed + // expression (`*x`, here). (Search for "link".) + // - if in lub mode, things can get fairly complicated. The + // easiest thing is just to make a fresh + // region variable [4], which effectively means we defer + // the decision to region inference (and regionck, which will add + // some more edges to this variable). However, this can wind up + // creating a crippling number of variables in some cases -- + // e.g., #32278 -- so we optimize one particular case [3]. + // Let me try to explain with some examples: + // - The "running example" above represents the simple case, + // where we have one `&` reference at the outer level and + // ownership all the rest of the way down. In this case, + // we want `LUB('a, 'b)` as the resulting region. + // - However, if there are nested borrows, that region is + // too strong. Consider a coercion from `&'a &'x Rc` to + // `&'b T`. In this case, `'a` is actually irrelevant. + // The pointer we want is `LUB('x, 'b`). If we choose `LUB('a,'b)` + // we get spurious errors (`ui/regions-lub-ref-ref-rc.rs`). + // (The errors actually show up in borrowck, typically, because + // this extra edge causes the region `'a` to be inferred to something + // too big, which then results in borrowck errors.) + // - We could track the innermost shared reference, but there is already + // code in regionck that has the job of creating links between + // the region of a borrow and the regions in the thing being + // borrowed (here, `'a` and `'x`), and it knows how to handle + // all the various cases. So instead we just make a region variable + // and let regionck figure it out. + let r = if !self.use_lub { + r_b // [2] above + } else if autoderefs == 1 { + r_a // [3] above + } else { + if r_borrow_var.is_none() { + // create var lazily, at most once + let r = autoderef.table.next_region_var(); + r_borrow_var = Some(r); // [4] above + } + r_borrow_var.unwrap() + }; + let derefd_ty_a = Ty::new_ref( + autoderef.table.interner, + r, + referent_ty, + mutbl_b, // [1] above + ); + // We need to construct a new `Coerce` because of lifetimes. + let mut coerce = Coerce { + table: autoderef.table, + has_errors: self.has_errors, + target_features: self.target_features, + use_lub: self.use_lub, + allow_two_phase: self.allow_two_phase, + coerce_never: self.coerce_never, + cause: self.cause.clone(), + }; + match coerce.unify_raw(derefd_ty_a, b) { + Ok(ok) => { + found = Some(ok); break; } Err(err) => { @@ -498,18 +508,24 @@ impl<'db> InferenceTable<'db> { // (e.g., in example above, the failure from relating `Vec` // to the target type), since that should be the least // confusing. - let InferOk { value: ty, goals } = match found { - Some(d) => d, - None => { - self.rollback_to(snapshot); - let err = first_error.expect("coerce_borrowed_pointer had no error"); - return Err(err); + let Some(InferOk { value: ty, mut obligations }) = found else { + if let Some(first_error) = first_error { + debug!("coerce_borrowed_pointer: failed with err = {:?}", first_error); + return Err(first_error); + } else { + // This may happen in the new trait solver since autoderef requires + // the pointee to be structurally normalizable, or else it'll just bail. + // So when we have a type like `&`, then we get no + // autoderef steps (even though there should be at least one). That means + // we get no type mismatches, since the loop above just exits early. + return Err(TypeError::Mismatch); } }; - if ty == from_ty && from_mt == Mutability::Not && autoderef.step_count() == 1 { + + if ty == a && mt_a.mutbl.is_not() && autoderef.step_count() == 1 { // As a special case, if we would produce `&'a *x`, that's // a total no-op. We end up with the type `&'a T` just as - // we started with. In that case, just skip it + // we started with. In that case, just skip it // altogether. This is just an optimization. // // Note that for `&mut`, we DO want to reborrow -- @@ -518,259 +534,1091 @@ impl<'db> InferenceTable<'db> { // `self.x` both have `&mut `type would be a move of // `self.x`, but we auto-coerce it to `foo(&mut *self.x)`, // which is a borrow. - always!(to_mt == Mutability::Not); // can only coerce &T -> &U - return success(vec![], ty, goals); + assert!(mutbl_b.is_not()); // can only coerce &T -> &U + return success(vec![], ty, obligations); } - let mut adjustments = auto_deref_adjust_steps(&autoderef); + let InferOk { value: mut adjustments, obligations: o } = + autoderef.adjust_steps_as_infer_ok(); + obligations.extend(o); + + // Now apply the autoref. We have to extract the region out of + // the final ref type we got. + let TyKind::Ref(region, _, _) = ty.kind() else { + panic!("expected a ref type, got {:?}", ty); + }; adjustments.push(Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(to_lt.clone(), to_mt)), - target: ty.clone(), + kind: Adjust::Borrow(AutoBorrow::Ref( + region.to_chalk(self.interner()), + mutbl_b.to_chalk(self.interner()), + )), + target: ty.to_chalk(self.interner()), }); - success(adjustments, ty, goals) + debug!("coerce_borrowed_pointer: succeeded ty={:?} adjustments={:?}", ty, adjustments); + + success(adjustments, ty, obligations) } - /// Attempts to coerce from the type of a Rust function item into a function pointer. - fn coerce_from_fn_item(&mut self, from_ty: Ty, to_ty: &Ty) -> CoerceResult<'db> { - match to_ty.kind(Interner) { - TyKind::Function(_) => { - let from_sig = from_ty.callable_sig(self.db).expect("FnDef had no sig"); + /// Performs [unsized coercion] by emulating a fulfillment loop on a + /// `CoerceUnsized` goal until all `CoerceUnsized` and `Unsize` goals + /// are successfully selected. + /// + /// [unsized coercion](https://doc.rust-lang.org/reference/type-coercions.html#unsized-coercions) + #[instrument(skip(self), level = "debug")] + fn coerce_unsized(&mut self, source: Ty<'db>, target: Ty<'db>) -> CoerceResult<'db> { + debug!(?source, ?target); + debug_assert!(self.table.shallow_resolve(source) == source); + debug_assert!(self.table.shallow_resolve(target) == target); - // FIXME check ABI: Intrinsics are not coercible to function pointers - // FIXME Safe `#[target_feature]` functions are not assignable to safe fn pointers (RFC 2396) - - // FIXME rustc normalizes assoc types in the sig here, not sure if necessary - - let from_sig = from_sig.to_fn_ptr(); - let from_fn_pointer = TyKind::Function(from_sig.clone()).intern(Interner); - let ok = self.coerce_from_safe_fn( - from_fn_pointer.clone(), - &from_sig, - to_ty, - |unsafe_ty| { - vec![ - Adjustment { - kind: Adjust::Pointer(PointerCast::ReifyFnPointer), - target: from_fn_pointer, - }, - Adjustment { - kind: Adjust::Pointer(PointerCast::UnsafeFnPointer), - target: unsafe_ty, - }, - ] - }, - simple(Adjust::Pointer(PointerCast::ReifyFnPointer)), - )?; - - Ok(ok) - } - _ => self.unify_and(&from_ty, to_ty, identity), + // We don't apply any coercions incase either the source or target + // aren't sufficiently well known but tend to instead just equate + // them both. + if source.is_infer() { + debug!("coerce_unsized: source is a TyVar, bailing out"); + return Err(TypeError::Mismatch); + } + if target.is_infer() { + debug!("coerce_unsized: target is a TyVar, bailing out"); + return Err(TypeError::Mismatch); } - } - fn coerce_from_fn_pointer( - &mut self, - from_ty: Ty, - from_f: &FnPointer, - to_ty: &Ty, - ) -> CoerceResult<'db> { - self.coerce_from_safe_fn( - from_ty, - from_f, - to_ty, - simple(Adjust::Pointer(PointerCast::UnsafeFnPointer)), - identity, - ) - } - - fn coerce_from_safe_fn( - &mut self, - from_ty: Ty, - from_fn_ptr: &FnPointer, - to_ty: &Ty, - to_unsafe: F, - normal: G, - ) -> CoerceResult<'db> - where - F: FnOnce(Ty) -> Vec, - G: FnOnce(Ty) -> Vec, - { - if let TyKind::Function(to_fn_ptr) = to_ty.kind(Interner) - && let (chalk_ir::Safety::Safe, chalk_ir::Safety::Unsafe) = - (from_fn_ptr.sig.safety, to_fn_ptr.sig.safety) + // This is an optimization because coercion is one of the most common + // operations that we do in typeck, since it happens at every assignment + // and call arg (among other positions). + // + // These targets are known to never be RHS in `LHS: CoerceUnsized`. + // That's because these are built-in types for which a core-provided impl + // doesn't exist, and for which a user-written impl is invalid. + // + // This is technically incomplete when users write impossible bounds like + // `where T: CoerceUnsized`, for example, but that trait is unstable + // and coercion is allowed to be incomplete. The only case where this matters + // is impossible bounds. + // + // Note that some of these types implement `LHS: Unsize`, but they + // do not implement *`CoerceUnsized`* which is the root obligation of the + // check below. + match target.kind() { + TyKind::Bool + | TyKind::Char + | TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) + | TyKind::Infer(rustc_type_ir::IntVar(_) | rustc_type_ir::FloatVar(_)) + | TyKind::Str + | TyKind::Array(_, _) + | TyKind::Slice(_) + | TyKind::FnDef(_, _) + | TyKind::FnPtr(_, _) + | TyKind::Dynamic(_, _, _) + | TyKind::Closure(_, _) + | TyKind::CoroutineClosure(_, _) + | TyKind::Coroutine(_, _) + | TyKind::CoroutineWitness(_, _) + | TyKind::Never + | TyKind::Tuple(_) => return Err(TypeError::Mismatch), + _ => {} + } + // Additionally, we ignore `&str -> &str` coercions, which happen very + // commonly since strings are one of the most used argument types in Rust, + // we do coercions when type checking call expressions. + if let TyKind::Ref(_, source_pointee, Mutability::Not) = source.kind() + && source_pointee.is_str() + && let TyKind::Ref(_, target_pointee, Mutability::Not) = target.kind() + && target_pointee.is_str() { - let from_unsafe = - TyKind::Function(safe_to_unsafe_fn_ty(from_fn_ptr.clone())).intern(Interner); - return self.unify_and(&from_unsafe, to_ty, to_unsafe); + return Err(TypeError::Mismatch); } - self.unify_and(&from_ty, to_ty, normal) + + let traits = ( + LangItem::Unsize.resolve_trait(self.table.db, self.table.trait_env.krate), + LangItem::CoerceUnsized.resolve_trait(self.table.db, self.table.trait_env.krate), + ); + let (Some(unsize_did), Some(coerce_unsized_did)) = traits else { + debug!("missing Unsize or CoerceUnsized traits"); + return Err(TypeError::Mismatch); + }; + + // Note, we want to avoid unnecessary unsizing. We don't want to coerce to + // a DST unless we have to. This currently comes out in the wash since + // we can't unify [T] with U. But to properly support DST, we need to allow + // that, at which point we will need extra checks on the target here. + + // Handle reborrows before selecting `Source: CoerceUnsized`. + let reborrow = match (source.kind(), target.kind()) { + (TyKind::Ref(_, ty_a, mutbl_a), TyKind::Ref(_, _, mutbl_b)) => { + coerce_mutbls(mutbl_a, mutbl_b)?; + + let r_borrow = self.table.next_region_var(); + + // We don't allow two-phase borrows here, at least for initial + // implementation. If it happens that this coercion is a function argument, + // the reborrow in coerce_borrowed_ptr will pick it up. + // let mutbl = AutoBorrowMutability::new(mutbl_b, AllowTwoPhase::No); + let mutbl = mutbl_b.to_chalk(self.interner()); + + Some(( + Adjustment { + kind: Adjust::Deref(None), + target: ty_a.to_chalk(self.interner()), + }, + Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref( + r_borrow.to_chalk(self.interner()), + mutbl, + )), + target: Ty::new_ref(self.interner(), r_borrow, ty_a, mutbl_b) + .to_chalk(self.interner()), + }, + )) + } + (TyKind::Ref(_, ty_a, mt_a), TyKind::RawPtr(_, mt_b)) => { + coerce_mutbls(mt_a, mt_b)?; + + Some(( + Adjustment { + kind: Adjust::Deref(None), + target: ty_a.to_chalk(self.interner()), + }, + Adjustment { + kind: Adjust::Borrow(AutoBorrow::RawPtr(mt_b.to_chalk(self.interner()))), + target: Ty::new_ptr(self.interner(), ty_a, mt_b).to_chalk(self.interner()), + }, + )) + } + _ => None, + }; + let coerce_source = + reborrow.as_ref().map_or(source, |(_, r)| r.target.to_nextsolver(self.interner())); + + // Setup either a subtyping or a LUB relationship between + // the `CoerceUnsized` target type and the expected type. + // We only have the latter, so we use an inference variable + // for the former and let type inference do the rest. + let coerce_target = self.table.next_ty_var(); + + let mut coercion = self.unify_and( + coerce_target, + target, + reborrow.into_iter().flat_map(|(deref, autoref)| [deref, autoref]), + Adjust::Pointer(PointerCast::Unsize), + )?; + + // Create an obligation for `Source: CoerceUnsized`. + let cause = self.cause.clone(); + + // Use a FIFO queue for this custom fulfillment procedure. + // + // A Vec (or SmallVec) is not a natural choice for a queue. However, + // this code path is hot, and this queue usually has a max length of 1 + // and almost never more than 3. By using a SmallVec we avoid an + // allocation, at the (very small) cost of (occasionally) having to + // shift subsequent elements down when removing the front element. + let mut queue: SmallVec<[PredicateObligation<'db>; 4]> = smallvec![Obligation::new( + self.interner(), + cause, + self.table.param_env, + TraitRef::new( + self.interner(), + coerce_unsized_did.into(), + [coerce_source, coerce_target] + ) + )]; + // Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid + // emitting a coercion in cases like `Foo<$1>` -> `Foo<$2>`, where + // inference might unify those two inner type variables later. + let traits = [coerce_unsized_did, unsize_did]; + while !queue.is_empty() { + let obligation = queue.remove(0); + let trait_pred = match obligation.predicate.kind().no_bound_vars() { + Some(PredicateKind::Clause(ClauseKind::Trait(trait_pred))) + if traits.contains(&trait_pred.def_id().0) => + { + self.infer_ctxt().resolve_vars_if_possible(trait_pred) + } + // Eagerly process alias-relate obligations in new trait solver, + // since these can be emitted in the process of solving trait goals, + // but we need to constrain vars before processing goals mentioning + // them. + Some(PredicateKind::AliasRelate(..)) => { + let mut ocx = ObligationCtxt::new(self.infer_ctxt()); + ocx.register_obligation(obligation); + if !ocx.select_where_possible().is_empty() { + return Err(TypeError::Mismatch); + } + coercion.obligations.extend(ocx.into_pending_obligations()); + continue; + } + _ => { + coercion.obligations.push(obligation); + continue; + } + }; + debug!("coerce_unsized resolve step: {:?}", trait_pred); + match self.infer_ctxt().select(&obligation.with(self.interner(), trait_pred)) { + // Uncertain or unimplemented. + Ok(None) => { + if trait_pred.def_id().0 == unsize_did { + let self_ty = trait_pred.self_ty(); + let unsize_ty = trait_pred.trait_ref.args.inner()[1].expect_ty(); + debug!("coerce_unsized: ambiguous unsize case for {:?}", trait_pred); + match (self_ty.kind(), unsize_ty.kind()) { + (TyKind::Infer(rustc_type_ir::TyVar(v)), TyKind::Dynamic(..)) + if self.table.type_var_is_sized(v) => + { + debug!("coerce_unsized: have sized infer {:?}", v); + coercion.obligations.push(obligation); + // `$0: Unsize` where we know that `$0: Sized`, try going + // for unsizing. + } + _ => { + // Some other case for `$0: Unsize`. Note that we + // hit this case even if `Something` is a sized type, so just + // don't do the coercion. + debug!("coerce_unsized: ambiguous unsize"); + return Err(TypeError::Mismatch); + } + } + } else { + debug!("coerce_unsized: early return - ambiguous"); + if !coerce_source.references_non_lt_error() + && !coerce_target.references_non_lt_error() + { + // rustc always early-returns here, even when the types contains errors. However not bailing + // improves error recovery, and while we don't implement generic consts properly, it also helps + // correct code. + return Err(TypeError::Mismatch); + } + } + } + Err(SelectionError::Unimplemented) => { + debug!("coerce_unsized: early return - can't prove obligation"); + return Err(TypeError::Mismatch); + } + + Err(SelectionError::TraitDynIncompatible(_)) => { + // Dyn compatibility errors in coercion will *always* be due to the + // fact that the RHS of the coercion is a non-dyn compatible `dyn Trait` + // written in source somewhere (otherwise we will never have lowered + // the dyn trait from HIR to middle). + // + // There's no reason to emit yet another dyn compatibility error, + // especially since the span will differ slightly and thus not be + // deduplicated at all! + self.set_tainted_by_errors(); + } + Err(_err) => { + // FIXME: Report an error: + // let guar = self.err_ctxt().report_selection_error( + // obligation.clone(), + // &obligation, + // &err, + // ); + self.set_tainted_by_errors(); + // Treat this like an obligation and follow through + // with the unsizing - the lack of a coercion should + // be silent, as it causes a type mismatch later. + } + + Ok(Some(ImplSource::UserDefined(impl_source))) => { + queue.extend(impl_source.nested); + } + Ok(Some(impl_source)) => queue.extend(impl_source.nested_obligations()), + } + } + + Ok(coercion) } - /// Attempts to coerce from the type of a non-capturing closure into a - /// function pointer. + fn coerce_from_safe_fn( + &mut self, + fn_ty_a: PolyFnSig<'db>, + b: Ty<'db>, + adjustment: Option, + ) -> CoerceResult<'db> { + debug_assert!(self.table.shallow_resolve(b) == b); + + self.commit_if_ok(|this| { + if let TyKind::FnPtr(_, hdr_b) = b.kind() + && fn_ty_a.safety().is_safe() + && !hdr_b.safety.is_safe() + { + let unsafe_a = Ty::safe_to_unsafe_fn_ty(this.interner(), fn_ty_a); + this.unify_and( + unsafe_a, + b, + adjustment.map(|kind| Adjustment { + kind, + target: Ty::new_fn_ptr(this.interner(), fn_ty_a).to_chalk(this.interner()), + }), + Adjust::Pointer(PointerCast::UnsafeFnPointer), + ) + } else { + let a = Ty::new_fn_ptr(this.interner(), fn_ty_a); + match adjustment { + Some(adjust) => this.unify_and(a, b, [], adjust), + None => this.unify(a, b), + } + } + }) + } + + fn coerce_from_fn_pointer(&mut self, fn_ty_a: PolyFnSig<'db>, b: Ty<'db>) -> CoerceResult<'db> { + debug!(?fn_ty_a, ?b, "coerce_from_fn_pointer"); + debug_assert!(self.table.shallow_resolve(b) == b); + + self.coerce_from_safe_fn(fn_ty_a, b, None) + } + + fn coerce_from_fn_item(&mut self, a: Ty<'db>, b: Ty<'db>) -> CoerceResult<'db> { + debug!("coerce_from_fn_item(a={:?}, b={:?})", a, b); + debug_assert!(self.table.shallow_resolve(a) == a); + debug_assert!(self.table.shallow_resolve(b) == b); + + match b.kind() { + TyKind::FnPtr(_, b_hdr) => { + let a_sig = a.fn_sig(self.interner()); + if let TyKind::FnDef(def_id, _) = a.kind() { + // Intrinsics are not coercible to function pointers + if let CallableDefId::FunctionId(def_id) = def_id.0 { + if FunctionSignature::is_intrinsic(self.table.db, def_id) { + return Err(TypeError::IntrinsicCast); + } + + let attrs = self.table.db.attrs(def_id.into()); + if attrs.by_key(sym::rustc_force_inline).exists() { + return Err(TypeError::ForceInlineCast); + } + + if b_hdr.safety.is_safe() && attrs.by_key(sym::target_feature).exists() { + let fn_target_features = + TargetFeatures::from_attrs_no_implications(&attrs); + // Allow the coercion if the current function has all the features that would be + // needed to call the coercee safely. + let (target_features, target_feature_is_safe) = + (self.target_features)(); + if target_feature_is_safe == TargetFeatureIsSafeInTarget::No + && !target_features.enabled.is_superset(&fn_target_features.enabled) + { + return Err(TypeError::TargetFeatureCast( + CallableIdWrapper(def_id.into()).into(), + )); + } + } + } + } + + self.coerce_from_safe_fn( + a_sig, + b, + Some(Adjust::Pointer(PointerCast::ReifyFnPointer)), + ) + } + _ => self.unify(a, b), + } + } + + /// Attempts to coerce from the type of a non-capturing closure + /// into a function pointer. fn coerce_closure_to_fn( &mut self, - from_ty: Ty, - from_substs: &Substitution, - to_ty: &Ty, + a: Ty<'db>, + _closure_def_id_a: InternedClosureId, + args_a: GenericArgs<'db>, + b: Ty<'db>, ) -> CoerceResult<'db> { - match to_ty.kind(Interner) { - // if from_substs is non-capturing (FIXME) - TyKind::Function(fn_ty) => { + debug_assert!(self.table.shallow_resolve(a) == a); + debug_assert!(self.table.shallow_resolve(b) == b); + + match b.kind() { + // FIXME: We need to have an `upvars_mentioned()` query: + // At this point we haven't done capture analysis, which means + // that the ClosureArgs just contains an inference variable instead + // of tuple of captured types. + // + // All we care here is if any variable is being captured and not the exact paths, + // so we check `upvars_mentioned` for root variables being captured. + TyKind::FnPtr(_, hdr) => + // if self + // .db + // .upvars_mentioned(closure_def_id_a.expect_local()) + // .is_none_or(|u| u.is_empty()) => + { // We coerce the closure, which has fn type // `extern "rust-call" fn((arg0,arg1,...)) -> _` // to // `fn(arg0,arg1,...) -> _` // or // `unsafe fn(arg0,arg1,...) -> _` - let safety = fn_ty.sig.safety; - let pointer_ty = coerce_closure_fn_ty(from_substs, safety); + let safety = hdr.safety; + let closure_sig = args_a.closure_sig_untupled().map_bound(|mut sig| { + sig.safety = hdr.safety; + sig + }); + let pointer_ty = Ty::new_fn_ptr(self.interner(), closure_sig); + debug!("coerce_closure_to_fn(a={:?}, b={:?}, pty={:?})", a, b, pointer_ty); self.unify_and( - &pointer_ty, - to_ty, - simple(Adjust::Pointer(PointerCast::ClosureFnPointer(safety))), + pointer_ty, + b, + [], + Adjust::Pointer(PointerCast::ClosureFnPointer( + safety.to_chalk(self.interner()), + )), ) } - _ => self.unify_and(&from_ty, to_ty, identity), + _ => self.unify(a, b), } } - /// Coerce a type using `from_ty: CoerceUnsized` + fn coerce_raw_ptr(&mut self, a: Ty<'db>, b: Ty<'db>, mutbl_b: Mutability) -> CoerceResult<'db> { + debug!("coerce_raw_ptr(a={:?}, b={:?})", a, b); + debug_assert!(self.table.shallow_resolve(a) == a); + debug_assert!(self.table.shallow_resolve(b) == b); + + let (is_ref, mt_a) = match a.kind() { + TyKind::Ref(_, ty, mutbl) => (true, TypeAndMut::> { ty, mutbl }), + TyKind::RawPtr(ty, mutbl) => (false, TypeAndMut { ty, mutbl }), + _ => return self.unify(a, b), + }; + coerce_mutbls(mt_a.mutbl, mutbl_b)?; + + // Check that the types which they point at are compatible. + let a_raw = Ty::new_ptr(self.interner(), mt_a.ty, mutbl_b); + // Although references and raw ptrs have the same + // representation, we still register an Adjust::DerefRef so that + // regionck knows that the region for `a` must be valid here. + if is_ref { + self.unify_and( + a_raw, + b, + [Adjustment { + kind: Adjust::Deref(None), + target: mt_a.ty.to_chalk(self.interner()), + }], + Adjust::Borrow(AutoBorrow::RawPtr(mutbl_b.to_chalk(self.interner()))), + ) + } else if mt_a.mutbl != mutbl_b { + self.unify_and(a_raw, b, [], Adjust::Pointer(PointerCast::MutToConstPointer)) + } else { + self.unify(a_raw, b) + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum CoerceNever { + No, + Yes, +} + +impl<'db> InferenceContext<'db> { + /// Attempt to coerce an expression to a type, and return the + /// adjusted type of the expression, if successful. + /// Adjustments are only recorded if the coercion succeeded. + /// The expressions *must not* have any preexisting adjustments. + pub(crate) fn coerce( + &mut self, + expr: ExprOrPatId, + expr_ty: Ty<'db>, + mut target: Ty<'db>, + allow_two_phase: AllowTwoPhase, + coerce_never: CoerceNever, + ) -> RelateResult<'db, Ty<'db>> { + let source = self.table.try_structurally_resolve_type(expr_ty); + target = self.table.try_structurally_resolve_type(target); + debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target); + + let cause = ObligationCause::new(); + let krate = self.krate(); + let mut coerce = Coerce { + table: &mut self.table, + has_errors: &mut self.result.has_errors, + cause, + allow_two_phase, + coerce_never: matches!(coerce_never, CoerceNever::Yes), + use_lub: false, + target_features: &mut || { + Self::target_features(self.db, &self.target_features, self.owner, krate) + }, + }; + let ok = coerce.commit_if_ok(|coerce| coerce.coerce(source, target))?; + + let (adjustments, _) = self.table.register_infer_ok(ok); + match expr { + ExprOrPatId::ExprId(expr) => self.write_expr_adj(expr, adjustments.into_boxed_slice()), + ExprOrPatId::PatId(pat) => self + .write_pat_adj(pat, adjustments.into_iter().map(|adjust| adjust.target).collect()), + } + Ok(target) + } + + /// Given some expressions, their known unified type and another expression, + /// tries to unify the types, potentially inserting coercions on any of the + /// provided expressions and returns their LUB (aka "common supertype"). /// - /// See: - fn try_coerce_unsized(&mut self, from_ty: &Ty, to_ty: &Ty) -> CoerceResult<'db> { - // These 'if' statements require some explanation. - // The `CoerceUnsized` trait is special - it is only - // possible to write `impl CoerceUnsized for A` where - // A and B have 'matching' fields. This rules out the following - // two types of blanket impls: - // - // `impl CoerceUnsized for SomeType` - // `impl CoerceUnsized for T` - // - // Both of these trigger a special `CoerceUnsized`-related error (E0376) - // - // We can take advantage of this fact to avoid performing unnecessary work. - // If either `source` or `target` is a type variable, then any applicable impl - // would need to be generic over the self-type (`impl CoerceUnsized for T`) - // or generic over the `CoerceUnsized` type parameter (`impl CoerceUnsized for - // SomeType`). - // - // However, these are exactly the kinds of impls which are forbidden by - // the compiler! Therefore, we can be sure that coercion will always fail - // when either the source or target type is a type variable. This allows us - // to skip performing any trait selection, and immediately bail out. - if from_ty.is_ty_var() { - return Err(TypeError); - } - if to_ty.is_ty_var() { - return Err(TypeError); + /// This is really an internal helper. From outside the coercion + /// module, you should instantiate a `CoerceMany` instance. + fn try_find_coercion_lub( + &mut self, + exprs: &[ExprId], + prev_ty: Ty<'db>, + new: ExprId, + new_ty: Ty<'db>, + ) -> RelateResult<'db, Ty<'db>> { + let prev_ty = self.table.try_structurally_resolve_type(prev_ty); + let new_ty = self.table.try_structurally_resolve_type(new_ty); + debug!( + "coercion::try_find_coercion_lub({:?}, {:?}, exprs={:?} exprs)", + prev_ty, + new_ty, + exprs.len() + ); + + // The following check fixes #88097, where the compiler erroneously + // attempted to coerce a closure type to itself via a function pointer. + if prev_ty == new_ty { + return Ok(prev_ty); } - // Handle reborrows before trying to solve `Source: CoerceUnsized`. - let reborrow = match (from_ty.kind(Interner), to_ty.kind(Interner)) { - (TyKind::Ref(from_mt, _, from_inner), &TyKind::Ref(to_mt, _, _)) => { - coerce_mutabilities(*from_mt, to_mt)?; + let is_force_inline = |ty: Ty<'db>| { + if let TyKind::FnDef(CallableIdWrapper(CallableDefId::FunctionId(did)), _) = ty.kind() { + self.db.attrs(did.into()).by_key(sym::rustc_force_inline).exists() + } else { + false + } + }; + if is_force_inline(prev_ty) || is_force_inline(new_ty) { + return Err(TypeError::ForceInlineCast); + } - let lt = self.new_lifetime_var(); - Some(( - Adjustment { kind: Adjust::Deref(None), target: from_inner.clone() }, - Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(lt.clone(), to_mt)), - target: TyKind::Ref(to_mt, lt, from_inner.clone()).intern(Interner), + // Special-case that coercion alone cannot handle: + // Function items or non-capturing closures of differing IDs or GenericArgs. + let (a_sig, b_sig) = { + let is_capturing_closure = |_ty: Ty<'db>| { + // FIXME: + // if let TyKind::Closure(closure_def_id, _args) = ty.kind() { + // self.db.upvars_mentioned(closure_def_id.expect_local()).is_some() + // } else { + // false + // } + false + }; + if is_capturing_closure(prev_ty) || is_capturing_closure(new_ty) { + (None, None) + } else { + match (prev_ty.kind(), new_ty.kind()) { + (TyKind::FnDef(..), TyKind::FnDef(..)) => { + // Don't reify if the function types have a LUB, i.e., they + // are the same function and their parameters have a LUB. + match self.table.commit_if_ok(|table| { + // We need to eagerly handle nested obligations due to lazy norm. + let mut ocx = ObligationCtxt::new(&table.infer_ctxt); + let value = + ocx.lub(&ObligationCause::new(), table.param_env, prev_ty, new_ty)?; + if ocx.select_where_possible().is_empty() { + Ok(InferOk { value, obligations: ocx.into_pending_obligations() }) + } else { + Err(TypeError::Mismatch) + } + }) { + // We have a LUB of prev_ty and new_ty, just return it. + Ok(ok) => return Ok(self.table.register_infer_ok(ok)), + Err(_) => ( + Some(prev_ty.fn_sig(self.table.interner)), + Some(new_ty.fn_sig(self.table.interner)), + ), + } + } + (TyKind::Closure(_, args), TyKind::FnDef(..)) => { + let b_sig = new_ty.fn_sig(self.table.interner); + let a_sig = args.closure_sig_untupled().map_bound(|mut sig| { + sig.safety = b_sig.safety(); + sig + }); + (Some(a_sig), Some(b_sig)) + } + (TyKind::FnDef(..), TyKind::Closure(_, args)) => { + let a_sig = prev_ty.fn_sig(self.table.interner); + let b_sig = args.closure_sig_untupled().map_bound(|mut sig| { + sig.safety = a_sig.safety(); + sig + }); + (Some(a_sig), Some(b_sig)) + } + (TyKind::Closure(_, args_a), TyKind::Closure(_, args_b)) => { + (Some(args_a.closure_sig_untupled()), Some(args_b.closure_sig_untupled())) + } + _ => (None, None), + } + } + }; + if let (Some(a_sig), Some(b_sig)) = (a_sig, b_sig) { + // The signature must match. + let sig = self + .table + .infer_ctxt + .at(&ObligationCause::new(), self.table.param_env) + .lub(a_sig, b_sig) + .map(|ok| self.table.register_infer_ok(ok))?; + + // Reify both sides and return the reified fn pointer type. + let fn_ptr = Ty::new_fn_ptr(self.table.interner, sig); + let prev_adjustment = match prev_ty.kind() { + TyKind::Closure(..) => Adjust::Pointer(PointerCast::ClosureFnPointer( + a_sig.safety().to_chalk(self.table.interner), + )), + TyKind::FnDef(..) => Adjust::Pointer(PointerCast::ReifyFnPointer), + _ => panic!("should not try to coerce a {prev_ty:?} to a fn pointer"), + }; + let next_adjustment = match new_ty.kind() { + TyKind::Closure(..) => Adjust::Pointer(PointerCast::ClosureFnPointer( + b_sig.safety().to_chalk(self.table.interner), + )), + TyKind::FnDef(..) => Adjust::Pointer(PointerCast::ReifyFnPointer), + _ => panic!("should not try to coerce a {new_ty:?} to a fn pointer"), + }; + for &expr in exprs { + self.write_expr_adj( + expr, + Box::new([Adjustment { + kind: prev_adjustment.clone(), + target: fn_ptr.to_chalk(self.table.interner), + }]), + ); + } + self.write_expr_adj( + new, + Box::new([Adjustment { + kind: next_adjustment, + target: fn_ptr.to_chalk(self.table.interner), + }]), + ); + return Ok(fn_ptr); + } + + // Configure a Coerce instance to compute the LUB. + // We don't allow two-phase borrows on any autorefs this creates since we + // probably aren't processing function arguments here and even if we were, + // they're going to get autorefed again anyway and we can apply 2-phase borrows + // at that time. + // + // NOTE: we set `coerce_never` to `true` here because coercion LUBs only + // operate on values and not places, so a never coercion is valid. + let krate = self.krate(); + let mut coerce = Coerce { + table: &mut self.table, + has_errors: &mut self.result.has_errors, + cause: ObligationCause::new(), + allow_two_phase: AllowTwoPhase::No, + coerce_never: true, + use_lub: true, + target_features: &mut || { + Self::target_features(self.db, &self.target_features, self.owner, krate) + }, + }; + + // First try to coerce the new expression to the type of the previous ones, + // but only if the new expression has no coercion already applied to it. + let mut first_error = None; + if !self.result.expr_adjustments.contains_key(&new) { + let result = coerce.commit_if_ok(|coerce| coerce.coerce(new_ty, prev_ty)); + match result { + Ok(ok) => { + let (adjustments, target) = self.table.register_infer_ok(ok); + self.write_expr_adj(new, adjustments.into_boxed_slice()); + debug!( + "coercion::try_find_coercion_lub: was able to coerce from new type {:?} to previous type {:?} ({:?})", + new_ty, prev_ty, target + ); + return Ok(target); + } + Err(e) => first_error = Some(e), + } + } + + match coerce.commit_if_ok(|coerce| coerce.coerce(prev_ty, new_ty)) { + Err(_) => { + // Avoid giving strange errors on failed attempts. + if let Some(e) = first_error { + Err(e) + } else { + Err(self + .table + .commit_if_ok(|table| { + table + .infer_ctxt + .at(&ObligationCause::new(), table.param_env) + .lub(prev_ty, new_ty) + }) + .unwrap_err()) + } + } + Ok(ok) => { + let (adjustments, target) = self.table.register_infer_ok(ok); + for &expr in exprs { + self.write_expr_adj(expr, adjustments.as_slice().into()); + } + debug!( + "coercion::try_find_coercion_lub: was able to coerce previous type {:?} to new type {:?} ({:?})", + prev_ty, new_ty, target + ); + Ok(target) + } + } + } +} + +/// CoerceMany encapsulates the pattern you should use when you have +/// many expressions that are all getting coerced to a common +/// type. This arises, for example, when you have a match (the result +/// of each arm is coerced to a common type). It also arises in less +/// obvious places, such as when you have many `break foo` expressions +/// that target the same loop, or the various `return` expressions in +/// a function. +/// +/// The basic protocol is as follows: +/// +/// - Instantiate the `CoerceMany` with an initial `expected_ty`. +/// This will also serve as the "starting LUB". The expectation is +/// that this type is something which all of the expressions *must* +/// be coercible to. Use a fresh type variable if needed. +/// - For each expression whose result is to be coerced, invoke `coerce()` with. +/// - In some cases we wish to coerce "non-expressions" whose types are implicitly +/// unit. This happens for example if you have a `break` with no expression, +/// or an `if` with no `else`. In that case, invoke `coerce_forced_unit()`. +/// - `coerce()` and `coerce_forced_unit()` may report errors. They hide this +/// from you so that you don't have to worry your pretty head about it. +/// But if an error is reported, the final type will be `err`. +/// - Invoking `coerce()` may cause us to go and adjust the "adjustments" on +/// previously coerced expressions. +/// - When all done, invoke `complete()`. This will return the LUB of +/// all your expressions. +/// - WARNING: I don't believe this final type is guaranteed to be +/// related to your initial `expected_ty` in any particular way, +/// although it will typically be a subtype, so you should check it. +/// - Invoking `complete()` may cause us to go and adjust the "adjustments" on +/// previously coerced expressions. +/// +/// Example: +/// +/// ```ignore (illustrative) +/// let mut coerce = CoerceMany::new(expected_ty); +/// for expr in exprs { +/// let expr_ty = fcx.check_expr_with_expectation(expr, expected); +/// coerce.coerce(fcx, &cause, expr, expr_ty); +/// } +/// let final_ty = coerce.complete(fcx); +/// ``` +#[derive(Debug, Clone)] +pub(crate) struct CoerceMany<'db, 'exprs> { + expected_ty: Ty<'db>, + final_ty: Option>, + expressions: Expressions<'exprs>, + pushed: usize, +} + +/// The type of a `CoerceMany` that is storing up the expressions into +/// a buffer. We use this for things like `break`. +pub(crate) type DynamicCoerceMany<'db> = CoerceMany<'db, 'db>; + +#[derive(Debug, Clone)] +enum Expressions<'exprs> { + Dynamic(SmallVec<[ExprId; 4]>), + UpFront(&'exprs [ExprId]), +} + +impl<'db, 'exprs> CoerceMany<'db, 'exprs> { + /// The usual case; collect the set of expressions dynamically. + /// If the full set of coercion sites is known before hand, + /// consider `with_coercion_sites()` instead to avoid allocation. + pub(crate) fn new(expected_ty: Ty<'db>) -> Self { + Self::make(expected_ty, Expressions::Dynamic(SmallVec::new())) + } + + /// As an optimization, you can create a `CoerceMany` with a + /// preexisting slice of expressions. In this case, you are + /// expected to pass each element in the slice to `coerce(...)` in + /// order. This is used with arrays in particular to avoid + /// needlessly cloning the slice. + pub(crate) fn with_coercion_sites( + expected_ty: Ty<'db>, + coercion_sites: &'exprs [ExprId], + ) -> Self { + Self::make(expected_ty, Expressions::UpFront(coercion_sites)) + } + + fn make(expected_ty: Ty<'db>, expressions: Expressions<'exprs>) -> Self { + CoerceMany { expected_ty, final_ty: None, expressions, pushed: 0 } + } + + /// Returns the "expected type" with which this coercion was + /// constructed. This represents the "downward propagated" type + /// that was given to us at the start of typing whatever construct + /// we are typing (e.g., the match expression). + /// + /// Typically, this is used as the expected type when + /// type-checking each of the alternative expressions whose types + /// we are trying to merge. + pub(crate) fn expected_ty(&self) -> Ty<'db> { + self.expected_ty + } + + /// Returns the current "merged type", representing our best-guess + /// at the LUB of the expressions we've seen so far (if any). This + /// isn't *final* until you call `self.complete()`, which will return + /// the merged type. + pub(crate) fn merged_ty(&self) -> Ty<'db> { + self.final_ty.unwrap_or(self.expected_ty) + } + + /// Indicates that the value generated by `expression`, which is + /// of type `expression_ty`, is one of the possibilities that we + /// could coerce from. This will record `expression`, and later + /// calls to `coerce` may come back and add adjustments and things + /// if necessary. + pub(crate) fn coerce( + &mut self, + icx: &mut InferenceContext<'db>, + cause: &ObligationCause, + expression: ExprId, + expression_ty: Ty<'db>, + ) { + self.coerce_inner(icx, cause, expression, expression_ty, false, false) + } + + /// Indicates that one of the inputs is a "forced unit". This + /// occurs in a case like `if foo { ... };`, where the missing else + /// generates a "forced unit". Another example is a `loop { break; + /// }`, where the `break` has no argument expression. We treat + /// these cases slightly differently for error-reporting + /// purposes. Note that these tend to correspond to cases where + /// the `()` expression is implicit in the source, and hence we do + /// not take an expression argument. + /// + /// The `augment_error` gives you a chance to extend the error + /// message, in case any results (e.g., we use this to suggest + /// removing a `;`). + pub(crate) fn coerce_forced_unit( + &mut self, + icx: &mut InferenceContext<'db>, + expr: ExprId, + cause: &ObligationCause, + label_unit_as_expected: bool, + ) { + self.coerce_inner( + icx, + cause, + expr, + icx.result.standard_types.unit.to_nextsolver(icx.table.interner), + true, + label_unit_as_expected, + ) + } + + /// The inner coercion "engine". If `expression` is `None`, this + /// is a forced-unit case, and hence `expression_ty` must be + /// `Nil`. + pub(crate) fn coerce_inner( + &mut self, + icx: &mut InferenceContext<'db>, + cause: &ObligationCause, + expression: ExprId, + mut expression_ty: Ty<'db>, + force_unit: bool, + label_expression_as_expected: bool, + ) { + // Incorporate whatever type inference information we have + // until now; in principle we might also want to process + // pending obligations, but doing so should only improve + // compatibility (hopefully that is true) by helping us + // uncover never types better. + if expression_ty.is_ty_var() { + expression_ty = icx.shallow_resolve(expression_ty); + } + + let (expected, found) = if label_expression_as_expected { + // In the case where this is a "forced unit", like + // `break`, we want to call the `()` "expected" + // since it is implied by the syntax. + // (Note: not all force-units work this way.)" + (expression_ty, self.merged_ty()) + } else { + // Otherwise, the "expected" type for error + // reporting is the current unification type, + // which is basically the LUB of the expressions + // we've seen so far (combined with the expected + // type) + (self.merged_ty(), expression_ty) + }; + + // Handle the actual type unification etc. + let result = if !force_unit { + if self.pushed == 0 { + // Special-case the first expression we are coercing. + // To be honest, I'm not entirely sure why we do this. + // We don't allow two-phase borrows, see comment in try_find_coercion_lub for why + icx.coerce( + expression.into(), + expression_ty, + self.expected_ty, + AllowTwoPhase::No, + CoerceNever::Yes, + ) + } else { + match self.expressions { + Expressions::Dynamic(ref exprs) => icx.try_find_coercion_lub( + exprs, + self.merged_ty(), + expression, + expression_ty, + ), + Expressions::UpFront(coercion_sites) => icx.try_find_coercion_lub( + &coercion_sites[0..self.pushed], + self.merged_ty(), + expression, + expression_ty, + ), + } + } + } else { + // this is a hack for cases where we default to `()` because + // the expression etc has been omitted from the source. An + // example is an `if let` without an else: + // + // if let Some(x) = ... { } + // + // we wind up with a second match arm that is like `_ => + // ()`. That is the case we are considering here. We take + // a different path to get the right "expected, found" + // message and so forth (and because we know that + // `expression_ty` will be unit). + // + // Another example is `break` with no argument expression. + assert!(expression_ty.is_unit(), "if let hack without unit type"); + icx.table + .infer_ctxt + .at(cause, icx.table.param_env) + .eq( + // needed for tests/ui/type-alias-impl-trait/issue-65679-inst-opaque-ty-from-val-twice.rs + DefineOpaqueTypes::Yes, + expected, + found, + ) + .map(|infer_ok| { + icx.table.register_infer_ok(infer_ok); + expression_ty + }) + }; + + debug!(?result); + match result { + Ok(v) => { + self.final_ty = Some(v); + match self.expressions { + Expressions::Dynamic(ref mut buffer) => buffer.push(expression), + Expressions::UpFront(coercion_sites) => { + // if the user gave us an array to validate, check that we got + // the next expression in the list, as expected + assert_eq!(coercion_sites[self.pushed], expression); + } + } + } + Err(_coercion_error) => { + // Mark that we've failed to coerce the types here to suppress + // any superfluous errors we might encounter while trying to + // emit or provide suggestions on how to fix the initial error. + icx.set_tainted_by_errors(); + + self.final_ty = Some(Ty::new_error(icx.table.interner, ErrorGuaranteed)); + + icx.result.type_mismatches.insert( + expression.into(), + if label_expression_as_expected { + TypeMismatch { + expected: found.to_chalk(icx.table.interner), + actual: expected.to_chalk(icx.table.interner), + } + } else { + TypeMismatch { + expected: expected.to_chalk(icx.table.interner), + actual: found.to_chalk(icx.table.interner), + } }, - )) + ); } - (TyKind::Ref(from_mt, _, from_inner), &TyKind::Raw(to_mt, _)) => { - coerce_mutabilities(*from_mt, to_mt)?; + } - Some(( - Adjustment { kind: Adjust::Deref(None), target: from_inner.clone() }, - Adjustment { - kind: Adjust::Borrow(AutoBorrow::RawPtr(to_mt)), - target: TyKind::Raw(to_mt, from_inner.clone()).intern(Interner), - }, - )) - } - _ => None, - }; - let coerce_from = - reborrow.as_ref().map_or_else(|| from_ty.clone(), |(_, adj)| adj.target.clone()); + self.pushed += 1; + } - let krate = self.trait_env.krate; - let coerce_unsized_trait = match LangItem::CoerceUnsized.resolve_trait(self.db, krate) { - Some(trait_) => trait_, - _ => return Err(TypeError), - }; - - let coerce_unsized_tref = { - let b = TyBuilder::trait_ref(self.db, coerce_unsized_trait); - if b.remaining() != 2 { - // The CoerceUnsized trait should have two generic params: Self and T. - return Err(TypeError); - } - b.push(coerce_from).push(to_ty.clone()).build() - }; - - let goal: Goal = coerce_unsized_tref.cast(Interner); - - self.commit_if_ok(|table| match table.solve_obligation(goal) { - Ok(Certainty::Yes) => Ok(()), - _ => Err(TypeError), - })?; - - let unsize = - Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target: to_ty.clone() }; - let adjustments = match reborrow { - None => vec![unsize], - Some((deref, autoref)) => vec![deref, autoref, unsize], - }; - success(adjustments, to_ty.clone(), vec![]) + pub(crate) fn complete(self, icx: &mut InferenceContext<'db>) -> Ty<'db> { + if let Some(final_ty) = self.final_ty { + final_ty + } else { + // If we only had inputs that were of type `!` (or no + // inputs at all), then the final type is `!`. + assert_eq!(self.pushed, 0); + icx.result.standard_types.never.to_nextsolver(icx.table.interner) + } } } -fn coerce_closure_fn_ty(closure_substs: &Substitution, safety: chalk_ir::Safety) -> Ty { - let closure_sig = ClosureSubst(closure_substs).sig_ty().clone(); - match closure_sig.kind(Interner) { - TyKind::Function(fn_ty) => TyKind::Function(FnPointer { - num_binders: fn_ty.num_binders, - sig: FnSig { safety, abi: FnAbi::Rust, variadic: fn_ty.sig.variadic }, - substitution: fn_ty.substitution.clone(), - }) - .intern(Interner), - _ => TyKind::Error.intern(Interner), - } +pub fn could_coerce( + db: &dyn HirDatabase, + env: Arc, + tys: &crate::Canonical<(crate::Ty, crate::Ty)>, +) -> bool { + coerce(db, env, tys).is_ok() } -fn safe_to_unsafe_fn_ty(fn_ty: FnPointer) -> FnPointer { - FnPointer { - num_binders: fn_ty.num_binders, - sig: FnSig { safety: chalk_ir::Safety::Unsafe, ..fn_ty.sig }, - substitution: fn_ty.substitution, - } -} +fn coerce<'db>( + db: &'db dyn HirDatabase, + env: Arc, + tys: &crate::Canonical<(crate::Ty, crate::Ty)>, +) -> Result<(Vec, crate::Ty), TypeError>> { + let mut table = InferenceTable::new(db, env); + let vars = table.fresh_subst(tys.binders.as_slice(Interner)); + let ty1_with_vars = vars.apply(tys.value.0.clone(), Interner); + let ty2_with_vars = vars.apply(tys.value.1.clone(), Interner); -fn coerce_mutabilities(from: Mutability, to: Mutability) -> Result<(), TypeError> { - match (from, to) { - (Mutability::Mut, Mutability::Mut | Mutability::Not) - | (Mutability::Not, Mutability::Not) => Ok(()), - (Mutability::Not, Mutability::Mut) => Err(TypeError), - } -} + let cause = ObligationCause::new(); + // FIXME: Target features. + let target_features = TargetFeatures::default(); + let mut coerce = Coerce { + table: &mut table, + has_errors: &mut false, + cause, + allow_two_phase: AllowTwoPhase::No, + coerce_never: true, + use_lub: false, + target_features: &mut || (&target_features, TargetFeatureIsSafeInTarget::No), + }; + let InferOk { value: (adjustments, ty), obligations } = coerce.coerce( + ty1_with_vars.to_nextsolver(coerce.table.interner), + ty2_with_vars.to_nextsolver(coerce.table.interner), + )?; + table.register_predicates(obligations); -pub(super) fn auto_deref_adjust_steps(autoderef: &Autoderef<'_, '_>) -> Vec { - let steps = autoderef.steps(); - let targets = - steps.iter().skip(1).map(|(_, ty)| ty.clone()).chain(iter::once(autoderef.final_ty())); - steps - .iter() - .map(|(kind, _source)| match kind { - // We do not know what kind of deref we require at this point yet - AutoderefKind::Overloaded => Some(OverloadedDeref(None)), - AutoderefKind::Builtin => None, - }) - .zip(targets) - .map(|(autoderef, target)| Adjustment { kind: Adjust::Deref(autoderef), target }) - .collect() + // default any type vars that weren't unified back to their original bound vars + // (kind of hacky) + let find_var = |iv| { + vars.iter(Interner).position(|v| match v.interned() { + chalk_ir::GenericArgData::Ty(ty) => ty.inference_var(Interner), + chalk_ir::GenericArgData::Lifetime(lt) => lt.inference_var(Interner), + chalk_ir::GenericArgData::Const(c) => c.inference_var(Interner), + } == Some(iv)) + }; + let fallback = |iv, kind, default, binder| match kind { + chalk_ir::VariableKind::Ty(_ty_kind) => find_var(iv) + .map_or(default, |i| crate::BoundVar::new(binder, i).to_ty(Interner).cast(Interner)), + chalk_ir::VariableKind::Lifetime => find_var(iv).map_or(default, |i| { + crate::BoundVar::new(binder, i).to_lifetime(Interner).cast(Interner) + }), + chalk_ir::VariableKind::Const(ty) => find_var(iv).map_or(default, |i| { + crate::BoundVar::new(binder, i).to_const(Interner, ty).cast(Interner) + }), + }; + // FIXME also map the types in the adjustments + Ok((adjustments, table.resolve_with_fallback(ty.to_chalk(table.interner), &fallback))) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 0a58ea11bb87..c5a51dfc4cf9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -1,12 +1,10 @@ //! Type inference for expressions. -use std::{ - iter::{repeat, repeat_with}, - mem, -}; +use std::{iter::repeat_with, mem}; use chalk_ir::{DebruijnIndex, Mutability, TyVariableKind, cast::Cast}; use either::Either; +use hir_def::hir::ClosureKind; use hir_def::{ BlockId, FieldId, GenericDefId, GenericParamId, ItemContainerId, Lookup, TupleFieldId, TupleId, expr_store::path::{GenericArg, GenericArgs, Path}, @@ -19,19 +17,23 @@ use hir_def::{ }; use hir_expand::name::Name; use intern::sym; +use rustc_type_ir::inherent::{AdtDef, IntoKind, SliceLike, Ty as _}; use stdx::always; use syntax::ast::RangeOp; +use tracing::debug; +use crate::autoderef::overloaded_deref_ty; +use crate::next_solver::ErrorGuaranteed; +use crate::next_solver::infer::DefineOpaqueTypes; +use crate::next_solver::obligation_ctxt::ObligationCtxt; use crate::{ Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, DeclContext, DeclOrigin, IncorrectGenericsLenKind, Interner, LifetimeElisionKind, Rawness, Scalar, - Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, - autoderef::{Autoderef, builtin_deref, deref_by_trait}, - consteval, + Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, consteval, generics::generics, infer::{ - BreakableKind, - coerce::{CoerceMany, CoerceNever, CoercionCause}, + AllowTwoPhase, BreakableKind, + coerce::{CoerceMany, CoerceNever}, find_continuable, pat::contains_explicit_ref_binding, }, @@ -42,7 +44,10 @@ use crate::{ }, mapping::{ToChalk, from_chalk}, method_resolution::{self, VisibleFromModule}, - next_solver::mapping::ChalkToNextSolver, + next_solver::{ + infer::traits::ObligationCause, + mapping::{ChalkToNextSolver, NextSolverToChalk}, + }, primitive::{self, UintTy}, static_lifetime, to_chalk_trait_id, traits::FnTrait, @@ -50,7 +55,7 @@ use crate::{ use super::{ BreakableContext, Diverges, Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch, - cast::CastCheck, coerce::auto_deref_adjust_steps, find_breakable, + cast::CastCheck, find_breakable, }; #[derive(Clone, Copy, PartialEq, Eq)] @@ -59,7 +64,7 @@ pub(crate) enum ExprIsRead { No, } -impl InferenceContext<'_> { +impl<'db> InferenceContext<'db> { pub(crate) fn infer_expr( &mut self, tgt_expr: ExprId, @@ -98,8 +103,14 @@ impl InferenceContext<'_> { } else { CoerceNever::No }; - match self.coerce(Some(expr), &ty, &target, coerce_never) { - Ok(res) => res, + match self.coerce( + expr.into(), + ty.to_nextsolver(self.table.interner), + target.to_nextsolver(self.table.interner), + AllowTwoPhase::No, + coerce_never, + ) { + Ok(res) => res.to_chalk(self.table.interner), Err(_) => { self.result.type_mismatches.insert( expr.into(), @@ -260,8 +271,15 @@ impl InferenceContext<'_> { } if let Some(target) = expected.only_has_type(&mut self.table) { - self.coerce(Some(expr), &ty, &target, CoerceNever::Yes) - .expect("never-to-any coercion should always succeed") + self.coerce( + expr.into(), + ty.to_nextsolver(self.table.interner), + target.to_nextsolver(self.table.interner), + AllowTwoPhase::No, + CoerceNever::Yes, + ) + .expect("never-to-any coercion should always succeed") + .to_chalk(self.table.interner) } else { ty } @@ -304,27 +322,41 @@ impl InferenceContext<'_> { let then_ty = self.infer_expr_inner(then_branch, expected, ExprIsRead::Yes); let then_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); - let mut coerce = CoerceMany::new(expected.coercion_target_type(&mut self.table)); - coerce.coerce(self, Some(then_branch), &then_ty, CoercionCause::Expr(then_branch)); + let mut coercion_sites = [then_branch, tgt_expr]; + if let Some(else_branch) = else_branch { + coercion_sites[1] = else_branch; + } + let mut coerce = CoerceMany::with_coercion_sites( + expected + .coercion_target_type(&mut self.table) + .to_nextsolver(self.table.interner), + &coercion_sites, + ); + coerce.coerce( + self, + &ObligationCause::new(), + then_branch, + then_ty.to_nextsolver(self.table.interner), + ); match else_branch { Some(else_branch) => { let else_ty = self.infer_expr_inner(else_branch, expected, ExprIsRead::Yes); let else_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); coerce.coerce( self, - Some(else_branch), - &else_ty, - CoercionCause::Expr(else_branch), + &ObligationCause::new(), + else_branch, + else_ty.to_nextsolver(self.table.interner), ); self.diverges = condition_diverges | then_diverges & else_diverges; } None => { - coerce.coerce_forced_unit(self, CoercionCause::Expr(tgt_expr)); + coerce.coerce_forced_unit(self, tgt_expr, &ObligationCause::new(), true); self.diverges = condition_diverges; } } - coerce.complete(self) + coerce.complete(self).to_chalk(self.table.interner) } &Expr::Let { pat, expr } => { let child_is_read = if self.pat_guaranteed_to_constitute_read_for_never(pat) { @@ -377,7 +409,15 @@ impl InferenceContext<'_> { } } Expr::Closure { body, args, ret_type, arg_types, closure_kind, capture_by: _ } => self - .infer_closure(body, args, ret_type, arg_types, *closure_kind, tgt_expr, expected), + .infer_closure( + *body, + args, + *ret_type, + arg_types, + *closure_kind, + tgt_expr, + expected, + ), Expr::Call { callee, args, .. } => self.infer_call(tgt_expr, *callee, args, expected), Expr::MethodCall { receiver, args, method_name, generic_args } => self .infer_method_call( @@ -416,7 +456,7 @@ impl InferenceContext<'_> { } _ => self.table.new_type_var(), }; - let mut coerce = CoerceMany::new(result_ty); + let mut coerce = CoerceMany::new(result_ty.to_nextsolver(self.table.interner)); for arm in arms.iter() { if let Some(guard_expr) = arm.guard { @@ -431,12 +471,17 @@ impl InferenceContext<'_> { let arm_ty = self.infer_expr_inner(arm.expr, &expected, ExprIsRead::Yes); all_arms_diverge &= self.diverges; - coerce.coerce(self, Some(arm.expr), &arm_ty, CoercionCause::Expr(arm.expr)); + coerce.coerce( + self, + &ObligationCause::new(), + arm.expr, + arm_ty.to_nextsolver(self.table.interner), + ); } self.diverges = matchee_diverges | all_arms_diverge; - coerce.complete(self) + coerce.complete(self).to_chalk(self.table.interner) } } Expr::Path(p) => self.infer_expr_path(p, tgt_expr.into(), tgt_expr), @@ -454,7 +499,7 @@ impl InferenceContext<'_> { let val_ty = if let Some(expr) = expr { let opt_coerce_to = match find_breakable(&mut self.breakables, label) { Some(ctxt) => match &ctxt.coerce { - Some(coerce) => coerce.expected_ty(), + Some(coerce) => coerce.expected_ty().to_chalk(self.table.interner), None => { self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop { expr: tgt_expr, @@ -478,11 +523,12 @@ impl InferenceContext<'_> { match find_breakable(&mut self.breakables, label) { Some(ctxt) => match ctxt.coerce.take() { Some(mut coerce) => { - let cause = match expr { - Some(expr) => CoercionCause::Expr(expr), - None => CoercionCause::Expr(tgt_expr), - }; - coerce.coerce(self, expr, &val_ty, cause); + coerce.coerce( + self, + &ObligationCause::new(), + expr.unwrap_or(tgt_expr), + val_ty.to_nextsolver(self.table.interner), + ); // Avoiding borrowck let ctxt = find_breakable(&mut self.breakables, label) @@ -514,7 +560,13 @@ impl InferenceContext<'_> { ); } else { let unit = self.result.standard_types.unit.clone(); - let _ = self.coerce(Some(tgt_expr), &unit, &yield_ty, CoerceNever::Yes); + let _ = self.coerce( + tgt_expr.into(), + unit.to_nextsolver(self.table.interner), + yield_ty.to_nextsolver(self.table.interner), + AllowTwoPhase::No, + CoerceNever::Yes, + ); } resume_ty } else { @@ -670,11 +722,23 @@ impl InferenceContext<'_> { Substitution::empty(Interner), ); } - if let Some(derefed) = builtin_deref(self.table.db, &inner_ty, true) { - self.table.structurally_resolve_type(derefed) + if let Some(derefed) = + inner_ty.to_nextsolver(self.table.interner).builtin_deref(self.db, true) + { + self.table + .structurally_resolve_type(&derefed.to_chalk(self.table.interner)) } else { - deref_by_trait(&mut self.table, inner_ty, false) - .unwrap_or_else(|| self.err_ty()) + let infer_ok = overloaded_deref_ty( + &self.table, + inner_ty.to_nextsolver(self.table.interner), + ); + match infer_ok { + Some(infer_ok) => self + .table + .register_infer_ok(infer_ok) + .to_chalk(self.table.interner), + None => self.err_ty(), + } } } UnaryOp::Neg => { @@ -1010,11 +1074,23 @@ impl InferenceContext<'_> { CallableSig::from_def(this.db, *def, parameters).to_fn_ptr(), ) .intern(Interner); - _ = this.coerce(Some(expr), &ty, &fnptr_ty, CoerceNever::Yes); + _ = this.coerce( + expr.into(), + ty.to_nextsolver(this.table.interner), + fnptr_ty.to_nextsolver(this.table.interner), + AllowTwoPhase::No, + CoerceNever::Yes, + ); } TyKind::Ref(mutbl, _, base_ty) => { let ptr_ty = TyKind::Raw(*mutbl, base_ty.clone()).intern(Interner); - _ = this.coerce(Some(expr), &ty, &ptr_ty, CoerceNever::Yes); + _ = this.coerce( + expr.into(), + ty.to_nextsolver(this.table.interner), + ptr_ty.to_nextsolver(this.table.interner), + AllowTwoPhase::No, + CoerceNever::Yes, + ); } _ => {} } @@ -1092,15 +1168,23 @@ impl InferenceContext<'_> { let ret_ty = self.table.new_type_var(); let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone()); - let prev_ret_coercion = self.return_coercion.replace(CoerceMany::new(ret_ty.clone())); + let prev_ret_coercion = self + .return_coercion + .replace(CoerceMany::new(ret_ty.to_nextsolver(self.table.interner))); // FIXME: We should handle async blocks like we handle closures let expected = &Expectation::has_type(ret_ty); let (_, inner_ty) = self.with_breakable_ctx(BreakableKind::Border, None, None, |this| { let ty = this.infer_block(tgt_expr, *id, statements, *tail, None, expected); if let Some(target) = expected.only_has_type(&mut this.table) { - match this.coerce(Some(tgt_expr), &ty, &target, CoerceNever::Yes) { - Ok(res) => res, + match this.coerce( + tgt_expr.into(), + ty.to_nextsolver(this.table.interner), + target.to_nextsolver(this.table.interner), + AllowTwoPhase::No, + CoerceNever::Yes, + ) { + Ok(res) => res.to_chalk(this.table.interner), Err(_) => { this.result.type_mismatches.insert( tgt_expr.into(), @@ -1209,13 +1293,21 @@ impl InferenceContext<'_> { (elem_ty, consteval::usize_const(self.db, Some(0), krate)) } Array::ElementList { elements, .. } => { - let mut coerce = CoerceMany::new(elem_ty); + let mut coerce = CoerceMany::with_coercion_sites( + elem_ty.to_nextsolver(self.table.interner), + elements, + ); for &expr in elements.iter() { let cur_elem_ty = self.infer_expr_inner(expr, &expected, ExprIsRead::Yes); - coerce.coerce(self, Some(expr), &cur_elem_ty, CoercionCause::Expr(expr)); + coerce.coerce( + self, + &ObligationCause::new(), + expr, + cur_elem_ty.to_nextsolver(self.table.interner), + ); } ( - coerce.complete(self), + coerce.complete(self).to_chalk(self.table.interner), consteval::usize_const(self.db, Some(elements.len() as u128), krate), ) } @@ -1254,11 +1346,17 @@ impl InferenceContext<'_> { .return_coercion .as_mut() .expect("infer_return called outside function body") - .expected_ty(); + .expected_ty() + .to_chalk(self.table.interner); let return_expr_ty = self.infer_expr_inner(expr, &Expectation::HasType(ret_ty), ExprIsRead::Yes); let mut coerce_many = self.return_coercion.take().unwrap(); - coerce_many.coerce(self, Some(expr), &return_expr_ty, CoercionCause::Expr(expr)); + coerce_many.coerce( + self, + &ObligationCause::new(), + expr, + return_expr_ty.to_nextsolver(self.table.interner), + ); self.return_coercion = Some(coerce_many); } @@ -1269,7 +1367,7 @@ impl InferenceContext<'_> { self.infer_return(expr); } else { let mut coerce = self.return_coercion.take().unwrap(); - coerce.coerce_forced_unit(self, CoercionCause::Expr(ret)); + coerce.coerce_forced_unit(self, ret, &ObligationCause::new(), true); self.return_coercion = Some(coerce); } } @@ -1286,7 +1384,7 @@ impl InferenceContext<'_> { fn infer_expr_become(&mut self, expr: ExprId) -> Ty { match &self.return_coercion { Some(return_coercion) => { - let ret_ty = return_coercion.expected_ty(); + let ret_ty = return_coercion.expected_ty().to_chalk(self.table.interner); let call_expr_ty = self.infer_expr_inner( expr, @@ -1540,9 +1638,10 @@ impl InferenceContext<'_> { }; if this .coerce( - Some(expr), - &this.result.standard_types.unit.clone(), - &t, + expr.into(), + this.result.standard_types.unit.to_nextsolver(this.table.interner), + t.to_nextsolver(this.table.interner), + AllowTwoPhase::No, coerce_never, ) .is_err() @@ -1563,6 +1662,7 @@ impl InferenceContext<'_> { }); self.resolver.reset_to_guard(g); if let Some(prev_env) = prev_env { + self.table.param_env = prev_env.env.to_nextsolver(self.table.interner); self.table.trait_env = prev_env; } @@ -1574,50 +1674,49 @@ impl InferenceContext<'_> { receiver_ty: &Ty, name: &Name, ) -> Option<(Ty, Either, Vec, bool)> { - let mut autoderef = Autoderef::new(&mut self.table, receiver_ty.clone(), false, false); + let interner = self.table.interner; + let mut autoderef = self.table.autoderef(receiver_ty.to_nextsolver(self.table.interner)); let mut private_field = None; let res = autoderef.by_ref().find_map(|(derefed_ty, _)| { - let (field_id, parameters) = match derefed_ty.kind(Interner) { - TyKind::Tuple(_, substs) => { + let (field_id, parameters) = match derefed_ty.kind() { + crate::next_solver::TyKind::Tuple(substs) => { return name.as_tuple_index().and_then(|idx| { - substs - .as_slice(Interner) - .get(idx) - .map(|a| a.assert_ty_ref(Interner)) - .cloned() - .map(|ty| { - ( - Either::Right(TupleFieldId { - tuple: TupleId( - self.tuple_field_accesses_rev - .insert_full(substs.clone()) - .0 - as u32, - ), - index: idx as u32, - }), - ty, - ) - }) + substs.as_slice().get(idx).copied().map(|ty| { + ( + Either::Right(TupleFieldId { + tuple: TupleId( + self.tuple_field_accesses_rev + .insert_full(substs.to_chalk(interner)) + .0 as u32, + ), + index: idx as u32, + }), + ty.to_chalk(interner), + ) + }) }); } - &TyKind::Adt(AdtId(hir_def::AdtId::StructId(s)), ref parameters) => { - let local_id = s.fields(self.db).field(name)?; - let field = FieldId { parent: s.into(), local_id }; - (field, parameters.clone()) - } - &TyKind::Adt(AdtId(hir_def::AdtId::UnionId(u)), ref parameters) => { - let local_id = u.fields(self.db).field(name)?; - let field = FieldId { parent: u.into(), local_id }; - (field, parameters.clone()) - } + crate::next_solver::TyKind::Adt(adt, parameters) => match adt.def_id().0 { + hir_def::AdtId::StructId(s) => { + let local_id = s.fields(self.db).field(name)?; + let field = FieldId { parent: s.into(), local_id }; + (field, parameters) + } + hir_def::AdtId::UnionId(u) => { + let local_id = u.fields(self.db).field(name)?; + let field = FieldId { parent: u.into(), local_id }; + (field, parameters) + } + hir_def::AdtId::EnumId(_) => return None, + }, _ => return None, }; + let parameters: crate::Substitution = parameters.to_chalk(interner); let is_visible = self.db.field_visibilities(field_id.parent)[field_id.local_id] .is_visible_from(self.db, self.resolver.module()); if !is_visible { if private_field.is_none() { - private_field = Some((field_id, parameters)); + private_field = Some((field_id, parameters.clone())); } return None; } @@ -1629,14 +1728,14 @@ impl InferenceContext<'_> { Some(match res { Some((field_id, ty)) => { - let adjustments = auto_deref_adjust_steps(&autoderef); + let adjustments = autoderef.adjust_steps(); let ty = self.process_remote_user_written_ty(ty); (ty, field_id, adjustments, true) } None => { let (field_id, subst) = private_field?; - let adjustments = auto_deref_adjust_steps(&autoderef); + let adjustments = autoderef.adjust_steps(); let ty = self.db.field_types(field_id.parent)[field_id.local_id] .clone() .substitute(Interner, &subst); @@ -1725,11 +1824,13 @@ impl InferenceContext<'_> { expected: &Expectation, ) -> Ty { let callee_ty = self.infer_expr(callee, &Expectation::none(), ExprIsRead::Yes); - let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone(), false, true); + let interner = self.table.interner; + let mut derefs = self.table.autoderef(callee_ty.to_nextsolver(interner)); let (res, derefed_callee) = loop { let Some((callee_deref_ty, _)) = derefs.next() else { break (None, callee_ty.clone()); }; + let callee_deref_ty = callee_deref_ty.to_chalk(interner); if let Some(res) = derefs.table.callable_sig(&callee_deref_ty, args.len()) { break (Some(res), callee_deref_ty); } @@ -1740,28 +1841,30 @@ impl InferenceContext<'_> { derefed_callee.callable_sig(self.db).is_some_and(|sig| sig.is_varargs) || res.is_none(); let (param_tys, ret_ty) = match res { Some((func, params, ret_ty)) => { - let mut adjustments = auto_deref_adjust_steps(&derefs); - if let TyKind::Closure(c, _) = - self.table.resolve_completely(callee_ty.clone()).kind(Interner) - { - self.add_current_closure_dependency(*c); - self.deferred_closures.entry(*c).or_default().push(( - derefed_callee.clone(), - callee_ty.clone(), - params.clone(), - tgt_expr, - )); - } + let params_chalk = + params.iter().map(|param| param.to_chalk(interner)).collect::>(); + let mut adjustments = derefs.adjust_steps(); if let Some(fn_x) = func { self.write_fn_trait_method_resolution( fn_x, &derefed_callee, &mut adjustments, &callee_ty, - ¶ms, + ¶ms_chalk, tgt_expr, ); } + if let &TyKind::Closure(c, _) = + self.table.resolve_completely(callee_ty.clone()).kind(Interner) + { + self.add_current_closure_dependency(c.into()); + self.deferred_closures.entry(c.into()).or_default().push(( + derefed_callee.clone(), + callee_ty.clone(), + params_chalk, + tgt_expr, + )); + } self.write_expr_adj(callee, adjustments.into_boxed_slice()); (params, ret_ty) } @@ -1770,7 +1873,7 @@ impl InferenceContext<'_> { call_expr: tgt_expr, found: callee_ty.clone(), }); - (Vec::new(), self.err_ty()) + (Vec::new(), crate::next_solver::Ty::new_error(interner, ErrorGuaranteed)) } }; let indices_to_skip = self.check_legacy_const_generics(derefed_callee, args); @@ -1791,29 +1894,24 @@ impl InferenceContext<'_> { tgt_expr: ExprId, args: &[ExprId], callee_ty: Ty, - param_tys: &[Ty], - ret_ty: Ty, + param_tys: &[crate::next_solver::Ty<'db>], + ret_ty: crate::next_solver::Ty<'db>, indices_to_skip: &[u32], is_varargs: bool, expected: &Expectation, ) -> Ty { self.register_obligations_for_call(&callee_ty); - let expected_inputs = self.expected_inputs_for_expected_output( - expected, - ret_ty.clone(), - param_tys.to_owned(), - ); - self.check_call_arguments( tgt_expr, - args, - &expected_inputs, param_tys, + ret_ty, + expected, + args, indices_to_skip, is_varargs, ); - self.normalize_associated_types_in(ret_ty) + self.table.normalize_associated_types_in_ns(ret_ty).to_chalk(self.table.interner) } fn infer_method_call( @@ -1826,6 +1924,21 @@ impl InferenceContext<'_> { expected: &Expectation, ) -> Ty { let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none(), ExprIsRead::Yes); + let receiver_ty = self.table.structurally_resolve_type(&receiver_ty); + + if matches!( + receiver_ty.kind(Interner), + TyKind::Error | TyKind::InferenceVar(_, TyVariableKind::General) + ) { + // Don't probe on error type, or on a fully unresolved infer var. + // FIXME: Emit an error if we're probing on an infer var (type annotations needed). + for &arg in args { + // Make sure we infer and record the arguments. + self.infer_expr_no_expect(arg, ExprIsRead::Yes); + } + return receiver_ty; + } + let canonicalized_receiver = self.canonicalize(receiver_ty.clone().to_nextsolver(self.table.interner)); @@ -1918,14 +2031,21 @@ impl InferenceContext<'_> { tgt_expr, args, callee_ty, - sig.params().get(strip_first as usize..).unwrap_or(&[]), - sig.ret().clone(), + &sig.params() + .get(strip_first as usize..) + .unwrap_or(&[]) + .iter() + .map(|param| param.to_nextsolver(self.table.interner)) + .collect::>(), + sig.ret().to_nextsolver(self.table.interner), &[], true, expected, ), None => { - self.check_call_arguments(tgt_expr, args, &[], &[], &[], true); + for &arg in args.iter() { + self.infer_expr_no_expect(arg, ExprIsRead::Yes); + } self.err_ty() } } @@ -1944,151 +2064,252 @@ impl InferenceContext<'_> { ) -> Ty { let method_ty = method_ty.substitute(Interner, &substs); self.register_obligations_for_call(&method_ty); + let interner = self.table.interner; let ((formal_receiver_ty, param_tys), ret_ty, is_varargs) = match method_ty.callable_sig(self.db) { Some(sig) => ( if !sig.params().is_empty() { - (sig.params()[0].clone(), sig.params()[1..].to_vec()) + ( + sig.params()[0].to_nextsolver(interner), + sig.params()[1..] + .iter() + .map(|param| param.to_nextsolver(interner)) + .collect(), + ) } else { - (self.err_ty(), Vec::new()) + (crate::next_solver::Ty::new_error(interner, ErrorGuaranteed), Vec::new()) }, - sig.ret().clone(), + sig.ret().to_nextsolver(interner), sig.is_varargs, ), None => { - let formal_receiver_ty = self.table.new_type_var(); - let ret_ty = self.table.new_type_var(); + let formal_receiver_ty = self.table.next_ty_var(); + let ret_ty = self.table.next_ty_var(); ((formal_receiver_ty, Vec::new()), ret_ty, true) } }; - self.unify(&formal_receiver_ty, &receiver_ty); + self.table.unify_ns(formal_receiver_ty, receiver_ty.to_nextsolver(interner)); - let expected_inputs = - self.expected_inputs_for_expected_output(expected, ret_ty.clone(), param_tys.clone()); - - self.check_call_arguments(tgt_expr, args, &expected_inputs, ¶m_tys, &[], is_varargs); - self.normalize_associated_types_in(ret_ty) + self.check_call_arguments(tgt_expr, ¶m_tys, ret_ty, expected, args, &[], is_varargs); + self.table.normalize_associated_types_in_ns(ret_ty).to_chalk(interner) } - fn expected_inputs_for_expected_output( + /// Generic function that factors out common logic from function calls, + /// method calls and overloaded operators. + pub(in super::super) fn check_call_arguments( &mut self, - expected_output: &Expectation, - output: Ty, - inputs: Vec, - ) -> Vec { - if let Some(expected_ty) = expected_output.only_has_type(&mut self.table) { - self.table.fudge_inference(|table| { - if table.try_unify(&expected_ty, &output).is_ok() { - table.resolve_with_fallback(inputs, &|var, kind, _, _| match kind { - chalk_ir::VariableKind::Ty(tk) => var.to_ty(Interner, tk).cast(Interner), - chalk_ir::VariableKind::Lifetime => { - var.to_lifetime(Interner).cast(Interner) - } - chalk_ir::VariableKind::Const(ty) => { - var.to_const(Interner, ty).cast(Interner) - } - }) - } else { - Vec::new() - } - }) - } else { - Vec::new() - } - } - - fn check_call_arguments( - &mut self, - expr: ExprId, - args: &[ExprId], - expected_inputs: &[Ty], - param_tys: &[Ty], + call_expr: ExprId, + // Types (as defined in the *signature* of the target function) + formal_input_tys: &[crate::next_solver::Ty<'db>], + formal_output: crate::next_solver::Ty<'db>, + // Expected output from the parent expression or statement + expectation: &Expectation, + // The expressions for each provided argument + provided_args: &[ExprId], skip_indices: &[u32], - ignore_arg_param_mismatch: bool, + // Whether the function is variadic, for example when imported from C + c_variadic: bool, ) { - let arg_count_mismatch = - !ignore_arg_param_mismatch && args.len() != param_tys.len() + skip_indices.len(); - if arg_count_mismatch { - self.push_diagnostic(InferenceDiagnostic::MismatchedArgCount { - call_expr: expr, - expected: param_tys.len() + skip_indices.len(), - found: args.len(), - }); + let interner = self.table.interner; + + // First, let's unify the formal method signature with the expectation eagerly. + // We use this to guide coercion inference; it's output is "fudged" which means + // any remaining type variables are assigned to new, unrelated variables. This + // is because the inference guidance here is only speculative. + let formal_output = self.table.resolve_vars_with_obligations(formal_output); + let expected_input_tys: Option> = expectation + .only_has_type(&mut self.table) + .and_then(|expected_output| { + self.table + .infer_ctxt + .fudge_inference_if_ok(|| { + let mut ocx = ObligationCtxt::new(&self.table.infer_ctxt); + + // Attempt to apply a subtyping relationship between the formal + // return type (likely containing type variables if the function + // is polymorphic) and the expected return type. + // No argument expectations are produced if unification fails. + let origin = ObligationCause::new(); + ocx.sup( + &origin, + self.table.param_env, + expected_output.to_nextsolver(interner), + formal_output, + )?; + if !ocx.select_where_possible().is_empty() { + return Err(crate::next_solver::TypeError::Mismatch); + } + + // Record all the argument types, with the args + // produced from the above subtyping unification. + Ok(Some( + formal_input_tys + .iter() + .map(|&ty| self.table.infer_ctxt.resolve_vars_if_possible(ty)) + .collect(), + )) + }) + .ok() + }) + .unwrap_or_default(); + + // If there are no external expectations at the call site, just use the types from the function defn + let expected_input_tys = if let Some(expected_input_tys) = &expected_input_tys { + assert_eq!(expected_input_tys.len(), formal_input_tys.len()); + expected_input_tys + } else { + formal_input_tys }; - // Quoting https://github.com/rust-lang/rust/blob/6ef275e6c3cb1384ec78128eceeb4963ff788dca/src/librustc_typeck/check/mod.rs#L3325 -- + let minimum_input_count = expected_input_tys.len(); + let provided_arg_count = provided_args.len() - skip_indices.len(); + + // Keep track of whether we *could possibly* be satisfied, i.e. whether we're on the happy path + // if the wrong number of arguments were supplied, we CAN'T be satisfied, + // and if we're c_variadic, the supplied arguments must be >= the minimum count from the function + // otherwise, they need to be identical, because rust doesn't currently support variadic functions + let args_count_matches = if c_variadic { + provided_arg_count >= minimum_input_count + } else { + provided_arg_count == minimum_input_count + }; + + if !args_count_matches { + self.push_diagnostic(InferenceDiagnostic::MismatchedArgCount { + call_expr, + expected: expected_input_tys.len() + skip_indices.len(), + found: provided_args.len(), + }); + } + + // We introduce a helper function to demand that a given argument satisfy a given input + // This is more complicated than just checking type equality, as arguments could be coerced + // This version writes those types back so further type checking uses the narrowed types + let demand_compatible = |this: &mut InferenceContext<'db>, idx| { + let formal_input_ty: crate::next_solver::Ty<'db> = formal_input_tys[idx]; + let expected_input_ty: crate::next_solver::Ty<'db> = expected_input_tys[idx]; + let provided_arg = provided_args[idx]; + + debug!("checking argument {}: {:?} = {:?}", idx, provided_arg, formal_input_ty); + + // We're on the happy path here, so we'll do a more involved check and write back types + // To check compatibility, we'll do 3 things: + // 1. Unify the provided argument with the expected type + let expectation = Expectation::rvalue_hint(this, expected_input_ty.to_chalk(interner)); + + let checked_ty = this + .infer_expr_inner(provided_arg, &expectation, ExprIsRead::Yes) + .to_nextsolver(interner); + + // 2. Coerce to the most detailed type that could be coerced + // to, which is `expected_ty` if `rvalue_hint` returns an + // `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise. + let coerced_ty = expectation + .only_has_type(&mut this.table) + .map(|it| it.to_nextsolver(interner)) + .unwrap_or(formal_input_ty); + + // Cause selection errors caused by resolving a single argument to point at the + // argument and not the call. This lets us customize the span pointed to in the + // fulfillment error to be more accurate. + let coerced_ty = this.table.resolve_vars_with_obligations(coerced_ty); + + let coerce_never = if this + .expr_guaranteed_to_constitute_read_for_never(provided_arg, ExprIsRead::Yes) + { + CoerceNever::Yes + } else { + CoerceNever::No + }; + let coerce_error = this + .coerce( + provided_arg.into(), + checked_ty, + coerced_ty, + AllowTwoPhase::Yes, + coerce_never, + ) + .err(); + if coerce_error.is_some() { + return Err((coerce_error, coerced_ty, checked_ty)); + } + + // 3. Check if the formal type is actually equal to the checked one + // and register any such obligations for future type checks. + let formal_ty_error = this + .table + .infer_ctxt + .at(&ObligationCause::new(), this.table.param_env) + .eq(DefineOpaqueTypes::Yes, formal_input_ty, coerced_ty); + + // If neither check failed, the types are compatible + match formal_ty_error { + Ok(crate::next_solver::infer::InferOk { obligations, value: () }) => { + this.table.register_predicates(obligations); + Ok(()) + } + Err(err) => Err((Some(err), coerced_ty, checked_ty)), + } + }; + + // Check the arguments. // We do this in a pretty awful way: first we type-check any arguments // that are not closures, then we type-check the closures. This is so // that we have more information about the types of arguments when we // type-check the functions. This isn't really the right way to do this. for check_closures in [false, true] { - let mut skip_indices = skip_indices.iter().copied().fuse().peekable(); - let param_iter = param_tys.iter().cloned().chain(repeat(self.err_ty())); - let expected_iter = expected_inputs - .iter() - .cloned() - .chain(param_iter.clone().skip(expected_inputs.len())); - for (idx, ((&arg, param_ty), expected_ty)) in - args.iter().zip(param_iter).zip(expected_iter).enumerate() - { - let is_closure = matches!(&self.body[arg], Expr::Closure { .. }); + // More awful hacks: before we check argument types, try to do + // an "opportunistic" trait resolution of any trait bounds on + // the call. This helps coercions. + if check_closures { + self.table.select_obligations_where_possible(); + } + + let mut skip_indices = skip_indices.iter().copied(); + // Check each argument, to satisfy the input it was provided for + // Visually, we're traveling down the diagonal of the compatibility matrix + for (idx, arg) in provided_args.iter().enumerate() { + if skip_indices.clone().next() == Some(idx as u32) { + skip_indices.next(); + continue; + } + + // For this check, we do *not* want to treat async coroutine closures (async blocks) + // as proper closures. Doing so would regress type inference when feeding + // the return value of an argument-position async block to an argument-position + // closure wrapped in a block. + // See . + let is_closure = if let Expr::Closure { closure_kind, .. } = self.body[*arg] { + !matches!(closure_kind, ClosureKind::Coroutine(_)) + } else { + false + }; if is_closure != check_closures { continue; } - while skip_indices.peek().is_some_and(|&i| i < idx as u32) { - skip_indices.next(); - } - if skip_indices.peek().copied() == Some(idx as u32) { + if idx >= minimum_input_count { + // Make sure we've checked this expr at least once. + self.infer_expr_no_expect(*arg, ExprIsRead::Yes); continue; } - // the difference between param_ty and expected here is that - // expected is the parameter when the expected *return* type is - // taken into account. So in `let _: &[i32] = identity(&[1, 2])` - // the expected type is already `&[i32]`, whereas param_ty is - // still an unbound type variable. We don't always want to force - // the parameter to coerce to the expected type (for example in - // `coerce_unsize_expected_type_4`). - let param_ty = self.normalize_associated_types_in(param_ty); - let expected_ty = self.normalize_associated_types_in(expected_ty); - let expected = Expectation::rvalue_hint(self, expected_ty); - // infer with the expected type we have... - let ty = self.infer_expr_inner(arg, &expected, ExprIsRead::Yes); - - // then coerce to either the expected type or just the formal parameter type - let coercion_target = if let Some(ty) = expected.only_has_type(&mut self.table) { - // if we are coercing to the expectation, unify with the - // formal parameter type to connect everything - self.unify(&ty, ¶m_ty); - ty - } else { - param_ty - }; - // The function signature may contain some unknown types, so we need to insert - // type vars here to avoid type mismatch false positive. - let coercion_target = self.insert_type_vars(coercion_target); - - // Any expression that produces a value of type `!` must have diverged, - // unless it's a place expression that isn't being read from, in which case - // diverging would be unsound since we may never actually read the `!`. - // e.g. `let _ = *never_ptr;` with `never_ptr: *const !`. - let coerce_never = - if self.expr_guaranteed_to_constitute_read_for_never(arg, ExprIsRead::Yes) { - CoerceNever::Yes - } else { - CoerceNever::No - }; - if self.coerce(Some(arg), &ty, &coercion_target, coerce_never).is_err() - && !arg_count_mismatch + if let Err((_error, expected, found)) = demand_compatible(self, idx) + && args_count_matches { + // Don't report type mismatches if there is a mismatch in args count. self.result.type_mismatches.insert( - arg.into(), - TypeMismatch { expected: coercion_target, actual: ty.clone() }, + (*arg).into(), + TypeMismatch { + expected: expected.to_chalk(interner), + actual: found.to_chalk(interner), + }, ); } } } + + if !args_count_matches {} } fn substs_for_method_call( @@ -2448,10 +2669,22 @@ impl InferenceContext<'_> { cb: impl FnOnce(&mut Self) -> T, ) -> (Option, T) { self.breakables.push({ - BreakableContext { kind, may_break: false, coerce: ty.map(CoerceMany::new), label } + BreakableContext { + kind, + may_break: false, + coerce: ty.map(|ty| CoerceMany::new(ty.to_nextsolver(self.table.interner))), + label, + } }); let res = cb(self); let ctx = self.breakables.pop().expect("breakable stack broken"); - (if ctx.may_break { ctx.coerce.map(|ctx| ctx.complete(self)) } else { None }, res) + ( + if ctx.may_break { + ctx.coerce.map(|ctx| ctx.complete(self).to_chalk(self.table.interner)) + } else { + None + }, + res, + ) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index 6781bc84d1c1..6e11fa942bdf 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -10,6 +10,8 @@ use hir_def::{ use hir_expand::name::Name; use stdx::TupleExt; +use crate::infer::AllowTwoPhase; +use crate::next_solver::mapping::{ChalkToNextSolver, NextSolverToChalk}; use crate::{ DeclContext, DeclOrigin, InferenceDiagnostic, Interner, Mutability, Scalar, Substitution, Ty, TyBuilder, TyExt, TyKind, @@ -303,16 +305,15 @@ impl InferenceContext<'_> { Pat::Path(path) => { let ty = self.infer_path(path, pat.into()).unwrap_or_else(|| self.err_ty()); let ty_inserted_vars = self.insert_type_vars_shallow(ty.clone()); - match self.table.coerce(&expected, &ty_inserted_vars, CoerceNever::Yes) { - Ok((adjustments, coerced_ty)) => { - if !adjustments.is_empty() { - self.result - .pat_adjustments - .entry(pat) - .or_default() - .extend(adjustments.into_iter().map(|adjust| adjust.target)); - } - self.write_pat_ty(pat, coerced_ty); + match self.coerce( + pat.into(), + expected.to_nextsolver(self.table.interner), + ty_inserted_vars.to_nextsolver(self.table.interner), + AllowTwoPhase::No, + CoerceNever::Yes, + ) { + Ok(coerced_ty) => { + self.write_pat_ty(pat, coerced_ty.to_chalk(self.table.interner)); return self.pat_ty_after_adjustment(pat); } Err(_) => { @@ -387,8 +388,14 @@ impl InferenceContext<'_> { ); // We are returning early to avoid the unifiability check below. let lhs_ty = self.insert_type_vars_shallow(result); - let ty = match self.coerce(None, &expected, &lhs_ty, CoerceNever::Yes) { - Ok(ty) => ty, + let ty = match self.coerce( + pat.into(), + expected.to_nextsolver(self.table.interner), + lhs_ty.to_nextsolver(self.table.interner), + AllowTwoPhase::No, + CoerceNever::Yes, + ) { + Ok(ty) => ty.to_chalk(self.table.interner), Err(_) => { self.result.type_mismatches.insert( pat.into(), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index 19b83d3c212d..77eaf83eec73 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -1,6 +1,6 @@ //! Unification and canonicalization logic. -use std::{fmt, mem}; +use std::fmt; use chalk_ir::{ CanonicalVarKind, FloatTy, IntTy, TyVariableKind, cast::Cast, fold::TypeFoldable, @@ -11,31 +11,37 @@ use hir_def::{AdtId, lang_item::LangItem}; use hir_expand::name::Name; use intern::sym; use rustc_hash::{FxHashMap, FxHashSet}; -use rustc_next_trait_solver::solve::HasChanged; -use rustc_type_ir::inherent::IntoKind; +use rustc_type_ir::inherent::Ty as _; use rustc_type_ir::{ - AliasRelationDirection, FloatVid, IntVid, TyVid, - inherent::{Span, Term as _}, + FloatVid, IntVid, TyVid, TypeVisitableExt, + inherent::{IntoKind, Span, Term as _}, relate::{Relate, solver_relating::RelateExt}, - solve::{Certainty, NoSolution}, + solve::{Certainty, GoalSource, NoSolution}, }; -use rustc_type_ir::{TypeSuperFoldable, TypeVisitableExt}; use smallvec::SmallVec; use triomphe::Arc; -use super::{InferOk, InferResult, InferenceContext, TypeError}; +use super::{InferResult, InferenceContext, TypeError}; +use crate::next_solver::ErrorGuaranteed; use crate::{ - AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue, DebruijnIndex, DomainGoal, - GenericArg, GenericArgData, Goal, GoalData, InEnvironment, InferenceVar, Interner, Lifetime, - OpaqueTyId, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Substitution, TraitEnvironment, - TraitRef, Ty, TyBuilder, TyExt, TyKind, VariableKind, WhereClause, + AliasTy, BoundVar, Canonical, Const, ConstValue, DebruijnIndex, GenericArg, GenericArgData, + Goal, GoalData, InEnvironment, InferenceVar, Interner, Lifetime, OpaqueTyId, ParamKind, + ProjectionTy, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, + VariableKind, WhereClause, consteval::unknown_const, db::HirDatabase, fold_generic_args, fold_tys_and_consts, + next_solver::infer::InferOk, next_solver::{ - self, Binder, DbInterner, Predicate, PredicateKind, SolverDefIds, Term, - infer::{DbInternerInferExt, InferCtxt, snapshot::CombinedSnapshot}, - mapping::{ChalkToNextSolver, InferenceVarExt, NextSolverToChalk}, + self, ClauseKind, DbInterner, ParamEnv, Predicate, PredicateKind, SolverDefIds, Term, + fulfill::FulfillmentCtxt, + infer::{ + DbInternerInferExt, InferCtxt, + snapshot::CombinedSnapshot, + traits::{Obligation, ObligationCause}, + }, + inspect::{InspectConfig, InspectGoal, ProofTreeVisitor}, + mapping::{ChalkToNextSolver, NextSolverToChalk}, }, to_chalk_trait_id, traits::{ @@ -50,43 +56,64 @@ impl<'db> InferenceContext<'db> { { self.table.canonicalize(t) } +} - pub(super) fn clauses_for_self_ty( - &mut self, - self_ty: InferenceVar, - ) -> SmallVec<[WhereClause; 4]> { - self.table.resolve_obligations_as_possible(); +struct NestedObligationsForSelfTy<'a, 'db> { + ctx: &'a InferenceTable<'db>, + self_ty: TyVid, + root_cause: &'a ObligationCause, + obligations_for_self_ty: &'a mut SmallVec<[Obligation<'db, Predicate<'db>>; 4]>, +} - let root = InferenceVar::from_vid(self.table.infer_ctxt.root_var(self_ty.to_vid())); - let pending_obligations = mem::take(&mut self.table.pending_obligations); - let obligations = pending_obligations - .iter() - .filter_map(|obligation| match obligation.to_chalk(self.table.interner).goal.data(Interner) { - GoalData::DomainGoal(DomainGoal::Holds(clause)) => { - let ty = match clause { - WhereClause::AliasEq(AliasEq { - alias: AliasTy::Projection(projection), - .. - }) => projection.self_type_parameter(self.db), - WhereClause::Implemented(trait_ref) => { - trait_ref.self_type_parameter(Interner) - } - WhereClause::TypeOutlives(to) => to.ty.clone(), - _ => return None, - }; - let ty = self.resolve_ty_shallow(&ty); - if matches!(ty.kind(Interner), TyKind::InferenceVar(iv, TyVariableKind::General) if *iv == root) { - Some(clause.clone()) - } else { - None - } - } - _ => None, - }) - .collect(); - self.table.pending_obligations = pending_obligations; +impl<'a, 'db> ProofTreeVisitor<'db> for NestedObligationsForSelfTy<'a, 'db> { + type Result = (); - obligations + fn config(&self) -> InspectConfig { + // Using an intentionally low depth to minimize the chance of future + // breaking changes in case we adapt the approach later on. This also + // avoids any hangs for exponentially growing proof trees. + InspectConfig { max_depth: 5 } + } + + fn visit_goal(&mut self, inspect_goal: &InspectGoal<'_, 'db>) { + // No need to walk into goal subtrees that certainly hold, since they + // wouldn't then be stalled on an infer var. + if inspect_goal.result() == Ok(Certainty::Yes) { + return; + } + + let db = self.ctx.interner; + let goal = inspect_goal.goal(); + if self.ctx.predicate_has_self_ty(goal.predicate, self.self_ty) + // We do not push the instantiated forms of goals as it would cause any + // aliases referencing bound vars to go from having escaping bound vars to + // being able to be normalized to an inference variable. + // + // This is mostly just a hack as arbitrary nested goals could still contain + // such aliases while having a different `GoalSource`. Closure signature inference + // however can't really handle *every* higher ranked `Fn` goal also being present + // in the form of `?c: Fn<(>::Assoc)`. + // + // This also just better matches the behaviour of the old solver where we do not + // encounter instantiated forms of goals, only nested goals that referred to bound + // vars from instantiated goals. + && !matches!(inspect_goal.source(), GoalSource::InstantiateHigherRanked) + { + self.obligations_for_self_ty.push(Obligation::new( + db, + self.root_cause.clone(), + goal.param_env, + goal.predicate, + )); + } + + // If there's a unique way to prove a given goal, recurse into + // that candidate. This means that for `impl Trait for () {}` + // and a `(): Trait` goal we recurse into the impl and look at + // the nested `?0: FnOnce(u32)` goal. + if let Some(candidate) = inspect_goal.unique_applicable_candidate() { + candidate.visit_nested_no_probe(self) + } } } @@ -119,7 +146,7 @@ pub fn could_unify_deeply( let ty2_with_vars = vars.apply(tys.value.1.clone(), Interner); let ty1_with_vars = table.normalize_associated_types_in(ty1_with_vars); let ty2_with_vars = table.normalize_associated_types_in(ty2_with_vars); - table.resolve_obligations_as_possible(); + table.select_obligations_where_possible(); table.propagate_diverging_flag(); let ty1_with_vars = table.resolve_completely(ty1_with_vars); let ty2_with_vars = table.resolve_completely(ty2_with_vars); @@ -186,35 +213,117 @@ bitflags::bitflags! { } #[derive(Clone)] -pub(crate) struct InferenceTable<'a> { - pub(crate) db: &'a dyn HirDatabase, - pub(crate) interner: DbInterner<'a>, +pub(crate) struct InferenceTable<'db> { + pub(crate) db: &'db dyn HirDatabase, + pub(crate) interner: DbInterner<'db>, pub(crate) trait_env: Arc, + pub(crate) param_env: ParamEnv<'db>, pub(crate) tait_coercion_table: Option>, - pub(crate) infer_ctxt: InferCtxt<'a>, + pub(crate) infer_ctxt: InferCtxt<'db>, diverging_tys: FxHashSet, - pending_obligations: Vec>>, + pub(super) fulfillment_cx: FulfillmentCtxt<'db>, } -pub(crate) struct InferenceTableSnapshot<'a> { +pub(crate) struct InferenceTableSnapshot<'db> { ctxt_snapshot: CombinedSnapshot, + obligations: FulfillmentCtxt<'db>, diverging_tys: FxHashSet, - pending_obligations: Vec>>, } -impl<'a> InferenceTable<'a> { - pub(crate) fn new(db: &'a dyn HirDatabase, trait_env: Arc) -> Self { +impl<'db> InferenceTable<'db> { + pub(crate) fn new(db: &'db dyn HirDatabase, trait_env: Arc) -> Self { let interner = DbInterner::new_with(db, Some(trait_env.krate), trait_env.block); + let infer_ctxt = interner.infer_ctxt().build(rustc_type_ir::TypingMode::Analysis { + defining_opaque_types_and_generators: SolverDefIds::new_from_iter(interner, []), + }); InferenceTable { db, interner, + param_env: trait_env.env.to_nextsolver(interner), trait_env, tait_coercion_table: None, - infer_ctxt: interner.infer_ctxt().build(rustc_type_ir::TypingMode::Analysis { - defining_opaque_types_and_generators: SolverDefIds::new_from_iter(interner, []), - }), + fulfillment_cx: FulfillmentCtxt::new(&infer_ctxt), + infer_ctxt, diverging_tys: FxHashSet::default(), - pending_obligations: Vec::new(), + } + } + + pub(crate) fn type_var_is_sized(&self, self_ty: TyVid) -> bool { + let Some(sized_did) = LangItem::Sized.resolve_trait(self.db, self.trait_env.krate) else { + return true; + }; + self.obligations_for_self_ty(self_ty).into_iter().any(|obligation| { + match obligation.predicate.kind().skip_binder() { + crate::next_solver::PredicateKind::Clause( + crate::next_solver::ClauseKind::Trait(data), + ) => data.def_id().0 == sized_did, + _ => false, + } + }) + } + + pub(super) fn obligations_for_self_ty( + &self, + self_ty: TyVid, + ) -> SmallVec<[Obligation<'db, Predicate<'db>>; 4]> { + let obligations = self.fulfillment_cx.pending_obligations(); + let mut obligations_for_self_ty = SmallVec::new(); + for obligation in obligations { + let mut visitor = NestedObligationsForSelfTy { + ctx: self, + self_ty, + obligations_for_self_ty: &mut obligations_for_self_ty, + root_cause: &obligation.cause, + }; + + let goal = obligation.as_goal(); + self.infer_ctxt.visit_proof_tree(goal, &mut visitor); + } + + obligations_for_self_ty.retain_mut(|obligation| { + obligation.predicate = self.infer_ctxt.resolve_vars_if_possible(obligation.predicate); + !obligation.predicate.has_placeholders() + }); + obligations_for_self_ty + } + + fn predicate_has_self_ty(&self, predicate: Predicate<'db>, expected_vid: TyVid) -> bool { + match predicate.kind().skip_binder() { + PredicateKind::Clause(ClauseKind::Trait(data)) => { + self.type_matches_expected_vid(expected_vid, data.self_ty()) + } + PredicateKind::Clause(ClauseKind::Projection(data)) => { + self.type_matches_expected_vid(expected_vid, data.projection_term.self_ty()) + } + PredicateKind::Clause(ClauseKind::ConstArgHasType(..)) + | PredicateKind::Subtype(..) + | PredicateKind::Coerce(..) + | PredicateKind::Clause(ClauseKind::RegionOutlives(..)) + | PredicateKind::Clause(ClauseKind::TypeOutlives(..)) + | PredicateKind::Clause(ClauseKind::WellFormed(..)) + | PredicateKind::DynCompatible(..) + | PredicateKind::NormalizesTo(..) + | PredicateKind::AliasRelate(..) + | PredicateKind::Clause(ClauseKind::ConstEvaluatable(..)) + | PredicateKind::ConstEquate(..) + | PredicateKind::Clause(ClauseKind::HostEffect(..)) + | PredicateKind::Clause(ClauseKind::UnstableFeature(_)) + | PredicateKind::Ambiguous => false, + } + } + + fn type_matches_expected_vid( + &self, + expected_vid: TyVid, + ty: crate::next_solver::Ty<'db>, + ) -> bool { + let ty = self.shallow_resolve(ty); + + match ty.kind() { + crate::next_solver::TyKind::Infer(rustc_type_ir::TyVar(found_vid)) => { + self.infer_ctxt.root_var(expected_vid) == self.infer_ctxt.root_var(found_vid) + } + _ => false, } } @@ -288,13 +397,13 @@ impl<'a> InferenceTable<'a> { .intern(Interner) } - pub(crate) fn canonicalize(&mut self, t: T) -> rustc_type_ir::Canonical, T> + pub(crate) fn canonicalize(&mut self, t: T) -> rustc_type_ir::Canonical, T> where - T: rustc_type_ir::TypeFoldable>, + T: rustc_type_ir::TypeFoldable>, { // try to resolve obligations before canonicalizing, since this might // result in new knowledge about variables - self.resolve_obligations_as_possible(); + self.select_obligations_where_possible(); self.infer_ctxt.canonicalize_response(t) } @@ -306,19 +415,24 @@ impl<'a> InferenceTable<'a> { /// to do it as well. pub(crate) fn normalize_associated_types_in(&mut self, ty: T) -> T where - T: ChalkToNextSolver<'a, U>, - U: NextSolverToChalk<'a, T> + rustc_type_ir::TypeFoldable>, + T: ChalkToNextSolver<'db, U>, + U: NextSolverToChalk<'db, T> + rustc_type_ir::TypeFoldable>, { self.normalize_associated_types_in_ns(ty.to_nextsolver(self.interner)) .to_chalk(self.interner) } + // FIXME: We should get rid of this method. We cannot deeply normalize during inference, only when finishing. + // Inference should use shallow normalization (`try_structurally_resolve_type()`) only, when needed. pub(crate) fn normalize_associated_types_in_ns(&mut self, ty: T) -> T where - T: rustc_type_ir::TypeFoldable>, + T: rustc_type_ir::TypeFoldable> + Clone, { let ty = self.resolve_vars_with_obligations(ty); - ty.fold_with(&mut Normalizer { table: self }) + self.infer_ctxt + .at(&ObligationCause::new(), self.param_env) + .deeply_normalize(ty.clone()) + .unwrap_or(ty) } /// Works almost same as [`Self::normalize_associated_types_in`], but this also resolves shallow @@ -388,8 +502,8 @@ impl<'a> InferenceTable<'a> { pub(crate) fn normalize_alias_ty( &mut self, - alias: crate::next_solver::Ty<'a>, - ) -> crate::next_solver::Ty<'a> { + alias: crate::next_solver::Ty<'db>, + ) -> crate::next_solver::Ty<'db> { let infer_term = self.infer_ctxt.next_ty_var(); let obligation = crate::next_solver::Predicate::new( self.interner, @@ -430,6 +544,10 @@ impl<'a> InferenceTable<'a> { self.new_var(TyVariableKind::General, false) } + pub(crate) fn next_ty_var(&mut self) -> crate::next_solver::Ty<'db> { + self.infer_ctxt.next_ty_var() + } + pub(crate) fn new_integer_var(&mut self) -> Ty { self.new_var(TyVariableKind::Integer, false) } @@ -454,6 +572,10 @@ impl<'a> InferenceTable<'a> { var.to_lifetime(Interner) } + pub(crate) fn next_region_var(&mut self) -> crate::next_solver::Region<'db> { + self.infer_ctxt.next_region_var() + } + pub(crate) fn resolve_with_fallback( &mut self, t: T, @@ -488,10 +610,10 @@ impl<'a> InferenceTable<'a> { pub(crate) fn instantiate_canonical_ns( &mut self, - canonical: rustc_type_ir::Canonical, T>, + canonical: rustc_type_ir::Canonical, T>, ) -> T where - T: rustc_type_ir::TypeFoldable>, + T: rustc_type_ir::TypeFoldable>, { self.infer_ctxt.instantiate_canonical(&canonical).0 } @@ -513,8 +635,8 @@ impl<'a> InferenceTable<'a> { pub(crate) fn resolve_completely(&mut self, t: T) -> T where - T: HasInterner + TypeFoldable + ChalkToNextSolver<'a, U>, - U: NextSolverToChalk<'a, T> + rustc_type_ir::TypeFoldable>, + T: HasInterner + TypeFoldable + ChalkToNextSolver<'db, U>, + U: NextSolverToChalk<'db, T> + rustc_type_ir::TypeFoldable>, { let t = self.resolve_with_fallback(t, &|_, _, d, _| d); let t = self.normalize_associated_types_in(t); @@ -566,7 +688,7 @@ impl<'a> InferenceTable<'a> { } /// Unify two relatable values (e.g. `Ty`) and register new trait goals that arise from that. - pub(crate) fn unify, U: Relate>>( + pub(crate) fn unify, U: Relate>>( &mut self, ty1: &T, ty2: &T, @@ -575,12 +697,20 @@ impl<'a> InferenceTable<'a> { Ok(r) => r, Err(_) => return false, }; - self.register_infer_ok(result); + self.register_obligations(result.goals); + true + } + + pub(crate) fn unify_ns>>(&mut self, lhs: T, rhs: T) -> bool { + let Ok(infer_ok) = self.try_unify_ns(lhs, rhs) else { + return false; + }; + self.register_obligations(infer_ok.goals); true } /// Unify two relatable values (e.g. `Ty`) and check whether trait goals which arise from that could be fulfilled - pub(crate) fn unify_deeply, U: Relate>>( + pub(crate) fn unify_deeply, U: Relate>>( &mut self, ty1: &T, ty2: &T, @@ -596,18 +726,27 @@ impl<'a> InferenceTable<'a> { /// Unify two relatable values (e.g. `Ty`) and return new trait goals arising from it, so the /// caller needs to deal with them. - pub(crate) fn try_unify, U: Relate>>( + pub(crate) fn try_unify, U: Relate>>( &mut self, t1: &T, t2: &T, - ) -> InferResult<'a, ()> { - let param_env = self.trait_env.env.to_nextsolver(self.interner); + ) -> InferResult<'db, ()> { let lhs = t1.to_nextsolver(self.interner); let rhs = t2.to_nextsolver(self.interner); + self.try_unify_ns(lhs, rhs) + } + + /// Unify two relatable values (e.g. `Ty`) and return new trait goals arising from it, so the + /// caller needs to deal with them. + pub(crate) fn try_unify_ns>>( + &mut self, + lhs: T, + rhs: T, + ) -> InferResult<'db, ()> { let variance = rustc_type_ir::Variance::Invariant; let span = crate::next_solver::Span::dummy(); - match self.infer_ctxt.relate(param_env, lhs, variance, rhs, span) { - Ok(goals) => Ok(InferOk { goals, value: () }), + match self.infer_ctxt.relate(self.param_env, lhs, variance, rhs, span) { + Ok(goals) => Ok(crate::infer::InferOk { goals, value: () }), Err(_) => Err(TypeError), } } @@ -616,17 +755,19 @@ impl<'a> InferenceTable<'a> { /// otherwise, return ty. #[tracing::instrument(skip(self))] pub(crate) fn resolve_ty_shallow(&mut self, ty: &Ty) -> Ty { - if !ty.data(Interner).flags.intersects(chalk_ir::TypeFlags::HAS_FREE_LOCAL_NAMES) { - return ty.clone(); - } - self.infer_ctxt - .resolve_vars_if_possible(ty.to_nextsolver(self.interner)) - .to_chalk(self.interner) + self.shallow_resolve(ty.to_nextsolver(self.interner)).to_chalk(self.interner) + } + + pub(crate) fn shallow_resolve( + &self, + ty: crate::next_solver::Ty<'db>, + ) -> crate::next_solver::Ty<'db> { + self.infer_ctxt.shallow_resolve(ty) } pub(crate) fn resolve_vars_with_obligations(&mut self, t: T) -> T where - T: rustc_type_ir::TypeFoldable>, + T: rustc_type_ir::TypeFoldable>, { use rustc_type_ir::TypeVisitableExt; @@ -640,7 +781,7 @@ impl<'a> InferenceTable<'a> { return t; } - self.resolve_obligations_as_possible(); + self.select_obligations_where_possible(); self.infer_ctxt.resolve_vars_if_possible(t) } @@ -659,42 +800,58 @@ impl<'a> InferenceTable<'a> { .to_chalk(self.interner) } - fn structurally_normalize_term(&mut self, term: Term<'a>) -> Term<'a> { - if term.to_alias_term().is_none() { - return term; - } - - let new_infer = self.infer_ctxt.next_term_var_of_kind(term); - - self.register_obligation(Predicate::new( - self.interner, - Binder::dummy(PredicateKind::AliasRelate( - term, - new_infer, - AliasRelationDirection::Equate, - )), - )); - self.resolve_obligations_as_possible(); - let res = self.infer_ctxt.resolve_vars_if_possible(new_infer); - if res == new_infer { term } else { res } + fn structurally_normalize_term(&mut self, term: Term<'db>) -> Term<'db> { + self.infer_ctxt + .at(&ObligationCause::new(), self.param_env) + .structurally_normalize_term(term, &mut self.fulfillment_cx) + .unwrap_or(term) } - pub(crate) fn snapshot(&mut self) -> InferenceTableSnapshot<'a> { + /// Try to resolve `ty` to a structural type, normalizing aliases. + /// + /// In case there is still ambiguity, the returned type may be an inference + /// variable. This is different from `structurally_resolve_type` which errors + /// in this case. + pub(crate) fn try_structurally_resolve_type( + &mut self, + ty: crate::next_solver::Ty<'db>, + ) -> crate::next_solver::Ty<'db> { + if let crate::next_solver::TyKind::Alias(..) = ty.kind() { + // We need to use a separate variable here as otherwise the temporary for + // `self.fulfillment_cx.borrow_mut()` is alive in the `Err` branch, resulting + // in a reentrant borrow, causing an ICE. + let result = self + .infer_ctxt + .at(&ObligationCause::misc(), self.param_env) + .structurally_normalize_ty(ty, &mut self.fulfillment_cx); + match result { + Ok(normalized_ty) => normalized_ty, + Err(_errors) => crate::next_solver::Ty::new_error(self.interner, ErrorGuaranteed), + } + } else { + self.resolve_vars_with_obligations(ty) + } + } + + pub(crate) fn snapshot(&mut self) -> InferenceTableSnapshot<'db> { let ctxt_snapshot = self.infer_ctxt.start_snapshot(); let diverging_tys = self.diverging_tys.clone(); - let pending_obligations = self.pending_obligations.clone(); - InferenceTableSnapshot { ctxt_snapshot, pending_obligations, diverging_tys } + let obligations = self.fulfillment_cx.clone(); + InferenceTableSnapshot { ctxt_snapshot, diverging_tys, obligations } } #[tracing::instrument(skip_all)] - pub(crate) fn rollback_to(&mut self, snapshot: InferenceTableSnapshot<'a>) { + pub(crate) fn rollback_to(&mut self, snapshot: InferenceTableSnapshot<'db>) { self.infer_ctxt.rollback_to(snapshot.ctxt_snapshot); self.diverging_tys = snapshot.diverging_tys; - self.pending_obligations = snapshot.pending_obligations; + self.fulfillment_cx = snapshot.obligations; } #[tracing::instrument(skip_all)] - pub(crate) fn run_in_snapshot(&mut self, f: impl FnOnce(&mut InferenceTable<'_>) -> T) -> T { + pub(crate) fn run_in_snapshot( + &mut self, + f: impl FnOnce(&mut InferenceTable<'db>) -> T, + ) -> T { let snapshot = self.snapshot(); let result = f(self); self.rollback_to(snapshot); @@ -703,7 +860,7 @@ impl<'a> InferenceTable<'a> { pub(crate) fn commit_if_ok( &mut self, - f: impl FnOnce(&mut InferenceTable<'_>) -> Result, + f: impl FnOnce(&mut InferenceTable<'db>) -> Result, ) -> Result { let snapshot = self.snapshot(); let result = f(self); @@ -735,7 +892,7 @@ impl<'a> InferenceTable<'a> { result.map(|m| m.1) } - pub(crate) fn register_obligation(&mut self, predicate: Predicate<'a>) { + pub(crate) fn register_obligation(&mut self, predicate: Predicate<'db>) { let goal = next_solver::Goal { param_env: self.trait_env.env.to_nextsolver(self.interner), predicate, @@ -746,7 +903,7 @@ impl<'a> InferenceTable<'a> { #[tracing::instrument(level = "debug", skip(self))] fn register_obligation_in_env( &mut self, - goal: next_solver::Goal<'a, next_solver::Predicate<'a>>, + goal: next_solver::Goal<'db, next_solver::Predicate<'db>>, ) { let result = next_trait_solve_in_ctxt(&self.infer_ctxt, goal); tracing::debug!(?result); @@ -754,119 +911,68 @@ impl<'a> InferenceTable<'a> { Ok((_, Certainty::Yes)) => {} Err(rustc_type_ir::solve::NoSolution) => {} Ok((_, Certainty::Maybe(_))) => { - self.pending_obligations.push(goal); + self.fulfillment_cx.register_predicate_obligation( + &self.infer_ctxt, + Obligation::new( + self.interner, + ObligationCause::new(), + goal.param_env, + goal.predicate, + ), + ); } } } - pub(crate) fn register_infer_ok(&mut self, infer_ok: InferOk<'a, T>) { - infer_ok.goals.into_iter().for_each(|goal| self.register_obligation_in_env(goal)); + pub(crate) fn register_infer_ok(&mut self, infer_ok: InferOk<'db, T>) -> T { + let InferOk { value, obligations } = infer_ok; + self.register_predicates(obligations); + value } - pub(crate) fn resolve_obligations_as_possible(&mut self) { - let _span = tracing::info_span!("resolve_obligations_as_possible").entered(); - let mut changed = true; - while mem::take(&mut changed) { - let mut obligations = mem::take(&mut self.pending_obligations); - - for goal in obligations.drain(..) { - tracing::debug!(obligation = ?goal); - - let result = next_trait_solve_in_ctxt(&self.infer_ctxt, goal); - let (has_changed, certainty) = match result { - Ok(result) => result, - Err(_) => { - continue; - } - }; - - if matches!(has_changed, HasChanged::Yes) { - changed = true; - } - - match certainty { - Certainty::Yes => {} - Certainty::Maybe(_) => self.pending_obligations.push(goal), - } - } - } - } - - pub(crate) fn fudge_inference>( + pub(crate) fn register_obligations( &mut self, - f: impl FnOnce(&mut Self) -> T, - ) -> T { - use chalk_ir::fold::TypeFolder; + obligations: Vec>>, + ) { + obligations.into_iter().for_each(|goal| self.register_obligation_in_env(goal)); + } - #[derive(chalk_derive::FallibleTypeFolder)] - #[has_interner(Interner)] - struct VarFudger<'a, 'b> { - table: &'a mut InferenceTable<'b>, - highest_known_var: InferenceVar, - } - impl TypeFolder for VarFudger<'_, '_> { - fn as_dyn(&mut self) -> &mut dyn TypeFolder { - self - } + pub(crate) fn select_obligations_where_possible(&mut self) { + self.fulfillment_cx.select_where_possible(&self.infer_ctxt); + } - fn interner(&self) -> Interner { - Interner - } - - fn fold_inference_ty( - &mut self, - var: chalk_ir::InferenceVar, - kind: TyVariableKind, - _outer_binder: chalk_ir::DebruijnIndex, - ) -> chalk_ir::Ty { - if var < self.highest_known_var { - var.to_ty(Interner, kind) - } else { - self.table.new_type_var() - } - } - - fn fold_inference_lifetime( - &mut self, - var: chalk_ir::InferenceVar, - _outer_binder: chalk_ir::DebruijnIndex, - ) -> chalk_ir::Lifetime { - if var < self.highest_known_var { - var.to_lifetime(Interner) - } else { - self.table.new_lifetime_var() - } - } - - fn fold_inference_const( - &mut self, - ty: chalk_ir::Ty, - var: chalk_ir::InferenceVar, - _outer_binder: chalk_ir::DebruijnIndex, - ) -> chalk_ir::Const { - if var < self.highest_known_var { - var.to_const(Interner, ty) - } else { - self.table.new_const_var(ty) - } - } + pub(super) fn register_predicate( + &mut self, + obligation: crate::next_solver::infer::traits::PredicateObligation<'db>, + ) { + if obligation.has_escaping_bound_vars() { + panic!("escaping bound vars in predicate {:?}", obligation); } - let snapshot = self.snapshot(); - let highest_known_var = self.new_type_var().inference_var(Interner).expect("inference_var"); - let result = f(self); - self.rollback_to(snapshot); - result - .fold_with(&mut VarFudger { table: self, highest_known_var }, DebruijnIndex::INNERMOST) + self.fulfillment_cx.register_predicate_obligation(&self.infer_ctxt, obligation); + } + + pub(super) fn register_predicates(&mut self, obligations: I) + where + I: IntoIterator>, + { + obligations.into_iter().for_each(|obligation| { + self.register_predicate(obligation); + }); } pub(crate) fn callable_sig( &mut self, ty: &Ty, num_args: usize, - ) -> Option<(Option, Vec, Ty)> { + ) -> Option<(Option, Vec>, crate::next_solver::Ty<'db>)> + { match ty.callable_sig(self.db) { - Some(sig) => Some((None, sig.params().to_vec(), sig.ret().clone())), + Some(sig) => Some(( + None, + sig.params().iter().map(|param| param.to_nextsolver(self.interner)).collect(), + sig.ret().to_nextsolver(self.interner), + )), None => { let (f, args_ty, return_ty) = self.callable_sig_from_fn_trait(ty, num_args)?; Some((Some(f), args_ty, return_ty)) @@ -878,7 +984,7 @@ impl<'a> InferenceTable<'a> { &mut self, ty: &Ty, num_args: usize, - ) -> Option<(FnTrait, Vec, Ty)> { + ) -> Option<(FnTrait, Vec>, crate::next_solver::Ty<'db>)> { for (fn_trait_name, output_assoc_name, subtraits) in [ (FnTrait::FnOnce, sym::Output, &[FnTrait::Fn, FnTrait::FnMut][..]), (FnTrait::AsyncFnMut, sym::CallRefFuture, &[FnTrait::AsyncFn]), @@ -898,7 +1004,7 @@ impl<'a> InferenceTable<'a> { ParamKind::Lifetime => unreachable!("Tuple with lifetime parameter"), ParamKind::Const(_) => unreachable!("Tuple with const parameter"), }; - arg_tys.push(arg.clone()); + arg_tys.push(arg.to_nextsolver(self.interner)); arg.cast(Interner) }) .build(); @@ -920,7 +1026,8 @@ impl<'a> InferenceTable<'a> { let goal: Goal = trait_ref.clone().cast(Interner); if !self.try_obligation(goal.clone()).no_solution() { self.register_obligation(goal.to_nextsolver(self.interner)); - let return_ty = self.normalize_projection_ty(projection); + let return_ty = + self.normalize_projection_ty(projection).to_nextsolver(self.interner); for &fn_x in subtraits { let fn_x_trait = fn_x.get_id(self.db, krate)?; trait_ref.trait_id = to_chalk_trait_id(fn_x_trait); @@ -969,8 +1076,8 @@ impl<'a> InferenceTable<'a> { /// Whenever you lower a user-written type, you should call this. pub(crate) fn process_user_written_ty(&mut self, ty: T) -> T where - T: HasInterner + TypeFoldable + ChalkToNextSolver<'a, U>, - U: NextSolverToChalk<'a, T> + rustc_type_ir::TypeFoldable>, + T: HasInterner + TypeFoldable + ChalkToNextSolver<'db, U>, + U: NextSolverToChalk<'db, T> + rustc_type_ir::TypeFoldable>, { self.process_remote_user_written_ty(ty) // FIXME: Register a well-formed obligation. @@ -980,13 +1087,14 @@ impl<'a> InferenceTable<'a> { /// while `process_user_written_ty()` should (but doesn't currently). pub(crate) fn process_remote_user_written_ty(&mut self, ty: T) -> T where - T: HasInterner + TypeFoldable + ChalkToNextSolver<'a, U>, - U: NextSolverToChalk<'a, T> + rustc_type_ir::TypeFoldable>, + T: HasInterner + TypeFoldable + ChalkToNextSolver<'db, U>, + U: NextSolverToChalk<'db, T> + rustc_type_ir::TypeFoldable>, { let ty = self.insert_type_vars(ty); // See https://github.com/rust-lang/rust/blob/cdb45c87e2cd43495379f7e867e3cc15dcee9f93/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs#L487-L495: // Even though the new solver only lazily normalizes usually, here we eagerly normalize so that not everything needs // to normalize before inspecting the `TyKind`. + // FIXME(next-solver): We should not deeply normalize here, only shallowly. self.normalize_associated_types_in(ty) } @@ -1074,7 +1182,10 @@ impl<'a> InferenceTable<'a> { impl fmt::Debug for InferenceTable<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("InferenceTable").finish() + f.debug_struct("InferenceTable") + .field("name", &self.infer_ctxt.inner.borrow().type_variable_storage) + .field("fulfillment_cx", &self.fulfillment_cx) + .finish() } } @@ -1091,7 +1202,7 @@ mod resolve { }; use rustc_type_ir::{FloatVid, IntVid, TyVid}; - #[derive(Copy, Clone, PartialEq, Eq)] + #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub(super) enum VarKind { Ty(TyVariableKind), Const, @@ -1264,62 +1375,3 @@ mod resolve { } } } - -/// This expects its input to be resolved. -struct Normalizer<'a, 'b> { - table: &'a mut InferenceTable<'b>, -} - -impl<'db> Normalizer<'_, 'db> { - fn normalize_alias_term( - &mut self, - alias_term: crate::next_solver::Term<'db>, - ) -> crate::next_solver::Term<'db> { - let infer_term = self.table.infer_ctxt.next_term_var_of_kind(alias_term); - let obligation = crate::next_solver::Predicate::new( - self.table.interner, - crate::next_solver::Binder::dummy(crate::next_solver::PredicateKind::AliasRelate( - alias_term, - infer_term, - rustc_type_ir::AliasRelationDirection::Equate, - )), - ); - self.table.register_obligation(obligation); - let term = self.table.resolve_vars_with_obligations(infer_term); - // Now normalize the result, because maybe it contains more aliases. - match term { - Term::Ty(term) => term.super_fold_with(self).into(), - Term::Const(term) => term.super_fold_with(self).into(), - } - } -} - -impl<'db> rustc_type_ir::TypeFolder> for Normalizer<'_, 'db> { - fn cx(&self) -> DbInterner<'db> { - self.table.interner - } - - fn fold_ty(&mut self, ty: crate::next_solver::Ty<'db>) -> crate::next_solver::Ty<'db> { - if !ty.has_aliases() { - return ty; - } - - let crate::next_solver::TyKind::Alias(..) = ty.kind() else { - return ty.super_fold_with(self); - }; - // FIXME: Handle escaping bound vars by replacing them with placeholders (relevant to when we handle HRTB only). - self.normalize_alias_term(ty.into()).expect_type() - } - - fn fold_const(&mut self, ct: crate::next_solver::Const<'db>) -> crate::next_solver::Const<'db> { - if !ct.has_aliases() { - return ct; - } - - let crate::next_solver::ConstKind::Unevaluated(..) = ct.kind() else { - return ct.super_fold_with(self); - }; - // FIXME: Handle escaping bound vars by replacing them with placeholders (relevant to when we handle HRTB only). - self.normalize_alias_term(ct.into()).expect_const() - } -} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index 2020a8b34b4f..f21673c732e4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -28,7 +28,6 @@ use crate::{ DbInterner, GenericArgs, ParamEnv, Ty, TyKind, TypingMode, infer::{DbInternerInferExt, traits::ObligationCause}, mapping::{ChalkToNextSolver, convert_args_for_result}, - project::solve_normalize::deeply_normalize, }, }; @@ -172,7 +171,7 @@ pub fn layout_of_ty_query<'db>( let cx = LayoutCx::new(dl); let infer_ctxt = interner.infer_ctxt().build(TypingMode::PostAnalysis); let cause = ObligationCause::dummy(); - let ty = deeply_normalize(infer_ctxt.at(&cause, ParamEnv::empty()), ty).unwrap_or(ty); + let ty = infer_ctxt.at(&cause, ParamEnv::empty()).deeply_normalize(ty).unwrap_or(ty); let result = match ty.kind() { TyKind::Adt(def, args) => { match def.inner().id { @@ -335,8 +334,8 @@ pub fn layout_of_ty_query<'db>( .clone() .substitute( Interner, - ClosureSubst(&convert_args_for_result(interner, args.inner())) - .parent_subst(), + &ClosureSubst(&convert_args_for_result(interner, args.inner())) + .parent_subst(db), ) .to_nextsolver(interner); db.layout_of_ty(ty, trait_env.clone()) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 02fdbf0ebf0d..3d3cbeaf4b2d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -115,7 +115,7 @@ pub use infer::{ Adjust, Adjustment, AutoBorrow, BindingMode, InferenceDiagnostic, InferenceResult, InferenceTyDiagnosticSource, OverloadedDeref, PointerCast, cast::CastError, - closure::{CaptureKind, CapturedItem}, + closure::analysis::{CaptureKind, CapturedItem}, could_coerce, could_unify, could_unify_deeply, }; pub use interner::Interner; @@ -133,8 +133,8 @@ pub use method_resolution::check_orphan_rules; pub use target_feature::TargetFeatures; pub use traits::TraitEnvironment; pub use utils::{ - Unsafety, all_super_traits, direct_super_traits, is_fn_unsafe_to_call, - target_feature_is_safe_in_target, + TargetFeatureIsSafeInTarget, Unsafety, all_super_traits, direct_super_traits, + is_fn_unsafe_to_call, target_feature_is_safe_in_target, }; pub use variance::Variance; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs index 5c29befe123c..2292e5c99413 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -17,6 +17,7 @@ use std::{ use base_db::Crate; use either::Either; +use hir_def::item_tree::FieldsShape; use hir_def::{ AdtId, AssocItemId, CallableDefId, ConstParamId, EnumVariantId, FunctionId, GenericDefId, GenericParamId, ImplId, ItemContainerId, LocalFieldId, Lookup, StructId, TraitId, TypeAliasId, @@ -34,6 +35,7 @@ use hir_def::{ TraitRef as HirTraitRef, TypeBound, TypeRef, TypeRefId, }, }; +use hir_def::{ConstId, StaticId}; use hir_expand::name::Name; use intern::sym; use la_arena::{Arena, ArenaMap, Idx}; @@ -53,6 +55,7 @@ use smallvec::{SmallVec, smallvec}; use stdx::never; use triomphe::Arc; +use crate::ValueTyDefId; use crate::{ FnAbi, ImplTraitId, Interner, ParamKind, TyDefId, TyLoweringDiagnostic, TyLoweringDiagnosticKind, @@ -206,6 +209,10 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } } + pub(crate) fn set_lifetime_elision(&mut self, lifetime_elision: LifetimeElisionKind<'db>) { + self.lifetime_elision = lifetime_elision; + } + pub(crate) fn with_debruijn( &mut self, debruijn: DebruijnIndex, @@ -958,6 +965,105 @@ pub(crate) fn ty_query<'db>(db: &'db dyn HirDatabase, def: TyDefId) -> EarlyBind } } +/// Build the declared type of a function. This should not need to look at the +/// function body. +fn type_for_fn<'db>(db: &'db dyn HirDatabase, def: FunctionId) -> EarlyBinder<'db, Ty<'db>> { + let interner = DbInterner::new_with(db, None, None); + EarlyBinder::bind(Ty::new_fn_def( + interner, + CallableDefId::FunctionId(def).into(), + GenericArgs::identity_for_item(interner, def.into()), + )) +} + +/// Build the declared type of a const. +fn type_for_const<'db>(db: &'db dyn HirDatabase, def: ConstId) -> EarlyBinder<'db, Ty<'db>> { + let resolver = def.resolver(db); + let data = db.const_signature(def); + let parent = def.loc(db).container; + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &data.store, + def.into(), + LifetimeElisionKind::AnonymousReportError, + ); + ctx.set_lifetime_elision(LifetimeElisionKind::for_const(ctx.interner, parent)); + EarlyBinder::bind(ctx.lower_ty(data.type_ref)) +} + +/// Build the declared type of a static. +fn type_for_static<'db>(db: &'db dyn HirDatabase, def: StaticId) -> EarlyBinder<'db, Ty<'db>> { + let resolver = def.resolver(db); + let module = resolver.module(); + let interner = DbInterner::new_with(db, Some(module.krate()), module.containing_block()); + let data = db.static_signature(def); + let parent = def.loc(db).container; + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &data.store, + def.into(), + LifetimeElisionKind::AnonymousReportError, + ); + ctx.set_lifetime_elision(LifetimeElisionKind::Elided(Region::new_static(ctx.interner))); + EarlyBinder::bind(ctx.lower_ty(data.type_ref)) +} + +/// Build the type of a tuple struct constructor. +fn type_for_struct_constructor<'db>( + db: &'db dyn HirDatabase, + def: StructId, +) -> Option>> { + let struct_data = def.fields(db); + match struct_data.shape { + FieldsShape::Record => None, + FieldsShape::Unit => Some(type_for_adt(db, def.into())), + FieldsShape::Tuple => { + let interner = DbInterner::new_with(db, None, None); + Some(EarlyBinder::bind(Ty::new_fn_def( + interner, + CallableDefId::StructId(def).into(), + GenericArgs::identity_for_item(interner, def.into()), + ))) + } + } +} + +/// Build the type of a tuple enum variant constructor. +fn type_for_enum_variant_constructor<'db>( + db: &'db dyn HirDatabase, + def: EnumVariantId, +) -> Option>> { + let struct_data = def.fields(db); + match struct_data.shape { + FieldsShape::Record => None, + FieldsShape::Unit => Some(type_for_adt(db, def.loc(db).parent.into())), + FieldsShape::Tuple => { + let interner = DbInterner::new_with(db, None, None); + Some(EarlyBinder::bind(Ty::new_fn_def( + interner, + CallableDefId::EnumVariantId(def).into(), + GenericArgs::identity_for_item(interner, def.loc(db).parent.into()), + ))) + } + } +} + +pub(crate) fn value_ty_query<'db>( + db: &'db dyn HirDatabase, + def: ValueTyDefId, +) -> Option>> { + match def { + ValueTyDefId::FunctionId(it) => Some(type_for_fn(db, it)), + ValueTyDefId::StructId(it) => type_for_struct_constructor(db, it), + ValueTyDefId::UnionId(it) => Some(type_for_adt(db, it.into())), + ValueTyDefId::EnumVariantId(it) => type_for_enum_variant_constructor(db, it), + ValueTyDefId::ConstId(it) => Some(type_for_const(db, it)), + ValueTyDefId::StaticId(it) => Some(type_for_static(db, it)), + } +} + pub(crate) fn type_for_type_alias_with_diagnostics_query<'db>( db: &'db dyn HirDatabase, t: TypeAliasId, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index ac85bf795072..a4427517a10b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -24,7 +24,7 @@ use triomphe::Arc; use crate::{ AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, GenericArgData, Goal, InEnvironment, Interner, Mutability, Scalar, Substitution, TraitEnvironment, TraitRef, - TraitRefExt, Ty, TyBuilder, TyExt, TyKind, TyVariableKind, VariableKind, WhereClause, + TraitRefExt, Ty, TyBuilder, TyExt, TyKind, VariableKind, WhereClause, autoderef::{self, AutoderefKind}, db::HirDatabase, from_chalk_trait_id, from_foreign_def_id, @@ -622,18 +622,23 @@ pub struct ReceiverAdjustments { } impl ReceiverAdjustments { - pub(crate) fn apply(&self, table: &mut InferenceTable<'_>, ty: Ty) -> (Ty, Vec) { - let mut ty = table.structurally_resolve_type(&ty); + pub(crate) fn apply( + &self, + table: &mut InferenceTable<'_>, + mut ty: Ty, + ) -> (Ty, Vec) { let mut adjust = Vec::new(); + let mut autoderef = table.autoderef(ty.to_nextsolver(table.interner)); + autoderef.next(); for _ in 0..self.autoderefs { - match autoderef::autoderef_step(table, ty.clone(), true, false) { + match autoderef.next() { None => { never!("autoderef not possible for {:?}", ty); ty = TyKind::Error.intern(Interner); break; } - Some((kind, new_ty)) => { - ty = new_ty.clone(); + Some((new_ty, _)) => { + ty = new_ty.to_chalk(autoderef.table.interner); let mutbl = match self.autoref { Some(AutorefOrPtrAdjustment::Autoref(m)) => Some(m), Some(AutorefOrPtrAdjustment::ToConstPtr) => Some(Mutability::Not), @@ -641,11 +646,11 @@ impl ReceiverAdjustments { None => None, }; adjust.push(Adjustment { - kind: Adjust::Deref(match kind { + kind: Adjust::Deref(match autoderef.steps().last().unwrap().1 { AutoderefKind::Overloaded => Some(OverloadedDeref(mutbl)), AutoderefKind::Builtin => None, }), - target: new_ty, + target: ty.clone(), }); } } @@ -1282,17 +1287,20 @@ fn iterate_method_candidates_by_receiver<'db>( name: Option<&Name>, callback: &mut dyn MethodCandidateCallback, ) -> ControlFlow<()> { + let interner = table.interner; let receiver_ty = table.instantiate_canonical_ns(receiver_ty); - let receiver_ty: crate::Ty = receiver_ty.to_chalk(table.interner); + let receiver_ty: crate::Ty = receiver_ty.to_chalk(interner); // We're looking for methods with *receiver* type receiver_ty. These could // be found in any of the derefs of receiver_ty, so we have to go through // that, including raw derefs. table.run_in_snapshot(|table| { let mut autoderef = - autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true, true); + autoderef::Autoderef::new_no_tracking(table, receiver_ty.to_nextsolver(interner)) + .include_raw_pointers() + .use_receiver_trait(); while let Some((self_ty, _)) = autoderef.next() { iterate_inherent_methods( - &self_ty, + &self_ty.to_chalk(interner), autoderef.table, name, Some(&receiver_ty), @@ -1308,15 +1316,18 @@ fn iterate_method_candidates_by_receiver<'db>( })?; table.run_in_snapshot(|table| { let mut autoderef = - autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true, true); + autoderef::Autoderef::new_no_tracking(table, receiver_ty.to_nextsolver(interner)) + .include_raw_pointers() + .use_receiver_trait(); while let Some((self_ty, _)) = autoderef.next() { - if matches!(self_ty.kind(Interner), TyKind::InferenceVar(_, TyVariableKind::General)) { + if matches!(self_ty.kind(), crate::next_solver::TyKind::Infer(rustc_type_ir::TyVar(_))) + { // don't try to resolve methods on unknown types return ControlFlow::Continue(()); } iterate_trait_method_candidates( - &self_ty, + &self_ty.to_chalk(interner), autoderef.table, traits_in_scope, name, @@ -1760,7 +1771,8 @@ fn is_valid_trait_method_candidate( for pred in infer_ok.into_obligations() { ctxt.register_predicate_obligation(&table.infer_ctxt, pred); } - check_that!(ctxt.select_all_or_error(&table.infer_ctxt).is_empty()); + // FIXME: Are we doing this correctly? Probably better to follow rustc more closely. + check_that!(ctxt.select_where_possible(&table.infer_ctxt).is_empty()); } check_that!(table.unify(receiver_ty, &expected_receiver)); @@ -1937,11 +1949,10 @@ fn autoderef_method_receiver<'db>( ) -> Vec<(next_solver::Canonical<'db, crate::next_solver::Ty<'db>>, ReceiverAdjustments)> { let interner = table.interner; let mut deref_chain = Vec::new(); - let mut autoderef = - autoderef::Autoderef::new_no_tracking(table, ty.to_chalk(interner), false, true); + let mut autoderef = autoderef::Autoderef::new_no_tracking(table, ty).use_receiver_trait(); while let Some((ty, derefs)) = autoderef.next() { deref_chain.push(( - autoderef.table.canonicalize(ty.to_nextsolver(interner)), + autoderef.table.canonicalize(ty), ReceiverAdjustments { autoref: None, autoderefs: derefs, unsize_array: false }, )); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs index 52df851c30d1..2c09fb9a89e7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs @@ -113,8 +113,13 @@ fn make_fetch_closure_field( let InternedClosure(def, _) = db.lookup_intern_closure(c.into()); let infer = db.infer(def); let (captures, _) = infer.closure_info(&c); - let parent_subst = ClosureSubst(subst).parent_subst(); - captures.get(f).expect("broken closure field").ty.clone().substitute(Interner, parent_subst) + let parent_subst = ClosureSubst(subst).parent_subst(db); + captures + .get(f) + .expect("broken closure field") + .ty + .clone() + .substitute(Interner, &parent_subst) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index cddd1fb7be52..6f950b8022c9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -712,13 +712,13 @@ impl<'db> Evaluator<'db> { let InternedClosure(def, _) = self.db.lookup_intern_closure(c.into()); let infer = self.db.infer(def); let (captures, _) = infer.closure_info(&c); - let parent_subst = ClosureSubst(subst).parent_subst(); + let parent_subst = ClosureSubst(subst).parent_subst(self.db); captures .get(f) .expect("broken closure field") .ty .clone() - .substitute(Interner, parent_subst) + .substitute(Interner, &parent_subst) }, self.crate_id, ); @@ -2772,7 +2772,7 @@ impl<'db> Evaluator<'db> { TyKind::Closure(closure, subst) => self.exec_closure( *closure, func_data, - &Substitution::from_iter(Interner, ClosureSubst(subst).parent_subst()), + &ClosureSubst(subst).parent_subst(self.db), destination, &args[1..], locals, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs index e27d334d2a99..f67778b0f12f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs @@ -4,6 +4,7 @@ use std::cmp::{self, Ordering}; use chalk_ir::TyKind; +use hir_def::signatures::FunctionSignature; use hir_def::{ CrateRootModuleId, builtin_type::{BuiltinInt, BuiltinUint}, @@ -63,17 +64,7 @@ impl Evaluator<'_> { let function_data = self.db.function_signature(def); let attrs = self.db.attrs(def.into()); - let is_intrinsic = attrs.by_key(sym::rustc_intrinsic).exists() - // Keep this around for a bit until extern "rustc-intrinsic" abis are no longer used - || (match &function_data.abi { - Some(abi) => *abi == sym::rust_dash_intrinsic, - None => match def.lookup(self.db).container { - hir_def::ItemContainerId::ExternBlockId(block) => { - block.abi(self.db) == Some(sym::rust_dash_intrinsic) - } - _ => false, - }, - }); + let is_intrinsic = FunctionSignature::is_intrinsic(self.db, def); if is_intrinsic { return self.exec_intrinsic( @@ -194,7 +185,7 @@ impl Evaluator<'_> { let infer = self.db.infer(closure_owner); let (captures, _) = infer.closure_info(id); let layout = self.layout(self_ty.to_nextsolver(interner))?; - let ty_iter = captures.iter().map(|c| c.ty(subst)); + let ty_iter = captures.iter().map(|c| c.ty(self.db, subst)); self.exec_clone_for_fields(ty_iter, layout, addr, def, locals, destination, span)?; } TyKind::Tuple(_, subst) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs index 5a56d99fbaa2..2a6e3a147a7d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs @@ -12,34 +12,37 @@ use crate::{ use super::{MirEvalError, interpret_mir}; fn eval_main(db: &TestDB, file_id: EditionedFileId) -> Result<(String, String), MirEvalError> { - let module_id = db.module_for_file(file_id.file_id(db)); - let def_map = module_id.def_map(db); - let scope = &def_map[module_id.local_id].scope; - let func_id = scope - .declarations() - .find_map(|x| match x { - hir_def::ModuleDefId::FunctionId(x) => { - if db.function_signature(x).name.display(db, Edition::CURRENT).to_string() == "main" - { - Some(x) - } else { - None + salsa::attach(db, || { + let module_id = db.module_for_file(file_id.file_id(db)); + let def_map = module_id.def_map(db); + let scope = &def_map[module_id.local_id].scope; + let func_id = scope + .declarations() + .find_map(|x| match x { + hir_def::ModuleDefId::FunctionId(x) => { + if db.function_signature(x).name.display(db, Edition::CURRENT).to_string() + == "main" + { + Some(x) + } else { + None + } } - } - _ => None, - }) - .expect("no main function found"); - let body = db - .monomorphized_mir_body( - func_id.into(), - Substitution::empty(Interner), - db.trait_environment(func_id.into()), - ) - .map_err(|e| MirEvalError::MirLowerError(func_id, e))?; + _ => None, + }) + .expect("no main function found"); + let body = db + .monomorphized_mir_body( + func_id.into(), + Substitution::empty(Interner), + db.trait_environment(func_id.into()), + ) + .map_err(|e| MirEvalError::MirLowerError(func_id, e))?; - let (result, output) = salsa::attach(db, || interpret_mir(db, body, false, None))?; - result?; - Ok((output.stdout().into_owned(), output.stderr().into_owned())) + let (result, output) = interpret_mir(db, body, false, None)?; + result?; + Ok((output.stdout().into_owned(), output.stderr().into_owned())) + }) } fn check_pass(#[rust_analyzer::rust_fixture] ra_fixture: &str) { @@ -53,43 +56,60 @@ fn check_pass_and_stdio( ) { let _tracing = setup_tracing(); let (db, file_ids) = TestDB::with_many_files(ra_fixture); - let file_id = *file_ids.last().unwrap(); - let x = eval_main(&db, file_id); - match x { - Err(e) => { - let mut err = String::new(); - let line_index = |size: TextSize| { - let mut size = u32::from(size) as usize; - let lines = ra_fixture.lines().enumerate(); - for (i, l) in lines { - if let Some(x) = size.checked_sub(l.len()) { - size = x; - } else { - return (i, size); + salsa::attach(&db, || { + let file_id = *file_ids.last().unwrap(); + let x = eval_main(&db, file_id); + match x { + Err(e) => { + let mut err = String::new(); + let line_index = |size: TextSize| { + let mut size = u32::from(size) as usize; + let lines = ra_fixture.lines().enumerate(); + for (i, l) in lines { + if let Some(x) = size.checked_sub(l.len()) { + size = x; + } else { + return (i, size); + } } - } - (usize::MAX, size) - }; - let span_formatter = |file, range: TextRange| { - format!("{:?} {:?}..{:?}", file, line_index(range.start()), line_index(range.end())) - }; - let krate = db.module_for_file(file_id.file_id(&db)).krate(); - e.pretty_print(&mut err, &db, span_formatter, DisplayTarget::from_crate(&db, krate)) + (usize::MAX, size) + }; + let span_formatter = |file, range: TextRange| { + format!( + "{:?} {:?}..{:?}", + file, + line_index(range.start()), + line_index(range.end()) + ) + }; + let krate = db.module_for_file(file_id.file_id(&db)).krate(); + e.pretty_print( + &mut err, + &db, + span_formatter, + DisplayTarget::from_crate(&db, krate), + ) .unwrap(); - panic!("Error in interpreting: {err}"); + panic!("Error in interpreting: {err}"); + } + Ok((stdout, stderr)) => { + assert_eq!(stdout, expected_stdout); + assert_eq!(stderr, expected_stderr); + } } - Ok((stdout, stderr)) => { - assert_eq!(stdout, expected_stdout); - assert_eq!(stderr, expected_stderr); - } - } + }) } fn check_panic(#[rust_analyzer::rust_fixture] ra_fixture: &str, expected_panic: &str) { let (db, file_ids) = TestDB::with_many_files(ra_fixture); - let file_id = *file_ids.last().unwrap(); - let e = eval_main(&db, file_id).unwrap_err(); - assert_eq!(e.is_panic().unwrap_or_else(|| panic!("unexpected error: {e:?}")), expected_panic); + salsa::attach(&db, || { + let file_id = *file_ids.last().unwrap(); + let e = eval_main(&db, file_id).unwrap_err(); + assert_eq!( + e.is_panic().unwrap_or_else(|| panic!("unexpected error: {e:?}")), + expected_panic + ); + }) } fn check_error_with( @@ -97,9 +117,11 @@ fn check_error_with( expect_err: impl FnOnce(MirEvalError) -> bool, ) { let (db, file_ids) = TestDB::with_many_files(ra_fixture); - let file_id = *file_ids.last().unwrap(); - let e = eval_main(&db, file_id).unwrap_err(); - assert!(expect_err(e)); + salsa::attach(&db, || { + let file_id = *file_ids.last().unwrap(); + let e = eval_main(&db, file_id).unwrap_err(); + assert!(expect_err(e)); + }) } #[test] @@ -492,7 +514,7 @@ fn main() { fn from_fn() { check_pass( r#" -//- minicore: fn, iterator +//- minicore: fn, iterator, sized struct FromFn(F); impl Option> Iterator for FromFn { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 45a1131e3339..50e416a66a64 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -2065,7 +2065,7 @@ pub fn mir_body_for_closure_query( }, }); ctx.result.param_locals.push(closure_local); - let Some(sig) = ClosureSubst(substs).sig_ty().callable_sig(db) else { + let Some(sig) = ClosureSubst(substs).sig_ty(db).callable_sig(db) else { implementation_error!("closure has not callable sig"); }; let resolver_guard = ctx.resolver.update_to_inner_scope(db, owner, expr); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs index 0225deebe4f3..34d8b649c981 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs @@ -9,14 +9,17 @@ pub mod fulfill; mod generic_arg; pub mod generics; pub mod infer; +pub(crate) mod inspect; pub mod interner; mod ir_print; pub mod mapping; +mod normalize; +pub mod obligation_ctxt; mod opaques; pub mod predicate; -pub(crate) mod project; mod region; mod solver; +mod structural_normalize; mod ty; pub mod util; @@ -37,8 +40,11 @@ pub type CanonicalVarValues<'db> = rustc_type_ir::CanonicalVarValues = rustc_type_ir::CanonicalVarKind>; pub type CanonicalQueryInput<'db, V> = rustc_type_ir::CanonicalQueryInput, V>; pub type AliasTy<'db> = rustc_type_ir::AliasTy>; +pub type FnSig<'db> = rustc_type_ir::FnSig>; pub type PolyFnSig<'db> = Binder<'db, rustc_type_ir::FnSig>>; pub type TypingMode<'db> = rustc_type_ir::TypingMode>; +pub type TypeError<'db> = rustc_type_ir::error::TypeError>; +pub type QueryResult<'db> = rustc_type_ir::solve::QueryResult>; pub type FxIndexMap = indexmap::IndexMap>; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs index 23789b06e828..0b3582051bc0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs @@ -4,16 +4,19 @@ use std::hash::Hash; use hir_def::{ConstParamId, TypeOrConstParamId}; use intern::{Interned, Symbol}; -use rustc_ast_ir::try_visit; -use rustc_ast_ir::visit::VisitorResult; +use rustc_ast_ir::{try_visit, visit::VisitorResult}; use rustc_type_ir::{ BoundVar, FlagComputation, Flags, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, - TypeVisitable, WithCachedTypeInfo, - inherent::{IntoKind, PlaceholderLike}, + TypeVisitable, TypeVisitableExt, WithCachedTypeInfo, + inherent::{IntoKind, ParamEnv as _, PlaceholderLike, SliceLike}, relate::Relate, }; -use crate::{ConstScalar, MemoryMap, interner::InternedWrapperNoDebug}; +use crate::{ + ConstScalar, MemoryMap, + interner::InternedWrapperNoDebug, + next_solver::{ClauseKind, ParamEnv}, +}; use super::{BoundVarKind, DbInterner, ErrorGuaranteed, GenericArgs, Placeholder, Ty}; @@ -96,6 +99,40 @@ impl std::fmt::Debug for ParamConst { } } +impl ParamConst { + pub fn find_const_ty_from_env<'db>(self, env: ParamEnv<'db>) -> Ty<'db> { + let mut candidates = env.caller_bounds().iter().filter_map(|clause| { + // `ConstArgHasType` are never desugared to be higher ranked. + match clause.kind().skip_binder() { + ClauseKind::ConstArgHasType(param_ct, ty) => { + assert!(!(param_ct, ty).has_escaping_bound_vars()); + + match param_ct.kind() { + ConstKind::Param(param_ct) if param_ct.index == self.index => Some(ty), + _ => None, + } + } + _ => None, + } + }); + + // N.B. it may be tempting to fix ICEs by making this function return + // `Option>` instead of `Ty<'db>`; however, this is generally + // considered to be a bandaid solution, since it hides more important + // underlying issues with how we construct generics and predicates of + // items. It's advised to fix the underlying issue rather than trying + // to modify this function. + let ty = candidates.next().unwrap_or_else(|| { + panic!("cannot find `{self:?}` in param-env: {env:#?}"); + }); + assert!( + candidates.next().is_none(), + "did not expect duplicate `ConstParamHasTy` for `{self:?}` in param-env: {env:#?}" + ); + ty + } +} + /// A type-level constant value. /// /// Represents a typed, fully evaluated constant. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs index a9c572d3f34e..1ae59beca272 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs @@ -17,7 +17,7 @@ pub enum Ctor { Enum(EnumVariantId), } -#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)] +#[derive(PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa::Supertype)] pub enum SolverDefId { AdtId(AdtId), ConstId(ConstId), @@ -32,6 +32,64 @@ pub enum SolverDefId { Ctor(Ctor), } +impl std::fmt::Debug for SolverDefId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let interner = DbInterner::conjure(); + let db = interner.db; + match *self { + SolverDefId::AdtId(AdtId::StructId(id)) => { + f.debug_tuple("AdtId").field(&db.struct_signature(id).name.as_str()).finish() + } + SolverDefId::AdtId(AdtId::EnumId(id)) => { + f.debug_tuple("AdtId").field(&db.enum_signature(id).name.as_str()).finish() + } + SolverDefId::AdtId(AdtId::UnionId(id)) => { + f.debug_tuple("AdtId").field(&db.union_signature(id).name.as_str()).finish() + } + SolverDefId::ConstId(id) => f + .debug_tuple("ConstId") + .field(&db.const_signature(id).name.as_ref().map_or("_", |name| name.as_str())) + .finish(), + SolverDefId::FunctionId(id) => { + f.debug_tuple("FunctionId").field(&db.function_signature(id).name.as_str()).finish() + } + SolverDefId::ImplId(id) => f.debug_tuple("ImplId").field(&id).finish(), + SolverDefId::StaticId(id) => { + f.debug_tuple("StaticId").field(&db.static_signature(id).name.as_str()).finish() + } + SolverDefId::TraitId(id) => { + f.debug_tuple("TraitId").field(&db.trait_signature(id).name.as_str()).finish() + } + SolverDefId::TypeAliasId(id) => f + .debug_tuple("TypeAliasId") + .field(&db.type_alias_signature(id).name.as_str()) + .finish(), + SolverDefId::InternedClosureId(id) => { + f.debug_tuple("InternedClosureId").field(&id).finish() + } + SolverDefId::InternedCoroutineId(id) => { + f.debug_tuple("InternedCoroutineId").field(&id).finish() + } + SolverDefId::InternedOpaqueTyId(id) => { + f.debug_tuple("InternedOpaqueTyId").field(&id).finish() + } + SolverDefId::Ctor(Ctor::Struct(id)) => { + f.debug_tuple("Ctor").field(&db.struct_signature(id).name.as_str()).finish() + } + SolverDefId::Ctor(Ctor::Enum(id)) => { + let parent_enum = id.loc(db).parent; + f.debug_tuple("Ctor") + .field(&format_args!( + "\"{}::{}\"", + db.enum_signature(parent_enum).name.as_str(), + parent_enum.enum_variants(db).variant_name_by_id(id).unwrap().as_str() + )) + .finish() + } + } + } +} + impl_from!( AdtId(StructId, EnumId, UnionId), ConstId, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs index 4258f4c7ac68..a8183ab42279 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs @@ -1,21 +1,28 @@ //! Fulfill loop for next-solver. -use std::marker::PhantomData; -use std::mem; -use std::ops::ControlFlow; -use std::vec::ExtractIf; +mod errors; -use rustc_next_trait_solver::delegate::SolverDelegate; -use rustc_next_trait_solver::solve::{ - GoalEvaluation, GoalStalledOn, HasChanged, SolverDelegateEvalExt, +use std::{marker::PhantomData, mem, ops::ControlFlow, vec::ExtractIf}; + +use rustc_hash::FxHashSet; +use rustc_next_trait_solver::{ + delegate::SolverDelegate, + solve::{GoalEvaluation, GoalStalledOn, HasChanged, SolverDelegateEvalExt}, +}; +use rustc_type_ir::{ + Interner, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, + inherent::{IntoKind, Span as _}, + solve::{Certainty, NoSolution}, }; -use rustc_type_ir::Interner; -use rustc_type_ir::inherent::Span as _; -use rustc_type_ir::solve::{Certainty, NoSolution}; -use crate::next_solver::infer::InferCtxt; -use crate::next_solver::infer::traits::{PredicateObligation, PredicateObligations}; -use crate::next_solver::{DbInterner, SolverContext, Span, TypingMode}; +use crate::next_solver::{ + DbInterner, SolverContext, SolverDefId, Span, Ty, TyKind, TypingMode, + infer::{ + InferCtxt, + traits::{PredicateObligation, PredicateObligations}, + }, + inspect::ProofTreeVisitor, +}; type PendingObligations<'db> = Vec<(PredicateObligation<'db>, Option>>)>; @@ -31,6 +38,7 @@ type PendingObligations<'db> = /// /// It is also likely that we want to use slightly different datastructures /// here as this will have to deal with far more root goals than `evaluate_all`. +#[derive(Debug, Clone)] pub struct FulfillmentCtxt<'db> { obligations: ObligationStorage<'db>, @@ -41,7 +49,7 @@ pub struct FulfillmentCtxt<'db> { usable_in_snapshot: usize, } -#[derive(Default, Debug)] +#[derive(Default, Debug, Clone)] struct ObligationStorage<'db> { /// Obligations which resulted in an overflow in fulfillment itself. /// @@ -123,10 +131,21 @@ impl<'db> FulfillmentCtxt<'db> { infcx: &InferCtxt<'db>, obligation: PredicateObligation<'db>, ) { - assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); + // FIXME: See the comment in `select_where_possible()`. + // assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); self.obligations.register(obligation, None); } + pub(crate) fn register_predicate_obligations( + &mut self, + infcx: &InferCtxt<'db>, + obligations: impl IntoIterator>, + ) { + // FIXME: See the comment in `select_where_possible()`. + // assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); + obligations.into_iter().for_each(|obligation| self.obligations.register(obligation, None)); + } + pub(crate) fn collect_remaining_errors( &mut self, infcx: &InferCtxt<'db>, @@ -143,7 +162,11 @@ impl<'db> FulfillmentCtxt<'db> { &mut self, infcx: &InferCtxt<'db>, ) -> Vec> { - assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); + // FIXME(next-solver): We should bring this assertion back. Currently it panics because + // there are places which use `InferenceTable` and open a snapshot and register obligations + // and select. They should use a different `ObligationCtxt` instead. Then we'll be also able + // to not put the obligations queue in `InferenceTable`'s snapshots. + // assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); let mut errors = Vec::new(); loop { let mut any_changed = false; @@ -216,9 +239,94 @@ impl<'db> FulfillmentCtxt<'db> { self.obligations.has_pending_obligations() } - fn pending_obligations(&self) -> PredicateObligations<'db> { + pub(crate) fn pending_obligations(&self) -> PredicateObligations<'db> { self.obligations.clone_pending() } + + pub(crate) fn drain_stalled_obligations_for_coroutines( + &mut self, + infcx: &InferCtxt<'db>, + ) -> PredicateObligations<'db> { + let stalled_coroutines = match infcx.typing_mode() { + TypingMode::Analysis { defining_opaque_types_and_generators } => { + defining_opaque_types_and_generators + } + TypingMode::Coherence + | TypingMode::Borrowck { defining_opaque_types: _ } + | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ } + | TypingMode::PostAnalysis => return Default::default(), + }; + let stalled_coroutines = stalled_coroutines.inner(); + + if stalled_coroutines.is_empty() { + return Default::default(); + } + + self.obligations + .drain_pending(|obl| { + infcx.probe(|_| { + infcx + .visit_proof_tree( + obl.as_goal(), + &mut StalledOnCoroutines { + stalled_coroutines, + cache: Default::default(), + }, + ) + .is_break() + }) + }) + .into_iter() + .map(|(o, _)| o) + .collect() + } +} + +/// Detect if a goal is stalled on a coroutine that is owned by the current typeck root. +/// +/// This function can (erroneously) fail to detect a predicate, i.e. it doesn't need to +/// be complete. However, this will lead to ambiguity errors, so we want to make it +/// accurate. +/// +/// This function can be also return false positives, which will lead to poor diagnostics +/// so we want to keep this visitor *precise* too. +pub struct StalledOnCoroutines<'a, 'db> { + pub stalled_coroutines: &'a [SolverDefId], + pub cache: FxHashSet>, +} + +impl<'db> ProofTreeVisitor<'db> for StalledOnCoroutines<'_, 'db> { + type Result = ControlFlow<()>; + + fn visit_goal(&mut self, inspect_goal: &super::inspect::InspectGoal<'_, 'db>) -> Self::Result { + inspect_goal.goal().predicate.visit_with(self)?; + + if let Some(candidate) = inspect_goal.unique_applicable_candidate() { + candidate.visit_nested_no_probe(self) + } else { + ControlFlow::Continue(()) + } + } +} + +impl<'db> TypeVisitor> for StalledOnCoroutines<'_, 'db> { + type Result = ControlFlow<()>; + + fn visit_ty(&mut self, ty: Ty<'db>) -> Self::Result { + if !self.cache.insert(ty) { + return ControlFlow::Continue(()); + } + + if let TyKind::Coroutine(def_id, _) = ty.kind() + && self.stalled_coroutines.contains(&def_id.into()) + { + ControlFlow::Break(()) + } else if ty.has_coroutines() { + ty.super_visit_with(self) + } else { + ControlFlow::Continue(()) + } + } } #[derive(Debug)] @@ -227,3 +335,10 @@ pub enum NextSolverError<'db> { Ambiguity(PredicateObligation<'db>), Overflow(PredicateObligation<'db>), } + +impl NextSolverError<'_> { + #[inline] + pub fn is_true_error(&self) -> bool { + matches!(self, NextSolverError::TrueError(_)) + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill/errors.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill/errors.rs new file mode 100644 index 000000000000..6cd9e55acf0e --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill/errors.rs @@ -0,0 +1,1332 @@ +//! Trait solving error diagnosis and reporting. +//! +//! This code isn't used by rust-analyzer (it should, but then it'll probably be better to re-port it from rustc). +//! It's only there because without it, debugging trait solver errors is a nightmare. + +use std::{fmt::Debug, ops::ControlFlow}; + +use rustc_next_trait_solver::solve::{GoalEvaluation, SolverDelegateEvalExt}; +use rustc_type_ir::{ + AliasRelationDirection, AliasTermKind, HostEffectPredicate, Interner, PredicatePolarity, + error::ExpectedFound, + inherent::{IntoKind, PlaceholderConst, SliceLike, Span as _}, + lang_items::SolverTraitLangItem, + solve::{CandidateSource, Certainty, GoalSource, MaybeCause, NoSolution}, +}; +use tracing::{instrument, trace}; + +use crate::next_solver::{ + AliasTerm, Binder, ClauseKind, Const, ConstKind, DbInterner, PolyTraitPredicate, PredicateKind, + SolverContext, SolverDefId, Span, Term, TraitPredicate, Ty, TyKind, TypeError, + fulfill::NextSolverError, + infer::{ + InferCtxt, + select::SelectionError, + traits::{Obligation, ObligationCause, PredicateObligation, PredicateObligations}, + }, + inspect::{self, ProofTreeVisitor}, + normalize::deeply_normalize_for_diagnostics, +}; + +#[derive(Debug)] +pub struct FulfillmentError<'db> { + pub obligation: PredicateObligation<'db>, + pub code: FulfillmentErrorCode<'db>, + /// Diagnostics only: the 'root' obligation which resulted in + /// the failure to process `obligation`. This is the obligation + /// that was initially passed to `register_predicate_obligation` + pub root_obligation: PredicateObligation<'db>, +} + +impl<'db> FulfillmentError<'db> { + pub fn new( + obligation: PredicateObligation<'db>, + code: FulfillmentErrorCode<'db>, + root_obligation: PredicateObligation<'db>, + ) -> FulfillmentError<'db> { + FulfillmentError { obligation, code, root_obligation } + } + + pub fn is_true_error(&self) -> bool { + match self.code { + FulfillmentErrorCode::Select(_) + | FulfillmentErrorCode::Project(_) + | FulfillmentErrorCode::Subtype(_, _) + | FulfillmentErrorCode::ConstEquate(_, _) => true, + FulfillmentErrorCode::Cycle(_) | FulfillmentErrorCode::Ambiguity { overflow: _ } => { + false + } + } + } +} + +#[derive(Debug, Clone)] +pub enum FulfillmentErrorCode<'db> { + /// Inherently impossible to fulfill; this trait is implemented if and only + /// if it is already implemented. + Cycle(PredicateObligations<'db>), + Select(SelectionError<'db>), + Project(MismatchedProjectionTypes<'db>), + Subtype(ExpectedFound>, TypeError<'db>), // always comes from a SubtypePredicate + ConstEquate(ExpectedFound>, TypeError<'db>), + Ambiguity { + /// Overflow is only `Some(suggest_recursion_limit)` when using the next generation + /// trait solver `-Znext-solver`. With the old solver overflow is eagerly handled by + /// emitting a fatal error instead. + overflow: Option, + }, +} + +#[derive(Debug, Clone)] +pub struct MismatchedProjectionTypes<'db> { + pub err: TypeError<'db>, +} + +pub(super) fn fulfillment_error_for_no_solution<'db>( + infcx: &InferCtxt<'db>, + root_obligation: PredicateObligation<'db>, +) -> FulfillmentError<'db> { + let obligation = find_best_leaf_obligation(infcx, &root_obligation, false); + + let code = match obligation.predicate.kind().skip_binder() { + PredicateKind::Clause(ClauseKind::Projection(_)) => { + FulfillmentErrorCode::Project( + // FIXME: This could be a `Sorts` if the term is a type + MismatchedProjectionTypes { err: TypeError::Mismatch }, + ) + } + PredicateKind::Clause(ClauseKind::ConstArgHasType(ct, expected_ty)) => { + let ct_ty = match ct.kind() { + ConstKind::Unevaluated(uv) => { + infcx.interner.type_of(uv.def).instantiate(infcx.interner, uv.args) + } + ConstKind::Param(param_ct) => param_ct.find_const_ty_from_env(obligation.param_env), + ConstKind::Value(cv) => cv.ty, + kind => panic!( + "ConstArgHasWrongType failed but we don't know how to compute type for {kind:?}" + ), + }; + FulfillmentErrorCode::Select(SelectionError::ConstArgHasWrongType { + ct, + ct_ty, + expected_ty, + }) + } + PredicateKind::NormalizesTo(..) => { + FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch }) + } + PredicateKind::AliasRelate(_, _, _) => { + FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch }) + } + PredicateKind::Subtype(pred) => { + let (a, b) = infcx.enter_forall_and_leak_universe( + obligation.predicate.kind().rebind((pred.a, pred.b)), + ); + let expected_found = ExpectedFound::new(a, b); + FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found)) + } + PredicateKind::Coerce(pred) => { + let (a, b) = infcx.enter_forall_and_leak_universe( + obligation.predicate.kind().rebind((pred.a, pred.b)), + ); + let expected_found = ExpectedFound::new(b, a); + FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found)) + } + PredicateKind::Clause(_) | PredicateKind::DynCompatible(_) | PredicateKind::Ambiguous => { + FulfillmentErrorCode::Select(SelectionError::Unimplemented) + } + PredicateKind::ConstEquate(..) => { + panic!("unexpected goal: {obligation:?}") + } + }; + + FulfillmentError { obligation, code, root_obligation } +} + +pub(super) fn fulfillment_error_for_stalled<'db>( + infcx: &InferCtxt<'db>, + root_obligation: PredicateObligation<'db>, +) -> FulfillmentError<'db> { + let (code, refine_obligation) = infcx.probe(|_| { + match <&SolverContext<'db>>::from(infcx).evaluate_root_goal( + root_obligation.as_goal(), + Span::dummy(), + None, + ) { + Ok(GoalEvaluation { certainty: Certainty::Maybe(MaybeCause::Ambiguity), .. }) => { + (FulfillmentErrorCode::Ambiguity { overflow: None }, true) + } + Ok(GoalEvaluation { + certainty: + Certainty::Maybe(MaybeCause::Overflow { + suggest_increasing_limit, + keep_constraints: _, + }), + .. + }) => ( + FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) }, + // Don't look into overflows because we treat overflows weirdly anyways. + // We discard the inference constraints from overflowing goals, so + // recomputing the goal again during `find_best_leaf_obligation` may apply + // inference guidance that makes other goals go from ambig -> pass, for example. + // + // FIXME: We should probably just look into overflows here. + false, + ), + Ok(GoalEvaluation { certainty: Certainty::Yes, .. }) => { + panic!( + "did not expect successful goal when collecting ambiguity errors for `{:?}`", + infcx.resolve_vars_if_possible(root_obligation.predicate), + ) + } + Err(_) => { + panic!( + "did not expect selection error when collecting ambiguity errors for `{:?}`", + infcx.resolve_vars_if_possible(root_obligation.predicate), + ) + } + } + }); + + FulfillmentError { + obligation: if refine_obligation { + find_best_leaf_obligation(infcx, &root_obligation, true) + } else { + root_obligation.clone() + }, + code, + root_obligation, + } +} + +pub(super) fn fulfillment_error_for_overflow<'db>( + infcx: &InferCtxt<'db>, + root_obligation: PredicateObligation<'db>, +) -> FulfillmentError<'db> { + FulfillmentError { + obligation: find_best_leaf_obligation(infcx, &root_obligation, true), + code: FulfillmentErrorCode::Ambiguity { overflow: Some(true) }, + root_obligation, + } +} + +#[instrument(level = "debug", skip(infcx), ret)] +fn find_best_leaf_obligation<'db>( + infcx: &InferCtxt<'db>, + obligation: &PredicateObligation<'db>, + consider_ambiguities: bool, +) -> PredicateObligation<'db> { + let obligation = infcx.resolve_vars_if_possible(obligation.clone()); + // FIXME: we use a probe here as the `BestObligation` visitor does not + // check whether it uses candidates which get shadowed by where-bounds. + // + // We should probably fix the visitor to not do so instead, as this also + // means the leaf obligation may be incorrect. + let obligation = infcx + .fudge_inference_if_ok(|| { + infcx + .visit_proof_tree( + obligation.as_goal(), + &mut BestObligation { obligation: obligation.clone(), consider_ambiguities }, + ) + .break_value() + .ok_or(()) + }) + .unwrap_or(obligation); + deeply_normalize_for_diagnostics(infcx, obligation.param_env, obligation) +} + +struct BestObligation<'db> { + obligation: PredicateObligation<'db>, + consider_ambiguities: bool, +} + +impl<'db> BestObligation<'db> { + fn with_derived_obligation( + &mut self, + derived_obligation: PredicateObligation<'db>, + and_then: impl FnOnce(&mut Self) -> >::Result, + ) -> >::Result { + let old_obligation = std::mem::replace(&mut self.obligation, derived_obligation); + let res = and_then(self); + self.obligation = old_obligation; + res + } + + /// Filter out the candidates that aren't interesting to visit for the + /// purposes of reporting errors. For ambiguities, we only consider + /// candidates that may hold. For errors, we only consider candidates that + /// *don't* hold and which have impl-where clauses that also don't hold. + fn non_trivial_candidates<'a>( + &self, + goal: &'a inspect::InspectGoal<'a, 'db>, + ) -> Vec> { + let mut candidates = goal.candidates(); + match self.consider_ambiguities { + true => { + // If we have an ambiguous obligation, we must consider *all* candidates + // that hold, or else we may guide inference causing other goals to go + // from ambig -> pass/fail. + candidates.retain(|candidate| candidate.result().is_ok()); + } + false => { + // We always handle rigid alias candidates separately as we may not add them for + // aliases whose trait bound doesn't hold. + candidates.retain(|c| !matches!(c.kind(), inspect::ProbeKind::RigidAlias { .. })); + // If we have >1 candidate, one may still be due to "boring" reasons, like + // an alias-relate that failed to hold when deeply evaluated. We really + // don't care about reasons like this. + if candidates.len() > 1 { + candidates.retain(|candidate| { + goal.infcx().probe(|_| { + candidate.instantiate_nested_goals().iter().any(|nested_goal| { + matches!( + nested_goal.source(), + GoalSource::ImplWhereBound + | GoalSource::AliasBoundConstCondition + | GoalSource::InstantiateHigherRanked + | GoalSource::AliasWellFormed + ) && nested_goal.result().is_err() + }) + }) + }); + } + } + } + + candidates + } + + /// HACK: We walk the nested obligations for a well-formed arg manually, + /// since there's nontrivial logic in `wf.rs` to set up an obligation cause. + /// Ideally we'd be able to track this better. + fn visit_well_formed_goal( + &mut self, + candidate: &inspect::InspectCandidate<'_, 'db>, + term: Term<'db>, + ) -> ControlFlow> { + let infcx = candidate.goal().infcx(); + let param_env = candidate.goal().goal().param_env; + + for obligation in wf::unnormalized_obligations(infcx, param_env, term).into_iter().flatten() + { + let nested_goal = candidate + .instantiate_proof_tree_for_nested_goal(GoalSource::Misc, obligation.as_goal()); + // Skip nested goals that aren't the *reason* for our goal's failure. + match (self.consider_ambiguities, nested_goal.result()) { + (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {} + _ => continue, + } + + self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?; + } + + ControlFlow::Break(self.obligation.clone()) + } + + /// If a normalization of an associated item or a trait goal fails without trying any + /// candidates it's likely that normalizing its self type failed. We manually detect + /// such cases here. + fn detect_error_in_self_ty_normalization( + &mut self, + goal: &inspect::InspectGoal<'_, 'db>, + self_ty: Ty<'db>, + ) -> ControlFlow> { + assert!(!self.consider_ambiguities); + let interner = goal.infcx().interner; + if let TyKind::Alias(..) = self_ty.kind() { + let infer_term = goal.infcx().next_ty_var(); + let pred = PredicateKind::AliasRelate( + self_ty.into(), + infer_term.into(), + AliasRelationDirection::Equate, + ); + let obligation = Obligation::new( + interner, + self.obligation.cause.clone(), + goal.goal().param_env, + pred, + ); + self.with_derived_obligation(obligation, |this| { + goal.infcx().visit_proof_tree_at_depth( + goal.goal().with(interner, pred), + goal.depth() + 1, + this, + ) + }) + } else { + ControlFlow::Continue(()) + } + } + + /// When a higher-ranked projection goal fails, check that the corresponding + /// higher-ranked trait goal holds or not. This is because the process of + /// instantiating and then re-canonicalizing the binder of the projection goal + /// forces us to be unable to see that the leak check failed in the nested + /// `NormalizesTo` goal, so we don't fall back to the rigid projection check + /// that should catch when a projection goal fails due to an unsatisfied trait + /// goal. + fn detect_trait_error_in_higher_ranked_projection( + &mut self, + goal: &inspect::InspectGoal<'_, 'db>, + ) -> ControlFlow> { + let interner = goal.infcx().interner; + if let Some(projection_clause) = goal.goal().predicate.as_projection_clause() + && !projection_clause.bound_vars().is_empty() + { + let pred = projection_clause.map_bound(|proj| proj.projection_term.trait_ref(interner)); + let obligation = Obligation::new( + interner, + self.obligation.cause.clone(), + goal.goal().param_env, + deeply_normalize_for_diagnostics(goal.infcx(), goal.goal().param_env, pred), + ); + self.with_derived_obligation(obligation, |this| { + goal.infcx().visit_proof_tree_at_depth( + goal.goal().with(interner, pred), + goal.depth() + 1, + this, + ) + }) + } else { + ControlFlow::Continue(()) + } + } + + /// It is likely that `NormalizesTo` failed without any applicable candidates + /// because the alias is not well-formed. + /// + /// As we only enter `RigidAlias` candidates if the trait bound of the associated type + /// holds, we discard these candidates in `non_trivial_candidates` and always manually + /// check this here. + fn detect_non_well_formed_assoc_item( + &mut self, + goal: &inspect::InspectGoal<'_, 'db>, + alias: AliasTerm<'db>, + ) -> ControlFlow> { + let interner = goal.infcx().interner; + let obligation = Obligation::new( + interner, + self.obligation.cause.clone(), + goal.goal().param_env, + alias.trait_ref(interner), + ); + self.with_derived_obligation(obligation, |this| { + goal.infcx().visit_proof_tree_at_depth( + goal.goal().with(interner, alias.trait_ref(interner)), + goal.depth() + 1, + this, + ) + }) + } + + /// If we have no candidates, then it's likely that there is a + /// non-well-formed alias in the goal. + fn detect_error_from_empty_candidates( + &mut self, + goal: &inspect::InspectGoal<'_, 'db>, + ) -> ControlFlow> { + let interner = goal.infcx().interner; + let pred_kind = goal.goal().predicate.kind(); + + match pred_kind.no_bound_vars() { + Some(PredicateKind::Clause(ClauseKind::Trait(pred))) => { + self.detect_error_in_self_ty_normalization(goal, pred.self_ty())?; + } + Some(PredicateKind::NormalizesTo(pred)) => { + if let AliasTermKind::ProjectionTy | AliasTermKind::ProjectionConst = + pred.alias.kind(interner) + { + self.detect_error_in_self_ty_normalization(goal, pred.alias.self_ty())?; + self.detect_non_well_formed_assoc_item(goal, pred.alias)?; + } + } + Some(_) | None => {} + } + + ControlFlow::Break(self.obligation.clone()) + } +} + +impl<'db> ProofTreeVisitor<'db> for BestObligation<'db> { + type Result = ControlFlow>; + + #[instrument(level = "trace", skip(self, goal), fields(goal = ?goal.goal()))] + fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'db>) -> Self::Result { + let interner = goal.infcx().interner; + // Skip goals that aren't the *reason* for our goal's failure. + match (self.consider_ambiguities, goal.result()) { + (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {} + _ => return ControlFlow::Continue(()), + } + + let pred = goal.goal().predicate; + + let candidates = self.non_trivial_candidates(goal); + let candidate = match candidates.as_slice() { + [candidate] => candidate, + [] => return self.detect_error_from_empty_candidates(goal), + _ => return ControlFlow::Break(self.obligation.clone()), + }; + + // // Don't walk into impls that have `do_not_recommend`. + // if let inspect::ProbeKind::TraitCandidate { + // source: CandidateSource::Impl(impl_def_id), + // result: _, + // } = candidate.kind() + // && interner.do_not_recommend_impl(impl_def_id) + // { + // trace!("#[do_not_recommend] -> exit"); + // return ControlFlow::Break(self.obligation.clone()); + // } + + // FIXME: Also, what about considering >1 layer up the stack? May be necessary + // for normalizes-to. + let child_mode = match pred.kind().skip_binder() { + PredicateKind::Clause(ClauseKind::Trait(trait_pred)) => { + ChildMode::Trait(pred.kind().rebind(trait_pred)) + } + PredicateKind::Clause(ClauseKind::HostEffect(host_pred)) => { + ChildMode::Host(pred.kind().rebind(host_pred)) + } + PredicateKind::NormalizesTo(normalizes_to) + if matches!( + normalizes_to.alias.kind(interner), + AliasTermKind::ProjectionTy | AliasTermKind::ProjectionConst + ) => + { + ChildMode::Trait(pred.kind().rebind(TraitPredicate { + trait_ref: normalizes_to.alias.trait_ref(interner), + polarity: PredicatePolarity::Positive, + })) + } + PredicateKind::Clause(ClauseKind::WellFormed(term)) => { + return self.visit_well_formed_goal(candidate, term); + } + _ => ChildMode::PassThrough, + }; + + let nested_goals = candidate.instantiate_nested_goals(); + + // If the candidate requires some `T: FnPtr` bound which does not hold should not be treated as + // an actual candidate, instead we should treat them as if the impl was never considered to + // have potentially applied. As if `impl Trait for for<..> fn(..A) -> R` was written + // instead of `impl Trait for T`. + // + // We do this as a separate loop so that we do not choose to tell the user about some nested + // goal before we encounter a `T: FnPtr` nested goal. + for nested_goal in &nested_goals { + if let Some(poly_trait_pred) = nested_goal.goal().predicate.as_trait_clause() + && interner + .is_trait_lang_item(poly_trait_pred.def_id(), SolverTraitLangItem::FnPtrTrait) + && let Err(NoSolution) = nested_goal.result() + { + return ControlFlow::Break(self.obligation.clone()); + } + } + + let mut impl_where_bound_count = 0; + for nested_goal in nested_goals { + trace!(nested_goal = ?(nested_goal.goal(), nested_goal.source(), nested_goal.result())); + + let nested_pred = nested_goal.goal().predicate; + + let make_obligation = || Obligation { + cause: ObligationCause::dummy(), + param_env: nested_goal.goal().param_env, + predicate: nested_pred, + recursion_depth: self.obligation.recursion_depth + 1, + }; + + let obligation; + match (child_mode, nested_goal.source()) { + ( + ChildMode::Trait(_) | ChildMode::Host(_), + GoalSource::Misc | GoalSource::TypeRelating | GoalSource::NormalizeGoal(_), + ) => { + continue; + } + (ChildMode::Trait(parent_trait_pred), GoalSource::ImplWhereBound) => { + obligation = make_obligation(); + impl_where_bound_count += 1; + } + ( + ChildMode::Host(parent_host_pred), + GoalSource::ImplWhereBound | GoalSource::AliasBoundConstCondition, + ) => { + obligation = make_obligation(); + impl_where_bound_count += 1; + } + // Skip over a higher-ranked predicate. + (_, GoalSource::InstantiateHigherRanked) => { + obligation = self.obligation.clone(); + } + (ChildMode::PassThrough, _) + | (_, GoalSource::AliasWellFormed | GoalSource::AliasBoundConstCondition) => { + obligation = make_obligation(); + } + } + + self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?; + } + + // alias-relate may fail because the lhs or rhs can't be normalized, + // and therefore is treated as rigid. + if let Some(PredicateKind::AliasRelate(lhs, rhs, _)) = pred.kind().no_bound_vars() { + goal.infcx().visit_proof_tree_at_depth( + goal.goal().with(interner, ClauseKind::WellFormed(lhs)), + goal.depth() + 1, + self, + )?; + goal.infcx().visit_proof_tree_at_depth( + goal.goal().with(interner, ClauseKind::WellFormed(rhs)), + goal.depth() + 1, + self, + )?; + } + + self.detect_trait_error_in_higher_ranked_projection(goal)?; + + ControlFlow::Break(self.obligation.clone()) + } +} + +#[derive(Debug, Copy, Clone)] +enum ChildMode<'db> { + // Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`, + // and skip all `GoalSource::Misc`, which represent useless obligations + // such as alias-eq which may not hold. + Trait(PolyTraitPredicate<'db>), + // Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`, + // and skip all `GoalSource::Misc`, which represent useless obligations + // such as alias-eq which may not hold. + Host(Binder<'db, HostEffectPredicate>>), + // Skip trying to derive an `ObligationCause` from this obligation, and + // report *all* sub-obligations as if they came directly from the parent + // obligation. + PassThrough, +} + +impl<'db> NextSolverError<'db> { + pub fn to_debuggable_error(&self, infcx: &InferCtxt<'db>) -> FulfillmentError<'db> { + match self { + NextSolverError::TrueError(obligation) => { + fulfillment_error_for_no_solution(infcx, obligation.clone()) + } + NextSolverError::Ambiguity(obligation) => { + fulfillment_error_for_stalled(infcx, obligation.clone()) + } + NextSolverError::Overflow(obligation) => { + fulfillment_error_for_overflow(infcx, obligation.clone()) + } + } + } +} + +mod wf { + use std::iter; + + use hir_def::ItemContainerId; + use rustc_type_ir::inherent::{ + AdtDef, BoundExistentialPredicates, GenericArg, GenericArgs as _, IntoKind, SliceLike, + Term as _, Ty as _, + }; + use rustc_type_ir::lang_items::SolverTraitLangItem; + use rustc_type_ir::{ + Interner, PredicatePolarity, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, + TypeVisitor, + }; + use tracing::{debug, instrument, trace}; + + use crate::next_solver::infer::InferCtxt; + use crate::next_solver::infer::traits::{ + Obligation, ObligationCause, PredicateObligation, PredicateObligations, + }; + use crate::next_solver::{ + AliasTerm, Binder, ClauseKind, Const, ConstKind, Ctor, DbInterner, ExistentialPredicate, + GenericArgs, ParamEnv, Predicate, PredicateKind, Region, SolverDefId, Term, TraitPredicate, + TraitRef, Ty, TyKind, + }; + + /// Compute the predicates that are required for a type to be well-formed. + /// + /// This is only intended to be used in the new solver, since it does not + /// take into account recursion depth or proper error-reporting spans. + pub fn unnormalized_obligations<'db>( + infcx: &InferCtxt<'db>, + param_env: ParamEnv<'db>, + term: Term<'db>, + ) -> Option> { + debug_assert_eq!(term, infcx.resolve_vars_if_possible(term)); + + // However, if `arg` IS an unresolved inference variable, returns `None`, + // because we are not able to make any progress at all. This is to prevent + // cycles where we say "?0 is WF if ?0 is WF". + if term.is_infer() { + return None; + } + + let mut wf = + WfPredicates { infcx, param_env, out: PredicateObligations::new(), recursion_depth: 0 }; + wf.add_wf_preds_for_term(term); + Some(wf.out) + } + + struct WfPredicates<'a, 'db> { + infcx: &'a InferCtxt<'db>, + param_env: ParamEnv<'db>, + out: PredicateObligations<'db>, + recursion_depth: usize, + } + + /// Controls whether we "elaborate" supertraits and so forth on the WF + /// predicates. This is a kind of hack to address #43784. The + /// underlying problem in that issue was a trait structure like: + /// + /// ```ignore (illustrative) + /// trait Foo: Copy { } + /// trait Bar: Foo { } + /// impl Foo for T { } + /// impl Bar for T { } + /// ``` + /// + /// Here, in the `Foo` impl, we will check that `T: Copy` holds -- but + /// we decide that this is true because `T: Bar` is in the + /// where-clauses (and we can elaborate that to include `T: + /// Copy`). This wouldn't be a problem, except that when we check the + /// `Bar` impl, we decide that `T: Foo` must hold because of the `Foo` + /// impl. And so nowhere did we check that `T: Copy` holds! + /// + /// To resolve this, we elaborate the WF requirements that must be + /// proven when checking impls. This means that (e.g.) the `impl Bar + /// for T` will be forced to prove not only that `T: Foo` but also `T: + /// Copy` (which it won't be able to do, because there is no `Copy` + /// impl for `T`). + #[derive(Debug, PartialEq, Eq, Copy, Clone)] + enum Elaborate { + All, + None, + } + + impl<'a, 'db> WfPredicates<'a, 'db> { + fn interner(&self) -> DbInterner<'db> { + self.infcx.interner + } + + /// Pushes the obligations required for `trait_ref` to be WF into `self.out`. + fn add_wf_preds_for_trait_pred( + &mut self, + trait_pred: TraitPredicate<'db>, + elaborate: Elaborate, + ) { + let tcx = self.interner(); + let trait_ref = trait_pred.trait_ref; + + // Negative trait predicates don't require supertraits to hold, just + // that their args are WF. + if trait_pred.polarity == PredicatePolarity::Negative { + self.add_wf_preds_for_negative_trait_pred(trait_ref); + return; + } + + // if the trait predicate is not const, the wf obligations should not be const as well. + let obligations = self.nominal_obligations(trait_ref.def_id.0.into(), trait_ref.args); + + debug!("compute_trait_pred obligations {:?}", obligations); + let param_env = self.param_env; + let depth = self.recursion_depth; + + let extend = |PredicateObligation { predicate, mut cause, .. }| { + Obligation::with_depth(tcx, cause, depth, param_env, predicate) + }; + + if let Elaborate::All = elaborate { + let implied_obligations = rustc_type_ir::elaborate::elaborate(tcx, obligations); + let implied_obligations = implied_obligations.map(extend); + self.out.extend(implied_obligations); + } else { + self.out.extend(obligations); + } + + self.out.extend( + trait_ref + .args + .iter() + .enumerate() + .filter_map(|(i, arg)| arg.as_term().map(|t| (i, t))) + .filter(|(_, term)| !term.has_escaping_bound_vars()) + .map(|(i, term)| { + let mut cause = ObligationCause::misc(); + // The first arg is the self ty - use the correct span for it. + Obligation::with_depth( + tcx, + cause, + depth, + param_env, + ClauseKind::WellFormed(term), + ) + }), + ); + } + + // Compute the obligations that are required for `trait_ref` to be WF, + // given that it is a *negative* trait predicate. + fn add_wf_preds_for_negative_trait_pred(&mut self, trait_ref: TraitRef<'db>) { + for arg in trait_ref.args { + if let Some(term) = arg.as_term() { + self.add_wf_preds_for_term(term); + } + } + } + + /// Pushes the obligations required for an alias (except inherent) to be WF + /// into `self.out`. + fn add_wf_preds_for_alias_term(&mut self, data: AliasTerm<'db>) { + // A projection is well-formed if + // + // (a) its predicates hold (*) + // (b) its args are wf + // + // (*) The predicates of an associated type include the predicates of + // the trait that it's contained in. For example, given + // + // trait A: Clone { + // type X where T: Copy; + // } + // + // The predicates of `<() as A>::X` are: + // [ + // `(): Sized` + // `(): Clone` + // `(): A` + // `i32: Sized` + // `i32: Clone` + // `i32: Copy` + // ] + let obligations = self.nominal_obligations(data.def_id, data.args); + self.out.extend(obligations); + + self.add_wf_preds_for_projection_args(data.args); + } + + fn add_wf_preds_for_projection_args(&mut self, args: GenericArgs<'db>) { + let tcx = self.interner(); + let cause = ObligationCause::new(); + let param_env = self.param_env; + let depth = self.recursion_depth; + + self.out.extend( + args.iter() + .filter_map(|arg| arg.as_term()) + .filter(|term| !term.has_escaping_bound_vars()) + .map(|term| { + Obligation::with_depth( + tcx, + cause.clone(), + depth, + param_env, + ClauseKind::WellFormed(term), + ) + }), + ); + } + + fn require_sized(&mut self, subty: Ty<'db>) { + if !subty.has_escaping_bound_vars() { + let cause = ObligationCause::new(); + let trait_ref = TraitRef::new( + self.interner(), + self.interner().require_trait_lang_item(SolverTraitLangItem::Sized), + [subty], + ); + self.out.push(Obligation::with_depth( + self.interner(), + cause, + self.recursion_depth, + self.param_env, + Binder::dummy(trait_ref), + )); + } + } + + /// Pushes all the predicates needed to validate that `term` is WF into `out`. + #[instrument(level = "debug", skip(self))] + fn add_wf_preds_for_term(&mut self, term: Term<'db>) { + term.visit_with(self); + debug!(?self.out); + } + + #[instrument(level = "debug", skip(self))] + fn nominal_obligations( + &mut self, + def_id: SolverDefId, + args: GenericArgs<'db>, + ) -> PredicateObligations<'db> { + // PERF: `Sized`'s predicates include `MetaSized`, but both are compiler implemented marker + // traits, so `MetaSized` will always be WF if `Sized` is WF and vice-versa. Determining + // the nominal obligations of `Sized` would in-effect just elaborate `MetaSized` and make + // the compiler do a bunch of work needlessly. + if let SolverDefId::TraitId(def_id) = def_id + && self.interner().is_trait_lang_item(def_id.into(), SolverTraitLangItem::Sized) + { + return Default::default(); + } + + self.interner() + .predicates_of(def_id) + .iter_instantiated(self.interner(), args) + .map(|pred| { + let cause = ObligationCause::new(); + Obligation::with_depth( + self.interner(), + cause, + self.recursion_depth, + self.param_env, + pred, + ) + }) + .filter(|pred| !pred.has_escaping_bound_vars()) + .collect() + } + + fn add_wf_preds_for_dyn_ty( + &mut self, + ty: Ty<'db>, + data: &[Binder<'db, ExistentialPredicate<'db>>], + region: Region<'db>, + ) { + // Imagine a type like this: + // + // trait Foo { } + // trait Bar<'c> : 'c { } + // + // &'b (Foo+'c+Bar<'d>) + // ^ + // + // In this case, the following relationships must hold: + // + // 'b <= 'c + // 'd <= 'c + // + // The first conditions is due to the normal region pointer + // rules, which say that a reference cannot outlive its + // referent. + // + // The final condition may be a bit surprising. In particular, + // you may expect that it would have been `'c <= 'd`, since + // usually lifetimes of outer things are conservative + // approximations for inner things. However, it works somewhat + // differently with trait objects: here the idea is that if the + // user specifies a region bound (`'c`, in this case) it is the + // "master bound" that *implies* that bounds from other traits are + // all met. (Remember that *all bounds* in a type like + // `Foo+Bar+Zed` must be met, not just one, hence if we write + // `Foo<'x>+Bar<'y>`, we know that the type outlives *both* 'x and + // 'y.) + // + // Note: in fact we only permit builtin traits, not `Bar<'d>`, I + // am looking forward to the future here. + if !data.has_escaping_bound_vars() && !region.has_escaping_bound_vars() { + let implicit_bounds = object_region_bounds(self.interner(), data); + + let explicit_bound = region; + + self.out.reserve(implicit_bounds.len()); + for implicit_bound in implicit_bounds { + let cause = ObligationCause::new(); + let outlives = Binder::dummy(rustc_type_ir::OutlivesPredicate( + explicit_bound, + implicit_bound, + )); + self.out.push(Obligation::with_depth( + self.interner(), + cause, + self.recursion_depth, + self.param_env, + outlives, + )); + } + + // We don't add any wf predicates corresponding to the trait ref's generic arguments + // which allows code like this to compile: + // ```rust + // trait Trait {} + // fn foo(_: &dyn Trait<[u32]>) {} + // ``` + } + } + } + + impl<'a, 'db> TypeVisitor> for WfPredicates<'a, 'db> { + type Result = (); + + fn visit_ty(&mut self, t: Ty<'db>) -> Self::Result { + debug!("wf bounds for t={:?} t.kind={:#?}", t, t.kind()); + + let tcx = self.interner(); + + match t.kind() { + TyKind::Bool + | TyKind::Char + | TyKind::Int(..) + | TyKind::Uint(..) + | TyKind::Float(..) + | TyKind::Error(_) + | TyKind::Str + | TyKind::CoroutineWitness(..) + | TyKind::Never + | TyKind::Param(_) + | TyKind::Bound(..) + | TyKind::Placeholder(..) + | TyKind::Foreign(..) => { + // WfScalar, WfParameter, etc + } + + // Can only infer to `TyKind::Int(_) | TyKind::Uint(_)`. + TyKind::Infer(rustc_type_ir::IntVar(_)) => {} + + // Can only infer to `TyKind::Float(_)`. + TyKind::Infer(rustc_type_ir::FloatVar(_)) => {} + + TyKind::Slice(subty) => { + self.require_sized(subty); + } + + TyKind::Array(subty, len) => { + self.require_sized(subty); + // Note that the len being WF is implicitly checked while visiting. + // Here we just check that it's of type usize. + let cause = ObligationCause::new(); + self.out.push(Obligation::with_depth( + tcx, + cause, + self.recursion_depth, + self.param_env, + Binder::dummy(PredicateKind::Clause(ClauseKind::ConstArgHasType( + len, + Ty::new_unit(self.interner()), + ))), + )); + } + + TyKind::Pat(base_ty, pat) => { + self.require_sized(base_ty); + } + + TyKind::Tuple(tys) => { + if let Some((_last, rest)) = tys.split_last() { + for &elem in rest { + self.require_sized(elem); + } + } + } + + TyKind::RawPtr(_, _) => { + // Simple cases that are WF if their type args are WF. + } + + TyKind::Alias( + rustc_type_ir::Projection | rustc_type_ir::Opaque | rustc_type_ir::Free, + data, + ) => { + let obligations = self.nominal_obligations(data.def_id, data.args); + self.out.extend(obligations); + } + TyKind::Alias(rustc_type_ir::Inherent, data) => { + return; + } + + TyKind::Adt(def, args) => { + // WfNominalType + let obligations = self.nominal_obligations(def.def_id().0.into(), args); + self.out.extend(obligations); + } + + TyKind::FnDef(did, args) => { + // HACK: Check the return type of function definitions for + // well-formedness to mostly fix #84533. This is still not + // perfect and there may be ways to abuse the fact that we + // ignore requirements with escaping bound vars. That's a + // more general issue however. + let fn_sig = tcx.fn_sig(did).instantiate(tcx, args); + fn_sig.output().skip_binder().visit_with(self); + + let did = match did.0 { + hir_def::CallableDefId::FunctionId(id) => id.into(), + hir_def::CallableDefId::StructId(id) => SolverDefId::Ctor(Ctor::Struct(id)), + hir_def::CallableDefId::EnumVariantId(id) => { + SolverDefId::Ctor(Ctor::Enum(id)) + } + }; + let obligations = self.nominal_obligations(did, args); + self.out.extend(obligations); + } + + TyKind::Ref(r, rty, _) => { + // WfReference + if !r.has_escaping_bound_vars() && !rty.has_escaping_bound_vars() { + let cause = ObligationCause::new(); + self.out.push(Obligation::with_depth( + tcx, + cause, + self.recursion_depth, + self.param_env, + Binder::dummy(PredicateKind::Clause(ClauseKind::TypeOutlives( + rustc_type_ir::OutlivesPredicate(rty, r), + ))), + )); + } + } + + TyKind::Coroutine(did, args, ..) => { + // Walk ALL the types in the coroutine: this will + // include the upvar types as well as the yield + // type. Note that this is mildly distinct from + // the closure case, where we have to be careful + // about the signature of the closure. We don't + // have the problem of implied bounds here since + // coroutines don't take arguments. + let obligations = self.nominal_obligations(did.0.into(), args); + self.out.extend(obligations); + } + + TyKind::Closure(did, args) => { + // Note that we cannot skip the generic types + // types. Normally, within the fn + // body where they are created, the generics will + // always be WF, and outside of that fn body we + // are not directly inspecting closure types + // anyway, except via auto trait matching (which + // only inspects the upvar types). + // But when a closure is part of a type-alias-impl-trait + // then the function that created the defining site may + // have had more bounds available than the type alias + // specifies. This may cause us to have a closure in the + // hidden type that is not actually well formed and + // can cause compiler crashes when the user abuses unsafe + // code to procure such a closure. + // See tests/ui/type-alias-impl-trait/wf_check_closures.rs + let obligations = self.nominal_obligations(did.0.into(), args); + self.out.extend(obligations); + // Only check the upvar types for WF, not the rest + // of the types within. This is needed because we + // capture the signature and it may not be WF + // without the implied bounds. Consider a closure + // like `|x: &'a T|` -- it may be that `T: 'a` is + // not known to hold in the creator's context (and + // indeed the closure may not be invoked by its + // creator, but rather turned to someone who *can* + // verify that). + // + // The special treatment of closures here really + // ought not to be necessary either; the problem + // is related to #25860 -- there is no way for us + // to express a fn type complete with the implied + // bounds that it is assuming. I think in reality + // the WF rules around fn are a bit messed up, and + // that is the rot problem: `fn(&'a T)` should + // probably always be WF, because it should be + // shorthand for something like `where(T: 'a) { + // fn(&'a T) }`, as discussed in #25860. + let upvars = args.as_closure().tupled_upvars_ty(); + return upvars.visit_with(self); + } + + TyKind::CoroutineClosure(did, args) => { + // See the above comments. The same apply to coroutine-closures. + let obligations = self.nominal_obligations(did.0.into(), args); + self.out.extend(obligations); + let upvars = args.as_coroutine_closure().tupled_upvars_ty(); + return upvars.visit_with(self); + } + + TyKind::FnPtr(..) => { + // Let the visitor iterate into the argument/return + // types appearing in the fn signature. + } + TyKind::UnsafeBinder(ty) => {} + + TyKind::Dynamic(data, r, _) => { + // WfObject + // + // Here, we defer WF checking due to higher-ranked + // regions. This is perhaps not ideal. + self.add_wf_preds_for_dyn_ty(t, data.as_slice(), r); + + // FIXME(#27579) RFC also considers adding trait + // obligations that don't refer to Self and + // checking those + if let Some(principal) = data.principal_def_id() { + self.out.push(Obligation::with_depth( + tcx, + ObligationCause::new(), + self.recursion_depth, + self.param_env, + Binder::dummy(PredicateKind::DynCompatible(principal)), + )); + } + } + + // Inference variables are the complicated case, since we don't + // know what type they are. We do two things: + // + // 1. Check if they have been resolved, and if so proceed with + // THAT type. + // 2. If not, we've at least simplified things (e.g., we went + // from `Vec?0>: WF` to `?0: WF`), so we can + // register a pending obligation and keep + // moving. (Goal is that an "inductive hypothesis" + // is satisfied to ensure termination.) + // See also the comment on `fn obligations`, describing cycle + // prevention, which happens before this can be reached. + TyKind::Infer(_) => { + let cause = ObligationCause::new(); + self.out.push(Obligation::with_depth( + tcx, + cause, + self.recursion_depth, + self.param_env, + Binder::dummy(PredicateKind::Clause(ClauseKind::WellFormed(t.into()))), + )); + } + } + + t.super_visit_with(self) + } + + fn visit_const(&mut self, c: Const<'db>) -> Self::Result { + let tcx = self.interner(); + + match c.kind() { + ConstKind::Unevaluated(uv) => { + if !c.has_escaping_bound_vars() { + let predicate = + Binder::dummy(PredicateKind::Clause(ClauseKind::ConstEvaluatable(c))); + let cause = ObligationCause::new(); + self.out.push(Obligation::with_depth( + tcx, + cause, + self.recursion_depth, + self.param_env, + predicate, + )); + + if let SolverDefId::ConstId(uv_def) = uv.def + && let ItemContainerId::ImplId(impl_) = + uv_def.loc(self.interner().db).container + && self.interner().db.impl_signature(impl_).target_trait.is_none() + { + return; // Subtree is handled by above function + } else { + let obligations = self.nominal_obligations(uv.def, uv.args); + self.out.extend(obligations); + } + } + } + ConstKind::Infer(_) => { + let cause = ObligationCause::new(); + + self.out.push(Obligation::with_depth( + tcx, + cause, + self.recursion_depth, + self.param_env, + Binder::dummy(PredicateKind::Clause(ClauseKind::WellFormed(c.into()))), + )); + } + ConstKind::Expr(_) => { + // FIXME(generic_const_exprs): this doesn't verify that given `Expr(N + 1)` the + // trait bound `typeof(N): Add` holds. This is currently unnecessary + // as `ConstKind::Expr` is only produced via normalization of `ConstKind::Unevaluated` + // which means that the `DefId` would have been typeck'd elsewhere. However in + // the future we may allow directly lowering to `ConstKind::Expr` in which case + // we would not be proving bounds we should. + + let predicate = + Binder::dummy(PredicateKind::Clause(ClauseKind::ConstEvaluatable(c))); + let cause = ObligationCause::new(); + self.out.push(Obligation::with_depth( + tcx, + cause, + self.recursion_depth, + self.param_env, + predicate, + )); + } + + ConstKind::Error(_) + | ConstKind::Param(_) + | ConstKind::Bound(..) + | ConstKind::Placeholder(..) => { + // These variants are trivially WF, so nothing to do here. + } + ConstKind::Value(..) => { + // FIXME: Enforce that values are structurally-matchable. + } + } + + c.super_visit_with(self) + } + + fn visit_predicate(&mut self, _p: Predicate<'db>) -> Self::Result { + panic!("predicate should not be checked for well-formedness"); + } + } + + /// Given an object type like `SomeTrait + Send`, computes the lifetime + /// bounds that must hold on the elided self type. These are derived + /// from the declarations of `SomeTrait`, `Send`, and friends -- if + /// they declare `trait SomeTrait : 'static`, for example, then + /// `'static` would appear in the list. + /// + /// N.B., in some cases, particularly around higher-ranked bounds, + /// this function returns a kind of conservative approximation. + /// That is, all regions returned by this function are definitely + /// required, but there may be other region bounds that are not + /// returned, as well as requirements like `for<'a> T: 'a`. + /// + /// Requires that trait definitions have been processed so that we can + /// elaborate predicates and walk supertraits. + pub fn object_region_bounds<'db>( + interner: DbInterner<'db>, + existential_predicates: &[Binder<'db, ExistentialPredicate<'db>>], + ) -> Vec> { + let erased_self_ty = Ty::new_unit(interner); + + let predicates = existential_predicates + .iter() + .map(|predicate| predicate.with_self_ty(interner, erased_self_ty)); + + rustc_type_ir::elaborate::elaborate(interner, predicates) + .filter_map(|pred| { + debug!(?pred); + match pred.kind().skip_binder() { + ClauseKind::TypeOutlives(rustc_type_ir::OutlivesPredicate(ref t, ref r)) => { + // Search for a bound of the form `erased_self_ty + // : 'a`, but be wary of something like `for<'a> + // erased_self_ty : 'a` (we interpret a + // higher-ranked bound like that as 'static, + // though at present the code in `fulfill.rs` + // considers such bounds to be unsatisfiable, so + // it's kind of a moot point since you could never + // construct such an object, but this seems + // correct even if that code changes). + if t == &erased_self_ty && !r.has_escaping_bound_vars() { + Some(*r) + } else { + None + } + } + ClauseKind::Trait(_) + | ClauseKind::HostEffect(..) + | ClauseKind::RegionOutlives(_) + | ClauseKind::Projection(_) + | ClauseKind::ConstArgHasType(_, _) + | ClauseKind::WellFormed(_) + | ClauseKind::UnstableFeature(_) + | ClauseKind::ConstEvaluatable(_) => None, + } + }) + .collect() + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs index 4e124d07d2b3..097bb85cbd49 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs @@ -6,12 +6,15 @@ use rustc_type_ir::{ ClosureArgs, CollectAndApply, ConstVid, CoroutineArgs, CoroutineClosureArgs, FnSig, FnSigTys, GenericArgKind, IntTy, Interner, TermKind, TyKind, TyVid, TypeFoldable, TypeVisitable, Variance, - inherent::{GenericArg as _, GenericsOf, IntoKind, SliceLike, Term as _, Ty as _}, + inherent::{ + GenericArg as _, GenericArgs as _, GenericsOf, IntoKind, SliceLike, Term as _, Ty as _, + }, relate::{Relate, VarianceDiagInfo}, }; use smallvec::SmallVec; use crate::db::HirDatabase; +use crate::next_solver::{Binder, PolyFnSig}; use super::{ Const, DbInterner, EarlyParamRegion, ErrorGuaranteed, ParamConst, Region, SolverDefId, Ty, Tys, @@ -240,6 +243,34 @@ impl<'db> GenericArgs<'db> { args.push(kind); } } + + pub fn closure_sig_untupled(self) -> PolyFnSig<'db> { + let TyKind::FnPtr(inputs_and_output, hdr) = + self.split_closure_args_untupled().closure_sig_as_fn_ptr_ty.kind() + else { + unreachable!("not a function pointer") + }; + inputs_and_output.with(hdr) + } + + /// A "sensible" `.split_closure_args()`, where the arguments are not in a tuple. + pub fn split_closure_args_untupled(self) -> rustc_type_ir::ClosureArgsParts> { + // FIXME: should use `ClosureSubst` when possible + match self.inner().as_slice() { + [parent_args @ .., closure_kind_ty, sig_ty, tupled_upvars_ty] => { + let interner = DbInterner::conjure(); + rustc_type_ir::ClosureArgsParts { + parent_args: GenericArgs::new_from_iter(interner, parent_args.iter().cloned()), + closure_sig_as_fn_ptr_ty: sig_ty.expect_ty(), + closure_kind_ty: closure_kind_ty.expect_ty(), + tupled_upvars_ty: tupled_upvars_ty.expect_ty(), + } + } + _ => { + unreachable!("unexpected closure sig"); + } + } + } } impl<'db> rustc_type_ir::relate::Relate> for GenericArgs<'db> { @@ -329,7 +360,7 @@ impl<'db> rustc_type_ir::inherent::GenericArgs> for GenericArgs< fn split_closure_args(self) -> rustc_type_ir::ClosureArgsParts> { // FIXME: should use `ClosureSubst` when possible match self.inner().as_slice() { - [parent_args @ .., sig_ty] => { + [parent_args @ .., closure_kind_ty, sig_ty, tupled_upvars_ty] => { let interner = DbInterner::conjure(); // This is stupid, but the next solver expects the first input to actually be a tuple let sig_ty = match sig_ty.expect_ty().kind() { @@ -354,8 +385,8 @@ impl<'db> rustc_type_ir::inherent::GenericArgs> for GenericArgs< rustc_type_ir::ClosureArgsParts { parent_args: GenericArgs::new_from_iter(interner, parent_args.iter().cloned()), closure_sig_as_fn_ptr_ty: sig_ty, - closure_kind_ty: Ty::new(interner, TyKind::Int(IntTy::I8)), - tupled_upvars_ty: Ty::new_unit(interner), + closure_kind_ty: closure_kind_ty.expect_ty(), + tupled_upvars_ty: tupled_upvars_ty.expect_ty(), } } _ => { @@ -392,14 +423,14 @@ impl<'db> rustc_type_ir::inherent::GenericArgs> for GenericArgs< fn split_coroutine_args(self) -> rustc_type_ir::CoroutineArgsParts> { let interner = DbInterner::conjure(); match self.inner().as_slice() { - [parent_args @ .., resume_ty, yield_ty, return_ty] => { + [parent_args @ .., kind_ty, resume_ty, yield_ty, return_ty, tupled_upvars_ty] => { rustc_type_ir::CoroutineArgsParts { parent_args: GenericArgs::new_from_iter(interner, parent_args.iter().cloned()), - kind_ty: Ty::new_unit(interner), + kind_ty: kind_ty.expect_ty(), resume_ty: resume_ty.expect_ty(), yield_ty: yield_ty.expect_ty(), return_ty: return_ty.expect_ty(), - tupled_upvars_ty: Ty::new_unit(interner), + tupled_upvars_ty: tupled_upvars_ty.expect_ty(), } } _ => panic!("GenericArgs were likely not for a Coroutine."), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/at.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/at.rs index d64c7ed626eb..8dfffe0d365e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/at.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/at.rs @@ -26,7 +26,7 @@ //! things. (That system should probably be refactored.) use rustc_type_ir::{ - FnSig, GenericArgKind, TypingMode, Variance, + FnSig, GenericArgKind, TypeFoldable, TypingMode, Variance, error::ExpectedFound, inherent::{IntoKind, Span as _}, relate::{Relate, TypeRelation, solver_relating::RelateExt}, @@ -36,6 +36,8 @@ use crate::next_solver::{ AliasTerm, AliasTy, Binder, Const, DbInterner, GenericArg, Goal, ParamEnv, PolyExistentialProjection, PolyExistentialTraitRef, PolyFnSig, Predicate, Region, Span, Term, TraitRef, Ty, + fulfill::{FulfillmentCtxt, NextSolverError}, + infer::relate::lattice::{LatticeOp, LatticeOpKind}, }; use super::{ @@ -210,6 +212,34 @@ impl<'a, 'db> At<'a, 'db> { } } + /// Deeply normalizes `value`, replacing all aliases which can by normalized in + /// the current environment. This errors in case normalization fails or is ambiguous. + pub fn deeply_normalize(self, value: T) -> Result>> + where + T: TypeFoldable>, + { + crate::next_solver::normalize::deeply_normalize(self, value) + } + + /// Computes the least-upper-bound, or mutual supertype, of two + /// values. The order of the arguments doesn't matter, but since + /// this can result in an error (e.g., if asked to compute LUB of + /// u32 and i32), it is meaningful to call one of them the + /// "expected type". + pub fn lub(self, expected: T, actual: T) -> InferResult<'db, T> + where + T: ToTrace<'db>, + { + let mut op = LatticeOp::new( + self.infcx, + ToTrace::to_trace(self.cause, expected, actual), + self.param_env, + LatticeOpKind::Lub, + ); + let value = op.relate(expected, actual)?; + Ok(InferOk { value, obligations: op.into_obligations() }) + } + fn goals_to_obligations(&self, goals: Vec>>) -> InferOk<'db, ()> { InferOk { value: (), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs index ce6c94128732..8e922abacb20 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs @@ -2,6 +2,7 @@ use std::cell::{Cell, RefCell}; use std::fmt; +use std::ops::Range; use std::sync::Arc; pub use BoundRegionConversionTime::*; @@ -55,6 +56,7 @@ mod opaque_types; pub mod region_constraints; pub mod relate; pub mod resolve; +pub(crate) mod select; pub(crate) mod snapshot; pub(crate) mod traits; mod type_variable; @@ -81,6 +83,10 @@ pub(crate) type UnificationTable<'a, 'db, T> = ut::UnificationTable< ut::InPlace, &'a mut InferCtxtUndoLogs<'db>>, >; +fn iter_idx_range + Into>(range: Range) -> impl Iterator { + (range.start.into()..range.end.into()).map(Into::into) +} + /// This type contains all the things within `InferCtxt` that sit within a /// `RefCell` and are involved with taking/rolling back snapshots. Snapshot /// operations are hot enough that we want only one call to `borrow_mut` per diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs index 50549694c3f2..7f15a467b3e8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs @@ -22,7 +22,7 @@ use crate::next_solver::{ AliasTy, Binder, DbInterner, OpaqueTypeKey, ParamTy, PlaceholderTy, Region, Ty, }; -#[derive(Clone, Default)] +#[derive(Debug, Clone, Default)] pub struct RegionConstraintStorage<'db> { /// For each `RegionVid`, the corresponding `RegionVariableOrigin`. pub(super) var_infos: IndexVec, @@ -239,7 +239,7 @@ pub struct VerifyIfEq<'db> { pub bound: Region<'db>, } -#[derive(Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub(crate) struct TwoRegions<'db> { a: Region<'db>, b: Region<'db>, @@ -458,6 +458,44 @@ impl<'db> RegionConstraintCollector<'db, '_> { } } + pub(super) fn lub_regions( + &mut self, + db: DbInterner<'db>, + a: Region<'db>, + b: Region<'db>, + ) -> Region<'db> { + // cannot add constraints once regions are resolved + debug!("RegionConstraintCollector: lub_regions({:?}, {:?})", a, b); + #[expect(clippy::if_same_then_else)] + if a.is_static() || b.is_static() { + a // nothing lives longer than static + } else if a == b { + a // LUB(a,a) = a + } else { + self.combine_vars(db, Lub, a, b) + } + } + + pub(super) fn glb_regions( + &mut self, + db: DbInterner<'db>, + a: Region<'db>, + b: Region<'db>, + ) -> Region<'db> { + // cannot add constraints once regions are resolved + debug!("RegionConstraintCollector: glb_regions({:?}, {:?})", a, b); + #[expect(clippy::if_same_then_else)] + if a.is_static() { + b // static lives longer than everything else + } else if b.is_static() { + a // static lives longer than everything else + } else if a == b { + a // GLB(a,a) = a + } else { + self.combine_vars(db, Glb, a, b) + } + } + /// Resolves a region var to its value in the unification table, if it exists. /// Otherwise, it is resolved to the root `ReVar` in the table. pub fn opportunistic_resolve_var( @@ -531,6 +569,17 @@ impl<'db> RegionConstraintCollector<'db, '_> { } } + pub fn vars_since_snapshot(&self, value_count: usize) -> Range { + RegionVid::from(value_count)..RegionVid::from(self.storage.unification_table.len()) + } + + /// See `InferCtxt::region_constraints_added_in_snapshot`. + pub fn region_constraints_added_in_snapshot(&self, mark: &Snapshot) -> bool { + self.undo_log + .region_constraints_in_snapshot(mark) + .any(|elt| matches!(elt, AddConstraint(_))) + } + #[inline] fn unification_table_mut(&mut self) -> super::UnificationTable<'_, 'db, RegionVidKey<'db>> { ut::UnificationTable::with_log(&mut self.storage.unification_table, self.undo_log) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/generalize.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/generalize.rs index de336c69b318..7e2735db3b77 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/generalize.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/generalize.rs @@ -350,7 +350,7 @@ impl<'db> Generalizer<'_, 'db> { // with inference variables can cause incorrect ambiguity. // // cc trait-system-refactor-initiative#110 - if self.infcx.next_trait_solver() && !alias.has_escaping_bound_vars() && !self.in_alias { + if !alias.has_escaping_bound_vars() && !self.in_alias { return Ok(self.next_ty_var_for_alias()); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/lattice.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/lattice.rs new file mode 100644 index 000000000000..c7f771ffe37f --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/lattice.rs @@ -0,0 +1,269 @@ +//! # Lattice variables +//! +//! Generic code for operating on [lattices] of inference variables +//! that are characterized by an upper- and lower-bound. +//! +//! The code is defined quite generically so that it can be +//! applied both to type variables, which represent types being inferred, +//! and fn variables, which represent function types being inferred. +//! (It may eventually be applied to their types as well.) +//! In some cases, the functions are also generic with respect to the +//! operation on the lattice (GLB vs LUB). +//! +//! ## Note +//! +//! Although all the functions are generic, for simplicity, comments in the source code +//! generally refer to type variables and the LUB operation. +//! +//! [lattices]: https://en.wikipedia.org/wiki/Lattice_(order) + +use rustc_type_ir::{ + AliasRelationDirection, TypeVisitableExt, Upcast, Variance, + inherent::{IntoKind, Span as _}, + relate::{ + Relate, StructurallyRelateAliases, TypeRelation, VarianceDiagInfo, + combine::{PredicateEmittingRelation, super_combine_consts, super_combine_tys}, + }, +}; + +use crate::next_solver::{ + AliasTy, Binder, Const, DbInterner, Goal, ParamEnv, Predicate, PredicateKind, Region, Span, Ty, + TyKind, + infer::{ + DefineOpaqueTypes, InferCtxt, TypeTrace, + relate::RelateResult, + traits::{Obligation, PredicateObligations}, + }, +}; + +#[derive(Clone, Copy)] +pub(crate) enum LatticeOpKind { + Glb, + Lub, +} + +impl LatticeOpKind { + fn invert(self) -> Self { + match self { + LatticeOpKind::Glb => LatticeOpKind::Lub, + LatticeOpKind::Lub => LatticeOpKind::Glb, + } + } +} + +/// A greatest lower bound" (common subtype) or least upper bound (common supertype). +pub(crate) struct LatticeOp<'infcx, 'db> { + infcx: &'infcx InferCtxt<'db>, + // Immutable fields + trace: TypeTrace<'db>, + param_env: ParamEnv<'db>, + // Mutable fields + kind: LatticeOpKind, + obligations: PredicateObligations<'db>, +} + +impl<'infcx, 'db> LatticeOp<'infcx, 'db> { + pub(crate) fn new( + infcx: &'infcx InferCtxt<'db>, + trace: TypeTrace<'db>, + param_env: ParamEnv<'db>, + kind: LatticeOpKind, + ) -> LatticeOp<'infcx, 'db> { + LatticeOp { infcx, trace, param_env, kind, obligations: PredicateObligations::new() } + } + + pub(crate) fn into_obligations(self) -> PredicateObligations<'db> { + self.obligations + } +} + +impl<'db> TypeRelation> for LatticeOp<'_, 'db> { + fn cx(&self) -> DbInterner<'db> { + self.infcx.interner + } + + fn relate_with_variance>>( + &mut self, + variance: Variance, + _info: VarianceDiagInfo>, + a: T, + b: T, + ) -> RelateResult<'db, T> { + match variance { + Variance::Invariant => { + self.obligations.extend( + self.infcx + .at(&self.trace.cause, self.param_env) + .eq_trace(DefineOpaqueTypes::Yes, self.trace.clone(), a, b)? + .into_obligations(), + ); + Ok(a) + } + Variance::Covariant => self.relate(a, b), + // FIXME(#41044) -- not correct, need test + Variance::Bivariant => Ok(a), + Variance::Contravariant => { + self.kind = self.kind.invert(); + let res = self.relate(a, b); + self.kind = self.kind.invert(); + res + } + } + } + + /// Relates two types using a given lattice. + fn tys(&mut self, a: Ty<'db>, b: Ty<'db>) -> RelateResult<'db, Ty<'db>> { + if a == b { + return Ok(a); + } + + let infcx = self.infcx; + + let a = infcx.shallow_resolve(a); + let b = infcx.shallow_resolve(b); + + match (a.kind(), b.kind()) { + // If one side is known to be a variable and one is not, + // create a variable (`v`) to represent the LUB. Make sure to + // relate `v` to the non-type-variable first (by passing it + // first to `relate_bound`). Otherwise, we would produce a + // subtype obligation that must then be processed. + // + // Example: if the LHS is a type variable, and RHS is + // `Box`, then we current compare `v` to the RHS first, + // which will instantiate `v` with `Box`. Then when `v` + // is compared to the LHS, we instantiate LHS with `Box`. + // But if we did in reverse order, we would create a `v <: + // LHS` (or vice versa) constraint and then instantiate + // `v`. This would require further processing to achieve same + // end-result; in particular, this screws up some of the logic + // in coercion, which expects LUB to figure out that the LHS + // is (e.g.) `Box`. A more obvious solution might be to + // iterate on the subtype obligations that are returned, but I + // think this suffices. -nmatsakis + (TyKind::Infer(rustc_type_ir::TyVar(..)), _) => { + let v = infcx.next_ty_var(); + self.relate_bound(v, b, a)?; + Ok(v) + } + (_, TyKind::Infer(rustc_type_ir::TyVar(..))) => { + let v = infcx.next_ty_var(); + self.relate_bound(v, a, b)?; + Ok(v) + } + + ( + TyKind::Alias(rustc_type_ir::Opaque, AliasTy { def_id: a_def_id, .. }), + TyKind::Alias(rustc_type_ir::Opaque, AliasTy { def_id: b_def_id, .. }), + ) if a_def_id == b_def_id => super_combine_tys(infcx, self, a, b), + + _ => super_combine_tys(infcx, self, a, b), + } + } + + fn regions(&mut self, a: Region<'db>, b: Region<'db>) -> RelateResult<'db, Region<'db>> { + let mut inner = self.infcx.inner.borrow_mut(); + let mut constraints = inner.unwrap_region_constraints(); + Ok(match self.kind { + // GLB(&'static u8, &'a u8) == &RegionLUB('static, 'a) u8 == &'static u8 + LatticeOpKind::Glb => constraints.lub_regions(self.cx(), a, b), + + // LUB(&'static u8, &'a u8) == &RegionGLB('static, 'a) u8 == &'a u8 + LatticeOpKind::Lub => constraints.glb_regions(self.cx(), a, b), + }) + } + + fn consts(&mut self, a: Const<'db>, b: Const<'db>) -> RelateResult<'db, Const<'db>> { + super_combine_consts(self.infcx, self, a, b) + } + + fn binders( + &mut self, + a: Binder<'db, T>, + b: Binder<'db, T>, + ) -> RelateResult<'db, Binder<'db, T>> + where + T: Relate>, + { + // GLB/LUB of a binder and itself is just itself + if a == b { + return Ok(a); + } + + if a.skip_binder().has_escaping_bound_vars() || b.skip_binder().has_escaping_bound_vars() { + // When higher-ranked types are involved, computing the GLB/LUB is + // very challenging, switch to invariance. This is obviously + // overly conservative but works ok in practice. + self.relate_with_variance(Variance::Invariant, VarianceDiagInfo::default(), a, b)?; + Ok(a) + } else { + Ok(Binder::dummy(self.relate(a.skip_binder(), b.skip_binder())?)) + } + } +} + +impl<'infcx, 'db> LatticeOp<'infcx, 'db> { + // Relates the type `v` to `a` and `b` such that `v` represents + // the LUB/GLB of `a` and `b` as appropriate. + // + // Subtle hack: ordering *may* be significant here. This method + // relates `v` to `a` first, which may help us to avoid unnecessary + // type variable obligations. See caller for details. + fn relate_bound(&mut self, v: Ty<'db>, a: Ty<'db>, b: Ty<'db>) -> RelateResult<'db, ()> { + let at = self.infcx.at(&self.trace.cause, self.param_env); + match self.kind { + LatticeOpKind::Glb => { + self.obligations.extend(at.sub(DefineOpaqueTypes::Yes, v, a)?.into_obligations()); + self.obligations.extend(at.sub(DefineOpaqueTypes::Yes, v, b)?.into_obligations()); + } + LatticeOpKind::Lub => { + self.obligations.extend(at.sub(DefineOpaqueTypes::Yes, a, v)?.into_obligations()); + self.obligations.extend(at.sub(DefineOpaqueTypes::Yes, b, v)?.into_obligations()); + } + } + Ok(()) + } +} + +impl<'db> PredicateEmittingRelation> for LatticeOp<'_, 'db> { + fn span(&self) -> Span { + Span::dummy() + } + + fn structurally_relate_aliases(&self) -> StructurallyRelateAliases { + StructurallyRelateAliases::No + } + + fn param_env(&self) -> ParamEnv<'db> { + self.param_env + } + + fn register_predicates( + &mut self, + preds: impl IntoIterator, Predicate<'db>>>, + ) { + self.obligations.extend(preds.into_iter().map(|pred| { + Obligation::new(self.infcx.interner, self.trace.cause.clone(), self.param_env, pred) + })) + } + + fn register_goals(&mut self, goals: impl IntoIterator>>) { + self.obligations.extend(goals.into_iter().map(|goal| { + Obligation::new( + self.infcx.interner, + self.trace.cause.clone(), + goal.param_env, + goal.predicate, + ) + })) + } + + fn register_alias_relate_predicate(&mut self, a: Ty<'db>, b: Ty<'db>) { + self.register_predicates([Binder::dummy(PredicateKind::AliasRelate( + a.into(), + b.into(), + // FIXME(deferred_projection_equality): This isn't right, I think? + AliasRelationDirection::Equate, + ))]); + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/mod.rs index 836ae39dc525..0cc1cf756a9c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/mod.rs @@ -9,5 +9,6 @@ use crate::next_solver::DbInterner; mod generalize; mod higher_ranked; +pub(crate) mod lattice; pub type RelateResult<'db, T> = rustc_type_ir::relate::RelateResult, T>; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/select.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/select.rs new file mode 100644 index 000000000000..d656d94f4f91 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/select.rs @@ -0,0 +1,334 @@ +use std::ops::ControlFlow; + +use hir_def::{ImplId, TraitId}; +use rustc_type_ir::{ + Interner, + solve::{BuiltinImplSource, CandidateSource, Certainty, inspect::ProbeKind}, +}; + +use crate::{ + db::InternedOpaqueTyId, + next_solver::{ + Const, ErrorGuaranteed, GenericArgs, Goal, TraitRef, Ty, TypeError, + infer::{ + InferCtxt, + traits::{Obligation, ObligationCause, PredicateObligation, TraitObligation}, + }, + inspect::{InspectCandidate, InspectGoal, ProofTreeVisitor}, + }, +}; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum SelectionError<'db> { + /// The trait is not implemented. + Unimplemented, + /// After a closure impl has selected, its "outputs" were evaluated + /// (which for closures includes the "input" type params) and they + /// didn't resolve. See `confirm_poly_trait_refs` for more. + SignatureMismatch(Box>), + /// The trait pointed by `DefId` is dyn-incompatible. + TraitDynIncompatible(TraitId), + /// A given constant couldn't be evaluated. + NotConstEvaluatable(NotConstEvaluatable), + /// Exceeded the recursion depth during type projection. + Overflow(OverflowError), + /// Computing an opaque type's hidden type caused an error (e.g. a cycle error). + /// We can thus not know whether the hidden type implements an auto trait, so + /// we should not presume anything about it. + OpaqueTypeAutoTraitLeakageUnknown(InternedOpaqueTyId), + /// Error for a `ConstArgHasType` goal + ConstArgHasWrongType { ct: Const<'db>, ct_ty: Ty<'db>, expected_ty: Ty<'db> }, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum NotConstEvaluatable { + Error(ErrorGuaranteed), + MentionsInfer, + MentionsParam, +} + +/// Indicates that trait evaluation caused overflow and in which pass. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum OverflowError { + Error(ErrorGuaranteed), + Canonical, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct SignatureMismatchData<'db> { + pub found_trait_ref: TraitRef<'db>, + pub expected_trait_ref: TraitRef<'db>, + pub terr: TypeError<'db>, +} + +/// When performing resolution, it is typically the case that there +/// can be one of three outcomes: +/// +/// - `Ok(Some(r))`: success occurred with result `r` +/// - `Ok(None)`: could not definitely determine anything, usually due +/// to inconclusive type inference. +/// - `Err(e)`: error `e` occurred +pub type SelectionResult<'db, T> = Result, SelectionError<'db>>; + +/// Given the successful resolution of an obligation, the `ImplSource` +/// indicates where the impl comes from. +/// +/// For example, the obligation may be satisfied by a specific impl (case A), +/// or it may be relative to some bound that is in scope (case B). +/// +/// ```ignore (illustrative) +/// impl Clone for Option { ... } // Impl_1 +/// impl Clone for Box { ... } // Impl_2 +/// impl Clone for i32 { ... } // Impl_3 +/// +/// fn foo(concrete: Option>, param: T, mixed: Option) { +/// // Case A: ImplSource points at a specific impl. Only possible when +/// // type is concretely known. If the impl itself has bounded +/// // type parameters, ImplSource will carry resolutions for those as well: +/// concrete.clone(); // ImplSource(Impl_1, [ImplSource(Impl_2, [ImplSource(Impl_3)])]) +/// +/// // Case B: ImplSource must be provided by caller. This applies when +/// // type is a type parameter. +/// param.clone(); // ImplSource::Param +/// +/// // Case C: A mix of cases A and B. +/// mixed.clone(); // ImplSource(Impl_1, [ImplSource::Param]) +/// } +/// ``` +/// +/// ### The type parameter `N` +/// +/// See explanation on `ImplSourceUserDefinedData`. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum ImplSource<'db, N> { + /// ImplSource identifying a particular impl. + UserDefined(ImplSourceUserDefinedData<'db, N>), + + /// Successful resolution to an obligation provided by the caller + /// for some type parameter. The `Vec` represents the + /// obligations incurred from normalizing the where-clause (if + /// any). + Param(Vec), + + /// Successful resolution for a builtin impl. + Builtin(BuiltinImplSource, Vec), +} + +impl<'db, N> ImplSource<'db, N> { + pub fn nested_obligations(self) -> Vec { + match self { + ImplSource::UserDefined(i) => i.nested, + ImplSource::Param(n) | ImplSource::Builtin(_, n) => n, + } + } + + pub fn borrow_nested_obligations(&self) -> &[N] { + match self { + ImplSource::UserDefined(i) => &i.nested, + ImplSource::Param(n) | ImplSource::Builtin(_, n) => n, + } + } + + pub fn borrow_nested_obligations_mut(&mut self) -> &mut [N] { + match self { + ImplSource::UserDefined(i) => &mut i.nested, + ImplSource::Param(n) | ImplSource::Builtin(_, n) => n, + } + } + + pub fn map(self, f: F) -> ImplSource<'db, M> + where + F: FnMut(N) -> M, + { + match self { + ImplSource::UserDefined(i) => ImplSource::UserDefined(ImplSourceUserDefinedData { + impl_def_id: i.impl_def_id, + args: i.args, + nested: i.nested.into_iter().map(f).collect(), + }), + ImplSource::Param(n) => ImplSource::Param(n.into_iter().map(f).collect()), + ImplSource::Builtin(source, n) => { + ImplSource::Builtin(source, n.into_iter().map(f).collect()) + } + } + } +} + +/// Identifies a particular impl in the source, along with a set of +/// generic parameters from the impl's type/lifetime parameters. The +/// `nested` vector corresponds to the nested obligations attached to +/// the impl's type parameters. +/// +/// The type parameter `N` indicates the type used for "nested +/// obligations" that are required by the impl. During type-check, this +/// is `Obligation`, as one might expect. During codegen, however, this +/// is `()`, because codegen only requires a shallow resolution of an +/// impl, and nested obligations are satisfied later. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ImplSourceUserDefinedData<'db, N> { + pub impl_def_id: ImplId, + pub args: GenericArgs<'db>, + pub nested: Vec, +} + +pub type Selection<'db> = ImplSource<'db, PredicateObligation<'db>>; + +impl<'db> InferCtxt<'db> { + pub(crate) fn select( + &self, + obligation: &TraitObligation<'db>, + ) -> SelectionResult<'db, Selection<'db>> { + self.visit_proof_tree( + Goal::new(self.interner, obligation.param_env, obligation.predicate), + &mut Select {}, + ) + .break_value() + .unwrap() + } +} + +struct Select {} + +impl<'db> ProofTreeVisitor<'db> for Select { + type Result = ControlFlow>>; + + fn visit_goal(&mut self, goal: &InspectGoal<'_, 'db>) -> Self::Result { + let mut candidates = goal.candidates(); + candidates.retain(|cand| cand.result().is_ok()); + + // No candidates -- not implemented. + if candidates.is_empty() { + return ControlFlow::Break(Err(SelectionError::Unimplemented)); + } + + // One candidate, no need to winnow. + if candidates.len() == 1 { + return ControlFlow::Break(Ok(to_selection(candidates.into_iter().next().unwrap()))); + } + + // Don't winnow until `Certainty::Yes` -- we don't need to winnow until + // codegen, and only on the good path. + if matches!(goal.result().unwrap(), Certainty::Maybe(..)) { + return ControlFlow::Break(Ok(None)); + } + + // We need to winnow. See comments on `candidate_should_be_dropped_in_favor_of`. + let mut i = 0; + while i < candidates.len() { + let should_drop_i = (0..candidates.len()) + .filter(|&j| i != j) + .any(|j| candidate_should_be_dropped_in_favor_of(&candidates[i], &candidates[j])); + if should_drop_i { + candidates.swap_remove(i); + } else { + i += 1; + if i > 1 { + return ControlFlow::Break(Ok(None)); + } + } + } + + ControlFlow::Break(Ok(to_selection(candidates.into_iter().next().unwrap()))) + } +} + +/// This is a lot more limited than the old solver's equivalent method. This may lead to more `Ok(None)` +/// results when selecting traits in polymorphic contexts, but we should never rely on the lack of ambiguity, +/// and should always just gracefully fail here. We shouldn't rely on this incompleteness. +fn candidate_should_be_dropped_in_favor_of<'db>( + victim: &InspectCandidate<'_, 'db>, + other: &InspectCandidate<'_, 'db>, +) -> bool { + // Don't winnow until `Certainty::Yes` -- we don't need to winnow until + // codegen, and only on the good path. + if matches!(other.result().unwrap(), Certainty::Maybe(..)) { + return false; + } + + let ProbeKind::TraitCandidate { source: victim_source, result: _ } = victim.kind() else { + return false; + }; + let ProbeKind::TraitCandidate { source: other_source, result: _ } = other.kind() else { + return false; + }; + + match (victim_source, other_source) { + (_, CandidateSource::CoherenceUnknowable) | (CandidateSource::CoherenceUnknowable, _) => { + panic!("should not have assembled a CoherenceUnknowable candidate") + } + + // In the old trait solver, we arbitrarily choose lower vtable candidates + // over higher ones. + ( + CandidateSource::BuiltinImpl(BuiltinImplSource::Object(a)), + CandidateSource::BuiltinImpl(BuiltinImplSource::Object(b)), + ) => a >= b, + ( + CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting(a)), + CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting(b)), + ) => a >= b, + // Prefer dyn candidates over non-dyn candidates. This is necessary to + // handle the unsoundness between `impl Any for T` and `dyn Any: Any`. + ( + CandidateSource::Impl(_) | CandidateSource::ParamEnv(_) | CandidateSource::AliasBound, + CandidateSource::BuiltinImpl(BuiltinImplSource::Object { .. }), + ) => true, + + // Prefer specializing candidates over specialized candidates. + (CandidateSource::Impl(victim_def_id), CandidateSource::Impl(other_def_id)) => { + victim.goal().infcx().interner.impl_specializes(other_def_id, victim_def_id) + } + + _ => false, + } +} + +fn to_selection<'db>(cand: InspectCandidate<'_, 'db>) -> Option> { + if let Certainty::Maybe(..) = cand.shallow_certainty() { + return None; + } + + let nested = match cand.result().expect("expected positive result") { + Certainty::Yes => Vec::new(), + Certainty::Maybe(_) => cand + .instantiate_nested_goals() + .into_iter() + .map(|nested| { + Obligation::new( + nested.infcx().interner, + ObligationCause::dummy(), + nested.goal().param_env, + nested.goal().predicate, + ) + }) + .collect(), + }; + + Some(match cand.kind() { + ProbeKind::TraitCandidate { source, result: _ } => match source { + CandidateSource::Impl(impl_def_id) => { + // FIXME: Remove this in favor of storing this in the tree + // For impl candidates, we do the rematch manually to compute the args. + ImplSource::UserDefined(ImplSourceUserDefinedData { + impl_def_id: impl_def_id.0, + args: cand.instantiate_impl_args(), + nested, + }) + } + CandidateSource::BuiltinImpl(builtin) => ImplSource::Builtin(builtin, nested), + CandidateSource::ParamEnv(_) | CandidateSource::AliasBound => ImplSource::Param(nested), + CandidateSource::CoherenceUnknowable => { + panic!("didn't expect to select an unknowable candidate") + } + }, + ProbeKind::NormalizedSelfTyAssembly + | ProbeKind::UnsizeAssembly + | ProbeKind::ProjectionCompatibility + | ProbeKind::OpaqueTypeStorageLookup { result: _ } + | ProbeKind::Root { result: _ } + | ProbeKind::ShadowedEnvProbing + | ProbeKind::RigidAlias { result: _ } => { + panic!("didn't expect to assemble trait candidate from {:#?}", cand.kind()) + } + }) +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/fudge.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/fudge.rs new file mode 100644 index 000000000000..74353574e329 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/fudge.rs @@ -0,0 +1,263 @@ +use std::ops::Range; + +use ena::{ + snapshot_vec as sv, + unify::{self as ut, UnifyKey}, +}; +use rustc_type_ir::{ + ConstVid, FloatVid, IntVid, RegionKind, RegionVid, TyVid, TypeFoldable, TypeFolder, + TypeSuperFoldable, TypeVisitableExt, inherent::IntoKind, +}; + +use crate::next_solver::{ + Const, ConstKind, DbInterner, Region, Ty, TyKind, + infer::{ + InferCtxt, UnificationTable, iter_idx_range, + snapshot::VariableLengths, + type_variable::TypeVariableOrigin, + unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey}, + }, +}; + +fn vars_since_snapshot<'db, T>( + table: &UnificationTable<'_, 'db, T>, + snapshot_var_len: usize, +) -> Range +where + T: UnifyKey, + super::UndoLog<'db>: From>>, +{ + T::from_index(snapshot_var_len as u32)..T::from_index(table.len() as u32) +} + +fn const_vars_since_snapshot<'db>( + table: &mut UnificationTable<'_, 'db, ConstVidKey<'db>>, + snapshot_var_len: usize, +) -> (Range, Vec) { + let range = vars_since_snapshot(table, snapshot_var_len); + let range = range.start.vid..range.end.vid; + + ( + range.clone(), + iter_idx_range(range) + .map(|index| match table.probe_value(index) { + ConstVariableValue::Known { value: _ } => { + ConstVariableOrigin { param_def_id: None } + } + ConstVariableValue::Unknown { origin, universe: _ } => origin, + }) + .collect(), + ) +} + +impl<'db> InferCtxt<'db> { + /// This rather funky routine is used while processing expected + /// types. What happens here is that we want to propagate a + /// coercion through the return type of a fn to its + /// argument. Consider the type of `Option::Some`, which is + /// basically `for fn(T) -> Option`. So if we have an + /// expression `Some(&[1, 2, 3])`, and that has the expected type + /// `Option<&[u32]>`, we would like to type check `&[1, 2, 3]` + /// with the expectation of `&[u32]`. This will cause us to coerce + /// from `&[u32; 3]` to `&[u32]` and make the users life more + /// pleasant. + /// + /// The way we do this is using `fudge_inference_if_ok`. What the + /// routine actually does is to start a snapshot and execute the + /// closure `f`. In our example above, what this closure will do + /// is to unify the expectation (`Option<&[u32]>`) with the actual + /// return type (`Option`, where `?T` represents the variable + /// instantiated for `T`). This will cause `?T` to be unified + /// with `&?a [u32]`, where `?a` is a fresh lifetime variable. The + /// input type (`?T`) is then returned by `f()`. + /// + /// At this point, `fudge_inference_if_ok` will normalize all type + /// variables, converting `?T` to `&?a [u32]` and end the + /// snapshot. The problem is that we can't just return this type + /// out, because it references the region variable `?a`, and that + /// region variable was popped when we popped the snapshot. + /// + /// So what we do is to keep a list (`region_vars`, in the code below) + /// of region variables created during the snapshot (here, `?a`). We + /// fold the return value and replace any such regions with a *new* + /// region variable (e.g., `?b`) and return the result (`&?b [u32]`). + /// This can then be used as the expectation for the fn argument. + /// + /// The important point here is that, for soundness purposes, the + /// regions in question are not particularly important. We will + /// use the expected types to guide coercions, but we will still + /// type-check the resulting types from those coercions against + /// the actual types (`?T`, `Option`) -- and remember that + /// after the snapshot is popped, the variable `?T` is no longer + /// unified. + pub fn fudge_inference_if_ok(&self, f: F) -> Result + where + F: FnOnce() -> Result, + T: TypeFoldable>, + { + let variable_lengths = self.variable_lengths(); + let (snapshot_vars, value) = self.probe(|_| { + let value = f()?; + // At this point, `value` could in principle refer + // to inference variables that have been created during + // the snapshot. Once we exit `probe()`, those are + // going to be popped, so we will have to + // eliminate any references to them. + let snapshot_vars = SnapshotVarData::new(self, variable_lengths); + Ok((snapshot_vars, self.resolve_vars_if_possible(value))) + })?; + + // At this point, we need to replace any of the now-popped + // type/region variables that appear in `value` with a fresh + // variable of the appropriate kind. We can't do this during + // the probe because they would just get popped then too. =) + Ok(self.fudge_inference(snapshot_vars, value)) + } + + fn fudge_inference>>( + &self, + snapshot_vars: SnapshotVarData, + value: T, + ) -> T { + // Micro-optimization: if no variables have been created, then + // `value` can't refer to any of them. =) So we can just return it. + if snapshot_vars.is_empty() { + value + } else { + value.fold_with(&mut InferenceFudger { infcx: self, snapshot_vars }) + } + } +} + +struct SnapshotVarData { + region_vars: Range, + type_vars: (Range, Vec), + int_vars: Range, + float_vars: Range, + const_vars: (Range, Vec), +} + +impl SnapshotVarData { + fn new(infcx: &InferCtxt<'_>, vars_pre_snapshot: VariableLengths) -> SnapshotVarData { + let mut inner = infcx.inner.borrow_mut(); + let region_vars = inner + .unwrap_region_constraints() + .vars_since_snapshot(vars_pre_snapshot.region_constraints_len); + let type_vars = inner.type_variables().vars_since_snapshot(vars_pre_snapshot.type_var_len); + let int_vars = + vars_since_snapshot(&inner.int_unification_table(), vars_pre_snapshot.int_var_len); + let float_vars = + vars_since_snapshot(&inner.float_unification_table(), vars_pre_snapshot.float_var_len); + + let const_vars = const_vars_since_snapshot( + &mut inner.const_unification_table(), + vars_pre_snapshot.const_var_len, + ); + SnapshotVarData { region_vars, type_vars, int_vars, float_vars, const_vars } + } + + fn is_empty(&self) -> bool { + let SnapshotVarData { region_vars, type_vars, int_vars, float_vars, const_vars } = self; + region_vars.is_empty() + && type_vars.0.is_empty() + && int_vars.is_empty() + && float_vars.is_empty() + && const_vars.0.is_empty() + } +} + +struct InferenceFudger<'a, 'db> { + infcx: &'a InferCtxt<'db>, + snapshot_vars: SnapshotVarData, +} + +impl<'a, 'db> TypeFolder> for InferenceFudger<'a, 'db> { + fn cx(&self) -> DbInterner<'db> { + self.infcx.interner + } + + fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> { + if let TyKind::Infer(infer_ty) = ty.kind() { + match infer_ty { + rustc_type_ir::TyVar(vid) => { + if self.snapshot_vars.type_vars.0.contains(&vid) { + // This variable was created during the fudging. + // Recreate it with a fresh variable here. + let idx = vid.as_usize() - self.snapshot_vars.type_vars.0.start.as_usize(); + let origin = self.snapshot_vars.type_vars.1[idx]; + self.infcx.next_ty_var_with_origin(origin) + } else { + // This variable was created before the + // "fudging". Since we refresh all type + // variables to their binding anyhow, we know + // that it is unbound, so we can just return + // it. + debug_assert!( + self.infcx.inner.borrow_mut().type_variables().probe(vid).is_unknown() + ); + ty + } + } + rustc_type_ir::IntVar(vid) => { + if self.snapshot_vars.int_vars.contains(&vid) { + self.infcx.next_int_var() + } else { + ty + } + } + rustc_type_ir::FloatVar(vid) => { + if self.snapshot_vars.float_vars.contains(&vid) { + self.infcx.next_float_var() + } else { + ty + } + } + rustc_type_ir::FreshTy(_) + | rustc_type_ir::FreshIntTy(_) + | rustc_type_ir::FreshFloatTy(_) => { + unreachable!("unexpected fresh infcx var") + } + } + } else if ty.has_infer() { + ty.super_fold_with(self) + } else { + ty + } + } + + fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { + if let RegionKind::ReVar(vid) = r.kind() { + if self.snapshot_vars.region_vars.contains(&vid) { + let idx = vid.index() - self.snapshot_vars.region_vars.start.index(); + self.infcx.next_region_var() + } else { + r + } + } else { + r + } + } + + fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> { + if let ConstKind::Infer(infer_ct) = ct.kind() { + match infer_ct { + rustc_type_ir::InferConst::Var(vid) => { + if self.snapshot_vars.const_vars.0.contains(&vid) { + let idx = vid.index() - self.snapshot_vars.const_vars.0.start.index(); + let origin = self.snapshot_vars.const_vars.1[idx]; + self.infcx.next_const_var_with_origin(origin) + } else { + ct + } + } + rustc_type_ir::InferConst::Fresh(_) => { + unreachable!("unexpected fresh infcx var") + } + } + } else if ct.has_infer() { + ct.super_fold_with(self) + } else { + ct + } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs index 8c7dfb25e07f..7b9ca96c5140 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs @@ -7,6 +7,7 @@ use tracing::{debug, instrument}; use super::InferCtxt; use super::region_constraints::RegionSnapshot; +mod fudge; pub(crate) mod undo_log; use undo_log::{Snapshot, UndoLog}; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs index 28ae56f4ee70..05a1013b3fbd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/snapshot/undo_log.rs @@ -178,6 +178,10 @@ impl<'db> InferCtxtUndoLogs<'db> { }) } + pub(crate) fn opaque_types_in_snapshot(&self, s: &Snapshot) -> bool { + self.logs[s.undo_len..].iter().any(|log| matches!(log, UndoLog::OpaqueTypes(..))) + } + fn assert_open_snapshot(&self, snapshot: &Snapshot) { // Failures here may indicate a failure to follow a stack discipline. assert!(self.logs.len() >= snapshot.undo_len); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs index f1df806ab318..68aa12d7bb0c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs @@ -7,14 +7,16 @@ use std::{ hash::{Hash, Hasher}, }; +use rustc_type_ir::elaborate::Elaboratable; use rustc_type_ir::{ PredicatePolarity, Upcast, solve::{Certainty, NoSolution}, }; +use rustc_type_ir::{TypeFoldable, TypeVisitable}; use crate::next_solver::{ - Binder, DbInterner, Goal, ParamEnv, PolyTraitPredicate, Predicate, SolverDefId, TraitPredicate, - Ty, + Binder, Clause, DbInterner, Goal, ParamEnv, PolyTraitPredicate, Predicate, SolverDefId, Span, + TraitPredicate, Ty, }; use super::InferCtxt; @@ -29,24 +31,29 @@ use super::InferCtxt; /// only live for a short period of time. #[derive(Clone, Debug, PartialEq, Eq)] pub struct ObligationCause { - /// The ID of the fn body that triggered this obligation. This is - /// used for region obligations to determine the precise - /// environment in which the region obligation should be evaluated - /// (in particular, closures can add new assumptions). See the - /// field `region_obligations` of the `FulfillmentContext` for more - /// information. - pub body_id: Option, + // FIXME: This should contain an `ExprId`/`PatId` etc., and a cause code. But for now we + // don't report trait solving diagnostics, so this is irrelevant. + _private: (), } impl ObligationCause { + #[expect( + clippy::new_without_default, + reason = "`new` is temporary, eventually we will provide span etc. here" + )] #[inline] - pub fn new(body_id: SolverDefId) -> ObligationCause { - ObligationCause { body_id: Some(body_id) } + pub fn new() -> ObligationCause { + ObligationCause { _private: () } } - #[inline(always)] + #[inline] pub fn dummy() -> ObligationCause { - ObligationCause { body_id: None } + ObligationCause::new() + } + + #[inline] + pub fn misc() -> ObligationCause { + ObligationCause::new() } } @@ -75,6 +82,72 @@ pub struct Obligation<'db, T> { pub recursion_depth: usize, } +/// For [`Obligation`], a sub-obligation is combined with the current obligation's +/// param-env and cause code. +impl<'db> Elaboratable> for PredicateObligation<'db> { + fn predicate(&self) -> Predicate<'db> { + self.predicate + } + + fn child(&self, clause: Clause<'db>) -> Self { + Obligation { + cause: self.cause.clone(), + param_env: self.param_env, + recursion_depth: 0, + predicate: clause.as_predicate(), + } + } + + fn child_with_derived_cause( + &self, + clause: Clause<'db>, + span: Span, + parent_trait_pred: PolyTraitPredicate<'db>, + index: usize, + ) -> Self { + let cause = ObligationCause::new(); + Obligation { + cause, + param_env: self.param_env, + recursion_depth: 0, + predicate: clause.as_predicate(), + } + } +} + +impl<'db, T: TypeVisitable>> TypeVisitable> for Obligation<'db, T> { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + rustc_ast_ir::try_visit!(self.param_env.visit_with(visitor)); + self.predicate.visit_with(visitor) + } +} + +impl<'db, T: TypeFoldable>> TypeFoldable> for Obligation<'db, T> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + Ok(Obligation { + cause: self.cause.clone(), + param_env: self.param_env.try_fold_with(folder)?, + predicate: self.predicate.try_fold_with(folder)?, + recursion_depth: self.recursion_depth, + }) + } + + fn fold_with>>(self, folder: &mut F) -> Self { + Obligation { + cause: self.cause.clone(), + param_env: self.param_env.fold_with(folder), + predicate: self.predicate.fold_with(folder), + recursion_depth: self.recursion_depth, + } + } +} + impl<'db, T: Copy> Obligation<'db, T> { pub fn as_goal(&self) -> Goal<'db, T> { Goal { param_env: self.param_env, predicate: self.predicate } @@ -156,15 +229,6 @@ impl<'db, O> Obligation<'db, O> { Obligation { cause, param_env, recursion_depth, predicate } } - pub fn misc( - tcx: DbInterner<'db>, - body_id: SolverDefId, - param_env: ParamEnv<'db>, - trait_ref: impl Upcast, O>, - ) -> Obligation<'db, O> { - Obligation::new(tcx, ObligationCause::new(body_id), param_env, trait_ref) - } - pub fn with

::Metadata @@ -491,6 +490,13 @@ define!( /// This allows bypassing normal validation to generate strange casts. fn CastPtrToPtr(operand: T) -> U ); +define!( + "mir_cast_unsize", + /// Emits a `CastKind::PointerCoercion(Unsize)` cast. + /// + /// This allows bypassing normal validation to generate strange casts. + fn CastUnsize(operand: T) -> U +); define!( "mir_make_place", #[doc(hidden)] diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 68f0b5ea2558..40fd2cdeb86f 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -126,7 +126,7 @@ fn check_rvalue<'tcx>( ) -> McfResult { match rvalue { Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())), - Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) => { + Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) => { check_place(cx, *place, span, body, msrv) }, Rvalue::CopyForDeref(place) => check_place(cx, *place, span, body, msrv), diff --git a/tests/mir-opt/building/custom/arrays.arrays.built.after.mir b/tests/mir-opt/building/custom/arrays.arrays.built.after.mir index 30d11e31e4d4..f9f24c8eabe4 100644 --- a/tests/mir-opt/building/custom/arrays.arrays.built.after.mir +++ b/tests/mir-opt/building/custom/arrays.arrays.built.after.mir @@ -3,12 +3,16 @@ fn arrays() -> usize { let mut _0: usize; let mut _1: [i32; C]; - let mut _2: usize; + let mut _2: *const [i32; C]; + let mut _3: *const [i32]; + let mut _4: usize; bb0: { _1 = [const 5_i32; C]; - _2 = Len(_1); - _0 = copy _2; + _2 = &raw const _1; + _3 = copy _2 as *const [i32] (PointerCoercion(Unsize, AsCast)); + _4 = PtrMetadata(copy _3); + _0 = copy _4; return; } } diff --git a/tests/mir-opt/building/custom/arrays.rs b/tests/mir-opt/building/custom/arrays.rs index 4bd6f93e1134..e9cdded4a0e6 100644 --- a/tests/mir-opt/building/custom/arrays.rs +++ b/tests/mir-opt/building/custom/arrays.rs @@ -10,7 +10,9 @@ fn arrays() -> usize { mir! { { let x = [5_i32; C]; - let c = Len(x); + let y = &raw const x; + let z = CastUnsize::<_, *const [i32]>(y); + let c = PtrMetadata(z); RET = c; Return() } diff --git a/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-abort.diff new file mode 100644 index 000000000000..fd9ef54fe775 --- /dev/null +++ b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-abort.diff @@ -0,0 +1,77 @@ +- // MIR for `main` before DataflowConstProp ++ // MIR for `main` after DataflowConstProp + + fn main() -> () { + let mut _0: (); + let _1: u32; + let mut _2: &[u32]; + let mut _3: &[u32; 3]; + let _4: &[u32; 3]; + let _5: [u32; 3]; + let _6: usize; + let mut _7: usize; + let mut _8: bool; + let mut _10: &[u32]; + let _11: usize; + let mut _12: usize; + let mut _13: bool; + let mut _14: &[u32; 3]; + scope 1 { + debug local => _1; + let _9: u32; + scope 2 { + debug constant => _9; + } + } + + bb0: { + StorageLive(_1); + StorageLive(_2); + StorageLive(_3); + StorageLive(_4); + _14 = const main::promoted[0]; + _4 = copy _14; + _3 = copy _4; + _2 = move _3 as &[u32] (PointerCoercion(Unsize, AsCast)); + StorageDead(_3); + StorageLive(_6); + _6 = const 1_usize; +- _7 = PtrMetadata(copy _2); +- _8 = Lt(copy _6, copy _7); +- assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, copy _6) -> [success: bb1, unwind unreachable]; ++ _7 = const 3_usize; ++ _8 = const true; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind unreachable]; + } + + bb1: { +- _1 = copy (*_2)[_6]; ++ _1 = copy (*_2)[1 of 2]; + StorageDead(_6); + StorageDead(_4); + StorageDead(_2); + StorageLive(_9); + StorageLive(_10); + _10 = const main::SLICE; + StorageLive(_11); + _11 = const 1_usize; +- _12 = PtrMetadata(copy _10); +- _13 = Lt(copy _11, copy _12); +- assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, copy _11) -> [success: bb2, unwind unreachable]; ++ _12 = const 3_usize; ++ _13 = const true; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb2, unwind unreachable]; + } + + bb2: { +- _9 = copy (*_10)[_11]; ++ _9 = copy (*_10)[1 of 2]; + StorageDead(_11); + StorageDead(_10); + _0 = const (); + StorageDead(_9); + StorageDead(_1); + return; + } + } + diff --git a/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-unwind.diff new file mode 100644 index 000000000000..1fec08c25620 --- /dev/null +++ b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-unwind.diff @@ -0,0 +1,77 @@ +- // MIR for `main` before DataflowConstProp ++ // MIR for `main` after DataflowConstProp + + fn main() -> () { + let mut _0: (); + let _1: u32; + let mut _2: &[u32]; + let mut _3: &[u32; 3]; + let _4: &[u32; 3]; + let _5: [u32; 3]; + let _6: usize; + let mut _7: usize; + let mut _8: bool; + let mut _10: &[u32]; + let _11: usize; + let mut _12: usize; + let mut _13: bool; + let mut _14: &[u32; 3]; + scope 1 { + debug local => _1; + let _9: u32; + scope 2 { + debug constant => _9; + } + } + + bb0: { + StorageLive(_1); + StorageLive(_2); + StorageLive(_3); + StorageLive(_4); + _14 = const main::promoted[0]; + _4 = copy _14; + _3 = copy _4; + _2 = move _3 as &[u32] (PointerCoercion(Unsize, AsCast)); + StorageDead(_3); + StorageLive(_6); + _6 = const 1_usize; +- _7 = PtrMetadata(copy _2); +- _8 = Lt(copy _6, copy _7); +- assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, copy _6) -> [success: bb1, unwind continue]; ++ _7 = const 3_usize; ++ _8 = const true; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind continue]; + } + + bb1: { +- _1 = copy (*_2)[_6]; ++ _1 = copy (*_2)[1 of 2]; + StorageDead(_6); + StorageDead(_4); + StorageDead(_2); + StorageLive(_9); + StorageLive(_10); + _10 = const main::SLICE; + StorageLive(_11); + _11 = const 1_usize; +- _12 = PtrMetadata(copy _10); +- _13 = Lt(copy _11, copy _12); +- assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, copy _11) -> [success: bb2, unwind continue]; ++ _12 = const 3_usize; ++ _13 = const true; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb2, unwind continue]; + } + + bb2: { +- _9 = copy (*_10)[_11]; ++ _9 = copy (*_10)[1 of 2]; + StorageDead(_11); + StorageDead(_10); + _0 = const (); + StorageDead(_9); + StorageDead(_1); + return; + } + } + diff --git a/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-abort.diff new file mode 100644 index 000000000000..fd9ef54fe775 --- /dev/null +++ b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-abort.diff @@ -0,0 +1,77 @@ +- // MIR for `main` before DataflowConstProp ++ // MIR for `main` after DataflowConstProp + + fn main() -> () { + let mut _0: (); + let _1: u32; + let mut _2: &[u32]; + let mut _3: &[u32; 3]; + let _4: &[u32; 3]; + let _5: [u32; 3]; + let _6: usize; + let mut _7: usize; + let mut _8: bool; + let mut _10: &[u32]; + let _11: usize; + let mut _12: usize; + let mut _13: bool; + let mut _14: &[u32; 3]; + scope 1 { + debug local => _1; + let _9: u32; + scope 2 { + debug constant => _9; + } + } + + bb0: { + StorageLive(_1); + StorageLive(_2); + StorageLive(_3); + StorageLive(_4); + _14 = const main::promoted[0]; + _4 = copy _14; + _3 = copy _4; + _2 = move _3 as &[u32] (PointerCoercion(Unsize, AsCast)); + StorageDead(_3); + StorageLive(_6); + _6 = const 1_usize; +- _7 = PtrMetadata(copy _2); +- _8 = Lt(copy _6, copy _7); +- assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, copy _6) -> [success: bb1, unwind unreachable]; ++ _7 = const 3_usize; ++ _8 = const true; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind unreachable]; + } + + bb1: { +- _1 = copy (*_2)[_6]; ++ _1 = copy (*_2)[1 of 2]; + StorageDead(_6); + StorageDead(_4); + StorageDead(_2); + StorageLive(_9); + StorageLive(_10); + _10 = const main::SLICE; + StorageLive(_11); + _11 = const 1_usize; +- _12 = PtrMetadata(copy _10); +- _13 = Lt(copy _11, copy _12); +- assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, copy _11) -> [success: bb2, unwind unreachable]; ++ _12 = const 3_usize; ++ _13 = const true; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb2, unwind unreachable]; + } + + bb2: { +- _9 = copy (*_10)[_11]; ++ _9 = copy (*_10)[1 of 2]; + StorageDead(_11); + StorageDead(_10); + _0 = const (); + StorageDead(_9); + StorageDead(_1); + return; + } + } + diff --git a/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-unwind.diff new file mode 100644 index 000000000000..1fec08c25620 --- /dev/null +++ b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-unwind.diff @@ -0,0 +1,77 @@ +- // MIR for `main` before DataflowConstProp ++ // MIR for `main` after DataflowConstProp + + fn main() -> () { + let mut _0: (); + let _1: u32; + let mut _2: &[u32]; + let mut _3: &[u32; 3]; + let _4: &[u32; 3]; + let _5: [u32; 3]; + let _6: usize; + let mut _7: usize; + let mut _8: bool; + let mut _10: &[u32]; + let _11: usize; + let mut _12: usize; + let mut _13: bool; + let mut _14: &[u32; 3]; + scope 1 { + debug local => _1; + let _9: u32; + scope 2 { + debug constant => _9; + } + } + + bb0: { + StorageLive(_1); + StorageLive(_2); + StorageLive(_3); + StorageLive(_4); + _14 = const main::promoted[0]; + _4 = copy _14; + _3 = copy _4; + _2 = move _3 as &[u32] (PointerCoercion(Unsize, AsCast)); + StorageDead(_3); + StorageLive(_6); + _6 = const 1_usize; +- _7 = PtrMetadata(copy _2); +- _8 = Lt(copy _6, copy _7); +- assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, copy _6) -> [success: bb1, unwind continue]; ++ _7 = const 3_usize; ++ _8 = const true; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind continue]; + } + + bb1: { +- _1 = copy (*_2)[_6]; ++ _1 = copy (*_2)[1 of 2]; + StorageDead(_6); + StorageDead(_4); + StorageDead(_2); + StorageLive(_9); + StorageLive(_10); + _10 = const main::SLICE; + StorageLive(_11); + _11 = const 1_usize; +- _12 = PtrMetadata(copy _10); +- _13 = Lt(copy _11, copy _12); +- assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, copy _11) -> [success: bb2, unwind continue]; ++ _12 = const 3_usize; ++ _13 = const true; ++ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb2, unwind continue]; + } + + bb2: { +- _9 = copy (*_10)[_11]; ++ _9 = copy (*_10)[1 of 2]; + StorageDead(_11); + StorageDead(_10); + _0 = const (); + StorageDead(_9); + StorageDead(_1); + return; + } + } + diff --git a/tests/mir-opt/dataflow-const-prop/slice_len.rs b/tests/mir-opt/dataflow-const-prop/slice_len.rs new file mode 100644 index 000000000000..e0e68f9fde54 --- /dev/null +++ b/tests/mir-opt/dataflow-const-prop/slice_len.rs @@ -0,0 +1,34 @@ +// EMIT_MIR_FOR_EACH_PANIC_STRATEGY +//@ test-mir-pass: DataflowConstProp +//@ compile-flags: -Zmir-enable-passes=+InstSimplify-after-simplifycfg +// EMIT_MIR_FOR_EACH_BIT_WIDTH + +// EMIT_MIR slice_len.main.DataflowConstProp.diff + +// CHECK-LABEL: fn main( +fn main() { + // CHECK: debug local => [[local:_.*]]; + // CHECK: debug constant => [[constant:_.*]]; + + // CHECK-NOT: {{_.*}} = Len( + // CHECK-NOT: {{_.*}} = Lt( + // CHECK-NOT: assert(move _ + // CHECK: {{_.*}} = const 3_usize; + // CHECK: {{_.*}} = const true; + // CHECK: assert(const true, + + // CHECK: [[local]] = copy (*{{_.*}})[1 of 2]; + let local = (&[1u32, 2, 3] as &[u32])[1]; + + // CHECK-NOT: {{_.*}} = Len( + // CHECK-NOT: {{_.*}} = Lt( + // CHECK-NOT: assert(move _ + const SLICE: &[u32] = &[1, 2, 3]; + // CHECK: {{_.*}} = const 3_usize; + // CHECK: {{_.*}} = const true; + // CHECK: assert(const true, + + // CHECK-NOT: [[constant]] = {{copy|move}} (*{{_.*}})[_ + // CHECK: [[constant]] = copy (*{{_.*}})[1 of 2]; + let constant = SLICE[1]; +} diff --git a/tests/mir-opt/gvn.array_len.GVN.panic-abort.diff b/tests/mir-opt/gvn.array_len.GVN.panic-abort.diff index 0d0477fe7729..59b65a52f4ee 100644 --- a/tests/mir-opt/gvn.array_len.GVN.panic-abort.diff +++ b/tests/mir-opt/gvn.array_len.GVN.panic-abort.diff @@ -12,8 +12,7 @@ } bb0: { -- StorageLive(_2); -+ nop; + StorageLive(_2); StorageLive(_3); _3 = &(*_1); _2 = move _3 as &[i32] (PointerCoercion(Unsize, Implicit)); @@ -23,8 +22,7 @@ - _0 = PtrMetadata(move _4); + _0 = const 42_usize; StorageDead(_4); -- StorageDead(_2); -+ nop; + StorageDead(_2); return; } } diff --git a/tests/mir-opt/gvn.array_len.GVN.panic-unwind.diff b/tests/mir-opt/gvn.array_len.GVN.panic-unwind.diff index 0d0477fe7729..59b65a52f4ee 100644 --- a/tests/mir-opt/gvn.array_len.GVN.panic-unwind.diff +++ b/tests/mir-opt/gvn.array_len.GVN.panic-unwind.diff @@ -12,8 +12,7 @@ } bb0: { -- StorageLive(_2); -+ nop; + StorageLive(_2); StorageLive(_3); _3 = &(*_1); _2 = move _3 as &[i32] (PointerCoercion(Unsize, Implicit)); @@ -23,8 +22,7 @@ - _0 = PtrMetadata(move _4); + _0 = const 42_usize; StorageDead(_4); -- StorageDead(_2); -+ nop; + StorageDead(_2); return; } } diff --git a/tests/mir-opt/instsimplify/combine_array_len.norm2.InstSimplify-after-simplifycfg.panic-abort.diff b/tests/mir-opt/instsimplify/combine_array_len.norm2.InstSimplify-after-simplifycfg.panic-abort.diff new file mode 100644 index 000000000000..feba0fe7c4a2 --- /dev/null +++ b/tests/mir-opt/instsimplify/combine_array_len.norm2.InstSimplify-after-simplifycfg.panic-abort.diff @@ -0,0 +1,71 @@ +- // MIR for `norm2` before InstSimplify-after-simplifycfg ++ // MIR for `norm2` after InstSimplify-after-simplifycfg + + fn norm2(_1: [f32; 2]) -> f32 { + debug x => _1; + let mut _0: f32; + let _2: f32; + let _3: usize; + let mut _4: bool; + let _6: usize; + let mut _7: bool; + let mut _8: f32; + let mut _9: f32; + let mut _10: f32; + let mut _11: f32; + let mut _12: f32; + let mut _13: f32; + scope 1 { + debug a => _2; + let _5: f32; + scope 2 { + debug b => _5; + } + } + + bb0: { + StorageLive(_2); + StorageLive(_3); + _3 = const 0_usize; + _4 = Lt(copy _3, const 2_usize); + assert(move _4, "index out of bounds: the length is {} but the index is {}", const 2_usize, copy _3) -> [success: bb1, unwind unreachable]; + } + + bb1: { + _2 = copy _1[_3]; + StorageDead(_3); + StorageLive(_5); + StorageLive(_6); + _6 = const 1_usize; + _7 = Lt(copy _6, const 2_usize); + assert(move _7, "index out of bounds: the length is {} but the index is {}", const 2_usize, copy _6) -> [success: bb2, unwind unreachable]; + } + + bb2: { + _5 = copy _1[_6]; + StorageDead(_6); + StorageLive(_8); + StorageLive(_9); + _9 = copy _2; + StorageLive(_10); + _10 = copy _2; + _8 = Mul(move _9, move _10); + StorageDead(_10); + StorageDead(_9); + StorageLive(_11); + StorageLive(_12); + _12 = copy _5; + StorageLive(_13); + _13 = copy _5; + _11 = Mul(move _12, move _13); + StorageDead(_13); + StorageDead(_12); + _0 = Add(move _8, move _11); + StorageDead(_11); + StorageDead(_8); + StorageDead(_5); + StorageDead(_2); + return; + } + } + diff --git a/tests/mir-opt/instsimplify/combine_array_len.norm2.InstSimplify-after-simplifycfg.panic-unwind.diff b/tests/mir-opt/instsimplify/combine_array_len.norm2.InstSimplify-after-simplifycfg.panic-unwind.diff new file mode 100644 index 000000000000..0ccf6a825c49 --- /dev/null +++ b/tests/mir-opt/instsimplify/combine_array_len.norm2.InstSimplify-after-simplifycfg.panic-unwind.diff @@ -0,0 +1,71 @@ +- // MIR for `norm2` before InstSimplify-after-simplifycfg ++ // MIR for `norm2` after InstSimplify-after-simplifycfg + + fn norm2(_1: [f32; 2]) -> f32 { + debug x => _1; + let mut _0: f32; + let _2: f32; + let _3: usize; + let mut _4: bool; + let _6: usize; + let mut _7: bool; + let mut _8: f32; + let mut _9: f32; + let mut _10: f32; + let mut _11: f32; + let mut _12: f32; + let mut _13: f32; + scope 1 { + debug a => _2; + let _5: f32; + scope 2 { + debug b => _5; + } + } + + bb0: { + StorageLive(_2); + StorageLive(_3); + _3 = const 0_usize; + _4 = Lt(copy _3, const 2_usize); + assert(move _4, "index out of bounds: the length is {} but the index is {}", const 2_usize, copy _3) -> [success: bb1, unwind continue]; + } + + bb1: { + _2 = copy _1[_3]; + StorageDead(_3); + StorageLive(_5); + StorageLive(_6); + _6 = const 1_usize; + _7 = Lt(copy _6, const 2_usize); + assert(move _7, "index out of bounds: the length is {} but the index is {}", const 2_usize, copy _6) -> [success: bb2, unwind continue]; + } + + bb2: { + _5 = copy _1[_6]; + StorageDead(_6); + StorageLive(_8); + StorageLive(_9); + _9 = copy _2; + StorageLive(_10); + _10 = copy _2; + _8 = Mul(move _9, move _10); + StorageDead(_10); + StorageDead(_9); + StorageLive(_11); + StorageLive(_12); + _12 = copy _5; + StorageLive(_13); + _13 = copy _5; + _11 = Mul(move _12, move _13); + StorageDead(_13); + StorageDead(_12); + _0 = Add(move _8, move _11); + StorageDead(_11); + StorageDead(_8); + StorageDead(_5); + StorageDead(_2); + return; + } + } + diff --git a/tests/mir-opt/instsimplify/combine_array_len.rs b/tests/mir-opt/instsimplify/combine_array_len.rs new file mode 100644 index 000000000000..91f43f75689d --- /dev/null +++ b/tests/mir-opt/instsimplify/combine_array_len.rs @@ -0,0 +1,15 @@ +// EMIT_MIR_FOR_EACH_PANIC_STRATEGY +//@ test-mir-pass: InstSimplify-after-simplifycfg + +// EMIT_MIR combine_array_len.norm2.InstSimplify-after-simplifycfg.diff +fn norm2(x: [f32; 2]) -> f32 { + // CHECK-LABEL: fn norm2( + // CHECK-NOT: Len( + let a = x[0]; + let b = x[1]; + a * a + b * b +} + +fn main() { + assert_eq!(norm2([3.0, 4.0]), 5.0 * 5.0); +} diff --git a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff index 08dee3697e07..5cf36b9aebf2 100644 --- a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff +++ b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff @@ -7,18 +7,16 @@ let _2: &[T]; let _3: &[T; 3]; let _4: [T; 3]; - let mut _5: usize; - let mut _6: bool; - let mut _10: !; + let mut _8: !; scope 1 { debug v => _2; + let _5: &T; + let _6: &T; let _7: &T; - let _8: &T; - let _9: &T; scope 2 { - debug v1 => _7; - debug v2 => _8; - debug v3 => _9; + debug v1 => _5; + debug v2 => _6; + debug v3 => _7; } } @@ -27,25 +25,23 @@ _4 = [copy _1, copy _1, copy _1]; _3 = &_4; _2 = copy _3 as &[T] (PointerCoercion(Unsize, Implicit)); - nop; - nop; goto -> bb2; } bb1: { - _10 = core::panicking::panic(const "internal error: entered unreachable code") -> unwind unreachable; + _8 = core::panicking::panic(const "internal error: entered unreachable code") -> unwind unreachable; } bb2: { + StorageLive(_5); + _5 = &(*_2)[0 of 3]; + StorageLive(_6); + _6 = &(*_2)[1 of 3]; StorageLive(_7); - _7 = &(*_2)[0 of 3]; - StorageLive(_8); - _8 = &(*_2)[1 of 3]; - StorageLive(_9); - _9 = &(*_2)[2 of 3]; - StorageDead(_9); - StorageDead(_8); + _7 = &(*_2)[2 of 3]; StorageDead(_7); + StorageDead(_6); + StorageDead(_5); StorageDead(_4); return; } diff --git a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff index aa44a2ad532b..0598a3aa3f19 100644 --- a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff +++ b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff @@ -7,18 +7,16 @@ let _2: &[T]; let _3: &[T; 3]; let _4: [T; 3]; - let mut _5: usize; - let mut _6: bool; - let mut _10: !; + let mut _8: !; scope 1 { debug v => _2; + let _5: &T; + let _6: &T; let _7: &T; - let _8: &T; - let _9: &T; scope 2 { - debug v1 => _7; - debug v2 => _8; - debug v3 => _9; + debug v1 => _5; + debug v2 => _6; + debug v3 => _7; } } @@ -27,25 +25,23 @@ _4 = [copy _1, copy _1, copy _1]; _3 = &_4; _2 = copy _3 as &[T] (PointerCoercion(Unsize, Implicit)); - nop; - nop; goto -> bb2; } bb1: { - _10 = core::panicking::panic(const "internal error: entered unreachable code") -> unwind continue; + _8 = core::panicking::panic(const "internal error: entered unreachable code") -> unwind continue; } bb2: { + StorageLive(_5); + _5 = &(*_2)[0 of 3]; + StorageLive(_6); + _6 = &(*_2)[1 of 3]; StorageLive(_7); - _7 = &(*_2)[0 of 3]; - StorageLive(_8); - _8 = &(*_2)[1 of 3]; - StorageLive(_9); - _9 = &(*_2)[2 of 3]; - StorageDead(_9); - StorageDead(_8); + _7 = &(*_2)[2 of 3]; StorageDead(_7); + StorageDead(_6); + StorageDead(_5); StorageDead(_4); return; } diff --git a/tests/mir-opt/lower_array_len.array_len_raw.GVN.panic-abort.diff b/tests/mir-opt/lower_array_len.array_len_raw.GVN.panic-abort.diff index 180a7db02979..714a12804e69 100644 --- a/tests/mir-opt/lower_array_len.array_len_raw.GVN.panic-abort.diff +++ b/tests/mir-opt/lower_array_len.array_len_raw.GVN.panic-abort.diff @@ -18,8 +18,7 @@ } bb0: { -- StorageLive(_2); -+ nop; + StorageLive(_2); StorageLive(_3); StorageLive(_4); _4 = &_1; @@ -41,8 +40,7 @@ bb1: { StorageDead(_6); StorageDead(_5); -- StorageDead(_2); -+ nop; + StorageDead(_2); StorageDead(_7); return; } diff --git a/tests/mir-opt/lower_array_len.array_len_raw.GVN.panic-unwind.diff b/tests/mir-opt/lower_array_len.array_len_raw.GVN.panic-unwind.diff index 180a7db02979..714a12804e69 100644 --- a/tests/mir-opt/lower_array_len.array_len_raw.GVN.panic-unwind.diff +++ b/tests/mir-opt/lower_array_len.array_len_raw.GVN.panic-unwind.diff @@ -18,8 +18,7 @@ } bb0: { -- StorageLive(_2); -+ nop; + StorageLive(_2); StorageLive(_3); StorageLive(_4); _4 = &_1; @@ -41,8 +40,7 @@ bb1: { StorageDead(_6); StorageDead(_5); -- StorageDead(_2); -+ nop; + StorageDead(_2); StorageDead(_7); return; } diff --git a/tests/mir-opt/lower_array_len.array_len_reborrow.GVN.panic-abort.diff b/tests/mir-opt/lower_array_len.array_len_reborrow.GVN.panic-abort.diff index 49964f8b49e2..b236b22f067d 100644 --- a/tests/mir-opt/lower_array_len.array_len_reborrow.GVN.panic-abort.diff +++ b/tests/mir-opt/lower_array_len.array_len_reborrow.GVN.panic-abort.diff @@ -17,8 +17,7 @@ } bb0: { -- StorageLive(_2); -+ nop; + StorageLive(_2); StorageLive(_3); StorageLive(_4); _4 = &mut _1; @@ -38,8 +37,7 @@ bb1: { StorageDead(_6); StorageDead(_5); -- StorageDead(_2); -+ nop; + StorageDead(_2); return; } } diff --git a/tests/mir-opt/lower_array_len.array_len_reborrow.GVN.panic-unwind.diff b/tests/mir-opt/lower_array_len.array_len_reborrow.GVN.panic-unwind.diff index 49964f8b49e2..b236b22f067d 100644 --- a/tests/mir-opt/lower_array_len.array_len_reborrow.GVN.panic-unwind.diff +++ b/tests/mir-opt/lower_array_len.array_len_reborrow.GVN.panic-unwind.diff @@ -17,8 +17,7 @@ } bb0: { -- StorageLive(_2); -+ nop; + StorageLive(_2); StorageLive(_3); StorageLive(_4); _4 = &mut _1; @@ -38,8 +37,7 @@ bb1: { StorageDead(_6); StorageDead(_5); -- StorageDead(_2); -+ nop; + StorageDead(_2); return; } } diff --git a/tests/mir-opt/reference_prop.debuginfo.ReferencePropagation.diff b/tests/mir-opt/reference_prop.debuginfo.ReferencePropagation.diff index 3da795b61f94..375b6096d88d 100644 --- a/tests/mir-opt/reference_prop.debuginfo.ReferencePropagation.diff +++ b/tests/mir-opt/reference_prop.debuginfo.ReferencePropagation.diff @@ -17,14 +17,15 @@ let mut _15: std::ops::RangeFull; let mut _16: usize; let mut _17: usize; - let mut _18: bool; - let _23: &&mut u8; - let _24: &mut u8; - let mut _25: debuginfo::T; + let mut _18: usize; + let mut _19: bool; + let _24: &&mut u8; + let _25: &mut u8; + let mut _26: debuginfo::T; scope 1 { debug ref_mut_u8 => _1; let _3: &u8; - let mut _28: &debuginfo::T; + let mut _29: &debuginfo::T; scope 2 { debug field => _3; let _5: &u8; @@ -32,22 +33,22 @@ - debug reborrow => _5; + debug reborrow => _1; let _9: &i32; - let _22: &&&mut u8; - let mut _27: &std::option::Option; + let _23: &&&mut u8; + let mut _28: &std::option::Option; scope 4 { debug variant_field => _9; } scope 5 { - debug constant_index => _19; - debug subslice => _20; - debug constant_index_from_end => _21; - let _19: &i32; - let _20: &[i32]; - let _21: &i32; - let mut _26: &[i32; 10]; + debug constant_index => _20; + debug subslice => _21; + debug constant_index_from_end => _22; + let _20: &i32; + let _21: &[i32]; + let _22: &i32; + let mut _27: &[i32; 10]; } scope 6 { - debug multiple_borrow => _22; + debug multiple_borrow => _23; } } } @@ -59,8 +60,8 @@ _2 = const 5_u8; _1 = &mut _2; StorageLive(_3); - _28 = const debuginfo::promoted[2]; - _3 = &((*_28).0: u8); + _29 = const debuginfo::promoted[2]; + _3 = &((*_29).0: u8); - StorageLive(_5); - _5 = &(*_1); - StorageLive(_6); @@ -76,8 +77,8 @@ bb2: { StorageLive(_9); - _27 = const debuginfo::promoted[1]; - _9 = &(((*_27) as Some).0: i32); + _28 = const debuginfo::promoted[1]; + _9 = &(((*_28) as Some).0: i32); - _6 = const (); StorageDead(_9); goto -> bb4; @@ -92,11 +93,11 @@ StorageDead(_7); - StorageDead(_6); - StorageLive(_10); -- StorageLive(_11); + StorageLive(_11); - StorageLive(_12); StorageLive(_13); - _26 = const debuginfo::promoted[0]; - _13 = &(*_26); + _27 = const debuginfo::promoted[0]; + _13 = &(*_27); StorageLive(_15); _15 = RangeFull; - _12 = <[i32; 10] as Index>::index(move _13, move _15) -> [return: bb5, unwind continue]; @@ -106,28 +107,28 @@ bb5: { StorageDead(_15); StorageDead(_13); -- _11 = &(*_12); -- _16 = Len((*_11)); -+ _16 = Len((*_12)); - _17 = const 3_usize; - _18 = Ge(move _16, move _17); - switchInt(move _18) -> [0: bb7, otherwise: bb6]; + _11 = &(*_12); + _17 = PtrMetadata(copy _11); + _16 = move _17; + _18 = const 3_usize; + _19 = Ge(move _16, move _18); + switchInt(move _19) -> [0: bb7, otherwise: bb6]; } bb6: { - StorageLive(_19); -- _19 = &(*_11)[1 of 3]; -+ _19 = &(*_12)[1 of 3]; StorageLive(_20); -- _20 = &(*_11)[2:-1]; -+ _20 = &(*_12)[2:-1]; +- _20 = &(*_11)[1 of 3]; ++ _20 = &(*_12)[1 of 3]; StorageLive(_21); -- _21 = &(*_11)[-1 of 3]; +- _21 = &(*_11)[2:-1]; ++ _21 = &(*_12)[2:-1]; + StorageLive(_22); +- _22 = &(*_11)[-1 of 3]; - _10 = const (); -+ _21 = &(*_12)[-1 of 3]; ++ _22 = &(*_12)[-1 of 3]; + StorageDead(_22); StorageDead(_21); StorageDead(_20); - StorageDead(_19); goto -> bb8; } @@ -138,21 +139,21 @@ bb8: { - StorageDead(_12); -- StorageDead(_11); + StorageDead(_11); - StorageDead(_10); - StorageLive(_22); StorageLive(_23); StorageLive(_24); StorageLive(_25); - _25 = T(const 6_u8); - _24 = &mut (_25.0: u8); + StorageLive(_26); + _26 = T(const 6_u8); + _25 = &mut (_26.0: u8); + _24 = &_25; _23 = &_24; - _22 = &_23; _0 = const (); + StorageDead(_26); StorageDead(_25); StorageDead(_24); StorageDead(_23); - StorageDead(_22); - StorageDead(_5); StorageDead(_3); StorageDead(_2); From f018b46558c2c4049cf26bd88d562eefa69ff336 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Tue, 16 Sep 2025 01:45:06 +0000 Subject: [PATCH 574/682] Update docs. --- library/core/src/intrinsics/mir.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/core/src/intrinsics/mir.rs b/library/core/src/intrinsics/mir.rs index c4bd10e606aa..a800ef1cb937 100644 --- a/library/core/src/intrinsics/mir.rs +++ b/library/core/src/intrinsics/mir.rs @@ -233,7 +233,8 @@ //! //! - Operands implicitly convert to `Use` rvalues. //! - `&`, `&mut`, `addr_of!`, and `addr_of_mut!` all work to create their associated rvalue. -//! - [`Discriminant`], [`Len`], and [`CopyForDeref`] have associated functions. +//! - [`CopyForDeref`], [`CastTransmute`], [`CastPtrToPtr`], [`CastUnsize`], and [`Discriminant`] +//! have associated functions. //! - Unary and binary operations use their normal Rust syntax - `a * b`, `!c`, etc. //! - The binary operation `Offset` can be created via [`Offset`]. //! - Checked binary operations are represented by wrapping the associated binop in [`Checked`]. From 4516fee8cbcbc6e85e4da34614aaa579315563c7 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sun, 14 Sep 2025 22:29:04 +0000 Subject: [PATCH 575/682] Remove Rvalue::Len. --- clippy_utils/src/qualify_min_const_fn.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 68f0b5ea2558..40fd2cdeb86f 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -126,7 +126,7 @@ fn check_rvalue<'tcx>( ) -> McfResult { match rvalue { Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())), - Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) => { + Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) => { check_place(cx, *place, span, body, msrv) }, Rvalue::CopyForDeref(place) => check_place(cx, *place, span, body, msrv), From de73af9ec47552dcade86e4ebc4edb8a68fddd2e Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Tue, 16 Sep 2025 22:44:35 +0000 Subject: [PATCH 576/682] Add test. --- ...onst_array_len.built.after.panic-abort.mir | 155 ++++++++++++++++++ ...nst_array_len.built.after.panic-unwind.mir | 155 ++++++++++++++++++ tests/mir-opt/building/match/array_len.rs | 31 ++++ ..._len.slice_len.built.after.panic-abort.mir | 115 +++++++++++++ ...len.slice_len.built.after.panic-unwind.mir | 115 +++++++++++++ ...implify-after-simplifycfg.panic-abort.diff | 71 ++++++++ ...mplify-after-simplifycfg.panic-unwind.diff | 71 ++++++++ .../mir-opt/instsimplify/combine_array_len.rs | 12 +- 8 files changed, 724 insertions(+), 1 deletion(-) create mode 100644 tests/mir-opt/building/match/array_len.const_array_len.built.after.panic-abort.mir create mode 100644 tests/mir-opt/building/match/array_len.const_array_len.built.after.panic-unwind.mir create mode 100644 tests/mir-opt/building/match/array_len.rs create mode 100644 tests/mir-opt/building/match/array_len.slice_len.built.after.panic-abort.mir create mode 100644 tests/mir-opt/building/match/array_len.slice_len.built.after.panic-unwind.mir create mode 100644 tests/mir-opt/instsimplify/combine_array_len.normN.InstSimplify-after-simplifycfg.panic-abort.diff create mode 100644 tests/mir-opt/instsimplify/combine_array_len.normN.InstSimplify-after-simplifycfg.panic-unwind.diff diff --git a/tests/mir-opt/building/match/array_len.const_array_len.built.after.panic-abort.mir b/tests/mir-opt/building/match/array_len.const_array_len.built.after.panic-abort.mir new file mode 100644 index 000000000000..8d16f074b1e3 --- /dev/null +++ b/tests/mir-opt/building/match/array_len.const_array_len.built.after.panic-abort.mir @@ -0,0 +1,155 @@ +// MIR for `const_array_len` after built + +fn const_array_len(_1: [T; 5]) -> () { + debug x => _1; + let mut _0: (); + let _6: (); + let mut _7: T; + let _8: (); + let mut _9: T; + let _10: (); + let mut _11: [T; 2]; + let _12: (); + let mut _13: T; + scope 1 { + debug a => _2; + debug b => _3; + debug rest => _4; + debug e => _5; + let _2: T; + let _3: T; + let _4: [T; 2]; + let _5: T; + } + + bb0: { + PlaceMention(_1); + falseEdge -> [real: bb2, imaginary: bb1]; + } + + bb1: { + goto -> bb7; + } + + bb2: { + StorageLive(_2); + _2 = move _1[0 of 5]; + StorageLive(_3); + _3 = move _1[1 of 5]; + StorageLive(_4); + _4 = move _1[2..4]; + StorageLive(_5); + _5 = move _1[4 of 5]; + StorageLive(_6); + StorageLive(_7); + _7 = move _2; + _6 = opaque::(move _7) -> [return: bb3, unwind: bb17]; + } + + bb3: { + StorageDead(_7); + StorageDead(_6); + StorageLive(_8); + StorageLive(_9); + _9 = move _3; + _8 = opaque::(move _9) -> [return: bb4, unwind: bb16]; + } + + bb4: { + StorageDead(_9); + StorageDead(_8); + StorageLive(_10); + StorageLive(_11); + _11 = move _4; + _10 = opaque::<[T; 2]>(move _11) -> [return: bb5, unwind: bb15]; + } + + bb5: { + StorageDead(_11); + StorageDead(_10); + StorageLive(_12); + StorageLive(_13); + _13 = move _5; + _12 = opaque::(move _13) -> [return: bb6, unwind: bb14]; + } + + bb6: { + StorageDead(_13); + StorageDead(_12); + _0 = const (); + drop(_5) -> [return: bb8, unwind: bb19]; + } + + bb7: { + _0 = const (); + goto -> bb12; + } + + bb8: { + StorageDead(_5); + drop(_4) -> [return: bb9, unwind: bb20]; + } + + bb9: { + StorageDead(_4); + drop(_3) -> [return: bb10, unwind: bb21]; + } + + bb10: { + StorageDead(_3); + drop(_2) -> [return: bb11, unwind: bb22]; + } + + bb11: { + StorageDead(_2); + goto -> bb12; + } + + bb12: { + drop(_1) -> [return: bb13, unwind: bb23]; + } + + bb13: { + return; + } + + bb14 (cleanup): { + drop(_13) -> [return: bb18, unwind terminate(cleanup)]; + } + + bb15 (cleanup): { + drop(_11) -> [return: bb18, unwind terminate(cleanup)]; + } + + bb16 (cleanup): { + drop(_9) -> [return: bb18, unwind terminate(cleanup)]; + } + + bb17 (cleanup): { + drop(_7) -> [return: bb18, unwind terminate(cleanup)]; + } + + bb18 (cleanup): { + drop(_5) -> [return: bb19, unwind terminate(cleanup)]; + } + + bb19 (cleanup): { + drop(_4) -> [return: bb20, unwind terminate(cleanup)]; + } + + bb20 (cleanup): { + drop(_3) -> [return: bb21, unwind terminate(cleanup)]; + } + + bb21 (cleanup): { + drop(_2) -> [return: bb22, unwind terminate(cleanup)]; + } + + bb22 (cleanup): { + drop(_1) -> [return: bb23, unwind terminate(cleanup)]; + } + + bb23 (cleanup): { + resume; + } +} diff --git a/tests/mir-opt/building/match/array_len.const_array_len.built.after.panic-unwind.mir b/tests/mir-opt/building/match/array_len.const_array_len.built.after.panic-unwind.mir new file mode 100644 index 000000000000..8d16f074b1e3 --- /dev/null +++ b/tests/mir-opt/building/match/array_len.const_array_len.built.after.panic-unwind.mir @@ -0,0 +1,155 @@ +// MIR for `const_array_len` after built + +fn const_array_len(_1: [T; 5]) -> () { + debug x => _1; + let mut _0: (); + let _6: (); + let mut _7: T; + let _8: (); + let mut _9: T; + let _10: (); + let mut _11: [T; 2]; + let _12: (); + let mut _13: T; + scope 1 { + debug a => _2; + debug b => _3; + debug rest => _4; + debug e => _5; + let _2: T; + let _3: T; + let _4: [T; 2]; + let _5: T; + } + + bb0: { + PlaceMention(_1); + falseEdge -> [real: bb2, imaginary: bb1]; + } + + bb1: { + goto -> bb7; + } + + bb2: { + StorageLive(_2); + _2 = move _1[0 of 5]; + StorageLive(_3); + _3 = move _1[1 of 5]; + StorageLive(_4); + _4 = move _1[2..4]; + StorageLive(_5); + _5 = move _1[4 of 5]; + StorageLive(_6); + StorageLive(_7); + _7 = move _2; + _6 = opaque::(move _7) -> [return: bb3, unwind: bb17]; + } + + bb3: { + StorageDead(_7); + StorageDead(_6); + StorageLive(_8); + StorageLive(_9); + _9 = move _3; + _8 = opaque::(move _9) -> [return: bb4, unwind: bb16]; + } + + bb4: { + StorageDead(_9); + StorageDead(_8); + StorageLive(_10); + StorageLive(_11); + _11 = move _4; + _10 = opaque::<[T; 2]>(move _11) -> [return: bb5, unwind: bb15]; + } + + bb5: { + StorageDead(_11); + StorageDead(_10); + StorageLive(_12); + StorageLive(_13); + _13 = move _5; + _12 = opaque::(move _13) -> [return: bb6, unwind: bb14]; + } + + bb6: { + StorageDead(_13); + StorageDead(_12); + _0 = const (); + drop(_5) -> [return: bb8, unwind: bb19]; + } + + bb7: { + _0 = const (); + goto -> bb12; + } + + bb8: { + StorageDead(_5); + drop(_4) -> [return: bb9, unwind: bb20]; + } + + bb9: { + StorageDead(_4); + drop(_3) -> [return: bb10, unwind: bb21]; + } + + bb10: { + StorageDead(_3); + drop(_2) -> [return: bb11, unwind: bb22]; + } + + bb11: { + StorageDead(_2); + goto -> bb12; + } + + bb12: { + drop(_1) -> [return: bb13, unwind: bb23]; + } + + bb13: { + return; + } + + bb14 (cleanup): { + drop(_13) -> [return: bb18, unwind terminate(cleanup)]; + } + + bb15 (cleanup): { + drop(_11) -> [return: bb18, unwind terminate(cleanup)]; + } + + bb16 (cleanup): { + drop(_9) -> [return: bb18, unwind terminate(cleanup)]; + } + + bb17 (cleanup): { + drop(_7) -> [return: bb18, unwind terminate(cleanup)]; + } + + bb18 (cleanup): { + drop(_5) -> [return: bb19, unwind terminate(cleanup)]; + } + + bb19 (cleanup): { + drop(_4) -> [return: bb20, unwind terminate(cleanup)]; + } + + bb20 (cleanup): { + drop(_3) -> [return: bb21, unwind terminate(cleanup)]; + } + + bb21 (cleanup): { + drop(_2) -> [return: bb22, unwind terminate(cleanup)]; + } + + bb22 (cleanup): { + drop(_1) -> [return: bb23, unwind terminate(cleanup)]; + } + + bb23 (cleanup): { + resume; + } +} diff --git a/tests/mir-opt/building/match/array_len.rs b/tests/mir-opt/building/match/array_len.rs new file mode 100644 index 000000000000..0d889ada9b60 --- /dev/null +++ b/tests/mir-opt/building/match/array_len.rs @@ -0,0 +1,31 @@ +// EMIT_MIR_FOR_EACH_PANIC_STRATEGY +//@ compile-flags: -Zmir-opt-level=0 + +fn opaque(x: T) {} + +// EMIT_MIR array_len.const_array_len.built.after.mir +fn const_array_len(x: [T; 5]) { + // CHECK-LABEL: fn const_array_len( + // CHECK-NOT: Len + // CHECK-NOT: PtrMetadata + // CHECK: = const 5_usize; + if let [a, b, rest @ .., e] = x { + opaque(a); + opaque(b); + opaque(rest); + opaque(e); + } +} + +// EMIT_MIR array_len.slice_len.built.after.mir +fn slice_len(x: &[T]) { + // CHECK-LABEL: fn slice_len( + // CHECK-NOT: Len + // CHECK: = PtrMetadata(copy _1); + if let [a, b, rest @ .., e] = x { + opaque(a); + opaque(b); + opaque(rest); + opaque(e); + } +} diff --git a/tests/mir-opt/building/match/array_len.slice_len.built.after.panic-abort.mir b/tests/mir-opt/building/match/array_len.slice_len.built.after.panic-abort.mir new file mode 100644 index 000000000000..d73f5a1b6f71 --- /dev/null +++ b/tests/mir-opt/building/match/array_len.slice_len.built.after.panic-abort.mir @@ -0,0 +1,115 @@ +// MIR for `slice_len` after built + +fn slice_len(_1: &[T]) -> () { + debug x => _1; + let mut _0: (); + let mut _2: usize; + let mut _3: usize; + let mut _4: usize; + let mut _5: bool; + let _10: (); + let mut _11: &T; + let _12: (); + let mut _13: &T; + let _14: (); + let mut _15: &[T]; + let _16: (); + let mut _17: &T; + scope 1 { + debug a => _6; + debug b => _7; + debug rest => _8; + debug e => _9; + let _6: &T; + let _7: &T; + let _8: &[T]; + let _9: &T; + } + + bb0: { + PlaceMention(_1); + _3 = PtrMetadata(copy _1); + _2 = move _3; + _4 = const 3_usize; + _5 = Ge(move _2, move _4); + switchInt(move _5) -> [0: bb1, otherwise: bb2]; + } + + bb1: { + goto -> bb9; + } + + bb2: { + falseEdge -> [real: bb4, imaginary: bb1]; + } + + bb3: { + goto -> bb1; + } + + bb4: { + StorageLive(_6); + _6 = &(*_1)[0 of 3]; + StorageLive(_7); + _7 = &(*_1)[1 of 3]; + StorageLive(_8); + _8 = &(*_1)[2:-1]; + StorageLive(_9); + _9 = &(*_1)[-1 of 3]; + StorageLive(_10); + StorageLive(_11); + _11 = copy _6; + _10 = opaque::<&T>(move _11) -> [return: bb5, unwind: bb11]; + } + + bb5: { + StorageDead(_11); + StorageDead(_10); + StorageLive(_12); + StorageLive(_13); + _13 = copy _7; + _12 = opaque::<&T>(move _13) -> [return: bb6, unwind: bb11]; + } + + bb6: { + StorageDead(_13); + StorageDead(_12); + StorageLive(_14); + StorageLive(_15); + _15 = copy _8; + _14 = opaque::<&[T]>(move _15) -> [return: bb7, unwind: bb11]; + } + + bb7: { + StorageDead(_15); + StorageDead(_14); + StorageLive(_16); + StorageLive(_17); + _17 = copy _9; + _16 = opaque::<&T>(move _17) -> [return: bb8, unwind: bb11]; + } + + bb8: { + StorageDead(_17); + StorageDead(_16); + _0 = const (); + StorageDead(_9); + StorageDead(_8); + StorageDead(_7); + StorageDead(_6); + goto -> bb10; + } + + bb9: { + _0 = const (); + goto -> bb10; + } + + bb10: { + return; + } + + bb11 (cleanup): { + resume; + } +} diff --git a/tests/mir-opt/building/match/array_len.slice_len.built.after.panic-unwind.mir b/tests/mir-opt/building/match/array_len.slice_len.built.after.panic-unwind.mir new file mode 100644 index 000000000000..d73f5a1b6f71 --- /dev/null +++ b/tests/mir-opt/building/match/array_len.slice_len.built.after.panic-unwind.mir @@ -0,0 +1,115 @@ +// MIR for `slice_len` after built + +fn slice_len(_1: &[T]) -> () { + debug x => _1; + let mut _0: (); + let mut _2: usize; + let mut _3: usize; + let mut _4: usize; + let mut _5: bool; + let _10: (); + let mut _11: &T; + let _12: (); + let mut _13: &T; + let _14: (); + let mut _15: &[T]; + let _16: (); + let mut _17: &T; + scope 1 { + debug a => _6; + debug b => _7; + debug rest => _8; + debug e => _9; + let _6: &T; + let _7: &T; + let _8: &[T]; + let _9: &T; + } + + bb0: { + PlaceMention(_1); + _3 = PtrMetadata(copy _1); + _2 = move _3; + _4 = const 3_usize; + _5 = Ge(move _2, move _4); + switchInt(move _5) -> [0: bb1, otherwise: bb2]; + } + + bb1: { + goto -> bb9; + } + + bb2: { + falseEdge -> [real: bb4, imaginary: bb1]; + } + + bb3: { + goto -> bb1; + } + + bb4: { + StorageLive(_6); + _6 = &(*_1)[0 of 3]; + StorageLive(_7); + _7 = &(*_1)[1 of 3]; + StorageLive(_8); + _8 = &(*_1)[2:-1]; + StorageLive(_9); + _9 = &(*_1)[-1 of 3]; + StorageLive(_10); + StorageLive(_11); + _11 = copy _6; + _10 = opaque::<&T>(move _11) -> [return: bb5, unwind: bb11]; + } + + bb5: { + StorageDead(_11); + StorageDead(_10); + StorageLive(_12); + StorageLive(_13); + _13 = copy _7; + _12 = opaque::<&T>(move _13) -> [return: bb6, unwind: bb11]; + } + + bb6: { + StorageDead(_13); + StorageDead(_12); + StorageLive(_14); + StorageLive(_15); + _15 = copy _8; + _14 = opaque::<&[T]>(move _15) -> [return: bb7, unwind: bb11]; + } + + bb7: { + StorageDead(_15); + StorageDead(_14); + StorageLive(_16); + StorageLive(_17); + _17 = copy _9; + _16 = opaque::<&T>(move _17) -> [return: bb8, unwind: bb11]; + } + + bb8: { + StorageDead(_17); + StorageDead(_16); + _0 = const (); + StorageDead(_9); + StorageDead(_8); + StorageDead(_7); + StorageDead(_6); + goto -> bb10; + } + + bb9: { + _0 = const (); + goto -> bb10; + } + + bb10: { + return; + } + + bb11 (cleanup): { + resume; + } +} diff --git a/tests/mir-opt/instsimplify/combine_array_len.normN.InstSimplify-after-simplifycfg.panic-abort.diff b/tests/mir-opt/instsimplify/combine_array_len.normN.InstSimplify-after-simplifycfg.panic-abort.diff new file mode 100644 index 000000000000..e9cb2c581772 --- /dev/null +++ b/tests/mir-opt/instsimplify/combine_array_len.normN.InstSimplify-after-simplifycfg.panic-abort.diff @@ -0,0 +1,71 @@ +- // MIR for `normN` before InstSimplify-after-simplifycfg ++ // MIR for `normN` after InstSimplify-after-simplifycfg + + fn normN(_1: [f32; N]) -> f32 { + debug x => _1; + let mut _0: f32; + let _2: f32; + let _3: usize; + let mut _4: bool; + let _6: usize; + let mut _7: bool; + let mut _8: f32; + let mut _9: f32; + let mut _10: f32; + let mut _11: f32; + let mut _12: f32; + let mut _13: f32; + scope 1 { + debug a => _2; + let _5: f32; + scope 2 { + debug b => _5; + } + } + + bb0: { + StorageLive(_2); + StorageLive(_3); + _3 = const 0_usize; + _4 = Lt(copy _3, const N); + assert(move _4, "index out of bounds: the length is {} but the index is {}", const N, copy _3) -> [success: bb1, unwind unreachable]; + } + + bb1: { + _2 = copy _1[_3]; + StorageDead(_3); + StorageLive(_5); + StorageLive(_6); + _6 = const 1_usize; + _7 = Lt(copy _6, const N); + assert(move _7, "index out of bounds: the length is {} but the index is {}", const N, copy _6) -> [success: bb2, unwind unreachable]; + } + + bb2: { + _5 = copy _1[_6]; + StorageDead(_6); + StorageLive(_8); + StorageLive(_9); + _9 = copy _2; + StorageLive(_10); + _10 = copy _2; + _8 = Mul(move _9, move _10); + StorageDead(_10); + StorageDead(_9); + StorageLive(_11); + StorageLive(_12); + _12 = copy _5; + StorageLive(_13); + _13 = copy _5; + _11 = Mul(move _12, move _13); + StorageDead(_13); + StorageDead(_12); + _0 = Add(move _8, move _11); + StorageDead(_11); + StorageDead(_8); + StorageDead(_5); + StorageDead(_2); + return; + } + } + diff --git a/tests/mir-opt/instsimplify/combine_array_len.normN.InstSimplify-after-simplifycfg.panic-unwind.diff b/tests/mir-opt/instsimplify/combine_array_len.normN.InstSimplify-after-simplifycfg.panic-unwind.diff new file mode 100644 index 000000000000..0ddc70f2003e --- /dev/null +++ b/tests/mir-opt/instsimplify/combine_array_len.normN.InstSimplify-after-simplifycfg.panic-unwind.diff @@ -0,0 +1,71 @@ +- // MIR for `normN` before InstSimplify-after-simplifycfg ++ // MIR for `normN` after InstSimplify-after-simplifycfg + + fn normN(_1: [f32; N]) -> f32 { + debug x => _1; + let mut _0: f32; + let _2: f32; + let _3: usize; + let mut _4: bool; + let _6: usize; + let mut _7: bool; + let mut _8: f32; + let mut _9: f32; + let mut _10: f32; + let mut _11: f32; + let mut _12: f32; + let mut _13: f32; + scope 1 { + debug a => _2; + let _5: f32; + scope 2 { + debug b => _5; + } + } + + bb0: { + StorageLive(_2); + StorageLive(_3); + _3 = const 0_usize; + _4 = Lt(copy _3, const N); + assert(move _4, "index out of bounds: the length is {} but the index is {}", const N, copy _3) -> [success: bb1, unwind continue]; + } + + bb1: { + _2 = copy _1[_3]; + StorageDead(_3); + StorageLive(_5); + StorageLive(_6); + _6 = const 1_usize; + _7 = Lt(copy _6, const N); + assert(move _7, "index out of bounds: the length is {} but the index is {}", const N, copy _6) -> [success: bb2, unwind continue]; + } + + bb2: { + _5 = copy _1[_6]; + StorageDead(_6); + StorageLive(_8); + StorageLive(_9); + _9 = copy _2; + StorageLive(_10); + _10 = copy _2; + _8 = Mul(move _9, move _10); + StorageDead(_10); + StorageDead(_9); + StorageLive(_11); + StorageLive(_12); + _12 = copy _5; + StorageLive(_13); + _13 = copy _5; + _11 = Mul(move _12, move _13); + StorageDead(_13); + StorageDead(_12); + _0 = Add(move _8, move _11); + StorageDead(_11); + StorageDead(_8); + StorageDead(_5); + StorageDead(_2); + return; + } + } + diff --git a/tests/mir-opt/instsimplify/combine_array_len.rs b/tests/mir-opt/instsimplify/combine_array_len.rs index 91f43f75689d..1c4d42d3bbe8 100644 --- a/tests/mir-opt/instsimplify/combine_array_len.rs +++ b/tests/mir-opt/instsimplify/combine_array_len.rs @@ -4,7 +4,16 @@ // EMIT_MIR combine_array_len.norm2.InstSimplify-after-simplifycfg.diff fn norm2(x: [f32; 2]) -> f32 { // CHECK-LABEL: fn norm2( - // CHECK-NOT: Len( + // CHECK-NOT: PtrMetadata( + let a = x[0]; + let b = x[1]; + a * a + b * b +} + +// EMIT_MIR combine_array_len.normN.InstSimplify-after-simplifycfg.diff +fn normN(x: [f32; N]) -> f32 { + // CHECK-LABEL: fn normN( + // CHECK-NOT: PtrMetadata( let a = x[0]; let b = x[1]; a * a + b * b @@ -12,4 +21,5 @@ fn norm2(x: [f32; 2]) -> f32 { fn main() { assert_eq!(norm2([3.0, 4.0]), 5.0 * 5.0); + assert_eq!(normN([3.0, 4.0]), 5.0 * 5.0); } From 40d879a47f940a8a6219ded487ce1298ecafe271 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sun, 14 Sep 2025 19:08:16 +0000 Subject: [PATCH 577/682] Add test. --- compiler/rustc_middle/src/mir/mod.rs | 2 +- compiler/rustc_middle/src/mir/query.rs | 2 +- ...await.b-{closure#0}.coroutine_resume.0.mir | 12 +- ....main-{closure#0}.StateTransform.after.mir | 208 ++++++++++++++++++ ....main-{closure#1}.StateTransform.after.mir | 208 ++++++++++++++++++ tests/mir-opt/building/coroutine.rs | 26 +++ ...ny.main-{closure#0}.coroutine_resume.0.mir | 6 +- 7 files changed, 453 insertions(+), 11 deletions(-) create mode 100644 tests/mir-opt/building/coroutine.main-{closure#0}.StateTransform.after.mir create mode 100644 tests/mir-opt/building/coroutine.main-{closure#1}.StateTransform.after.mir create mode 100644 tests/mir-opt/building/coroutine.rs diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index da2245b12d2c..28142382b130 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -471,7 +471,7 @@ impl<'tcx> Body<'tcx> { /// Returns an iterator over all function arguments. #[inline] - pub fn args_iter(&self) -> impl Iterator + ExactSizeIterator { + pub fn args_iter(&self) -> impl Iterator + ExactSizeIterator + use<> { (1..self.arg_count + 1).map(Local::new) } diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index 466b9c7a3c23..a509c40c89cd 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -17,7 +17,7 @@ use crate::ty::{self, CoroutineArgsExt, OpaqueHiddenType, Ty}; rustc_index::newtype_index! { #[derive(HashStable)] #[encodable] - #[debug_format = "_{}"] + #[debug_format = "_s{}"] pub struct CoroutineSavedLocal {} } diff --git a/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir b/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir index 9bff257e0639..945d6299e437 100644 --- a/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir +++ b/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir @@ -1,7 +1,7 @@ // MIR for `b::{closure#0}` 0 coroutine_resume /* coroutine_layout = CoroutineLayout { field_tys: { - _0: CoroutineSavedTy { + _s0: CoroutineSavedTy { ty: Coroutine( DefId(0:5 ~ async_await[ccf8]::a::{closure#0}), [ @@ -18,7 +18,7 @@ }, ignore_for_traits: false, }, - _1: CoroutineSavedTy { + _s1: CoroutineSavedTy { ty: Coroutine( DefId(0:5 ~ async_await[ccf8]::a::{closure#0}), [ @@ -40,12 +40,12 @@ Unresumed(0): [], Returned (1): [], Panicked (2): [], - Suspend0 (3): [_0], - Suspend1 (4): [_1], + Suspend0 (3): [_s0], + Suspend1 (4): [_s1], }, storage_conflicts: BitMatrix(2x2) { - (_0, _0), - (_1, _1), + (_s0, _s0), + (_s1, _s1), }, } */ diff --git a/tests/mir-opt/building/coroutine.main-{closure#0}.StateTransform.after.mir b/tests/mir-opt/building/coroutine.main-{closure#0}.StateTransform.after.mir new file mode 100644 index 000000000000..349cfa89838d --- /dev/null +++ b/tests/mir-opt/building/coroutine.main-{closure#0}.StateTransform.after.mir @@ -0,0 +1,208 @@ +// MIR for `main::{closure#0}` after StateTransform +/* coroutine_layout = CoroutineLayout { + field_tys: { + _s0: CoroutineSavedTy { + ty: std::string::String, + source_info: SourceInfo { + span: $DIR/coroutine.rs:17:32: 17:35 (#0), + scope: scope[0], + }, + ignore_for_traits: false, + }, + }, + variant_fields: { + Unresumed(0): [], + Returned (1): [], + Panicked (2): [], + Suspend0 (3): [_s0], + Suspend1 (4): [_s0], + }, + storage_conflicts: BitMatrix(1x1) { + (_s0, _s0), + }, +} */ + +fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}>, _2: String) -> CoroutineState<(&str, String, &Location<'_>), ()> { + debug arg => (((*(_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44})) as variant#4).0: std::string::String); + let mut _0: std::ops::CoroutineState<(&str, std::string::String, &std::panic::Location<'_>), ()>; + let _3: std::string::String; + let mut _4: (&str, std::string::String, &std::panic::Location<'_>); + let mut _5: std::string::String; + let mut _6: &std::string::String; + let mut _7: &std::panic::Location<'_>; + let _8: std::string::String; + let mut _9: (&str, std::string::String, &std::panic::Location<'_>); + let mut _10: &str; + let _11: &str; + let mut _12: std::string::String; + let mut _13: &std::string::String; + let mut _14: &std::panic::Location<'_>; + let _15: &std::panic::Location<'_>; + let mut _16: (); + let _17: std::string::String; + let mut _18: u32; + let mut _19: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}; + let mut _20: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}; + let mut _21: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}; + let mut _22: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}; + let mut _23: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}; + let mut _24: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}; + let mut _25: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}; + let mut _26: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}; + + bb0: { + _19 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}); + _18 = discriminant((*_19)); + switchInt(move _18) -> [0: bb1, 1: bb19, 3: bb17, 4: bb18, otherwise: bb20]; + } + + bb1: { + _20 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}); + (((*_20) as variant#4).0: std::string::String) = move _2; + StorageLive(_3); + StorageLive(_4); + StorageLive(_5); + StorageLive(_6); + _21 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}); + _6 = &(((*_21) as variant#4).0: std::string::String); + _5 = ::clone(move _6) -> [return: bb2, unwind unreachable]; + } + + bb2: { + StorageDead(_6); + StorageLive(_7); + _7 = Location::<'_>::caller() -> [return: bb3, unwind unreachable]; + } + + bb3: { + _4 = (const "first", move _5, move _7); + StorageDead(_7); + goto -> bb4; + } + + bb4: { + StorageDead(_5); + _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Yielded(move _4); + StorageDead(_3); + StorageDead(_4); + _22 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}); + discriminant((*_22)) = 3; + return; + } + + bb5: { + goto -> bb6; + } + + bb6: { + StorageDead(_4); + drop(_3) -> [return: bb7, unwind unreachable]; + } + + bb7: { + StorageDead(_3); + StorageLive(_8); + StorageLive(_9); + StorageLive(_10); + StorageLive(_11); + _11 = const "second"; + _10 = &(*_11); + StorageLive(_12); + StorageLive(_13); + _23 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}); + _13 = &(((*_23) as variant#4).0: std::string::String); + _12 = ::clone(move _13) -> [return: bb8, unwind unreachable]; + } + + bb8: { + StorageDead(_13); + StorageLive(_14); + StorageLive(_15); + _15 = Location::<'_>::caller() -> [return: bb9, unwind unreachable]; + } + + bb9: { + _14 = &(*_15); + _9 = (move _10, move _12, move _14); + StorageDead(_14); + goto -> bb10; + } + + bb10: { + StorageDead(_12); + StorageDead(_10); + _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Yielded(move _9); + StorageDead(_8); + StorageDead(_9); + StorageDead(_11); + StorageDead(_15); + _24 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}); + discriminant((*_24)) = 4; + return; + } + + bb11: { + goto -> bb12; + } + + bb12: { + StorageDead(_9); + drop(_8) -> [return: bb13, unwind unreachable]; + } + + bb13: { + StorageDead(_15); + StorageDead(_11); + StorageDead(_8); + _16 = const (); + _25 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}); + drop((((*_25) as variant#4).0: std::string::String)) -> [return: bb14, unwind unreachable]; + } + + bb14: { + goto -> bb16; + } + + bb15: { + _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Complete(move _16); + _26 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}); + discriminant((*_26)) = 1; + return; + } + + bb16: { + goto -> bb15; + } + + bb17: { + StorageLive(_3); + StorageLive(_4); + _3 = move _2; + goto -> bb5; + } + + bb18: { + StorageLive(_8); + StorageLive(_9); + StorageLive(_11); + StorageLive(_15); + _8 = move _2; + goto -> bb11; + } + + bb19: { + assert(const false, "coroutine resumed after completion") -> [success: bb19, unwind unreachable]; + } + + bb20: { + unreachable; + } +} + +ALLOC0 (size: 6, align: 1) { + 73 65 63 6f 6e 64 │ second +} + +ALLOC1 (size: 5, align: 1) { + 66 69 72 73 74 │ first +} diff --git a/tests/mir-opt/building/coroutine.main-{closure#1}.StateTransform.after.mir b/tests/mir-opt/building/coroutine.main-{closure#1}.StateTransform.after.mir new file mode 100644 index 000000000000..dd8fb4189636 --- /dev/null +++ b/tests/mir-opt/building/coroutine.main-{closure#1}.StateTransform.after.mir @@ -0,0 +1,208 @@ +// MIR for `main::{closure#1}` after StateTransform +/* coroutine_layout = CoroutineLayout { + field_tys: { + _s0: CoroutineSavedTy { + ty: std::string::String, + source_info: SourceInfo { + span: $DIR/coroutine.rs:22:54: 22:57 (#0), + scope: scope[0], + }, + ignore_for_traits: false, + }, + }, + variant_fields: { + Unresumed(0): [], + Returned (1): [], + Panicked (2): [], + Suspend0 (3): [_s0], + Suspend1 (4): [_s0], + }, + storage_conflicts: BitMatrix(1x1) { + (_s0, _s0), + }, +} */ + +fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}>, _2: String) -> CoroutineState<(&str, String, &Location<'_>), ()> { + debug arg => (((*(_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66})) as variant#4).0: std::string::String); + let mut _0: std::ops::CoroutineState<(&str, std::string::String, &std::panic::Location<'_>), ()>; + let _3: std::string::String; + let mut _4: (&str, std::string::String, &std::panic::Location<'_>); + let mut _5: std::string::String; + let mut _6: &std::string::String; + let mut _7: &std::panic::Location<'_>; + let _8: std::string::String; + let mut _9: (&str, std::string::String, &std::panic::Location<'_>); + let mut _10: &str; + let _11: &str; + let mut _12: std::string::String; + let mut _13: &std::string::String; + let mut _14: &std::panic::Location<'_>; + let _15: &std::panic::Location<'_>; + let mut _16: (); + let _17: std::string::String; + let mut _18: u32; + let mut _19: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}; + let mut _20: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}; + let mut _21: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}; + let mut _22: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}; + let mut _23: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}; + let mut _24: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}; + let mut _25: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}; + let mut _26: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}; + + bb0: { + _19 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}); + _18 = discriminant((*_19)); + switchInt(move _18) -> [0: bb1, 1: bb19, 3: bb17, 4: bb18, otherwise: bb20]; + } + + bb1: { + _20 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}); + (((*_20) as variant#4).0: std::string::String) = move _2; + StorageLive(_3); + StorageLive(_4); + StorageLive(_5); + StorageLive(_6); + _21 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}); + _6 = &(((*_21) as variant#4).0: std::string::String); + _5 = ::clone(move _6) -> [return: bb2, unwind unreachable]; + } + + bb2: { + StorageDead(_6); + StorageLive(_7); + _7 = Location::<'_>::caller() -> [return: bb3, unwind unreachable]; + } + + bb3: { + _4 = (const "first", move _5, move _7); + StorageDead(_7); + goto -> bb4; + } + + bb4: { + StorageDead(_5); + _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Yielded(move _4); + StorageDead(_3); + StorageDead(_4); + _22 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}); + discriminant((*_22)) = 3; + return; + } + + bb5: { + goto -> bb6; + } + + bb6: { + StorageDead(_4); + drop(_3) -> [return: bb7, unwind unreachable]; + } + + bb7: { + StorageDead(_3); + StorageLive(_8); + StorageLive(_9); + StorageLive(_10); + StorageLive(_11); + _11 = const "second"; + _10 = &(*_11); + StorageLive(_12); + StorageLive(_13); + _23 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}); + _13 = &(((*_23) as variant#4).0: std::string::String); + _12 = ::clone(move _13) -> [return: bb8, unwind unreachable]; + } + + bb8: { + StorageDead(_13); + StorageLive(_14); + StorageLive(_15); + _15 = Location::<'_>::caller() -> [return: bb9, unwind unreachable]; + } + + bb9: { + _14 = &(*_15); + _9 = (move _10, move _12, move _14); + StorageDead(_14); + goto -> bb10; + } + + bb10: { + StorageDead(_12); + StorageDead(_10); + _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Yielded(move _9); + StorageDead(_8); + StorageDead(_9); + StorageDead(_11); + StorageDead(_15); + _24 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}); + discriminant((*_24)) = 4; + return; + } + + bb11: { + goto -> bb12; + } + + bb12: { + StorageDead(_9); + drop(_8) -> [return: bb13, unwind unreachable]; + } + + bb13: { + StorageDead(_15); + StorageDead(_11); + StorageDead(_8); + _16 = const (); + _25 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}); + drop((((*_25) as variant#4).0: std::string::String)) -> [return: bb14, unwind unreachable]; + } + + bb14: { + goto -> bb16; + } + + bb15: { + _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Complete(move _16); + _26 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}); + discriminant((*_26)) = 1; + return; + } + + bb16: { + goto -> bb15; + } + + bb17: { + StorageLive(_3); + StorageLive(_4); + _3 = move _2; + goto -> bb5; + } + + bb18: { + StorageLive(_8); + StorageLive(_9); + StorageLive(_11); + StorageLive(_15); + _8 = move _2; + goto -> bb11; + } + + bb19: { + assert(const false, "coroutine resumed after completion") -> [success: bb19, unwind unreachable]; + } + + bb20: { + unreachable; + } +} + +ALLOC0 (size: 6, align: 1) { + 73 65 63 6f 6e 64 │ second +} + +ALLOC1 (size: 5, align: 1) { + 66 69 72 73 74 │ first +} diff --git a/tests/mir-opt/building/coroutine.rs b/tests/mir-opt/building/coroutine.rs new file mode 100644 index 000000000000..4b3310f7dd8b --- /dev/null +++ b/tests/mir-opt/building/coroutine.rs @@ -0,0 +1,26 @@ +// skip-filecheck +//@ edition:2024 +//@ compile-flags: -Zmir-opt-level=0 -C panic=abort + +#![feature(stmt_expr_attributes)] +#![feature(closure_track_caller)] +#![feature(coroutine_trait)] +#![feature(coroutines)] + +use std::ops::{Coroutine, CoroutineState}; +use std::pin::Pin; +use std::panic::Location; + +// EMIT_MIR coroutine.main-{closure#0}.StateTransform.after.mir +// EMIT_MIR coroutine.main-{closure#1}.StateTransform.after.mir +fn main() { + let simple = #[coroutine] |arg: String| { + yield ("first", arg.clone(), Location::caller()); + yield ("second", arg.clone(), Location::caller()); + }; + + let track_caller = #[track_caller] #[coroutine] |arg: String| { + yield ("first", arg.clone(), Location::caller()); + yield ("second", arg.clone(), Location::caller()); + }; +} diff --git a/tests/mir-opt/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir b/tests/mir-opt/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir index f8b3f68d21e6..83159b5055ac 100644 --- a/tests/mir-opt/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir +++ b/tests/mir-opt/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir @@ -1,7 +1,7 @@ // MIR for `main::{closure#0}` 0 coroutine_resume /* coroutine_layout = CoroutineLayout { field_tys: { - _0: CoroutineSavedTy { + _s0: CoroutineSavedTy { ty: HasDrop, source_info: SourceInfo { span: $DIR/coroutine_tiny.rs:22:13: 22:15 (#0), @@ -14,10 +14,10 @@ Unresumed(0): [], Returned (1): [], Panicked (2): [], - Suspend0 (3): [_0], + Suspend0 (3): [_s0], }, storage_conflicts: BitMatrix(1x1) { - (_0, _0), + (_s0, _s0), }, } */ From fe3a784ef23603180efef1501d80b265663fdec2 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sun, 14 Sep 2025 15:15:49 +0000 Subject: [PATCH 578/682] Do not renumber resume local. --- compiler/rustc_mir_transform/src/coroutine.rs | 47 +++---- ...#0}.coroutine_drop_async.0.panic-abort.mir | 9 +- ...0}.coroutine_drop_async.0.panic-unwind.mir | 9 +- ...await.a-{closure#0}.coroutine_resume.0.mir | 10 +- ...await.b-{closure#0}.coroutine_resume.0.mir | 18 ++- ....main-{closure#0}.StateTransform.after.mir | 59 +++++---- ....main-{closure#1}.StateTransform.after.mir | 59 +++++---- tests/mir-opt/building/coroutine.rs | 9 +- ...losure#0}.coroutine_drop.0.panic-abort.mir | 7 +- ...osure#0}.coroutine_drop.0.panic-unwind.mir | 7 +- ...ny.main-{closure#0}.coroutine_resume.0.mir | 10 +- ...y.run2-{closure#0}.Inline.panic-abort.diff | 114 ++++++++-------- ....run2-{closure#0}.Inline.panic-unwind.diff | 122 +++++++++--------- 13 files changed, 228 insertions(+), 252 deletions(-) diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index 4603c695dedd..08316aaed3b4 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -1340,14 +1340,13 @@ fn create_cases<'tcx>( } } - if operation == Operation::Resume { + if operation == Operation::Resume && point.resume_arg != CTX_ARG.into() { // Move the resume argument to the destination place of the `Yield` terminator - let resume_arg = CTX_ARG; statements.push(Statement::new( source_info, StatementKind::Assign(Box::new(( point.resume_arg, - Rvalue::Use(Operand::Move(resume_arg.into())), + Rvalue::Use(Operand::Move(CTX_ARG.into())), ))), )); } @@ -1439,7 +1438,10 @@ fn check_field_tys_sized<'tcx>( } impl<'tcx> crate::MirPass<'tcx> for StateTransform { + #[instrument(level = "debug", skip(self, tcx, body), ret)] fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + debug!(def_id = ?body.source.def_id()); + let Some(old_yield_ty) = body.yield_ty() else { // This only applies to coroutines return; @@ -1518,31 +1520,7 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform { cleanup_async_drops(body); } - // We also replace the resume argument and insert an `Assign`. - // This is needed because the resume argument `_2` might be live across a `yield`, in which - // case there is no `Assign` to it that the transform can turn into a store to the coroutine - // state. After the yield the slot in the coroutine state would then be uninitialized. - let resume_local = CTX_ARG; - let resume_ty = body.local_decls[resume_local].ty; - let old_resume_local = replace_local(resume_local, resume_ty, body, tcx); - - // When first entering the coroutine, move the resume argument into its old local - // (which is now a generator interior). - let source_info = SourceInfo::outermost(body.span); - let stmts = &mut body.basic_blocks_mut()[START_BLOCK].statements; - stmts.insert( - 0, - Statement::new( - source_info, - StatementKind::Assign(Box::new(( - old_resume_local.into(), - Rvalue::Use(Operand::Move(resume_local.into())), - ))), - ), - ); - let always_live_locals = always_storage_live_locals(body); - let movable = coroutine_kind.movability() == hir::Movability::Movable; let liveness_info = locals_live_across_suspend_points(tcx, body, &always_live_locals, movable); @@ -1583,6 +1561,21 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform { }; transform.visit_body(body); + // MIR parameters are not explicitly assigned-to when entering the MIR body. + // If we want to save their values inside the coroutine state, we need to do so explicitly. + let source_info = SourceInfo::outermost(body.span); + let args_iter = body.args_iter(); + body.basic_blocks.as_mut()[START_BLOCK].statements.splice( + 0..0, + args_iter.filter_map(|local| { + let (ty, variant_index, idx) = transform.remap[local]?; + let lhs = transform.make_field(variant_index, idx, ty); + let rhs = Rvalue::Use(Operand::Move(local.into())); + let assign = StatementKind::Assign(Box::new((lhs, rhs))); + Some(Statement::new(source_info, assign)) + }), + ); + // Update our MIR struct to reflect the changes we've made body.arg_count = 2; // self, resume arg body.spread_arg = None; diff --git a/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-abort.mir b/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-abort.mir index 347e4119cd0e..050aac7c3ff0 100644 --- a/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-abort.mir +++ b/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-abort.mir @@ -1,7 +1,7 @@ // MIR for `a::{closure#0}` 0 coroutine_drop_async fn a::{closure#0}(_1: Pin<&mut {async fn body of a()}>, _2: &mut Context<'_>) -> Poll<()> { - debug _task_context => _19; + debug _task_context => _2; debug x => ((*(_1.0: &mut {async fn body of a()})).0: T); let mut _0: std::task::Poll<()>; let _3: T; @@ -20,15 +20,14 @@ fn a::{closure#0}(_1: Pin<&mut {async fn body of a()}>, _2: &mut Context<'_>) let mut _16: &mut impl std::future::Future; let mut _17: std::pin::Pin<&mut impl std::future::Future>; let mut _18: isize; - let mut _19: &mut std::task::Context<'_>; - let mut _20: u32; + let mut _19: u32; scope 1 { debug x => (((*(_1.0: &mut {async fn body of a()})) as variant#4).0: T); } bb0: { - _20 = discriminant((*(_1.0: &mut {async fn body of a()}))); - switchInt(move _20) -> [0: bb9, 3: bb12, 4: bb13, otherwise: bb14]; + _19 = discriminant((*(_1.0: &mut {async fn body of a()}))); + switchInt(move _19) -> [0: bb9, 3: bb12, 4: bb13, otherwise: bb14]; } bb1: { diff --git a/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-unwind.mir b/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-unwind.mir index b1cf5373f919..796e95ff3d82 100644 --- a/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-unwind.mir +++ b/tests/mir-opt/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-unwind.mir @@ -1,7 +1,7 @@ // MIR for `a::{closure#0}` 0 coroutine_drop_async fn a::{closure#0}(_1: Pin<&mut {async fn body of a()}>, _2: &mut Context<'_>) -> Poll<()> { - debug _task_context => _19; + debug _task_context => _2; debug x => ((*(_1.0: &mut {async fn body of a()})).0: T); let mut _0: std::task::Poll<()>; let _3: T; @@ -20,15 +20,14 @@ fn a::{closure#0}(_1: Pin<&mut {async fn body of a()}>, _2: &mut Context<'_>) let mut _16: &mut impl std::future::Future; let mut _17: std::pin::Pin<&mut impl std::future::Future>; let mut _18: isize; - let mut _19: &mut std::task::Context<'_>; - let mut _20: u32; + let mut _19: u32; scope 1 { debug x => (((*(_1.0: &mut {async fn body of a()})) as variant#4).0: T); } bb0: { - _20 = discriminant((*(_1.0: &mut {async fn body of a()}))); - switchInt(move _20) -> [0: bb12, 2: bb18, 3: bb16, 4: bb17, otherwise: bb19]; + _19 = discriminant((*(_1.0: &mut {async fn body of a()}))); + switchInt(move _19) -> [0: bb12, 2: bb18, 3: bb16, 4: bb17, otherwise: bb19]; } bb1: { diff --git a/tests/mir-opt/building/async_await.a-{closure#0}.coroutine_resume.0.mir b/tests/mir-opt/building/async_await.a-{closure#0}.coroutine_resume.0.mir index 7480324b1779..2e2876cb3fcf 100644 --- a/tests/mir-opt/building/async_await.a-{closure#0}.coroutine_resume.0.mir +++ b/tests/mir-opt/building/async_await.a-{closure#0}.coroutine_resume.0.mir @@ -10,19 +10,17 @@ } */ fn a::{closure#0}(_1: Pin<&mut {async fn body of a()}>, _2: &mut Context<'_>) -> Poll<()> { - debug _task_context => _4; + debug _task_context => _2; let mut _0: std::task::Poll<()>; let mut _3: (); - let mut _4: &mut std::task::Context<'_>; - let mut _5: u32; + let mut _4: u32; bb0: { - _5 = discriminant((*(_1.0: &mut {async fn body of a()}))); - switchInt(move _5) -> [0: bb1, 1: bb4, otherwise: bb5]; + _4 = discriminant((*(_1.0: &mut {async fn body of a()}))); + switchInt(move _4) -> [0: bb1, 1: bb4, otherwise: bb5]; } bb1: { - _4 = move _2; _3 = const (); goto -> bb3; } diff --git a/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir b/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir index 945d6299e437..20fc4112ef28 100644 --- a/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir +++ b/tests/mir-opt/building/async_await.b-{closure#0}.coroutine_resume.0.mir @@ -50,7 +50,7 @@ } */ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> Poll<()> { - debug _task_context => _38; + debug _task_context => _2; let mut _0: std::task::Poll<()>; let _3: (); let mut _4: {async fn body of a()}; @@ -85,8 +85,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> let mut _35: &mut std::task::Context<'_>; let mut _36: (); let mut _37: (); - let mut _38: &mut std::task::Context<'_>; - let mut _39: u32; + let mut _38: u32; scope 1 { debug __awaitee => (((*(_1.0: &mut {async fn body of b()})) as variant#3).0: {async fn body of a()}); let _17: (); @@ -103,12 +102,11 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> } bb0: { - _39 = discriminant((*(_1.0: &mut {async fn body of b()}))); - switchInt(move _39) -> [0: bb1, 1: bb29, 3: bb27, 4: bb28, otherwise: bb8]; + _38 = discriminant((*(_1.0: &mut {async fn body of b()}))); + switchInt(move _38) -> [0: bb1, 1: bb29, 3: bb27, 4: bb28, otherwise: bb8]; } bb1: { - _38 = move _2; StorageLive(_3); StorageLive(_4); StorageLive(_5); @@ -143,7 +141,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> StorageLive(_13); StorageLive(_14); StorageLive(_15); - _15 = copy _38; + _15 = copy _2; _14 = move _15; goto -> bb6; } @@ -198,7 +196,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> bb11: { StorageDead(_20); - _38 = move _19; + _2 = move _19; StorageDead(_19); _7 = const (); goto -> bb4; @@ -245,7 +243,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> StorageLive(_29); StorageLive(_30); StorageLive(_31); - _31 = copy _38; + _31 = copy _2; _30 = move _31; goto -> bb18; } @@ -295,7 +293,7 @@ fn b::{closure#0}(_1: Pin<&mut {async fn body of b()}>, _2: &mut Context<'_>) -> bb22: { StorageDead(_36); - _38 = move _35; + _2 = move _35; StorageDead(_35); _7 = const (); goto -> bb16; diff --git a/tests/mir-opt/building/coroutine.main-{closure#0}.StateTransform.after.mir b/tests/mir-opt/building/coroutine.main-{closure#0}.StateTransform.after.mir index 349cfa89838d..d8fdb446135b 100644 --- a/tests/mir-opt/building/coroutine.main-{closure#0}.StateTransform.after.mir +++ b/tests/mir-opt/building/coroutine.main-{closure#0}.StateTransform.after.mir @@ -4,7 +4,7 @@ _s0: CoroutineSavedTy { ty: std::string::String, source_info: SourceInfo { - span: $DIR/coroutine.rs:17:32: 17:35 (#0), + span: $DIR/coroutine.rs:18:6: 18:9 (#0), scope: scope[0], }, ignore_for_traits: false, @@ -22,8 +22,8 @@ }, } */ -fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}>, _2: String) -> CoroutineState<(&str, String, &Location<'_>), ()> { - debug arg => (((*(_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44})) as variant#4).0: std::string::String); +fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}>, _2: String) -> CoroutineState<(&str, String, &Location<'_>), ()> { + debug arg => (((*(_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18})) as variant#4).0: std::string::String); let mut _0: std::ops::CoroutineState<(&str, std::string::String, &std::panic::Location<'_>), ()>; let _3: std::string::String; let mut _4: (&str, std::string::String, &std::panic::Location<'_>); @@ -39,32 +39,31 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}>, _ let mut _14: &std::panic::Location<'_>; let _15: &std::panic::Location<'_>; let mut _16: (); - let _17: std::string::String; - let mut _18: u32; - let mut _19: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}; - let mut _20: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}; - let mut _21: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}; - let mut _22: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}; - let mut _23: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}; - let mut _24: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}; - let mut _25: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}; - let mut _26: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}; + let mut _17: u32; + let mut _18: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}; + let mut _19: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}; + let mut _20: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}; + let mut _21: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}; + let mut _22: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}; + let mut _23: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}; + let mut _24: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}; + let mut _25: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}; bb0: { - _19 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}); - _18 = discriminant((*_19)); - switchInt(move _18) -> [0: bb1, 1: bb19, 3: bb17, 4: bb18, otherwise: bb20]; + _18 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}); + _17 = discriminant((*_18)); + switchInt(move _17) -> [0: bb1, 1: bb19, 3: bb17, 4: bb18, otherwise: bb20]; } bb1: { - _20 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}); - (((*_20) as variant#4).0: std::string::String) = move _2; + _19 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}); + (((*_19) as variant#4).0: std::string::String) = move _2; StorageLive(_3); StorageLive(_4); StorageLive(_5); StorageLive(_6); - _21 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}); - _6 = &(((*_21) as variant#4).0: std::string::String); + _20 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}); + _6 = &(((*_20) as variant#4).0: std::string::String); _5 = ::clone(move _6) -> [return: bb2, unwind unreachable]; } @@ -85,8 +84,8 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}>, _ _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Yielded(move _4); StorageDead(_3); StorageDead(_4); - _22 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}); - discriminant((*_22)) = 3; + _21 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}); + discriminant((*_21)) = 3; return; } @@ -109,8 +108,8 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}>, _ _10 = &(*_11); StorageLive(_12); StorageLive(_13); - _23 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}); - _13 = &(((*_23) as variant#4).0: std::string::String); + _22 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}); + _13 = &(((*_22) as variant#4).0: std::string::String); _12 = ::clone(move _13) -> [return: bb8, unwind unreachable]; } @@ -136,8 +135,8 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}>, _ StorageDead(_9); StorageDead(_11); StorageDead(_15); - _24 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}); - discriminant((*_24)) = 4; + _23 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}); + discriminant((*_23)) = 4; return; } @@ -155,8 +154,8 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}>, _ StorageDead(_11); StorageDead(_8); _16 = const (); - _25 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}); - drop((((*_25) as variant#4).0: std::string::String)) -> [return: bb14, unwind unreachable]; + _24 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}); + drop((((*_24) as variant#4).0: std::string::String)) -> [return: bb14, unwind unreachable]; } bb14: { @@ -165,8 +164,8 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}>, _ bb15: { _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Complete(move _16); - _26 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:17:31: 17:44}); - discriminant((*_26)) = 1; + _25 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:18:5: 18:18}); + discriminant((*_25)) = 1; return; } diff --git a/tests/mir-opt/building/coroutine.main-{closure#1}.StateTransform.after.mir b/tests/mir-opt/building/coroutine.main-{closure#1}.StateTransform.after.mir index dd8fb4189636..dd17afad656b 100644 --- a/tests/mir-opt/building/coroutine.main-{closure#1}.StateTransform.after.mir +++ b/tests/mir-opt/building/coroutine.main-{closure#1}.StateTransform.after.mir @@ -4,7 +4,7 @@ _s0: CoroutineSavedTy { ty: std::string::String, source_info: SourceInfo { - span: $DIR/coroutine.rs:22:54: 22:57 (#0), + span: $DIR/coroutine.rs:25:6: 25:9 (#0), scope: scope[0], }, ignore_for_traits: false, @@ -22,8 +22,8 @@ }, } */ -fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}>, _2: String) -> CoroutineState<(&str, String, &Location<'_>), ()> { - debug arg => (((*(_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66})) as variant#4).0: std::string::String); +fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}>, _2: String) -> CoroutineState<(&str, String, &Location<'_>), ()> { + debug arg => (((*(_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18})) as variant#4).0: std::string::String); let mut _0: std::ops::CoroutineState<(&str, std::string::String, &std::panic::Location<'_>), ()>; let _3: std::string::String; let mut _4: (&str, std::string::String, &std::panic::Location<'_>); @@ -39,32 +39,31 @@ fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}>, _ let mut _14: &std::panic::Location<'_>; let _15: &std::panic::Location<'_>; let mut _16: (); - let _17: std::string::String; - let mut _18: u32; - let mut _19: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}; - let mut _20: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}; - let mut _21: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}; - let mut _22: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}; - let mut _23: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}; - let mut _24: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}; - let mut _25: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}; - let mut _26: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}; + let mut _17: u32; + let mut _18: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}; + let mut _19: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}; + let mut _20: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}; + let mut _21: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}; + let mut _22: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}; + let mut _23: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}; + let mut _24: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}; + let mut _25: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}; bb0: { - _19 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}); - _18 = discriminant((*_19)); - switchInt(move _18) -> [0: bb1, 1: bb19, 3: bb17, 4: bb18, otherwise: bb20]; + _18 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}); + _17 = discriminant((*_18)); + switchInt(move _17) -> [0: bb1, 1: bb19, 3: bb17, 4: bb18, otherwise: bb20]; } bb1: { - _20 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}); - (((*_20) as variant#4).0: std::string::String) = move _2; + _19 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}); + (((*_19) as variant#4).0: std::string::String) = move _2; StorageLive(_3); StorageLive(_4); StorageLive(_5); StorageLive(_6); - _21 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}); - _6 = &(((*_21) as variant#4).0: std::string::String); + _20 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}); + _6 = &(((*_20) as variant#4).0: std::string::String); _5 = ::clone(move _6) -> [return: bb2, unwind unreachable]; } @@ -85,8 +84,8 @@ fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}>, _ _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Yielded(move _4); StorageDead(_3); StorageDead(_4); - _22 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}); - discriminant((*_22)) = 3; + _21 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}); + discriminant((*_21)) = 3; return; } @@ -109,8 +108,8 @@ fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}>, _ _10 = &(*_11); StorageLive(_12); StorageLive(_13); - _23 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}); - _13 = &(((*_23) as variant#4).0: std::string::String); + _22 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}); + _13 = &(((*_22) as variant#4).0: std::string::String); _12 = ::clone(move _13) -> [return: bb8, unwind unreachable]; } @@ -136,8 +135,8 @@ fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}>, _ StorageDead(_9); StorageDead(_11); StorageDead(_15); - _24 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}); - discriminant((*_24)) = 4; + _23 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}); + discriminant((*_23)) = 4; return; } @@ -155,8 +154,8 @@ fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}>, _ StorageDead(_11); StorageDead(_8); _16 = const (); - _25 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}); - drop((((*_25) as variant#4).0: std::string::String)) -> [return: bb14, unwind unreachable]; + _24 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}); + drop((((*_24) as variant#4).0: std::string::String)) -> [return: bb14, unwind unreachable]; } bb14: { @@ -165,8 +164,8 @@ fn main::{closure#1}(_1: Pin<&mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}>, _ bb15: { _0 = CoroutineState::<(&str, String, &Location<'_>), ()>::Complete(move _16); - _26 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:22:53: 22:66}); - discriminant((*_26)) = 1; + _25 = deref_copy (_1.0: &mut {coroutine@$DIR/coroutine.rs:25:5: 25:18}); + discriminant((*_25)) = 1; return; } diff --git a/tests/mir-opt/building/coroutine.rs b/tests/mir-opt/building/coroutine.rs index 4b3310f7dd8b..6d50c4d90b1a 100644 --- a/tests/mir-opt/building/coroutine.rs +++ b/tests/mir-opt/building/coroutine.rs @@ -8,18 +8,21 @@ #![feature(coroutines)] use std::ops::{Coroutine, CoroutineState}; -use std::pin::Pin; use std::panic::Location; +use std::pin::Pin; // EMIT_MIR coroutine.main-{closure#0}.StateTransform.after.mir // EMIT_MIR coroutine.main-{closure#1}.StateTransform.after.mir fn main() { - let simple = #[coroutine] |arg: String| { + let simple = #[coroutine] + |arg: String| { yield ("first", arg.clone(), Location::caller()); yield ("second", arg.clone(), Location::caller()); }; - let track_caller = #[track_caller] #[coroutine] |arg: String| { + let track_caller = #[track_caller] + #[coroutine] + |arg: String| { yield ("first", arg.clone(), Location::caller()); yield ("second", arg.clone(), Location::caller()); }; diff --git a/tests/mir-opt/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-abort.mir b/tests/mir-opt/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-abort.mir index 7e033916fd34..33fbca7f77ed 100644 --- a/tests/mir-opt/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-abort.mir +++ b/tests/mir-opt/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-abort.mir @@ -7,15 +7,14 @@ fn main::{closure#0}(_1: *mut {coroutine@$DIR/coroutine_drop_cleanup.rs:12:5: 12 let _4: (); let mut _5: (); let mut _6: (); - let mut _7: (); - let mut _8: u32; + let mut _7: u32; scope 1 { debug _s => (((*_1) as variant#3).0: std::string::String); } bb0: { - _8 = discriminant((*_1)); - switchInt(move _8) -> [0: bb5, 3: bb8, otherwise: bb9]; + _7 = discriminant((*_1)); + switchInt(move _7) -> [0: bb5, 3: bb8, otherwise: bb9]; } bb1: { diff --git a/tests/mir-opt/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-unwind.mir b/tests/mir-opt/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-unwind.mir index 613ef2909b5e..69e7219af9ff 100644 --- a/tests/mir-opt/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-unwind.mir +++ b/tests/mir-opt/coroutine_drop_cleanup.main-{closure#0}.coroutine_drop.0.panic-unwind.mir @@ -7,15 +7,14 @@ fn main::{closure#0}(_1: *mut {coroutine@$DIR/coroutine_drop_cleanup.rs:12:5: 12 let _4: (); let mut _5: (); let mut _6: (); - let mut _7: (); - let mut _8: u32; + let mut _7: u32; scope 1 { debug _s => (((*_1) as variant#3).0: std::string::String); } bb0: { - _8 = discriminant((*_1)); - switchInt(move _8) -> [0: bb7, 3: bb10, otherwise: bb11]; + _7 = discriminant((*_1)); + switchInt(move _7) -> [0: bb7, 3: bb10, otherwise: bb11]; } bb1: { diff --git a/tests/mir-opt/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir b/tests/mir-opt/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir index 83159b5055ac..9905cc3e00f9 100644 --- a/tests/mir-opt/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir +++ b/tests/mir-opt/coroutine_tiny.main-{closure#0}.coroutine_resume.0.mir @@ -22,7 +22,7 @@ } */ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine_tiny.rs:21:5: 21:13}>, _2: u8) -> CoroutineState<(), ()> { - debug _x => _10; + debug _x => _2; let mut _0: std::ops::CoroutineState<(), ()>; let _3: HasDrop; let mut _4: !; @@ -31,19 +31,17 @@ fn main::{closure#0}(_1: Pin<&mut {coroutine@$DIR/coroutine_tiny.rs:21:5: 21:13} let mut _7: (); let _8: (); let mut _9: (); - let _10: u8; - let mut _11: u32; + let mut _10: u32; scope 1 { debug _d => (((*(_1.0: &mut {coroutine@$DIR/coroutine_tiny.rs:21:5: 21:13})) as variant#3).0: HasDrop); } bb0: { - _11 = discriminant((*(_1.0: &mut {coroutine@$DIR/coroutine_tiny.rs:21:5: 21:13}))); - switchInt(move _11) -> [0: bb1, 3: bb5, otherwise: bb6]; + _10 = discriminant((*(_1.0: &mut {coroutine@$DIR/coroutine_tiny.rs:21:5: 21:13}))); + switchInt(move _10) -> [0: bb1, 3: bb5, otherwise: bb6]; } bb1: { - _10 = move _2; nop; (((*(_1.0: &mut {coroutine@$DIR/coroutine_tiny.rs:21:5: 21:13})) as variant#3).0: HasDrop) = HasDrop; StorageLive(_4); diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff index 22e6ea722dda..2ae86e2eb8bb 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff @@ -39,8 +39,8 @@ + let mut _28: &mut std::task::Context<'_>; + let mut _29: (); + let mut _30: (); -+ let mut _31: &mut std::task::Context<'_>; -+ let mut _32: u32; ++ let mut _31: u32; ++ let mut _32: &mut {async fn body of ActionPermit<'_, T>::perform()}; + let mut _33: &mut {async fn body of ActionPermit<'_, T>::perform()}; + let mut _34: &mut {async fn body of ActionPermit<'_, T>::perform()}; + let mut _35: &mut {async fn body of ActionPermit<'_, T>::perform()}; @@ -48,7 +48,6 @@ + let mut _37: &mut {async fn body of ActionPermit<'_, T>::perform()}; + let mut _38: &mut {async fn body of ActionPermit<'_, T>::perform()}; + let mut _39: &mut {async fn body of ActionPermit<'_, T>::perform()}; -+ let mut _40: &mut {async fn body of ActionPermit<'_, T>::perform()}; + scope 7 { + let mut _15: std::future::Ready<()>; + scope 8 { @@ -58,14 +57,14 @@ + scope 12 (inlined Pin::<&mut std::future::Ready<()>>::new_unchecked) { + } + scope 13 (inlined as Future>::poll) { -+ let mut _42: (); -+ let mut _43: std::option::Option<()>; -+ let mut _44: &mut std::option::Option<()>; -+ let mut _45: &mut std::future::Ready<()>; -+ let mut _46: &mut std::pin::Pin<&mut std::future::Ready<()>>; ++ let mut _41: (); ++ let mut _42: std::option::Option<()>; ++ let mut _43: &mut std::option::Option<()>; ++ let mut _44: &mut std::future::Ready<()>; ++ let mut _45: &mut std::pin::Pin<&mut std::future::Ready<()>>; + scope 14 (inlined > as DerefMut>::deref_mut) { + scope 15 (inlined Pin::<&mut std::future::Ready<()>>::as_mut) { -+ let mut _47: &mut &mut std::future::Ready<()>; ++ let mut _46: &mut &mut std::future::Ready<()>; + scope 16 (inlined Pin::<&mut std::future::Ready<()>>::new_unchecked) { + } + scope 18 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) { @@ -75,22 +74,22 @@ + } + } + scope 19 (inlined Option::<()>::take) { -+ let mut _48: std::option::Option<()>; ++ let mut _47: std::option::Option<()>; + scope 20 (inlined std::mem::replace::>) { + scope 21 { + } + } + } + scope 22 (inlined #[track_caller] Option::<()>::expect) { -+ let mut _49: isize; -+ let mut _50: !; ++ let mut _48: isize; ++ let mut _49: !; + scope 23 { + } + } + } + } + scope 10 (inlined ready::<()>) { -+ let mut _41: std::option::Option<()>; ++ let mut _40: std::option::Option<()>; + } + scope 11 (inlined as IntoFuture>::into_future) { + } @@ -145,10 +144,9 @@ + StorageLive(_37); + StorageLive(_38); + StorageLive(_39); -+ StorageLive(_40); -+ _33 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ _32 = discriminant((*_33)); -+ switchInt(move _32) -> [0: bb3, 1: bb10, 3: bb9, otherwise: bb5]; ++ _32 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ _31 = discriminant((*_32)); ++ switchInt(move _31) -> [0: bb3, 1: bb10, 3: bb9, otherwise: bb5]; } - bb3: { @@ -158,7 +156,6 @@ + } + + bb2: { -+ StorageDead(_40); + StorageDead(_39); + StorageDead(_38); + StorageDead(_37); @@ -186,23 +183,22 @@ } + bb3: { -+ _31 = move _9; ++ _33 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); + _34 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ _35 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ (((*_34) as variant#3).0: ActionPermit<'_, T>) = move ((*_35).0: ActionPermit<'_, T>); ++ (((*_33) as variant#3).0: ActionPermit<'_, T>) = move ((*_34).0: ActionPermit<'_, T>); + StorageLive(_12); + StorageLive(_13); + StorageLive(_14); + _14 = (); -+ StorageLive(_41); -+ _41 = Option::<()>::Some(copy _14); -+ _13 = std::future::Ready::<()>(move _41); -+ StorageDead(_41); ++ StorageLive(_40); ++ _40 = Option::<()>::Some(copy _14); ++ _13 = std::future::Ready::<()>(move _40); ++ StorageDead(_40); + StorageDead(_14); + _12 = move _13; + StorageDead(_13); -+ _36 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ (((*_36) as variant#3).1: std::future::Ready<()>) = move _12; ++ _35 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ (((*_35) as variant#3).1: std::future::Ready<()>) = move _12; + goto -> bb4; + } + @@ -214,39 +210,39 @@ + StorageLive(_19); + StorageLive(_20); + StorageLive(_21); -+ _37 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ _21 = &mut (((*_37) as variant#3).1: std::future::Ready<()>); ++ _36 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ _21 = &mut (((*_36) as variant#3).1: std::future::Ready<()>); + _20 = &mut (*_21); + _19 = Pin::<&mut std::future::Ready<()>> { pointer: copy _20 }; + StorageDead(_20); + StorageLive(_22); + StorageLive(_23); + StorageLive(_24); -+ _24 = copy _31; ++ _24 = copy _9; + _23 = move _24; + _22 = &mut (*_23); + StorageDead(_24); ++ StorageLive(_44); + StorageLive(_45); -+ StorageLive(_46); -+ StorageLive(_50); ++ StorageLive(_49); ++ StorageLive(_41); + StorageLive(_42); + StorageLive(_43); -+ StorageLive(_44); -+ _46 = &mut _19; ++ _45 = &mut _19; ++ StorageLive(_46); ++ _46 = &mut (_19.0: &mut std::future::Ready<()>); ++ _44 = copy (_19.0: &mut std::future::Ready<()>); ++ StorageDead(_46); ++ _43 = &mut ((*_44).0: std::option::Option<()>); + StorageLive(_47); -+ _47 = &mut (_19.0: &mut std::future::Ready<()>); -+ _45 = copy (_19.0: &mut std::future::Ready<()>); ++ _47 = Option::<()>::None; ++ _42 = copy ((*_44).0: std::option::Option<()>); ++ ((*_44).0: std::option::Option<()>) = copy _47; + StorageDead(_47); -+ _44 = &mut ((*_45).0: std::option::Option<()>); ++ StorageDead(_43); + StorageLive(_48); -+ _48 = Option::<()>::None; -+ _43 = copy ((*_45).0: std::option::Option<()>); -+ ((*_45).0: std::option::Option<()>) = copy _48; -+ StorageDead(_48); -+ StorageDead(_44); -+ StorageLive(_49); -+ _49 = discriminant(_43); -+ switchInt(move _49) -> [0: bb11, 1: bb12, otherwise: bb5]; ++ _48 = discriminant(_42); ++ switchInt(move _48) -> [0: bb11, 1: bb12, otherwise: bb5]; } + + bb5: { @@ -266,8 +262,8 @@ + StorageDead(_12); + StorageDead(_28); + StorageDead(_29); -+ _38 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ discriminant((*_38)) = 3; ++ _37 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ discriminant((*_37)) = 3; + goto -> bb2; + } + @@ -281,14 +277,14 @@ + StorageDead(_18); + StorageDead(_17); + StorageDead(_12); -+ _39 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ drop((((*_39) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb8, unwind unreachable]; ++ _38 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ drop((((*_38) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb8, unwind unreachable]; + } + + bb8: { + _7 = Poll::<()>::Ready(move _30); -+ _40 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ discriminant((*_40)) = 1; ++ _39 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ discriminant((*_39)) = 1; + goto -> bb2; + } + @@ -298,7 +294,7 @@ + StorageLive(_29); + _28 = move _9; + StorageDead(_29); -+ _31 = move _28; ++ _9 = move _28; + StorageDead(_28); + _16 = const (); + goto -> bb4; @@ -309,18 +305,18 @@ + } + + bb11: { -+ _50 = option::expect_failed(const "`Ready` polled after completion") -> unwind unreachable; ++ _49 = option::expect_failed(const "`Ready` polled after completion") -> unwind unreachable; + } + + bb12: { -+ _42 = move ((_43 as Some).0: ()); -+ StorageDead(_49); -+ StorageDead(_43); -+ _18 = Poll::<()>::Ready(move _42); ++ _41 = move ((_42 as Some).0: ()); ++ StorageDead(_48); + StorageDead(_42); -+ StorageDead(_50); -+ StorageDead(_46); ++ _18 = Poll::<()>::Ready(move _41); ++ StorageDead(_41); ++ StorageDead(_49); + StorageDead(_45); ++ StorageDead(_44); + StorageDead(_22); + StorageDead(_19); + _25 = discriminant(_18); diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff index 8b027e988b8e..d7ae931aaae5 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff @@ -39,8 +39,8 @@ + let mut _28: &mut std::task::Context<'_>; + let mut _29: (); + let mut _30: (); -+ let mut _31: &mut std::task::Context<'_>; -+ let mut _32: u32; ++ let mut _31: u32; ++ let mut _32: &mut {async fn body of ActionPermit<'_, T>::perform()}; + let mut _33: &mut {async fn body of ActionPermit<'_, T>::perform()}; + let mut _34: &mut {async fn body of ActionPermit<'_, T>::perform()}; + let mut _35: &mut {async fn body of ActionPermit<'_, T>::perform()}; @@ -50,7 +50,6 @@ + let mut _39: &mut {async fn body of ActionPermit<'_, T>::perform()}; + let mut _40: &mut {async fn body of ActionPermit<'_, T>::perform()}; + let mut _41: &mut {async fn body of ActionPermit<'_, T>::perform()}; -+ let mut _42: &mut {async fn body of ActionPermit<'_, T>::perform()}; + scope 7 { + let mut _15: std::future::Ready<()>; + scope 8 { @@ -60,14 +59,14 @@ + scope 12 (inlined Pin::<&mut std::future::Ready<()>>::new_unchecked) { + } + scope 13 (inlined as Future>::poll) { -+ let mut _44: (); -+ let mut _45: std::option::Option<()>; -+ let mut _46: &mut std::option::Option<()>; -+ let mut _47: &mut std::future::Ready<()>; -+ let mut _48: &mut std::pin::Pin<&mut std::future::Ready<()>>; ++ let mut _43: (); ++ let mut _44: std::option::Option<()>; ++ let mut _45: &mut std::option::Option<()>; ++ let mut _46: &mut std::future::Ready<()>; ++ let mut _47: &mut std::pin::Pin<&mut std::future::Ready<()>>; + scope 14 (inlined > as DerefMut>::deref_mut) { + scope 15 (inlined Pin::<&mut std::future::Ready<()>>::as_mut) { -+ let mut _49: &mut &mut std::future::Ready<()>; ++ let mut _48: &mut &mut std::future::Ready<()>; + scope 16 (inlined Pin::<&mut std::future::Ready<()>>::new_unchecked) { + } + scope 18 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) { @@ -77,22 +76,22 @@ + } + } + scope 19 (inlined Option::<()>::take) { -+ let mut _50: std::option::Option<()>; ++ let mut _49: std::option::Option<()>; + scope 20 (inlined std::mem::replace::>) { + scope 21 { + } + } + } + scope 22 (inlined #[track_caller] Option::<()>::expect) { -+ let mut _51: isize; -+ let mut _52: !; ++ let mut _50: isize; ++ let mut _51: !; + scope 23 { + } + } + } + } + scope 10 (inlined ready::<()>) { -+ let mut _43: std::option::Option<()>; ++ let mut _42: std::option::Option<()>; + } + scope 11 (inlined as IntoFuture>::into_future) { + } @@ -149,10 +148,9 @@ + StorageLive(_39); + StorageLive(_40); + StorageLive(_41); -+ StorageLive(_42); -+ _33 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ _32 = discriminant((*_33)); -+ switchInt(move _32) -> [0: bb5, 1: bb15, 2: bb14, 3: bb13, otherwise: bb7]; ++ _32 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ _31 = discriminant((*_32)); ++ switchInt(move _31) -> [0: bb5, 1: bb15, 2: bb14, 3: bb13, otherwise: bb7]; } - bb3: { @@ -170,7 +168,6 @@ + } + + bb4: { -+ StorageDead(_42); + StorageDead(_41); + StorageDead(_40); + StorageDead(_39); @@ -203,23 +200,22 @@ - StorageDead(_2); - return; + bb5: { -+ _31 = move _9; ++ _33 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); + _34 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ _35 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ (((*_34) as variant#3).0: ActionPermit<'_, T>) = move ((*_35).0: ActionPermit<'_, T>); ++ (((*_33) as variant#3).0: ActionPermit<'_, T>) = move ((*_34).0: ActionPermit<'_, T>); + StorageLive(_12); + StorageLive(_13); + StorageLive(_14); + _14 = (); -+ StorageLive(_43); -+ _43 = Option::<()>::Some(copy _14); -+ _13 = std::future::Ready::<()>(move _43); -+ StorageDead(_43); ++ StorageLive(_42); ++ _42 = Option::<()>::Some(copy _14); ++ _13 = std::future::Ready::<()>(move _42); ++ StorageDead(_42); + StorageDead(_14); + _12 = move _13; + StorageDead(_13); -+ _36 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ (((*_36) as variant#3).1: std::future::Ready<()>) = move _12; ++ _35 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ (((*_35) as variant#3).1: std::future::Ready<()>) = move _12; + goto -> bb6; } @@ -231,39 +227,39 @@ + StorageLive(_19); + StorageLive(_20); + StorageLive(_21); -+ _37 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ _21 = &mut (((*_37) as variant#3).1: std::future::Ready<()>); ++ _36 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ _21 = &mut (((*_36) as variant#3).1: std::future::Ready<()>); + _20 = &mut (*_21); + _19 = Pin::<&mut std::future::Ready<()>> { pointer: copy _20 }; + StorageDead(_20); + StorageLive(_22); + StorageLive(_23); + StorageLive(_24); -+ _24 = copy _31; ++ _24 = copy _9; + _23 = move _24; + _22 = &mut (*_23); + StorageDead(_24); ++ StorageLive(_46); + StorageLive(_47); -+ StorageLive(_48); -+ StorageLive(_52); ++ StorageLive(_51); ++ StorageLive(_43); + StorageLive(_44); + StorageLive(_45); -+ StorageLive(_46); -+ _48 = &mut _19; ++ _47 = &mut _19; ++ StorageLive(_48); ++ _48 = &mut (_19.0: &mut std::future::Ready<()>); ++ _46 = copy (_19.0: &mut std::future::Ready<()>); ++ StorageDead(_48); ++ _45 = &mut ((*_46).0: std::option::Option<()>); + StorageLive(_49); -+ _49 = &mut (_19.0: &mut std::future::Ready<()>); -+ _47 = copy (_19.0: &mut std::future::Ready<()>); ++ _49 = Option::<()>::None; ++ _44 = copy ((*_46).0: std::option::Option<()>); ++ ((*_46).0: std::option::Option<()>) = copy _49; + StorageDead(_49); -+ _46 = &mut ((*_47).0: std::option::Option<()>); ++ StorageDead(_45); + StorageLive(_50); -+ _50 = Option::<()>::None; -+ _45 = copy ((*_47).0: std::option::Option<()>); -+ ((*_47).0: std::option::Option<()>) = copy _50; -+ StorageDead(_50); -+ StorageDead(_46); -+ StorageLive(_51); -+ _51 = discriminant(_45); -+ switchInt(move _51) -> [0: bb16, 1: bb17, otherwise: bb7]; ++ _50 = discriminant(_44); ++ switchInt(move _50) -> [0: bb16, 1: bb17, otherwise: bb7]; } - bb6 (cleanup): { @@ -285,8 +281,8 @@ + StorageDead(_12); + StorageDead(_28); + StorageDead(_29); -+ _38 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ discriminant((*_38)) = 3; ++ _37 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ discriminant((*_37)) = 3; + goto -> bb4; + } + @@ -300,14 +296,14 @@ + StorageDead(_18); + StorageDead(_17); + StorageDead(_12); -+ _39 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ drop((((*_39) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb10, unwind: bb12]; ++ _38 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ drop((((*_38) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb10, unwind: bb12]; + } + + bb10: { + _7 = Poll::<()>::Ready(move _30); -+ _40 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ discriminant((*_40)) = 1; ++ _39 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ discriminant((*_39)) = 1; + goto -> bb4; + } + @@ -319,13 +315,13 @@ + StorageDead(_18); + StorageDead(_17); + StorageDead(_12); -+ _41 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ drop((((*_41) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb12, unwind terminate(cleanup)]; ++ _40 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ drop((((*_40) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb12, unwind terminate(cleanup)]; + } + + bb12 (cleanup): { -+ _42 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); -+ discriminant((*_42)) = 2; ++ _41 = deref_copy (_8.0: &mut {async fn body of ActionPermit<'_, T>::perform()}); ++ discriminant((*_41)) = 2; + goto -> bb2; + } + @@ -335,7 +331,7 @@ + StorageLive(_29); + _28 = move _9; + StorageDead(_29); -+ _31 = move _28; ++ _9 = move _28; + StorageDead(_28); + _16 = const (); + goto -> bb6; @@ -350,18 +346,18 @@ + } + + bb16: { -+ _52 = option::expect_failed(const "`Ready` polled after completion") -> bb11; ++ _51 = option::expect_failed(const "`Ready` polled after completion") -> bb11; + } + + bb17: { -+ _44 = move ((_45 as Some).0: ()); -+ StorageDead(_51); -+ StorageDead(_45); -+ _18 = Poll::<()>::Ready(move _44); ++ _43 = move ((_44 as Some).0: ()); ++ StorageDead(_50); + StorageDead(_44); -+ StorageDead(_52); -+ StorageDead(_48); ++ _18 = Poll::<()>::Ready(move _43); ++ StorageDead(_43); ++ StorageDead(_51); + StorageDead(_47); ++ StorageDead(_46); + StorageDead(_22); + StorageDead(_19); + _25 = discriminant(_18); From eddd755f284093b0d3a8ce027f68653d5f5773d1 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Tue, 16 Sep 2025 22:58:54 +0000 Subject: [PATCH 579/682] Bless ui. --- .../ui/rustc_public-ir-print/async-closure.rs | 2 +- .../async-closure.stdout | 44 +++++++++---------- 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/tests/ui/rustc_public-ir-print/async-closure.rs b/tests/ui/rustc_public-ir-print/async-closure.rs index 80f96e09cfc7..bd8c7e888a37 100644 --- a/tests/ui/rustc_public-ir-print/async-closure.rs +++ b/tests/ui/rustc_public-ir-print/async-closure.rs @@ -1,6 +1,6 @@ //@ compile-flags: -Z unpretty=stable-mir --crate-type lib -C panic=abort -Zmir-opt-level=0 //@ check-pass -//@ only-x86_64 +//@ only-64bit //@ edition: 2024 //@ needs-unwind unwind edges are different with panic=abort diff --git a/tests/ui/rustc_public-ir-print/async-closure.stdout b/tests/ui/rustc_public-ir-print/async-closure.stdout index 73e9b8fc097a..5113dc5048b9 100644 --- a/tests/ui/rustc_public-ir-print/async-closure.stdout +++ b/tests/ui/rustc_public-ir-print/async-closure.stdout @@ -40,30 +40,28 @@ fn foo::{closure#0}::{closure#0}(_1: Pin<&mut {async closure body@$DIR/async-clo let _3: i32; let mut _4: &i32; let mut _5: (); - let mut _6: &mut Context<'_>; - let mut _7: u32; + let mut _6: u32; + let mut _7: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}; let mut _8: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}; let mut _9: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}; - let mut _10: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}; - debug _task_context => _6; + debug _task_context => _2; debug y => (*((*(_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})).0: &i32)); debug y => _3; bb0: { - _8 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); - _7 = discriminant((*_8)); - switchInt(move _7) -> [0: bb1, 1: bb2, otherwise: bb3]; + _7 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); + _6 = discriminant((*_7)); + switchInt(move _6) -> [0: bb1, 1: bb2, otherwise: bb3]; } bb1: { - _6 = move _2; StorageLive(_3); - _9 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); - _4 = CopyForDeref(((*_9).0: &i32)); + _8 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); + _4 = CopyForDeref(((*_8).0: &i32)); _3 = (*_4); _5 = (); StorageDead(_3); _0 = std::task::Poll::Ready(move _5); - _10 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); - discriminant((*_10)) = 1; + _9 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); + discriminant((*_9)) = 1; return; } bb2: { @@ -78,30 +76,28 @@ fn foo::{closure#0}::{synthetic#0}(_1: Pin<&mut {async closure body@$DIR/async-c let _3: i32; let mut _4: &i32; let mut _5: (); - let mut _6: &mut Context<'_>; - let mut _7: u32; + let mut _6: u32; + let mut _7: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}; let mut _8: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}; let mut _9: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}; - let mut _10: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6}; - debug _task_context => _6; + debug _task_context => _2; debug y => (*((*(_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})).0: &i32)); debug y => _3; bb0: { - _8 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); - _7 = discriminant((*_8)); - switchInt(move _7) -> [0: bb1, 1: bb2, otherwise: bb3]; + _7 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); + _6 = discriminant((*_7)); + switchInt(move _6) -> [0: bb1, 1: bb2, otherwise: bb3]; } bb1: { - _6 = move _2; StorageLive(_3); - _9 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); - _4 = CopyForDeref(((*_9).0: &i32)); + _8 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); + _4 = CopyForDeref(((*_8).0: &i32)); _3 = (*_4); _5 = (); StorageDead(_3); _0 = std::task::Poll::Ready(move _5); - _10 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); - discriminant((*_10)) = 1; + _9 = CopyForDeref((_1.0: &mut {async closure body@$DIR/async-closure.rs:9:22: 11:6})); + discriminant((*_9)) = 1; return; } bb2: { From 541989611144cdf5f2b5d7340b4f579ec004c799 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 16 Sep 2025 17:32:37 +1000 Subject: [PATCH 580/682] Use `LLVMDIBuilderCreateSubroutineType` --- .../rustc_codegen_llvm/src/debuginfo/metadata.rs | 14 +++++++++++--- compiler/rustc_codegen_llvm/src/debuginfo/mod.rs | 8 ++++---- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 13 ++++++++----- compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 7 ------- 4 files changed, 23 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index dc3a84b6a151..db821b3dee71 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -318,7 +318,7 @@ fn build_subroutine_type_di_node<'ll, 'tcx>( debug_context(cx).type_map.unique_id_to_di_node.borrow_mut().remove(&unique_type_id); - let fn_di_node = create_subroutine_type(cx, create_DIArray(DIB(cx), &signature_di_nodes[..])); + let fn_di_node = create_subroutine_type(cx, &signature_di_nodes[..]); // This is actually a function pointer, so wrap it in pointer DI. let name = compute_debuginfo_type_name(cx.tcx, fn_ty, false); @@ -346,9 +346,17 @@ fn build_subroutine_type_di_node<'ll, 'tcx>( pub(super) fn create_subroutine_type<'ll>( cx: &CodegenCx<'ll, '_>, - signature: &'ll DICompositeType, + signature: &[Option<&'ll llvm::Metadata>], ) -> &'ll DICompositeType { - unsafe { llvm::LLVMRustDIBuilderCreateSubroutineType(DIB(cx), signature) } + unsafe { + llvm::LLVMDIBuilderCreateSubroutineType( + DIB(cx), + None, // ("File" is ignored and has no effect) + signature.as_ptr(), + signature.len() as c_uint, + DIFlags::FlagZero, // (default value) + ) + } } /// Create debuginfo for `dyn SomeTrait` types. Currently these are empty structs diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 79334f7f9fe8..126082aa3aa6 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -349,7 +349,7 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { let file_metadata = file_metadata(self, &loc.file); let function_type_metadata = - create_subroutine_type(self, get_function_signature(self, fn_abi)); + create_subroutine_type(self, &get_function_signature(self, fn_abi)); let mut name = String::with_capacity(64); type_names::push_item_name(tcx, def_id, false, &mut name); @@ -441,9 +441,9 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { fn get_function_signature<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, - ) -> &'ll DIArray { + ) -> Vec> { if cx.sess().opts.debuginfo != DebugInfo::Full { - return create_DIArray(DIB(cx), &[]); + return vec![]; } let mut signature = Vec::with_capacity(fn_abi.args.len() + 1); @@ -484,7 +484,7 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { .extend(fn_abi.args.iter().map(|arg| Some(type_di_node(cx, arg.layout.ty)))); } - create_DIArray(DIB(cx), &signature[..]) + signature } fn get_template_parameters<'ll, 'tcx>( diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 0679f55ab7f0..af0ec6a33b0d 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1870,6 +1870,14 @@ unsafe extern "C" { Scope: &'ll Metadata, InlinedAt: Option<&'ll Metadata>, ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateSubroutineType<'ll>( + Builder: &DIBuilder<'ll>, + File: Option<&'ll Metadata>, // (ignored and has no effect) + ParameterTypes: *const Option<&'ll Metadata>, + NumParameterTypes: c_uint, + Flags: DIFlags, // (default is `DIFlags::DIFlagZero`) + ) -> &'ll Metadata; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2172,11 +2180,6 @@ unsafe extern "C" { SourceLen: size_t, ) -> &'a DIFile; - pub(crate) fn LLVMRustDIBuilderCreateSubroutineType<'a>( - Builder: &DIBuilder<'a>, - ParameterTypes: &'a DIArray, - ) -> &'a DICompositeType; - pub(crate) fn LLVMRustDIBuilderCreateFunction<'a>( Builder: &DIBuilder<'a>, Scope: &'a DIDescriptor, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 361a5f765510..a1c3cce99202 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1026,13 +1026,6 @@ LLVMRustDIBuilderCreateFile(LLVMDIBuilderRef Builder, const char *Filename, CSInfo, oSource)); } -extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateSubroutineType(LLVMDIBuilderRef Builder, - LLVMMetadataRef ParameterTypes) { - return wrap(unwrap(Builder)->createSubroutineType( - DITypeRefArray(unwrap(ParameterTypes)))); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFunction( LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, size_t NameLen, const char *LinkageName, size_t LinkageNameLen, From 2552deb9cd84b3bebaec98902d9d42bee2315822 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 16 Sep 2025 18:21:46 +1000 Subject: [PATCH 581/682] Use `LLVMDIBuilderCreateUnionType` --- .../src/debuginfo/metadata/type_map.rs | 13 +++++--- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 33 ++++++++++--------- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 13 -------- 3 files changed, 25 insertions(+), 34 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs index 18a783a348a4..af72d4ea3de0 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs @@ -1,5 +1,6 @@ use std::cell::RefCell; +use libc::c_uint; use rustc_abi::{Align, Size, VariantIdx}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; @@ -191,6 +192,7 @@ pub(super) fn stub<'ll, 'tcx>( containing_scope: Option<&'ll DIScope>, flags: DIFlags, ) -> StubInfo<'ll, 'tcx> { + let no_elements: &[Option<&llvm::Metadata>] = &[]; let empty_array = create_DIArray(DIB(cx), &[]); let unique_type_id_str = unique_type_id.generate_unique_id_string(cx.tcx); @@ -227,19 +229,20 @@ pub(super) fn stub<'ll, 'tcx>( } } Stub::Union => unsafe { - llvm::LLVMRustDIBuilderCreateUnionType( + llvm::LLVMDIBuilderCreateUnionType( DIB(cx), containing_scope, - name.as_c_char_ptr(), + name.as_ptr(), name.len(), file_metadata, line_number, size.bits(), align.bits() as u32, flags, - Some(empty_array), - 0, - unique_type_id_str.as_c_char_ptr(), + no_elements.as_ptr(), + no_elements.len() as c_uint, + 0u32, // (Objective-C runtime version; default is 0) + unique_type_id_str.as_ptr(), unique_type_id_str.len(), ) }, diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index af0ec6a33b0d..58aebe4c391e 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1878,6 +1878,23 @@ unsafe extern "C" { NumParameterTypes: c_uint, Flags: DIFlags, // (default is `DIFlags::DIFlagZero`) ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateUnionType<'ll>( + Builder: &DIBuilder<'ll>, + Scope: Option<&'ll Metadata>, + Name: *const c_uchar, // See "PTR_LEN_STR". + NameLen: size_t, + File: &'ll Metadata, + LineNumber: c_uint, + SizeInBits: u64, + AlignInBits: u32, + Flags: DIFlags, + Elements: *const Option<&'ll Metadata>, + NumElements: c_uint, + RunTimeLang: c_uint, // (optional Objective-C runtime version; default is 0) + UniqueId: *const c_uchar, // See "PTR_LEN_STR". + UniqueIdLen: size_t, + ) -> &'ll Metadata; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2391,22 +2408,6 @@ unsafe extern "C" { IsScoped: bool, ) -> &'a DIType; - pub(crate) fn LLVMRustDIBuilderCreateUnionType<'a>( - Builder: &DIBuilder<'a>, - Scope: Option<&'a DIScope>, - Name: *const c_char, - NameLen: size_t, - File: &'a DIFile, - LineNumber: c_uint, - SizeInBits: u64, - AlignInBits: u32, - Flags: DIFlags, - Elements: Option<&'a DIArray>, - RunTimeLang: c_uint, - UniqueId: *const c_char, - UniqueIdLen: size_t, - ) -> &'a DIType; - pub(crate) fn LLVMRustDIBuilderCreateVariantPart<'a>( Builder: &DIBuilder<'a>, Scope: &'a DIScope, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index a1c3cce99202..2cbfcf79c83d 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1268,19 +1268,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerationType( /* RunTimeLang */ 0, "", IsScoped)); } -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateUnionType( - LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, - size_t NameLen, LLVMMetadataRef File, unsigned LineNumber, - uint64_t SizeInBits, uint32_t AlignInBits, LLVMDIFlags Flags, - LLVMMetadataRef Elements, unsigned RunTimeLang, const char *UniqueId, - size_t UniqueIdLen) { - return wrap(unwrap(Builder)->createUnionType( - unwrapDI(Scope), StringRef(Name, NameLen), - unwrapDI(File), LineNumber, SizeInBits, AlignInBits, - fromRust(Flags), DINodeArray(unwrapDI(Elements)), RunTimeLang, - StringRef(UniqueId, UniqueIdLen))); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateTemplateTypeParameter( LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, size_t NameLen, LLVMMetadataRef Ty) { From bef8f646a632215c362ee45d88d2390e47e65d4c Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 16 Sep 2025 18:37:23 +1000 Subject: [PATCH 582/682] Use `LLVMDIBuilderCreateArrayType` --- .../src/debuginfo/metadata.rs | 14 ++++++-------- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 17 +++++++++-------- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 9 --------- 3 files changed, 15 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index db821b3dee71..fcbfe6ca2242 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -32,9 +32,7 @@ use self::type_map::{DINodeCreationResult, Stub, UniqueTypeId}; use super::CodegenUnitDebugContext; use super::namespace::mangled_name_of_instance; use super::type_names::{compute_debuginfo_type_name, compute_debuginfo_vtable_name}; -use super::utils::{ - DIB, create_DIArray, debug_context, get_namespace_for_item, is_node_local_to_unit, -}; +use super::utils::{DIB, debug_context, get_namespace_for_item, is_node_local_to_unit}; use crate::common::{AsCCharPtr, CodegenCx}; use crate::debuginfo::dwarf_const; use crate::debuginfo::metadata::type_map::build_type_with_children; @@ -119,17 +117,17 @@ fn build_fixed_size_array_di_node<'ll, 'tcx>( .try_to_target_usize(cx.tcx) .expect("expected monomorphic const in codegen") as c_longlong; - let subrange = - unsafe { Some(llvm::LLVMRustDIBuilderGetOrCreateSubrange(DIB(cx), 0, upper_bound)) }; + let subrange = unsafe { llvm::LLVMRustDIBuilderGetOrCreateSubrange(DIB(cx), 0, upper_bound) }; + let subscripts = &[subrange]; - let subscripts = create_DIArray(DIB(cx), &[subrange]); let di_node = unsafe { - llvm::LLVMRustDIBuilderCreateArrayType( + llvm::LLVMDIBuilderCreateArrayType( DIB(cx), size.bits(), align.bits() as u32, element_type_di_node, - subscripts, + subscripts.as_ptr(), + subscripts.len() as c_uint, ) }; diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 58aebe4c391e..e2426980c6ad 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1895,6 +1895,15 @@ unsafe extern "C" { UniqueId: *const c_uchar, // See "PTR_LEN_STR". UniqueIdLen: size_t, ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateArrayType<'ll>( + Builder: &DIBuilder<'ll>, + Size: u64, + Align: u32, + Ty: &'ll Metadata, + Subscripts: *const &'ll Metadata, + NumSubscripts: c_uint, + ) -> &'ll Metadata; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2355,14 +2364,6 @@ unsafe extern "C" { AlignInBits: u32, ) -> &'a DIVariable; - pub(crate) fn LLVMRustDIBuilderCreateArrayType<'a>( - Builder: &DIBuilder<'a>, - Size: u64, - AlignInBits: u32, - Ty: &'a DIType, - Subscripts: &'a DIArray, - ) -> &'a DIType; - pub(crate) fn LLVMRustDIBuilderGetOrCreateSubrange<'a>( Builder: &DIBuilder<'a>, Lo: i64, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 2cbfcf79c83d..d5be7aeb236e 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1211,15 +1211,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariable( } } -extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateArrayType(LLVMDIBuilderRef Builder, uint64_t Size, - uint32_t AlignInBits, LLVMMetadataRef Ty, - LLVMMetadataRef Subscripts) { - return wrap(unwrap(Builder)->createArrayType( - Size, AlignInBits, unwrapDI(Ty), - DINodeArray(unwrapDI(Subscripts)))); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderGetOrCreateSubrange(LLVMDIBuilderRef Builder, int64_t Lo, int64_t Count) { From 3e9048d9a47bb9db2b95adda85cd544a30bfcb8a Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 16 Sep 2025 18:46:45 +1000 Subject: [PATCH 583/682] Use `LLVMDIBuilderCreateBasicType` --- .../src/debuginfo/metadata.rs | 5 +++-- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 17 +++++++++-------- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 8 -------- 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index fcbfe6ca2242..4c210b46f724 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -840,12 +840,13 @@ fn create_basic_type<'ll, 'tcx>( encoding: u32, ) -> &'ll DIBasicType { unsafe { - llvm::LLVMRustDIBuilderCreateBasicType( + llvm::LLVMDIBuilderCreateBasicType( DIB(cx), - name.as_c_char_ptr(), + name.as_ptr(), name.len(), size.bits(), encoding, + DIFlags::FlagZero, ) } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index e2426980c6ad..63fdc079bae4 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1904,6 +1904,15 @@ unsafe extern "C" { Subscripts: *const &'ll Metadata, NumSubscripts: c_uint, ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateBasicType<'ll>( + Builder: &DIBuilder<'ll>, + Name: *const c_uchar, // See "PTR_LEN_STR". + NameLen: size_t, + SizeInBits: u64, + Encoding: c_uint, // (`LLVMDWARFTypeEncoding`) + Flags: DIFlags, // (default is `DIFlags::DIFlagZero`) + ) -> &'ll Metadata; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2239,14 +2248,6 @@ unsafe extern "C" { TParam: &'a DIArray, ) -> &'a DISubprogram; - pub(crate) fn LLVMRustDIBuilderCreateBasicType<'a>( - Builder: &DIBuilder<'a>, - Name: *const c_char, - NameLen: size_t, - SizeInBits: u64, - Encoding: c_uint, - ) -> &'a DIBasicType; - pub(crate) fn LLVMRustDIBuilderCreateTypedef<'a>( Builder: &DIBuilder<'a>, Type: &'a DIBasicType, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index d5be7aeb236e..80d3db3b99d9 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1064,14 +1064,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateMethod( return wrap(Sub); } -extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateBasicType(LLVMDIBuilderRef Builder, const char *Name, - size_t NameLen, uint64_t SizeInBits, - unsigned Encoding) { - return wrap(unwrap(Builder)->createBasicType(StringRef(Name, NameLen), - SizeInBits, Encoding)); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateTypedef(LLVMDIBuilderRef Builder, LLVMMetadataRef Type, const char *Name, size_t NameLen, From bae6fde27057c4b8fe216eb39a4ba6867aeeb8fb Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 16 Sep 2025 20:09:34 +1000 Subject: [PATCH 584/682] Use `LLVMDIBuilderCreatePointerType` --- .../src/debuginfo/metadata.rs | 68 ++++++++++--------- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 20 +++--- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 9 --- 3 files changed, 45 insertions(+), 52 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 4c210b46f724..0903ddab2854 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -173,17 +173,13 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>( "ptr_type={ptr_type}, pointee_type={pointee_type}", ); - let di_node = unsafe { - llvm::LLVMRustDIBuilderCreatePointerType( - DIB(cx), - pointee_type_di_node, - pointer_size.bits(), - pointer_align.abi.bits() as u32, - 0, // Ignore DWARF address space. - ptr_type_debuginfo_name.as_c_char_ptr(), - ptr_type_debuginfo_name.len(), - ) - }; + let di_node = create_pointer_type( + cx, + pointee_type_di_node, + pointer_size, + pointer_align.abi, + &ptr_type_debuginfo_name, + ); DINodeCreationResult { di_node, already_stored_in_typemap: false } } @@ -231,17 +227,13 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>( // The data pointer type is a regular, thin pointer, regardless of whether this // is a slice or a trait object. - let data_ptr_type_di_node = unsafe { - llvm::LLVMRustDIBuilderCreatePointerType( - DIB(cx), - pointee_type_di_node, - addr_field.size.bits(), - addr_field.align.abi.bits() as u32, - 0, // Ignore DWARF address space. - std::ptr::null(), - 0, - ) - }; + let data_ptr_type_di_node = create_pointer_type( + cx, + pointee_type_di_node, + addr_field.size, + addr_field.align.abi, + "", + ); smallvec![ build_field_di_node( @@ -327,17 +319,7 @@ fn build_subroutine_type_di_node<'ll, 'tcx>( } _ => unreachable!(), }; - let di_node = unsafe { - llvm::LLVMRustDIBuilderCreatePointerType( - DIB(cx), - fn_di_node, - size.bits(), - align.bits() as u32, - 0, // Ignore DWARF address space. - name.as_c_char_ptr(), - name.len(), - ) - }; + let di_node = create_pointer_type(cx, fn_di_node, size, align, &name); DINodeCreationResult::new(di_node, false) } @@ -357,6 +339,26 @@ pub(super) fn create_subroutine_type<'ll>( } } +fn create_pointer_type<'ll>( + cx: &CodegenCx<'ll, '_>, + pointee_ty: &'ll llvm::Metadata, + size: Size, + align: Align, + name: &str, +) -> &'ll llvm::Metadata { + unsafe { + llvm::LLVMDIBuilderCreatePointerType( + DIB(cx), + pointee_ty, + size.bits(), + align.bits() as u32, + 0, // Ignore DWARF address space. + name.as_ptr(), + name.len(), + ) + } +} + /// Create debuginfo for `dyn SomeTrait` types. Currently these are empty structs /// we with the correct type name (e.g. "dyn SomeTrait + Sync"). fn build_dyn_type_di_node<'ll, 'tcx>( diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 63fdc079bae4..bd26590e7d36 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1913,6 +1913,16 @@ unsafe extern "C" { Encoding: c_uint, // (`LLVMDWARFTypeEncoding`) Flags: DIFlags, // (default is `DIFlags::DIFlagZero`) ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreatePointerType<'ll>( + Builder: &DIBuilder<'ll>, + PointeeTy: &'ll Metadata, + SizeInBits: u64, + AlignInBits: u32, + AddressSpace: c_uint, // (optional DWARF address space; default is 0) + Name: *const c_uchar, // See "PTR_LEN_STR". + NameLen: size_t, + ) -> &'ll Metadata; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2258,16 +2268,6 @@ unsafe extern "C" { Scope: Option<&'a DIScope>, ) -> &'a DIDerivedType; - pub(crate) fn LLVMRustDIBuilderCreatePointerType<'a>( - Builder: &DIBuilder<'a>, - PointeeTy: &'a DIType, - SizeInBits: u64, - AlignInBits: u32, - AddressSpace: c_uint, - Name: *const c_char, - NameLen: size_t, - ) -> &'a DIDerivedType; - pub(crate) fn LLVMRustDIBuilderCreateStructType<'a>( Builder: &DIBuilder<'a>, Scope: Option<&'a DIDescriptor>, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 80d3db3b99d9..e6be7e2961b4 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1074,15 +1074,6 @@ LLVMRustDIBuilderCreateTypedef(LLVMDIBuilderRef Builder, LLVMMetadataRef Type, LineNo, unwrapDIPtr(Scope))); } -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreatePointerType( - LLVMDIBuilderRef Builder, LLVMMetadataRef PointeeTy, uint64_t SizeInBits, - uint32_t AlignInBits, unsigned AddressSpace, const char *Name, - size_t NameLen) { - return wrap(unwrap(Builder)->createPointerType( - unwrapDI(PointeeTy), SizeInBits, AlignInBits, AddressSpace, - StringRef(Name, NameLen))); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStructType( LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNumber, From af88d14cac15dd47452ce670e65c1ed56dae9586 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 16 Sep 2025 20:29:56 +1000 Subject: [PATCH 585/682] Use `LLVMDIBuilderCreateStructType` --- .../src/debuginfo/metadata/type_map.rs | 14 +++--- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 43 ++++++++++--------- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 14 ------ 3 files changed, 29 insertions(+), 42 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs index af72d4ea3de0..37200fdc41af 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs @@ -10,7 +10,7 @@ use rustc_middle::bug; use rustc_middle::ty::{self, ExistentialTraitRef, Ty, TyCtxt}; use super::{DefinitionLocation, SmallVec, UNKNOWN_LINE_NUMBER, unknown_file_metadata}; -use crate::common::{AsCCharPtr, CodegenCx}; +use crate::common::CodegenCx; use crate::debuginfo::utils::{DIB, create_DIArray, debug_context}; use crate::llvm::debuginfo::{DIFlags, DIScope, DIType}; use crate::llvm::{self}; @@ -193,7 +193,6 @@ pub(super) fn stub<'ll, 'tcx>( flags: DIFlags, ) -> StubInfo<'ll, 'tcx> { let no_elements: &[Option<&llvm::Metadata>] = &[]; - let empty_array = create_DIArray(DIB(cx), &[]); let unique_type_id_str = unique_type_id.generate_unique_id_string(cx.tcx); let (file_metadata, line_number) = if let Some(def_location) = def_location { @@ -209,10 +208,10 @@ pub(super) fn stub<'ll, 'tcx>( _ => None, }; unsafe { - llvm::LLVMRustDIBuilderCreateStructType( + llvm::LLVMDIBuilderCreateStructType( DIB(cx), containing_scope, - name.as_c_char_ptr(), + name.as_ptr(), name.len(), file_metadata, line_number, @@ -220,10 +219,11 @@ pub(super) fn stub<'ll, 'tcx>( align.bits() as u32, flags, None, - empty_array, - 0, + no_elements.as_ptr(), + no_elements.len() as c_uint, + 0u32, // (Objective-C runtime version; default is 0) vtable_holder, - unique_type_id_str.as_c_char_ptr(), + unique_type_id_str.as_ptr(), unique_type_id_str.len(), ) } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index bd26590e7d36..f88eaebc7006 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -24,9 +24,9 @@ use rustc_target::spec::SymbolVisibility; use super::RustString; use super::debuginfo::{ - DIArray, DIBasicType, DIBuilder, DICompositeType, DIDerivedType, DIDescriptor, DIEnumerator, - DIFile, DIFlags, DIGlobalVariableExpression, DILocation, DISPFlags, DIScope, DISubprogram, - DISubrange, DITemplateTypeParameter, DIType, DIVariable, DebugEmissionKind, DebugNameTableKind, + DIArray, DIBasicType, DIBuilder, DIDerivedType, DIDescriptor, DIEnumerator, DIFile, DIFlags, + DIGlobalVariableExpression, DILocation, DISPFlags, DIScope, DISubprogram, DISubrange, + DITemplateTypeParameter, DIType, DIVariable, DebugEmissionKind, DebugNameTableKind, }; use crate::llvm; @@ -1923,6 +1923,25 @@ unsafe extern "C" { Name: *const c_uchar, // See "PTR_LEN_STR". NameLen: size_t, ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateStructType<'ll>( + Builder: &DIBuilder<'ll>, + Scope: Option<&'ll Metadata>, + Name: *const c_uchar, // See "PTR_LEN_STR". + NameLen: size_t, + File: &'ll Metadata, + LineNumber: c_uint, + SizeInBits: u64, + AlignInBits: u32, + Flags: DIFlags, + DerivedFrom: Option<&'ll Metadata>, + Elements: *const Option<&'ll Metadata>, + NumElements: c_uint, + RunTimeLang: c_uint, // (optional Objective-C runtime version; default is 0) + VTableHolder: Option<&'ll Metadata>, + UniqueId: *const c_uchar, // See "PTR_LEN_STR". + UniqueIdLen: size_t, + ) -> &'ll Metadata; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2268,24 +2287,6 @@ unsafe extern "C" { Scope: Option<&'a DIScope>, ) -> &'a DIDerivedType; - pub(crate) fn LLVMRustDIBuilderCreateStructType<'a>( - Builder: &DIBuilder<'a>, - Scope: Option<&'a DIDescriptor>, - Name: *const c_char, - NameLen: size_t, - File: &'a DIFile, - LineNumber: c_uint, - SizeInBits: u64, - AlignInBits: u32, - Flags: DIFlags, - DerivedFrom: Option<&'a DIType>, - Elements: &'a DIArray, - RunTimeLang: c_uint, - VTableHolder: Option<&'a DIType>, - UniqueId: *const c_char, - UniqueIdLen: size_t, - ) -> &'a DICompositeType; - pub(crate) fn LLVMRustDIBuilderCreateMemberType<'a>( Builder: &DIBuilder<'a>, Scope: &'a DIDescriptor, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index e6be7e2961b4..b4ca641674f0 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1074,20 +1074,6 @@ LLVMRustDIBuilderCreateTypedef(LLVMDIBuilderRef Builder, LLVMMetadataRef Type, LineNo, unwrapDIPtr(Scope))); } -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStructType( - LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, - size_t NameLen, LLVMMetadataRef File, unsigned LineNumber, - uint64_t SizeInBits, uint32_t AlignInBits, LLVMDIFlags Flags, - LLVMMetadataRef DerivedFrom, LLVMMetadataRef Elements, unsigned RunTimeLang, - LLVMMetadataRef VTableHolder, const char *UniqueId, size_t UniqueIdLen) { - return wrap(unwrap(Builder)->createStructType( - unwrapDI(Scope), StringRef(Name, NameLen), - unwrapDI(File), LineNumber, SizeInBits, AlignInBits, - fromRust(Flags), unwrapDI(DerivedFrom), - DINodeArray(unwrapDI(Elements)), RunTimeLang, - unwrapDI(VTableHolder), StringRef(UniqueId, UniqueIdLen))); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantPart( LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNumber, From 26f3337d4eda0ba22b615744fda0185d0ee344b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 17 Sep 2025 04:16:47 +0200 Subject: [PATCH 586/682] Remove `DynKind` --- .../src/diagnostics/explain_borrow.rs | 4 +-- compiler/rustc_borrowck/src/lib.rs | 4 +-- compiler/rustc_borrowck/src/type_check/mod.rs | 6 ++--- .../rustc_codegen_cranelift/src/abi/mod.rs | 2 +- .../rustc_codegen_cranelift/src/unsize.rs | 4 +-- .../src/value_and_place.rs | 3 +-- compiler/rustc_codegen_ssa/src/base.rs | 6 ++--- compiler/rustc_codegen_ssa/src/meth.rs | 2 +- compiler/rustc_codegen_ssa/src/mir/block.rs | 2 +- .../rustc_const_eval/src/interpret/call.rs | 6 ++--- .../rustc_const_eval/src/interpret/cast.rs | 4 +-- .../src/interpret/eval_context.rs | 2 +- .../src/interpret/intrinsics.rs | 2 +- .../rustc_const_eval/src/interpret/stack.rs | 2 +- .../rustc_const_eval/src/interpret/traits.rs | 2 +- .../src/interpret/validity.rs | 2 +- .../rustc_const_eval/src/interpret/visitor.rs | 2 +- .../rustc_const_eval/src/util/type_name.rs | 2 +- .../rustc_hir_analysis/src/collect/dump.rs | 2 +- .../src/hir_ty_lowering/dyn_compatibility.rs | 5 ++-- .../src/hir_ty_lowering/errors.rs | 2 +- .../src/hir_ty_lowering/mod.rs | 7 +---- .../src/variance/constraints.rs | 2 +- compiler/rustc_hir_typeck/src/cast.rs | 8 ++---- compiler/rustc_hir_typeck/src/coercion.rs | 2 +- .../rustc_hir_typeck/src/method/suggest.rs | 2 +- .../src/deref_into_dyn_supertrait.rs | 4 +-- compiler/rustc_lint/src/types.rs | 2 +- compiler/rustc_lint/src/unused.rs | 2 +- compiler/rustc_middle/src/ty/context.rs | 2 +- compiler/rustc_middle/src/ty/layout.rs | 6 ++--- compiler/rustc_middle/src/ty/print/pretty.rs | 6 ++--- .../src/ty/significant_drop_order.rs | 2 +- .../rustc_middle/src/ty/structural_impls.rs | 14 +++++----- compiler/rustc_middle/src/ty/sty.rs | 14 +++++----- .../src/move_paths/builder.rs | 4 +-- compiler/rustc_mir_transform/src/coroutine.rs | 2 +- .../src/canonicalizer.rs | 2 +- .../src/solve/assembly/mod.rs | 2 +- .../src/solve/assembly/structural_traits.rs | 6 ++--- .../src/solve/normalizes_to/mod.rs | 4 +-- .../src/solve/trait_goals.rs | 12 ++++----- compiler/rustc_passes/src/check_export.rs | 2 +- compiler/rustc_pattern_analysis/src/rustc.rs | 2 +- compiler/rustc_public/src/ty.rs | 11 +++----- .../src/unstable/convert/internal.rs | 19 ++----------- .../src/unstable/convert/stable/ty.rs | 27 +++++-------------- compiler/rustc_public/src/visitor.rs | 2 +- .../src/cfi/typeid/itanium_cxx_abi/encode.rs | 6 ++--- .../cfi/typeid/itanium_cxx_abi/transform.rs | 8 +++--- compiler/rustc_symbol_mangling/src/export.rs | 2 +- compiler/rustc_symbol_mangling/src/v0.rs | 6 ++--- .../nice_region_error/static_impl_trait.rs | 2 +- .../error_reporting/infer/note_and_explain.rs | 12 +++------ .../src/error_reporting/infer/region.rs | 2 +- .../src/error_reporting/traits/mod.rs | 2 +- .../traits/on_unimplemented.rs | 2 +- .../src/error_reporting/traits/suggestions.rs | 6 ++--- .../src/traits/select/candidate_assembly.rs | 8 +++--- .../src/traits/select/confirmation.rs | 14 +++++----- .../src/traits/vtable.rs | 6 ++--- .../rustc_trait_selection/src/traits/wf.rs | 2 +- compiler/rustc_ty_utils/src/layout.rs | 2 +- compiler/rustc_ty_utils/src/ty.rs | 4 +-- compiler/rustc_type_ir/src/flags.rs | 2 +- compiler/rustc_type_ir/src/inherent.rs | 9 ++----- compiler/rustc_type_ir/src/lib.rs | 1 - compiler/rustc_type_ir/src/outlives.rs | 2 +- compiler/rustc_type_ir/src/relate.rs | 15 ++++------- compiler/rustc_type_ir/src/ty_kind.rs | 22 +++------------ compiler/rustc_type_ir/src/walk.rs | 2 +- src/librustdoc/clean/mod.rs | 2 +- .../clippy_utils/src/qualify_min_const_fn.rs | 2 +- src/tools/clippy/clippy_utils/src/ty/mod.rs | 4 +-- 74 files changed, 142 insertions(+), 237 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index fda96dde8269..7ca07bb9b434 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -438,7 +438,7 @@ impl<'tcx> BorrowExplanation<'tcx> { let elaborated_args = std::iter::zip(*args, &generics.own_params).map(|(arg, param)| { - if let Some(ty::Dynamic(obj, _, ty::Dyn)) = arg.as_type().map(Ty::kind) { + if let Some(ty::Dynamic(obj, _)) = arg.as_type().map(Ty::kind) { let default = tcx.object_lifetime_default(param.def_id); let re_static = tcx.lifetimes.re_static; @@ -464,7 +464,7 @@ impl<'tcx> BorrowExplanation<'tcx> { has_dyn = true; - Ty::new_dynamic(tcx, obj, implied_region, ty::Dyn).into() + Ty::new_dynamic(tcx, obj, implied_region).into() } else { arg } diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 5d2dda8b0e7c..5b20c5536c39 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1904,7 +1904,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { | ty::Slice(_) | ty::FnDef(_, _) | ty::FnPtr(..) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Closure(_, _) | ty::CoroutineClosure(_, _) | ty::Coroutine(_, _) @@ -1950,7 +1950,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { | ty::Ref(_, _, _) | ty::FnDef(_, _) | ty::FnPtr(..) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::CoroutineWitness(..) | ty::Never | ty::UnsafeBinder(_) diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 8c5447fe1e9e..8e6150fe9da2 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1487,9 +1487,9 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { unsize_to: None, }, ); - } else if let ty::Dynamic(src_tty, _src_lt, ty::Dyn) = + } else if let ty::Dynamic(src_tty, _src_lt) = *self.struct_tail(src.ty, location).kind() - && let ty::Dynamic(dst_tty, dst_lt, ty::Dyn) = + && let ty::Dynamic(dst_tty, dst_lt) = *self.struct_tail(dst.ty, location).kind() && src_tty.principal().is_some() && dst_tty.principal().is_some() @@ -1511,7 +1511,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { // FIXME: Once we disallow casting `*const dyn Trait + 'short` // to `*const dyn Trait + 'long`, then this can just be `src_lt`. dst_lt, - ty::Dyn, ); let dst_obj = Ty::new_dynamic( tcx, @@ -1519,7 +1518,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { &dst_tty.without_auto_traits().collect::>(), ), dst_lt, - ty::Dyn, ); debug!(?src_tty, ?dst_tty, ?src_obj, ?dst_obj); diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 7d0731c77bdc..29ee46194de1 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -715,7 +715,7 @@ pub(crate) fn codegen_drop<'tcx>( fx.bcx.ins().jump(ret_block, &[]); } else { match ty.kind() { - ty::Dynamic(_, _, ty::Dyn) => { + ty::Dynamic(_, _) => { // IN THIS ARM, WE HAVE: // ty = *mut (dyn Trait) // which is: exists ( *mut T, Vtable ) diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs index 2aee0b2e9742..643c7feb89a2 100644 --- a/compiler/rustc_codegen_cranelift/src/unsize.rs +++ b/compiler/rustc_codegen_cranelift/src/unsize.rs @@ -30,9 +30,7 @@ pub(crate) fn unsized_info<'tcx>( fx.pointer_type, len.try_to_target_usize(fx.tcx).expect("expected monomorphic const in codegen") as i64, ), - (&ty::Dynamic(data_a, _, src_dyn_kind), &ty::Dynamic(data_b, _, target_dyn_kind)) - if src_dyn_kind == target_dyn_kind => - { + (&ty::Dynamic(data_a, _), &ty::Dynamic(data_b, _)) => { let old_info = old_info.expect("unsized_info: missing old info for trait upcasting coercion"); let b_principal_def_id = data_b.principal_def_id(); diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index 9d73f200afe2..4519fa1a270e 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -909,8 +909,7 @@ pub(crate) fn assert_assignable<'tcx>( ); // fn(&T) -> for<'l> fn(&'l T) is allowed } - (&ty::Dynamic(from_traits, _, _from_kind), &ty::Dynamic(to_traits, _, _to_kind)) => { - // FIXME(dyn-star): Do the right thing with DynKinds + (&ty::Dynamic(from_traits, _), &ty::Dynamic(to_traits, _)) => { for (from, to) in from_traits.iter().zip(to_traits) { let from = fx.tcx.normalize_erasing_late_bound_regions(fx.typing_env(), from); let to = fx.tcx.normalize_erasing_late_bound_regions(fx.typing_env(), to); diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 45b028aa8eff..68a2f43ec67b 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -168,9 +168,7 @@ fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( (&ty::Array(_, len), &ty::Slice(_)) => cx.const_usize( len.try_to_target_usize(cx.tcx()).expect("expected monomorphic const in codegen"), ), - (&ty::Dynamic(data_a, _, src_dyn_kind), &ty::Dynamic(data_b, _, target_dyn_kind)) - if src_dyn_kind == target_dyn_kind => - { + (&ty::Dynamic(data_a, _), &ty::Dynamic(data_b, _)) => { let old_info = old_info.expect("unsized_info: missing old info for trait upcasting coercion"); let b_principal_def_id = data_b.principal_def_id(); @@ -208,7 +206,7 @@ fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( old_info } } - (_, ty::Dynamic(data, _, _)) => meth::get_vtable( + (_, ty::Dynamic(data, _)) => meth::get_vtable( cx, source, data.principal() diff --git a/compiler/rustc_codegen_ssa/src/meth.rs b/compiler/rustc_codegen_ssa/src/meth.rs index 34ad35a729b9..2fa466b50017 100644 --- a/compiler/rustc_codegen_ssa/src/meth.rs +++ b/compiler/rustc_codegen_ssa/src/meth.rs @@ -78,7 +78,7 @@ fn dyn_trait_in_self<'tcx>( ) -> Option> { for arg in ty.peel_refs().walk() { if let GenericArgKind::Type(ty) = arg.kind() - && let ty::Dynamic(data, _, _) = ty.kind() + && let ty::Dynamic(data, _) = ty.kind() { // FIXME(arbitrary_self_types): This is likely broken for receivers which // have a "non-self" trait objects as a generic argument. diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 6492ef73956b..1b218a0d3395 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -614,7 +614,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let (maybe_null, drop_fn, fn_abi, drop_instance) = match ty.kind() { // FIXME(eddyb) perhaps move some of this logic into // `Instance::resolve_drop_in_place`? - ty::Dynamic(_, _, ty::Dyn) => { + ty::Dynamic(_, _) => { // IN THIS ARM, WE HAVE: // ty = *mut (dyn Trait) // which is: exists ( *mut T, Vtable ) diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 4cb88d44e1b1..23e4a2921ea6 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -658,7 +658,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let val = self.read_immediate(&receiver)?; break self.ref_to_mplace(&val)?; } - ty::Dynamic(.., ty::Dyn) => break receiver.assert_mem_place(), // no immediate unsized values + ty::Dynamic(..) => break receiver.assert_mem_place(), // no immediate unsized values _ => { // Not there yet, search for the only non-ZST field. // (The rules for `DispatchFromDyn` ensure there's exactly one such field.) @@ -675,7 +675,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // (For that reason we also cannot use `unpack_dyn_trait`.) let receiver_tail = self.tcx.struct_tail_for_codegen(receiver_place.layout.ty, self.typing_env); - let ty::Dynamic(receiver_trait, _, ty::Dyn) = receiver_tail.kind() else { + let ty::Dynamic(receiver_trait, _) = receiver_tail.kind() else { span_bug!(self.cur_span(), "dynamic call on non-`dyn` type {}", receiver_tail) }; assert!(receiver_place.layout.is_unsized()); @@ -822,7 +822,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // instead we do the virtual call stuff ourselves. It's easier here than in `eval_fn_call` // since we can just get a place of the underlying type and use `mplace_to_ref`. let place = match place.layout.ty.kind() { - ty::Dynamic(data, _, ty::Dyn) => { + ty::Dynamic(data, _) => { // Dropping a trait object. Need to find actual drop fn. self.unpack_dyn_trait(&place, data)? } diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs index e3afeda5b7c9..0075740e0317 100644 --- a/compiler/rustc_const_eval/src/interpret/cast.rs +++ b/compiler/rustc_const_eval/src/interpret/cast.rs @@ -386,7 +386,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ); self.write_immediate(val, dest) } - (ty::Dynamic(data_a, _, ty::Dyn), ty::Dynamic(data_b, _, ty::Dyn)) => { + (ty::Dynamic(data_a, _), ty::Dynamic(data_b, _)) => { let val = self.read_immediate(src)?; // MIR building generates odd NOP casts, prevent them from causing unexpected trouble. // See . @@ -436,7 +436,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let new_vptr = self.get_vtable_ptr(ty, data_b)?; self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest) } - (_, &ty::Dynamic(data, _, ty::Dyn)) => { + (_, &ty::Dynamic(data, _)) => { // Initial cast from sized to dyn trait let vtable = self.get_vtable_ptr(src_pointee_ty, data)?; let ptr = self.read_pointer(src)?; diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 91ed71ac3e54..0e4a98f0941a 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -469,7 +469,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } interp_ok(Some((full_size, full_align))) } - ty::Dynamic(expected_trait, _, ty::Dyn) => { + ty::Dynamic(expected_trait, _) => { let vtable = metadata.unwrap_meta().to_pointer(self)?; // Read size and align from vtable (already checks size). interp_ok(Some(self.get_vtable_size_and_align(vtable, Some(expected_trait))?)) diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index 5e3d0a15d8bc..418dd658121d 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -181,7 +181,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { | ty::Ref(_, _, _) | ty::FnDef(_, _) | ty::FnPtr(..) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Closure(_, _) | ty::CoroutineClosure(_, _) | ty::Coroutine(_, _) diff --git a/compiler/rustc_const_eval/src/interpret/stack.rs b/compiler/rustc_const_eval/src/interpret/stack.rs index 7cabfd961212..1c1c59da9d88 100644 --- a/compiler/rustc_const_eval/src/interpret/stack.rs +++ b/compiler/rustc_const_eval/src/interpret/stack.rs @@ -509,7 +509,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { | ty::Never | ty::Error(_) => true, - ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => false, + ty::Str | ty::Slice(_) | ty::Dynamic(_, _) | ty::Foreign(..) => false, ty::Tuple(tys) => tys.last().is_none_or(|ty| is_very_trivially_sized(*ty)), diff --git a/compiler/rustc_const_eval/src/interpret/traits.rs b/compiler/rustc_const_eval/src/interpret/traits.rs index 870f9a396ae2..d982ed961674 100644 --- a/compiler/rustc_const_eval/src/interpret/traits.rs +++ b/compiler/rustc_const_eval/src/interpret/traits.rs @@ -109,7 +109,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { expected_trait: &'tcx ty::List>, ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { assert!( - matches!(mplace.layout.ty.kind(), ty::Dynamic(_, _, ty::Dyn)), + matches!(mplace.layout.ty.kind(), ty::Dynamic(_, _)), "`unpack_dyn_trait` only makes sense on `dyn*` types" ); let vtable = mplace.meta().unwrap_meta().to_pointer(self)?; diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 02e3d90f4af7..9adc3fa46318 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -449,7 +449,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { ) -> InterpResult<'tcx> { let tail = self.ecx.tcx.struct_tail_for_codegen(pointee.ty, self.ecx.typing_env); match tail.kind() { - ty::Dynamic(data, _, ty::Dyn) => { + ty::Dynamic(data, _) => { let vtable = meta.unwrap_meta().to_pointer(self.ecx)?; // Make sure it is a genuine vtable pointer for the right trait. try_validation!( diff --git a/compiler/rustc_const_eval/src/interpret/visitor.rs b/compiler/rustc_const_eval/src/interpret/visitor.rs index 82c50fac6c0e..b5de10c7dcd1 100644 --- a/compiler/rustc_const_eval/src/interpret/visitor.rs +++ b/compiler/rustc_const_eval/src/interpret/visitor.rs @@ -90,7 +90,7 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized { // Special treatment for special types, where the (static) layout is not sufficient. match *ty.kind() { // If it is a trait object, switch to the real type that was used to create it. - ty::Dynamic(data, _, ty::Dyn) => { + ty::Dynamic(data, _) => { // Dyn types. This is unsized, and the actual dynamic type of the data is given by the // vtable stored in the place metadata. // unsized values are never immediate, so we can assert_mem_place diff --git a/compiler/rustc_const_eval/src/util/type_name.rs b/compiler/rustc_const_eval/src/util/type_name.rs index 5bcf96abd8cb..db651811551f 100644 --- a/compiler/rustc_const_eval/src/util/type_name.rs +++ b/compiler/rustc_const_eval/src/util/type_name.rs @@ -41,7 +41,7 @@ impl<'tcx> Printer<'tcx> for TypeNamePrinter<'tcx> { | ty::FnPtr(..) | ty::Never | ty::Tuple(_) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::UnsafeBinder(_) => self.pretty_print_type(ty), // Placeholders (all printed as `_` to uniformize them). diff --git a/compiler/rustc_hir_analysis/src/collect/dump.rs b/compiler/rustc_hir_analysis/src/collect/dump.rs index c3f965d84569..44cc2dec1cb5 100644 --- a/compiler/rustc_hir_analysis/src/collect/dump.rs +++ b/compiler/rustc_hir_analysis/src/collect/dump.rs @@ -152,7 +152,7 @@ pub(crate) fn vtables<'tcx>(tcx: TyCtxt<'tcx>) { ); continue; }; - let ty::Dynamic(data, _, _) = *ty.kind() else { + let ty::Dynamic(data, _) = *ty.kind() else { tcx.dcx() .span_err(attr.span(), "`rustc_dump_vtable` to type alias of dyn type"); continue; diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs index 76bb59e3f090..c823a4a0430f 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs @@ -6,7 +6,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_lint_defs::builtin::UNUSED_ASSOCIATED_TYPE_BOUNDS; use rustc_middle::ty::elaborate::ClauseWithSupertraitSpan; use rustc_middle::ty::{ - self, BottomUpFolder, DynKind, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeFoldable, + self, BottomUpFolder, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, Upcast, }; use rustc_span::{ErrorGuaranteed, Span}; @@ -28,7 +28,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir_id: hir::HirId, hir_bounds: &[hir::PolyTraitRef<'tcx>], lifetime: &hir::Lifetime, - representation: DynKind, ) -> Ty<'tcx> { let tcx = self.tcx(); let dummy_self = tcx.types.trait_object_dummy_self; @@ -431,7 +430,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }; debug!(?region_bound); - Ty::new_dynamic(tcx, existential_predicates, region_bound, representation) + Ty::new_dynamic(tcx, existential_predicates, region_bound) } /// Check that elaborating the principal of a trait ref doesn't lead to projections diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 0cf9cb7193f7..165051744641 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -859,7 +859,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { bound_spans.get_mut_or_insert_default(tcx.def_span(def.did())).push(msg) } // Point at the trait object that couldn't satisfy the bound. - ty::Dynamic(preds, _, _) => { + ty::Dynamic(preds, _) => { for pred in preds.iter() { match pred.skip_binder() { ty::ExistentialPredicate::Trait(tr) => { diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index c5e079fe89ac..b64129dc54e6 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -23,7 +23,6 @@ mod lint; use std::assert_matches::assert_matches; use std::slice; -use rustc_ast::TraitObjectSyntax; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{ @@ -2428,7 +2427,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ), hir::TyKind::TraitObject(bounds, tagged_ptr) => { let lifetime = tagged_ptr.pointer(); - let repr = tagged_ptr.tag(); if let Some(guar) = self.prohibit_or_lint_bare_trait_object_ty(hir_ty) { // Don't continue with type analysis if the `dyn` keyword is missing @@ -2436,10 +2434,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // keyword like `impl` Ty::new_error(tcx, guar) } else { - let repr = match repr { - TraitObjectSyntax::Dyn | TraitObjectSyntax::None => ty::Dyn, - }; - self.lower_trait_object_ty(hir_ty.span, hir_ty.hir_id, bounds, lifetime, repr) + self.lower_trait_object_ty(hir_ty.span, hir_ty.hir_id, bounds, lifetime) } } // If we encounter a fully qualified path with RTN generics, then it must have diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index 960ec7f66ab1..be841675821c 100644 --- a/compiler/rustc_hir_analysis/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs @@ -281,7 +281,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { self.add_constraints_from_args(current, data.def_id, data.args, variance); } - ty::Dynamic(data, r, _) => { + ty::Dynamic(data, r) => { // The type `dyn Trait +'a` is covariant w/r/t `'a`: self.add_constraints_from_region(current, r, variance); diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 27540fd1a438..40b21c45bc56 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -103,7 +103,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok(match *t.kind() { ty::Slice(_) | ty::Str => Some(PointerKind::Length), - ty::Dynamic(tty, _, ty::Dyn) => Some(PointerKind::VTable(tty)), + ty::Dynamic(tty, _) => Some(PointerKind::VTable(tty)), ty::Adt(def, args) if def.is_struct() => match def.non_enum_variant().tail_opt() { None => Some(PointerKind::Thin), Some(f) => { @@ -250,9 +250,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { // cases now. We do a more thorough check at the end, once // inference is more completely known. match cast_ty.kind() { - ty::Dynamic(_, _, ty::Dyn) | ty::Slice(..) => { - Err(check.report_cast_to_unsized_type(fcx)) - } + ty::Dynamic(_, _) | ty::Slice(..) => Err(check.report_cast_to_unsized_type(fcx)), _ => Ok(check), } } @@ -900,7 +898,6 @@ impl<'a, 'tcx> CastCheck<'tcx> { &src_tty.without_auto_traits().collect::>(), ), tcx.lifetimes.re_erased, - ty::Dyn, ); let dst_obj = Ty::new_dynamic( tcx, @@ -908,7 +905,6 @@ impl<'a, 'tcx> CastCheck<'tcx> { &dst_tty.without_auto_traits().collect::>(), ), tcx.lifetimes.re_erased, - ty::Dyn, ); // `dyn Src = dyn Dst`, this checks for matching traits/generics/projections diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index b99f811db1a1..ced2cf2b57b1 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -558,7 +558,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { | ty::Slice(_) | ty::FnDef(_, _) | ty::FnPtr(_, _) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Closure(_, _) | ty::CoroutineClosure(_, _) | ty::Coroutine(_, _) diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index c8f6c06b720d..a39ac0fcb6e3 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -1008,7 +1008,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { bound_spans.get_mut_or_insert_default(tcx.def_span(def.did())).push(msg) } // Point at the trait object that couldn't satisfy the bound. - ty::Dynamic(preds, _, _) => { + ty::Dynamic(preds, _) => { for pred in preds.iter() { match pred.skip_binder() { ty::ExistentialPredicate::Trait(tr) => { diff --git a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs index e1c51ff82990..703f757abd50 100644 --- a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs +++ b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs @@ -66,11 +66,11 @@ impl<'tcx> LateLintPass<'tcx> for DerefIntoDynSupertrait { && tcx.is_lang_item(did, LangItem::Deref) // the self type is `dyn t_principal` && let self_ty = tcx.type_of(item.owner_id).instantiate_identity() - && let ty::Dynamic(data, _, ty::Dyn) = self_ty.kind() + && let ty::Dynamic(data, _) = self_ty.kind() && let Some(self_principal) = data.principal() // `::Target` is `dyn target_principal` && let Some(target) = cx.get_associated_type(self_ty, did, sym::Target) - && let ty::Dynamic(data, _, ty::Dyn) = target.kind() + && let ty::Dynamic(data, _) = target.kind() && let Some(target_principal) = data.principal() // `target_principal` is a supertrait of `t_principal` && let Some(supertrait_principal) = supertraits(tcx, self_principal.with_self_ty(tcx, self_ty)) diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 88527fa2e6e7..eaec0c9857d2 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -320,7 +320,7 @@ fn lint_wide_pointer<'tcx>( }; (!ty.is_sized(cx.tcx, cx.typing_env())) - .then(|| (refs, modifiers, matches!(ty.kind(), ty::Dynamic(_, _, ty::Dyn)))) + .then(|| (refs, modifiers, matches!(ty.kind(), ty::Dynamic(_, _)))) }; // the left and right operands can have references, remove any explicit references diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 22d89d246127..95c5bea6be89 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -312,7 +312,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { }) .map(|inner| MustUsePath::Opaque(Box::new(inner))) } - ty::Dynamic(binders, _, _) => binders.iter().find_map(|predicate| { + ty::Dynamic(binders, _) => binders.iter().find_map(|predicate| { if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() { let def_id = trait_ref.def_id; diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 79700d485c44..7d3e2c9965da 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -574,7 +574,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { | ty::Ref(_, _, _) | ty::FnDef(_, _) | ty::FnPtr(..) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(_, _) diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 2114d080dfa4..9524057eebc6 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -812,7 +812,7 @@ where | ty::CoroutineWitness(..) | ty::Foreign(..) | ty::Pat(_, _) - | ty::Dynamic(_, _, ty::Dyn) => { + | ty::Dynamic(_, _) => { bug!("TyAndLayout::field({:?}): not applicable", this) } @@ -878,7 +878,7 @@ where // `std::mem::uninitialized::<&dyn Trait>()`, for example. if let ty::Adt(def, args) = metadata.kind() && tcx.is_lang_item(def.did(), LangItem::DynMetadata) - && let ty::Dynamic(data, _, ty::Dyn) = args.type_at(0).kind() + && let ty::Dynamic(data, _) = args.type_at(0).kind() { mk_dyn_vtable(data.principal()) } else { @@ -887,7 +887,7 @@ where } else { match tcx.struct_tail_for_codegen(pointee, cx.typing_env()).kind() { ty::Slice(_) | ty::Str => tcx.types.usize, - ty::Dynamic(data, _, ty::Dyn) => mk_dyn_vtable(data.principal()), + ty::Dynamic(data, _) => mk_dyn_vtable(data.principal()), _ => bug!("TyAndLayout::field({:?}): not applicable", this), } }; diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index fc821ffdaa68..1b7ef8de8454 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -784,14 +784,12 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { }, }, ty::Adt(def, args) => self.print_def_path(def.did(), args)?, - ty::Dynamic(data, r, repr) => { + ty::Dynamic(data, r) => { let print_r = self.should_print_optional_region(r); if print_r { write!(self, "(")?; } - match repr { - ty::Dyn => write!(self, "dyn ")?, - } + write!(self, "dyn ")?; data.print(self)?; if print_r { write!(self, " + ")?; diff --git a/compiler/rustc_middle/src/ty/significant_drop_order.rs b/compiler/rustc_middle/src/ty/significant_drop_order.rs index 5ada9ecc80cf..f1aa7076d98a 100644 --- a/compiler/rustc_middle/src/ty/significant_drop_order.rs +++ b/compiler/rustc_middle/src/ty/significant_drop_order.rs @@ -132,7 +132,7 @@ pub fn ty_dtor_span<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option { | ty::Ref(_, _, _) | ty::FnPtr(_, _) | ty::Tuple(_) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Alias(_, _) | ty::Bound(_, _) | ty::Pat(_, _) diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 89ef46b1ae57..11d109b463d9 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -390,11 +390,9 @@ impl<'tcx> TypeSuperFoldable> for Ty<'tcx> { ty::Array(typ, sz) => ty::Array(typ.try_fold_with(folder)?, sz.try_fold_with(folder)?), ty::Slice(typ) => ty::Slice(typ.try_fold_with(folder)?), ty::Adt(tid, args) => ty::Adt(tid, args.try_fold_with(folder)?), - ty::Dynamic(trait_ty, region, representation) => ty::Dynamic( - trait_ty.try_fold_with(folder)?, - region.try_fold_with(folder)?, - representation, - ), + ty::Dynamic(trait_ty, region) => { + ty::Dynamic(trait_ty.try_fold_with(folder)?, region.try_fold_with(folder)?) + } ty::Tuple(ts) => ty::Tuple(ts.try_fold_with(folder)?), ty::FnDef(def_id, args) => ty::FnDef(def_id, args.try_fold_with(folder)?), ty::FnPtr(sig_tys, hdr) => ty::FnPtr(sig_tys.try_fold_with(folder)?, hdr), @@ -437,8 +435,8 @@ impl<'tcx> TypeSuperFoldable> for Ty<'tcx> { ty::Array(typ, sz) => ty::Array(typ.fold_with(folder), sz.fold_with(folder)), ty::Slice(typ) => ty::Slice(typ.fold_with(folder)), ty::Adt(tid, args) => ty::Adt(tid, args.fold_with(folder)), - ty::Dynamic(trait_ty, region, representation) => { - ty::Dynamic(trait_ty.fold_with(folder), region.fold_with(folder), representation) + ty::Dynamic(trait_ty, region) => { + ty::Dynamic(trait_ty.fold_with(folder), region.fold_with(folder)) } ty::Tuple(ts) => ty::Tuple(ts.fold_with(folder)), ty::FnDef(def_id, args) => ty::FnDef(def_id, args.fold_with(folder)), @@ -481,7 +479,7 @@ impl<'tcx> TypeSuperVisitable> for Ty<'tcx> { } ty::Slice(typ) => typ.visit_with(visitor), ty::Adt(_, args) => args.visit_with(visitor), - ty::Dynamic(trait_ty, reg, _) => { + ty::Dynamic(trait_ty, reg) => { try_visit!(trait_ty.visit_with(visitor)); reg.visit_with(visitor) } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 755fc68d86f3..2bea79779995 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -17,7 +17,7 @@ use rustc_span::{DUMMY_SP, Span, Symbol, sym}; use rustc_type_ir::TyKind::*; use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::walk::TypeWalker; -use rustc_type_ir::{self as ir, BoundVar, CollectAndApply, DynKind, TypeVisitableExt, elaborate}; +use rustc_type_ir::{self as ir, BoundVar, CollectAndApply, TypeVisitableExt, elaborate}; use tracing::instrument; use ty::util::IntTypeExt; @@ -734,7 +734,6 @@ impl<'tcx> Ty<'tcx> { tcx: TyCtxt<'tcx>, obj: &'tcx List>, reg: ty::Region<'tcx>, - repr: DynKind, ) -> Ty<'tcx> { if cfg!(debug_assertions) { let projection_count = obj @@ -767,7 +766,7 @@ impl<'tcx> Ty<'tcx> { but it has {projection_count}" ); } - Ty::new(tcx, Dynamic(obj, reg, repr)) + Ty::new(tcx, Dynamic(obj, reg)) } #[inline] @@ -980,9 +979,8 @@ impl<'tcx> rustc_type_ir::inherent::Ty> for Ty<'tcx> { interner: TyCtxt<'tcx>, preds: &'tcx List>, region: ty::Region<'tcx>, - kind: ty::DynKind, ) -> Self { - Ty::new_dynamic(interner, preds, region, kind) + Ty::new_dynamic(interner, preds, region) } fn new_coroutine( @@ -1356,7 +1354,7 @@ impl<'tcx> Ty<'tcx> { #[inline] pub fn is_trait(self) -> bool { - matches!(self.kind(), Dynamic(_, _, ty::Dyn)) + matches!(self.kind(), Dynamic(_, _)) } #[inline] @@ -1671,7 +1669,7 @@ impl<'tcx> Ty<'tcx> { ty::Str | ty::Slice(_) => Ok(tcx.types.usize), - ty::Dynamic(_, _, ty::Dyn) => { + ty::Dynamic(_, _) => { let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, DUMMY_SP); Ok(tcx.type_of(dyn_metadata).instantiate(tcx, &[tail.into()])) } @@ -1853,7 +1851,7 @@ impl<'tcx> Ty<'tcx> { | ty::Never | ty::Error(_) => true, - ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::Dyn) => match sizedness { + ty::Str | ty::Slice(_) | ty::Dynamic(_, _) => match sizedness { SizedTraitKind::Sized => false, SizedTraitKind::MetaSized => true, }, diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 48718cad597a..18f41c8b97f6 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -152,7 +152,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { | ty::Slice(_) | ty::FnDef(_, _) | ty::FnPtr(..) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(_, _) @@ -196,7 +196,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { | ty::Ref(_, _, _) | ty::FnDef(_, _) | ty::FnPtr(..) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::CoroutineWitness(..) | ty::Never | ty::UnsafeBinder(_) diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index 4603c695dedd..d3cc23818c7e 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -1891,7 +1891,7 @@ fn check_must_not_suspend_ty<'tcx>( } has_emitted } - ty::Dynamic(binder, _, _) => { + ty::Dynamic(binder, _) => { let mut has_emitted = false; for predicate in binder.iter() { if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() { diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index a8f2b4e8db66..4b4ec4956eb8 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -390,7 +390,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { | ty::FnDef(_, _) | ty::FnPtr(..) | ty::UnsafeBinder(_) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(_, _) diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index fb777496e31e..7de7870bbb12 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -86,7 +86,7 @@ where ) -> Result, NoSolution> { Self::probe_and_match_goal_against_assumption(ecx, source, goal, assumption, |ecx| { let cx = ecx.cx(); - let ty::Dynamic(bounds, _, _) = goal.predicate.self_ty().kind() else { + let ty::Dynamic(bounds, _) = goal.predicate.self_ty().kind() else { panic!("expected object type in `probe_and_consider_object_bound_candidate`"); }; match structural_traits::predicates_for_object_candidate( diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index f6eab286ba70..c40739d12e68 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -383,7 +383,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable( | ty::Slice(_) | ty::RawPtr(_, _) | ty::Ref(_, _, _) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Coroutine(_, _) | ty::CoroutineWitness(..) | ty::Never diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index 54b92ebac1de..653c59c5d424 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -665,7 +665,7 @@ where ty::Str | ty::Slice(_) => Ty::new_usize(cx), - ty::Dynamic(_, _, ty::Dyn) => { + ty::Dynamic(_, _) => { let dyn_metadata = cx.require_lang_item(SolverLangItem::DynMetadata); cx.type_of(dyn_metadata) .instantiate(cx, &[I::GenericArg::from(goal.predicate.self_ty())]) @@ -916,7 +916,7 @@ where | ty::Adt(_, _) | ty::Str | ty::Slice(_) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Tuple(_) | ty::Error(_) => self_ty.discriminant_ty(ecx.cx()), diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index a69e867289ce..3974114e9b43 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -817,15 +817,13 @@ where } // Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`. - ( - ty::Dynamic(a_data, a_region, ty::Dyn), - ty::Dynamic(b_data, b_region, ty::Dyn), - ) => ecx.consider_builtin_dyn_upcast_candidates( - goal, a_data, a_region, b_data, b_region, - ), + (ty::Dynamic(a_data, a_region), ty::Dynamic(b_data, b_region)) => ecx + .consider_builtin_dyn_upcast_candidates( + goal, a_data, a_region, b_data, b_region, + ), // `T` -> `dyn Trait` unsizing. - (_, ty::Dynamic(b_region, b_data, ty::Dyn)) => result_to_single( + (_, ty::Dynamic(b_region, b_data)) => result_to_single( ecx.consider_builtin_unsize_to_dyn_candidate(goal, b_region, b_data), ), diff --git a/compiler/rustc_passes/src/check_export.rs b/compiler/rustc_passes/src/check_export.rs index 6eded3a9eb9a..fee920221e1d 100644 --- a/compiler/rustc_passes/src/check_export.rs +++ b/compiler/rustc_passes/src/check_export.rs @@ -295,7 +295,7 @@ impl<'tcx, 'a> TypeVisitor> for ExportableItemsChecker<'tcx, 'a> { | ty::Ref(_, _, _) | ty::Param(_) | ty::Closure(_, _) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Coroutine(_, _) | ty::Foreign(_) | ty::Str diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index c9bf4fe4449b..d9f8085083eb 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -407,7 +407,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { | ty::FnDef(_, _) | ty::FnPtr(..) | ty::Pat(_, _) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(_, _) diff --git a/compiler/rustc_public/src/ty.rs b/compiler/rustc_public/src/ty.rs index bcc77ff849da..0afb94c18d7b 100644 --- a/compiler/rustc_public/src/ty.rs +++ b/compiler/rustc_public/src/ty.rs @@ -333,7 +333,7 @@ impl TyKind { #[inline] pub fn is_trait(&self) -> bool { - matches!(self, TyKind::RigidTy(RigidTy::Dynamic(_, _, DynKind::Dyn))) + matches!(self, TyKind::RigidTy(RigidTy::Dynamic(_, _))) } #[inline] @@ -472,7 +472,7 @@ impl TyKind { } pub fn trait_principal(&self) -> Option> { - if let TyKind::RigidTy(RigidTy::Dynamic(predicates, _, _)) = self { + if let TyKind::RigidTy(RigidTy::Dynamic(predicates, _)) = self { if let Some(Binder { value: ExistentialPredicate::Trait(trait_ref), bound_vars }) = predicates.first() { @@ -562,7 +562,7 @@ pub enum RigidTy { Closure(ClosureDef, GenericArgs), Coroutine(CoroutineDef, GenericArgs), CoroutineClosure(CoroutineClosureDef, GenericArgs), - Dynamic(Vec>, Region, DynKind), + Dynamic(Vec>, Region), Never, Tuple(Vec), CoroutineWitness(CoroutineWitnessDef, GenericArgs), @@ -1206,11 +1206,6 @@ pub enum BoundRegionKind { BrEnv, } -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -pub enum DynKind { - Dyn, -} - #[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum ExistentialPredicate { Trait(ExistentialTraitRef), diff --git a/compiler/rustc_public/src/unstable/convert/internal.rs b/compiler/rustc_public/src/unstable/convert/internal.rs index 66f767a98f5b..dc9abd88614d 100644 --- a/compiler/rustc_public/src/unstable/convert/internal.rs +++ b/compiler/rustc_public/src/unstable/convert/internal.rs @@ -14,7 +14,7 @@ use crate::mir::alloc::AllocId; use crate::mir::mono::{Instance, MonoItem, StaticDef}; use crate::mir::{BinOp, Mutability, Place, ProjectionElem, RawPtrKind, Safety, UnOp}; use crate::ty::{ - Abi, AdtDef, Binder, BoundRegionKind, BoundTyKind, BoundVariableKind, ClosureKind, DynKind, + Abi, AdtDef, Binder, BoundRegionKind, BoundTyKind, BoundVariableKind, ClosureKind, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FloatTy, FnSig, GenericArgKind, GenericArgs, IntTy, MirConst, Movability, Pattern, Region, RigidTy, Span, TermKind, TraitRef, Ty, TyConst, UintTy, VariantDef, VariantIdx, @@ -188,10 +188,9 @@ impl RustcInternal for RigidTy { def.0.internal(tables, tcx), args.internal(tables, tcx), ), - RigidTy::Dynamic(predicate, region, dyn_kind) => rustc_ty::TyKind::Dynamic( + RigidTy::Dynamic(predicate, region) => rustc_ty::TyKind::Dynamic( tcx.mk_poly_existential_predicates(&predicate.internal(tables, tcx)), region.internal(tables, tcx), - dyn_kind.internal(tables, tcx), ), RigidTy::Tuple(tys) => { rustc_ty::TyKind::Tuple(tcx.mk_type_list(&tys.internal(tables, tcx))) @@ -460,20 +459,6 @@ impl RustcInternal for BoundVariableKind { } } -impl RustcInternal for DynKind { - type T<'tcx> = rustc_ty::DynKind; - - fn internal<'tcx>( - &self, - _tables: &mut Tables<'_, BridgeTys>, - _tcx: impl InternalCx<'tcx>, - ) -> Self::T<'tcx> { - match self { - DynKind::Dyn => rustc_ty::DynKind::Dyn, - } - } -} - impl RustcInternal for ExistentialPredicate { type T<'tcx> = rustc_ty::ExistentialPredicate<'tcx>; diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index 5131611eb02b..7f14f878d373 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -48,16 +48,6 @@ impl<'tcx> Stable<'tcx> for ty::AliasTerm<'tcx> { } } -impl<'tcx> Stable<'tcx> for ty::DynKind { - type T = crate::ty::DynKind; - - fn stable(&self, _: &mut Tables<'_, BridgeTys>, _: &CompilerCtxt<'_, BridgeTys>) -> Self::T { - match self { - ty::Dyn => crate::ty::DynKind::Dyn, - } - } -} - impl<'tcx> Stable<'tcx> for ty::ExistentialPredicate<'tcx> { type T = crate::ty::ExistentialPredicate; @@ -439,16 +429,13 @@ impl<'tcx> Stable<'tcx> for ty::TyKind<'tcx> { } // FIXME(unsafe_binders): ty::UnsafeBinder(_) => todo!(), - ty::Dynamic(existential_predicates, region, dyn_kind) => { - TyKind::RigidTy(RigidTy::Dynamic( - existential_predicates - .iter() - .map(|existential_predicate| existential_predicate.stable(tables, cx)) - .collect(), - region.stable(tables, cx), - dyn_kind.stable(tables, cx), - )) - } + ty::Dynamic(existential_predicates, region) => TyKind::RigidTy(RigidTy::Dynamic( + existential_predicates + .iter() + .map(|existential_predicate| existential_predicate.stable(tables, cx)) + .collect(), + region.stable(tables, cx), + )), ty::Closure(def_id, generic_args) => TyKind::RigidTy(RigidTy::Closure( tables.closure_def(*def_id), generic_args.stable(tables, cx), diff --git a/compiler/rustc_public/src/visitor.rs b/compiler/rustc_public/src/visitor.rs index 87f1cc6ae69d..acc333476961 100644 --- a/compiler/rustc_public/src/visitor.rs +++ b/compiler/rustc_public/src/visitor.rs @@ -171,7 +171,7 @@ impl Visitable for RigidTy { | RigidTy::CoroutineClosure(_, args) | RigidTy::FnDef(_, args) => args.visit(visitor), RigidTy::FnPtr(sig) => sig.visit(visitor), - RigidTy::Dynamic(pred, r, _) => { + RigidTy::Dynamic(pred, r) => { pred.visit(visitor)?; r.visit(visitor) } diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs index f4a14b36ce5c..ec7a4a81a71a 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs @@ -626,12 +626,10 @@ pub(crate) fn encode_ty<'tcx>( } // Trait types - ty::Dynamic(predicates, region, kind) => { + ty::Dynamic(predicates, region) => { // u3dynIE, where is , as // vendor extended type. - let mut s = String::from(match kind { - ty::Dyn => "u3dynI", - }); + let mut s = String::from("u3dynI"); s.push_str(&encode_predicates(tcx, predicates, dict, options)); s.push_str(&encode_region(*region, dict)); s.push('E'); diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs index 577a16a0d257..82a2a64f2307 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs @@ -268,7 +268,7 @@ fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tc let preds = tcx.mk_poly_existential_predicates_from_iter( iter::once(principal_pred).chain(assoc_preds.into_iter()), ); - Ty::new_dynamic(tcx, preds, tcx.lifetimes.re_erased, ty::Dyn) + Ty::new_dynamic(tcx, preds, tcx.lifetimes.re_erased) } /// Transforms an instance for LLVM CFI and cross-language LLVM CFI support using Itanium C++ ABI @@ -334,7 +334,7 @@ pub(crate) fn transform_instance<'tcx>( ty::List::empty(), )); let predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(predicate)]); - let self_ty = Ty::new_dynamic(tcx, predicates, tcx.lifetimes.re_erased, ty::Dyn); + let self_ty = Ty::new_dynamic(tcx, predicates, tcx.lifetimes.re_erased); instance.args = tcx.mk_args_trait(self_ty, List::empty()); } else if let ty::InstanceKind::Virtual(def_id, _) = instance.def { // Transform self into a trait object of the trait that defines the method for virtual @@ -347,7 +347,7 @@ pub(crate) fn transform_instance<'tcx>( // drop_in_place won't have a defining trait, skip the upcast None => instance.args.type_at(0), }; - let ty::Dynamic(preds, lifetime, kind) = upcast_ty.kind() else { + let ty::Dynamic(preds, lifetime) = upcast_ty.kind() else { bug!("Tried to remove autotraits from non-dynamic type {upcast_ty}"); }; let self_ty = if preds.principal().is_some() { @@ -355,7 +355,7 @@ pub(crate) fn transform_instance<'tcx>( tcx.mk_poly_existential_predicates_from_iter(preds.into_iter().filter(|pred| { !matches!(pred.skip_binder(), ty::ExistentialPredicate::AutoTrait(..)) })); - Ty::new_dynamic(tcx, filtered_preds, *lifetime, *kind) + Ty::new_dynamic(tcx, filtered_preds, *lifetime) } else { // If there's no principal type, re-encode it as a unit, since we don't know anything // about it. This technically discards the knowledge that it was a type that was made diff --git a/compiler/rustc_symbol_mangling/src/export.rs b/compiler/rustc_symbol_mangling/src/export.rs index 76ac82cf95a1..3896e06a627b 100644 --- a/compiler/rustc_symbol_mangling/src/export.rs +++ b/compiler/rustc_symbol_mangling/src/export.rs @@ -110,7 +110,7 @@ impl<'tcx> AbiHashStable<'tcx> for Ty<'tcx> { | ty::RawPtr(_, _) | ty::FnDef(_, _) | ty::FnPtr(_, _) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Closure(_, _) | ty::CoroutineClosure(_, _) | ty::Coroutine(_, _) diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 1605b4958ba3..9fa7e2f10039 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -577,10 +577,8 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> { // FIXME(unsafe_binder): ty::UnsafeBinder(..) => todo!(), - ty::Dynamic(predicates, r, kind) => { - self.push(match kind { - ty::Dyn => "D", - }); + ty::Dynamic(predicates, r) => { + self.push("D"); self.print_dyn_existential(predicates)?; r.print(self)?; } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs index 3edc365c886e..5b8315841765 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs @@ -399,7 +399,7 @@ pub struct TraitObjectVisitor(pub FxIndexSet); impl<'tcx> TypeVisitor> for TraitObjectVisitor { fn visit_ty(&mut self, t: Ty<'tcx>) { match t.kind() { - ty::Dynamic(preds, re, _) if re.is_static() => { + ty::Dynamic(preds, re) if re.is_static() => { if let Some(def_id) = preds.principal_def_id() { self.0.insert(def_id); } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs index f997842a607f..b0b858aa270c 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs @@ -292,7 +292,7 @@ impl Trait for X { ); } } - (ty::Dynamic(t, _, ty::DynKind::Dyn), ty::Alias(ty::Opaque, alias)) + (ty::Dynamic(t, _), ty::Alias(ty::Opaque, alias)) if let Some(def_id) = t.principal_def_id() && tcx .explicit_item_self_bounds(alias.def_id) @@ -314,9 +314,7 @@ impl Trait for X { values.found, values.expected, )); } - (ty::Dynamic(t, _, ty::DynKind::Dyn), _) - if let Some(def_id) = t.principal_def_id() => - { + (ty::Dynamic(t, _), _) if let Some(def_id) = t.principal_def_id() => { let mut has_matching_impl = false; tcx.for_each_relevant_impl(def_id, values.found, |did| { if DeepRejectCtxt::relate_rigid_infer(tcx) @@ -335,9 +333,7 @@ impl Trait for X { )); } } - (_, ty::Dynamic(t, _, ty::DynKind::Dyn)) - if let Some(def_id) = t.principal_def_id() => - { + (_, ty::Dynamic(t, _)) if let Some(def_id) = t.principal_def_id() => { let mut has_matching_impl = false; tcx.for_each_relevant_impl(def_id, values.expected, |did| { if DeepRejectCtxt::relate_rigid_infer(tcx) @@ -489,7 +485,7 @@ impl Trait for X { && let Some(then) = blk.expr && def.is_box() && let boxed_ty = args.type_at(0) - && let ty::Dynamic(t, _, _) = boxed_ty.kind() + && let ty::Dynamic(t, _) = boxed_ty.kind() && let Some(def_id) = t.principal_def_id() && let mut impl_def_ids = vec![] && let _ = diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs index 518d4fe17e8f..9a8ccea3aca8 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs @@ -430,7 +430,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let mut alt_span = None; if let Some(ty) = ty && sub.is_static() - && let ty::Dynamic(preds, _, ty::DynKind::Dyn) = ty.kind() + && let ty::Dynamic(preds, _) = ty.kind() && let Some(def_id) = preds.principal_def_id() { for (clause, span) in diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs index f794ff632c59..b3d1b8e3888a 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -554,7 +554,7 @@ fn attempt_dyn_to_enum_suggestion( // defaults to assuming that things are *not* sized, whereas we want to // fall back to assuming that things may be sized. match impl_type.kind() { - ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::DynKind::Dyn) => { + ty::Str | ty::Slice(_) | ty::Dynamic(_, _) => { return None; } _ => {} diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index bb5c6469f34b..00c123981e1c 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -237,7 +237,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } } - if let ty::Dynamic(traits, _, _) = self_ty.kind() { + if let ty::Dynamic(traits, _) = self_ty.kind() { for t in traits.iter() { if let ty::ExistentialPredicate::Trait(trait_ref) = t.skip_binder() { self_types.push(self.tcx.def_path_str(trait_ref.def_id)); diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index f2f840581cf0..37e622102e70 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -1131,7 +1131,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }, ) } - ty::Dynamic(data, _, ty::Dyn) => data.iter().find_map(|pred| { + ty::Dynamic(data, _) => data.iter().find_map(|pred| { if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder() && self.tcx.is_lang_item(proj.def_id, LangItem::FnOnceOutput) // for existential projection, args are shifted over by 1 @@ -1520,7 +1520,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let ty::Ref(_, object_ty, hir::Mutability::Not) = target_ty.kind() else { return; }; - let ty::Dynamic(predicates, _, ty::Dyn) = object_ty.kind() else { + let ty::Dynamic(predicates, _) = object_ty.kind() else { return; }; let self_ref_ty = Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, self_ty); @@ -1883,7 +1883,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let ObligationCauseCode::SizedReturnType = obligation.cause.code() else { return false; }; - let ty::Dynamic(_, _, ty::Dyn) = trait_pred.self_ty().skip_binder().kind() else { + let ty::Dynamic(_, _) = trait_pred.self_ty().skip_binder().kind() else { return false; }; diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 62795c8a3a68..60f1fcb26c00 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -669,7 +669,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // These may potentially implement `FnPtr` ty::Placeholder(..) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Alias(_, _) | ty::Infer(_) | ty::Param(..) @@ -991,7 +991,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match (source.kind(), target.kind()) { // Trait+Kx+'a -> Trait+Ky+'b (upcasts). - (&ty::Dynamic(a_data, a_region, ty::Dyn), &ty::Dynamic(b_data, b_region, ty::Dyn)) => { + (&ty::Dynamic(a_data, a_region), &ty::Dynamic(b_data, b_region)) => { // Upcast coercions permit several things: // // 1. Dropping auto traits, e.g., `Foo + Send` to `Foo` @@ -1054,7 +1054,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } // `T` -> `Trait` - (_, &ty::Dynamic(_, _, ty::Dyn)) => { + (_, &ty::Dynamic(_, _)) => { candidates.vec.push(BuiltinUnsizeCandidate); } @@ -1327,7 +1327,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Pat(_, _) | ty::FnPtr(..) | ty::UnsafeBinder(_) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(_, _) diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 488094b15ac6..7ad65a1df8e9 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -1023,10 +1023,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let a_ty = self.infcx.shallow_resolve(predicate.self_ty()); let b_ty = self.infcx.shallow_resolve(predicate.trait_ref.args.type_at(1)); - let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else { + let ty::Dynamic(a_data, a_region) = *a_ty.kind() else { bug!("expected `dyn` type in `confirm_trait_upcasting_unsize_candidate`") }; - let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else { + let ty::Dynamic(b_data, b_region) = *b_ty.kind() else { bug!("expected `dyn` type in `confirm_trait_upcasting_unsize_candidate`") }; @@ -1062,10 +1062,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!(?source, ?target, "confirm_builtin_unsize_candidate"); Ok(match (source.kind(), target.kind()) { - // Trait+Kx+'a -> Trait+Ky+'b (auto traits and lifetime subtyping). - (&ty::Dynamic(data_a, r_a, dyn_a), &ty::Dynamic(data_b, r_b, dyn_b)) - if dyn_a == dyn_b => - { + // `dyn Trait + Kx + 'a` -> `dyn Trait + Ky + 'b` (auto traits and lifetime subtyping). + (&ty::Dynamic(data_a, r_a), &ty::Dynamic(data_b, r_b)) => { // See `assemble_candidates_for_unsizing` for more info. // We already checked the compatibility of auto traits within `assemble_candidates_for_unsizing`. let existential_predicates = if data_b.principal().is_some() { @@ -1098,7 +1096,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .map(ty::Binder::dummy), ) }; - let source_trait = Ty::new_dynamic(tcx, existential_predicates, r_b, dyn_a); + let source_trait = Ty::new_dynamic(tcx, existential_predicates, r_b); // Require that the traits involved in this upcast are **equal**; // only the **lifetime bound** is changed. @@ -1122,7 +1120,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } // `T` -> `dyn Trait` - (_, &ty::Dynamic(data, r, ty::Dyn)) => { + (_, &ty::Dynamic(data, r)) => { let mut object_dids = data.auto_traits().chain(data.principal_def_id()); if let Some(did) = object_dids.find(|did| !tcx.is_dyn_compatible(*did)) { return Err(SelectionError::TraitDynIncompatible(did)); diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index 7e8a41457d41..584c8e2a27c8 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -317,7 +317,7 @@ pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRe "vtable trait ref should be normalized" ); - let ty::Dynamic(source, _, _) = *key.self_ty().kind() else { + let ty::Dynamic(source, _) = *key.self_ty().kind() else { bug!(); }; let source_principal = tcx.instantiate_bound_regions_with_erased( @@ -384,13 +384,13 @@ pub(crate) fn supertrait_vtable_slot<'tcx>( let (source, target) = key; // If the target principal is `None`, we can just return `None`. - let ty::Dynamic(target_data, _, _) = *target.kind() else { + let ty::Dynamic(target_data, _) = *target.kind() else { bug!(); }; let target_principal = tcx.instantiate_bound_regions_with_erased(target_data.principal()?); // Given that we have a target principal, it is a bug for there not to be a source principal. - let ty::Dynamic(source_data, _, _) = *source.kind() else { + let ty::Dynamic(source_data, _) = *source.kind() else { bug!(); }; let source_principal = tcx.instantiate_bound_regions_with_erased( diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index adce9850b594..45cbb56b1c2e 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -915,7 +915,7 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { // We recurse into the binder below. } - ty::Dynamic(data, r, _) => { + ty::Dynamic(data, r) => { // WfObject // // Here, we defer WF checking due to higher-ranked diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 79f7e228e2ad..643e3db8f83f 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -476,7 +476,7 @@ fn layout_of_uncached<'tcx>( } // Odd unit types. - ty::FnDef(..) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => { + ty::FnDef(..) | ty::Dynamic(_, _) | ty::Foreign(..) => { let sized = matches!(ty.kind(), ty::FnDef(..)); tcx.mk_layout(LayoutData::unit(cx, sized)) } diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index a5987757dc34..18a9a7c22d9d 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -40,7 +40,7 @@ fn sizedness_constraint_for_ty<'tcx>( | ty::CoroutineWitness(..) | ty::Never => None, - ty::Str | ty::Slice(..) | ty::Dynamic(_, _, ty::Dyn) => match sizedness { + ty::Str | ty::Slice(..) | ty::Dynamic(_, _) => match sizedness { // Never `Sized` SizedTraitKind::Sized => Some(ty), // Always `MetaSized` @@ -366,7 +366,7 @@ fn impl_self_is_guaranteed_unsized<'tcx>(tcx: TyCtxt<'tcx>, impl_def_id: DefId) ); match tail.kind() { - ty::Dynamic(_, _, ty::Dyn) | ty::Slice(_) | ty::Str => true, + ty::Dynamic(_, _) | ty::Slice(_) | ty::Str => true, ty::Bool | ty::Char | ty::Int(_) diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index 23b7f55fbbe5..24704c5bb535 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -288,7 +288,7 @@ impl FlagComputation { self.add_alias_ty(data); } - ty::Dynamic(obj, r, _) => { + ty::Dynamic(obj, r) => { for predicate in obj.iter() { self.bound_computation(predicate, |computation, predicate| match predicate { ty::ExistentialPredicate::Trait(tr) => { diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index ecfc05a6e20e..b5b552dbaec0 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -76,12 +76,7 @@ pub trait Ty>: fn new_foreign(interner: I, def_id: I::ForeignId) -> Self; - fn new_dynamic( - interner: I, - preds: I::BoundExistentialPredicates, - region: I::Region, - kind: ty::DynKind, - ) -> Self; + fn new_dynamic(interner: I, preds: I::BoundExistentialPredicates, region: I::Region) -> Self; fn new_coroutine(interner: I, def_id: I::CoroutineId, args: I::GenericArgs) -> Self; @@ -167,7 +162,7 @@ pub trait Ty>: fn is_guaranteed_unsized_raw(self) -> bool { match self.kind() { - ty::Dynamic(_, _, ty::Dyn) | ty::Slice(_) | ty::Str => true, + ty::Dynamic(_, _) | ty::Slice(_) | ty::Str => true, ty::Bool | ty::Char | ty::Int(_) diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index 50b588029aed..61e0b67b1639 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -57,7 +57,6 @@ mod upcast; mod visit; pub use AliasTyKind::*; -pub use DynKind::*; pub use InferTy::*; pub use RegionKind::*; pub use TyKind::*; diff --git a/compiler/rustc_type_ir/src/outlives.rs b/compiler/rustc_type_ir/src/outlives.rs index b09a378d3414..c7dccea6adc1 100644 --- a/compiler/rustc_type_ir/src/outlives.rs +++ b/compiler/rustc_type_ir/src/outlives.rs @@ -203,7 +203,7 @@ impl TypeVisitor for OutlivesCollector<'_, I> { | ty::Ref(_, _, _) | ty::FnPtr(..) | ty::UnsafeBinder(_) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Tuple(_) => { ty.super_visit_with(self); } diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index 690a5f65e084..09add529286d 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -412,16 +412,11 @@ pub fn structurally_relate_tys>( (ty::Foreign(a_id), ty::Foreign(b_id)) if a_id == b_id => Ok(Ty::new_foreign(cx, a_id)), - (ty::Dynamic(a_obj, a_region, a_repr), ty::Dynamic(b_obj, b_region, b_repr)) - if a_repr == b_repr => - { - Ok(Ty::new_dynamic( - cx, - relation.relate(a_obj, b_obj)?, - relation.relate(a_region, b_region)?, - a_repr, - )) - } + (ty::Dynamic(a_obj, a_region), ty::Dynamic(b_obj, b_region)) => Ok(Ty::new_dynamic( + cx, + relation.relate(a_obj, b_obj)?, + relation.relate(a_region, b_region)?, + )), (ty::Coroutine(a_id, a_args), ty::Coroutine(b_id, b_args)) if a_id == b_id => { // All Coroutine types with the same id represent diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 225d85f79c3d..dda59283677c 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -19,20 +19,6 @@ use crate::{self as ty, DebruijnIndex, FloatTy, IntTy, Interner, UintTy}; mod closure; -/// Specifies how a trait object is represented. -/// -/// This used to have a variant `DynStar`, but that variant has been removed, -/// and it's likely this whole enum will be removed soon. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -#[cfg_attr( - feature = "nightly", - derive(Encodable_NoContext, Decodable_NoContext, HashStable_NoContext) -)] -pub enum DynKind { - /// An unsized `dyn Trait` object - Dyn, -} - #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] #[cfg_attr( feature = "nightly", @@ -165,7 +151,7 @@ pub enum TyKind { UnsafeBinder(UnsafeBinderInner), /// A trait object. Written as `dyn for<'b> Trait<'b, Assoc = u32> + Send + 'a`. - Dynamic(I::BoundExistentialPredicates, I::Region, DynKind), + Dynamic(I::BoundExistentialPredicates, I::Region), /// The anonymous type of a closure. Used to represent the type of `|a| a`. /// @@ -314,7 +300,7 @@ impl TyKind { | ty::FnDef(_, _) | ty::FnPtr(..) | ty::UnsafeBinder(_) - | ty::Dynamic(_, _, _) + | ty::Dynamic(_, _) | ty::Closure(_, _) | ty::CoroutineClosure(_, _) | ty::Coroutine(_, _) @@ -367,9 +353,7 @@ impl fmt::Debug for TyKind { FnPtr(sig_tys, hdr) => write!(f, "{:?}", sig_tys.with(*hdr)), // FIXME(unsafe_binder): print this like `unsafe<'a> T<'a>`. UnsafeBinder(binder) => write!(f, "{:?}", binder), - Dynamic(p, r, repr) => match repr { - DynKind::Dyn => write!(f, "dyn {p:?} + {r:?}"), - }, + Dynamic(p, r) => write!(f, "dyn {p:?} + {r:?}"), Closure(d, s) => f.debug_tuple("Closure").field(d).field(&s).finish(), CoroutineClosure(d, s) => f.debug_tuple("CoroutineClosure").field(d).field(&s).finish(), Coroutine(d, s) => f.debug_tuple("Coroutine").field(d).field(&s).finish(), diff --git a/compiler/rustc_type_ir/src/walk.rs b/compiler/rustc_type_ir/src/walk.rs index 9912fad1756e..6d51817a7bf4 100644 --- a/compiler/rustc_type_ir/src/walk.rs +++ b/compiler/rustc_type_ir/src/walk.rs @@ -109,7 +109,7 @@ fn push_inner(stack: &mut TypeWalkerStack, parent: I::GenericArg ty::Alias(_, data) => { stack.extend(data.args.iter().rev()); } - ty::Dynamic(obj, lt, _) => { + ty::Dynamic(obj, lt) => { stack.push(lt.into()); stack.extend( obj.iter() diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 5ccacafea015..0afb969d5c84 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -2093,7 +2093,7 @@ pub(crate) fn clean_middle_ty<'tcx>( ); Type::Path { path } } - ty::Dynamic(obj, reg, _) => { + ty::Dynamic(obj, reg) => { // HACK: pick the first `did` as the `did` of the trait object. Someone // might want to implement "native" support for marker-trait-only // trait objects. diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 68f0b5ea2558..ac87e21b82f1 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -86,7 +86,7 @@ fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, msrv: Msrv) ty::FnPtr(..) => { return Err((span, "function pointers in const fn are unstable".into())); }, - ty::Dynamic(preds, _, _) => { + ty::Dynamic(preds, _) => { for pred in *preds { match pred.skip_binder() { ty::ExistentialPredicate::AutoTrait(_) | ty::ExistentialPredicate::Projection(_) => { diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs index 9f77a1c4d9b6..3e41bce1dc4c 100644 --- a/src/tools/clippy/clippy_utils/src/ty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs @@ -349,7 +349,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { } false }, - ty::Dynamic(binder, _, _) => { + ty::Dynamic(binder, _) => { for predicate in *binder { if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() && find_attr!(cx.tcx.get_all_attrs(trait_ref.def_id), AttributeKind::MustUse { .. }) @@ -673,7 +673,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option Some(ExprFnSig::Sig(sig_tys.with(hdr), None)), - ty::Dynamic(bounds, _, _) => { + ty::Dynamic(bounds, _) => { let lang_items = cx.tcx.lang_items(); match bounds.principal() { Some(bound) From 34062b124f00eb7c35da2e3981cfb5b45dd26039 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Wed, 17 Sep 2025 05:34:19 +0200 Subject: [PATCH 587/682] Merge modules dyn_compatibility and lint into dyn_trait --- .../{dyn_compatibility.rs => dyn_trait.rs} | 553 +++++++++++++++++- .../src/hir_ty_lowering/lint.rs | 533 ----------------- .../src/hir_ty_lowering/mod.rs | 14 +- 3 files changed, 550 insertions(+), 550 deletions(-) rename compiler/rustc_hir_analysis/src/hir_ty_lowering/{dyn_compatibility.rs => dyn_trait.rs} (51%) delete mode 100644 compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs similarity index 51% rename from compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs rename to compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs index c823a4a0430f..c248cd7fec2e 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs @@ -1,16 +1,22 @@ +use rustc_ast::TraitObjectSyntax; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; -use rustc_errors::struct_span_code_err; +use rustc_errors::{ + Applicability, Diag, EmissionGuarantee, StashKey, Suggestions, struct_span_code_err, +}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_lint_defs::builtin::UNUSED_ASSOCIATED_TYPE_BOUNDS; +use rustc_hir::def_id::DefId; +use rustc_lint_defs::builtin::{BARE_TRAIT_OBJECTS, UNUSED_ASSOCIATED_TYPE_BOUNDS}; use rustc_middle::ty::elaborate::ClauseWithSupertraitSpan; use rustc_middle::ty::{ self, BottomUpFolder, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, Upcast, }; +use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::{ErrorGuaranteed, Span}; use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility; +use rustc_trait_selection::error_reporting::traits::suggestions::NextTypeParamName; use rustc_trait_selection::traits; use smallvec::{SmallVec, smallvec}; use tracing::{debug, instrument}; @@ -28,10 +34,24 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir_id: hir::HirId, hir_bounds: &[hir::PolyTraitRef<'tcx>], lifetime: &hir::Lifetime, + syntax: TraitObjectSyntax, ) -> Ty<'tcx> { let tcx = self.tcx(); let dummy_self = tcx.types.trait_object_dummy_self; + match syntax { + TraitObjectSyntax::Dyn => {} + TraitObjectSyntax::None => { + match self.prohibit_or_lint_bare_trait_object_ty(span, hir_id, hir_bounds) { + // Don't continue with type analysis if the `dyn` keyword is missing. + // It generates confusing errors, especially if the user meant to use + // another keyword like `impl`. + Some(guar) => return Ty::new_error(tcx, guar), + None => {} + } + } + } + let mut user_written_bounds = Vec::new(); let mut potential_assoc_types = Vec::new(); for poly_trait_ref in hir_bounds.iter() { @@ -46,10 +66,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - let ast_bounds: Vec<_> = - hir_bounds.iter().map(|&trait_ref| hir::GenericBound::Trait(trait_ref)).collect(); - - self.add_default_traits(&mut user_written_bounds, dummy_self, &ast_bounds, None, span); + self.add_default_traits( + &mut user_written_bounds, + dummy_self, + &hir_bounds + .iter() + .map(|&trait_ref| hir::GenericBound::Trait(trait_ref)) + .collect::>(), + None, + span, + ); let (elaborated_trait_bounds, elaborated_projection_bounds) = traits::expand_trait_aliases(tcx, user_written_bounds.iter().copied()); @@ -482,6 +508,521 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }, ); } + + /// Prohibit or lint against *bare* trait object types depending on the edition. + /// + /// *Bare* trait object types are ones that aren't preceded by the keyword `dyn`. + /// In edition 2021 and onward we emit a hard error for them. + fn prohibit_or_lint_bare_trait_object_ty( + &self, + span: Span, + hir_id: hir::HirId, + hir_bounds: &[hir::PolyTraitRef<'tcx>], + ) -> Option { + let tcx = self.tcx(); + let [poly_trait_ref, ..] = hir_bounds else { return None }; + + let in_path = match tcx.parent_hir_node(hir_id) { + hir::Node::Ty(hir::Ty { + kind: hir::TyKind::Path(hir::QPath::TypeRelative(qself, _)), + .. + }) + | hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Path(hir::QPath::TypeRelative(qself, _)), + .. + }) + | hir::Node::PatExpr(hir::PatExpr { + kind: hir::PatExprKind::Path(hir::QPath::TypeRelative(qself, _)), + .. + }) if qself.hir_id == hir_id => true, + _ => false, + }; + let needs_bracket = in_path + && !tcx + .sess + .source_map() + .span_to_prev_source(span) + .ok() + .is_some_and(|s| s.trim_end().ends_with('<')); + + let is_global = poly_trait_ref.trait_ref.path.is_global(); + + let mut sugg = vec![( + span.shrink_to_lo(), + format!( + "{}dyn {}", + if needs_bracket { "<" } else { "" }, + if is_global { "(" } else { "" }, + ), + )]; + + if is_global || needs_bracket { + sugg.push(( + span.shrink_to_hi(), + format!( + "{}{}", + if is_global { ")" } else { "" }, + if needs_bracket { ">" } else { "" }, + ), + )); + } + + if span.edition().at_least_rust_2021() { + let mut diag = rustc_errors::struct_span_code_err!( + self.dcx(), + span, + E0782, + "{}", + "expected a type, found a trait" + ); + if span.can_be_used_for_suggestions() + && poly_trait_ref.trait_ref.trait_def_id().is_some() + && !self.maybe_suggest_impl_trait(span, hir_id, hir_bounds, &mut diag) + && !self.maybe_suggest_dyn_trait(hir_id, sugg, &mut diag) + { + self.maybe_suggest_add_generic_impl_trait(span, hir_id, &mut diag); + } + // Check if the impl trait that we are considering is an impl of a local trait. + self.maybe_suggest_blanket_trait_impl(span, hir_id, &mut diag); + self.maybe_suggest_assoc_ty_bound(hir_id, &mut diag); + self.maybe_suggest_typoed_method( + hir_id, + poly_trait_ref.trait_ref.trait_def_id(), + &mut diag, + ); + // In case there is an associated type with the same name + // Add the suggestion to this error + if let Some(mut sugg) = + self.dcx().steal_non_err(span, StashKey::AssociatedTypeSuggestion) + && let Suggestions::Enabled(ref mut s1) = diag.suggestions + && let Suggestions::Enabled(ref mut s2) = sugg.suggestions + { + s1.append(s2); + sugg.cancel(); + } + Some(diag.emit()) + } else { + tcx.node_span_lint(BARE_TRAIT_OBJECTS, hir_id, span, |lint| { + lint.primary_message("trait objects without an explicit `dyn` are deprecated"); + if span.can_be_used_for_suggestions() { + lint.multipart_suggestion_verbose( + "if this is a dyn-compatible trait, use `dyn`", + sugg, + Applicability::MachineApplicable, + ); + } + self.maybe_suggest_blanket_trait_impl(span, hir_id, lint); + }); + None + } + } + + /// For a struct or enum with an invalid bare trait object field, suggest turning + /// it into a generic type bound. + fn maybe_suggest_add_generic_impl_trait( + &self, + span: Span, + hir_id: hir::HirId, + diag: &mut Diag<'_>, + ) -> bool { + let tcx = self.tcx(); + + let parent_hir_id = tcx.parent_hir_id(hir_id); + let parent_item = tcx.hir_get_parent_item(hir_id).def_id; + + let generics = match tcx.hir_node_by_def_id(parent_item) { + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Struct(_, generics, variant), + .. + }) => { + if !variant.fields().iter().any(|field| field.hir_id == parent_hir_id) { + return false; + } + generics + } + hir::Node::Item(hir::Item { kind: hir::ItemKind::Enum(_, generics, def), .. }) => { + if !def + .variants + .iter() + .flat_map(|variant| variant.data.fields().iter()) + .any(|field| field.hir_id == parent_hir_id) + { + return false; + } + generics + } + _ => return false, + }; + + let Ok(rendered_ty) = tcx.sess.source_map().span_to_snippet(span) else { + return false; + }; + + let param = "TUV" + .chars() + .map(|c| c.to_string()) + .chain((0..).map(|i| format!("P{i}"))) + .find(|s| !generics.params.iter().any(|param| param.name.ident().as_str() == s)) + .expect("we definitely can find at least one param name to generate"); + let mut sugg = vec![(span, param.to_string())]; + if let Some(insertion_span) = generics.span_for_param_suggestion() { + sugg.push((insertion_span, format!(", {param}: {}", rendered_ty))); + } else { + sugg.push((generics.where_clause_span, format!("<{param}: {}>", rendered_ty))); + } + diag.multipart_suggestion_verbose( + "you might be missing a type parameter", + sugg, + Applicability::MachineApplicable, + ); + true + } + + /// Make sure that we are in the condition to suggest the blanket implementation. + fn maybe_suggest_blanket_trait_impl( + &self, + span: Span, + hir_id: hir::HirId, + diag: &mut Diag<'_, G>, + ) { + let tcx = self.tcx(); + let parent_id = tcx.hir_get_parent_item(hir_id).def_id; + if let hir::Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { self_ty: impl_self_ty, of_trait, generics, .. }), + .. + }) = tcx.hir_node_by_def_id(parent_id) + && hir_id == impl_self_ty.hir_id + { + let Some(of_trait) = of_trait else { + diag.span_suggestion_verbose( + impl_self_ty.span.shrink_to_hi(), + "you might have intended to implement this trait for a given type", + format!(" for /* Type */"), + Applicability::HasPlaceholders, + ); + return; + }; + if !of_trait.trait_ref.trait_def_id().is_some_and(|def_id| def_id.is_local()) { + return; + } + let of_trait_span = of_trait.trait_ref.path.span; + // make sure that we are not calling unwrap to abort during the compilation + let Ok(of_trait_name) = tcx.sess.source_map().span_to_snippet(of_trait_span) else { + return; + }; + + let Ok(impl_trait_name) = self.tcx().sess.source_map().span_to_snippet(span) else { + return; + }; + let sugg = self.add_generic_param_suggestion(generics, span, &impl_trait_name); + diag.multipart_suggestion( + format!( + "alternatively use a blanket implementation to implement `{of_trait_name}` for \ + all types that also implement `{impl_trait_name}`" + ), + sugg, + Applicability::MaybeIncorrect, + ); + } + } + + /// Try our best to approximate when adding `dyn` would be helpful for a bare + /// trait object. + /// + /// Right now, this is if the type is either directly nested in another ty, + /// or if it's in the tail field within a struct. This approximates what the + /// user would've gotten on edition 2015, except for the case where we have + /// an *obvious* knock-on `Sized` error. + fn maybe_suggest_dyn_trait( + &self, + hir_id: hir::HirId, + sugg: Vec<(Span, String)>, + diag: &mut Diag<'_>, + ) -> bool { + let tcx = self.tcx(); + + // Look at the direct HIR parent, since we care about the relationship between + // the type and the thing that directly encloses it. + match tcx.parent_hir_node(hir_id) { + // These are all generally ok. Namely, when a trait object is nested + // into another expression or ty, it's either very certain that they + // missed the ty (e.g. `&Trait`) or it's not really possible to tell + // what their intention is, so let's not give confusing suggestions and + // just mention `dyn`. The user can make up their mind what to do here. + hir::Node::Ty(_) + | hir::Node::Expr(_) + | hir::Node::PatExpr(_) + | hir::Node::PathSegment(_) + | hir::Node::AssocItemConstraint(_) + | hir::Node::TraitRef(_) + | hir::Node::Item(_) + | hir::Node::WherePredicate(_) => {} + + hir::Node::Field(field) => { + // Enums can't have unsized fields, fields can only have an unsized tail field. + if let hir::Node::Item(hir::Item { + kind: hir::ItemKind::Struct(_, _, variant), .. + }) = tcx.parent_hir_node(field.hir_id) + && variant + .fields() + .last() + .is_some_and(|tail_field| tail_field.hir_id == field.hir_id) + { + // Ok + } else { + return false; + } + } + _ => return false, + } + + // FIXME: Only emit this suggestion if the trait is dyn-compatible. + diag.multipart_suggestion_verbose( + "you can add the `dyn` keyword if you want a trait object", + sugg, + Applicability::MachineApplicable, + ); + true + } + + fn add_generic_param_suggestion( + &self, + generics: &hir::Generics<'_>, + self_ty_span: Span, + impl_trait_name: &str, + ) -> Vec<(Span, String)> { + // check if the trait has generics, to make a correct suggestion + let param_name = generics.params.next_type_param_name(None); + + let add_generic_sugg = if let Some(span) = generics.span_for_param_suggestion() { + (span, format!(", {param_name}: {impl_trait_name}")) + } else { + (generics.span, format!("<{param_name}: {impl_trait_name}>")) + }; + vec![(self_ty_span, param_name), add_generic_sugg] + } + + /// Make sure that we are in the condition to suggest `impl Trait`. + fn maybe_suggest_impl_trait( + &self, + span: Span, + hir_id: hir::HirId, + hir_bounds: &[hir::PolyTraitRef<'tcx>], + diag: &mut Diag<'_>, + ) -> bool { + let tcx = self.tcx(); + let parent_id = tcx.hir_get_parent_item(hir_id).def_id; + // FIXME: If `type_alias_impl_trait` is enabled, also look for `Trait0` + // and suggest `Trait0`. + // Functions are found in three different contexts. + // 1. Independent functions + // 2. Functions inside trait blocks + // 3. Functions inside impl blocks + let (sig, generics) = match tcx.hir_node_by_def_id(parent_id) { + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Fn { sig, generics, .. }, .. + }) => (sig, generics), + hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(sig, _), + generics, + .. + }) => (sig, generics), + hir::Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Fn(sig, _), + generics, + .. + }) => (sig, generics), + _ => return false, + }; + let Ok(trait_name) = tcx.sess.source_map().span_to_snippet(span) else { + return false; + }; + let impl_sugg = vec![(span.shrink_to_lo(), "impl ".to_string())]; + // Check if trait object is safe for suggesting dynamic dispatch. + let is_dyn_compatible = hir_bounds.iter().all(|bound| match bound.trait_ref.path.res { + Res::Def(DefKind::Trait, id) => tcx.is_dyn_compatible(id), + _ => false, + }); + + let borrowed = matches!( + tcx.parent_hir_node(hir_id), + hir::Node::Ty(hir::Ty { kind: hir::TyKind::Ref(..), .. }) + ); + + // Suggestions for function return type. + if let hir::FnRetTy::Return(ty) = sig.decl.output + && ty.peel_refs().hir_id == hir_id + { + let pre = if !is_dyn_compatible { + format!("`{trait_name}` is dyn-incompatible, ") + } else { + String::new() + }; + let msg = format!( + "{pre}use `impl {trait_name}` to return an opaque type, as long as you return a \ + single underlying type", + ); + + diag.multipart_suggestion_verbose(msg, impl_sugg, Applicability::MachineApplicable); + + // Suggest `Box` for return type + if is_dyn_compatible { + // If the return type is `&Trait`, we don't want + // the ampersand to be displayed in the `Box` + // suggestion. + let suggestion = if borrowed { + vec![(ty.span, format!("Box"))] + } else { + vec![ + (ty.span.shrink_to_lo(), "Box".to_string()), + ] + }; + + diag.multipart_suggestion_verbose( + "alternatively, you can return an owned trait object", + suggestion, + Applicability::MachineApplicable, + ); + } + return true; + } + + // Suggestions for function parameters. + for ty in sig.decl.inputs { + if ty.peel_refs().hir_id != hir_id { + continue; + } + let sugg = self.add_generic_param_suggestion(generics, span, &trait_name); + diag.multipart_suggestion_verbose( + format!("use a new generic type parameter, constrained by `{trait_name}`"), + sugg, + Applicability::MachineApplicable, + ); + diag.multipart_suggestion_verbose( + "you can also use an opaque type, but users won't be able to specify the type \ + parameter when calling the `fn`, having to rely exclusively on type inference", + impl_sugg, + Applicability::MachineApplicable, + ); + if !is_dyn_compatible { + diag.note(format!( + "`{trait_name}` is dyn-incompatible, otherwise a trait object could be used" + )); + } else { + // No ampersand in suggestion if it's borrowed already + let (dyn_str, paren_dyn_str) = + if borrowed { ("dyn ", "(dyn ") } else { ("&dyn ", "&(dyn ") }; + + let sugg = if let [_, _, ..] = hir_bounds { + // There is more than one trait bound, we need surrounding parentheses. + vec![ + (span.shrink_to_lo(), paren_dyn_str.to_string()), + (span.shrink_to_hi(), ")".to_string()), + ] + } else { + vec![(span.shrink_to_lo(), dyn_str.to_string())] + }; + diag.multipart_suggestion_verbose( + format!( + "alternatively, use a trait object to accept any type that implements \ + `{trait_name}`, accessing its methods at runtime using dynamic dispatch", + ), + sugg, + Applicability::MachineApplicable, + ); + } + return true; + } + false + } + + fn maybe_suggest_assoc_ty_bound(&self, hir_id: hir::HirId, diag: &mut Diag<'_>) { + let mut parents = self.tcx().hir_parent_iter(hir_id); + + if let Some((c_hir_id, hir::Node::AssocItemConstraint(constraint))) = parents.next() + && let Some(obj_ty) = constraint.ty() + && let Some((_, hir::Node::TraitRef(trait_ref))) = parents.next() + { + if let Some((_, hir::Node::Ty(ty))) = parents.next() + && let hir::TyKind::TraitObject(..) = ty.kind + { + // Assoc ty bounds aren't permitted inside trait object types. + return; + } + + if trait_ref + .path + .segments + .iter() + .find_map(|seg| { + seg.args.filter(|args| args.constraints.iter().any(|c| c.hir_id == c_hir_id)) + }) + .is_none_or(|args| args.parenthesized != hir::GenericArgsParentheses::No) + { + // Only consider angle-bracketed args (where we have a `=` to replace with `:`). + return; + } + + let lo = if constraint.gen_args.span_ext.is_dummy() { + constraint.ident.span + } else { + constraint.gen_args.span_ext + }; + let hi = obj_ty.span; + + if !lo.eq_ctxt(hi) { + return; + } + + diag.span_suggestion_verbose( + lo.between(hi), + "you might have meant to write a bound here", + ": ", + Applicability::MaybeIncorrect, + ); + } + } + + fn maybe_suggest_typoed_method( + &self, + hir_id: hir::HirId, + trait_def_id: Option, + diag: &mut Diag<'_>, + ) { + let tcx = self.tcx(); + let Some(trait_def_id) = trait_def_id else { + return; + }; + let hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Path(hir::QPath::TypeRelative(path_ty, segment)), + .. + }) = tcx.parent_hir_node(hir_id) + else { + return; + }; + if path_ty.hir_id != hir_id { + return; + } + let names: Vec<_> = tcx + .associated_items(trait_def_id) + .in_definition_order() + .filter(|assoc| assoc.namespace() == hir::def::Namespace::ValueNS) + .map(|cand| cand.name()) + .collect(); + if let Some(typo) = find_best_match_for_name(&names, segment.ident.name, None) { + diag.span_suggestion_verbose( + segment.ident.span, + format!( + "you may have misspelled this associated item, causing `{}` \ + to be interpreted as a type rather than a trait", + tcx.item_name(trait_def_id), + ), + typo, + Applicability::MaybeIncorrect, + ); + } + } } fn replace_dummy_self_with_error<'tcx, T: TypeFoldable>>( diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs deleted file mode 100644 index 56998b5b53cc..000000000000 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs +++ /dev/null @@ -1,533 +0,0 @@ -use rustc_ast::TraitObjectSyntax; -use rustc_errors::codes::*; -use rustc_errors::{Diag, EmissionGuarantee, ErrorGuaranteed, StashKey, Suggestions}; -use rustc_hir as hir; -use rustc_hir::def::{DefKind, Namespace, Res}; -use rustc_hir::def_id::DefId; -use rustc_lint_defs::Applicability; -use rustc_lint_defs::builtin::BARE_TRAIT_OBJECTS; -use rustc_span::Span; -use rustc_span::edit_distance::find_best_match_for_name; -use rustc_trait_selection::error_reporting::traits::suggestions::NextTypeParamName; - -use super::HirTyLowerer; - -impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { - /// Prohibit or lint against *bare* trait object types depending on the edition. - /// - /// *Bare* trait object types are ones that aren't preceded by the keyword `dyn`. - /// In edition 2021 and onward we emit a hard error for them. - pub(super) fn prohibit_or_lint_bare_trait_object_ty( - &self, - self_ty: &hir::Ty<'_>, - ) -> Option { - let tcx = self.tcx(); - - let poly_trait_ref = if let hir::TyKind::TraitObject([poly_trait_ref, ..], tagged_ptr) = - self_ty.kind - && let TraitObjectSyntax::None = tagged_ptr.tag() - { - poly_trait_ref - } else { - return None; - }; - - let in_path = match tcx.parent_hir_node(self_ty.hir_id) { - hir::Node::Ty(hir::Ty { - kind: hir::TyKind::Path(hir::QPath::TypeRelative(qself, _)), - .. - }) - | hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Path(hir::QPath::TypeRelative(qself, _)), - .. - }) - | hir::Node::PatExpr(hir::PatExpr { - kind: hir::PatExprKind::Path(hir::QPath::TypeRelative(qself, _)), - .. - }) if qself.hir_id == self_ty.hir_id => true, - _ => false, - }; - let needs_bracket = in_path - && !tcx - .sess - .source_map() - .span_to_prev_source(self_ty.span) - .ok() - .is_some_and(|s| s.trim_end().ends_with('<')); - - let is_global = poly_trait_ref.trait_ref.path.is_global(); - - let mut sugg = vec![( - self_ty.span.shrink_to_lo(), - format!( - "{}dyn {}", - if needs_bracket { "<" } else { "" }, - if is_global { "(" } else { "" }, - ), - )]; - - if is_global || needs_bracket { - sugg.push(( - self_ty.span.shrink_to_hi(), - format!( - "{}{}", - if is_global { ")" } else { "" }, - if needs_bracket { ">" } else { "" }, - ), - )); - } - - if self_ty.span.edition().at_least_rust_2021() { - let mut diag = rustc_errors::struct_span_code_err!( - self.dcx(), - self_ty.span, - E0782, - "{}", - "expected a type, found a trait" - ); - if self_ty.span.can_be_used_for_suggestions() - && poly_trait_ref.trait_ref.trait_def_id().is_some() - && !self.maybe_suggest_impl_trait(self_ty, &mut diag) - && !self.maybe_suggest_dyn_trait(self_ty, sugg, &mut diag) - { - self.maybe_suggest_add_generic_impl_trait(self_ty, &mut diag); - } - // Check if the impl trait that we are considering is an impl of a local trait. - self.maybe_suggest_blanket_trait_impl(self_ty, &mut diag); - self.maybe_suggest_assoc_ty_bound(self_ty, &mut diag); - self.maybe_suggest_typoed_method( - self_ty, - poly_trait_ref.trait_ref.trait_def_id(), - &mut diag, - ); - // In case there is an associated type with the same name - // Add the suggestion to this error - if let Some(mut sugg) = - self.dcx().steal_non_err(self_ty.span, StashKey::AssociatedTypeSuggestion) - && let Suggestions::Enabled(ref mut s1) = diag.suggestions - && let Suggestions::Enabled(ref mut s2) = sugg.suggestions - { - s1.append(s2); - sugg.cancel(); - } - Some(diag.emit()) - } else { - tcx.node_span_lint(BARE_TRAIT_OBJECTS, self_ty.hir_id, self_ty.span, |lint| { - lint.primary_message("trait objects without an explicit `dyn` are deprecated"); - if self_ty.span.can_be_used_for_suggestions() { - lint.multipart_suggestion_verbose( - "if this is a dyn-compatible trait, use `dyn`", - sugg, - Applicability::MachineApplicable, - ); - } - self.maybe_suggest_blanket_trait_impl(self_ty, lint); - }); - None - } - } - - /// For a struct or enum with an invalid bare trait object field, suggest turning - /// it into a generic type bound. - fn maybe_suggest_add_generic_impl_trait( - &self, - self_ty: &hir::Ty<'_>, - diag: &mut Diag<'_>, - ) -> bool { - let tcx = self.tcx(); - - let parent_hir_id = tcx.parent_hir_id(self_ty.hir_id); - let parent_item = tcx.hir_get_parent_item(self_ty.hir_id).def_id; - - let generics = match tcx.hir_node_by_def_id(parent_item) { - hir::Node::Item(hir::Item { - kind: hir::ItemKind::Struct(_, generics, variant), - .. - }) => { - if !variant.fields().iter().any(|field| field.hir_id == parent_hir_id) { - return false; - } - generics - } - hir::Node::Item(hir::Item { kind: hir::ItemKind::Enum(_, generics, def), .. }) => { - if !def - .variants - .iter() - .flat_map(|variant| variant.data.fields().iter()) - .any(|field| field.hir_id == parent_hir_id) - { - return false; - } - generics - } - _ => return false, - }; - - let Ok(rendered_ty) = tcx.sess.source_map().span_to_snippet(self_ty.span) else { - return false; - }; - - let param = "TUV" - .chars() - .map(|c| c.to_string()) - .chain((0..).map(|i| format!("P{i}"))) - .find(|s| !generics.params.iter().any(|param| param.name.ident().as_str() == s)) - .expect("we definitely can find at least one param name to generate"); - let mut sugg = vec![(self_ty.span, param.to_string())]; - if let Some(insertion_span) = generics.span_for_param_suggestion() { - sugg.push((insertion_span, format!(", {param}: {}", rendered_ty))); - } else { - sugg.push((generics.where_clause_span, format!("<{param}: {}>", rendered_ty))); - } - diag.multipart_suggestion_verbose( - "you might be missing a type parameter", - sugg, - Applicability::MachineApplicable, - ); - true - } - /// Make sure that we are in the condition to suggest the blanket implementation. - fn maybe_suggest_blanket_trait_impl( - &self, - self_ty: &hir::Ty<'_>, - diag: &mut Diag<'_, G>, - ) { - let tcx = self.tcx(); - let parent_id = tcx.hir_get_parent_item(self_ty.hir_id).def_id; - if let hir::Node::Item(hir::Item { - kind: hir::ItemKind::Impl(hir::Impl { self_ty: impl_self_ty, of_trait, generics, .. }), - .. - }) = tcx.hir_node_by_def_id(parent_id) - && self_ty.hir_id == impl_self_ty.hir_id - { - let Some(of_trait) = of_trait else { - diag.span_suggestion_verbose( - impl_self_ty.span.shrink_to_hi(), - "you might have intended to implement this trait for a given type", - format!(" for /* Type */"), - Applicability::HasPlaceholders, - ); - return; - }; - if !of_trait.trait_ref.trait_def_id().is_some_and(|def_id| def_id.is_local()) { - return; - } - let of_trait_span = of_trait.trait_ref.path.span; - // make sure that we are not calling unwrap to abort during the compilation - let Ok(of_trait_name) = tcx.sess.source_map().span_to_snippet(of_trait_span) else { - return; - }; - - let Ok(impl_trait_name) = self.tcx().sess.source_map().span_to_snippet(self_ty.span) - else { - return; - }; - let sugg = self.add_generic_param_suggestion(generics, self_ty.span, &impl_trait_name); - diag.multipart_suggestion( - format!( - "alternatively use a blanket implementation to implement `{of_trait_name}` for \ - all types that also implement `{impl_trait_name}`" - ), - sugg, - Applicability::MaybeIncorrect, - ); - } - } - - /// Try our best to approximate when adding `dyn` would be helpful for a bare - /// trait object. - /// - /// Right now, this is if the type is either directly nested in another ty, - /// or if it's in the tail field within a struct. This approximates what the - /// user would've gotten on edition 2015, except for the case where we have - /// an *obvious* knock-on `Sized` error. - fn maybe_suggest_dyn_trait( - &self, - self_ty: &hir::Ty<'_>, - sugg: Vec<(Span, String)>, - diag: &mut Diag<'_>, - ) -> bool { - let tcx = self.tcx(); - - // Look at the direct HIR parent, since we care about the relationship between - // the type and the thing that directly encloses it. - match tcx.parent_hir_node(self_ty.hir_id) { - // These are all generally ok. Namely, when a trait object is nested - // into another expression or ty, it's either very certain that they - // missed the ty (e.g. `&Trait`) or it's not really possible to tell - // what their intention is, so let's not give confusing suggestions and - // just mention `dyn`. The user can make up their mind what to do here. - hir::Node::Ty(_) - | hir::Node::Expr(_) - | hir::Node::PatExpr(_) - | hir::Node::PathSegment(_) - | hir::Node::AssocItemConstraint(_) - | hir::Node::TraitRef(_) - | hir::Node::Item(_) - | hir::Node::WherePredicate(_) => {} - - hir::Node::Field(field) => { - // Enums can't have unsized fields, fields can only have an unsized tail field. - if let hir::Node::Item(hir::Item { - kind: hir::ItemKind::Struct(_, _, variant), .. - }) = tcx.parent_hir_node(field.hir_id) - && variant - .fields() - .last() - .is_some_and(|tail_field| tail_field.hir_id == field.hir_id) - { - // Ok - } else { - return false; - } - } - _ => return false, - } - - // FIXME: Only emit this suggestion if the trait is dyn-compatible. - diag.multipart_suggestion_verbose( - "you can add the `dyn` keyword if you want a trait object", - sugg, - Applicability::MachineApplicable, - ); - true - } - - fn add_generic_param_suggestion( - &self, - generics: &hir::Generics<'_>, - self_ty_span: Span, - impl_trait_name: &str, - ) -> Vec<(Span, String)> { - // check if the trait has generics, to make a correct suggestion - let param_name = generics.params.next_type_param_name(None); - - let add_generic_sugg = if let Some(span) = generics.span_for_param_suggestion() { - (span, format!(", {param_name}: {impl_trait_name}")) - } else { - (generics.span, format!("<{param_name}: {impl_trait_name}>")) - }; - vec![(self_ty_span, param_name), add_generic_sugg] - } - - /// Make sure that we are in the condition to suggest `impl Trait`. - fn maybe_suggest_impl_trait(&self, self_ty: &hir::Ty<'_>, diag: &mut Diag<'_>) -> bool { - let tcx = self.tcx(); - let parent_id = tcx.hir_get_parent_item(self_ty.hir_id).def_id; - // FIXME: If `type_alias_impl_trait` is enabled, also look for `Trait0` - // and suggest `Trait0`. - // Functions are found in three different contexts. - // 1. Independent functions - // 2. Functions inside trait blocks - // 3. Functions inside impl blocks - let (sig, generics) = match tcx.hir_node_by_def_id(parent_id) { - hir::Node::Item(hir::Item { - kind: hir::ItemKind::Fn { sig, generics, .. }, .. - }) => (sig, generics), - hir::Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(sig, _), - generics, - .. - }) => (sig, generics), - hir::Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Fn(sig, _), - generics, - .. - }) => (sig, generics), - _ => return false, - }; - let Ok(trait_name) = tcx.sess.source_map().span_to_snippet(self_ty.span) else { - return false; - }; - let impl_sugg = vec![(self_ty.span.shrink_to_lo(), "impl ".to_string())]; - // Check if trait object is safe for suggesting dynamic dispatch. - let is_dyn_compatible = match self_ty.kind { - hir::TyKind::TraitObject(objects, ..) => { - objects.iter().all(|o| match o.trait_ref.path.res { - Res::Def(DefKind::Trait, id) => tcx.is_dyn_compatible(id), - _ => false, - }) - } - _ => false, - }; - - let borrowed = matches!( - tcx.parent_hir_node(self_ty.hir_id), - hir::Node::Ty(hir::Ty { kind: hir::TyKind::Ref(..), .. }) - ); - - // Suggestions for function return type. - if let hir::FnRetTy::Return(ty) = sig.decl.output - && ty.peel_refs().hir_id == self_ty.hir_id - { - let pre = if !is_dyn_compatible { - format!("`{trait_name}` is dyn-incompatible, ") - } else { - String::new() - }; - let msg = format!( - "{pre}use `impl {trait_name}` to return an opaque type, as long as you return a \ - single underlying type", - ); - - diag.multipart_suggestion_verbose(msg, impl_sugg, Applicability::MachineApplicable); - - // Suggest `Box` for return type - if is_dyn_compatible { - // If the return type is `&Trait`, we don't want - // the ampersand to be displayed in the `Box` - // suggestion. - let suggestion = if borrowed { - vec![(ty.span, format!("Box"))] - } else { - vec![ - (ty.span.shrink_to_lo(), "Box".to_string()), - ] - }; - - diag.multipart_suggestion_verbose( - "alternatively, you can return an owned trait object", - suggestion, - Applicability::MachineApplicable, - ); - } - return true; - } - - // Suggestions for function parameters. - for ty in sig.decl.inputs { - if ty.peel_refs().hir_id != self_ty.hir_id { - continue; - } - let sugg = self.add_generic_param_suggestion(generics, self_ty.span, &trait_name); - diag.multipart_suggestion_verbose( - format!("use a new generic type parameter, constrained by `{trait_name}`"), - sugg, - Applicability::MachineApplicable, - ); - diag.multipart_suggestion_verbose( - "you can also use an opaque type, but users won't be able to specify the type \ - parameter when calling the `fn`, having to rely exclusively on type inference", - impl_sugg, - Applicability::MachineApplicable, - ); - if !is_dyn_compatible { - diag.note(format!( - "`{trait_name}` is dyn-incompatible, otherwise a trait object could be used" - )); - } else { - // No ampersand in suggestion if it's borrowed already - let (dyn_str, paren_dyn_str) = - if borrowed { ("dyn ", "(dyn ") } else { ("&dyn ", "&(dyn ") }; - - let sugg = if let hir::TyKind::TraitObject([_, _, ..], _) = self_ty.kind { - // There is more than one trait bound, we need surrounding parentheses. - vec![ - (self_ty.span.shrink_to_lo(), paren_dyn_str.to_string()), - (self_ty.span.shrink_to_hi(), ")".to_string()), - ] - } else { - vec![(self_ty.span.shrink_to_lo(), dyn_str.to_string())] - }; - diag.multipart_suggestion_verbose( - format!( - "alternatively, use a trait object to accept any type that implements \ - `{trait_name}`, accessing its methods at runtime using dynamic dispatch", - ), - sugg, - Applicability::MachineApplicable, - ); - } - return true; - } - false - } - - fn maybe_suggest_assoc_ty_bound(&self, self_ty: &hir::Ty<'_>, diag: &mut Diag<'_>) { - let mut parents = self.tcx().hir_parent_iter(self_ty.hir_id); - - if let Some((c_hir_id, hir::Node::AssocItemConstraint(constraint))) = parents.next() - && let Some(obj_ty) = constraint.ty() - && let Some((_, hir::Node::TraitRef(trait_ref))) = parents.next() - { - if let Some((_, hir::Node::Ty(ty))) = parents.next() - && let hir::TyKind::TraitObject(..) = ty.kind - { - // Assoc ty bounds aren't permitted inside trait object types. - return; - } - - if trait_ref - .path - .segments - .iter() - .find_map(|seg| { - seg.args.filter(|args| args.constraints.iter().any(|c| c.hir_id == c_hir_id)) - }) - .is_none_or(|args| args.parenthesized != hir::GenericArgsParentheses::No) - { - // Only consider angle-bracketed args (where we have a `=` to replace with `:`). - return; - } - - let lo = if constraint.gen_args.span_ext.is_dummy() { - constraint.ident.span - } else { - constraint.gen_args.span_ext - }; - let hi = obj_ty.span; - - if !lo.eq_ctxt(hi) { - return; - } - - diag.span_suggestion_verbose( - lo.between(hi), - "you might have meant to write a bound here", - ": ", - Applicability::MaybeIncorrect, - ); - } - } - - fn maybe_suggest_typoed_method( - &self, - self_ty: &hir::Ty<'_>, - trait_def_id: Option, - diag: &mut Diag<'_>, - ) { - let tcx = self.tcx(); - let Some(trait_def_id) = trait_def_id else { - return; - }; - let hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Path(hir::QPath::TypeRelative(path_ty, segment)), - .. - }) = tcx.parent_hir_node(self_ty.hir_id) - else { - return; - }; - if path_ty.hir_id != self_ty.hir_id { - return; - } - let names: Vec<_> = tcx - .associated_items(trait_def_id) - .in_definition_order() - .filter(|assoc| assoc.namespace() == Namespace::ValueNS) - .map(|cand| cand.name()) - .collect(); - if let Some(typo) = find_best_match_for_name(&names, segment.ident.name, None) { - diag.span_suggestion_verbose( - segment.ident.span, - format!( - "you may have misspelled this associated item, causing `{}` \ - to be interpreted as a type rather than a trait", - tcx.item_name(trait_def_id), - ), - typo, - Applicability::MaybeIncorrect, - ); - } - } -} diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index b64129dc54e6..9b198d044542 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -15,10 +15,9 @@ mod bounds; mod cmse; -mod dyn_compatibility; +mod dyn_trait; pub mod errors; pub mod generics; -mod lint; use std::assert_matches::assert_matches; use std::slice; @@ -2427,15 +2426,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ), hir::TyKind::TraitObject(bounds, tagged_ptr) => { let lifetime = tagged_ptr.pointer(); - - if let Some(guar) = self.prohibit_or_lint_bare_trait_object_ty(hir_ty) { - // Don't continue with type analysis if the `dyn` keyword is missing - // It generates confusing errors, especially if the user meant to use another - // keyword like `impl` - Ty::new_error(tcx, guar) - } else { - self.lower_trait_object_ty(hir_ty.span, hir_ty.hir_id, bounds, lifetime) - } + let syntax = tagged_ptr.tag(); + self.lower_trait_object_ty(hir_ty.span, hir_ty.hir_id, bounds, lifetime, syntax) } // If we encounter a fully qualified path with RTN generics, then it must have // *not* gone through `lower_ty_maybe_return_type_notation`, and therefore From 06e881042d3f58fdb60fde58de5bf112b2184b11 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Wed, 17 Sep 2025 04:52:58 +0000 Subject: [PATCH 588/682] Prepare for merging from rust-lang/rust This updates the rust-version file to 3f1552a273e43e15f6ed240d00e1efdd6a53e65e. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index f27677cd5038..0eb16a943d6a 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -9d82de19dfae60e55c291f5f28e28cfc2c1b9630 +3f1552a273e43e15f6ed240d00e1efdd6a53e65e From 11107679ee19cc54d4f6cf66501fd62f123e9eae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelo=20Dom=C3=ADnguez?= Date: Mon, 14 Jul 2025 17:05:51 +0000 Subject: [PATCH 589/682] Add test for autodiff abi handling --- tests/codegen-llvm/autodiff/abi_handling.rs | 210 ++++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 tests/codegen-llvm/autodiff/abi_handling.rs diff --git a/tests/codegen-llvm/autodiff/abi_handling.rs b/tests/codegen-llvm/autodiff/abi_handling.rs new file mode 100644 index 000000000000..454ec698b917 --- /dev/null +++ b/tests/codegen-llvm/autodiff/abi_handling.rs @@ -0,0 +1,210 @@ +//@ revisions: debug release + +//@[debug] compile-flags: -Zautodiff=Enable -C opt-level=0 -Clto=fat +//@[release] compile-flags: -Zautodiff=Enable -C opt-level=3 -Clto=fat +//@ no-prefer-dynamic +//@ needs-enzyme + +// This test checks that Rust types are lowered to LLVM-IR types in a way +// we expect and Enzyme can handle. We explicitly check release mode to +// ensure that LLVM's O3 pipeline doesn't rewrite function signatures +// into forms that Enzyme can't process correctly. + +#![feature(autodiff)] + +use std::autodiff::{autodiff_forward, autodiff_reverse}; + +#[derive(Copy, Clone)] +struct Input { + x: f32, + y: f32, +} + +#[derive(Copy, Clone)] +struct Wrapper { + z: f32, +} + +#[derive(Copy, Clone)] +struct NestedInput { + x: f32, + y: Wrapper, +} + +fn square(x: f32) -> f32 { + x * x +} + +// CHECK-LABEL: ; abi_handling::df1 +// CHECK-NEXT: Function Attrs +// debug-NEXT: define internal { float, float } +// debug-SAME: (ptr align 4 %x, ptr align 4 %bx_0) +// release-NEXT: define internal fastcc float +// release-SAME: (float %x.0.val, float %x.4.val) + +// CHECK-LABEL: ; abi_handling::f1 +// CHECK-NEXT: Function Attrs +// debug-NEXT: define internal float +// debug-SAME: (ptr align 4 %x) +// release-NEXT: define internal fastcc noundef float +// release-SAME: (float %x.0.val, float %x.4.val) +#[autodiff_forward(df1, Dual, Dual)] +#[inline(never)] +fn f1(x: &[f32; 2]) -> f32 { + x[0] + x[1] +} + +// CHECK-LABEL: ; abi_handling::df2 +// CHECK-NEXT: Function Attrs +// debug-NEXT: define internal { float, float } +// debug-SAME: (ptr %f, float %x, float %dret) +// release-NEXT: define internal fastcc float +// release-SAME: (float noundef %x) + +// CHECK-LABEL: ; abi_handling::f2 +// CHECK-NEXT: Function Attrs +// debug-NEXT: define internal float +// debug-SAME: (ptr %f, float %x) +// release-NEXT: define internal fastcc noundef float +// release-SAME: (float noundef %x) +#[autodiff_reverse(df2, Const, Active, Active)] +#[inline(never)] +fn f2(f: fn(f32) -> f32, x: f32) -> f32 { + f(x) +} + +// CHECK-LABEL: ; abi_handling::df3 +// CHECK-NEXT: Function Attrs +// debug-NEXT: define internal { float, float } +// debug-SAME: (ptr align 4 %x, ptr align 4 %bx_0, ptr align 4 %y, ptr align 4 %by_0) +// release-NEXT: define internal fastcc { float, float } +// release-SAME: (float %x.0.val) + +// CHECK-LABEL: ; abi_handling::f3 +// CHECK-NEXT: Function Attrs +// debug-NEXT: define internal float +// debug-SAME: (ptr align 4 %x, ptr align 4 %y) +// release-NEXT: define internal fastcc noundef float +// release-SAME: (float %x.0.val) +#[autodiff_forward(df3, Dual, Dual, Dual)] +#[inline(never)] +fn f3<'a>(x: &'a f32, y: &'a f32) -> f32 { + *x * *y +} + +// CHECK-LABEL: ; abi_handling::df4 +// CHECK-NEXT: Function Attrs +// debug-NEXT: define internal { float, float } +// debug-SAME: (float %x.0, float %x.1, float %bx_0.0, float %bx_0.1) +// release-NEXT: define internal fastcc { float, float } +// release-SAME: (float noundef %x.0, float noundef %x.1) + +// CHECK-LABEL: ; abi_handling::f4 +// CHECK-NEXT: Function Attrs +// debug-NEXT: define internal float +// debug-SAME: (float %x.0, float %x.1) +// release-NEXT: define internal fastcc noundef float +// release-SAME: (float noundef %x.0, float noundef %x.1) +#[autodiff_forward(df4, Dual, Dual)] +#[inline(never)] +fn f4(x: (f32, f32)) -> f32 { + x.0 * x.1 +} + +// CHECK-LABEL: ; abi_handling::df5 +// CHECK-NEXT: Function Attrs +// debug-NEXT: define internal { float, float } +// debug-SAME: (float %i.0, float %i.1, float %bi_0.0, float %bi_0.1) +// release-NEXT: define internal fastcc { float, float } +// release-SAME: (float noundef %i.0, float noundef %i.1) + +// CHECK-LABEL: ; abi_handling::f5 +// CHECK-NEXT: Function Attrs +// debug-NEXT: define internal float +// debug-SAME: (float %i.0, float %i.1) +// release-NEXT: define internal fastcc noundef float +// release-SAME: (float noundef %i.0, float noundef %i.1) +#[autodiff_forward(df5, Dual, Dual)] +#[inline(never)] +fn f5(i: Input) -> f32 { + i.x + i.y +} + +// CHECK-LABEL: ; abi_handling::df6 +// CHECK-NEXT: Function Attrs +// debug-NEXT: define internal { float, float } +// debug-SAME: (float %i.0, float %i.1, float %bi_0.0, float %bi_0.1) +// release-NEXT: define internal fastcc { float, float } +// release-SAME: float noundef %i.0, float noundef %i.1 +// release-SAME: float noundef %bi_0.0, float noundef %bi_0.1 + +// CHECK-LABEL: ; abi_handling::f6 +// CHECK-NEXT: Function Attrs +// debug-NEXT: define internal float +// debug-SAME: (float %i.0, float %i.1) +// release-NEXT: define internal fastcc noundef float +// release-SAME: (float noundef %i.0, float noundef %i.1) +#[autodiff_forward(df6, Dual, Dual)] +#[inline(never)] +fn f6(i: NestedInput) -> f32 { + i.x + i.y.z * i.y.z +} + +// CHECK-LABEL: ; abi_handling::df7 +// CHECK-NEXT: Function Attrs +// debug-NEXT: define internal { float, float } +// debug-SAME: (ptr align 4 %x.0, ptr align 4 %x.1, ptr align 4 %bx_0.0, ptr align 4 %bx_0.1) +// release-NEXT: define internal fastcc { float, float } +// release-SAME: (float %x.0.0.val, float %x.1.0.val) + +// CHECK-LABEL: ; abi_handling::f7 +// CHECK-NEXT: Function Attrs +// debug-NEXT: define internal float +// debug-SAME: (ptr align 4 %x.0, ptr align 4 %x.1) +// release-NEXT: define internal fastcc noundef float +// release-SAME: (float %x.0.0.val, float %x.1.0.val) +#[autodiff_forward(df7, Dual, Dual)] +#[inline(never)] +fn f7(x: (&f32, &f32)) -> f32 { + x.0 * x.1 +} + +fn main() { + let x = std::hint::black_box(2.0); + let y = std::hint::black_box(3.0); + let z = std::hint::black_box(4.0); + static Y: f32 = std::hint::black_box(3.2); + + let in_f1 = [x, y]; + dbg!(f1(&in_f1)); + let res_f1 = df1(&in_f1, &[1.0, 0.0]); + dbg!(res_f1); + + dbg!(f2(square, x)); + let res_f2 = df2(square, x, 1.0); + dbg!(res_f2); + + dbg!(f3(&x, &Y)); + let res_f3 = df3(&x, &Y, &1.0, &0.0); + dbg!(res_f3); + + let in_f4 = (x, y); + dbg!(f4(in_f4)); + let res_f4 = df4(in_f4, (1.0, 0.0)); + dbg!(res_f4); + + let in_f5 = Input { x, y }; + dbg!(f5(in_f5)); + let res_f5 = df5(in_f5, Input { x: 1.0, y: 0.0 }); + dbg!(res_f5); + + let in_f6 = NestedInput { x, y: Wrapper { z: y } }; + dbg!(f6(in_f6)); + let res_f6 = df6(in_f6, NestedInput { x, y: Wrapper { z } }); + dbg!(res_f6); + + let in_f7 = (&x, &y); + dbg!(f7(in_f7)); + let res_f7 = df7(in_f7, (&1.0, &0.0)); + dbg!(res_f7); +} From 466bec90292a1b04a50a5889ab13fddd53c925ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelo=20Dom=C3=ADnguez?= Date: Mon, 14 Jul 2025 17:07:01 +0000 Subject: [PATCH 590/682] Adjust autodiff actitivies for ScalarPair --- .../rustc_codegen_llvm/src/builder/autodiff.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index e2df3265f6f7..a1cc4fdbdf67 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -80,6 +80,23 @@ pub(crate) fn adjust_activity_to_abi<'tcx>( continue; } } + + let pci = PseudoCanonicalInput { typing_env: TypingEnv::fully_monomorphized(), value: *ty }; + + let layout = match tcx.layout_of(pci) { + Ok(layout) => layout.layout, + Err(_) => { + bug!("failed to compute layout for type {:?}", ty); + } + }; + + match layout.backend_repr() { + rustc_abi::BackendRepr::ScalarPair(_, _) => { + new_activities.push(da[i].clone()); + new_positions.push(i + 1); + } + _ => {} + } } // now add the extra activities coming from slices // Reverse order to not invalidate the indices From 8dbd1b014ac43cc9c950e3b7e112b62d0963ec17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelo=20Dom=C3=ADnguez?= Date: Tue, 15 Jul 2025 09:56:51 +0000 Subject: [PATCH 591/682] doc and move single branch match to an if let --- compiler/rustc_codegen_llvm/src/builder/autodiff.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index a1cc4fdbdf67..a19a0d867ace 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -90,12 +90,12 @@ pub(crate) fn adjust_activity_to_abi<'tcx>( } }; - match layout.backend_repr() { - rustc_abi::BackendRepr::ScalarPair(_, _) => { - new_activities.push(da[i].clone()); - new_positions.push(i + 1); - } - _ => {} + // If the argument is lowered as a `ScalarPair`, we need to duplicate its activity. + // Otherwise, the number of activities won't match the number of LLVM arguments and + // this will lead to errors when verifying the Enzyme call. + if let rustc_abi::BackendRepr::ScalarPair(_, _) = layout.backend_repr() { + new_activities.push(da[i].clone()); + new_positions.push(i + 1); } } // now add the extra activities coming from slices From ef90d22ddad5f2f43689e17fde5f048bcd83fd5a Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Wed, 17 Sep 2025 20:04:57 +0800 Subject: [PATCH 592/682] update enzyme submodule --- src/tools/enzyme | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/enzyme b/src/tools/enzyme index 58af4e9e6c04..09f4820b78e2 160000 --- a/src/tools/enzyme +++ b/src/tools/enzyme @@ -1 +1 @@ -Subproject commit 58af4e9e6c047534ba059b12af17cecb8a2e9f9e +Subproject commit 09f4820b78e2d71b85a3278bbb41dc3a012e84dd From 0bf85d35ec9ed9cbffd274def09027c5fe9a8f3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelo=20Dom=C3=ADnguez?= Date: Sun, 3 Aug 2025 11:12:34 +0000 Subject: [PATCH 593/682] Support ZST args --- .../rustc_codegen_llvm/src/builder/autodiff.rs | 11 ++++++++++- tests/ui/autodiff/zst.rs | 17 +++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 tests/ui/autodiff/zst.rs diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index a19a0d867ace..78deffa3a7a6 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -29,6 +29,7 @@ pub(crate) fn adjust_activity_to_abi<'tcx>( let mut new_activities = vec![]; let mut new_positions = vec![]; + let mut del_activities = 0; for (i, ty) in sig.inputs().iter().enumerate() { if let Some(inner_ty) = ty.builtin_deref(true) { if inner_ty.is_slice() { @@ -90,12 +91,20 @@ pub(crate) fn adjust_activity_to_abi<'tcx>( } }; + // For ZST, just ignore and don't add its activity, as this arg won't be present + // in the LLVM passed to Enzyme. + // FIXME(Sa4dUs): Enforce ZST corresponding diff activity be `Const` + if layout.is_zst() { + del_activities += 1; + da.remove(i); + } + // If the argument is lowered as a `ScalarPair`, we need to duplicate its activity. // Otherwise, the number of activities won't match the number of LLVM arguments and // this will lead to errors when verifying the Enzyme call. if let rustc_abi::BackendRepr::ScalarPair(_, _) = layout.backend_repr() { new_activities.push(da[i].clone()); - new_positions.push(i + 1); + new_positions.push(i + 1 - del_activities); } } // now add the extra activities coming from slices diff --git a/tests/ui/autodiff/zst.rs b/tests/ui/autodiff/zst.rs new file mode 100644 index 000000000000..7b9b5f5f20bd --- /dev/null +++ b/tests/ui/autodiff/zst.rs @@ -0,0 +1,17 @@ +//@ compile-flags: -Zautodiff=Enable -C opt-level=3 -Clto=fat +//@ no-prefer-dynamic +//@ needs-enzyme +//@ build-pass + +// Check that differentiating functions with ZST args does not break + +#![feature(autodiff)] + +#[core::autodiff::autodiff_forward(fd_inner, Const, Dual)] +fn f(_zst: (), _x: &mut f64) {} + +fn fd(x: &mut f64, xd: &mut f64) { + fd_inner((), x, xd); +} + +fn main() {} From 923d1be6b6caec99a5eb46b3fb236f45f9e18233 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 17 Sep 2025 21:06:38 +1000 Subject: [PATCH 594/682] Use `LLVMDIBuilderCreateMemberType` --- .../src/debuginfo/metadata.rs | 4 +-- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 28 +++++++++---------- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 11 -------- 3 files changed, 16 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 0903ddab2854..f2000311ae47 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -1034,10 +1034,10 @@ fn create_member_type<'ll, 'tcx>( type_di_node: &'ll DIType, ) -> &'ll DIType { unsafe { - llvm::LLVMRustDIBuilderCreateMemberType( + llvm::LLVMDIBuilderCreateMemberType( DIB(cx), owner, - name.as_c_char_ptr(), + name.as_ptr(), name.len(), file_metadata, line_number, diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 71d8b7d25fe7..2549296d12c6 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1943,6 +1943,20 @@ unsafe extern "C" { UniqueId: *const c_uchar, // See "PTR_LEN_STR". UniqueIdLen: size_t, ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateMemberType<'ll>( + Builder: &DIBuilder<'ll>, + Scope: &'ll Metadata, + Name: *const c_uchar, // See "PTR_LEN_STR". + NameLen: size_t, + File: &'ll Metadata, + LineNo: c_uint, + SizeInBits: u64, + AlignInBits: u32, + OffsetInBits: u64, + Flags: DIFlags, + Ty: &'ll Metadata, + ) -> &'ll Metadata; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2288,20 +2302,6 @@ unsafe extern "C" { Scope: Option<&'a DIScope>, ) -> &'a DIDerivedType; - pub(crate) fn LLVMRustDIBuilderCreateMemberType<'a>( - Builder: &DIBuilder<'a>, - Scope: &'a DIDescriptor, - Name: *const c_char, - NameLen: size_t, - File: &'a DIFile, - LineNo: c_uint, - SizeInBits: u64, - AlignInBits: u32, - OffsetInBits: u64, - Flags: DIFlags, - Ty: &'a DIType, - ) -> &'a DIDerivedType; - pub(crate) fn LLVMRustDIBuilderCreateVariantMemberType<'a>( Builder: &DIBuilder<'a>, Scope: &'a DIScope, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index b4ca641674f0..af2469eeb934 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1088,17 +1088,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantPart( StringRef(UniqueId, UniqueIdLen))); } -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateMemberType( - LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, - size_t NameLen, LLVMMetadataRef File, unsigned LineNo, uint64_t SizeInBits, - uint32_t AlignInBits, uint64_t OffsetInBits, LLVMDIFlags Flags, - LLVMMetadataRef Ty) { - return wrap(unwrap(Builder)->createMemberType( - unwrapDI(Scope), StringRef(Name, NameLen), - unwrapDI(File), LineNo, SizeInBits, AlignInBits, OffsetInBits, - fromRust(Flags), unwrapDI(Ty))); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantMemberType( LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNo, uint64_t SizeInBits, From bb21dbeac79da68586d584ed2c7f8e87286fd8a8 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 17 Sep 2025 21:52:44 +1000 Subject: [PATCH 595/682] Use `LLVMDIBuilderCreateStaticMemberType` --- .../src/debuginfo/metadata/enums/cpp_like.rs | 65 +++++++++++++------ compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 26 ++++---- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 10 --- 3 files changed, 57 insertions(+), 44 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs index a5c808957410..28945d6a2d7d 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs @@ -11,7 +11,7 @@ use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, AdtDef, CoroutineArgs, CoroutineArgsExt, Ty}; use smallvec::smallvec; -use crate::common::{AsCCharPtr, CodegenCx}; +use crate::common::CodegenCx; use crate::debuginfo::dwarf_const::DW_TAG_const_type; use crate::debuginfo::metadata::enums::DiscrResult; use crate::debuginfo::metadata::type_map::{self, Stub, UniqueTypeId}; @@ -378,20 +378,17 @@ fn build_single_variant_union_fields<'ll, 'tcx>( variant_struct_type_wrapper_di_node, None, ), - unsafe { - llvm::LLVMRustDIBuilderCreateStaticMemberType( - DIB(cx), - enum_type_di_node, - TAG_FIELD_NAME.as_c_char_ptr(), - TAG_FIELD_NAME.len(), - unknown_file_metadata(cx), - UNKNOWN_LINE_NUMBER, - variant_names_type_di_node, - visibility_flags, - Some(cx.const_u64(SINGLE_VARIANT_VIRTUAL_DISR)), - tag_base_type_align.bits() as u32, - ) - } + create_static_member_type( + cx, + enum_type_di_node, + TAG_FIELD_NAME, + unknown_file_metadata(cx), + UNKNOWN_LINE_NUMBER, + variant_names_type_di_node, + visibility_flags, + Some(cx.const_u64(SINGLE_VARIANT_VIRTUAL_DISR)), + tag_base_type_align, + ), ] } @@ -576,21 +573,20 @@ fn build_variant_struct_wrapper_type_di_node<'ll, 'tcx>( // directly inspected via the debugger visualizer - which compares it to the `tag` value // (whose type is not modified at all) it shouldn't cause any real problems. let (t_di, align) = if name == ASSOC_CONST_DISCR_NAME { - (type_di_node_, align.bits() as u32) + (type_di_node_, align) } else { let ty_u64 = Ty::new_uint(cx.tcx, ty::UintTy::U64); - (type_di_node(cx, ty_u64), Align::EIGHT.bits() as u32) + (type_di_node(cx, ty_u64), Align::EIGHT) }; // must wrap type in a `const` modifier for LLDB to be able to inspect the value of the member let field_type = llvm::LLVMRustDIBuilderCreateQualifiedType(DIB(cx), DW_TAG_const_type, t_di); - llvm::LLVMRustDIBuilderCreateStaticMemberType( - DIB(cx), + create_static_member_type( + cx, wrapper_struct_type_di_node, - name.as_c_char_ptr(), - name.len(), + name, unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER, field_type, @@ -975,3 +971,30 @@ fn variant_struct_wrapper_type_name(variant_index: VariantIdx) -> Cow<'static, s .map(|&s| Cow::from(s)) .unwrap_or_else(|| format!("Variant{}", variant_index.as_usize()).into()) } + +fn create_static_member_type<'ll>( + cx: &CodegenCx<'ll, '_>, + scope: &'ll llvm::Metadata, + name: &str, + file: &'ll llvm::Metadata, + line_number: c_uint, + ty: &'ll llvm::Metadata, + flags: DIFlags, + value: Option<&'ll llvm::Value>, + align: Align, +) -> &'ll llvm::Metadata { + unsafe { + llvm::LLVMDIBuilderCreateStaticMemberType( + DIB(cx), + scope, + name.as_ptr(), + name.len(), + file, + line_number, + ty, + flags, + value, + align.bits() as c_uint, + ) + } +} diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 2549296d12c6..a157a9b8c261 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1957,6 +1957,19 @@ unsafe extern "C" { Flags: DIFlags, Ty: &'ll Metadata, ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateStaticMemberType<'ll>( + Builder: &DIBuilder<'ll>, + Scope: &'ll Metadata, + Name: *const c_uchar, // See "PTR_LEN_STR". + NameLen: size_t, + File: &'ll Metadata, + LineNumber: c_uint, + Type: &'ll Metadata, + Flags: DIFlags, + ConstantVal: Option<&'ll Value>, + AlignInBits: u32, + ) -> &'ll Metadata; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2317,19 +2330,6 @@ unsafe extern "C" { Ty: &'a DIType, ) -> &'a DIType; - pub(crate) fn LLVMRustDIBuilderCreateStaticMemberType<'a>( - Builder: &DIBuilder<'a>, - Scope: &'a DIDescriptor, - Name: *const c_char, - NameLen: size_t, - File: &'a DIFile, - LineNo: c_uint, - Ty: &'a DIType, - Flags: DIFlags, - val: Option<&'a Value>, - AlignInBits: u32, - ) -> &'a DIDerivedType; - pub(crate) fn LLVMRustDIBuilderCreateQualifiedType<'a>( Builder: &DIBuilder<'a>, Tag: c_uint, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index af2469eeb934..1b2303f5e745 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1103,16 +1103,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantMemberType( fromRust(Flags), unwrapDI(Ty))); } -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticMemberType( - LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, - size_t NameLen, LLVMMetadataRef File, unsigned LineNo, LLVMMetadataRef Ty, - LLVMDIFlags Flags, LLVMValueRef val, uint32_t AlignInBits) { - return wrap(unwrap(Builder)->createStaticMemberType( - unwrapDI(Scope), StringRef(Name, NameLen), - unwrapDI(File), LineNo, unwrapDI(Ty), fromRust(Flags), - unwrap(val), llvm::dwarf::DW_TAG_member, AlignInBits)); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateQualifiedType(LLVMDIBuilderRef Builder, unsigned Tag, LLVMMetadataRef Type) { From 002771ab5c3a329038843d578d5f83c0289a959d Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 17 Sep 2025 21:36:59 +1000 Subject: [PATCH 596/682] Use `LLVMDIBuilderCreateQualifiedType` --- .../src/debuginfo/metadata/enums/cpp_like.rs | 8 +++++--- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 14 ++++++++------ compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 7 ------- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs index 28945d6a2d7d..4ecc3086e1bd 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs @@ -567,7 +567,8 @@ fn build_variant_struct_wrapper_type_di_node<'ll, 'tcx>( let build_assoc_const = |name: &str, type_di_node_: &'ll DIType, value: u64, - align: Align| unsafe { + align: Align| + -> &'ll llvm::Metadata { // FIXME: Currently we force all DISCR_* values to be u64's as LLDB seems to have // problems inspecting other value types. Since DISCR_* is typically only going to be // directly inspected via the debugger visualizer - which compares it to the `tag` value @@ -580,8 +581,9 @@ fn build_variant_struct_wrapper_type_di_node<'ll, 'tcx>( }; // must wrap type in a `const` modifier for LLDB to be able to inspect the value of the member - let field_type = - llvm::LLVMRustDIBuilderCreateQualifiedType(DIB(cx), DW_TAG_const_type, t_di); + let field_type = unsafe { + llvm::LLVMDIBuilderCreateQualifiedType(DIB(cx), DW_TAG_const_type, t_di) + }; create_static_member_type( cx, diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index a157a9b8c261..7c8a3261bb82 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1970,6 +1970,14 @@ unsafe extern "C" { ConstantVal: Option<&'ll Value>, AlignInBits: u32, ) -> &'ll Metadata; + + /// Creates a "qualified type" in the C/C++ sense, by adding modifiers + /// like `const` or `volatile`. + pub(crate) fn LLVMDIBuilderCreateQualifiedType<'ll>( + Builder: &DIBuilder<'ll>, + Tag: c_uint, // (DWARF tag, e.g. `DW_TAG_const_type`) + Type: &'ll Metadata, + ) -> &'ll Metadata; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2330,12 +2338,6 @@ unsafe extern "C" { Ty: &'a DIType, ) -> &'a DIType; - pub(crate) fn LLVMRustDIBuilderCreateQualifiedType<'a>( - Builder: &DIBuilder<'a>, - Tag: c_uint, - Type: &'a DIType, - ) -> &'a DIDerivedType; - pub(crate) fn LLVMRustDIBuilderCreateStaticVariable<'a>( Builder: &DIBuilder<'a>, Context: Option<&'a DIScope>, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 1b2303f5e745..eff1b7562059 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1103,13 +1103,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantMemberType( fromRust(Flags), unwrapDI(Ty))); } -extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateQualifiedType(LLVMDIBuilderRef Builder, unsigned Tag, - LLVMMetadataRef Type) { - return wrap( - unwrap(Builder)->createQualifiedType(Tag, unwrapDI(Type))); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticVariable( LLVMDIBuilderRef Builder, LLVMMetadataRef Context, const char *Name, size_t NameLen, const char *LinkageName, size_t LinkageNameLen, From 6b51f7c07666b1b1446201477ad887fdfa26e08a Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 17 Sep 2025 21:36:59 +1000 Subject: [PATCH 597/682] Use `LLVMDIBuilderCreateTypedef` --- .../src/debuginfo/metadata.rs | 9 ++++---- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 23 ++++++++++--------- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 10 -------- 3 files changed, 17 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index f2000311ae47..aa8b8bd152dc 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -821,14 +821,15 @@ fn build_basic_type_di_node<'ll, 'tcx>( }; let typedef_di_node = unsafe { - llvm::LLVMRustDIBuilderCreateTypedef( + llvm::LLVMDIBuilderCreateTypedef( DIB(cx), ty_di_node, - typedef_name.as_c_char_ptr(), + typedef_name.as_ptr(), typedef_name.len(), unknown_file_metadata(cx), - 0, - None, + 0, // (no line number) + None, // (no scope) + 0u32, // (no alignment specified) ) }; diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 7c8a3261bb82..1124ebc3d44b 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -24,7 +24,7 @@ use rustc_target::spec::SymbolVisibility; use super::RustString; use super::debuginfo::{ - DIArray, DIBasicType, DIBuilder, DIDerivedType, DIDescriptor, DIEnumerator, DIFile, DIFlags, + DIArray, DIBuilder, DIDerivedType, DIDescriptor, DIEnumerator, DIFile, DIFlags, DIGlobalVariableExpression, DILocation, DISPFlags, DIScope, DISubprogram, DISubrange, DITemplateTypeParameter, DIType, DIVariable, DebugEmissionKind, DebugNameTableKind, }; @@ -1978,6 +1978,17 @@ unsafe extern "C" { Tag: c_uint, // (DWARF tag, e.g. `DW_TAG_const_type`) Type: &'ll Metadata, ) -> &'ll Metadata; + + pub(crate) fn LLVMDIBuilderCreateTypedef<'ll>( + Builder: &DIBuilder<'ll>, + Type: &'ll Metadata, + Name: *const c_uchar, // See "PTR_LEN_STR". + NameLen: size_t, + File: &'ll Metadata, + LineNo: c_uint, + Scope: Option<&'ll Metadata>, + AlignInBits: u32, // (optional; default is 0) + ) -> &'ll Metadata; } #[link(name = "llvm-wrapper", kind = "static")] @@ -2313,16 +2324,6 @@ unsafe extern "C" { TParam: &'a DIArray, ) -> &'a DISubprogram; - pub(crate) fn LLVMRustDIBuilderCreateTypedef<'a>( - Builder: &DIBuilder<'a>, - Type: &'a DIBasicType, - Name: *const c_char, - NameLen: size_t, - File: &'a DIFile, - LineNo: c_uint, - Scope: Option<&'a DIScope>, - ) -> &'a DIDerivedType; - pub(crate) fn LLVMRustDIBuilderCreateVariantMemberType<'a>( Builder: &DIBuilder<'a>, Scope: &'a DIScope, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index eff1b7562059..64151962321f 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -1064,16 +1064,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateMethod( return wrap(Sub); } -extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateTypedef(LLVMDIBuilderRef Builder, LLVMMetadataRef Type, - const char *Name, size_t NameLen, - LLVMMetadataRef File, unsigned LineNo, - LLVMMetadataRef Scope) { - return wrap(unwrap(Builder)->createTypedef( - unwrap(Type), StringRef(Name, NameLen), unwrap(File), - LineNo, unwrapDIPtr(Scope))); -} - extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateVariantPart( LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, const char *Name, size_t NameLen, LLVMMetadataRef File, unsigned LineNumber, From ea8baccbb15fd58afcde959eed7dc73741dd626d Mon Sep 17 00:00:00 2001 From: Tsukasa OI Date: Wed, 17 Sep 2025 12:35:21 +0000 Subject: [PATCH 598/682] rustc_codegen_llvm: Name major version of LLVM It makes LLVM version comparison clearer. --- compiler/rustc_codegen_llvm/src/llvm_util.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 8461c8b03d5a..393361a1afe2 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -228,6 +228,7 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option Some(LLVMFeature::with_dependencies( "sse4.2", @@ -260,7 +261,7 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option None, // only existed in 18 ("arm", "fp16") => Some(LLVMFeature::new("fullfp16")), // Filter out features that are not supported by the current LLVM version - ("loongarch32" | "loongarch64", "32s") if get_version().0 < 21 => None, + ("loongarch32" | "loongarch64", "32s") if major < 21 => None, // Enable the evex512 target feature if an avx512 target feature is enabled. ("x86", s) if s.starts_with("avx512") => Some(LLVMFeature::with_dependencies( s, From d9f67cbb8be4500ba73bb30177c05153cbc9424a Mon Sep 17 00:00:00 2001 From: Tsukasa OI Date: Wed, 17 Sep 2025 12:35:21 +0000 Subject: [PATCH 599/682] rustc_codegen_llvm: Simplify `arch` conversion This commit simplifies construction of `arch` from `sess.target.arch`. It also preserves a reference to `sess.target.arch` as `raw_arch` to make this function future proof. --- compiler/rustc_codegen_llvm/src/llvm_util.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 393361a1afe2..6849cd241644 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -217,16 +217,13 @@ impl<'a> IntoIterator for LLVMFeature<'a> { /// Rust can also be build with an external precompiled version of LLVM which might lead to failures /// if the oldest tested / supported LLVM version doesn't yet support the relevant intrinsics. pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option> { - let arch = if sess.target.arch == "x86_64" { - "x86" - } else if sess.target.arch == "arm64ec" { - "aarch64" - } else if sess.target.arch == "sparc64" { - "sparc" - } else if sess.target.arch == "powerpc64" { - "powerpc" - } else { - &*sess.target.arch + let raw_arch = &*sess.target.arch; + let arch = match raw_arch { + "x86_64" => "x86", + "arm64ec" => "aarch64", + "sparc64" => "sparc", + "powerpc64" => "powerpc", + _ => raw_arch, }; let (major, _, _) = get_version(); match (arch, s) { From a1a3cd043815a8b6e5faf53f33002d8dec1b2f30 Mon Sep 17 00:00:00 2001 From: Tsukasa OI Date: Wed, 17 Sep 2025 12:35:21 +0000 Subject: [PATCH 600/682] rustc_codegen_llvm: Reorder conversion cases For maintainability, this commit reorders target feature conversion cases by the architecture. --- compiler/rustc_codegen_llvm/src/llvm_util.rs | 22 ++++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 6849cd241644..45c5c9aa5514 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -227,15 +227,6 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option Some(LLVMFeature::with_dependencies( - "sse4.2", - smallvec![TargetFeatureFoldStrength::EnableOnly("crc32")], - )), - ("x86", "pclmulqdq") => Some(LLVMFeature::new("pclmul")), - ("x86", "rdrand") => Some(LLVMFeature::new("rdrnd")), - ("x86", "bmi1") => Some(LLVMFeature::new("bmi")), - ("x86", "cmpxchg16b") => Some(LLVMFeature::new("cx16")), - ("x86", "lahfsahf") => Some(LLVMFeature::new("sahf")), ("aarch64", "rcpc2") => Some(LLVMFeature::new("rcpc-immo")), ("aarch64", "dpb") => Some(LLVMFeature::new("ccpp")), ("aarch64", "dpb2") => Some(LLVMFeature::new("ccdp")), @@ -259,13 +250,22 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option Some(LLVMFeature::new("fullfp16")), // Filter out features that are not supported by the current LLVM version ("loongarch32" | "loongarch64", "32s") if major < 21 => None, + ("powerpc", "power8-crypto") => Some(LLVMFeature::new("crypto")), + ("sparc", "leoncasa") => Some(LLVMFeature::new("hasleoncasa")), + ("x86", "sse4.2") => Some(LLVMFeature::with_dependencies( + "sse4.2", + smallvec![TargetFeatureFoldStrength::EnableOnly("crc32")], + )), + ("x86", "pclmulqdq") => Some(LLVMFeature::new("pclmul")), + ("x86", "rdrand") => Some(LLVMFeature::new("rdrnd")), + ("x86", "bmi1") => Some(LLVMFeature::new("bmi")), + ("x86", "cmpxchg16b") => Some(LLVMFeature::new("cx16")), + ("x86", "lahfsahf") => Some(LLVMFeature::new("sahf")), // Enable the evex512 target feature if an avx512 target feature is enabled. ("x86", s) if s.starts_with("avx512") => Some(LLVMFeature::with_dependencies( s, smallvec![TargetFeatureFoldStrength::EnableOnly("evex512")], )), - ("sparc", "leoncasa") => Some(LLVMFeature::new("hasleoncasa")), - ("powerpc", "power8-crypto") => Some(LLVMFeature::new("crypto")), ("x86", "avx10.1") => Some(LLVMFeature::new("avx10.1-512")), ("x86", "avx10.2") => Some(LLVMFeature::new("avx10.2-512")), ("x86", "apxf") => Some(LLVMFeature::with_dependencies( From 6e101637fa9485e71e77217ab32cbfb0f013e6e5 Mon Sep 17 00:00:00 2001 From: Bart Jacobs Date: Wed, 17 Sep 2025 15:46:23 +0200 Subject: [PATCH 601/682] Add the `rust-analyzer.semanticHighlighting.comments.enable` configuration value --- src/tools/rust-analyzer/crates/ide/src/lib.rs | 14 ++++++ .../crates/ide/src/syntax_highlighting.rs | 4 ++ .../ide/src/syntax_highlighting/html.rs | 42 +++++++++------- .../ide/src/syntax_highlighting/inject.rs | 2 + .../highlight_comments_disabled.html | 48 +++++++++++++++++++ .../ide/src/syntax_highlighting/tests.rs | 37 ++++++++++++-- .../crates/rust-analyzer/src/config.rs | 8 ++++ .../docs/book/src/configuration_generated.md | 11 +++++ .../rust-analyzer/editors/code/package.json | 10 ++++ 9 files changed, 157 insertions(+), 19 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_comments_disabled.html diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index cddf5f04f244..481c2312176b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -703,6 +703,20 @@ impl Analysis { }) } + /// Computes syntax highlighting for the given file. + pub fn highlight_as_html_with_config( + &self, + config: HighlightConfig, + file_id: FileId, + rainbow: bool, + ) -> Cancellable { + // highlighting may construct a new database for "speculative" execution, so we can't currently attach the database + // highlighting instead sets up the attach hook where neceesary for the trait solver + Cancelled::catch(|| { + syntax_highlighting::highlight_as_html_with_config(&self.db, config, file_id, rainbow) + }) + } + /// Computes syntax highlighting for the given file. pub fn highlight_as_html(&self, file_id: FileId, rainbow: bool) -> Cancellable { // highlighting may construct a new database for "speculative" execution, so we can't currently attach the database diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs index f98770805a45..4e43387f8d9d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs @@ -35,6 +35,7 @@ use crate::{ }; pub(crate) use html::highlight_as_html; +pub(crate) use html::highlight_as_html_with_config; #[derive(Debug, Clone, Copy)] pub struct HlRange { @@ -47,6 +48,8 @@ pub struct HlRange { pub struct HighlightConfig { /// Whether to highlight strings pub strings: bool, + /// Whether to highlight comments + pub comments: bool, /// Whether to highlight punctuation pub punctuation: bool, /// Whether to specialize punctuation highlights @@ -588,6 +591,7 @@ fn descend_token( fn filter_by_config(highlight: &mut Highlight, config: HighlightConfig) -> bool { match &mut highlight.tag { HlTag::StringLiteral if !config.strings => return false, + HlTag::Comment if !config.comments => return false, // If punctuation is disabled, make the macro bang part of the macro call again. tag @ HlTag::Punctuation(HlPunct::MacroBang) => { if !config.macro_bang { diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs index 9fd807f031f1..358ac9b4ef35 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/html.rs @@ -10,7 +10,12 @@ use crate::{ syntax_highlighting::{HighlightConfig, highlight}, }; -pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: bool) -> String { +pub(crate) fn highlight_as_html_with_config( + db: &RootDatabase, + config: HighlightConfig, + file_id: FileId, + rainbow: bool, +) -> String { let sema = Semantics::new(db); let file_id = sema .attach_first_edition(file_id) @@ -27,21 +32,7 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo ) } - let hl_ranges = highlight( - db, - HighlightConfig { - strings: true, - punctuation: true, - specialize_punctuation: true, - specialize_operator: true, - operator: true, - inject_doc_comment: true, - macro_bang: true, - syntactic_name_ref_highlighting: false, - }, - file_id.file_id(db), - None, - ); + let hl_ranges = highlight(db, config, file_id.file_id(db), None); let text = file.to_string(); let mut buf = String::new(); buf.push_str(STYLE); @@ -66,6 +57,25 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo buf } +pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: bool) -> String { + highlight_as_html_with_config( + db, + HighlightConfig { + strings: true, + comments: true, + punctuation: true, + specialize_punctuation: true, + specialize_operator: true, + operator: true, + inject_doc_comment: true, + macro_bang: true, + syntactic_name_ref_highlighting: false, + }, + file_id, + rainbow, + ) +} + //FIXME: like, real html escaping fn html_escape(text: &str) -> String { text.replace('<', "<").replace('>', ">") diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs index abe7be8c6888..4bb730802414 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs @@ -80,6 +80,7 @@ pub(super) fn ra_fixture( .highlight( HighlightConfig { syntactic_name_ref_highlighting: false, + comments: true, punctuation: true, operator: true, strings: true, @@ -250,6 +251,7 @@ pub(super) fn doc_comment( db, HighlightConfig { syntactic_name_ref_highlighting: true, + comments: true, punctuation: true, operator: true, strings: true, diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_comments_disabled.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_comments_disabled.html new file mode 100644 index 000000000000..4607448bebaa --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_comments_disabled.html @@ -0,0 +1,48 @@ + + +

// This is a regular comment
+/// This is a doc comment
+fn main() {
+    // Another comment
+    println!("Hello, world!");
+}
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs index dd359326c61d..8198701d6843 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs @@ -9,6 +9,7 @@ use crate::{FileRange, HighlightConfig, HlTag, TextRange, fixture}; const HL_CONFIG: HighlightConfig = HighlightConfig { strings: true, + comments: true, punctuation: true, specialize_punctuation: true, specialize_operator: true, @@ -1220,14 +1221,23 @@ fn foo(x: &fn(&dyn Trait)) {} /// Highlights the code given by the `ra_fixture` argument, renders the /// result as HTML, and compares it with the HTML file given as `snapshot`. /// Note that the `snapshot` file is overwritten by the rendered HTML. +fn check_highlighting_with_config( + #[rust_analyzer::rust_fixture] ra_fixture: &str, + config: HighlightConfig, + expect: ExpectFile, + rainbow: bool, +) { + let (analysis, file_id) = fixture::file(ra_fixture.trim()); + let actual_html = &analysis.highlight_as_html_with_config(config, file_id, rainbow).unwrap(); + expect.assert_eq(actual_html) +} + fn check_highlighting( #[rust_analyzer::rust_fixture] ra_fixture: &str, expect: ExpectFile, rainbow: bool, ) { - let (analysis, file_id) = fixture::file(ra_fixture.trim()); - let actual_html = &analysis.highlight_as_html(file_id, rainbow).unwrap(); - expect.assert_eq(actual_html) + check_highlighting_with_config(ra_fixture, HL_CONFIG, expect, rainbow) } #[test] @@ -1435,3 +1445,24 @@ fn main() { false, ); } + +#[test] +fn test_comment_highlighting_disabled() { + // Test that comments are not highlighted when disabled + check_highlighting_with_config( + r#" +// This is a regular comment +/// This is a doc comment +fn main() { + // Another comment + println!("Hello, world!"); +} +"#, + HighlightConfig { + comments: false, // Disable comment highlighting + ..HL_CONFIG + }, + expect_file!["./test_data/highlight_comments_disabled.html"], + false, + ); +} diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index c2252185a3aa..8fe0a5d2eb66 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -382,6 +382,13 @@ config_data! { /// Exclude tests from find-all-references and call-hierarchy. references_excludeTests: bool = false, + /// Use semantic tokens for comments. + /// + /// In some editors (e.g. vscode) semantic tokens override other highlighting grammars. + /// By disabling semantic tokens for comments, other grammars can be used to highlight + /// their contents. + semanticHighlighting_comments_enable: bool = true, + /// Inject additional highlighting into doc comments. /// /// When enabled, rust-analyzer will highlight rust source in doc comments as well as intra @@ -1968,6 +1975,7 @@ impl Config { pub fn highlighting_config(&self) -> HighlightConfig { HighlightConfig { strings: self.semanticHighlighting_strings_enable().to_owned(), + comments: self.semanticHighlighting_comments_enable().to_owned(), punctuation: self.semanticHighlighting_punctuation_enable().to_owned(), specialize_punctuation: self .semanticHighlighting_punctuation_specialization_enable() diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md index 9a51212462db..50dacd88f407 100644 --- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -1378,6 +1378,17 @@ Enables the use of rustfmt's unstable range formatting command for the available on a nightly build. +## rust-analyzer.semanticHighlighting.comments.enable {#semanticHighlighting.comments.enable} + +Default: `true` + +Use semantic tokens for comments. + +In some editors (e.g. vscode) semantic tokens override other highlighting grammars. +By disabling semantic tokens for comments, other grammars can be used to highlight +their contents. + + ## rust-analyzer.semanticHighlighting.doc.comment.inject.enable {#semanticHighlighting.doc.comment.inject.enable} Default: `true` diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 2b2e25e11c88..1d27a1205355 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -2850,6 +2850,16 @@ } } }, + { + "title": "Semantic Highlighting", + "properties": { + "rust-analyzer.semanticHighlighting.comments.enable": { + "markdownDescription": "Use semantic tokens for comments.\n\nIn some editors (e.g. vscode) semantic tokens override other highlighting grammars.\nBy disabling semantic tokens for comments, other grammars can be used to highlight\ntheir contents.", + "default": true, + "type": "boolean" + } + } + }, { "title": "Semantic Highlighting", "properties": { From e04567c363e1f0417bf8bf24830c2bc536020582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelo=20Dom=C3=ADnguez?= Date: Wed, 17 Sep 2025 13:58:17 +0000 Subject: [PATCH 602/682] Check ZST via `PassMode` --- .../src/builder/autodiff.rs | 20 ++++++++++++++++--- compiler/rustc_codegen_llvm/src/intrinsic.rs | 3 ++- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index 78deffa3a7a6..566877f4a1ec 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -3,8 +3,9 @@ use std::ptr; use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode}; use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, BuilderMethods}; -use rustc_middle::ty::{PseudoCanonicalInput, Ty, TyCtxt, TypingEnv}; +use rustc_middle::ty::{Instance, PseudoCanonicalInput, TyCtxt, TypingEnv}; use rustc_middle::{bug, ty}; +use rustc_target::callconv::PassMode; use tracing::debug; use crate::builder::{Builder, PlaceRef, UNNAMED}; @@ -16,9 +17,12 @@ use crate::value::Value; pub(crate) fn adjust_activity_to_abi<'tcx>( tcx: TyCtxt<'tcx>, - fn_ty: Ty<'tcx>, + instance: Instance<'tcx>, + typing_env: TypingEnv<'tcx>, da: &mut Vec, ) { + let fn_ty = instance.ty(tcx, typing_env); + if !matches!(fn_ty.kind(), ty::FnDef(..)) { bug!("expected fn def for autodiff, got {:?}", fn_ty); } @@ -27,6 +31,13 @@ pub(crate) fn adjust_activity_to_abi<'tcx>( // All we do is decide how to handle the arguments. let sig = fn_ty.fn_sig(tcx).skip_binder(); + // FIXME(Sa4dUs): pass proper varargs once we have support for differentiating variadic functions + let Ok(fn_abi) = + tcx.fn_abi_of_instance(typing_env.as_query_input((instance, ty::List::empty()))) + else { + bug!("failed to get fn_abi of instance with empty varargs"); + }; + let mut new_activities = vec![]; let mut new_positions = vec![]; let mut del_activities = 0; @@ -91,10 +102,13 @@ pub(crate) fn adjust_activity_to_abi<'tcx>( } }; + let pass_mode = &fn_abi.args[i].mode; + // For ZST, just ignore and don't add its activity, as this arg won't be present // in the LLVM passed to Enzyme. + // Some targets pass ZST indirectly in the C ABI, in that case, handle it as a normal arg // FIXME(Sa4dUs): Enforce ZST corresponding diff activity be `Const` - if layout.is_zst() { + if *pass_mode == PassMode::Ignore { del_activities += 1; da.remove(i); } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 06c3d8ed6bc2..9e6e76064912 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -1198,7 +1198,8 @@ fn codegen_autodiff<'ll, 'tcx>( adjust_activity_to_abi( tcx, - fn_source.ty(tcx, TypingEnv::fully_monomorphized()), + fn_source, + TypingEnv::fully_monomorphized(), &mut diff_attrs.input_activity, ); From 28b0e4e15e0ffd0c5f747f94dadb7a243566449d Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 17 Sep 2025 22:57:25 +0800 Subject: [PATCH 603/682] Fix applicable on variant field for change_visibility Enum variant fields do not allow visibility Example --- ```rust enum Foo { Variant($0String), } ``` **Before this PR**: ```rust enum Foo { Variant(pub(crate) String), } ``` **After this PR**: Assist not applicable --- .../ide-assists/src/handlers/change_visibility.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/change_visibility.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/change_visibility.rs index 9b9f0c4522ed..7119d5b9c23e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/change_visibility.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/change_visibility.rs @@ -65,11 +65,13 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { if field.visibility().is_some() { return None; } + check_is_not_variant(&field)?; (vis_offset(field.syntax()), field_name.syntax().text_range()) } else if let Some(field) = ctx.find_node_at_offset::() { if field.visibility().is_some() { return None; } + check_is_not_variant(&field)?; (vis_offset(field.syntax()), field.syntax().text_range()) } else { return None; @@ -134,6 +136,11 @@ fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> { None } +fn check_is_not_variant(field: &impl AstNode) -> Option<()> { + let kind = field.syntax().parent()?.parent()?.kind(); + (kind != SyntaxKind::VARIANT).then_some(()) +} + #[cfg(test)] mod tests { use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; @@ -239,6 +246,13 @@ mod tests { ); } + #[test] + fn not_applicable_for_enum_variant_fields() { + check_assist_not_applicable(change_visibility, r"pub enum Foo { Foo1($0i32) }"); + + check_assist_not_applicable(change_visibility, r"pub enum Foo { Foo1 { $0n: i32 } }"); + } + #[test] fn change_visibility_target() { check_assist_target(change_visibility, "$0fn foo() {}", "fn"); From 301092e249f3773fd41c8af7170acd88a2681c89 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Wed, 17 Sep 2025 17:36:17 +0200 Subject: [PATCH 604/682] Remove automatic feature freeze comment The feature freeze period is over. --- .github/workflows/feature_freeze.yml | 45 --------------------- book/src/README.md | 4 -- book/src/SUMMARY.md | 1 - book/src/development/adding_lints.md | 3 -- book/src/development/feature_freeze.md | 55 -------------------------- 5 files changed, 108 deletions(-) delete mode 100644 .github/workflows/feature_freeze.yml delete mode 100644 book/src/development/feature_freeze.md diff --git a/.github/workflows/feature_freeze.yml b/.github/workflows/feature_freeze.yml deleted file mode 100644 index 5b139e767007..000000000000 --- a/.github/workflows/feature_freeze.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: Feature freeze check - -on: - pull_request_target: - types: - - opened - branches: - - master - paths: - - 'clippy_lints/src/declared_lints.rs' - -jobs: - auto-comment: - runs-on: ubuntu-latest - - permissions: - pull-requests: write - - # Do not in any case add code that runs anything coming from the content - # of the pull request, as malicious code would be able to access the private - # GitHub token. - steps: - - name: Add freeze warning comment - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GITHUB_REPOSITORY: ${{ github.repository }} - PR_NUMBER: ${{ github.event.pull_request.number }} - run: | - COMMENT=$(echo "**Seems that you are trying to add a new lint!**\n\ - \n\ - We are currently in a [feature freeze](https://doc.rust-lang.org/nightly/clippy/development/feature_freeze.html), so we are delaying all lint-adding PRs to September 18 and [focusing on bugfixes](https://github.com/rust-lang/rust-clippy/issues/15086).\n\ - \n\ - Thanks a lot for your contribution, and sorry for the inconvenience.\n\ - \n\ - With ❤ from the Clippy team.\n\ - \n\ - @rustbot note Feature-freeze\n\ - @rustbot blocked\n\ - @rustbot label +A-lint" - ) - curl -s -H "Authorization: Bearer $GITHUB_TOKEN" \ - -H "Content-Type: application/vnd.github.raw+json" \ - -X POST \ - --data "{\"body\":\"${COMMENT}\"}" \ - "https://api.github.com/repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" diff --git a/book/src/README.md b/book/src/README.md index db73b49ecc24..5d2c3972b060 100644 --- a/book/src/README.md +++ b/book/src/README.md @@ -1,9 +1,5 @@ # Clippy -[### IMPORTANT NOTE FOR CONTRIBUTORS ================](development/feature_freeze.md) - ----- - [![License: MIT OR Apache-2.0](https://img.shields.io/crates/l/clippy.svg)](https://github.com/rust-lang/rust-clippy#license) A collection of lints to catch common mistakes and improve your diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index b66c3481e493..39fe7358ed87 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -13,7 +13,6 @@ - [GitLab CI](continuous_integration/gitlab.md) - [Travis CI](continuous_integration/travis.md) - [Development](development/README.md) - - [IMPORTANT: FEATURE FREEZE](development/feature_freeze.md) - [Basics](development/basics.md) - [Adding Lints](development/adding_lints.md) - [Defining Lints](development/defining_lints.md) diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md index a42a29837446..2b89e94cf8f4 100644 --- a/book/src/development/adding_lints.md +++ b/book/src/development/adding_lints.md @@ -1,8 +1,5 @@ # Adding a new lint -[### IMPORTANT NOTE FOR CONTRIBUTORS ================](feature_freeze.md) - - You are probably here because you want to add a new lint to Clippy. If this is the first time you're contributing to Clippy, this document guides you through creating an example lint from scratch. diff --git a/book/src/development/feature_freeze.md b/book/src/development/feature_freeze.md deleted file mode 100644 index 260cb136cc07..000000000000 --- a/book/src/development/feature_freeze.md +++ /dev/null @@ -1,55 +0,0 @@ -# IMPORTANT: FEATURE FREEZE - -This is a temporary notice. - -From the 26th of June until the 18th of September we will perform a feature freeze. Only bugfix PRs will be reviewed -except already open ones. Every feature-adding PR opened in between those dates will be moved into a -milestone to be reviewed separately at another time. - -We do this because of the long backlog of bugs that need to be addressed -in order to continue being the state-of-the-art linter that Clippy has become known for being. - -## For contributors - -If you are a contributor or are planning to become one, **please do not open a lint-adding PR**, we have lots of open -bugs of all levels of difficulty that you can address instead! - -We currently have about 800 lints, each one posing a maintainability challenge that needs to account to every possible -use case of the whole ecosystem. Bugs are natural in every software, but the Clippy team considers that Clippy needs a -refinement period. - -If you open a PR at this time, we will not review it but push it into a milestone until the refinement period ends, -adding additional load into our reviewing schedules. - -## I want to help, what can I do - -Thanks a lot to everyone who wants to help Clippy become better software in this feature freeze period! -If you'd like to help, making a bugfix, making sure that it works, and opening a PR is a great step! - -To find things to fix, go to the [tracking issue][tracking_issue], find an issue that you like, go there and claim that -issue with `@rustbot claim`. - -As a general metric and always taking into account your skill and knowledge level, you can use this guide: - -- 🟥 [ICEs][search_ice], these are compiler errors that causes Clippy to panic and crash. Usually involves high-level -debugging, sometimes interacting directly with the upstream compiler. Difficult to fix but a great challenge that -improves a lot developer workflows! - -- 🟧 [Suggestion causes bug][sugg_causes_bug], Clippy suggested code that changed logic in some silent way. -Unacceptable, as this may have disastrous consequences. Easier to fix than ICEs - -- 🟨 [Suggestion causes error][sugg_causes_error], Clippy suggested code snippet that caused a compiler error -when applied. We need to make sure that Clippy doesn't suggest using a variable twice at the same time or similar -easy-to-happen occurrences. - -- 🟩 [False positives][false_positive], a lint should not have fired, the easiest of them all, as this is "just" -identifying the root of a false positive and making an exception for those cases. - -Note that false negatives do not have priority unless the case is very clear, as they are a feature-request in a -trench coat. - -[search_ice]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc+state%3Aopen+label%3A%22I-ICE%22 -[sugg_causes_bug]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc%20state%3Aopen%20label%3AI-suggestion-causes-bug -[sugg_causes_error]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc%20state%3Aopen%20label%3AI-suggestion-causes-error%20 -[false_positive]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc%20state%3Aopen%20label%3AI-false-positive -[tracking_issue]: https://github.com/rust-lang/rust-clippy/issues/15086 From e84b5ca1dd40d09b261b0a495c6cbd6a5d8cb194 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Wed, 17 Sep 2025 18:51:42 +0200 Subject: [PATCH 605/682] Do not look for `Cargo.toml` inside `target` This test, which checks that we do not define new profiles directly in Clippy's multiple `Cargo.toml` files, must not look inside `target` as `lintcheck` might place some third-party sources there. Of course those third-party sources are allowed to define profiles in their `Cargo.toml`. --- tests/no-profile-in-cargo-toml.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/no-profile-in-cargo-toml.rs b/tests/no-profile-in-cargo-toml.rs index 2ad9bfb75dee..1f8c4fae9b31 100644 --- a/tests/no-profile-in-cargo-toml.rs +++ b/tests/no-profile-in-cargo-toml.rs @@ -17,6 +17,9 @@ fn no_profile_in_cargo_toml() { // keep it fast and simple. for entry in WalkDir::new(".") .into_iter() + // Do not recurse into `target` as lintcheck might put some sources (and their + // `Cargo.toml`) there. + .filter_entry(|e| e.file_name() != "target") .filter_map(Result::ok) .filter(|e| e.file_name().to_str() == Some("Cargo.toml")) { From b2b43b25a845ff624d4b4c0efb604ed3b684a3bd Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Wed, 17 Sep 2025 12:59:07 -0400 Subject: [PATCH 606/682] Add space after brace in `Box<[T]>::new_uninit_slice` example --- library/alloc/src/boxed.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 98c9f6b51ab8..7d2dd12c6eae 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -640,7 +640,7 @@ impl Box<[T]> { /// values[0].write(1); /// values[1].write(2); /// values[2].write(3); - /// let values = unsafe {values.assume_init() }; + /// let values = unsafe { values.assume_init() }; /// /// assert_eq!(*values, [1, 2, 3]) /// ``` From 205189c8c767b3910a630f79b156417b82b3558b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Sun, 24 Aug 2025 11:19:51 +0200 Subject: [PATCH 607/682] port `#[rustc_coherence_is_core]` to the new attribute parsing infrastructure --- .../rustc_attr_parsing/src/attributes/crate_level.rs | 9 +++++++++ compiler/rustc_attr_parsing/src/attributes/traits.rs | 8 -------- compiler/rustc_attr_parsing/src/context.rs | 12 ++++++------ compiler/rustc_hir/src/attrs/data_structures.rs | 6 +++--- compiler/rustc_hir/src/attrs/encode_cross_crate.rs | 2 +- compiler/rustc_middle/src/hir/map.rs | 2 +- compiler/rustc_passes/src/check_attr.rs | 2 +- 7 files changed, 21 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index 4611de444599..0a340cd5e933 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -174,3 +174,12 @@ impl NoArgsAttributeParser for NoStdParser { const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoStd; } + +pub(crate) struct RustcCoherenceIsCoreParser; + +impl NoArgsAttributeParser for RustcCoherenceIsCoreParser { + const PATH: &[Symbol] = &[sym::rustc_coherence_is_core]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcCoherenceIsCore; +} diff --git a/compiler/rustc_attr_parsing/src/attributes/traits.rs b/compiler/rustc_attr_parsing/src/attributes/traits.rs index c756bce96e28..ced3bcad2293 100644 --- a/compiler/rustc_attr_parsing/src/attributes/traits.rs +++ b/compiler/rustc_attr_parsing/src/attributes/traits.rs @@ -149,14 +149,6 @@ impl NoArgsAttributeParser for AllowIncoherentImplParser { const CREATE: fn(Span) -> AttributeKind = AttributeKind::AllowIncoherentImpl; } -pub(crate) struct CoherenceIsCoreParser; -impl NoArgsAttributeParser for CoherenceIsCoreParser { - const PATH: &[Symbol] = &[sym::rustc_coherence_is_core]; - const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; - const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::CrateLevel; - const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::CoherenceIsCore; -} - pub(crate) struct FundamentalParser; impl NoArgsAttributeParser for FundamentalParser { const PATH: &[Symbol] = &[sym::fundamental]; diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 58b132924841..ee5b7322b024 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -26,7 +26,7 @@ use crate::attributes::codegen_attrs::{ use crate::attributes::confusables::ConfusablesParser; use crate::attributes::crate_level::{ CrateNameParser, MoveSizeLimitParser, NoCoreParser, NoStdParser, PatternComplexityLimitParser, - RecursionLimitParser, TypeLengthLimitParser, + RecursionLimitParser, RustcCoherenceIsCoreParser, TypeLengthLimitParser, }; use crate::attributes::deprecation::DeprecationParser; use crate::attributes::dummy::DummyParser; @@ -61,10 +61,10 @@ use crate::attributes::stability::{ }; use crate::attributes::test_attrs::{IgnoreParser, ShouldPanicParser}; use crate::attributes::traits::{ - AllowIncoherentImplParser, CoherenceIsCoreParser, CoinductiveParser, ConstTraitParser, - DenyExplicitImplParser, DoNotImplementViaObjectParser, FundamentalParser, MarkerParser, - ParenSugarParser, PointeeParser, SkipDuringMethodDispatchParser, SpecializationTraitParser, - TypeConstParser, UnsafeSpecializationMarkerParser, + AllowIncoherentImplParser, CoinductiveParser, ConstTraitParser, DenyExplicitImplParser, + DoNotImplementViaObjectParser, FundamentalParser, MarkerParser, ParenSugarParser, + PointeeParser, SkipDuringMethodDispatchParser, SpecializationTraitParser, TypeConstParser, + UnsafeSpecializationMarkerParser, }; use crate::attributes::transparency::TransparencyParser; use crate::attributes::{AttributeParser as _, Combine, Single, WithoutArgs}; @@ -206,7 +206,6 @@ attribute_parsers!( Single>, Single>, Single>, - Single>, Single>, Single>, Single>, @@ -234,6 +233,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 04b144abd03d..0784675b177a 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -444,9 +444,6 @@ pub enum AttributeKind { span: Span, }, - /// Represents `#[rustc_coherence_is_core]`. - CoherenceIsCore, - /// Represents `#[rustc_coinductive]`. Coinductive(Span), @@ -639,6 +636,9 @@ pub enum AttributeKind { /// Represents `#[rustc_builtin_macro]`. RustcBuiltinMacro { builtin_name: Option, helper_attrs: ThinVec, span: Span }, + /// Represents `#[rustc_coherence_is_core]` + RustcCoherenceIsCore(Span), + /// Represents `#[rustc_layout_scalar_valid_range_end]`. RustcLayoutScalarValidRangeEnd(Box, Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index cb4feeb05f1a..563e7a58c6d5 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -26,7 +26,6 @@ impl AttributeKind { AsPtr(..) => Yes, AutomaticallyDerived(..) => Yes, BodyStability { .. } => No, - CoherenceIsCore => No, Coinductive(..) => No, Cold(..) => No, Confusables { .. } => Yes, @@ -84,6 +83,7 @@ impl AttributeKind { RecursionLimit { .. } => No, Repr { .. } => No, RustcBuiltinMacro { .. } => Yes, + RustcCoherenceIsCore(..) => No, RustcLayoutScalarValidRangeEnd(..) => Yes, RustcLayoutScalarValidRangeStart(..) => Yes, RustcObjectLifetimeDefault => No, diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs index 4370816d38e5..430cd329408f 100644 --- a/compiler/rustc_middle/src/hir/map.rs +++ b/compiler/rustc_middle/src/hir/map.rs @@ -370,7 +370,7 @@ impl<'tcx> TyCtxt<'tcx> { } pub fn hir_rustc_coherence_is_core(self) -> bool { - find_attr!(self.hir_krate_attrs(), AttributeKind::CoherenceIsCore) + find_attr!(self.hir_krate_attrs(), AttributeKind::RustcCoherenceIsCore(..)) } pub fn hir_get_module(self, module: LocalModDefId) -> (&'tcx Mod<'tcx>, Span, HirId) { diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 60f575cb8441..4d5a8447695b 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -246,7 +246,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::Repr { .. } | AttributeKind::Cold(..) | AttributeKind::ExportName { .. } - | AttributeKind::CoherenceIsCore | AttributeKind::Fundamental | AttributeKind::Optimize(..) | AttributeKind::LinkSection { .. } @@ -278,6 +277,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::NoStd { .. } | AttributeKind::ObjcClass { .. } | AttributeKind::ObjcSelector { .. } + | AttributeKind::RustcCoherenceIsCore(..) ) => { /* do nothing */ } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); From 1d8971cf4e5f954c7fc9a2611dcf359a832f4c7a Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Tue, 16 Sep 2025 10:34:04 +0000 Subject: [PATCH 608/682] Cleanup `FnDecl::inner_full_print` --- src/librustdoc/display.rs | 6 +- src/librustdoc/html/format.rs | 109 +++++++++++++++++++--------------- 2 files changed, 63 insertions(+), 52 deletions(-) diff --git a/src/librustdoc/display.rs b/src/librustdoc/display.rs index aa0fad265208..db868c5c9a8f 100644 --- a/src/librustdoc/display.rs +++ b/src/librustdoc/display.rs @@ -10,7 +10,7 @@ pub(crate) trait Joined: IntoIterator { /// /// The performance of `joined` is slightly better than `format`, since it doesn't need to use a `Cell` to keep track of whether [`fmt`](Display::fmt) /// was already called (`joined`'s API doesn't allow it be called more than once). - fn joined(self, sep: &str, f: &mut Formatter<'_>) -> fmt::Result; + fn joined(self, sep: impl Display, f: &mut Formatter<'_>) -> fmt::Result; } impl Joined for I @@ -18,12 +18,12 @@ where I: IntoIterator, T: Display, { - fn joined(self, sep: &str, f: &mut Formatter<'_>) -> fmt::Result { + fn joined(self, sep: impl Display, f: &mut Formatter<'_>) -> fmt::Result { let mut iter = self.into_iter(); let Some(first) = iter.next() else { return Ok(()) }; first.fmt(f)?; for item in iter { - f.write_str(sep)?; + sep.fmt(f)?; item.fmt(f)?; } Ok(()) diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 493fdc6fb1b3..8c75f301841f 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -1264,6 +1264,7 @@ impl std::fmt::Write for WriteCounter { } // Implements Display by emitting the given number of spaces. +#[derive(Clone, Copy)] struct Indent(usize); impl Display for Indent { @@ -1275,6 +1276,37 @@ impl Display for Indent { } } +impl clean::Parameter { + fn print(&self, cx: &Context<'_>) -> impl fmt::Display { + fmt::from_fn(move |f| { + if let Some(self_ty) = self.to_receiver() { + match self_ty { + clean::SelfTy => f.write_str("self"), + clean::BorrowedRef { lifetime, mutability, type_: box clean::SelfTy } => { + f.write_str(if f.alternate() { "&" } else { "&" })?; + if let Some(lt) = lifetime { + write!(f, "{lt} ", lt = lt.print())?; + } + write!(f, "{mutability}self", mutability = mutability.print_with_space()) + } + _ => { + f.write_str("self: ")?; + self_ty.print(cx).fmt(f) + } + } + } else { + if self.is_const { + write!(f, "const ")?; + } + if let Some(name) = self.name { + write!(f, "{name}: ")?; + } + self.type_.print(cx).fmt(f) + } + }) + } +} + impl clean::FnDecl { pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| { @@ -1333,63 +1365,42 @@ impl clean::FnDecl { f: &mut fmt::Formatter<'_>, cx: &Context<'_>, ) -> fmt::Result { - let amp = if f.alternate() { "&" } else { "&" }; + f.write_char('(')?; - write!(f, "(")?; - if let Some(n) = line_wrapping_indent - && !self.inputs.is_empty() - { - write!(f, "\n{}", Indent(n + 4))?; - } + if !self.inputs.is_empty() { + let line_wrapping_indent = line_wrapping_indent.map(|n| Indent(n + 4)); - let last_input_index = self.inputs.len().checked_sub(1); - for (i, param) in self.inputs.iter().enumerate() { - if let Some(selfty) = param.to_receiver() { - match selfty { - clean::SelfTy => { - write!(f, "self")?; - } - clean::BorrowedRef { lifetime, mutability, type_: box clean::SelfTy } => { - write!(f, "{amp}")?; - if let Some(lt) = lifetime { - write!(f, "{lt} ", lt = lt.print())?; - } - write!(f, "{mutability}self", mutability = mutability.print_with_space())?; - } - _ => { - write!(f, "self: ")?; - selfty.print(cx).fmt(f)?; - } - } - } else { - if param.is_const { - write!(f, "const ")?; - } - if let Some(name) = param.name { - write!(f, "{name}: ")?; - } - param.type_.print(cx).fmt(f)?; + if let Some(indent) = line_wrapping_indent { + write!(f, "\n{indent}")?; } - match (line_wrapping_indent, last_input_index) { - (_, None) => (), - (None, Some(last_i)) if i != last_i => write!(f, ", ")?, - (None, Some(_)) => (), - (Some(n), Some(last_i)) if i != last_i => write!(f, ",\n{}", Indent(n + 4))?, - (Some(_), Some(_)) => writeln!(f, ",")?, + + let sep = fmt::from_fn(|f| { + if let Some(indent) = line_wrapping_indent { + write!(f, ",\n{indent}") + } else { + f.write_str(", ") + } + }); + + self.inputs.iter().map(|param| param.print(cx)).joined(sep, f)?; + + if line_wrapping_indent.is_some() { + writeln!(f, ",")? + } + + if self.c_variadic { + match line_wrapping_indent { + None => write!(f, ", ...")?, + Some(indent) => writeln!(f, "{indent}...")?, + }; } } - if self.c_variadic { - match line_wrapping_indent { - None => write!(f, ", ...")?, - Some(n) => writeln!(f, "{}...", Indent(n + 4))?, - }; + if let Some(n) = line_wrapping_indent { + write!(f, "{}", Indent(n))? } - match line_wrapping_indent { - None => write!(f, ")")?, - Some(n) => write!(f, "{})", Indent(n))?, - }; + f.write_char(')')?; self.print_output(cx).fmt(f) } From c1f782919bac1071a514eafb87e2463be25fc99e Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Wed, 17 Sep 2025 19:23:28 +0200 Subject: [PATCH 609/682] `match_as_ref`: do not lint if other arm is not `None => None` --- clippy_lints/src/manual_option_as_slice.rs | 6 ++--- clippy_utils/src/lib.rs | 16 +++++++----- tests/ui/match_as_ref.fixed | 30 ++++++++++++++++++++++ tests/ui/match_as_ref.rs | 30 ++++++++++++++++++++++ 4 files changed, 73 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/manual_option_as_slice.rs b/clippy_lints/src/manual_option_as_slice.rs index 922db174e3d4..b036e78cdedc 100644 --- a/clippy_lints/src/manual_option_as_slice.rs +++ b/clippy_lints/src/manual_option_as_slice.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::msrvs::Msrv; -use clippy_utils::{is_none_arm, msrvs, peel_hir_expr_refs, sym}; +use clippy_utils::{is_none_pattern, msrvs, peel_hir_expr_refs, sym}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Arm, Expr, ExprKind, LangItem, Pat, PatKind, QPath, is_range_literal}; @@ -60,8 +60,8 @@ impl LateLintPass<'_> for ManualOptionAsSlice { } match expr.kind { ExprKind::Match(scrutinee, [arm1, arm2], _) => { - if is_none_arm(cx, arm2) && check_arms(cx, arm2, arm1) - || is_none_arm(cx, arm1) && check_arms(cx, arm1, arm2) + if is_none_pattern(cx, arm2.pat) && check_arms(cx, arm2, arm1) + || is_none_pattern(cx, arm1.pat) && check_arms(cx, arm1, arm2) { check_as_ref(cx, scrutinee, span, self.msrv); } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 5223cd872a68..3e48397fbedb 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -329,13 +329,17 @@ pub fn is_wild(pat: &Pat<'_>) -> bool { matches!(pat.kind, PatKind::Wild) } -// Checks if arm has the form `None => None` -pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { - matches!( - arm.pat.kind, +/// Checks if the `pat` is `None`. +pub fn is_none_pattern(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { + matches!(pat.kind, PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. }) - if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone) - ) + if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionNone)) +} + +/// Checks if `arm` has the form `None => None`. +pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { + is_none_pattern(cx, arm.pat) + && matches!(peel_blocks(arm.body).kind, ExprKind::Path(qpath) if is_res_lang_ctor(cx, cx.qpath_res(&qpath, arm.body.hir_id), OptionNone)) } /// Checks if the given `QPath` belongs to a type alias. diff --git a/tests/ui/match_as_ref.fixed b/tests/ui/match_as_ref.fixed index 8c07076af4a4..a39f0c9299bd 100644 --- a/tests/ui/match_as_ref.fixed +++ b/tests/ui/match_as_ref.fixed @@ -41,3 +41,33 @@ fn main() { None => None, }; } + +mod issue15691 { + use std::ops::{Deref, DerefMut}; + + struct A(B); + struct B; + + impl Deref for A { + type Target = B; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl DerefMut for A { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + fn func() { + let mut a = Some(A(B)); + let mut b = Some(B); + // Do not lint, we don't have `None => None` + let _ = match b { + Some(ref mut x) => Some(x), + None => a.as_deref_mut(), + }; + } +} diff --git a/tests/ui/match_as_ref.rs b/tests/ui/match_as_ref.rs index 3a5b1227331e..049928167901 100644 --- a/tests/ui/match_as_ref.rs +++ b/tests/ui/match_as_ref.rs @@ -53,3 +53,33 @@ fn main() { None => None, }; } + +mod issue15691 { + use std::ops::{Deref, DerefMut}; + + struct A(B); + struct B; + + impl Deref for A { + type Target = B; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl DerefMut for A { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + fn func() { + let mut a = Some(A(B)); + let mut b = Some(B); + // Do not lint, we don't have `None => None` + let _ = match b { + Some(ref mut x) => Some(x), + None => a.as_deref_mut(), + }; + } +} From 9cdc09b604f2a3bdab334911bac99d23ed556c23 Mon Sep 17 00:00:00 2001 From: LorrensP-2158466 Date: Tue, 16 Sep 2025 09:48:08 +0200 Subject: [PATCH 610/682] readdir for freebsd --- .../src/shims/unix/freebsd/foreign_items.rs | 6 +- src/tools/miri/src/shims/unix/fs.rs | 60 ++++++++++++------- .../src/shims/unix/linux/foreign_items.rs | 2 +- .../src/shims/unix/solarish/foreign_items.rs | 2 +- src/tools/miri/tests/pass/shims/fs.rs | 3 +- 5 files changed, 46 insertions(+), 27 deletions(-) diff --git a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs index 9e247053fbcd..413df85ee3aa 100644 --- a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs @@ -159,7 +159,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let result = this.macos_fbsd_readdir_r(dirp, entry, result)?; this.write_scalar(result, dest)?; } - + "readdir" | "readdir@FBSD_1.0" => { + let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let result = this.readdir64("dirent", dirp)?; + this.write_scalar(result, dest)?; + } // Miscellaneous "__error" => { let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index f9bcacf64c41..22bec9bd8394 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -900,14 +900,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } - fn linux_solarish_readdir64( - &mut self, - dirent_type: &str, - dirp_op: &OpTy<'tcx>, - ) -> InterpResult<'tcx, Scalar> { + fn readdir64(&mut self, dirent_type: &str, dirp_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - if !matches!(&*this.tcx.sess.target.os, "linux" | "solaris" | "illumos") { + if !matches!(&*this.tcx.sess.target.os, "linux" | "solaris" | "illumos" | "freebsd") { panic!("`linux_solaris_readdir64` should not be called on {}", this.tcx.sess.target.os); } @@ -926,6 +922,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let entry = match open_dir.read_dir.next() { Some(Ok(dir_entry)) => { + // If the host is a Unix system, fill in the inode number with its real value. + // If not, use 0 as a fallback value. + #[cfg(unix)] + let ino = std::os::unix::fs::DirEntryExt::ino(&dir_entry); + #[cfg(not(unix))] + let ino = 0u64; + // Write the directory entry into a newly allocated buffer. // The name is written with write_bytes, while the rest of the // dirent64 (or dirent) struct is written using write_int_fields. @@ -947,6 +950,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // pub d_reclen: c_ushort, // pub d_name: [c_char; 3], // } + // + // On FreeBSD: + // pub struct dirent{ + // pub d_fileno: uint32_t, + // pub d_reclen: uint16_t, + // pub d_type: uint8_t, + // pub d_namlen: uint8_t, + // pub d_name: [c_char; 256] + // } let mut name = dir_entry.file_name(); // not a Path as there are no separators! name.push("\0"); // Add a NUL terminator @@ -965,31 +977,35 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { MiriMemoryKind::Runtime.into(), AllocInit::Uninit, )?; - let entry: Pointer = entry.into(); + let entry = this.ptr_to_mplace(entry.into(), dirent_layout); - // If the host is a Unix system, fill in the inode number with its real value. - // If not, use 0 as a fallback value. - #[cfg(unix)] - let ino = std::os::unix::fs::DirEntryExt::ino(&dir_entry); - #[cfg(not(unix))] - let ino = 0u64; - - let file_type = this.file_type_to_d_type(dir_entry.file_type())?; + // Write common fields + let ino_name = + if this.tcx.sess.target.os == "freebsd" { "d_fileno" } else { "d_ino" }; this.write_int_fields_named( - &[("d_ino", ino.into()), ("d_off", 0), ("d_reclen", size.into())], - &this.ptr_to_mplace(entry, dirent_layout), + &[(ino_name, ino.into()), ("d_reclen", size.into())], + &entry, )?; - if let Some(d_type) = this - .try_project_field_named(&this.ptr_to_mplace(entry, dirent_layout), "d_type")? - { + // Write "optional" fields. + if let Some(d_off) = this.try_project_field_named(&entry, "d_off")? { + this.write_null(&d_off)?; + } + + if let Some(d_namlen) = this.try_project_field_named(&entry, "d_namlen")? { + this.write_int(name_len.strict_sub(1), &d_namlen)?; + } + + let file_type = this.file_type_to_d_type(dir_entry.file_type())?; + if let Some(d_type) = this.try_project_field_named(&entry, "d_type")? { this.write_int(file_type, &d_type)?; } - let name_ptr = entry.wrapping_offset(Size::from_bytes(d_name_offset), this); + // The name is not a normal field, we already computed the offset above. + let name_ptr = entry.ptr().wrapping_offset(Size::from_bytes(d_name_offset), this); this.write_bytes_ptr(name_ptr, name_bytes.iter().copied())?; - Some(entry) + Some(entry.ptr()) } None => { // end of stream: return NULL diff --git a/src/tools/miri/src/shims/unix/linux/foreign_items.rs b/src/tools/miri/src/shims/unix/linux/foreign_items.rs index e7e0c3b6ecd9..79052698f4ba 100644 --- a/src/tools/miri/src/shims/unix/linux/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/linux/foreign_items.rs @@ -38,7 +38,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // File related shims "readdir64" => { let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; - let result = this.linux_solarish_readdir64("dirent64", dirp)?; + let result = this.readdir64("dirent64", dirp)?; this.write_scalar(result, dest)?; } "sync_file_range" => { diff --git a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs index d7033a65fe22..31269bf00c94 100644 --- a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs @@ -106,7 +106,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "readdir" => { let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; - let result = this.linux_solarish_readdir64("dirent", dirp)?; + let result = this.readdir64("dirent", dirp)?; this.write_scalar(result, dest)?; } diff --git a/src/tools/miri/tests/pass/shims/fs.rs b/src/tools/miri/tests/pass/shims/fs.rs index 0748007b3c05..022dcc5dcbaf 100644 --- a/src/tools/miri/tests/pass/shims/fs.rs +++ b/src/tools/miri/tests/pass/shims/fs.rs @@ -28,8 +28,7 @@ fn main() { test_from_raw_os_error(); test_file_clone(); // Windows file handling is very incomplete. - // FIXME: read_dir broken on FreeBSD (https://github.com/rust-lang/miri/issues/4587) - if cfg!(not(windows)) && !cfg!(target_os = "freebsd") { + if cfg!(not(windows)) { test_file_set_len(); test_file_sync(); test_rename(); From 80db273b6445cdbbac943c047ca4a10ee5c7ea45 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Wed, 17 Sep 2025 16:28:46 -0400 Subject: [PATCH 611/682] Remove jujutsu directory from search tools. --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 36a4cdc1c352..666c4ceac4db 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,6 @@ helper.txt # mdbook generated output /book/book + +# Remove jujutsu directory from search tools +.jj From 912785d966395d36cd2cebe5d0959316fdd28cef Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sat, 13 Sep 2025 18:07:35 +0000 Subject: [PATCH 612/682] Lint overlapping assignments in MIR. --- compiler/rustc_middle/src/mir/visit.rs | 17 +++++++ compiler/rustc_mir_transform/src/dest_prop.rs | 18 +------- compiler/rustc_mir_transform/src/lint.rs | 44 ++++++++++++++----- tests/ui/mir/lint/assignment-overlap.rs | 2 +- 4 files changed, 53 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 07a15b3cd18a..81df239dee42 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -1475,3 +1475,20 @@ impl PlaceContext { } } } + +/// Small utility to visit places and locals without manually implementing a full visitor. +pub struct VisitPlacesWith(pub F); + +impl<'tcx, F> Visitor<'tcx> for VisitPlacesWith +where + F: FnMut(Place<'tcx>, PlaceContext), +{ + fn visit_local(&mut self, local: Local, ctxt: PlaceContext, _: Location) { + (self.0)(local.into(), ctxt); + } + + fn visit_place(&mut self, place: &Place<'tcx>, ctxt: PlaceContext, location: Location) { + (self.0)(*place, ctxt); + self.visit_projection(place.as_ref(), ctxt, location); + } +} diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index abd1cd4e35ac..74c22ff10c19 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -141,7 +141,7 @@ use rustc_data_structures::union_find::UnionFind; use rustc_index::bit_set::DenseBitSet; use rustc_index::interval::SparseIntervalMatrix; use rustc_index::{IndexVec, newtype_index}; -use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; +use rustc_middle::mir::visit::{MutVisitor, PlaceContext, VisitPlacesWith, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; use rustc_mir_dataflow::impls::{DefUse, MaybeLiveLocals}; @@ -503,22 +503,6 @@ impl TwoStepIndex { } } -struct VisitPlacesWith(F); - -impl<'tcx, F> Visitor<'tcx> for VisitPlacesWith -where - F: FnMut(Place<'tcx>, PlaceContext), -{ - fn visit_local(&mut self, local: Local, ctxt: PlaceContext, _: Location) { - (self.0)(local.into(), ctxt); - } - - fn visit_place(&mut self, place: &Place<'tcx>, ctxt: PlaceContext, location: Location) { - (self.0)(*place, ctxt); - self.visit_projection(place.as_ref(), ctxt, location); - } -} - /// Add points depending on the result of the given dataflow analysis. fn save_as_intervals<'tcx>( elements: &DenseLocationMap, diff --git a/compiler/rustc_mir_transform/src/lint.rs b/compiler/rustc_mir_transform/src/lint.rs index f472c7cb493d..2ab49645dc44 100644 --- a/compiler/rustc_mir_transform/src/lint.rs +++ b/compiler/rustc_mir_transform/src/lint.rs @@ -6,7 +6,7 @@ use std::borrow::Cow; use rustc_data_structures::fx::FxHashSet; use rustc_index::bit_set::DenseBitSet; -use rustc_middle::mir::visit::{PlaceContext, Visitor}; +use rustc_middle::mir::visit::{PlaceContext, VisitPlacesWith, Visitor}; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; use rustc_mir_dataflow::impls::{MaybeStorageDead, MaybeStorageLive, always_storage_live_locals}; @@ -79,15 +79,39 @@ impl<'a, 'tcx> Visitor<'tcx> for Lint<'a, 'tcx> { fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { match &statement.kind { StatementKind::Assign(box (dest, rvalue)) => { - if let Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) = rvalue { - // The sides of an assignment must not alias. Currently this just checks whether - // the places are identical. - if dest == src { - self.fail( - location, - "encountered `Assign` statement with overlapping memory", - ); - } + let forbid_aliasing = match rvalue { + Rvalue::Use(..) + | Rvalue::CopyForDeref(..) + | Rvalue::Repeat(..) + | Rvalue::Aggregate(..) + | Rvalue::Cast(..) + | Rvalue::ShallowInitBox(..) + | Rvalue::WrapUnsafeBinder(..) => true, + Rvalue::ThreadLocalRef(..) + | Rvalue::NullaryOp(..) + | Rvalue::UnaryOp(..) + | Rvalue::BinaryOp(..) + | Rvalue::Ref(..) + | Rvalue::RawPtr(..) + | Rvalue::Discriminant(..) => false, + }; + // The sides of an assignment must not alias. + if forbid_aliasing { + VisitPlacesWith(|src: Place<'tcx>, _| { + if *dest == src + || (dest.local == src.local + && !dest.is_indirect() + && !src.is_indirect()) + { + self.fail( + location, + format!( + "encountered `{statement:?}` statement with overlapping memory" + ), + ); + } + }) + .visit_rvalue(rvalue, location); } } StatementKind::StorageLive(local) => { diff --git a/tests/ui/mir/lint/assignment-overlap.rs b/tests/ui/mir/lint/assignment-overlap.rs index bbc140904671..5d1213a77585 100644 --- a/tests/ui/mir/lint/assignment-overlap.rs +++ b/tests/ui/mir/lint/assignment-overlap.rs @@ -13,7 +13,7 @@ pub fn main() { let a: [u8; 1024]; { a = a; //~ ERROR broken MIR - //~^ ERROR encountered `Assign` statement with overlapping memory + //~^ ERROR encountered `_1 = copy _1` statement with overlapping memory Return() } } From 8d7ec96c00994e3e4da652201792ae2040c979c1 Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Fri, 12 Sep 2025 18:35:21 -0400 Subject: [PATCH 613/682] add `[const] PartialEq` bound to `PartialOrd` This change is included for discussion purposes. The PartialOrd bound on PartialEq is not strictly necessary. It is, rather, logical: anything which is orderable should by definition have equality. Is the same true for constness? Should every type which is const orderable also have const equality? --- library/core/src/cmp.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 95896ab14418..7f369d19c3d1 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -1351,7 +1351,9 @@ pub macro Ord($item:item) { #[rustc_diagnostic_item = "PartialOrd"] #[allow(multiple_supertrait_upcastable)] // FIXME(sized_hierarchy): remove this #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] -pub const trait PartialOrd: PartialEq + PointeeSized { +pub const trait PartialOrd: + [const] PartialEq + PointeeSized +{ /// This method returns an ordering between `self` and `other` values if one exists. /// /// # Examples From f90075862346c78b4c0c373e48fb8929168d277e Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 17 Sep 2025 14:47:21 -0700 Subject: [PATCH 614/682] std: Fix WASI implementation of `remove_dir_all` This commit is a change to the WASI-specific implementation of the `std::fs::remove_dir_all` function. Specifically it changes how directory entries are read of a directory-being-deleted to specifically buffer them all into a `Vec` before actually proceeding to delete anything. This is necessary to fix an interaction with how the WASIp1 `fd_readdir` API works to have everything work out in the face of mutations while reading a directory. The basic problem is that `fd_readdir`, the WASIp1 API for reading directories, is not a stateful read of a directory but instead a "seekable" read of a directory. Its `cookie` argument enables seeking anywhere within the directory at any time to read further entries. Native host implementations do not have this ability, however, which means that this seeking property must be achieved by re-reading the directory. The problem with this is that WASIp1 has under-specified semantics around what should happen if a directory is mutated between two calls to `fd_readdir`. In essence there's not really any possible implementation in hosts except to read the entire directory and support seeking through the already-read list. This implementation is not possible in the WASIp1-to-WASIp2 adapter that is primarily used to create components for the `wasm32-wasip2` target where it has constrained memory requirements and can't buffer up arbitrarily sized directories. The WASIp1 API definitions are effectively "dead" now at the standards level meaning that `fd_readdir` won't be changing nor will a replacement be coming. For the `wasm32-wasip2` target this will get fixed once filesystem APIs are updated to use WASIp2 directly instead of WASIp1, making this buffering unnecessary. In essence while this is a hack it's sort of the least invasive thing that works everywhere for now. I don't think this is viable to fix in hosts so guests compiled to wasm are going to have to work around it by not relying on any guarantees about what happens to a directory if it's mutated between reads. --- library/std/src/sys/fs/wasi.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/library/std/src/sys/fs/wasi.rs b/library/std/src/sys/fs/wasi.rs index b65d86de12a3..0b65b9cb389d 100644 --- a/library/std/src/sys/fs/wasi.rs +++ b/library/std/src/sys/fs/wasi.rs @@ -848,7 +848,14 @@ fn remove_dir_all_recursive(parent: &WasiFd, path: &Path) -> io::Result<()> { // Iterate over all the entries in this directory, and travel recursively if // necessary - for entry in ReadDir::new(fd, dummy_root) { + // + // Note that all directory entries for this directory are read first before + // any removal is done. This works around the fact that the WASIp1 API for + // reading directories is not well-designed for handling mutations between + // invocations of reading a directory. By reading all the entries at once + // this ensures that, at least without concurrent modifications, it should + // be possible to delete everything. + for entry in ReadDir::new(fd, dummy_root).collect::>() { let entry = entry?; let path = crate::str::from_utf8(&entry.name).map_err(|_| { io::const_error!(io::ErrorKind::Uncategorized, "invalid utf-8 file name found") From 9ecca51bbe151c4ce0e374c46d1a0bfd1ec63d49 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 16 Sep 2025 17:08:14 -0500 Subject: [PATCH 615/682] Remove ImplSubject --- compiler/rustc_infer/src/infer/at.rs | 19 +------------------ compiler/rustc_middle/src/hir/mod.rs | 9 +-------- compiler/rustc_middle/src/ty/mod.rs | 6 ------ compiler/rustc_middle/src/ty/relate.rs | 24 ------------------------ src/librustdoc/html/render/mod.rs | 10 +++++----- 5 files changed, 7 insertions(+), 61 deletions(-) diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index ad19cdef4e75..70e3d7dc9fef 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -28,7 +28,7 @@ use relate::lattice::{LatticeOp, LatticeOpKind}; use rustc_middle::bug; use rustc_middle::ty::relate::solver_relating::RelateExt as NextSolverRelate; -use rustc_middle::ty::{Const, ImplSubject, TypingMode}; +use rustc_middle::ty::{Const, TypingMode}; use super::*; use crate::infer::relate::type_relating::TypeRelating; @@ -304,23 +304,6 @@ impl<'a, 'tcx> At<'a, 'tcx> { } } -impl<'tcx> ToTrace<'tcx> for ImplSubject<'tcx> { - fn to_trace(cause: &ObligationCause<'tcx>, a: Self, b: Self) -> TypeTrace<'tcx> { - match (a, b) { - (ImplSubject::Trait(trait_ref_a), ImplSubject::Trait(trait_ref_b)) => { - ToTrace::to_trace(cause, trait_ref_a, trait_ref_b) - } - (ImplSubject::Inherent(ty_a), ImplSubject::Inherent(ty_b)) => { - ToTrace::to_trace(cause, ty_a, ty_b) - } - (ImplSubject::Trait(_), ImplSubject::Inherent(_)) - | (ImplSubject::Inherent(_), ImplSubject::Trait(_)) => { - bug!("can not trace TraitRef and Ty"); - } - } - } -} - impl<'tcx> ToTrace<'tcx> for Ty<'tcx> { fn to_trace(cause: &ObligationCause<'tcx>, a: Self, b: Self) -> TypeTrace<'tcx> { TypeTrace { diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index 67bc89692ff7..9e3162785f4b 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -18,7 +18,7 @@ use rustc_macros::{Decodable, Encodable, HashStable}; use rustc_span::{ErrorGuaranteed, ExpnId, Span}; use crate::query::Providers; -use crate::ty::{EarlyBinder, ImplSubject, TyCtxt}; +use crate::ty::TyCtxt; /// Gather the LocalDefId for each item-like within a module, including items contained within /// bodies. The Ids are in visitor order. This is used to partition a pass between modules. @@ -154,13 +154,6 @@ impl<'tcx> TyCtxt<'tcx> { LocalModDefId::new_unchecked(id) } - pub fn impl_subject(self, def_id: DefId) -> EarlyBinder<'tcx, ImplSubject<'tcx>> { - match self.impl_trait_ref(def_id) { - Some(t) => t.map_bound(ImplSubject::Trait), - None => self.type_of(def_id).map_bound(ImplSubject::Inherent), - } - } - /// Returns `true` if this is a foreign item (i.e., linked via `extern { ... }`). pub fn is_foreign_item(self, def_id: impl Into) -> bool { self.opt_parent(def_id.into()) diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index d4c001f625e1..0ffef393a33b 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -254,12 +254,6 @@ pub struct ImplTraitHeader<'tcx> { pub constness: hir::Constness, } -#[derive(Copy, Clone, PartialEq, Eq, Debug, TypeFoldable, TypeVisitable)] -pub enum ImplSubject<'tcx> { - Trait(TraitRef<'tcx>), - Inherent(Ty<'tcx>), -} - #[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable, Debug)] #[derive(TypeFoldable, TypeVisitable)] pub enum Asyncness { diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index dc1d60f3d43c..2f96970af478 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -7,30 +7,6 @@ use crate::ty::{self as ty, Ty, TyCtxt}; pub type RelateResult<'tcx, T> = rustc_type_ir::relate::RelateResult, T>; -impl<'tcx> Relate> for ty::ImplSubject<'tcx> { - #[inline] - fn relate>>( - relation: &mut R, - a: ty::ImplSubject<'tcx>, - b: ty::ImplSubject<'tcx>, - ) -> RelateResult<'tcx, ty::ImplSubject<'tcx>> { - match (a, b) { - (ty::ImplSubject::Trait(trait_ref_a), ty::ImplSubject::Trait(trait_ref_b)) => { - let trait_ref = ty::TraitRef::relate(relation, trait_ref_a, trait_ref_b)?; - Ok(ty::ImplSubject::Trait(trait_ref)) - } - (ty::ImplSubject::Inherent(ty_a), ty::ImplSubject::Inherent(ty_b)) => { - let ty = Ty::relate(relation, ty_a, ty_b)?; - Ok(ty::ImplSubject::Inherent(ty)) - } - (ty::ImplSubject::Trait(_), ty::ImplSubject::Inherent(_)) - | (ty::ImplSubject::Inherent(_), ty::ImplSubject::Trait(_)) => { - bug!("can not relate TraitRef and Ty"); - } - } - } -} - impl<'tcx> Relate> for Ty<'tcx> { #[inline] fn relate>>( diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index b4ef47d1e269..6d684449b6d2 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -2454,11 +2454,11 @@ fn get_id_for_impl(tcx: TyCtxt<'_>, impl_id: ItemId) -> String { (ty, Some(ty::TraitRef::new(tcx, trait_, [ty]))) } ItemId::Blanket { impl_id, .. } | ItemId::DefId(impl_id) => { - match tcx.impl_subject(impl_id).skip_binder() { - ty::ImplSubject::Trait(trait_ref) => { - (trait_ref.args[0].expect_ty(), Some(trait_ref)) - } - ty::ImplSubject::Inherent(ty) => (ty, None), + if let Some(trait_ref) = tcx.impl_trait_ref(impl_id) { + let trait_ref = trait_ref.skip_binder(); + (trait_ref.self_ty(), Some(trait_ref)) + } else { + (tcx.type_of(impl_id).skip_binder(), None) } } }; From 15ce2093d53e3274e79d55858baa82ccc9c09b02 Mon Sep 17 00:00:00 2001 From: Daniel Gulotta Date: Wed, 17 Sep 2025 20:39:20 -0700 Subject: [PATCH 616/682] test cases for option_if_let_else --- tests/ui/option_if_let_else.fixed | 10 ++++++ tests/ui/option_if_let_else.rs | 16 ++++++++++ tests/ui/option_if_let_else.stderr | 50 ++++++++++++++++++++---------- 3 files changed, 60 insertions(+), 16 deletions(-) diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index 0f86de5646cd..6ce067f5c246 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -125,6 +125,16 @@ fn complex_subpat() -> DummyEnum { DummyEnum::Two } +// #10335 +pub fn test_result_err_ignored_1(r: Result<&[u8], &[u8]>) -> Vec { + r.map_or_else(|_| Vec::new(), |s| s.to_owned()) +} + +// #10335 +pub fn test_result_err_ignored_2(r: Result<&[u8], &[u8]>) -> Vec { + r.map_or_else(|_| Vec::new(), |s| s.to_owned()) +} + fn main() { let optional = Some(5); let _ = optional.map_or(5, |x| x + 2); diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index 7aabd778f87e..096d3aabf28d 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -152,6 +152,22 @@ fn complex_subpat() -> DummyEnum { DummyEnum::Two } +// #10335 +pub fn test_result_err_ignored_1(r: Result<&[u8], &[u8]>) -> Vec { + match r { + //~^ option_if_let_else + Ok(s) => s.to_owned(), + Err(_) => Vec::new(), + } +} + +// #10335 +pub fn test_result_err_ignored_2(r: Result<&[u8], &[u8]>) -> Vec { + if let Ok(s) = r { s.to_owned() } + //~^ option_if_let_else + else { Vec::new() } +} + fn main() { let optional = Some(5); let _ = if let Some(x) = optional { x + 2 } else { 5 }; diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr index 2e2fe6f20492..21a80ae038d8 100644 --- a/tests/ui/option_if_let_else.stderr +++ b/tests/ui/option_if_let_else.stderr @@ -188,14 +188,32 @@ LL + true LL + }) | +error: use Option::map_or_else instead of an if let/else + --> tests/ui/option_if_let_else.rs:157:5 + | +LL | / match r { +LL | | +LL | | Ok(s) => s.to_owned(), +LL | | Err(_) => Vec::new(), +LL | | } + | |_____^ help: try: `r.map_or_else(|_| Vec::new(), |s| s.to_owned())` + +error: use Option::map_or_else instead of an if let/else + --> tests/ui/option_if_let_else.rs:166:5 + | +LL | / if let Ok(s) = r { s.to_owned() } +LL | | +LL | | else { Vec::new() } + | |_______________________^ help: try: `r.map_or_else(|_| Vec::new(), |s| s.to_owned())` + error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:157:13 + --> tests/ui/option_if_let_else.rs:173:13 | LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:168:13 + --> tests/ui/option_if_let_else.rs:184:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ @@ -217,13 +235,13 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:197:13 + --> tests/ui/option_if_let_else.rs:213:13 | LL | let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or(s.len(), |x| s.len() + x)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:202:13 + --> tests/ui/option_if_let_else.rs:218:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ @@ -245,7 +263,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:242:13 + --> tests/ui/option_if_let_else.rs:258:13 | LL | let _ = match s { | _____________^ @@ -256,7 +274,7 @@ LL | | }; | |_____^ help: try: `s.map_or(1, |string| string.len())` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:247:13 + --> tests/ui/option_if_let_else.rs:263:13 | LL | let _ = match Some(10) { | _____________^ @@ -267,7 +285,7 @@ LL | | }; | |_____^ help: try: `Some(10).map_or(5, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:254:13 + --> tests/ui/option_if_let_else.rs:270:13 | LL | let _ = match res { | _____________^ @@ -278,7 +296,7 @@ LL | | }; | |_____^ help: try: `res.map_or(1, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:259:13 + --> tests/ui/option_if_let_else.rs:275:13 | LL | let _ = match res { | _____________^ @@ -289,13 +307,13 @@ LL | | }; | |_____^ help: try: `res.map_or(1, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:264:13 + --> tests/ui/option_if_let_else.rs:280:13 | LL | let _ = if let Ok(a) = res { a + 1 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `res.map_or(5, |a| a + 1)` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:282:17 + --> tests/ui/option_if_let_else.rs:298:17 | LL | let _ = match initial { | _________________^ @@ -306,7 +324,7 @@ LL | | }; | |_________^ help: try: `initial.as_ref().map_or(42, |value| do_something(value))` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:290:17 + --> tests/ui/option_if_let_else.rs:306:17 | LL | let _ = match initial { | _________________^ @@ -317,7 +335,7 @@ LL | | }; | |_________^ help: try: `initial.as_mut().map_or(42, |value| do_something2(value))` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:314:24 + --> tests/ui/option_if_let_else.rs:330:24 | LL | let mut _hashmap = if let Some(hm) = &opt { | ________________________^ @@ -329,19 +347,19 @@ LL | | }; | |_____^ help: try: `opt.as_ref().map_or_else(HashMap::new, |hm| hm.clone())` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:321:19 + --> tests/ui/option_if_let_else.rs:337:19 | LL | let mut _hm = if let Some(hm) = &opt { hm.clone() } else { new_map!() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.as_ref().map_or_else(|| new_map!(), |hm| hm.clone())` error: use Option::map_or instead of an if let/else - --> tests/ui/option_if_let_else.rs:372:22 + --> tests/ui/option_if_let_else.rs:388:22 | LL | let _ = unsafe { if let Some(o) = *opt_raw_ptr { o } else { 1 } }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(*opt_raw_ptr).map_or(1, |o| o)` error: use Option::map_or_else instead of an if let/else - --> tests/ui/option_if_let_else.rs:378:13 + --> tests/ui/option_if_let_else.rs:394:13 | LL | let _ = match res { | _____________^ @@ -351,5 +369,5 @@ LL | | Err(_) => String::new(), LL | | }; | |_____^ help: try: `res.map_or_else(|_| String::new(), |s| s.clone())` -error: aborting due to 27 previous errors +error: aborting due to 29 previous errors From 8b0a25486040b91ede0120ff5a797517e0973895 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 17 Sep 2025 13:46:46 +1000 Subject: [PATCH 617/682] Move target machine command-line quoting from C++ to Rust --- .../src/back/command_line_args.rs | 37 +++++++++++++++++++ .../src/back/command_line_args/tests.rs | 25 +++++++++++++ compiler/rustc_codegen_llvm/src/back/mod.rs | 1 + .../src/back/owned_target_machine.rs | 14 +++---- compiler/rustc_codegen_llvm/src/back/write.rs | 30 ++++++--------- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 6 ++- .../rustc_llvm/llvm-wrapper/PassWrapper.cpp | 28 ++++---------- 7 files changed, 92 insertions(+), 49 deletions(-) create mode 100644 compiler/rustc_codegen_llvm/src/back/command_line_args.rs create mode 100644 compiler/rustc_codegen_llvm/src/back/command_line_args/tests.rs diff --git a/compiler/rustc_codegen_llvm/src/back/command_line_args.rs b/compiler/rustc_codegen_llvm/src/back/command_line_args.rs new file mode 100644 index 000000000000..b14713969b34 --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/back/command_line_args.rs @@ -0,0 +1,37 @@ +#[cfg(test)] +mod tests; + +/// Joins command-line arguments into a single space-separated string, quoting +/// and escaping individual arguments as necessary. +/// +/// The result is intended to be informational, for embedding in debug metadata, +/// and might not be properly quoted/escaped for actual command-line use. +pub(crate) fn quote_command_line_args(args: &[String]) -> String { + // Start with a decent-sized buffer, since rustc invocations tend to be long. + let mut buf = String::with_capacity(128); + + for arg in args { + if !buf.is_empty() { + buf.push(' '); + } + + print_arg_quoted(&mut buf, arg); + } + + buf +} + +/// Equivalent to LLVM's `sys::printArg` with quoting always enabled +/// (see llvm/lib/Support/Program.cpp). +fn print_arg_quoted(buf: &mut String, arg: &str) { + buf.reserve(arg.len() + 2); + + buf.push('"'); + for ch in arg.chars() { + if matches!(ch, '"' | '\\' | '$') { + buf.push('\\'); + } + buf.push(ch); + } + buf.push('"'); +} diff --git a/compiler/rustc_codegen_llvm/src/back/command_line_args/tests.rs b/compiler/rustc_codegen_llvm/src/back/command_line_args/tests.rs new file mode 100644 index 000000000000..69641fed3bc9 --- /dev/null +++ b/compiler/rustc_codegen_llvm/src/back/command_line_args/tests.rs @@ -0,0 +1,25 @@ +#[test] +fn quote_command_line_args() { + use super::quote_command_line_args; + + struct Case<'a> { + args: &'a [&'a str], + expected: &'a str, + } + + let cases = &[ + Case { args: &[], expected: "" }, + Case { args: &["--hello", "world"], expected: r#""--hello" "world""# }, + Case { args: &["--hello world"], expected: r#""--hello world""# }, + Case { + args: &["plain", "$dollar", "spa ce", r"back\slash", r#""quote""#, "plain"], + expected: r#""plain" "\$dollar" "spa ce" "back\\slash" "\"quote\"" "plain""#, + }, + ]; + + for &Case { args, expected } in cases { + let args = args.iter().copied().map(str::to_owned).collect::>(); + let actual = quote_command_line_args(&args); + assert_eq!(actual, expected, "args {args:?}"); + } +} diff --git a/compiler/rustc_codegen_llvm/src/back/mod.rs b/compiler/rustc_codegen_llvm/src/back/mod.rs index 6cb89f80ab89..fe3883e8c73e 100644 --- a/compiler/rustc_codegen_llvm/src/back/mod.rs +++ b/compiler/rustc_codegen_llvm/src/back/mod.rs @@ -1,4 +1,5 @@ pub(crate) mod archive; +mod command_line_args; pub(crate) mod lto; pub(crate) mod owned_target_machine; mod profiling; diff --git a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs index 6d8178320feb..d5228f0e0dee 100644 --- a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs +++ b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs @@ -1,4 +1,3 @@ -use std::assert_matches::assert_matches; use std::ffi::CStr; use std::marker::PhantomData; use std::ptr::NonNull; @@ -39,13 +38,10 @@ impl OwnedTargetMachine { output_obj_file: &CStr, debug_info_compression: &CStr, use_emulated_tls: bool, - args_cstr_buff: &[u8], + argv0: &str, + command_line_args: &str, use_wasm_eh: bool, ) -> Result> { - // The argument list is passed as the concatenation of one or more C strings. - // This implies that there must be a last byte, and it must be 0. - assert_matches!(args_cstr_buff, [.., b'\0'], "the last byte must be a NUL terminator"); - // SAFETY: llvm::LLVMRustCreateTargetMachine copies pointed to data let tm_ptr = unsafe { llvm::LLVMRustCreateTargetMachine( @@ -70,8 +66,10 @@ impl OwnedTargetMachine { output_obj_file.as_ptr(), debug_info_compression.as_ptr(), use_emulated_tls, - args_cstr_buff.as_ptr(), - args_cstr_buff.len(), + argv0.as_ptr(), + argv0.len(), + command_line_args.as_ptr(), + command_line_args.len(), use_wasm_eh, ) }; diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index bda81fbd19e8..c4881f0aafc8 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -31,6 +31,7 @@ use rustc_span::{BytePos, InnerSpan, Pos, SpanData, SyntaxContext, sym}; use rustc_target::spec::{CodeModel, FloatAbi, RelocModel, SanitizerSet, SplitDebuginfo, TlsModel}; use tracing::{debug, trace}; +use crate::back::command_line_args::quote_command_line_args; use crate::back::lto::ThinBuffer; use crate::back::owned_target_machine::OwnedTargetMachine; use crate::back::profiling::{ @@ -249,23 +250,15 @@ pub(crate) fn target_machine_factory( let use_emulated_tls = matches!(sess.tls_model(), TlsModel::Emulated); - // copy the exe path, followed by path all into one buffer - // null terminating them so we can use them as null terminated strings - let args_cstr_buff = { - let mut args_cstr_buff: Vec = Vec::new(); - let exe_path = std::env::current_exe().unwrap_or_default(); - let exe_path_str = exe_path.into_os_string().into_string().unwrap_or_default(); - - args_cstr_buff.extend_from_slice(exe_path_str.as_bytes()); - args_cstr_buff.push(0); - - for arg in sess.expanded_args.iter() { - args_cstr_buff.extend_from_slice(arg.as_bytes()); - args_cstr_buff.push(0); - } - - args_cstr_buff - }; + // Command-line information to be included in the target machine. + // This seems to only be used for embedding in PDB debuginfo files. + // FIXME(Zalathar): Maybe skip this for non-PDB targets? + let argv0 = std::env::current_exe() + .unwrap_or_default() + .into_os_string() + .into_string() + .unwrap_or_default(); + let command_line_args = quote_command_line_args(&sess.expanded_args); let debuginfo_compression = sess.opts.debuginfo_compression.to_string(); match sess.opts.debuginfo_compression { @@ -323,7 +316,8 @@ pub(crate) fn target_machine_factory( &output_obj_file, &debuginfo_compression, use_emulated_tls, - &args_cstr_buff, + &argv0, + &command_line_args, use_wasm_eh, ) }) diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 71d8b7d25fe7..c1b5649a5829 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2488,8 +2488,10 @@ unsafe extern "C" { OutputObjFile: *const c_char, DebugInfoCompression: *const c_char, UseEmulatedTls: bool, - ArgsCstrBuff: *const c_uchar, // See "PTR_LEN_STR". - ArgsCstrBuffLen: usize, + Argv0: *const c_uchar, // See "PTR_LEN_STR". + Argv0Len: size_t, + CommandLineArgs: *const c_uchar, // See "PTR_LEN_STR". + CommandLineArgsLen: size_t, UseWasmEH: bool, ) -> *mut TargetMachine; diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index ab5d5c03e817..7518b40799bc 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -271,8 +271,9 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( bool TrapUnreachable, bool Singlethread, bool VerboseAsm, bool EmitStackSizeSection, bool RelaxELFRelocations, bool UseInitArray, const char *SplitDwarfFile, const char *OutputObjFile, - const char *DebugInfoCompression, bool UseEmulatedTls, - const char *ArgsCstrBuff, size_t ArgsCstrBuffLen, bool UseWasmEH) { + const char *DebugInfoCompression, bool UseEmulatedTls, const char *Argv0, + size_t Argv0Len, const char *CommandLineArgs, size_t CommandLineArgsLen, + bool UseWasmEH) { auto OptLevel = fromRust(RustOptLevel); auto RM = fromRust(RustReloc); @@ -343,25 +344,10 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( Options.EmitStackSizeSection = EmitStackSizeSection; - if (ArgsCstrBuff != nullptr) { - size_t buffer_offset = 0; - assert(ArgsCstrBuff[ArgsCstrBuffLen - 1] == '\0'); - auto Arg0 = std::string(ArgsCstrBuff); - buffer_offset = Arg0.size() + 1; - - std::string CommandlineArgs; - raw_string_ostream OS(CommandlineArgs); - ListSeparator LS(" "); - for (StringRef Arg : split(StringRef(ArgsCstrBuff + buffer_offset, - ArgsCstrBuffLen - buffer_offset), - '\0')) { - OS << LS; - sys::printArg(OS, Arg, /*Quote=*/true); - } - OS.flush(); - Options.MCOptions.Argv0 = Arg0; - Options.MCOptions.CommandlineArgs = CommandlineArgs; - } + if (Argv0 != nullptr) + Options.MCOptions.Argv0 = {Argv0, Argv0Len}; + if (CommandLineArgs != nullptr) + Options.MCOptions.CommandlineArgs = {CommandLineArgs, CommandLineArgsLen}; #if LLVM_VERSION_GE(21, 0) TargetMachine *TM = TheTarget->createTargetMachine(Trip, CPU, Feature, From 6e74905be29b861bcfd2780a8c81495deffec6c3 Mon Sep 17 00:00:00 2001 From: Haidong Zhang Date: Wed, 3 Sep 2025 17:40:45 +0800 Subject: [PATCH 618/682] Set lto="fat" automatically when compiling with RUSTFLAGS="-Zautodiff=Enable". --- compiler/rustc_codegen_ssa/messages.ftl | 2 -- compiler/rustc_codegen_ssa/src/errors.rs | 4 ---- compiler/rustc_session/src/config.rs | 5 +++++ compiler/rustc_session/src/session.rs | 7 +++++++ 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 1dd65d38a2be..91c3806df4c3 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -8,8 +8,6 @@ codegen_ssa_aix_strip_not_used = using host's `strip` binary to cross-compile to codegen_ssa_archive_build_failure = failed to build archive at `{$path}`: {$error} -codegen_ssa_autodiff_without_lto = using the autodiff feature requires using fat-lto - codegen_ssa_bare_instruction_set = `#[instruction_set]` requires an argument codegen_ssa_binary_output_to_tty = option `-o` or `--emit` is used to write binary output type `{$shorthand}` to stdout, but stdout is a tty diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index fb5a82051405..d5c30c5c7a6b 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -37,10 +37,6 @@ pub(crate) struct CguNotRecorded<'a> { pub cgu_name: &'a str, } -#[derive(Diagnostic)] -#[diag(codegen_ssa_autodiff_without_lto)] -pub struct AutodiffWithoutLto; - #[derive(Diagnostic)] #[diag(codegen_ssa_unknown_reuse_kind)] pub(crate) struct UnknownReuseKind { diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 297df7c2c976..795cb2b2cfeb 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1509,6 +1509,11 @@ impl Options { pub fn get_symbol_mangling_version(&self) -> SymbolManglingVersion { self.cg.symbol_mangling_version.unwrap_or(SymbolManglingVersion::Legacy) } + + #[inline] + pub fn autodiff_enabled(&self) -> bool { + self.unstable_opts.autodiff.contains(&AutoDiff::Enable) + } } impl UnstableOptions { diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 3525c7c1d1a9..d0dd2cdac0c4 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -600,6 +600,13 @@ impl Session { /// Calculates the flavor of LTO to use for this compilation. pub fn lto(&self) -> config::Lto { + // Autodiff currently requires fat-lto to have access to the llvm-ir of all (indirectly) used functions and types. + // fat-lto is the easiest solution to this requirement, but quite expensive. + // FIXME(autodiff): Make autodiff also work with embed-bc instead of fat-lto. + if self.opts.autodiff_enabled() { + return config::Lto::Fat; + } + // If our target has codegen requirements ignore the command line if self.target.requires_lto { return config::Lto::Fat; From 2c1f1f0e9bb706d9adbb71e0bc33923c4478eb85 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Mon, 8 Sep 2025 10:17:14 +0200 Subject: [PATCH 619/682] Add GenMC estimation mode. Improve error handling and output printing. --- src/tools/miri/doc/genmc.md | 3 + .../genmc-sys/cpp/include/MiriInterface.hpp | 8 +- .../cpp/src/MiriInterface/Exploration.cpp | 14 ++ .../genmc-sys/cpp/src/MiriInterface/Setup.cpp | 21 ++- src/tools/miri/genmc-sys/src/lib.rs | 55 +++++- src/tools/miri/src/bin/miri.rs | 9 +- .../miri/src/concurrency/genmc/config.rs | 17 ++ src/tools/miri/src/concurrency/genmc/dummy.rs | 12 -- src/tools/miri/src/concurrency/genmc/mod.rs | 61 +++++-- src/tools/miri/src/concurrency/genmc/run.rs | 166 +++++++++++++----- .../genmc/fail/data_race/mpu2_rels_rlx.stderr | 2 + .../data_race/weak_orderings.rel_rlx.stderr | 2 + .../data_race/weak_orderings.rlx_acq.stderr | 2 + .../data_race/weak_orderings.rlx_rlx.stderr | 2 + .../tests/genmc/fail/loom/buggy_inc.stderr | 2 + .../fail/loom/store_buffering.genmc.stderr | 2 + .../fail/simple/2w2w_weak.relaxed4.stderr | 2 + .../fail/simple/2w2w_weak.release4.stderr | 2 + .../fail/simple/2w2w_weak.sc3_rel1.stderr | 2 + .../cas_failure_ord_racy_key_init.stderr | 4 +- .../genmc/pass/atomics/cas_simple.stderr | 4 +- .../tests/genmc/pass/atomics/rmw_ops.stderr | 4 +- .../miri/tests/genmc/pass/litmus/2cowr.stderr | 4 +- .../genmc/pass/litmus/2w2w_2sc_scf.stderr | 4 +- .../pass/litmus/2w2w_3sc_1rel.release1.stderr | 4 +- .../pass/litmus/2w2w_3sc_1rel.release2.stderr | 4 +- .../genmc/pass/litmus/2w2w_4rel.sc.stderr | 4 +- .../genmc/pass/litmus/2w2w_4rel.weak.stderr | 4 +- .../tests/genmc/pass/litmus/2w2w_4sc.stderr | 4 +- .../genmc/pass/litmus/IRIW-acq-sc.stderr | 4 +- .../tests/genmc/pass/litmus/IRIWish.stderr | 4 +- .../miri/tests/genmc/pass/litmus/LB.stderr | 4 +- .../tests/genmc/pass/litmus/LB_incMPs.stderr | 4 +- .../miri/tests/genmc/pass/litmus/MP.stderr | 4 +- .../genmc/pass/litmus/MPU2_rels_acqf.stderr | 4 +- .../genmc/pass/litmus/MPU_rels_acq.stderr | 4 +- .../tests/genmc/pass/litmus/MP_incMPs.stderr | 4 +- .../genmc/pass/litmus/MP_rels_acqf.stderr | 4 +- .../miri/tests/genmc/pass/litmus/SB.stderr | 4 +- .../tests/genmc/pass/litmus/SB_2sc_scf.stderr | 4 +- .../tests/genmc/pass/litmus/Z6_U.sc.stderr | 4 +- .../tests/genmc/pass/litmus/Z6_U.weak.stderr | 4 +- .../tests/genmc/pass/litmus/Z6_acq.stderr | 4 +- .../tests/genmc/pass/litmus/atomicpo.stderr | 4 +- .../tests/genmc/pass/litmus/casdep.stderr | 4 +- .../miri/tests/genmc/pass/litmus/ccr.stderr | 4 +- .../miri/tests/genmc/pass/litmus/cii.stderr | 4 +- .../miri/tests/genmc/pass/litmus/corr.stderr | 4 +- .../miri/tests/genmc/pass/litmus/corr0.stderr | 4 +- .../miri/tests/genmc/pass/litmus/corr1.stderr | 4 +- .../miri/tests/genmc/pass/litmus/corr2.stderr | 4 +- .../miri/tests/genmc/pass/litmus/corw.stderr | 4 +- .../miri/tests/genmc/pass/litmus/cowr.stderr | 4 +- .../genmc/pass/litmus/cumul-release.stderr | 4 +- .../tests/genmc/pass/litmus/default.stderr | 4 +- .../genmc/pass/litmus/detour.join.stderr | 4 +- .../genmc/pass/litmus/detour.no_join.stderr | 4 +- .../genmc/pass/litmus/fr_w_w_w_reads.stderr | 4 +- .../miri/tests/genmc/pass/litmus/inc2w.stderr | 4 +- .../genmc/pass/litmus/inc_inc_RR_W_RR.stderr | 4 +- .../miri/tests/genmc/pass/litmus/riwi.stderr | 4 +- .../tests/genmc/pass/litmus/viktor-relseq.rs | 4 +- .../genmc/pass/litmus/viktor-relseq.stderr | 6 +- src/tools/miri/tests/ui.rs | 2 + 64 files changed, 390 insertions(+), 174 deletions(-) diff --git a/src/tools/miri/doc/genmc.md b/src/tools/miri/doc/genmc.md index 8af697be34a6..44e11dcbec44 100644 --- a/src/tools/miri/doc/genmc.md +++ b/src/tools/miri/doc/genmc.md @@ -24,6 +24,8 @@ Note that `cargo miri test` in GenMC mode is currently not supported. ### Supported Parameters - `-Zmiri-genmc`: Enable GenMC mode (not required if any other GenMC options are used). +- `-Zmiri-genmc-estimate`: This enables estimation of the concurrent execution space and verification time, before running the full verification. This should help users detect when their program is too complex to fully verify in a reasonable time. This will explore enough executions to make a good estimation, but at least 10 and at most `estimation-max` executions. +- `-Zmiri-genmc-estimation-max={MAX_ITERATIONS}`: Set the maximum number of executions that will be explored during estimation (default: 1000). - `-Zmiri-genmc-print-exec-graphs={none,explored,blocked,all}`: Make GenMC print the execution graph of the program after every explored, every blocked, or after every execution (default: None). - `-Zmiri-genmc-print-exec-graphs`: Shorthand for suffix `=explored`. - `-Zmiri-genmc-print-genmc-output`: Print the output that GenMC provides. NOTE: this output is quite verbose and the events in the printed execution graph are hard to map back to the Rust code location they originate from. @@ -36,6 +38,7 @@ Note that `cargo miri test` in GenMC mode is currently not supported. - `debug1`: Print revisits considered by GenMC. - `debug2`: Print the execution graph after every memory access. - `debug3`: Print reads-from values considered by GenMC. +- `-Zmiri-genmc-verbose`: Show more information, such as estimated number of executions, and time taken for verification. #### Regular Miri parameters useful for GenMC mode diff --git a/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp index 662eb0e173ca..444c9375319a 100644 --- a/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp +++ b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp @@ -31,6 +31,7 @@ enum class LogLevel : std::uint8_t; struct GenmcScalar; struct SchedulingResult; +struct EstimationResult; struct LoadResult; struct StoreResult; struct ReadModifyWriteResult; @@ -66,7 +67,7 @@ struct MiriGenmcShim : private GenMCDriver { /// `logLevel`, causing a data race. The safest way to use these functions is to call /// `set_log_level_raw` once, and only then start creating handles. There should not be any /// other (safe) way to create a `MiriGenmcShim`. - /* unsafe */ static auto create_handle(const GenmcParams& params) + /* unsafe */ static auto create_handle(const GenmcParams& params, bool estimation_mode) -> std::unique_ptr; virtual ~MiriGenmcShim() {} @@ -183,6 +184,11 @@ struct MiriGenmcShim : private GenMCDriver { return nullptr; } + /**** Printing and estimation mode functionality. ****/ + + /// Get the results of a run in estimation mode. + auto get_estimation_results() const -> EstimationResult; + private: /** Increment the event index in the given thread by 1 and return the new event. */ [[nodiscard]] inline auto inc_pos(ThreadId tid) -> Event { diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Exploration.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Exploration.cpp index c51b59e86517..5e7188f17e0d 100644 --- a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Exploration.cpp +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Exploration.cpp @@ -10,7 +10,9 @@ #include "Support/Verbosity.hpp" // C++ headers: +#include #include +#include auto MiriGenmcShim::schedule_next( const int curr_thread_id, @@ -40,3 +42,15 @@ auto MiriGenmcShim::handle_execution_end() -> std::unique_ptr { GenMCDriver::handleExecutionEnd(); return {}; } + +/**** Estimation mode result ****/ + +auto MiriGenmcShim::get_estimation_results() const -> EstimationResult { + const auto& res = getResult(); + return EstimationResult { + .mean = static_cast(res.estimationMean), + .sd = static_cast(std::sqrt(res.estimationVariance)), + .explored_execs = static_cast(res.explored), + .blocked_execs = static_cast(res.exploredBlocked), + }; +} diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp index a17a83aa06e1..af13f0d07746 100644 --- a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp @@ -58,7 +58,7 @@ static auto to_genmc_verbosity_level(const LogLevel log_level) -> VerbosityLevel logLevel = to_genmc_verbosity_level(log_level); } -/* unsafe */ auto MiriGenmcShim::create_handle(const GenmcParams& params) +/* unsafe */ auto MiriGenmcShim::create_handle(const GenmcParams& params, bool estimation_mode) -> std::unique_ptr { auto conf = std::make_shared(); @@ -82,7 +82,8 @@ static auto to_genmc_verbosity_level(const LogLevel log_level) -> VerbosityLevel // Miri. conf->warnOnGraphSize = 1024 * 1024; - // We only support the `RC11` memory model for Rust, and `SC` when weak memory emulation is disabled. + // We only support the `RC11` memory model for Rust, and `SC` when weak memory emulation is + // disabled. conf->model = params.disable_weak_memory_emulation ? ModelType::SC : ModelType::RC11; // This prints the seed that GenMC picks for randomized scheduling during estimation mode. @@ -119,12 +120,20 @@ static auto to_genmc_verbosity_level(const LogLevel log_level) -> VerbosityLevel ); conf->symmetryReduction = params.do_symmetry_reduction; - // FIXME(genmc): expose this setting to Miri (useful for testing Miri-GenMC). - conf->schedulePolicy = SchedulePolicy::WF; + // Set the scheduling policy. GenMC uses `WFR` for estimation mode. + // For normal verification, `WF` has the best performance and is the GenMC default. + // Other scheduling policies are used by GenMC for testing and for modes currently + // unsupported with Miri such as bounding, which uses LTR. + conf->schedulePolicy = estimation_mode ? SchedulePolicy::WFR : SchedulePolicy::WF; + // Set the min and max number of executions tested in estimation mode. + conf->estimationMin = 10; // default taken from GenMC + conf->estimationMax = params.estimation_max; + // Deviation threshold % under which estimation is deemed good enough. + conf->sdThreshold = 10; // default taken from GenMC // Set the mode used for this driver, either estimation or verification. - // FIXME(genmc): implement estimation mode. - const auto mode = GenMCDriver::Mode(GenMCDriver::VerificationMode {}); + const auto mode = estimation_mode ? GenMCDriver::Mode(GenMCDriver::EstimationMode {}) + : GenMCDriver::Mode(GenMCDriver::VerificationMode {}); // Running Miri-GenMC without race detection is not supported. // Disabling this option also changes the behavior of the replay scheduler to only schedule diff --git a/src/tools/miri/genmc-sys/src/lib.rs b/src/tools/miri/genmc-sys/src/lib.rs index 31bc2606adc0..733b3d780b18 100644 --- a/src/tools/miri/genmc-sys/src/lib.rs +++ b/src/tools/miri/genmc-sys/src/lib.rs @@ -27,6 +27,7 @@ static GENMC_LOG_LEVEL: OnceLock = OnceLock::new(); pub fn create_genmc_driver_handle( params: &GenmcParams, genmc_log_level: LogLevel, + do_estimation: bool, ) -> UniquePtr { // SAFETY: Only setting the GenMC log level once is guaranteed by the `OnceLock`. // No other place calls `set_log_level_raw`, so the `logLevel` value in GenMC will not change once we initialize it once. @@ -40,7 +41,7 @@ pub fn create_genmc_driver_handle( }), "Attempt to change the GenMC log level after it was already set" ); - unsafe { MiriGenmcShim::create_handle(params) } + unsafe { MiriGenmcShim::create_handle(params, do_estimation) } } impl GenmcScalar { @@ -54,6 +55,7 @@ impl GenmcScalar { impl Default for GenmcParams { fn default() -> Self { Self { + estimation_max: 1000, // default taken from GenMC print_random_schedule_seed: false, do_symmetry_reduction: false, // GenMC graphs can be quite large since Miri produces a lot of (non-atomic) events. @@ -70,6 +72,20 @@ impl Default for LogLevel { } } +impl FromStr for SchedulePolicy { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + Ok(match s { + "wf" => SchedulePolicy::WF, + "wfr" => SchedulePolicy::WFR, + "arbitrary" | "random" => SchedulePolicy::Arbitrary, + "ltr" => SchedulePolicy::LTR, + _ => return Err("invalid scheduling policy"), + }) + } +} + impl FromStr for LogLevel { type Err = &'static str; @@ -92,9 +108,12 @@ mod ffi { /**** Types shared between Miri/Rust and Miri/C++ through cxx_bridge: ****/ /// Parameters that will be given to GenMC for setting up the model checker. - /// (The fields of this struct are visible to both Rust and C++) + /// The fields of this struct are visible to both Rust and C++. + /// Note that this struct is #[repr(C)], so the order of fields matters. #[derive(Clone, Debug)] struct GenmcParams { + /// Maximum number of executions explored in estimation mode. + pub estimation_max: u32, pub print_random_schedule_seed: bool, pub do_symmetry_reduction: bool, pub print_execution_graphs: ExecutiongraphPrinting, @@ -165,6 +184,19 @@ mod ffi { next_thread: i32, } + #[must_use] + #[derive(Debug)] + struct EstimationResult { + /// Expected number of total executions. + mean: f64, + /// Standard deviation of the total executions estimate. + sd: f64, + /// Number of explored executions during the estimation. + explored_execs: u64, + /// Number of encounteded blocked executions during the estimation. + blocked_execs: u64, + } + #[must_use] #[derive(Debug)] struct LoadResult { @@ -214,6 +246,14 @@ mod ffi { /**** These are GenMC types that we have to copy-paste here since cxx does not support "importing" externally defined C++ types. ****/ + #[derive(Clone, Copy, Debug)] + enum SchedulePolicy { + LTR, + WF, + WFR, + Arbitrary, + } + #[derive(Debug)] /// Corresponds to GenMC's type with the same name. /// Should only be modified if changed by GenMC. @@ -272,6 +312,7 @@ mod ffi { type ActionKind; type MemOrdering; type RMWBinOp; + type SchedulePolicy; /// Set the log level for GenMC. /// @@ -295,7 +336,10 @@ mod ffi { /// start creating handles. /// There should not be any other (safe) way to create a `MiriGenmcShim`. #[Self = "MiriGenmcShim"] - unsafe fn create_handle(params: &GenmcParams) -> UniquePtr; + unsafe fn create_handle( + params: &GenmcParams, + estimation_mode: bool, + ) -> UniquePtr; /// Get the bit mask that GenMC expects for global memory allocations. fn get_global_alloc_static_mask() -> u64; @@ -403,5 +447,10 @@ mod ffi { fn get_result_message(self: &MiriGenmcShim) -> UniquePtr; /// If an error occurred, return a string describing the error, otherwise, return `nullptr`. fn get_error_string(self: &MiriGenmcShim) -> UniquePtr; + + /**** Printing functionality. ****/ + + /// Get the results of a run in estimation mode. + fn get_estimation_results(self: &MiriGenmcShim) -> EstimationResult; } } diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 731fe283a219..8b15a7863476 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -186,18 +186,17 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { optimizations is usually marginal at best."); } - if let Some(_genmc_config) = &config.genmc_config { + // Run in GenMC mode if enabled. + if config.genmc_config.is_some() { + // This is the entry point used in GenMC mode. + // This closure will be called multiple times to explore the concurrent execution space of the program. let eval_entry_once = |genmc_ctx: Rc| { miri::eval_entry(tcx, entry_def_id, entry_type, &config, Some(genmc_ctx)) }; - - // FIXME(genmc): add estimation mode here. - let return_code = run_genmc_mode(&config, eval_entry_once, tcx).unwrap_or_else(|| { tcx.dcx().abort_if_errors(); rustc_driver::EXIT_FAILURE }); - exit(return_code); }; diff --git a/src/tools/miri/src/concurrency/genmc/config.rs b/src/tools/miri/src/concurrency/genmc/config.rs index 34933d423f06..c7cfa6012b8d 100644 --- a/src/tools/miri/src/concurrency/genmc/config.rs +++ b/src/tools/miri/src/concurrency/genmc/config.rs @@ -10,11 +10,15 @@ use crate::{IsolatedOp, MiriConfig, RejectOpWith}; pub struct GenmcConfig { /// Parameters sent to the C++ side to create a new handle to the GenMC model checker. pub(super) params: GenmcParams, + pub(super) do_estimation: bool, /// Print the output message that GenMC generates when an error occurs. /// This error message is currently hard to use, since there is no clear mapping between the events that GenMC sees and the Rust code location where this event was produced. pub(super) print_genmc_output: bool, /// The log level for GenMC. pub(super) log_level: LogLevel, + /// Enable more verbose output, such as number of executions estimate + /// and time to completion of verification step. + pub(super) verbose_output: bool, } impl GenmcConfig { @@ -57,8 +61,21 @@ impl GenmcConfig { "Invalid suffix to GenMC argument '-Zmiri-genmc-print-exec-graphs', expected '', '=none', '=explored', '=blocked' or '=all'" )), } + } else if trimmed_arg == "estimate" { + // FIXME(genmc): should this be on by default (like for GenMC)? + // Enable estimating the execution space and require time before running the actual verification. + genmc_config.do_estimation = true; + } else if let Some(estimation_max_str) = trimmed_arg.strip_prefix("estimation-max=") { + // Set the maximum number of executions to explore during estimation. + genmc_config.params.estimation_max = estimation_max_str.parse().ok().filter(|estimation_max| *estimation_max > 0).ok_or_else(|| { + format!( + "'-Zmiri-genmc-estimation-max=...' expects a positive integer argument, but got '{estimation_max_str}'" + ) + })?; } else if trimmed_arg == "print-genmc-output" { genmc_config.print_genmc_output = true; + } else if trimmed_arg == "verbose" { + genmc_config.verbose_output = true; } else { return Err(format!("Invalid GenMC argument: \"-Zmiri-genmc-{trimmed_arg}\"")); } diff --git a/src/tools/miri/src/concurrency/genmc/dummy.rs b/src/tools/miri/src/concurrency/genmc/dummy.rs index 92b34b83ee0f..c28984cef35a 100644 --- a/src/tools/miri/src/concurrency/genmc/dummy.rs +++ b/src/tools/miri/src/concurrency/genmc/dummy.rs @@ -39,18 +39,6 @@ mod run { impl GenmcCtx { // We don't provide the `new` function in the dummy module. - pub fn get_blocked_execution_count(&self) -> usize { - unreachable!() - } - - pub fn get_explored_execution_count(&self) -> usize { - unreachable!() - } - - pub fn is_exploration_done(&self) -> bool { - unreachable!() - } - /**** Memory access handling ****/ pub(super) fn set_ongoing_action_data_race_free(&self, _enable: bool) { diff --git a/src/tools/miri/src/concurrency/genmc/mod.rs b/src/tools/miri/src/concurrency/genmc/mod.rs index 7270c6681060..0086d3f2bf0b 100644 --- a/src/tools/miri/src/concurrency/genmc/mod.rs +++ b/src/tools/miri/src/concurrency/genmc/mod.rs @@ -2,8 +2,8 @@ use std::cell::{Cell, RefCell}; use std::sync::Arc; use genmc_sys::{ - GENMC_GLOBAL_ADDRESSES_MASK, GenmcScalar, MemOrdering, MiriGenmcShim, RMWBinOp, UniquePtr, - create_genmc_driver_handle, + EstimationResult, GENMC_GLOBAL_ADDRESSES_MASK, GenmcScalar, MemOrdering, MiriGenmcShim, + RMWBinOp, UniquePtr, create_genmc_driver_handle, }; use rustc_abi::{Align, Size}; use rustc_const_eval::interpret::{AllocId, InterpCx, InterpResult, interp_ok}; @@ -16,6 +16,7 @@ use self::helper::{ MAX_ACCESS_SIZE, Warnings, emit_warning, genmc_scalar_to_scalar, maybe_upgrade_compare_exchange_success_orderings, scalar_to_genmc_scalar, to_genmc_rmw_op, }; +use self::run::GenmcMode; use self::thread_id_map::ThreadIdMap; use crate::concurrency::genmc::helper::split_access; use crate::intrinsics::AtomicRmwOp; @@ -37,6 +38,16 @@ pub use genmc_sys::GenmcParams; pub use self::config::GenmcConfig; pub use self::run::run_genmc_mode; +#[derive(Debug)] +pub enum ExecutionEndResult { + /// An error occurred at the end of the execution. + Error(String), + /// No errors occurred, and there are more executions to explore. + Continue, + /// No errors occurred and we are finished. + Stop, +} + #[derive(Clone, Copy, Debug)] pub enum ExitType { MainThreadFinish, @@ -128,26 +139,33 @@ pub struct GenmcCtx { /// GenMC Context creation and administrative / query actions impl GenmcCtx { /// Create a new `GenmcCtx` from a given config. - fn new(miri_config: &MiriConfig, global_state: Arc) -> Self { + fn new(miri_config: &MiriConfig, global_state: Arc, mode: GenmcMode) -> Self { let genmc_config = miri_config.genmc_config.as_ref().unwrap(); - let handle = - RefCell::new(create_genmc_driver_handle(&genmc_config.params, genmc_config.log_level)); + let handle = RefCell::new(create_genmc_driver_handle( + &genmc_config.params, + genmc_config.log_level, + /* do_estimation: */ mode == GenmcMode::Estimation, + )); Self { handle, exec_state: Default::default(), global_state } } + fn get_estimation_results(&self) -> EstimationResult { + self.handle.borrow().get_estimation_results() + } + /// Get the number of blocked executions encountered by GenMC. - pub fn get_blocked_execution_count(&self) -> u64 { + fn get_blocked_execution_count(&self) -> u64 { self.handle.borrow().get_blocked_execution_count() } /// Get the number of explored executions encountered by GenMC. - pub fn get_explored_execution_count(&self) -> u64 { + fn get_explored_execution_count(&self) -> u64 { self.handle.borrow().get_explored_execution_count() } /// Check if GenMC encountered an error that wasn't immediately returned during execution. /// Returns a string representation of the error if one occurred. - pub fn try_get_error(&self) -> Option { + fn try_get_error(&self) -> Option { self.handle .borrow() .get_error_string() @@ -157,7 +175,7 @@ impl GenmcCtx { /// Check if GenMC encountered an error that wasn't immediately returned during execution. /// Returns a string representation of the error if one occurred. - pub fn get_result_message(&self) -> String { + fn get_result_message(&self) -> String { self.handle .borrow() .get_result_message() @@ -166,13 +184,6 @@ impl GenmcCtx { .expect("there should always be a message") } - /// This function determines if we should continue exploring executions or if we are done. - /// - /// In GenMC mode, the input program should be repeatedly executed until this function returns `true` or an error is found. - pub fn is_exploration_done(&self) -> bool { - self.handle.borrow_mut().pin_mut().is_exploration_done() - } - /// Select whether data race free actions should be allowed. This function should be used carefully! /// /// If `true` is passed, allow for data races to happen without triggering an error, until this function is called again with argument `false`. @@ -218,13 +229,25 @@ impl GenmcCtx { /// This function will also check for those, and return their error description. /// /// To get the all messages (warnings, errors) that GenMC produces, use the `get_result_message` method. - fn handle_execution_end(&self) -> Option { + fn handle_execution_end(&self) -> ExecutionEndResult { let result = self.handle.borrow_mut().pin_mut().handle_execution_end(); - result.as_ref().map(|msg| msg.to_string_lossy().to_string())?; + if let Some(error) = result.as_ref() { + return ExecutionEndResult::Error(error.to_string_lossy().to_string()); + } + + // GenMC decides if there is more to explore: + let exploration_done = self.handle.borrow_mut().pin_mut().is_exploration_done(); // GenMC currently does not return an error value immediately in all cases. + // Both `handle_execution_end` and `is_exploration_done` can produce such errors. // We manually query for any errors here to ensure we don't miss any. - self.try_get_error() + if let Some(error) = self.try_get_error() { + ExecutionEndResult::Error(error) + } else if exploration_done { + ExecutionEndResult::Stop + } else { + ExecutionEndResult::Continue + } } /**** Memory access handling ****/ diff --git a/src/tools/miri/src/concurrency/genmc/run.rs b/src/tools/miri/src/concurrency/genmc/run.rs index 7e4ed816a76a..bc2f5f7b79ec 100644 --- a/src/tools/miri/src/concurrency/genmc/run.rs +++ b/src/tools/miri/src/concurrency/genmc/run.rs @@ -1,32 +1,65 @@ use std::rc::Rc; use std::sync::Arc; +use std::time::Instant; +use genmc_sys::EstimationResult; use rustc_middle::ty::TyCtxt; use super::GlobalState; +use crate::concurrency::genmc::ExecutionEndResult; use crate::rustc_const_eval::interpret::PointerArithmetic; -use crate::{GenmcCtx, MiriConfig}; +use crate::{GenmcConfig, GenmcCtx, MiriConfig}; + +#[derive(Clone, Copy, PartialEq, Eq)] +pub(super) enum GenmcMode { + Estimation, + Verification, +} + +impl GenmcMode { + /// Return whether warnings on unsupported features should be printed in this mode. + fn print_unsupported_warnings(self) -> bool { + self == GenmcMode::Verification + } +} /// Do a complete run of the program in GenMC mode. /// This will call `eval_entry` multiple times, until either: /// - An error is detected (indicated by a `None` return value) /// - All possible executions are explored. /// -/// FIXME(genmc): add estimation mode setting. +/// Returns `None` is an error is detected, or `Some(return_value)` with the return value of the last run of the program. pub fn run_genmc_mode<'tcx>( config: &MiriConfig, eval_entry: impl Fn(Rc) -> Option, tcx: TyCtxt<'tcx>, ) -> Option { let genmc_config = config.genmc_config.as_ref().unwrap(); + // Run in Estimation mode if requested. + if genmc_config.do_estimation { + eprintln!("Estimating GenMC verification time..."); + run_genmc_mode_impl(config, &eval_entry, tcx, GenmcMode::Estimation)?; + } + // Run in Verification mode. + eprintln!("Running GenMC Verification..."); + run_genmc_mode_impl(config, &eval_entry, tcx, GenmcMode::Verification) +} + +fn run_genmc_mode_impl<'tcx>( + config: &MiriConfig, + eval_entry: &impl Fn(Rc) -> Option, + tcx: TyCtxt<'tcx>, + mode: GenmcMode, +) -> Option { + let time_start = Instant::now(); + let genmc_config = config.genmc_config.as_ref().unwrap(); // There exists only one `global_state` per full run in GenMC mode. // It is shared by all `GenmcCtx` in this run. // FIXME(genmc): implement multithreading once GenMC supports it. - // FIXME(genmc): disable warnings in estimation mode once it is added. let global_state = - Arc::new(GlobalState::new(tcx.target_usize_max(), /* print_warnings */ true)); - let genmc_ctx = Rc::new(GenmcCtx::new(config, global_state)); + Arc::new(GlobalState::new(tcx.target_usize_max(), mode.print_unsupported_warnings())); + let genmc_ctx = Rc::new(GenmcCtx::new(config, global_state, mode)); // `rep` is used to report the progress, Miri will panic on wrap-around. for rep in 0u64.. { @@ -37,46 +70,97 @@ pub fn run_genmc_mode<'tcx>( // Execute the program until completion to get the return value, or return if an error happens: let Some(return_code) = eval_entry(genmc_ctx.clone()) else { - // If requested, print the output GenMC produced: - if genmc_config.print_genmc_output { - eprintln!("== raw GenMC output ========================="); - eprintln!("{}", genmc_ctx.get_result_message()); - eprintln!("== end of raw GenMC output =================="); - } + genmc_ctx.print_genmc_output(genmc_config); return None; }; - // We inform GenMC that the execution is complete. If there was an error, we print it. - if let Some(error) = genmc_ctx.handle_execution_end() { - // This can be reached for errors that affect the entire execution, not just a specific event. - // For instance, linearizability checking and liveness checking report their errors this way. - // Neither are supported by Miri-GenMC at the moment though. However, GenMC also - // treats races on deallocation as global errors, so this code path is still reachable. - // Since we don't have any span information for the error at this point, - // we just print GenMC's error message. - eprintln!("(GenMC) Error detected: {error}"); - eprintln!(); - eprintln!("{}", genmc_ctx.get_result_message()); - return None; + // We inform GenMC that the execution is complete. + // If there was an error, we print it. + match genmc_ctx.handle_execution_end() { + ExecutionEndResult::Continue => continue, + ExecutionEndResult::Stop => { + let elapsed_time_sec = Instant::now().duration_since(time_start).as_secs_f64(); + // Print the output for the current mode. + if mode == GenmcMode::Estimation { + genmc_ctx.print_estimation_output(genmc_config, elapsed_time_sec); + } else { + genmc_ctx.print_verification_output(genmc_config, elapsed_time_sec); + } + // Return the return code of the last execution. + return Some(return_code); + } + ExecutionEndResult::Error(error) => { + // This can be reached for errors that affect the entire execution, not just a specific event. + // For instance, linearizability checking and liveness checking report their errors this way. + // Neither are supported by Miri-GenMC at the moment though. However, GenMC also + // treats races on deallocation as global errors, so this code path is still reachable. + // Since we don't have any span information for the error at this point, + // we just print GenMC's error string, and the full GenMC output if requested. + eprintln!("(GenMC) Error detected: {error}"); + genmc_ctx.print_genmc_output(genmc_config); + return None; + } } - - // Check if we've explored enough executions: - if !genmc_ctx.is_exploration_done() { - continue; - } - - eprintln!("(GenMC) Verification complete. No errors were detected."); - - let explored_execution_count = genmc_ctx.get_explored_execution_count(); - let blocked_execution_count = genmc_ctx.get_blocked_execution_count(); - - eprintln!("Number of complete executions explored: {explored_execution_count}"); - if blocked_execution_count > 0 { - eprintln!("Number of blocked executions seen: {blocked_execution_count}"); - } - - // Return the return code of the last execution. - return Some(return_code); } unreachable!() } + +impl GenmcCtx { + /// Print the full output message produced by GenMC if requested, or a hint on how to enable it. + /// + /// This message can be very verbose and is likely not useful for the average user. + /// This function should be called *after* Miri has printed all of its output. + fn print_genmc_output(&self, genmc_config: &GenmcConfig) { + if genmc_config.print_genmc_output { + eprintln!("GenMC error report:"); + eprintln!("{}", self.get_result_message()); + } else { + eprintln!( + "(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.)" + ); + } + } + + /// Given the time taken for the estimation mode run, print the expected time range for verification. + /// Verbose output also includes information about the expected number of executions and how many estimation rounds were explored or got blocked. + fn print_estimation_output(&self, genmc_config: &GenmcConfig, elapsed_time_sec: f64) { + let EstimationResult { mean, sd, explored_execs, blocked_execs } = + self.get_estimation_results(); + #[allow(clippy::as_conversions)] + let time_per_exec_sec = elapsed_time_sec / (explored_execs as f64 + blocked_execs as f64); + let estimated_mean_sec = time_per_exec_sec * mean; + let estimated_sd_sec = time_per_exec_sec * sd; + + if genmc_config.verbose_output { + eprintln!("Finished estimation in {elapsed_time_sec:.2?}s"); + if blocked_execs != 0 { + eprintln!(" Explored executions: {explored_execs}"); + eprintln!(" Blocked executions: {blocked_execs}"); + } + eprintln!("Expected number of executions: {mean:.0} ± {sd:.0}"); + } + // The estimation can be out-of-bounds of an `f64`. + if !(mean.is_finite() && mean >= 0.0 && sd.is_finite() && sd >= 0.0) { + eprintln!("WARNING: Estimation gave weird results, there may have been an overflow."); + } + eprintln!("Expected verification time: {estimated_mean_sec:.2}s ± {estimated_sd_sec:.2}s"); + } + + /// Given the time taken for the verification mode run, print the expected time range for verification. + /// Verbose output also includes information about the expected number of executions and how many estimation rounds were explored or got blocked. + fn print_verification_output(&self, genmc_config: &GenmcConfig, elapsed_time_sec: f64) { + let explored_execution_count = self.get_explored_execution_count(); + let blocked_execution_count = self.get_blocked_execution_count(); + eprintln!( + "Verification complete with {} executions. No errors found.", + explored_execution_count + blocked_execution_count + ); + if genmc_config.verbose_output { + if blocked_execution_count > 0 { + eprintln!("Number of complete executions explored: {explored_execution_count}"); + eprintln!("Number of blocked executions seen: {blocked_execution_count}"); + } + eprintln!("Verification took {elapsed_time_sec:.2?}s."); + } + } +} diff --git a/src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.stderr b/src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.stderr index 946d9a2124b8..00de74009d9b 100644 --- a/src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.stderr +++ b/src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.stderr @@ -1,3 +1,4 @@ +Running GenMC Verification... error: Undefined Behavior: Non-atomic race --> tests/genmc/fail/data_race/mpu2_rels_rlx.rs:LL:CC | @@ -15,5 +16,6 @@ note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/f LL | f(); | ^^^ +(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rel_rlx.stderr b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rel_rlx.stderr index 121ded2a181c..501957a90d3a 100644 --- a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rel_rlx.stderr +++ b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rel_rlx.stderr @@ -1,3 +1,4 @@ +Running GenMC Verification... error: Undefined Behavior: Non-atomic race --> tests/genmc/fail/data_race/weak_orderings.rs:LL:CC | @@ -15,5 +16,6 @@ note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/f LL | f(); | ^^^ +(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_acq.stderr b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_acq.stderr index 121ded2a181c..501957a90d3a 100644 --- a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_acq.stderr +++ b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_acq.stderr @@ -1,3 +1,4 @@ +Running GenMC Verification... error: Undefined Behavior: Non-atomic race --> tests/genmc/fail/data_race/weak_orderings.rs:LL:CC | @@ -15,5 +16,6 @@ note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/f LL | f(); | ^^^ +(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_rlx.stderr b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_rlx.stderr index 121ded2a181c..501957a90d3a 100644 --- a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_rlx.stderr +++ b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_rlx.stderr @@ -1,3 +1,4 @@ +Running GenMC Verification... error: Undefined Behavior: Non-atomic race --> tests/genmc/fail/data_race/weak_orderings.rs:LL:CC | @@ -15,5 +16,6 @@ note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/f LL | f(); | ^^^ +(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/fail/loom/buggy_inc.stderr b/src/tools/miri/tests/genmc/fail/loom/buggy_inc.stderr index 5a8948ff9b4b..9c3ba1d4c592 100644 --- a/src/tools/miri/tests/genmc/fail/loom/buggy_inc.stderr +++ b/src/tools/miri/tests/genmc/fail/loom/buggy_inc.stderr @@ -1,3 +1,4 @@ +Running GenMC Verification... error: abnormal termination: the program aborted execution --> tests/genmc/fail/loom/buggy_inc.rs:LL:CC | @@ -9,5 +10,6 @@ LL | std::process::abort(); note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace +(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/fail/loom/store_buffering.genmc.stderr b/src/tools/miri/tests/genmc/fail/loom/store_buffering.genmc.stderr index a6c3ed7055e7..db92d0573fad 100644 --- a/src/tools/miri/tests/genmc/fail/loom/store_buffering.genmc.stderr +++ b/src/tools/miri/tests/genmc/fail/loom/store_buffering.genmc.stderr @@ -1,3 +1,4 @@ +Running GenMC Verification... error: abnormal termination: the program aborted execution --> tests/genmc/fail/loom/store_buffering.rs:LL:CC | @@ -9,5 +10,6 @@ LL | std::process::abort(); note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace +(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr index cbfb6ec833eb..773f86a97596 100644 --- a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr +++ b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr @@ -1,3 +1,4 @@ +Running GenMC Verification... error: abnormal termination: the program aborted execution --> tests/genmc/fail/simple/2w2w_weak.rs:LL:CC | @@ -9,5 +10,6 @@ LL | std::process::abort(); note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace +(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.release4.stderr b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.release4.stderr index cbfb6ec833eb..773f86a97596 100644 --- a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.release4.stderr +++ b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.release4.stderr @@ -1,3 +1,4 @@ +Running GenMC Verification... error: abnormal termination: the program aborted execution --> tests/genmc/fail/simple/2w2w_weak.rs:LL:CC | @@ -9,5 +10,6 @@ LL | std::process::abort(); note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace +(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr index cbfb6ec833eb..773f86a97596 100644 --- a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr +++ b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr @@ -1,3 +1,4 @@ +Running GenMC Verification... error: abnormal termination: the program aborted execution --> tests/genmc/fail/simple/2w2w_weak.rs:LL:CC | @@ -9,5 +10,6 @@ LL | std::process::abort(); note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace +(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.stderr b/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.stderr index d97b8b3d92f4..31438f9352fe 100644 --- a/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.stderr +++ b/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.stderr @@ -1,3 +1,4 @@ +Running GenMC Verification... warning: GenMC currently does not model the failure ordering for `compare_exchange`. Success ordering 'Release' was upgraded to 'AcqRel' to match failure ordering 'Acquire'. Miri with GenMC might miss bugs related to this memory access. --> tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.rs:LL:CC | @@ -18,5 +19,4 @@ note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/p LL | f(); | ^^^ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 2 +Verification complete with 2 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/atomics/cas_simple.stderr b/src/tools/miri/tests/genmc/pass/atomics/cas_simple.stderr index 3b22247ee44c..7867be2dbe8e 100644 --- a/src/tools/miri/tests/genmc/pass/atomics/cas_simple.stderr +++ b/src/tools/miri/tests/genmc/pass/atomics/cas_simple.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 1 +Running GenMC Verification... +Verification complete with 1 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/atomics/rmw_ops.stderr b/src/tools/miri/tests/genmc/pass/atomics/rmw_ops.stderr index 3b22247ee44c..7867be2dbe8e 100644 --- a/src/tools/miri/tests/genmc/pass/atomics/rmw_ops.stderr +++ b/src/tools/miri/tests/genmc/pass/atomics/rmw_ops.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 1 +Running GenMC Verification... +Verification complete with 1 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/2cowr.stderr b/src/tools/miri/tests/genmc/pass/litmus/2cowr.stderr index 0667962f99c8..a67635dee1be 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/2cowr.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/2cowr.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 4 +Running GenMC Verification... +Verification complete with 4 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_2sc_scf.stderr b/src/tools/miri/tests/genmc/pass/litmus/2w2w_2sc_scf.stderr index 115b1986ce55..485142e945a7 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/2w2w_2sc_scf.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_2sc_scf.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 3 +Running GenMC Verification... +Verification complete with 3 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release1.stderr b/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release1.stderr index 0667962f99c8..a67635dee1be 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release1.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release1.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 4 +Running GenMC Verification... +Verification complete with 4 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release2.stderr b/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release2.stderr index 0667962f99c8..a67635dee1be 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release2.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release2.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 4 +Running GenMC Verification... +Verification complete with 4 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.sc.stderr b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.sc.stderr index 115b1986ce55..485142e945a7 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.sc.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.sc.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 3 +Running GenMC Verification... +Verification complete with 3 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.weak.stderr b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.weak.stderr index 0667962f99c8..a67635dee1be 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.weak.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.weak.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 4 +Running GenMC Verification... +Verification complete with 4 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_4sc.stderr b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4sc.stderr index 115b1986ce55..485142e945a7 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/2w2w_4sc.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4sc.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 3 +Running GenMC Verification... +Verification complete with 3 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.stderr b/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.stderr index c760b4460511..d6ec73a8c667 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 16 +Running GenMC Verification... +Verification complete with 16 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/IRIWish.stderr b/src/tools/miri/tests/genmc/pass/litmus/IRIWish.stderr index 4fc77b63f380..7ea2dd508513 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/IRIWish.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/IRIWish.stderr @@ -1,3 +1,4 @@ +Running GenMC Verification... [1, 1, 1, 1, 1] [1, 1, 1, 0, 1] [1, 1, 1, 0, 0] @@ -26,5 +27,4 @@ [0, 0, 0, 0, 0] [0, 0, 0, 0, 1] [0, 0, 0, 0, 0] -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 28 +Verification complete with 28 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/LB.stderr b/src/tools/miri/tests/genmc/pass/litmus/LB.stderr index 115b1986ce55..485142e945a7 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/LB.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/LB.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 3 +Running GenMC Verification... +Verification complete with 3 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/LB_incMPs.stderr b/src/tools/miri/tests/genmc/pass/litmus/LB_incMPs.stderr index c879e95a1708..e414cd5e064d 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/LB_incMPs.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/LB_incMPs.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 15 +Running GenMC Verification... +Verification complete with 15 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/MP.stderr b/src/tools/miri/tests/genmc/pass/litmus/MP.stderr index 115b1986ce55..485142e945a7 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/MP.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/MP.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 3 +Running GenMC Verification... +Verification complete with 3 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.stderr b/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.stderr index 4551c00b0575..29b59ce3bc1a 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.stderr @@ -1,3 +1,4 @@ +Running GenMC Verification... X=1, Y=2, a=Err(1), b=Ok(1), c=2 X=1, Y=2, a=Err(1), b=Ok(1), c=1 X=1, Y=2, a=Err(1), b=Ok(1), c=0 @@ -34,5 +35,4 @@ X=1, Y=1, a=Err(0), b=Err(0), c=0 X=1, Y=1, a=Err(0), b=Err(0), c=1 X=1, Y=1, a=Err(0), b=Err(0), c=0 X=1, Y=1, a=Err(0), b=Err(0), c=0 -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 36 +Verification complete with 36 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.stderr b/src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.stderr index c5e34b00642a..423ee6dc3999 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 13 +Running GenMC Verification... +Verification complete with 13 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/MP_incMPs.stderr b/src/tools/miri/tests/genmc/pass/litmus/MP_incMPs.stderr index 2be9a6c7fbde..f527b6120232 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/MP_incMPs.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/MP_incMPs.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 7 +Running GenMC Verification... +Verification complete with 7 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.stderr b/src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.stderr index 0667962f99c8..a67635dee1be 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 4 +Running GenMC Verification... +Verification complete with 4 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/SB.stderr b/src/tools/miri/tests/genmc/pass/litmus/SB.stderr index 0667962f99c8..a67635dee1be 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/SB.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/SB.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 4 +Running GenMC Verification... +Verification complete with 4 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/SB_2sc_scf.stderr b/src/tools/miri/tests/genmc/pass/litmus/SB_2sc_scf.stderr index 115b1986ce55..485142e945a7 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/SB_2sc_scf.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/SB_2sc_scf.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 3 +Running GenMC Verification... +Verification complete with 3 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/Z6_U.sc.stderr b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.sc.stderr index 8dd2fbe1b975..c8fbb8951a38 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/Z6_U.sc.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.sc.stderr @@ -1,3 +1,4 @@ +Running GenMC Verification... a=2, b=1, X=1, Y=3 a=4, b=1, X=1, Y=4 a=3, b=1, X=1, Y=3 @@ -16,5 +17,4 @@ a=3, b=0, X=1, Y=1 a=1, b=1, X=1, Y=3 a=1, b=1, X=1, Y=1 a=1, b=0, X=1, Y=1 -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 18 +Verification complete with 18 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/Z6_U.weak.stderr b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.weak.stderr index 65622575de21..72c59d33f77c 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/Z6_U.weak.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.weak.stderr @@ -1,3 +1,4 @@ +Running GenMC Verification... a=2, b=1, X=1, Y=3 a=4, b=1, X=1, Y=4 a=4, b=0, X=1, Y=4 @@ -20,5 +21,4 @@ a=1, b=1, X=1, Y=3 a=1, b=0, X=1, Y=3 a=1, b=1, X=1, Y=1 a=1, b=0, X=1, Y=1 -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 22 +Verification complete with 22 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/Z6_acq.stderr b/src/tools/miri/tests/genmc/pass/litmus/Z6_acq.stderr index 2be9a6c7fbde..f527b6120232 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/Z6_acq.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/Z6_acq.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 7 +Running GenMC Verification... +Verification complete with 7 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/atomicpo.stderr b/src/tools/miri/tests/genmc/pass/litmus/atomicpo.stderr index 0667962f99c8..a67635dee1be 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/atomicpo.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/atomicpo.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 4 +Running GenMC Verification... +Verification complete with 4 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/casdep.stderr b/src/tools/miri/tests/genmc/pass/litmus/casdep.stderr index 01701dfe6918..bde951866d01 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/casdep.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/casdep.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 2 +Running GenMC Verification... +Verification complete with 2 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/ccr.stderr b/src/tools/miri/tests/genmc/pass/litmus/ccr.stderr index 01701dfe6918..bde951866d01 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/ccr.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/ccr.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 2 +Running GenMC Verification... +Verification complete with 2 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/cii.stderr b/src/tools/miri/tests/genmc/pass/litmus/cii.stderr index be75e68fde77..b9bdf2245aed 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/cii.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/cii.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 6 +Running GenMC Verification... +Verification complete with 6 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr.stderr b/src/tools/miri/tests/genmc/pass/litmus/corr.stderr index be75e68fde77..b9bdf2245aed 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/corr.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/corr.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 6 +Running GenMC Verification... +Verification complete with 6 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr0.stderr b/src/tools/miri/tests/genmc/pass/litmus/corr0.stderr index 0667962f99c8..a67635dee1be 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/corr0.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/corr0.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 4 +Running GenMC Verification... +Verification complete with 4 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr1.stderr b/src/tools/miri/tests/genmc/pass/litmus/corr1.stderr index f6d07e9c77b2..384425fc43c6 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/corr1.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/corr1.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 36 +Running GenMC Verification... +Verification complete with 36 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr2.stderr b/src/tools/miri/tests/genmc/pass/litmus/corr2.stderr index 78a90b63feab..07fd2d4de180 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/corr2.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/corr2.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 72 +Running GenMC Verification... +Verification complete with 72 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/corw.stderr b/src/tools/miri/tests/genmc/pass/litmus/corw.stderr index 115b1986ce55..485142e945a7 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/corw.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/corw.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 3 +Running GenMC Verification... +Verification complete with 3 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/cowr.stderr b/src/tools/miri/tests/genmc/pass/litmus/cowr.stderr index 115b1986ce55..485142e945a7 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/cowr.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/cowr.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 3 +Running GenMC Verification... +Verification complete with 3 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/cumul-release.stderr b/src/tools/miri/tests/genmc/pass/litmus/cumul-release.stderr index 00394048ec5c..a031dfc8977a 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/cumul-release.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/cumul-release.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 8 +Running GenMC Verification... +Verification complete with 8 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/default.stderr b/src/tools/miri/tests/genmc/pass/litmus/default.stderr index e0313930282e..87d38d250411 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/default.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/default.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 12 +Running GenMC Verification... +Verification complete with 12 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/detour.join.stderr b/src/tools/miri/tests/genmc/pass/litmus/detour.join.stderr index 15017249dc3a..5006f11fefb1 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/detour.join.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/detour.join.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 9 +Running GenMC Verification... +Verification complete with 9 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/detour.no_join.stderr b/src/tools/miri/tests/genmc/pass/litmus/detour.no_join.stderr index 15017249dc3a..5006f11fefb1 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/detour.no_join.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/detour.no_join.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 9 +Running GenMC Verification... +Verification complete with 9 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.stderr b/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.stderr index 3b6ba238f534..c09b50e282f1 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 210 +Running GenMC Verification... +Verification complete with 210 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/inc2w.stderr b/src/tools/miri/tests/genmc/pass/litmus/inc2w.stderr index be75e68fde77..b9bdf2245aed 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/inc2w.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/inc2w.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 6 +Running GenMC Verification... +Verification complete with 6 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/inc_inc_RR_W_RR.stderr b/src/tools/miri/tests/genmc/pass/litmus/inc_inc_RR_W_RR.stderr index b5f8cd15b683..770fb7ef8804 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/inc_inc_RR_W_RR.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/inc_inc_RR_W_RR.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 600 +Running GenMC Verification... +Verification complete with 600 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/riwi.stderr b/src/tools/miri/tests/genmc/pass/litmus/riwi.stderr index 115b1986ce55..485142e945a7 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/riwi.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/riwi.stderr @@ -1,2 +1,2 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 3 +Running GenMC Verification... +Verification complete with 3 executions. No errors found. diff --git a/src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.rs b/src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.rs index a193dcf0683e..3256c9f42119 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.rs @@ -1,6 +1,8 @@ -//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -Zmiri-genmc-estimate // Translated from GenMC's "litmus/viktor-relseq" test. +// +// This test also checks that we can run the GenMC estimation mode. #![no_main] diff --git a/src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.stderr b/src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.stderr index d63ac5199d5d..c53d5c569b9c 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.stderr +++ b/src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.stderr @@ -1,2 +1,4 @@ -(GenMC) Verification complete. No errors were detected. -Number of complete executions explored: 180 +Estimating GenMC verification time... +Expected verification time: [MEAN] ± [SD] +Running GenMC Verification... +Verification complete with 180 executions. No errors found. diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs index b7286d9a3673..efaaf9fc8417 100644 --- a/src/tools/miri/tests/ui.rs +++ b/src/tools/miri/tests/ui.rs @@ -277,6 +277,8 @@ regexes! { r"\bsys/([a-z_]+)/[a-z]+\b" => "sys/$1/PLATFORM", // erase paths into the crate registry r"[^ ]*/\.?cargo/registry/.*/(.*\.rs)" => "CARGO_REGISTRY/.../$1", + // remove time print from GenMC estimation mode output. + "\nExpected verification time: .* ± .*" => "\nExpected verification time: [MEAN] ± [SD]", } enum Dependencies { From 72fb9b28a62e56d266e34764ef7330ca09266f48 Mon Sep 17 00:00:00 2001 From: lukaslueg Date: Thu, 18 Sep 2025 11:51:29 +0200 Subject: [PATCH 620/682] Remove feature-freeze from gh templates --- .github/ISSUE_TEMPLATE/new_lint.yml | 4 +--- .github/PULL_REQUEST_TEMPLATE.md | 4 ---- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/new_lint.yml b/.github/ISSUE_TEMPLATE/new_lint.yml index a8202f6378fd..6ad16aead601 100644 --- a/.github/ISSUE_TEMPLATE/new_lint.yml +++ b/.github/ISSUE_TEMPLATE/new_lint.yml @@ -1,7 +1,5 @@ name: New lint suggestion -description: | - Suggest a new Clippy lint (currently not accepting new lints) - Check out the Clippy book for more information about the feature freeze. +description: Suggest a new Clippy lint. labels: ["A-lint"] body: - type: markdown diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 83bfd8e9c686..9e49f60892d2 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -32,10 +32,6 @@ order to get feedback. Delete this line and everything above before opening your PR. -Note that we are currently not taking in new PRs that add new lints. We are in a -feature freeze. Check out the book for more information. If you open a -feature-adding pull request, its review will be delayed. - --- *Please write a short comment explaining your change (or "none" for internal only changes)* From 68473ad11b058ed6a533eec889e28d1695fd2f8f Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Thu, 18 Sep 2025 11:54:58 +0200 Subject: [PATCH 621/682] Check that `.unwrap_or(LITERAL)` doesn't trigger `unwrap_or_default` --- tests/ui/or_fun_call.fixed | 12 ++++++ tests/ui/or_fun_call.rs | 12 ++++++ tests/ui/or_fun_call.stderr | 76 ++++++++++++++++++------------------- 3 files changed, 62 insertions(+), 38 deletions(-) diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 0a8525a12f5e..ac771c34ef49 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -77,6 +77,18 @@ fn or_fun_call() { with_default_type.unwrap_or_default(); //~^ unwrap_or_default + let with_default_literal = Some(1); + with_default_literal.unwrap_or(0); + // Do not lint because `.unwrap_or_default()` wouldn't be simpler + + let with_default_literal = Some(1.0); + with_default_literal.unwrap_or(0.0); + // Do not lint because `.unwrap_or_default()` wouldn't be simpler + + let with_default_literal = Some("foo"); + with_default_literal.unwrap_or(""); + // Do not lint because `.unwrap_or_default()` wouldn't be simpler + let self_default = None::; self_default.unwrap_or_else(::default); //~^ or_fun_call diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index b4f9b950a7fe..75762fb020ac 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -77,6 +77,18 @@ fn or_fun_call() { with_default_type.unwrap_or(u64::default()); //~^ unwrap_or_default + let with_default_literal = Some(1); + with_default_literal.unwrap_or(0); + // Do not lint because `.unwrap_or_default()` wouldn't be simpler + + let with_default_literal = Some(1.0); + with_default_literal.unwrap_or(0.0); + // Do not lint because `.unwrap_or_default()` wouldn't be simpler + + let with_default_literal = Some("foo"); + with_default_literal.unwrap_or(""); + // Do not lint because `.unwrap_or_default()` wouldn't be simpler + let self_default = None::; self_default.unwrap_or(::default()); //~^ or_fun_call diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index 3e4df772668d..0f0cd3329b66 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -47,175 +47,175 @@ LL | with_default_type.unwrap_or(u64::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:81:18 + --> tests/ui/or_fun_call.rs:93:18 | LL | self_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(::default)` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:85:18 + --> tests/ui/or_fun_call.rs:97:18 | LL | real_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:89:14 + --> tests/ui/or_fun_call.rs:101:14 | LL | with_vec.unwrap_or(vec![]); | ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:93:21 + --> tests/ui/or_fun_call.rs:105:21 | LL | without_default.unwrap_or(Foo::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(Foo::new)` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:97:19 + --> tests/ui/or_fun_call.rs:109:19 | LL | map.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:101:23 + --> tests/ui/or_fun_call.rs:113:23 | LL | map_vec.entry(42).or_insert(vec![]); | ^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:105:21 + --> tests/ui/or_fun_call.rs:117:21 | LL | btree.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:109:25 + --> tests/ui/or_fun_call.rs:121:25 | LL | btree_vec.entry(42).or_insert(vec![]); | ^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:113:21 + --> tests/ui/or_fun_call.rs:125:21 | LL | let _ = stringy.unwrap_or(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `ok_or` - --> tests/ui/or_fun_call.rs:118:17 + --> tests/ui/or_fun_call.rs:130:17 | LL | let _ = opt.ok_or(format!("{} world.", hello)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ok_or_else(|| format!("{} world.", hello))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:123:21 + --> tests/ui/or_fun_call.rs:135:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:126:21 + --> tests/ui/or_fun_call.rs:138:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])` error: function call inside of `or` - --> tests/ui/or_fun_call.rs:151:35 + --> tests/ui/or_fun_call.rs:163:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_else(|| Some("b".to_string()))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:194:18 + --> tests/ui/or_fun_call.rs:206:18 | LL | None.unwrap_or(ptr_to_ref(s)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| ptr_to_ref(s))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:202:14 + --> tests/ui/or_fun_call.rs:214:14 | LL | None.unwrap_or(unsafe { ptr_to_ref(s) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:205:14 + --> tests/ui/or_fun_call.rs:217:14 | LL | None.unwrap_or( unsafe { ptr_to_ref(s) } ); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:281:25 + --> tests/ui/or_fun_call.rs:293:25 | LL | let _ = Some(4).map_or(g(), |v| v); | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(g, |v| v)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:283:25 + --> tests/ui/or_fun_call.rs:295:25 | LL | let _ = Some(4).map_or(g(), f); | ^^^^^^^^^^^^^^ help: try: `map_or_else(g, f)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:286:25 + --> tests/ui/or_fun_call.rs:298:25 | LL | let _ = Some(4).map_or("asd".to_string().len() as i32, f); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| "asd".to_string().len() as i32, f)` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:317:18 + --> tests/ui/or_fun_call.rs:329:18 | LL | with_new.unwrap_or_else(Vec::new); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:321:28 + --> tests/ui/or_fun_call.rs:333:28 | LL | with_default_trait.unwrap_or_else(Default::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:325:27 + --> tests/ui/or_fun_call.rs:337:27 | LL | with_default_type.unwrap_or_else(u64::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:329:22 + --> tests/ui/or_fun_call.rs:341:22 | LL | real_default.unwrap_or_else(::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/or_fun_call.rs:333:23 + --> tests/ui/or_fun_call.rs:345:23 | LL | map.entry(42).or_insert_with(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/or_fun_call.rs:337:25 + --> tests/ui/or_fun_call.rs:349:25 | LL | btree.entry(42).or_insert_with(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:341:25 + --> tests/ui/or_fun_call.rs:353:25 | LL | let _ = stringy.unwrap_or_else(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:383:17 + --> tests/ui/or_fun_call.rs:395:17 | LL | let _ = opt.unwrap_or({ f() }); // suggest `.unwrap_or_else(f)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(f)` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:388:17 + --> tests/ui/or_fun_call.rs:400:17 | LL | let _ = opt.unwrap_or(f() + 1); // suggest `.unwrap_or_else(|| f() + 1)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| f() + 1)` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:393:17 + --> tests/ui/or_fun_call.rs:405:17 | LL | let _ = opt.unwrap_or({ | _________________^ @@ -235,55 +235,55 @@ LL ~ }); | error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:399:17 + --> tests/ui/or_fun_call.rs:411:17 | LL | let _ = opt.map_or(f() + 1, |v| v); // suggest `.map_or_else(|| f() + 1, |v| v)` | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| f() + 1, |v| v)` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:404:17 + --> tests/ui/or_fun_call.rs:416:17 | LL | let _ = opt.unwrap_or({ i32::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:411:21 + --> tests/ui/or_fun_call.rs:423:21 | LL | let _ = opt_foo.unwrap_or(Foo { val: String::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Foo { val: String::default() })` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:426:19 + --> tests/ui/or_fun_call.rs:438:19 | LL | let _ = x.map_or(g(), |v| v); | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), |v| v)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:428:19 + --> tests/ui/or_fun_call.rs:440:19 | LL | let _ = x.map_or(g(), f); | ^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), f)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:431:19 + --> tests/ui/or_fun_call.rs:443:19 | LL | let _ = x.map_or("asd".to_string().len() as i32, f); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|_| "asd".to_string().len() as i32, f)` error: function call inside of `get_or_insert` - --> tests/ui/or_fun_call.rs:442:15 + --> tests/ui/or_fun_call.rs:454:15 | LL | let _ = x.get_or_insert(g()); | ^^^^^^^^^^^^^^^^^^ help: try: `get_or_insert_with(g)` error: function call inside of `and` - --> tests/ui/or_fun_call.rs:452:15 + --> tests/ui/or_fun_call.rs:464:15 | LL | let _ = x.and(g()); | ^^^^^^^^ help: try: `and_then(|_| g())` error: function call inside of `and` - --> tests/ui/or_fun_call.rs:462:15 + --> tests/ui/or_fun_call.rs:474:15 | LL | let _ = x.and(g()); | ^^^^^^^^ help: try: `and_then(|_| g())` From ad21dff50ab20a4a8c306eddffc779dfa9b0ee47 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Thu, 18 Sep 2025 11:54:58 +0200 Subject: [PATCH 622/682] Do not replace `.unwrap_or(vec![])` by `.unwrap_or_default()` `.unwrap_or(vec![])` is as readable as `.unwrap_or_default()`. --- clippy_lints/src/methods/or_fun_call.rs | 7 ++ tests/ui/or_fun_call.fixed | 4 ++ tests/ui/or_fun_call.rs | 10 ++- tests/ui/or_fun_call.stderr | 88 ++++++++++++------------- 4 files changed, 62 insertions(+), 47 deletions(-) diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 04f0e3c0479e..71b2f251eded 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -2,6 +2,7 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::eager_or_lazy::switch_to_lazy_eval; +use clippy_utils::higher::VecArgs; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{expr_type_is_certain, implements_trait, is_type_diagnostic_item}; use clippy_utils::visitors::for_each_expr; @@ -97,6 +98,12 @@ pub(super) fn check<'tcx>( return false; } + // `.unwrap_or(vec![])` is as readable as `.unwrap_or_default()`. And if the expression is a + // non-empty `Vec`, then it will not be a default value anyway. Bail out in all cases. + if call_expr.and_then(|call_expr| VecArgs::hir(cx, call_expr)).is_some() { + return false; + } + // needs to target Default::default in particular or be *::new and have a Default impl // available if (is_new(fun) && output_type_implements_default(fun)) diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index ac771c34ef49..7a0be97017eb 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -89,6 +89,10 @@ fn or_fun_call() { with_default_literal.unwrap_or(""); // Do not lint because `.unwrap_or_default()` wouldn't be simpler + let with_default_vec_macro = Some(vec![1, 2, 3]); + with_default_vec_macro.unwrap_or(vec![]); + // Do not lint because `.unwrap_or_default()` wouldn't be simpler + let self_default = None::; self_default.unwrap_or_else(::default); //~^ or_fun_call diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 75762fb020ac..724af606de9c 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -89,6 +89,10 @@ fn or_fun_call() { with_default_literal.unwrap_or(""); // Do not lint because `.unwrap_or_default()` wouldn't be simpler + let with_default_vec_macro = Some(vec![1, 2, 3]); + with_default_vec_macro.unwrap_or(vec![]); + // Do not lint because `.unwrap_or_default()` wouldn't be simpler + let self_default = None::; self_default.unwrap_or(::default()); //~^ or_fun_call @@ -98,7 +102,7 @@ fn or_fun_call() { //~^ unwrap_or_default let with_vec = Some(vec![1]); - with_vec.unwrap_or(vec![]); + with_vec.unwrap_or(Vec::new()); //~^ unwrap_or_default let without_default = Some(Foo); @@ -110,7 +114,7 @@ fn or_fun_call() { //~^ unwrap_or_default let mut map_vec = HashMap::>::new(); - map_vec.entry(42).or_insert(vec![]); + map_vec.entry(42).or_insert(Vec::new()); //~^ unwrap_or_default let mut btree = BTreeMap::::new(); @@ -118,7 +122,7 @@ fn or_fun_call() { //~^ unwrap_or_default let mut btree_vec = BTreeMap::>::new(); - btree_vec.entry(42).or_insert(vec![]); + btree_vec.entry(42).or_insert(Vec::new()); //~^ unwrap_or_default let stringy = Some(String::new()); diff --git a/tests/ui/or_fun_call.stderr b/tests/ui/or_fun_call.stderr index 0f0cd3329b66..40b25f91154d 100644 --- a/tests/ui/or_fun_call.stderr +++ b/tests/ui/or_fun_call.stderr @@ -47,175 +47,175 @@ LL | with_default_type.unwrap_or(u64::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:93:18 + --> tests/ui/or_fun_call.rs:97:18 | LL | self_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(::default)` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:97:18 + --> tests/ui/or_fun_call.rs:101:18 | LL | real_default.unwrap_or(::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:101:14 + --> tests/ui/or_fun_call.rs:105:14 | -LL | with_vec.unwrap_or(vec![]); - | ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` +LL | with_vec.unwrap_or(Vec::new()); + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:105:21 + --> tests/ui/or_fun_call.rs:109:21 | LL | without_default.unwrap_or(Foo::new()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(Foo::new)` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:109:19 + --> tests/ui/or_fun_call.rs:113:19 | LL | map.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:113:23 + --> tests/ui/or_fun_call.rs:117:23 | -LL | map_vec.entry(42).or_insert(vec![]); - | ^^^^^^^^^^^^^^^^^ help: try: `or_default()` +LL | map_vec.entry(42).or_insert(Vec::new()); + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:117:21 + --> tests/ui/or_fun_call.rs:121:21 | LL | btree.entry(42).or_insert(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert` to construct default value - --> tests/ui/or_fun_call.rs:121:25 + --> tests/ui/or_fun_call.rs:125:25 | -LL | btree_vec.entry(42).or_insert(vec![]); - | ^^^^^^^^^^^^^^^^^ help: try: `or_default()` +LL | btree_vec.entry(42).or_insert(Vec::new()); + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:125:21 + --> tests/ui/or_fun_call.rs:129:21 | LL | let _ = stringy.unwrap_or(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `ok_or` - --> tests/ui/or_fun_call.rs:130:17 + --> tests/ui/or_fun_call.rs:134:17 | LL | let _ = opt.ok_or(format!("{} world.", hello)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ok_or_else(|| format!("{} world.", hello))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:135:21 + --> tests/ui/or_fun_call.rs:139:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:138:21 + --> tests/ui/or_fun_call.rs:142:21 | LL | let _ = Some(1).unwrap_or(map[&1]); | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])` error: function call inside of `or` - --> tests/ui/or_fun_call.rs:163:35 + --> tests/ui/or_fun_call.rs:167:35 | LL | let _ = Some("a".to_string()).or(Some("b".to_string())); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_else(|| Some("b".to_string()))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:206:18 + --> tests/ui/or_fun_call.rs:210:18 | LL | None.unwrap_or(ptr_to_ref(s)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| ptr_to_ref(s))` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:214:14 + --> tests/ui/or_fun_call.rs:218:14 | LL | None.unwrap_or(unsafe { ptr_to_ref(s) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:217:14 + --> tests/ui/or_fun_call.rs:221:14 | LL | None.unwrap_or( unsafe { ptr_to_ref(s) } ); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:293:25 + --> tests/ui/or_fun_call.rs:297:25 | LL | let _ = Some(4).map_or(g(), |v| v); | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(g, |v| v)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:295:25 + --> tests/ui/or_fun_call.rs:299:25 | LL | let _ = Some(4).map_or(g(), f); | ^^^^^^^^^^^^^^ help: try: `map_or_else(g, f)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:298:25 + --> tests/ui/or_fun_call.rs:302:25 | LL | let _ = Some(4).map_or("asd".to_string().len() as i32, f); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| "asd".to_string().len() as i32, f)` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:329:18 + --> tests/ui/or_fun_call.rs:333:18 | LL | with_new.unwrap_or_else(Vec::new); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:333:28 + --> tests/ui/or_fun_call.rs:337:28 | LL | with_default_trait.unwrap_or_else(Default::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:337:27 + --> tests/ui/or_fun_call.rs:341:27 | LL | with_default_type.unwrap_or_else(u64::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:341:22 + --> tests/ui/or_fun_call.rs:345:22 | LL | real_default.unwrap_or_else(::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/or_fun_call.rs:345:23 + --> tests/ui/or_fun_call.rs:349:23 | LL | map.entry(42).or_insert_with(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/or_fun_call.rs:349:25 + --> tests/ui/or_fun_call.rs:353:25 | LL | btree.entry(42).or_insert_with(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/or_fun_call.rs:353:25 + --> tests/ui/or_fun_call.rs:357:25 | LL | let _ = stringy.unwrap_or_else(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:395:17 + --> tests/ui/or_fun_call.rs:399:17 | LL | let _ = opt.unwrap_or({ f() }); // suggest `.unwrap_or_else(f)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(f)` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:400:17 + --> tests/ui/or_fun_call.rs:404:17 | LL | let _ = opt.unwrap_or(f() + 1); // suggest `.unwrap_or_else(|| f() + 1)` | ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| f() + 1)` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:405:17 + --> tests/ui/or_fun_call.rs:409:17 | LL | let _ = opt.unwrap_or({ | _________________^ @@ -235,55 +235,55 @@ LL ~ }); | error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:411:17 + --> tests/ui/or_fun_call.rs:415:17 | LL | let _ = opt.map_or(f() + 1, |v| v); // suggest `.map_or_else(|| f() + 1, |v| v)` | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| f() + 1, |v| v)` error: use of `unwrap_or` to construct default value - --> tests/ui/or_fun_call.rs:416:17 + --> tests/ui/or_fun_call.rs:420:17 | LL | let _ = opt.unwrap_or({ i32::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: function call inside of `unwrap_or` - --> tests/ui/or_fun_call.rs:423:21 + --> tests/ui/or_fun_call.rs:427:21 | LL | let _ = opt_foo.unwrap_or(Foo { val: String::default() }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Foo { val: String::default() })` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:438:19 + --> tests/ui/or_fun_call.rs:442:19 | LL | let _ = x.map_or(g(), |v| v); | ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), |v| v)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:440:19 + --> tests/ui/or_fun_call.rs:444:19 | LL | let _ = x.map_or(g(), f); | ^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), f)` error: function call inside of `map_or` - --> tests/ui/or_fun_call.rs:443:19 + --> tests/ui/or_fun_call.rs:447:19 | LL | let _ = x.map_or("asd".to_string().len() as i32, f); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|_| "asd".to_string().len() as i32, f)` error: function call inside of `get_or_insert` - --> tests/ui/or_fun_call.rs:454:15 + --> tests/ui/or_fun_call.rs:458:15 | LL | let _ = x.get_or_insert(g()); | ^^^^^^^^^^^^^^^^^^ help: try: `get_or_insert_with(g)` error: function call inside of `and` - --> tests/ui/or_fun_call.rs:464:15 + --> tests/ui/or_fun_call.rs:468:15 | LL | let _ = x.and(g()); | ^^^^^^^^ help: try: `and_then(|_| g())` error: function call inside of `and` - --> tests/ui/or_fun_call.rs:474:15 + --> tests/ui/or_fun_call.rs:478:15 | LL | let _ = x.and(g()); | ^^^^^^^^ help: try: `and_then(|_| g())` From f4e19c68786211f3c3cf2593442629599678800a Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 11 Sep 2025 13:08:36 +0200 Subject: [PATCH 623/682] support calls on opaque types :< --- compiler/rustc_hir_analysis/src/autoderef.rs | 15 ++- compiler/rustc_hir_typeck/src/callee.rs | 26 +++- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 37 +++--- compiler/rustc_hir_typeck/src/method/mod.rs | 29 ++++- compiler/rustc_hir_typeck/src/op.rs | 15 ++- compiler/rustc_hir_typeck/src/place_op.rs | 28 ++++- compiler/rustc_infer/src/infer/context.rs | 3 + compiler/rustc_infer/src/infer/mod.rs | 54 ++++++++ .../src/solve/assembly/mod.rs | 40 ++++-- .../src/solve/eval_ctxt/canonical.rs | 25 ++-- .../src/solve/eval_ctxt/mod.rs | 76 +++++++----- .../rustc_next_trait_solver/src/solve/mod.rs | 35 +++--- .../src/solve/search_graph.rs | 2 +- .../src/solve/fulfill.rs | 4 +- .../src/solve/fulfill/derive_errors.rs | 22 ++-- .../src/solve/inspect/analyse.rs | 2 +- .../rustc_trait_selection/src/solve/select.rs | 8 +- .../src/traits/coherence.rs | 2 +- .../src/traits/query/evaluate_obligation.rs | 17 +++ compiler/rustc_type_ir/src/infer_ctxt.rs | 3 +- compiler/rustc_type_ir/src/solve/mod.rs | 80 +++++++++++- .../ambiguous-ops.current.stderr | 62 ++++++++++ .../non-defining-uses/ambiguous-ops.rs | 117 ++++++++++++++++++ .../function-call-on-infer.rs | 73 +++++++++++ .../impl-deref-function-call.rs | 56 +++++++++ .../shex_compat-regression-test.rs | 19 +++ .../return-block-type-inference-15965.stderr | 6 +- ...oxed-closures-failed-recursive-fn-2.stderr | 2 +- 28 files changed, 734 insertions(+), 124 deletions(-) create mode 100644 tests/ui/impl-trait/non-defining-uses/ambiguous-ops.current.stderr create mode 100644 tests/ui/impl-trait/non-defining-uses/ambiguous-ops.rs create mode 100644 tests/ui/impl-trait/non-defining-uses/function-call-on-infer.rs create mode 100644 tests/ui/impl-trait/non-defining-uses/impl-deref-function-call.rs create mode 100644 tests/ui/impl-trait/non-defining-uses/shex_compat-regression-test.rs diff --git a/compiler/rustc_hir_analysis/src/autoderef.rs b/compiler/rustc_hir_analysis/src/autoderef.rs index e8237471e1b6..88bd3339e4e1 100644 --- a/compiler/rustc_hir_analysis/src/autoderef.rs +++ b/compiler/rustc_hir_analysis/src/autoderef.rs @@ -68,7 +68,14 @@ impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> { return None; } - if self.state.cur_ty.is_ty_var() { + // We want to support method and function calls for `impl Deref`. + // + // To do so we don't eagerly bail if the current type is the hidden type of an + // opaque type and instead return `None` in `fn overloaded_deref_ty` if the + // opaque does not have a `Deref` item-bound. + if let &ty::Infer(ty::TyVar(vid)) = self.state.cur_ty.kind() + && !self.infcx.has_opaques_with_sub_unified_hidden_type(vid) + { return None; } @@ -160,7 +167,11 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { self.param_env, ty::Binder::dummy(trait_ref), ); - if !self.infcx.next_trait_solver() && !self.infcx.predicate_may_hold(&obligation) { + // We detect whether the self type implements `Deref` before trying to + // structurally normalize. We use `predicate_may_hold_opaque_types_jank` + // to support not-yet-defined opaque types. It will succeed for `impl Deref` + // but fail for `impl OtherTrait`. + if !self.infcx.predicate_may_hold_opaque_types_jank(&obligation) { debug!("overloaded_deref_ty: cannot match obligation"); return None; } diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index c6a4d78dcc83..f59fcab46661 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -25,6 +25,7 @@ use tracing::{debug, instrument}; use super::method::MethodCallee; use super::method::probe::ProbeScope; use super::{Expectation, FnCtxt, TupleArgumentsFlag}; +use crate::method::TreatNotYetDefinedOpaques; use crate::{errors, fluent_generated}; /// Checks that it is legal to call methods of the trait corresponding @@ -78,7 +79,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => self.check_expr(callee_expr), }; - let expr_ty = self.structurally_resolve_type(call_expr.span, original_callee_ty); + let expr_ty = self.try_structurally_resolve_type(call_expr.span, original_callee_ty); let mut autoderef = self.autoderef(callee_expr.span, expr_ty); let mut result = None; @@ -200,7 +201,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { arg_exprs: &'tcx [hir::Expr<'tcx>], autoderef: &Autoderef<'a, 'tcx>, ) -> Option> { - let adjusted_ty = self.structurally_resolve_type(autoderef.span(), autoderef.final_ty()); + let adjusted_ty = + self.try_structurally_resolve_type(autoderef.span(), autoderef.final_ty()); // If the callee is a function pointer or a closure, then we're all set. match *adjusted_ty.kind() { @@ -297,6 +299,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; } + ty::Infer(ty::TyVar(vid)) => { + // If we end up with an inference variable which is not the hidden type of + // an opaque, emit an error. + if !self.has_opaques_with_sub_unified_hidden_type(vid) { + self.type_must_be_known_at_this_point(autoderef.span(), adjusted_ty); + return None; + } + } + ty::Error(_) => { return None; } @@ -367,26 +378,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ty::new_tup_from_iter(self.tcx, arg_exprs.iter().map(|e| self.next_ty_var(e.span))) }); + // We use `TreatNotYetDefinedOpaques::AsRigid` here so that if the `adjusted_ty` + // is `Box` we choose `FnOnce` instead of `Fn`. + // + // We try all the different call traits in order and choose the first + // one which may apply. So if we treat opaques as inference variables + // `Box: Fn` is considered ambiguous and chosen. if let Some(ok) = self.lookup_method_for_operator( self.misc(call_expr.span), method_name, trait_def_id, adjusted_ty, opt_input_type, + TreatNotYetDefinedOpaques::AsRigid, ) { let method = self.register_infer_ok_obligations(ok); let mut autoref = None; if borrow { // Check for &self vs &mut self in the method signature. Since this is either // the Fn or FnMut trait, it should be one of those. - let ty::Ref(_, _, mutbl) = method.sig.inputs()[0].kind() else { + let ty::Ref(_, _, mutbl) = *method.sig.inputs()[0].kind() else { bug!("Expected `FnMut`/`Fn` to take receiver by-ref/by-mut") }; // For initial two-phase borrow // deployment, conservatively omit // overloaded function call ops. - let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::No); + let mutbl = AutoBorrowMutability::new(mutbl, AllowTwoPhase::No); autoref = Some(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)), diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 7370124e800d..3444523974af 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -1469,24 +1469,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(crate) fn structurally_resolve_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> { let ty = self.try_structurally_resolve_type(sp, ty); - if !ty.is_ty_var() { - ty - } else { - let e = self.tainted_by_errors().unwrap_or_else(|| { - self.err_ctxt() - .emit_inference_failure_err( - self.body_id, - sp, - ty.into(), - TypeAnnotationNeeded::E0282, - true, - ) - .emit() - }); - let err = Ty::new_error(self.tcx, e); - self.demand_suptype(sp, err, ty); - err - } + if !ty.is_ty_var() { ty } else { self.type_must_be_known_at_this_point(sp, ty) } + } + + #[cold] + pub(crate) fn type_must_be_known_at_this_point(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> { + let guar = self.tainted_by_errors().unwrap_or_else(|| { + self.err_ctxt() + .emit_inference_failure_err( + self.body_id, + sp, + ty.into(), + TypeAnnotationNeeded::E0282, + true, + ) + .emit() + }); + let err = Ty::new_error(self.tcx, guar); + self.demand_suptype(sp, err, ty); + err } pub(crate) fn structurally_resolve_const( diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index 652644ad78cc..04f112e4a39c 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -317,7 +317,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { )?; Ok(pick) } +} +/// Used by [FnCtxt::lookup_method_for_operator] with `-Znext-solver`. +/// +/// With `AsRigid` we error on `impl Opaque: NotInItemBounds` while +/// `AsInfer` just treats it as ambiguous and succeeds. This is necessary +/// as we want [FnCtxt::check_expr_call] to treat not-yet-defined opaque +/// types as rigid to support `impl Deref` and +/// `Box`. +/// +/// We only want to treat opaque types as rigid if we need to eagerly choose +/// between multiple candidates. We otherwise treat them as ordinary inference +/// variable to avoid rejecting otherwise correct code. +#[derive(Debug)] +pub(super) enum TreatNotYetDefinedOpaques { + AsInfer, + AsRigid, +} + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// `lookup_method_in_trait` is used for overloaded operators. /// It does a very narrow slice of what the normal probe/confirm path does. /// In particular, it doesn't really do any probing: it simply constructs @@ -331,6 +350,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { trait_def_id: DefId, self_ty: Ty<'tcx>, opt_rhs_ty: Option>, + treat_opaques: TreatNotYetDefinedOpaques, ) -> Option>> { // Construct a trait-reference `self_ty : Trait` let args = GenericArgs::for_item(self.tcx, trait_def_id, |param, _| match param.kind { @@ -360,7 +380,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); // Now we want to know if this can be matched - if !self.predicate_may_hold(&obligation) { + let matches_trait = match treat_opaques { + TreatNotYetDefinedOpaques::AsInfer => self.predicate_may_hold(&obligation), + TreatNotYetDefinedOpaques::AsRigid => { + self.predicate_may_hold_opaque_types_jank(&obligation) + } + }; + + if !matches_trait { debug!("--> Cannot match obligation"); // Cannot be matched, no such method resolution is possible. return None; diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 11defc3aa033..054435379434 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -21,6 +21,7 @@ use {rustc_ast as ast, rustc_hir as hir}; use super::FnCtxt; use super::method::MethodCallee; use crate::Expectation; +use crate::method::TreatNotYetDefinedOpaques; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Checks a `a = b` @@ -974,8 +975,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }, ); - let method = - self.lookup_method_for_operator(cause.clone(), opname, trait_did, lhs_ty, opt_rhs_ty); + // We don't consider any other candidates if this lookup fails + // so we can freely treat opaque types as inference variables here + // to allow more code to compile. + let treat_opaques = TreatNotYetDefinedOpaques::AsInfer; + let method = self.lookup_method_for_operator( + cause.clone(), + opname, + trait_did, + lhs_ty, + opt_rhs_ty, + treat_opaques, + ); match method { Some(ok) => { let method = self.register_infer_ok_obligations(ok); diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index 1125e9840804..a48db2cc855c 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -12,7 +12,7 @@ use rustc_span::{Span, sym}; use tracing::debug; use {rustc_ast as ast, rustc_hir as hir}; -use crate::method::MethodCallee; +use crate::method::{MethodCallee, TreatNotYetDefinedOpaques}; use crate::{FnCtxt, PlaceOp}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -210,7 +210,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; }; - self.lookup_method_for_operator(self.misc(span), imm_op, imm_tr, base_ty, opt_rhs_ty) + // FIXME(trait-system-refactor-initiative#231): we may want to treat + // opaque types as rigid here to support `impl Deref>`. + let treat_opaques = TreatNotYetDefinedOpaques::AsInfer; + self.lookup_method_for_operator( + self.misc(span), + imm_op, + imm_tr, + base_ty, + opt_rhs_ty, + treat_opaques, + ) } fn try_mutable_overloaded_place_op( @@ -230,7 +240,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; }; - self.lookup_method_for_operator(self.misc(span), mut_op, mut_tr, base_ty, opt_rhs_ty) + // We have to replace the operator with the mutable variant for the + // program to compile, so we don't really have a choice here and want + // to just try using `DerefMut` even if its not in the item bounds + // of the opaque. + let treat_opaques = TreatNotYetDefinedOpaques::AsInfer; + self.lookup_method_for_operator( + self.misc(span), + mut_op, + mut_tr, + base_ty, + opt_rhs_ty, + treat_opaques, + ) } /// Convert auto-derefs, indices, etc of an expression from `Deref` and `Index` diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index 14cc590720ac..5ffa7304efaf 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -302,6 +302,9 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { .map(|(k, h)| (k, h.ty)) .collect() } + fn opaques_with_sub_unified_hidden_type(&self, ty: ty::TyVid) -> Vec> { + self.opaques_with_sub_unified_hidden_type(ty) + } fn register_hidden_type_in_storage( &self, diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 9d3886aff1c1..c9fc124d3bf8 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1004,6 +1004,60 @@ impl<'tcx> InferCtxt<'tcx> { self.inner.borrow_mut().opaque_type_storage.iter_opaque_types().collect() } + pub fn has_opaques_with_sub_unified_hidden_type(&self, ty_vid: TyVid) -> bool { + if !self.next_trait_solver() { + return false; + } + + let ty_sub_vid = self.sub_unification_table_root_var(ty_vid); + let inner = &mut *self.inner.borrow_mut(); + let mut type_variables = inner.type_variable_storage.with_log(&mut inner.undo_log); + inner.opaque_type_storage.iter_opaque_types().any(|(_, hidden_ty)| { + if let ty::Infer(ty::TyVar(hidden_vid)) = *hidden_ty.ty.kind() { + let opaque_sub_vid = type_variables.sub_unification_table_root_var(hidden_vid); + if opaque_sub_vid == ty_sub_vid { + return true; + } + } + + false + }) + } + + /// Searches for an opaque type key whose hidden type is related to `ty_vid`. + /// + /// This only checks for a subtype relation, it does not require equality. + pub fn opaques_with_sub_unified_hidden_type(&self, ty_vid: TyVid) -> Vec> { + // Avoid accidentally allowing more code to compile with the old solver. + if !self.next_trait_solver() { + return vec![]; + } + + let ty_sub_vid = self.sub_unification_table_root_var(ty_vid); + let inner = &mut *self.inner.borrow_mut(); + // This is iffy, can't call `type_variables()` as we're already + // borrowing the `opaque_type_storage` here. + let mut type_variables = inner.type_variable_storage.with_log(&mut inner.undo_log); + inner + .opaque_type_storage + .iter_opaque_types() + .filter_map(|(key, hidden_ty)| { + if let ty::Infer(ty::TyVar(hidden_vid)) = *hidden_ty.ty.kind() { + let opaque_sub_vid = type_variables.sub_unification_table_root_var(hidden_vid); + if opaque_sub_vid == ty_sub_vid { + return Some(ty::AliasTy::new_from_args( + self.tcx, + key.def_id.into(), + key.args, + )); + } + } + + None + }) + .collect() + } + #[inline(always)] pub fn can_define_opaque_ty(&self, id: impl Into) -> bool { debug_assert!(!self.next_trait_solver()); diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index fb777496e31e..62dea547890b 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -23,7 +23,8 @@ use crate::delegate::SolverDelegate; use crate::solve::inspect::ProbeKind; use crate::solve::{ BuiltinImplSource, CandidateSource, CanonicalResponse, Certainty, EvalCtxt, Goal, GoalSource, - MaybeCause, NoSolution, ParamEnvSource, QueryResult, has_no_inference_or_external_constraints, + MaybeCause, NoSolution, OpaqueTypesJank, ParamEnvSource, QueryResult, + has_no_inference_or_external_constraints, }; enum AliasBoundKind { @@ -474,7 +475,7 @@ where // // cc trait-system-refactor-initiative#105 let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc); - let certainty = Certainty::Maybe(cause); + let certainty = Certainty::Maybe { cause, opaque_types_jank: OpaqueTypesJank::AllGood }; self.probe_trait_candidate(source) .enter(|this| this.evaluate_added_goals_and_make_canonical_response(certainty)) } @@ -974,11 +975,21 @@ where candidates: &mut Vec>, ) { let self_ty = goal.predicate.self_ty(); - // If the self type is sub unified with any opaque type, we - // also look at blanket impls for it. - let mut assemble_blanket_impls = false; - for alias_ty in self.opaques_with_sub_unified_hidden_type(self_ty) { - assemble_blanket_impls = true; + // We only use this hack during HIR typeck. + let opaque_types = match self.typing_mode() { + TypingMode::Analysis { .. } => self.opaques_with_sub_unified_hidden_type(self_ty), + TypingMode::Coherence + | TypingMode::Borrowck { .. } + | TypingMode::PostBorrowckAnalysis { .. } + | TypingMode::PostAnalysis => vec![], + }; + + if opaque_types.is_empty() { + candidates.extend(self.forced_ambiguity(MaybeCause::Ambiguity)); + return; + } + + for &alias_ty in &opaque_types { debug!("self ty is sub unified with {alias_ty:?}"); struct ReplaceOpaque { @@ -1028,10 +1039,11 @@ where } } - // We also need to consider blanket impls for not-yet-defined opaque types. + // If the self type is sub unified with any opaque type, we also look at blanket + // impls for it. // // See tests/ui/impl-trait/non-defining-uses/use-blanket-impl.rs for an example. - if assemble_blanket_impls && assemble_from.should_assemble_impl_candidates() { + if assemble_from.should_assemble_impl_candidates() { let cx = self.cx(); cx.for_each_blanket_impl(goal.predicate.trait_def_id(cx), |impl_def_id| { // For every `default impl`, there's always a non-default `impl` @@ -1062,7 +1074,15 @@ where } if candidates.is_empty() { - candidates.extend(self.forced_ambiguity(MaybeCause::Ambiguity)); + let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc); + let certainty = Certainty::Maybe { + cause: MaybeCause::Ambiguity, + opaque_types_jank: OpaqueTypesJank::ErrorIfRigidSelfTy, + }; + candidates + .extend(self.probe_trait_candidate(source).enter(|this| { + this.evaluate_added_goals_and_make_canonical_response(certainty) + })); } } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs index 169832ca5fbd..889588afe61f 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs @@ -15,6 +15,7 @@ use rustc_index::IndexVec; use rustc_type_ir::data_structures::HashSet; use rustc_type_ir::inherent::*; use rustc_type_ir::relate::solver_relating::RelateExt; +use rustc_type_ir::solve::OpaqueTypesJank; use rustc_type_ir::{ self as ty, Canonical, CanonicalVarKind, CanonicalVarValues, InferCtxtLike, Interner, TypeFoldable, @@ -141,8 +142,10 @@ where } }; - if let Certainty::Maybe(cause @ MaybeCause::Overflow { keep_constraints: false, .. }) = - certainty + if let Certainty::Maybe { + cause: cause @ MaybeCause::Overflow { keep_constraints: false, .. }, + opaque_types_jank, + } = certainty { // If we have overflow, it's probable that we're substituting a type // into itself infinitely and any partial substitutions in the query @@ -155,7 +158,7 @@ where // // Changing this to retain some constraints in the future // won't be a breaking change, so this is good enough for now. - return Ok(self.make_ambiguous_response_no_constraints(cause)); + return Ok(self.make_ambiguous_response_no_constraints(cause, opaque_types_jank)); } let external_constraints = @@ -199,10 +202,13 @@ where .count(); if num_non_region_vars > self.cx().recursion_limit() { debug!(?num_non_region_vars, "too many inference variables -> overflow"); - return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow { - suggest_increasing_limit: true, - keep_constraints: false, - })); + return Ok(self.make_ambiguous_response_no_constraints( + MaybeCause::Overflow { + suggest_increasing_limit: true, + keep_constraints: false, + }, + OpaqueTypesJank::AllGood, + )); } } } @@ -216,13 +222,14 @@ where /// ambiguity but return constrained variables to guide inference. pub(in crate::solve) fn make_ambiguous_response_no_constraints( &self, - maybe_cause: MaybeCause, + cause: MaybeCause, + opaque_types_jank: OpaqueTypesJank, ) -> CanonicalResponse { response_no_constraints_raw( self.cx(), self.max_input_universe, self.variables, - Certainty::Maybe(maybe_cause), + Certainty::Maybe { cause, opaque_types_jank }, ) } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 3e3a5246f3d7..5df7c92d8814 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -8,6 +8,7 @@ use rustc_type_ir::inherent::*; use rustc_type_ir::relate::Relate; use rustc_type_ir::relate::solver_relating::RelateExt; use rustc_type_ir::search_graph::{CandidateHeadUsages, PathKind}; +use rustc_type_ir::solve::OpaqueTypesJank; use rustc_type_ir::{ self as ty, CanonicalVarValues, InferCtxtLike, Interner, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, @@ -151,6 +152,15 @@ pub trait SolverDelegateEvalExt: SolverDelegate { stalled_on: Option>, ) -> Result, NoSolution>; + /// Checks whether evaluating `goal` may hold while treating not-yet-defined + /// opaque types as being kind of rigid. + /// + /// See the comment on [OpaqueTypesJank] for more details. + fn root_goal_may_hold_opaque_types_jank( + &self, + goal: Goal::Predicate>, + ) -> bool; + /// Check whether evaluating `goal` with a depth of `root_depth` may /// succeed. This only returns `false` if the goal is guaranteed to /// not hold. In case evaluation overflows and fails with ambiguity this @@ -193,6 +203,24 @@ where }) } + fn root_goal_may_hold_opaque_types_jank( + &self, + goal: Goal::Predicate>, + ) -> bool { + self.probe(|| { + EvalCtxt::enter_root(self, self.cx().recursion_limit(), I::Span::dummy(), |ecx| { + ecx.evaluate_goal(GoalSource::Misc, goal, None) + }) + .is_ok_and(|r| match r.certainty { + Certainty::Yes => true, + Certainty::Maybe { cause: _, opaque_types_jank } => match opaque_types_jank { + OpaqueTypesJank::AllGood => true, + OpaqueTypesJank::ErrorIfRigidSelfTy => false, + }, + }) + }) + } + fn root_goal_may_hold_with_depth( &self, root_depth: usize, @@ -407,8 +435,12 @@ where // If we have run this goal before, and it was stalled, check that any of the goal's // args have changed. Otherwise, we don't need to re-run the goal because it'll remain // stalled, since it'll canonicalize the same way and evaluation is pure. - if let Some(GoalStalledOn { num_opaques, ref stalled_vars, ref sub_roots, stalled_cause }) = - stalled_on + if let Some(GoalStalledOn { + num_opaques, + ref stalled_vars, + ref sub_roots, + stalled_certainty, + }) = stalled_on && !stalled_vars.iter().any(|value| self.delegate.is_changed_arg(*value)) && !sub_roots .iter() @@ -419,7 +451,7 @@ where NestedNormalizationGoals::empty(), GoalEvaluation { goal, - certainty: Certainty::Maybe(stalled_cause), + certainty: stalled_certainty, has_changed: HasChanged::No, stalled_on, }, @@ -468,7 +500,7 @@ where let stalled_on = match certainty { Certainty::Yes => None, - Certainty::Maybe(stalled_cause) => match has_changed { + Certainty::Maybe { .. } => match has_changed { // FIXME: We could recompute a *new* set of stalled variables by walking // through the orig values, resolving, and computing the root vars of anything // that is not resolved. Only when *these* have changed is it meaningful @@ -518,7 +550,7 @@ where .len(), stalled_vars, sub_roots, - stalled_cause, + stalled_certainty: certainty, }) } }, @@ -634,7 +666,7 @@ where if let Some(certainty) = self.delegate.compute_goal_fast_path(goal, self.origin_span) { match certainty { Certainty::Yes => {} - Certainty::Maybe(_) => { + Certainty::Maybe { .. } => { self.nested_goals.push((source, goal, None)); unchanged_certainty = unchanged_certainty.map(|c| c.and(certainty)); } @@ -710,7 +742,7 @@ where match certainty { Certainty::Yes => {} - Certainty::Maybe(_) => { + Certainty::Maybe { .. } => { self.nested_goals.push((source, with_resolved_vars, stalled_on)); unchanged_certainty = unchanged_certainty.map(|c| c.and(certainty)); } @@ -724,7 +756,7 @@ where match certainty { Certainty::Yes => {} - Certainty::Maybe(_) => { + Certainty::Maybe { .. } => { self.nested_goals.push((source, goal, stalled_on)); unchanged_certainty = unchanged_certainty.map(|c| c.and(certainty)); } @@ -1184,28 +1216,12 @@ where pub(crate) fn opaques_with_sub_unified_hidden_type( &self, self_ty: I::Ty, - ) -> impl Iterator> + use<'a, D, I> { - let delegate = self.delegate; - delegate - .clone_opaque_types_lookup_table() - .into_iter() - .chain(delegate.clone_duplicate_opaque_types()) - .filter_map(move |(key, hidden_ty)| { - if let ty::Infer(ty::TyVar(self_vid)) = self_ty.kind() { - if let ty::Infer(ty::TyVar(hidden_vid)) = hidden_ty.kind() { - if delegate.sub_unification_table_root_var(self_vid) - == delegate.sub_unification_table_root_var(hidden_vid) - { - return Some(ty::AliasTy::new_from_args( - delegate.cx(), - key.def_id.into(), - key.args, - )); - } - } - } - None - }) + ) -> Vec> { + if let ty::Infer(ty::TyVar(vid)) = self_ty.kind() { + self.delegate.opaques_with_sub_unified_hidden_type(vid) + } else { + vec![] + } } } diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index cd27c9c26c1f..fb900b592d13 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -158,9 +158,10 @@ where if self.may_use_unstable_feature(param_env, symbol) { self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } else { - self.evaluate_added_goals_and_make_canonical_response(Certainty::Maybe( - MaybeCause::Ambiguity, - )) + self.evaluate_added_goals_and_make_canonical_response(Certainty::Maybe { + cause: MaybeCause::Ambiguity, + opaque_types_jank: OpaqueTypesJank::AllGood, + }) } } @@ -278,18 +279,21 @@ where fn bail_with_ambiguity(&mut self, candidates: &[Candidate]) -> CanonicalResponse { debug_assert!(candidates.len() > 1); - let maybe_cause = - candidates.iter().fold(MaybeCause::Ambiguity, |maybe_cause, candidates| { - // Pull down the certainty of `Certainty::Yes` to ambiguity when combining + let (cause, opaque_types_jank) = candidates.iter().fold( + (MaybeCause::Ambiguity, OpaqueTypesJank::AllGood), + |(c, jank), candidates| { + // We pull down the certainty of `Certainty::Yes` to ambiguity when combining // these responses, b/c we're combining more than one response and this we // don't know which one applies. - let candidate = match candidates.result.value.certainty { - Certainty::Yes => MaybeCause::Ambiguity, - Certainty::Maybe(candidate) => candidate, - }; - maybe_cause.or(candidate) - }); - self.make_ambiguous_response_no_constraints(maybe_cause) + match candidates.result.value.certainty { + Certainty::Yes => (c, jank), + Certainty::Maybe { cause, opaque_types_jank } => { + (c.or(cause), jank.or(opaque_types_jank)) + } + } + }, + ); + self.make_ambiguous_response_no_constraints(cause, opaque_types_jank) } /// If we fail to merge responses we flounder and return overflow or ambiguity. @@ -427,6 +431,7 @@ pub struct GoalStalledOn { pub num_opaques: usize, pub stalled_vars: Vec, pub sub_roots: Vec, - /// The cause that will be returned on subsequent evaluations if this goal remains stalled. - pub stalled_cause: MaybeCause, + /// The certainty that will be returned on subsequent evaluations if this + /// goal remains stalled. + pub stalled_certainty: Certainty, } diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs index f0342e0523ff..289325d70550 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -93,7 +93,7 @@ where fn is_ambiguous_result(result: QueryResult) -> bool { result.is_ok_and(|response| { has_no_inference_or_external_constraints(response) - && matches!(response.value.certainty, Certainty::Maybe(_)) + && matches!(response.value.certainty, Certainty::Maybe { .. }) }) } diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 575e0472e0e3..bff4f6ce3fc6 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -204,7 +204,7 @@ where // // Only goals proven via the trait solver should be region dependent. Certainty::Yes => {} - Certainty::Maybe(_) => { + Certainty::Maybe { .. } => { self.obligations.register(obligation, None); } } @@ -258,7 +258,7 @@ where infcx.push_hir_typeck_potentially_region_dependent_goal(obligation); } } - Certainty::Maybe(_) => self.obligations.register(obligation, stalled_on), + Certainty::Maybe { .. } => self.obligations.register(obligation, stalled_on), } } diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs index e31d1052d16c..eef0ddcbf596 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -95,15 +95,17 @@ pub(super) fn fulfillment_error_for_stalled<'tcx>( root_obligation.cause.span, None, ) { - Ok(GoalEvaluation { certainty: Certainty::Maybe(MaybeCause::Ambiguity), .. }) => { - (FulfillmentErrorCode::Ambiguity { overflow: None }, true) - } + Ok(GoalEvaluation { + certainty: Certainty::Maybe { cause: MaybeCause::Ambiguity, .. }, + .. + }) => (FulfillmentErrorCode::Ambiguity { overflow: None }, true), Ok(GoalEvaluation { certainty: - Certainty::Maybe(MaybeCause::Overflow { - suggest_increasing_limit, - keep_constraints: _, - }), + Certainty::Maybe { + cause: + MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: _ }, + .. + }, .. }) => ( FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) }, @@ -266,7 +268,8 @@ impl<'tcx> BestObligation<'tcx> { ); // Skip nested goals that aren't the *reason* for our goal's failure. match (self.consider_ambiguities, nested_goal.result()) { - (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {} + (true, Ok(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. })) + | (false, Err(_)) => {} _ => continue, } @@ -407,7 +410,8 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { let tcx = goal.infcx().tcx; // Skip goals that aren't the *reason* for our goal's failure. match (self.consider_ambiguities, goal.result()) { - (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {} + (true, Ok(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. })) | (false, Err(_)) => { + } _ => return ControlFlow::Continue(()), } diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index 342d7121fc37..086a7a44786d 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -332,7 +332,7 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> { inspect::ProbeStep::MakeCanonicalResponse { shallow_certainty: c } => { assert_matches!( shallow_certainty.replace(c), - None | Some(Certainty::Maybe(MaybeCause::Ambiguity)) + None | Some(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. }) ); } inspect::ProbeStep::NestedProbe(ref probe) => { diff --git a/compiler/rustc_trait_selection/src/solve/select.rs b/compiler/rustc_trait_selection/src/solve/select.rs index fb1adc2fd2ac..8d01c880f8c5 100644 --- a/compiler/rustc_trait_selection/src/solve/select.rs +++ b/compiler/rustc_trait_selection/src/solve/select.rs @@ -62,7 +62,7 @@ impl<'tcx> inspect::ProofTreeVisitor<'tcx> for Select { // Don't winnow until `Certainty::Yes` -- we don't need to winnow until // codegen, and only on the good path. - if matches!(goal.result().unwrap(), Certainty::Maybe(..)) { + if matches!(goal.result().unwrap(), Certainty::Maybe { .. }) { return ControlFlow::Break(Ok(None)); } @@ -95,7 +95,7 @@ fn candidate_should_be_dropped_in_favor_of<'tcx>( ) -> bool { // Don't winnow until `Certainty::Yes` -- we don't need to winnow until // codegen, and only on the good path. - if matches!(other.result().unwrap(), Certainty::Maybe(..)) { + if matches!(other.result().unwrap(), Certainty::Maybe { .. }) { return false; } @@ -143,13 +143,13 @@ fn to_selection<'tcx>( span: Span, cand: inspect::InspectCandidate<'_, 'tcx>, ) -> Option> { - if let Certainty::Maybe(..) = cand.shallow_certainty() { + if let Certainty::Maybe { .. } = cand.shallow_certainty() { return None; } let nested = match cand.result().expect("expected positive result") { Certainty::Yes => thin_vec![], - Certainty::Maybe(_) => cand + Certainty::Maybe { .. } => cand .instantiate_nested_goals(span) .into_iter() .map(|nested| { diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 39afd77a8b68..8e8c7dd7c9d4 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -682,7 +682,7 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a, 'tcx> { // was irrelevant. match goal.result() { Ok(Certainty::Yes) | Err(NoSolution) => return, - Ok(Certainty::Maybe(_)) => {} + Ok(Certainty::Maybe { .. }) => {} } // For bound predicates we simply call `infcx.enter_forall` diff --git a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs index 9e1a2a3e7d28..ae731505abfa 100644 --- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs +++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs @@ -1,8 +1,11 @@ +use rustc_infer::traits::solve::Goal; use rustc_macros::extension; use rustc_middle::span_bug; +use rustc_next_trait_solver::solve::SolverDelegateEvalExt; use crate::infer::InferCtxt; use crate::infer::canonical::OriginalQueryValues; +use crate::solve::SolverDelegate; use crate::traits::{ EvaluationResult, ObligationCtxt, OverflowError, PredicateObligation, SelectionContext, }; @@ -15,6 +18,20 @@ impl<'tcx> InferCtxt<'tcx> { self.evaluate_obligation_no_overflow(obligation).may_apply() } + /// See the comment on [OpaqueTypesJank](crate::solve::OpaqueTypesJank) + /// for more details. + fn predicate_may_hold_opaque_types_jank(&self, obligation: &PredicateObligation<'tcx>) -> bool { + if self.next_trait_solver() { + <&SolverDelegate<'tcx>>::from(self).root_goal_may_hold_opaque_types_jank(Goal::new( + self.tcx, + obligation.param_env, + obligation.predicate, + )) + } else { + self.predicate_may_hold(obligation) + } + } + /// Evaluates whether the predicate can be satisfied in the given /// `ParamEnv`, and returns `false` if not certain. However, this is /// not entirely accurate if inference variables are involved. diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index 56962b4597b8..f743b84bce68 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -6,7 +6,7 @@ use crate::fold::TypeFoldable; use crate::inherent::*; use crate::relate::RelateResult; use crate::relate::combine::PredicateEmittingRelation; -use crate::{self as ty, Interner}; +use crate::{self as ty, Interner, TyVid}; /// The current typing mode of an inference context. We unfortunately have some /// slightly different typing rules depending on the current context. See the @@ -271,6 +271,7 @@ pub trait InferCtxtLike: Sized { &self, prev_entries: Self::OpaqueTypeStorageEntries, ) -> Vec<(ty::OpaqueTypeKey, ::Ty)>; + fn opaques_with_sub_unified_hidden_type(&self, ty: TyVid) -> Vec>; fn register_hidden_type_in_storage( &self, diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index b48a8f46ebef..1a1606d82685 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -269,11 +269,70 @@ impl NestedNormalizationGoals { #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] pub enum Certainty { Yes, - Maybe(MaybeCause), + Maybe { cause: MaybeCause, opaque_types_jank: OpaqueTypesJank }, +} + +/// Supporting not-yet-defined opaque types in HIR typeck is somewhat +/// challenging. Ideally we'd normalize them to a new inference variable +/// and just defer type inference which relies on the opaque until we've +/// constrained the hidden type. +/// +/// This doesn't work for method and function calls as we need to guide type +/// inference for the function arguments. We treat not-yet-defined opaque types +/// as if they were rigid instead in these places. +/// +/// When we encounter a `?hidden_type_of_opaque: Trait` goal, we use the +/// item bounds and blanket impls to guide inference by constraining other type +/// variables, see `EvalCtxt::try_assemble_bounds_via_registered_opaques`. We +/// always keep the certainty as `Maybe` so that we properly prove these goals +/// once the hidden type has been constrained. +/// +/// If we fail to prove the trait goal via item bounds or blanket impls, the +/// goal would have errored if the opaque type were rigid. In this case, we +/// set `OpaqueTypesJank::ErrorIfRigidSelfTy` in the [Certainty]. +/// +/// Places in HIR typeck where we want to treat not-yet-defined opaque types as if +/// they were kind of rigid then use `fn root_goal_may_hold_opaque_types_jank` which +/// returns `false` if the goal doesn't hold or if `OpaqueTypesJank::ErrorIfRigidSelfTy` +/// is set (i.e. proving it required relies on some `?hidden_ty: NotInItemBounds` goal). +/// +/// This is subtly different from actually treating not-yet-defined opaque types as +/// rigid, e.g. it allows constraining opaque types if they are not the self-type of +/// a goal. It is good enough for now and only matters for very rare type inference +/// edge cases. We can improve this later on if necessary. +#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] +pub enum OpaqueTypesJank { + AllGood, + ErrorIfRigidSelfTy, +} +impl OpaqueTypesJank { + fn and(self, other: OpaqueTypesJank) -> OpaqueTypesJank { + match (self, other) { + (OpaqueTypesJank::AllGood, OpaqueTypesJank::AllGood) => OpaqueTypesJank::AllGood, + (OpaqueTypesJank::ErrorIfRigidSelfTy, _) | (_, OpaqueTypesJank::ErrorIfRigidSelfTy) => { + OpaqueTypesJank::ErrorIfRigidSelfTy + } + } + } + + pub fn or(self, other: OpaqueTypesJank) -> OpaqueTypesJank { + match (self, other) { + (OpaqueTypesJank::ErrorIfRigidSelfTy, OpaqueTypesJank::ErrorIfRigidSelfTy) => { + OpaqueTypesJank::ErrorIfRigidSelfTy + } + (OpaqueTypesJank::AllGood, _) | (_, OpaqueTypesJank::AllGood) => { + OpaqueTypesJank::AllGood + } + } + } } impl Certainty { - pub const AMBIGUOUS: Certainty = Certainty::Maybe(MaybeCause::Ambiguity); + pub const AMBIGUOUS: Certainty = Certainty::Maybe { + cause: MaybeCause::Ambiguity, + opaque_types_jank: OpaqueTypesJank::AllGood, + }; /// Use this function to merge the certainty of multiple nested subgoals. /// @@ -290,14 +349,23 @@ impl Certainty { pub fn and(self, other: Certainty) -> Certainty { match (self, other) { (Certainty::Yes, Certainty::Yes) => Certainty::Yes, - (Certainty::Yes, Certainty::Maybe(_)) => other, - (Certainty::Maybe(_), Certainty::Yes) => self, - (Certainty::Maybe(a), Certainty::Maybe(b)) => Certainty::Maybe(a.and(b)), + (Certainty::Yes, Certainty::Maybe { .. }) => other, + (Certainty::Maybe { .. }, Certainty::Yes) => self, + ( + Certainty::Maybe { cause: a_cause, opaque_types_jank: a_jank }, + Certainty::Maybe { cause: b_cause, opaque_types_jank: b_jank }, + ) => Certainty::Maybe { + cause: a_cause.and(b_cause), + opaque_types_jank: a_jank.and(b_jank), + }, } } pub const fn overflow(suggest_increasing_limit: bool) -> Certainty { - Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: false }) + Certainty::Maybe { + cause: MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: false }, + opaque_types_jank: OpaqueTypesJank::AllGood, + } } } diff --git a/tests/ui/impl-trait/non-defining-uses/ambiguous-ops.current.stderr b/tests/ui/impl-trait/non-defining-uses/ambiguous-ops.current.stderr new file mode 100644 index 000000000000..c54c1bba028c --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/ambiguous-ops.current.stderr @@ -0,0 +1,62 @@ +error[E0369]: cannot add `{integer}` to `impl Sized` + --> $DIR/ambiguous-ops.rs:17:15 + | +LL | add() + 1 + | ----- ^ - {integer} + | | + | impl Sized + +error[E0368]: binary assignment operation `*=` cannot be applied to type `impl Sized` + --> $DIR/ambiguous-ops.rs:31:9 + | +LL | temp *= 2; + | ----^^^^^ + | | + | cannot use `*=` on type `impl Sized` + +error[E0614]: type `DerefWrapper` cannot be dereferenced + --> $DIR/ambiguous-ops.rs:57:22 + | +LL | let _rarw = &*explicit_deref(); + | ^^^^^^^^^^^^^^^^^ can't be dereferenced + +error[E0614]: type `DerefWrapper` cannot be dereferenced + --> $DIR/ambiguous-ops.rs:69:9 + | +LL | *explicit_deref_mut() = 1; + | ^^^^^^^^^^^^^^^^^^^^^ can't be dereferenced + +error[E0277]: the type `impl Sized` cannot be indexed by `_` + --> $DIR/ambiguous-ops.rs:94:18 + | +LL | let _y = explicit_index()[0]; + | ^^^^^^^^^^^^^^^^ `impl Sized` cannot be indexed by `_` + | + = help: the trait `Index<_>` is not implemented for `impl Sized` +note: required for `IndexWrapper` to implement `Index<_>` + --> $DIR/ambiguous-ops.rs:81:22 + | +LL | impl, U> Index for IndexWrapper { + | -------- ^^^^^^^^ ^^^^^^^^^^^^^^^ + | | + | unsatisfied trait bound introduced here + +error[E0277]: the type `impl Sized` cannot be indexed by `_` + --> $DIR/ambiguous-ops.rs:106:9 + | +LL | explicit_index_mut()[0] = 1; + | ^^^^^^^^^^^^^^^^^^^^ `impl Sized` cannot be indexed by `_` + | + = help: the trait `Index<_>` is not implemented for `impl Sized` +note: required for `IndexWrapper` to implement `Index<_>` + --> $DIR/ambiguous-ops.rs:81:22 + | +LL | impl, U> Index for IndexWrapper { + | -------- ^^^^^^^^ ^^^^^^^^^^^^^^^ + | | + | unsatisfied trait bound introduced here + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0277, E0368, E0369, E0614. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/impl-trait/non-defining-uses/ambiguous-ops.rs b/tests/ui/impl-trait/non-defining-uses/ambiguous-ops.rs new file mode 100644 index 000000000000..0aa5715339dc --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/ambiguous-ops.rs @@ -0,0 +1,117 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] check-pass + +// Make sure we support non-call operations for opaque types even if +// its not part of its item bounds. + +use std::ops::{Deref, DerefMut, Index, IndexMut}; + +fn mk() -> T { + todo!() +} + +fn add() -> impl Sized { + let unconstrained = if false { + add() + 1 + //[current]~^ ERROR cannot add `{integer}` to `impl Sized + } else { + let with_infer = mk(); + let _ = with_infer + 1; + with_infer + }; + let _: u32 = unconstrained; + 1u32 +} + +fn mul_assign() -> impl Sized { + if false { + let mut temp = mul_assign(); + temp *= 2; + //[current]~^ ERROR binary assignment operation `*=` cannot be applied to type `impl Sized` + } + + let mut with_infer = mk(); + with_infer *= 2; + let _: u32 = with_infer; + + 1u32 +} + +struct DerefWrapper(T); +impl Deref for DerefWrapper { + type Target = T::Target; + fn deref(&self) -> &Self::Target { + &*self.0 + } +} +impl DerefMut for DerefWrapper { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut *self.0 + } +} + +fn explicit_deref() -> DerefWrapper { + if false { + let _rarw = &*explicit_deref(); + //[current]~^ ERROR type `DerefWrapper` cannot be dereferenced + + let mut with_infer = DerefWrapper(mk()); + let _rarw = &*with_infer; + with_infer + } else { + DerefWrapper(&1u32) + } +} +fn explicit_deref_mut() -> DerefWrapper { + if false { + *explicit_deref_mut() = 1; + //[current]~^ ERROR type `DerefWrapper` cannot be dereferenced + + let mut with_infer = DerefWrapper(Default::default()); + *with_infer = 1; + with_infer + } else { + DerefWrapper(Box::new(1u32)) + } +} + +struct IndexWrapper(T); +impl, U> Index for IndexWrapper { + type Output = T::Output; + fn index(&self, index: U) -> &Self::Output { + &self.0[index] + } +} +impl, U> IndexMut for IndexWrapper { + fn index_mut(&mut self, index: U) -> &mut Self::Output { + &mut self.0[index] + } +} +fn explicit_index() -> IndexWrapper { + if false { + let _y = explicit_index()[0]; + //[current]~^ ERROR the type `impl Sized` cannot be indexed by `_` + + let with_infer = IndexWrapper(Default::default()); + let _y = with_infer[0]; + with_infer + } else { + IndexWrapper([1u32]) + } +} +fn explicit_index_mut() -> IndexWrapper { + if false { + explicit_index_mut()[0] = 1; + //[current]~^ ERROR the type `impl Sized` cannot be indexed by `_` + + let mut with_infer = IndexWrapper(Default::default()); + with_infer[0] = 1; + with_infer + } else { + IndexWrapper([1u32]) + } +} + +fn main() {} diff --git a/tests/ui/impl-trait/non-defining-uses/function-call-on-infer.rs b/tests/ui/impl-trait/non-defining-uses/function-call-on-infer.rs new file mode 100644 index 000000000000..9b9156ee4c71 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/function-call-on-infer.rs @@ -0,0 +1,73 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass + +// Regression test for trait-system-refactor-initiative#181. Make sure calling +// opaque types works. + +fn fn_trait() -> impl Fn() { + if false { + let f = fn_trait(); + f(); + } + + || () +} + +fn fn_trait_ref() -> impl Fn() { + if false { + let f = &fn_trait(); + f(); + } + || () +} + +fn fn_mut() -> impl FnMut() -> usize { + if false { + let mut f = fn_mut(); + f(); + } + + let mut state = 0; + move || { + state += 1; + state + } +} + +fn fn_mut_ref() -> impl FnMut() -> usize { + if false { + let mut f = &mut fn_mut(); + f(); + } + + let mut state = 0; + move || { + state += 1; + state + } +} + + +fn fn_once() -> impl FnOnce() { + if false { + let mut f = fn_once(); + f(); + } + + let string = String::new(); + move || drop(string) +} + +fn fn_once_ref() -> impl FnOnce() { + if false { + let mut f = Box::new(fn_once_ref()); + f(); + } + + let string = String::new(); + move || drop(string) +} + +fn main() {} diff --git a/tests/ui/impl-trait/non-defining-uses/impl-deref-function-call.rs b/tests/ui/impl-trait/non-defining-uses/impl-deref-function-call.rs new file mode 100644 index 000000000000..5ff0dae55cc3 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/impl-deref-function-call.rs @@ -0,0 +1,56 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass + +// Regression test for trait-system-refactor-initiative#181. We want to +// be able to step through `impl Deref` in its defining scope. +use std::ops::{Deref, DerefMut}; +fn impl_deref_fn() -> impl Deref usize)> { + if false { + let func = impl_deref_fn(); + func(|s| s.len()); + } + + &((|_| ()) as fn(_)) +} + +fn impl_deref_impl_fn() -> impl Deref { + if false { + let func = impl_deref_impl_fn(); + func(); + } + + &|| () +} + +fn impl_deref_impl_deref_impl_fn() -> impl Deref> { + if false { + let func = impl_deref_impl_deref_impl_fn(); + func(); + } + + &&|| () +} + + +fn impl_deref_mut_impl_fn() -> impl DerefMut { + if false { + let func = impl_deref_impl_fn(); + func(); + } + + Box::new(|| ()) +} + + +fn impl_deref_mut_impl_fn_mut() -> impl DerefMut { + if false { + let mut func = impl_deref_mut_impl_fn_mut(); + func(); + } + + let mut state = 0; + Box::new(move || state += 1) +} +fn main() {} diff --git a/tests/ui/impl-trait/non-defining-uses/shex_compat-regression-test.rs b/tests/ui/impl-trait/non-defining-uses/shex_compat-regression-test.rs new file mode 100644 index 000000000000..aa1b51d6906e --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/shex_compat-regression-test.rs @@ -0,0 +1,19 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass + +// Regression test for trait-system-refactor-initiative#181. + +struct ShExCompactPrinter; + +struct TripleExpr; + +impl ShExCompactPrinter { + fn pp_triple_expr(&self) -> impl Fn(&TripleExpr, &ShExCompactPrinter) + '_ { + move |te, printer| { + printer.pp_triple_expr()(te, printer); + } + } +} +fn main() {} diff --git a/tests/ui/inference/return-block-type-inference-15965.stderr b/tests/ui/inference/return-block-type-inference-15965.stderr index fc4f2defe7ff..bfee90419220 100644 --- a/tests/ui/inference/return-block-type-inference-15965.stderr +++ b/tests/ui/inference/return-block-type-inference-15965.stderr @@ -1,10 +1,8 @@ error[E0282]: type annotations needed --> $DIR/return-block-type-inference-15965.rs:5:9 | -LL | / { return () } -LL | | -LL | | () - | |______^ cannot infer type +LL | { return () } + | ^^^^^^^^^^^^^ cannot infer type error: aborting due to 1 previous error diff --git a/tests/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-2.stderr b/tests/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-2.stderr index 058dbb1e220f..739182e120b4 100644 --- a/tests/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-2.stderr +++ b/tests/ui/unboxed-closures/unboxed-closures-failed-recursive-fn-2.stderr @@ -5,7 +5,7 @@ LL | let mut closure0 = None; | ^^^^^^^^^^^^ ... LL | return c(); - | --- type must be known at this point + | - type must be known at this point | help: consider giving `closure0` an explicit type, where the placeholders `_` are specified | From 9913c47da2b616fee57f308071d6adc39bff4568 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 15 Sep 2025 14:39:01 +0200 Subject: [PATCH 624/682] add tests, silence type annotations needed errors for opaques --- compiler/rustc_hir_typeck/src/opaque_types.rs | 34 ++++++----- ...ncorrect-choice-diagnostics.current.stderr | 53 +++++++++++++++++ ...r-incorrect-choice-diagnostics.next.stderr | 57 +++++++++++++++++++ .../call-expr-incorrect-choice-diagnostics.rs | 52 +++++++++++++++++ .../deref-constrains-self-ty.current.stderr | 16 ++++++ .../deref-constrains-self-ty.rs | 28 +++++++++ ...uble-wrap-with-defining-use.current.stderr | 4 +- .../double-wrap-with-defining-use.rs | 1 - .../ice-issue-146191.current.stderr | 2 +- .../ice-issue-146191.next.stderr | 10 +--- .../non-defining-uses/ice-issue-146191.rs | 1 - .../two_tait_defining_each_other2.next.stderr | 8 +-- .../two_tait_defining_each_other2.rs | 3 +- 13 files changed, 231 insertions(+), 38 deletions(-) create mode 100644 tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.current.stderr create mode 100644 tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.next.stderr create mode 100644 tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.rs create mode 100644 tests/ui/impl-trait/non-defining-uses/deref-constrains-self-ty.current.stderr create mode 100644 tests/ui/impl-trait/non-defining-uses/deref-constrains-self-ty.rs diff --git a/compiler/rustc_hir_typeck/src/opaque_types.rs b/compiler/rustc_hir_typeck/src/opaque_types.rs index 97feac3d0099..5cefa506b5a0 100644 --- a/compiler/rustc_hir_typeck/src/opaque_types.rs +++ b/compiler/rustc_hir_typeck/src/opaque_types.rs @@ -117,21 +117,25 @@ impl<'tcx> FnCtxt<'_, 'tcx> { ) } UsageKind::UnconstrainedHiddenType(hidden_type) => { - let infer_var = hidden_type - .ty - .walk() - .filter_map(ty::GenericArg::as_term) - .find(|term| term.is_infer()) - .unwrap_or_else(|| hidden_type.ty.into()); - self.err_ctxt() - .emit_inference_failure_err( - self.body_id, - hidden_type.span, - infer_var, - TypeAnnotationNeeded::E0282, - false, - ) - .emit() + if let Some(guar) = self.tainted_by_errors() { + guar + } else { + let infer_var = hidden_type + .ty + .walk() + .filter_map(ty::GenericArg::as_term) + .find(|term| term.is_infer()) + .unwrap_or_else(|| hidden_type.ty.into()); + self.err_ctxt() + .emit_inference_failure_err( + self.body_id, + hidden_type.span, + infer_var, + TypeAnnotationNeeded::E0282, + false, + ) + .emit() + } } UsageKind::HasDefiningUse => continue, }; diff --git a/tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.current.stderr b/tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.current.stderr new file mode 100644 index 000000000000..e213dab5d961 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.current.stderr @@ -0,0 +1,53 @@ +error[E0382]: use of moved value: `var` + --> $DIR/call-expr-incorrect-choice-diagnostics.rs:14:9 + | +LL | let mut var = item_bound_is_too_weak(); + | ------- move occurs because `var` has type `impl FnOnce()`, which does not implement the `Copy` trait +LL | var(); + | ----- `var` moved due to this call +LL | var(); + | ^^^ value used here after move + | +note: this value implements `FnOnce`, which causes it to be moved when called + --> $DIR/call-expr-incorrect-choice-diagnostics.rs:13:9 + | +LL | var(); + | ^^^ + +error[E0618]: expected function, found `impl Sized` + --> $DIR/call-expr-incorrect-choice-diagnostics.rs:24:9 + | +LL | fn opaque_type_no_impl_fn() -> impl Sized { + | ----------------------------------------- `opaque_type_no_impl_fn` defined here returns `impl Sized` +LL | if false { +LL | opaque_type_no_impl_fn()(); + | ^^^^^^^^^^^^^^^^^^^^^^^^-- + | | + | call expression requires function + +error[E0618]: expected function, found `impl Sized` + --> $DIR/call-expr-incorrect-choice-diagnostics.rs:34:9 + | +LL | fn opaque_type_no_impl_fn_incorrect() -> impl Sized { + | --------------------------------------------------- `opaque_type_no_impl_fn_incorrect` defined here returns `impl Sized` +LL | if false { +LL | opaque_type_no_impl_fn_incorrect()(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-- + | | + | call expression requires function + +error[E0618]: expected function, found `impl Deref` + --> $DIR/call-expr-incorrect-choice-diagnostics.rs:44:9 + | +LL | fn opaque_type_deref_no_impl_fn() -> impl Deref { + | -------------------------------------------------------------------- `opaque_type_deref_no_impl_fn` defined here returns `impl Deref` +LL | if false { +LL | opaque_type_deref_no_impl_fn()(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-- + | | + | call expression requires function + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0382, E0618. +For more information about an error, try `rustc --explain E0382`. diff --git a/tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.next.stderr b/tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.next.stderr new file mode 100644 index 000000000000..5678349cad32 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.next.stderr @@ -0,0 +1,57 @@ +error[E0382]: use of moved value: `var` + --> $DIR/call-expr-incorrect-choice-diagnostics.rs:14:9 + | +LL | let mut var = item_bound_is_too_weak(); + | ------- move occurs because `var` has type `{closure@$DIR/call-expr-incorrect-choice-diagnostics.rs:19:5: 19:12}`, which does not implement the `Copy` trait +LL | var(); + | ----- `var` moved due to this call +LL | var(); + | ^^^ value used here after move + | +note: this value implements `FnOnce`, which causes it to be moved when called + --> $DIR/call-expr-incorrect-choice-diagnostics.rs:13:9 + | +LL | var(); + | ^^^ +help: consider cloning the value if the performance cost is acceptable + | +LL | var.clone()(); + | ++++++++ + +error[E0618]: expected function, found `_` + --> $DIR/call-expr-incorrect-choice-diagnostics.rs:24:9 + | +LL | fn opaque_type_no_impl_fn() -> impl Sized { + | ----------------------------------------- `opaque_type_no_impl_fn` defined here returns `_` +LL | if false { +LL | opaque_type_no_impl_fn()(); + | ^^^^^^^^^^^^^^^^^^^^^^^^-- + | | + | call expression requires function + +error[E0618]: expected function, found `_` + --> $DIR/call-expr-incorrect-choice-diagnostics.rs:34:9 + | +LL | fn opaque_type_no_impl_fn_incorrect() -> impl Sized { + | --------------------------------------------------- `opaque_type_no_impl_fn_incorrect` defined here returns `_` +LL | if false { +LL | opaque_type_no_impl_fn_incorrect()(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-- + | | + | call expression requires function + +error[E0618]: expected function, found `_` + --> $DIR/call-expr-incorrect-choice-diagnostics.rs:44:9 + | +LL | fn opaque_type_deref_no_impl_fn() -> impl Deref { + | -------------------------------------------------------------------- `opaque_type_deref_no_impl_fn` defined here returns `_` +LL | if false { +LL | opaque_type_deref_no_impl_fn()(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-- + | | + | call expression requires function + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0382, E0618. +For more information about an error, try `rustc --explain E0382`. diff --git a/tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.rs b/tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.rs new file mode 100644 index 000000000000..1d73985f78a1 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/call-expr-incorrect-choice-diagnostics.rs @@ -0,0 +1,52 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) + +// Testing the errors in case we've made a wrong choice when +// calling an opaque. + +use std::ops::Deref; + +fn item_bound_is_too_weak() -> impl FnOnce() { + if false { + let mut var = item_bound_is_too_weak(); + var(); + var(); + //~^ ERROR use of moved value: `var` + } + + let mut state = String::new(); + move || state.push('a') +} + +fn opaque_type_no_impl_fn() -> impl Sized { + if false { + opaque_type_no_impl_fn()(); + //[current]~^ ERROR expected function, found `impl Sized` + //[next]~^^ ERROR expected function, found `_` + } + + 1 +} + +fn opaque_type_no_impl_fn_incorrect() -> impl Sized { + if false { + opaque_type_no_impl_fn_incorrect()(); + //[current]~^ ERROR expected function, found `impl Sized` + //[next]~^^ ERROR expected function, found `_` + } + + || () +} + +fn opaque_type_deref_no_impl_fn() -> impl Deref { + if false { + opaque_type_deref_no_impl_fn()(); + //[current]~^ ERROR expected function, found `impl Deref` + //[next]~^^ ERROR expected function, found `_` + } + + &1 +} + +fn main() {} diff --git a/tests/ui/impl-trait/non-defining-uses/deref-constrains-self-ty.current.stderr b/tests/ui/impl-trait/non-defining-uses/deref-constrains-self-ty.current.stderr new file mode 100644 index 000000000000..bbe90e5873d0 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/deref-constrains-self-ty.current.stderr @@ -0,0 +1,16 @@ +error[E0599]: no method named `len` found for struct `Wrapper` in the current scope + --> $DIR/deref-constrains-self-ty.rs:22:32 + | +LL | struct Wrapper(T); + | ----------------- method `len` not found for this struct +... +LL | let _ = Wrapper(foo()).len(); + | ^^^ method not found in `Wrapper` + | + = help: items from traits can only be used if the trait is implemented and in scope + = note: the following trait defines an item `len`, perhaps you need to implement it: + candidate #1: `ExactSizeIterator` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/impl-trait/non-defining-uses/deref-constrains-self-ty.rs b/tests/ui/impl-trait/non-defining-uses/deref-constrains-self-ty.rs new file mode 100644 index 000000000000..d143878bc746 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/deref-constrains-self-ty.rs @@ -0,0 +1,28 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] check-pass + +// A test which shows that autoderef can constrain opaque types even +// though it's supposed to treat not-yet-defined opaque types as +// mostly rigid. I don't think this should necessarily compile :shrug: +use std::ops::Deref; + +struct Wrapper(T); + +impl Deref for Wrapper> { + type Target = Vec; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +fn foo() -> impl Sized { + if false { + let _ = Wrapper(foo()).len(); + //[current]~^ ERROR no method named `len` found for struct `Wrapper` in the current scope + } + + std::iter::once(1).collect() +} +fn main() {} diff --git a/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.current.stderr b/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.current.stderr index 30424ec58f91..bac5b3e0cf44 100644 --- a/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.current.stderr +++ b/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.current.stderr @@ -1,5 +1,5 @@ error[E0792]: expected generic type parameter, found `impl Foo` - --> $DIR/double-wrap-with-defining-use.rs:12:26 + --> $DIR/double-wrap-with-defining-use.rs:11:26 | LL | fn a(x: T) -> impl Foo { | - this generic parameter must be used with a generic type parameter @@ -7,7 +7,7 @@ LL | if true { x } else { a(a(x)) } | ^^^^^^^ error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias - --> $DIR/double-wrap-with-defining-use.rs:12:26 + --> $DIR/double-wrap-with-defining-use.rs:11:26 | LL | if true { x } else { a(a(x)) } | ^^^^^^^ diff --git a/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.rs b/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.rs index 734b1920772f..39b327eff18e 100644 --- a/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.rs +++ b/tests/ui/impl-trait/non-defining-uses/double-wrap-with-defining-use.rs @@ -1,7 +1,6 @@ // Regression test for ICE from issue #140545 // The error message is confusing and wrong, but that's a different problem (#139350) -//@ edition:2018 //@ revisions: current next //@[next] compile-flags: -Znext-solver //@ ignore-compare-mode-next-solver (explicit revisions) diff --git a/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.current.stderr b/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.current.stderr index ccbe2d3593c6..5dc66f454652 100644 --- a/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.current.stderr +++ b/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.current.stderr @@ -5,7 +5,7 @@ LL | fn create_complex_future() -> impl Future { | ^^^^^^^^^^^^^^^^ the trait `ReturnsSend` is not implemented for `()` | help: this trait has no implementations, consider adding one - --> $DIR/ice-issue-146191.rs:14:1 + --> $DIR/ice-issue-146191.rs:13:1 | LL | trait ReturnsSend {} | ^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.next.stderr b/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.next.stderr index e8b551c65fc0..4a88359ca967 100644 --- a/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.next.stderr +++ b/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.next.stderr @@ -4,14 +4,6 @@ error[E0282]: type annotations needed LL | async { create_complex_future().await } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type -error[E0282]: type annotations needed - --> $DIR/ice-issue-146191.rs:8:5 - | -LL | async { create_complex_future().await } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.rs b/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.rs index 356f7d01eb9b..84f139da4e32 100644 --- a/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.rs +++ b/tests/ui/impl-trait/non-defining-uses/ice-issue-146191.rs @@ -8,7 +8,6 @@ fn create_complex_future() -> impl Future { async { create_complex_future().await } //[current]~^ ERROR recursion in an async block requires //[next]~^^ ERROR type annotations needed - //[next]~| ERROR type annotations needed } trait ReturnsSend {} diff --git a/tests/ui/impl-trait/two_tait_defining_each_other2.next.stderr b/tests/ui/impl-trait/two_tait_defining_each_other2.next.stderr index 785e5fdeb643..9b18a9715f21 100644 --- a/tests/ui/impl-trait/two_tait_defining_each_other2.next.stderr +++ b/tests/ui/impl-trait/two_tait_defining_each_other2.next.stderr @@ -4,12 +4,6 @@ error[E0282]: type annotations needed LL | fn muh(x: A) -> B { | ^ cannot infer type -error[E0282]: type annotations needed - --> $DIR/two_tait_defining_each_other2.rs:14:5 - | -LL | x // B's hidden type is A (opaquely) - | ^ cannot infer type - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/impl-trait/two_tait_defining_each_other2.rs b/tests/ui/impl-trait/two_tait_defining_each_other2.rs index 99262f4bc4b3..ec2963249f9d 100644 --- a/tests/ui/impl-trait/two_tait_defining_each_other2.rs +++ b/tests/ui/impl-trait/two_tait_defining_each_other2.rs @@ -12,8 +12,7 @@ trait Foo {} fn muh(x: A) -> B { //[next]~^ ERROR: type annotations needed x // B's hidden type is A (opaquely) - //[next]~^ ERROR: type annotations needed - //[current]~^^ ERROR opaque type's hidden type cannot be another opaque type + //[current]~^ ERROR opaque type's hidden type cannot be another opaque type } struct Bar; From 92646739fe52bc4c40756db71f78fe83b666847e Mon Sep 17 00:00:00 2001 From: aklaiber Date: Tue, 16 Sep 2025 06:36:22 +0200 Subject: [PATCH 625/682] Add regression test for issue 91831 --- .../ex3-both-anon-regions-one-is-struct-5.rs | 13 ++++++++++++ ...3-both-anon-regions-one-is-struct-5.stderr | 20 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-5.rs create mode 100644 tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-5.stderr diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-5.rs b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-5.rs new file mode 100644 index 000000000000..16039f177b4d --- /dev/null +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-5.rs @@ -0,0 +1,13 @@ +// Regression test for #91831 + +struct Foo<'a>(&'a i32); + +impl<'a> Foo<'a> { + fn modify(&'a mut self) {} +} + +fn bar(foo: &mut Foo) { + foo.modify(); //~ ERROR lifetime may not live long enough +} + +fn main() {} diff --git a/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-5.stderr b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-5.stderr new file mode 100644 index 000000000000..f02b65230b6e --- /dev/null +++ b/tests/ui/lifetimes/lifetime-errors/ex3-both-anon-regions-one-is-struct-5.stderr @@ -0,0 +1,20 @@ +error: lifetime may not live long enough + --> $DIR/ex3-both-anon-regions-one-is-struct-5.rs:10:5 + | +LL | fn bar(foo: &mut Foo) { + | --- - let's call the lifetime of this reference `'1` + | | + | has type `&mut Foo<'2>` +LL | foo.modify(); + | ^^^^^^^^^^^^ argument requires that `'1` must outlive `'2` + | + = note: requirement occurs because of a mutable reference to `Foo<'_>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance +help: consider introducing a named lifetime parameter + | +LL | fn bar<'a>(foo: &'a mut Foo<'a>) { + | ++++ ++ ++++ + +error: aborting due to 1 previous error + From 2ed53732932716647b72f5f3e32a06da8634d6e2 Mon Sep 17 00:00:00 2001 From: Amanda Stjerna Date: Thu, 18 Sep 2025 13:11:37 +0200 Subject: [PATCH 626/682] Clean up universe evaluation during type test evaluation The logic was, as the removed comments suggest, hackish and meant to implement previous logic that was factored out. The new logic does exactly what the comments say, and is much less surprising. --- .../rustc_borrowck/src/handle_placeholders.rs | 10 +++------- .../rustc_borrowck/src/region_infer/mod.rs | 20 ++++++++++++++----- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_borrowck/src/handle_placeholders.rs b/compiler/rustc_borrowck/src/handle_placeholders.rs index 94379cdebf73..6be90994015f 100644 --- a/compiler/rustc_borrowck/src/handle_placeholders.rs +++ b/compiler/rustc_borrowck/src/handle_placeholders.rs @@ -166,13 +166,9 @@ impl RegionTracker { } } - /// Determine if the tracked universes of the two SCCs are compatible. - pub(crate) fn universe_compatible_with(&self, other: Self) -> bool { - // HACK: We first check whether we can name the highest existential universe - // of `other`. This only exists to avoid errors in case that scc already - // depends on a placeholder it cannot name itself. - self.max_nameable_universe().can_name(other.max_nameable_universe()) - || other.reachable_placeholders.can_be_named_by(self.max_nameable_universe()) + /// Determine if we can name all the placeholders in `other`. + pub(crate) fn can_name_all_placeholders(&self, other: Self) -> bool { + other.reachable_placeholders.can_be_named_by(self.max_nameable_universe.0) } /// If this SCC reaches a placeholder it can't name, return it. diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index f57456949bb5..5f4bfd9df48e 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -571,11 +571,15 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } - /// Returns `true` if all the elements in the value of `scc_b` are nameable + /// Returns `true` if all the placeholders in the value of `scc_b` are nameable /// 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 { - self.scc_annotations[scc_a].universe_compatible_with(self.scc_annotations[scc_b]) + fn can_name_all_placeholders( + &self, + scc_a: ConstraintSccIndex, + scc_b: ConstraintSccIndex, + ) -> bool { + self.scc_annotations[scc_a].can_name_all_placeholders(self.scc_annotations[scc_b]) } /// Once regions have been propagated, this method is used to see @@ -964,16 +968,22 @@ impl<'tcx> RegionInferenceContext<'tcx> { return true; } + let fr_static = self.universal_regions().fr_static; + // If we are checking that `'sup: 'sub`, and `'sub` contains // some placeholder that `'sup` cannot name, then this is only // true if `'sup` outlives static. - if !self.universe_compatible(sub_region_scc, sup_region_scc) { + // + // Avoid infinite recursion if `sub_region` is already `'static` + if sub_region != fr_static + && !self.can_name_all_placeholders(sup_region_scc, sub_region_scc) + { debug!( "sub universe `{sub_region_scc:?}` is not nameable \ by super `{sup_region_scc:?}`, promoting to static", ); - return self.eval_outlives(sup_region, self.universal_regions().fr_static); + return self.eval_outlives(sup_region, fr_static); } // Both the `sub_region` and `sup_region` consist of the union From 495d7ee587dc1b8d99fd9f0bce2f72b0072e3aca Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Thu, 18 Sep 2025 09:41:23 -0400 Subject: [PATCH 627/682] Include patch in release notes This should fix triagebot publication of the GitHub release on merge. --- RELEASES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 2b65d070d5fd..aa5f37fffebf 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,5 +1,5 @@ -Version 1.90 (2025-09-18) -========================== +Version 1.90.0 (2025-09-18) +=========================== From 7e270ab27e80c06275c80fa1041f5ff9af3ffcd4 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Thu, 18 Sep 2025 22:22:29 +0900 Subject: [PATCH 628/682] chore: Update rustc deps --- src/tools/rust-analyzer/Cargo.lock | 44 +++++++++---------- src/tools/rust-analyzer/Cargo.toml | 16 +++---- .../crates/hir-ty/src/next_solver/interner.rs | 14 ++++++ 3 files changed, 44 insertions(+), 30 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index b70b89ea543d..9475391acdb7 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1863,9 +1863,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_abi" -version = "0.128.0" +version = "0.129.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da95e732b424802b1f043ab4007c78a0fc515ab249587abbea4634bf5fdce9a" +checksum = "aa338fe027a8915009ca4a5a1cb7dde5fb4bc4170a928cb9462fda9d2ec52cec" dependencies = [ "bitflags 2.9.1", "ra-ap-rustc_hashes", @@ -1875,24 +1875,24 @@ dependencies = [ [[package]] name = "ra-ap-rustc_ast_ir" -version = "0.128.0" +version = "0.129.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3838d9d7a3a5cdc511cfb6ad78740ce532f75a2366d3fc3b9853ea1b5c872779" +checksum = "a8468ef77e5359b3a51e327406f29ca2283a4feef93d3ba04f6740b274636922" [[package]] name = "ra-ap-rustc_hashes" -version = "0.128.0" +version = "0.129.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdc8995d268d3bb4ece910f575ea5a063d6003e193ec155d15703b65882d53fb" +checksum = "300bc3264ccc1e7a5b3f065023a02e612774206d8ad685b3b05c2e4e317f8daa" dependencies = [ "rustc-stable-hash", ] [[package]] name = "ra-ap-rustc_index" -version = "0.128.0" +version = "0.129.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed0ccdf6e5627c6c3e54e571e52ce0bc8b94d5f0b94b7460269ca68a4706be69" +checksum = "5eaa4a3ff61302e45c17ee72e067a39179081c19a12aa03192975a095f5d4e4b" dependencies = [ "ra-ap-rustc_index_macros", "smallvec", @@ -1900,9 +1900,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index_macros" -version = "0.128.0" +version = "0.129.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd28f42362b5c9fb9b8766c3189df02a402b13363600c6885e11027889f03ee6" +checksum = "0f7af0d51ee6bd5280be8e2eb7e9ac5cd9fc87af7a99f50cdb1316a8779c15ab" dependencies = [ "proc-macro2", "quote", @@ -1911,9 +1911,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_lexer" -version = "0.128.0" +version = "0.129.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1c31a82f091b910a27ee53a86a9af28a2df10c3484e2f1bbfe70633aa84dee9" +checksum = "ee4e7df9bf702c855de7bea5e3c14b96f0728d4712edb663b0f4b183622341fc" dependencies = [ "memchr", "unicode-properties", @@ -1922,9 +1922,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_next_trait_solver" -version = "0.128.0" +version = "0.129.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cac6c2b5a8924209d4ca682cbc507252c58a664911e0ef463c112882ba6f72" +checksum = "15768080a276088a4a6af1e08a4ca622c57d5b4845ce5329dbbd71a2e025eecb" dependencies = [ "derive-where", "ra-ap-rustc_index", @@ -1935,9 +1935,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_parse_format" -version = "0.128.0" +version = "0.129.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a085a1cf902dcca8abbc537faaef154bbccbbb51850f779ce5484ae3782b5d8f" +checksum = "e6f0c54b200c47768eaf142b1c829da9be1a3331a5defa4ac60bad4996f474e9" dependencies = [ "ra-ap-rustc_lexer", "rustc-literal-escaper 0.0.5", @@ -1945,9 +1945,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_pattern_analysis" -version = "0.128.0" +version = "0.129.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ba32e3985367bc34856b41c7604133649d4a367eb5d7bdf50623025731459d8" +checksum = "a078fbbefda17d8d5d2c9d6b5a1f9ee1e23fae5f057e74784f6b95c189b0b048" dependencies = [ "ra-ap-rustc_index", "rustc-hash 2.1.1", @@ -1958,9 +1958,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_type_ir" -version = "0.128.0" +version = "0.129.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9911d72f75d85d21fe88374d7bcec94f2200feffb7234108a24cc3da7c3591" +checksum = "644e980122cdb7f2d7e175f33224dc6df414e8cf3e5dfbba9047e63336d9737a" dependencies = [ "arrayvec", "bitflags 2.9.1", @@ -1978,9 +1978,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_type_ir_macros" -version = "0.128.0" +version = "0.129.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22f539b87991683ce17cc52e62600fdf2b4a8af43952db30387edc1a576d3b43" +checksum = "827d242d444cea86d9a64b5ce99db1462c9d43c7c6226d44ec2bc621b7c253c0" dependencies = [ "proc-macro2", "quote", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index c5ffad544a6b..94ec1a07438f 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -89,14 +89,14 @@ vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" } edition = { path = "./crates/edition", version = "0.0.0" } -ra-ap-rustc_lexer = { version = "0.128", default-features = false } -ra-ap-rustc_parse_format = { version = "0.128", default-features = false } -ra-ap-rustc_index = { version = "0.128", default-features = false } -ra-ap-rustc_abi = { version = "0.128", default-features = false } -ra-ap-rustc_pattern_analysis = { version = "0.128", default-features = false } -ra-ap-rustc_ast_ir = { version = "0.128", default-features = false } -ra-ap-rustc_type_ir = { version = "0.128", default-features = false } -ra-ap-rustc_next_trait_solver = { version = "0.128", default-features = false } +ra-ap-rustc_lexer = { version = "0.129", default-features = false } +ra-ap-rustc_parse_format = { version = "0.129", default-features = false } +ra-ap-rustc_index = { version = "0.129", default-features = false } +ra-ap-rustc_abi = { version = "0.129", default-features = false } +ra-ap-rustc_pattern_analysis = { version = "0.129", default-features = false } +ra-ap-rustc_ast_ir = { version = "0.129", default-features = false } +ra-ap-rustc_type_ir = { version = "0.129", default-features = false } +ra-ap-rustc_next_trait_solver = { version = "0.129", default-features = false } # local crates that aren't published to crates.io. These should not have versions. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 062f6aebf160..9cf56bef9578 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -1674,6 +1674,20 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { } } + fn for_each_blanket_impl(self, trait_def_id: Self::TraitId, mut f: impl FnMut(Self::ImplId)) { + let Some(krate) = self.krate else { return }; + + for impls in self.db.trait_impls_in_deps(krate).iter() { + for impl_id in impls.for_trait(trait_def_id.0) { + let impl_data = self.db.impl_signature(impl_id); + let self_ty_ref = &impl_data.store[impl_data.self_ty]; + if matches!(self_ty_ref, hir_def::type_ref::TypeRef::TypeParam(_)) { + f(impl_id.into()); + } + } + } + } + fn has_item_definition(self, def_id: Self::DefId) -> bool { // FIXME(next-solver): should check if the associated item has a value. true From 2c7350269c38ae3284702597c9fd016453d445cf Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 18 Sep 2025 16:59:20 +0200 Subject: [PATCH 629/682] Bump nightly version -> 2025-09-18 --- clippy_utils/README.md | 2 +- rust-toolchain.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_utils/README.md b/clippy_utils/README.md index e01f563c49e7..2c66fdc73f53 100644 --- a/clippy_utils/README.md +++ b/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2025-09-04 +nightly-2025-09-18 ``` diff --git a/rust-toolchain.toml b/rust-toolchain.toml index ec2f24a0a6d8..9c102de44820 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-09-04" +channel = "nightly-2025-09-18" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" From 6b14443a028b9338ddf0c57962f2c6cea1170f5c Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 18 Sep 2025 16:59:44 +0200 Subject: [PATCH 630/682] Bump Clippy version -> 0.1.92 --- Cargo.toml | 2 +- clippy_config/Cargo.toml | 2 +- clippy_lints/Cargo.toml | 2 +- clippy_utils/Cargo.toml | 2 +- declare_clippy_lint/Cargo.toml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b3618932ded7..e06383499893 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.91" +version = "0.1.92" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_config/Cargo.toml b/clippy_config/Cargo.toml index 6ad2cf0d0b10..f8c748290e41 100644 --- a/clippy_config/Cargo.toml +++ b/clippy_config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_config" -version = "0.1.91" +version = "0.1.92" edition = "2024" publish = false diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 70184ee2ca59..51e59ae20507 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.91" +version = "0.1.92" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index bdf7431f29f2..d58b47bf6deb 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.91" +version = "0.1.92" edition = "2024" description = "Helpful tools for writing lints, provided as they are used in Clippy" repository = "https://github.com/rust-lang/rust-clippy" diff --git a/declare_clippy_lint/Cargo.toml b/declare_clippy_lint/Cargo.toml index ec0e59e70549..4de7b5fb5924 100644 --- a/declare_clippy_lint/Cargo.toml +++ b/declare_clippy_lint/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "declare_clippy_lint" -version = "0.1.91" +version = "0.1.92" edition = "2024" repository = "https://github.com/rust-lang/rust-clippy" license = "MIT OR Apache-2.0" From 5d1619b3ec4b843715c795789b7173e48066db5a Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 18 Sep 2025 17:21:54 +0200 Subject: [PATCH 631/682] Update Cargo.lock --- Cargo.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d39cfefea0c7..4d1c6d4c46b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -568,7 +568,7 @@ checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "clippy" -version = "0.1.91" +version = "0.1.92" dependencies = [ "anstream", "askama", @@ -595,7 +595,7 @@ dependencies = [ [[package]] name = "clippy_config" -version = "0.1.91" +version = "0.1.92" dependencies = [ "clippy_utils", "itertools", @@ -618,7 +618,7 @@ dependencies = [ [[package]] name = "clippy_lints" -version = "0.1.91" +version = "0.1.92" dependencies = [ "arrayvec", "cargo_metadata 0.18.1", @@ -649,7 +649,7 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.91" +version = "0.1.92" dependencies = [ "arrayvec", "itertools", @@ -1051,7 +1051,7 @@ dependencies = [ [[package]] name = "declare_clippy_lint" -version = "0.1.91" +version = "0.1.92" [[package]] name = "derive-where" From c6b4f6f33f6e78d575a477a3398af44e47b18630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Thu, 18 Sep 2025 19:03:35 +0300 Subject: [PATCH 632/682] Bump rustc crates again --- src/tools/rust-analyzer/Cargo.lock | 44 +++++++++---------- src/tools/rust-analyzer/Cargo.toml | 16 +++---- .../crates/hir-ty/src/display.rs | 6 +-- .../crates/hir-ty/src/infer/coerce.rs | 2 +- .../crates/hir-ty/src/lower_nextsolver.rs | 2 +- .../crates/hir-ty/src/method_resolution.rs | 2 +- .../crates/hir-ty/src/mir/eval.rs | 2 +- .../hir-ty/src/next_solver/fulfill/errors.rs | 2 +- .../crates/hir-ty/src/next_solver/mapping.rs | 6 +-- .../crates/hir-ty/src/next_solver/ty.rs | 23 ++++------ .../crates/hir-ty/src/next_solver/util.rs | 2 +- 11 files changed, 50 insertions(+), 57 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 9475391acdb7..be21cfd9c24f 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1863,9 +1863,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_abi" -version = "0.129.0" +version = "0.130.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa338fe027a8915009ca4a5a1cb7dde5fb4bc4170a928cb9462fda9d2ec52cec" +checksum = "dd3df655461690c1554bc0e355f19495eef6cd729dc29f01bd07cb7cd2a0990a" dependencies = [ "bitflags 2.9.1", "ra-ap-rustc_hashes", @@ -1875,24 +1875,24 @@ dependencies = [ [[package]] name = "ra-ap-rustc_ast_ir" -version = "0.129.0" +version = "0.130.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8468ef77e5359b3a51e327406f29ca2283a4feef93d3ba04f6740b274636922" +checksum = "10f55c57676d67dba036171d23f7941a9c3b884181c5e93d3bd5fa599f8f129b" [[package]] name = "ra-ap-rustc_hashes" -version = "0.129.0" +version = "0.130.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "300bc3264ccc1e7a5b3f065023a02e612774206d8ad685b3b05c2e4e317f8daa" +checksum = "feec9ffe50d93b8f17770b49d4cbf46280e074a105c7c9325c8d2171cf395b76" dependencies = [ "rustc-stable-hash", ] [[package]] name = "ra-ap-rustc_index" -version = "0.129.0" +version = "0.130.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eaa4a3ff61302e45c17ee72e067a39179081c19a12aa03192975a095f5d4e4b" +checksum = "a73bab902bcdeceac4a9630ed732d19f6b7189cbe8e828a94156a780bd8e1196" dependencies = [ "ra-ap-rustc_index_macros", "smallvec", @@ -1900,9 +1900,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index_macros" -version = "0.129.0" +version = "0.130.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f7af0d51ee6bd5280be8e2eb7e9ac5cd9fc87af7a99f50cdb1316a8779c15ab" +checksum = "88c7906b654b9c4188aee3fde82a7449bac6e156a5d86ad923bae05bd5040690" dependencies = [ "proc-macro2", "quote", @@ -1911,9 +1911,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_lexer" -version = "0.129.0" +version = "0.130.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee4e7df9bf702c855de7bea5e3c14b96f0728d4712edb663b0f4b183622341fc" +checksum = "acec3893f60dca2a37f9e838c08f3f3c8e012ce36194c2b84da96781e794e359" dependencies = [ "memchr", "unicode-properties", @@ -1922,9 +1922,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_next_trait_solver" -version = "0.129.0" +version = "0.130.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15768080a276088a4a6af1e08a4ca622c57d5b4845ce5329dbbd71a2e025eecb" +checksum = "c4626e22dc21062bdac1a8340b497365e1983a69cc271d7d052a62348b88bd7f" dependencies = [ "derive-where", "ra-ap-rustc_index", @@ -1935,9 +1935,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_parse_format" -version = "0.129.0" +version = "0.130.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6f0c54b200c47768eaf142b1c829da9be1a3331a5defa4ac60bad4996f474e9" +checksum = "47a95720d31edf45a8ccde190c14954425af0d53c9b6d4702fdd905b3951a2e1" dependencies = [ "ra-ap-rustc_lexer", "rustc-literal-escaper 0.0.5", @@ -1945,9 +1945,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_pattern_analysis" -version = "0.129.0" +version = "0.130.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a078fbbefda17d8d5d2c9d6b5a1f9ee1e23fae5f057e74784f6b95c189b0b048" +checksum = "0c1ebf6272bf11f0ba3b909e3405f547de32ea19d745b8fef11f1fb643b72fcd" dependencies = [ "ra-ap-rustc_index", "rustc-hash 2.1.1", @@ -1958,9 +1958,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_type_ir" -version = "0.129.0" +version = "0.130.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "644e980122cdb7f2d7e175f33224dc6df414e8cf3e5dfbba9047e63336d9737a" +checksum = "a9aa8f1344940e097514403c53bb5a1e4f5c3986da888461bc651373d195090b" dependencies = [ "arrayvec", "bitflags 2.9.1", @@ -1978,9 +1978,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_type_ir_macros" -version = "0.129.0" +version = "0.130.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "827d242d444cea86d9a64b5ce99db1462c9d43c7c6226d44ec2bc621b7c253c0" +checksum = "30c65f1a5da2f3c39d72a56687694568abb203b3737a2321090e112da04958b3" dependencies = [ "proc-macro2", "quote", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 94ec1a07438f..e81c4088122b 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -89,14 +89,14 @@ vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" } edition = { path = "./crates/edition", version = "0.0.0" } -ra-ap-rustc_lexer = { version = "0.129", default-features = false } -ra-ap-rustc_parse_format = { version = "0.129", default-features = false } -ra-ap-rustc_index = { version = "0.129", default-features = false } -ra-ap-rustc_abi = { version = "0.129", default-features = false } -ra-ap-rustc_pattern_analysis = { version = "0.129", default-features = false } -ra-ap-rustc_ast_ir = { version = "0.129", default-features = false } -ra-ap-rustc_type_ir = { version = "0.129", default-features = false } -ra-ap-rustc_next_trait_solver = { version = "0.129", default-features = false } +ra-ap-rustc_lexer = { version = "0.130", default-features = false } +ra-ap-rustc_parse_format = { version = "0.130", default-features = false } +ra-ap-rustc_index = { version = "0.130", default-features = false } +ra-ap-rustc_abi = { version = "0.130", default-features = false } +ra-ap-rustc_pattern_analysis = { version = "0.130", default-features = false } +ra-ap-rustc_ast_ir = { version = "0.130", default-features = false } +ra-ap-rustc_type_ir = { version = "0.130", default-features = false } +ra-ap-rustc_next_trait_solver = { version = "0.130", default-features = false } # local crates that aren't published to crates.io. These should not have versions. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 0a514f389b47..519e4b59237f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -897,7 +897,7 @@ fn render_const_scalar_inner( } f.write_str("]") } - TyKind::Dynamic(_, _, _) => { + TyKind::Dynamic(_, _) => { let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap()); let ty_id = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap()); let Ok(t) = memory_map.vtable_ty(ty_id) else { @@ -1064,7 +1064,7 @@ fn render_const_scalar_inner( | TyKind::Bound(_, _) | TyKind::Infer(_) => f.write_str(""), // The below arms are unreachable, since we handled them in ref case. - TyKind::Slice(_) | TyKind::Str | TyKind::Dynamic(_, _, _) => f.write_str(""), + TyKind::Slice(_) | TyKind::Str | TyKind::Dynamic(_, _) => f.write_str(""), } } @@ -1213,7 +1213,7 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { }) }; let (preds_to_print, has_impl_fn_pred) = match t.kind() { - TyKind::Dynamic(bounds, region, _) => { + TyKind::Dynamic(bounds, region) => { let render_lifetime = f.render_region(region); ( bounds.len() + render_lifetime as usize, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index 88b10e87e531..7930d8b0ed68 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -611,7 +611,7 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { | TyKind::Slice(_) | TyKind::FnDef(_, _) | TyKind::FnPtr(_, _) - | TyKind::Dynamic(_, _, _) + | TyKind::Dynamic(_, _) | TyKind::Closure(_, _) | TyKind::CoroutineClosure(_, _) | TyKind::Coroutine(_, _) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs index 2292e5c99413..b8e0599dba28 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -790,7 +790,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { }, None => Region::new_static(self.interner), }; - Ty::new_dynamic(self.interner, bounds, region, rustc_type_ir::DynKind::Dyn) + Ty::new_dynamic(self.interner, bounds, region) } else { // FIXME: report error // (additional non-auto traits, associated type rebound, or no resolved trait) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index a4427517a10b..7fa3d31fe5fd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -164,7 +164,7 @@ impl TyFingerprint { rustc_ast_ir::Mutability::Not => TyFingerprint::RawPtr(Mutability::Not), }, TyKind::Foreign(def) => TyFingerprint::ForeignType(crate::to_foreign_def_id(def.0)), - TyKind::Dynamic(bounds, _, _) => { + TyKind::Dynamic(bounds, _) => { let trait_ref = bounds .as_slice() .iter() diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index 6f950b8022c9..3e658cb93ed8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -2446,7 +2446,7 @@ impl<'db> Evaluator<'db> { | TyKind::Foreign(_) | TyKind::Error(_) | TyKind::Placeholder(_) - | TyKind::Dynamic(_, _, _) + | TyKind::Dynamic(_, _) | TyKind::Alias(_, _) | TyKind::Bound(_, _) | TyKind::Infer(_) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill/errors.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill/errors.rs index 6cd9e55acf0e..b49fac18c5dd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill/errors.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill/errors.rs @@ -1146,7 +1146,7 @@ mod wf { } TyKind::UnsafeBinder(ty) => {} - TyKind::Dynamic(data, r, _) => { + TyKind::Dynamic(data, r) => { // WfObject // // Here, we defer WF checking due to higher-ranked diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index d8a86ea083e0..b24b996b0927 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -370,8 +370,7 @@ impl<'db> ChalkToNextSolver<'db, Ty<'db>> for chalk_ir::Ty { }), ); let region = dyn_ty.lifetime.to_nextsolver(interner); - let kind = rustc_type_ir::DynKind::Dyn; - rustc_type_ir::TyKind::Dynamic(bounds, region, kind) + rustc_type_ir::TyKind::Dynamic(bounds, region) } chalk_ir::TyKind::Alias(alias_ty) => match alias_ty { chalk_ir::AliasTy::Projection(projection_ty) => { @@ -1445,8 +1444,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) TyKind::Function(fnptr) } - rustc_type_ir::TyKind::Dynamic(preds, region, dyn_kind) => { - assert!(matches!(dyn_kind, rustc_type_ir::DynKind::Dyn)); + rustc_type_ir::TyKind::Dynamic(preds, region) => { let self_ty = Ty::new_bound( interner, DebruijnIndex::from_u32(1), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index e6c444d0d6be..70139e866694 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -178,7 +178,7 @@ impl<'db> Ty<'db> { | TyKind::Never | TyKind::Error(_) => true, - TyKind::Str | TyKind::Slice(_) | TyKind::Dynamic(_, _, _) => match sizedness { + TyKind::Str | TyKind::Slice(_) | TyKind::Dynamic(_, _) => match sizedness { SizedTraitKind::Sized => false, SizedTraitKind::MetaSized => true, }, @@ -421,7 +421,7 @@ impl<'db> TypeSuperVisitable> for Ty<'db> { } TyKind::Slice(typ) => typ.visit_with(visitor), TyKind::Adt(_, args) => args.visit_with(visitor), - TyKind::Dynamic(ref trait_ty, ref reg, _) => { + TyKind::Dynamic(ref trait_ty, ref reg) => { try_visit!(trait_ty.visit_with(visitor)); reg.visit_with(visitor) } @@ -486,11 +486,9 @@ impl<'db> TypeSuperFoldable> for Ty<'db> { } TyKind::Slice(typ) => TyKind::Slice(typ.try_fold_with(folder)?), TyKind::Adt(tid, args) => TyKind::Adt(tid, args.try_fold_with(folder)?), - TyKind::Dynamic(trait_ty, region, representation) => TyKind::Dynamic( - trait_ty.try_fold_with(folder)?, - region.try_fold_with(folder)?, - representation, - ), + TyKind::Dynamic(trait_ty, region) => { + TyKind::Dynamic(trait_ty.try_fold_with(folder)?, region.try_fold_with(folder)?) + } TyKind::Tuple(ts) => TyKind::Tuple(ts.try_fold_with(folder)?), TyKind::FnDef(def_id, args) => TyKind::FnDef(def_id, args.try_fold_with(folder)?), TyKind::FnPtr(sig_tys, hdr) => TyKind::FnPtr(sig_tys.try_fold_with(folder)?, hdr), @@ -537,11 +535,9 @@ impl<'db> TypeSuperFoldable> for Ty<'db> { TyKind::Array(typ, sz) => TyKind::Array(typ.fold_with(folder), sz.fold_with(folder)), TyKind::Slice(typ) => TyKind::Slice(typ.fold_with(folder)), TyKind::Adt(tid, args) => TyKind::Adt(tid, args.fold_with(folder)), - TyKind::Dynamic(trait_ty, region, representation) => TyKind::Dynamic( - trait_ty.fold_with(folder), - region.fold_with(folder), - representation, - ), + TyKind::Dynamic(trait_ty, region) => { + TyKind::Dynamic(trait_ty.fold_with(folder), region.fold_with(folder)) + } TyKind::Tuple(ts) => TyKind::Tuple(ts.fold_with(folder)), TyKind::FnDef(def_id, args) => TyKind::FnDef(def_id, args.fold_with(folder)), TyKind::FnPtr(sig_tys, hdr) => TyKind::FnPtr(sig_tys.fold_with(folder), hdr), @@ -676,9 +672,8 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> { interner: DbInterner<'db>, preds: as rustc_type_ir::Interner>::BoundExistentialPredicates, region: as rustc_type_ir::Interner>::Region, - kind: rustc_type_ir::DynKind, ) -> Self { - Ty::new(interner, TyKind::Dynamic(preds, region, kind)) + Ty::new(interner, TyKind::Dynamic(preds, region)) } fn new_coroutine( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs index a50f20a565ee..a7f9817f9c08 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs @@ -461,7 +461,7 @@ pub fn sizedness_constraint_for_ty<'db>( | CoroutineWitness(..) | Never => None, // these are never sized - Str | Slice(..) | Dynamic(_, _, rustc_type_ir::DynKind::Dyn) => match sizedness { + Str | Slice(..) | Dynamic(_, _) => match sizedness { // Never `Sized` SizedTraitKind::Sized => Some(ty), // Always `MetaSized` From 7f55f5761cd44b8ad49efb976ae650602fc2d42a Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Fri, 12 Sep 2025 20:29:23 +0000 Subject: [PATCH 633/682] Allow windows resource compiler to be overridden It is now required to provide a resource compiler on windows when compiling rust. This allows toolchain builders to explicitly provide a path to an alternative, such as llvm-rc, instead of the one that's provided by the Windows SDK. --- bootstrap.example.toml | 3 +++ compiler/rustc_windows_rc/src/lib.rs | 7 +++++-- src/bootstrap/src/core/builder/cargo.rs | 5 +++++ src/bootstrap/src/core/config/config.rs | 3 +++ src/bootstrap/src/core/config/toml/build.rs | 1 + src/bootstrap/src/utils/change_tracker.rs | 5 +++++ 6 files changed, 22 insertions(+), 2 deletions(-) diff --git a/bootstrap.example.toml b/bootstrap.example.toml index 51529751dd58..0cd571134ef1 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -325,6 +325,9 @@ # Defaults to the Python interpreter used to execute x.py. #build.python = "python" +# The path to (or name of) the resource compiler executable to use on Windows. +#build.windows-rc = "rc.exe" + # The path to the REUSE executable to use. Note that REUSE is not required in # most cases, as our tooling relies on a cached (and shrunk) copy of the # REUSE output present in the git repository and in our source tarballs. diff --git a/compiler/rustc_windows_rc/src/lib.rs b/compiler/rustc_windows_rc/src/lib.rs index caa5e5ef2765..5e95557501ea 100644 --- a/compiler/rustc_windows_rc/src/lib.rs +++ b/compiler/rustc_windows_rc/src/lib.rs @@ -35,8 +35,11 @@ pub fn compile_windows_resource_file( resources_dir.push("resources"); fs::create_dir_all(&resources_dir).unwrap(); - let resource_compiler = - find_resource_compiler(&env::var("CARGO_CFG_TARGET_ARCH").unwrap()).expect("found rc.exe"); + let resource_compiler = if let Ok(path) = env::var("RUSTC_WINDOWS_RC") { + path.into() + } else { + find_resource_compiler(&env::var("CARGO_CFG_TARGET_ARCH").unwrap()).expect("found rc.exe") + }; let rc_path = resources_dir.join(file_stem.with_extension("rc")); diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 6121bf7d9cd6..ee2bb710674c 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -1227,6 +1227,11 @@ impl Builder<'_> { rustflags.arg("-Zehcont-guard"); } + // Optionally override the rc.exe when compiling rustc on Windows. + if let Some(windows_rc) = &self.config.windows_rc { + cargo.env("RUSTC_WINDOWS_RC", windows_rc); + } + // For `cargo doc` invocations, make rustdoc print the Rust version into the docs // This replaces spaces with tabs because RUSTDOCFLAGS does not // support arguments with regular spaces. Hopefully someday Cargo will diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index efb7ad916997..a97399b3d4ae 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -273,6 +273,7 @@ pub struct Config { pub gdb: Option, pub lldb: Option, pub python: Option, + pub windows_rc: Option, pub reuse: Option, pub cargo_native_static: bool, pub configure_args: Vec, @@ -450,6 +451,7 @@ impl Config { nodejs: build_nodejs, npm: build_npm, python: build_python, + windows_rc: build_windows_rc, reuse: build_reuse, locked_deps: build_locked_deps, vendor: build_vendor, @@ -1342,6 +1344,7 @@ impl Config { .unwrap_or(rust_debug == Some(true)), vendor, verbose_tests, + windows_rc: build_windows_rc.map(PathBuf::from), // tidy-alphabetical-end } } diff --git a/src/bootstrap/src/core/config/toml/build.rs b/src/bootstrap/src/core/config/toml/build.rs index 25c19f1070a3..a9d4d3961c9b 100644 --- a/src/bootstrap/src/core/config/toml/build.rs +++ b/src/bootstrap/src/core/config/toml/build.rs @@ -37,6 +37,7 @@ define_config! { nodejs: Option = "nodejs", npm: Option = "npm", python: Option = "python", + windows_rc: Option = "windows-rc", reuse: Option = "reuse", locked_deps: Option = "locked-deps", vendor: Option = "vendor", diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 2c48cebd2df0..6b187578c31e 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -551,4 +551,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "There is now a bootstrap option called `rust.parallel-frontend-threads`, which can be used to set the number of threads for the compiler frontend used during compilation of Rust code.", }, + ChangeInfo { + change_id: 146663, + severity: ChangeSeverity::Info, + summary: "New option `build.windows-rc` that will override which resource compiler on Windows will be used to compile Rust.", + }, ]; From 60f0407ba313fbea163ee4fe21a8a71ba633ecc6 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 19 Sep 2025 03:04:14 +0900 Subject: [PATCH 634/682] fix: Fix `indexmap` with `in-rust-tree` --- src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs index deee8dd1ff06..073a02908dee 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs @@ -45,9 +45,4 @@ pub type PolyFnSig<'db> = Binder<'db, rustc_type_ir::FnSig>>; pub type TypingMode<'db> = rustc_type_ir::TypingMode>; pub type TypeError<'db> = rustc_type_ir::error::TypeError>; pub type QueryResult<'db> = rustc_type_ir::solve::QueryResult>; - -#[cfg(feature = "in-rust-tree")] -use rustc_data_structure::sorted_map::index_map as indexmap; - -pub type FxIndexMap = - indexmap::IndexMap>; +pub type FxIndexMap = rustc_type_ir::data_structures::IndexMap; From 3ba8b8e0e5284ad3c33eb6a1490a6adcea4c105d Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 19 Sep 2025 03:26:03 +0900 Subject: [PATCH 635/682] minor: Yet another rustc crates bump --- src/tools/rust-analyzer/Cargo.lock | 44 +++++++++---------- src/tools/rust-analyzer/Cargo.toml | 16 +++---- .../crates/hir-ty/src/infer/unify.rs | 2 +- .../crates/hir-ty/src/next_solver/fulfill.rs | 4 +- .../hir-ty/src/next_solver/fulfill/errors.rs | 22 ++++++---- .../hir-ty/src/next_solver/infer/context.rs | 9 ++++ .../hir-ty/src/next_solver/infer/select.rs | 8 ++-- .../crates/hir-ty/src/next_solver/inspect.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/traits.rs | 4 +- 9 files changed, 62 insertions(+), 49 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index be21cfd9c24f..97c4f06dd596 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1863,9 +1863,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_abi" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd3df655461690c1554bc0e355f19495eef6cd729dc29f01bd07cb7cd2a0990a" +checksum = "016c05852e89655395fbf7d5e729e31cd58b2690480b3a8468eb2b38c91f3756" dependencies = [ "bitflags 2.9.1", "ra-ap-rustc_hashes", @@ -1875,24 +1875,24 @@ dependencies = [ [[package]] name = "ra-ap-rustc_ast_ir" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f55c57676d67dba036171d23f7941a9c3b884181c5e93d3bd5fa599f8f129b" +checksum = "c5bff48bd0a26f17a4e2e8610bc393296c3d002e221f5c6c4d2875e6a64a3b4b" [[package]] name = "ra-ap-rustc_hashes" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feec9ffe50d93b8f17770b49d4cbf46280e074a105c7c9325c8d2171cf395b76" +checksum = "5d15e7571f6f31f6112fd2fcbc3450a6ef477cc6bfe51643a2b110436a7455f0" dependencies = [ "rustc-stable-hash", ] [[package]] name = "ra-ap-rustc_index" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a73bab902bcdeceac4a9630ed732d19f6b7189cbe8e828a94156a780bd8e1196" +checksum = "60a30f0a15682f1194e5812cc3544266f3494ca8fb3c5033e8b9c08f4f8b8c6d" dependencies = [ "ra-ap-rustc_index_macros", "smallvec", @@ -1900,9 +1900,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index_macros" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88c7906b654b9c4188aee3fde82a7449bac6e156a5d86ad923bae05bd5040690" +checksum = "e2f82c9176b964591e1657a9f0fae2850525542d39075c49c2459afc291f4804" dependencies = [ "proc-macro2", "quote", @@ -1911,9 +1911,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_lexer" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acec3893f60dca2a37f9e838c08f3f3c8e012ce36194c2b84da96781e794e359" +checksum = "585c71ff7da5ca1e8a0c65d5e7cec7ab2a7e9e45b6859543bd2d0be21bee631c" dependencies = [ "memchr", "unicode-properties", @@ -1922,9 +1922,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_next_trait_solver" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4626e22dc21062bdac1a8340b497365e1983a69cc271d7d052a62348b88bd7f" +checksum = "839f521a6cd97c71b2b6681604ace38a9c6737f7a223d072fa8f909879f9dc4b" dependencies = [ "derive-where", "ra-ap-rustc_index", @@ -1935,9 +1935,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_parse_format" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47a95720d31edf45a8ccde190c14954425af0d53c9b6d4702fdd905b3951a2e1" +checksum = "83e7872a4fa0620937b60fc6270aa8c31864cb324357553e2be5b2bdc25fa89a" dependencies = [ "ra-ap-rustc_lexer", "rustc-literal-escaper 0.0.5", @@ -1945,9 +1945,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_pattern_analysis" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c1ebf6272bf11f0ba3b909e3405f547de32ea19d745b8fef11f1fb643b72fcd" +checksum = "4ecd7f9b960c8cf1e9d02a25297f52a520134132164cfedd3e69fee4f294d41c" dependencies = [ "ra-ap-rustc_index", "rustc-hash 2.1.1", @@ -1958,9 +1958,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_type_ir" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9aa8f1344940e097514403c53bb5a1e4f5c3986da888461bc651373d195090b" +checksum = "9494498f8f7c57a5b4ea4e35a6c554cca8d5323adb534d13dc6cd117a7e00e4d" dependencies = [ "arrayvec", "bitflags 2.9.1", @@ -1978,9 +1978,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_type_ir_macros" -version = "0.130.0" +version = "0.131.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30c65f1a5da2f3c39d72a56687694568abb203b3737a2321090e112da04958b3" +checksum = "28ed893760b86529080af59438c28dfa9ca976b821cfd0a3b0eb9591932847e6" dependencies = [ "proc-macro2", "quote", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index e81c4088122b..513af11a87aa 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -89,14 +89,14 @@ vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" } edition = { path = "./crates/edition", version = "0.0.0" } -ra-ap-rustc_lexer = { version = "0.130", default-features = false } -ra-ap-rustc_parse_format = { version = "0.130", default-features = false } -ra-ap-rustc_index = { version = "0.130", default-features = false } -ra-ap-rustc_abi = { version = "0.130", default-features = false } -ra-ap-rustc_pattern_analysis = { version = "0.130", default-features = false } -ra-ap-rustc_ast_ir = { version = "0.130", default-features = false } -ra-ap-rustc_type_ir = { version = "0.130", default-features = false } -ra-ap-rustc_next_trait_solver = { version = "0.130", default-features = false } +ra-ap-rustc_lexer = { version = "0.131", default-features = false } +ra-ap-rustc_parse_format = { version = "0.131", default-features = false } +ra-ap-rustc_index = { version = "0.131", default-features = false } +ra-ap-rustc_abi = { version = "0.131", default-features = false } +ra-ap-rustc_pattern_analysis = { version = "0.131", default-features = false } +ra-ap-rustc_ast_ir = { version = "0.131", default-features = false } +ra-ap-rustc_type_ir = { version = "0.131", default-features = false } +ra-ap-rustc_next_trait_solver = { version = "0.131", default-features = false } # local crates that aren't published to crates.io. These should not have versions. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index 77eaf83eec73..1687857ae1ac 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -910,7 +910,7 @@ impl<'db> InferenceTable<'db> { match result { Ok((_, Certainty::Yes)) => {} Err(rustc_type_ir::solve::NoSolution) => {} - Ok((_, Certainty::Maybe(_))) => { + Ok((_, Certainty::Maybe { .. })) => { self.fulfillment_cx.register_predicate_obligation( &self.infer_ctxt, Obligation::new( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs index a8183ab42279..34dff37972e7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs @@ -182,7 +182,7 @@ impl<'db> FulfillmentCtxt<'db> { if let Some(certainty) = delegate.compute_goal_fast_path(goal, Span::dummy()) { match certainty { Certainty::Yes => {} - Certainty::Maybe(_) => { + Certainty::Maybe { .. } => { self.obligations.register(obligation, None); } } @@ -211,7 +211,7 @@ impl<'db> FulfillmentCtxt<'db> { match certainty { Certainty::Yes => {} - Certainty::Maybe(_) => self.obligations.register(obligation, stalled_on), + Certainty::Maybe { .. } => self.obligations.register(obligation, stalled_on), } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill/errors.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill/errors.rs index b49fac18c5dd..ab4a229fbc05 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill/errors.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill/errors.rs @@ -153,15 +153,17 @@ pub(super) fn fulfillment_error_for_stalled<'db>( Span::dummy(), None, ) { - Ok(GoalEvaluation { certainty: Certainty::Maybe(MaybeCause::Ambiguity), .. }) => { - (FulfillmentErrorCode::Ambiguity { overflow: None }, true) - } + Ok(GoalEvaluation { + certainty: Certainty::Maybe { cause: MaybeCause::Ambiguity, .. }, + .. + }) => (FulfillmentErrorCode::Ambiguity { overflow: None }, true), Ok(GoalEvaluation { certainty: - Certainty::Maybe(MaybeCause::Overflow { - suggest_increasing_limit, - keep_constraints: _, - }), + Certainty::Maybe { + cause: + MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: _ }, + .. + }, .. }) => ( FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) }, @@ -314,7 +316,8 @@ impl<'db> BestObligation<'db> { .instantiate_proof_tree_for_nested_goal(GoalSource::Misc, obligation.as_goal()); // Skip nested goals that aren't the *reason* for our goal's failure. match (self.consider_ambiguities, nested_goal.result()) { - (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {} + (true, Ok(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. })) + | (false, Err(_)) => {} _ => continue, } @@ -456,7 +459,8 @@ impl<'db> ProofTreeVisitor<'db> for BestObligation<'db> { let interner = goal.infcx().interner; // Skip goals that aren't the *reason* for our goal's failure. match (self.consider_ambiguities, goal.result()) { - (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {} + (true, Ok(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. })) | (false, Err(_)) => { + } _ => return ControlFlow::Continue(()), } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs index 45ce7e6f6cc7..5aa5ad14af55 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/context.rs @@ -321,4 +321,13 @@ impl<'db> rustc_type_ir::InferCtxtLike for InferCtxt<'db> { fn sub_unify_ty_vids_raw(&self, a: rustc_type_ir::TyVid, b: rustc_type_ir::TyVid) { self.sub_unify_ty_vids_raw(a, b); } + + fn opaques_with_sub_unified_hidden_type( + &self, + _ty: TyVid, + ) -> Vec> { + // FIXME: I guess we are okay without this for now since currently r-a lacks of + // detailed checks over opaque types. Might need to implement this in future. + vec![] + } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/select.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/select.rs index d656d94f4f91..4f111fa66266 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/select.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/select.rs @@ -208,7 +208,7 @@ impl<'db> ProofTreeVisitor<'db> for Select { // Don't winnow until `Certainty::Yes` -- we don't need to winnow until // codegen, and only on the good path. - if matches!(goal.result().unwrap(), Certainty::Maybe(..)) { + if matches!(goal.result().unwrap(), Certainty::Maybe { .. }) { return ControlFlow::Break(Ok(None)); } @@ -241,7 +241,7 @@ fn candidate_should_be_dropped_in_favor_of<'db>( ) -> bool { // Don't winnow until `Certainty::Yes` -- we don't need to winnow until // codegen, and only on the good path. - if matches!(other.result().unwrap(), Certainty::Maybe(..)) { + if matches!(other.result().unwrap(), Certainty::Maybe { .. }) { return false; } @@ -284,13 +284,13 @@ fn candidate_should_be_dropped_in_favor_of<'db>( } fn to_selection<'db>(cand: InspectCandidate<'_, 'db>) -> Option> { - if let Certainty::Maybe(..) = cand.shallow_certainty() { + if let Certainty::Maybe { .. } = cand.shallow_certainty() { return None; } let nested = match cand.result().expect("expected positive result") { Certainty::Yes => Vec::new(), - Certainty::Maybe(_) => cand + Certainty::Maybe { .. } => cand .instantiate_nested_goals() .into_iter() .map(|nested| { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs index cab3c69118f6..f9d0aa99cf4f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs @@ -350,7 +350,7 @@ impl<'a, 'db> InspectGoal<'a, 'db> { inspect::ProbeStep::MakeCanonicalResponse { shallow_certainty: c } => { assert!(matches!( shallow_certainty.replace(c), - None | Some(Certainty::Maybe(MaybeCause::Ambiguity)) + None | Some(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. }) )); } inspect::ProbeStep::NestedProbe(ref probe) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs index d73d7da4b9b9..8b6836c0e90b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -277,7 +277,7 @@ pub fn next_trait_solve( Ok((_, Certainty::Yes, args)) => NextTraitSolveResult::Certain( convert_canonical_args_for_result(DbInterner::new_with(db, Some(krate), block), args), ), - Ok((_, Certainty::Maybe(_), args)) => { + Ok((_, Certainty::Maybe { .. }, args)) => { let subst = convert_canonical_args_for_result( DbInterner::new_with(db, Some(krate), block), args, @@ -316,7 +316,7 @@ pub fn next_trait_solve_canonical_in_ctxt<'db>( Ok((_, Certainty::Yes, args)) => NextTraitSolveResult::Certain( convert_canonical_args_for_result(infer_ctxt.interner, args), ), - Ok((_, Certainty::Maybe(_), args)) => { + Ok((_, Certainty::Maybe { .. }, args)) => { let subst = convert_canonical_args_for_result(infer_ctxt.interner, args); NextTraitSolveResult::Uncertain(chalk_ir::Canonical { binders: subst.binders, From 00bfe9ce6ed3d21b39a40309974dda5c19dc6fb0 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 18 Sep 2025 20:41:06 +0200 Subject: [PATCH 636/682] tweak genmc error report note --- src/tools/miri/src/concurrency/genmc/run.rs | 10 +++++----- .../tests/genmc/fail/data_race/mpu2_rels_rlx.stderr | 3 ++- .../genmc/fail/data_race/weak_orderings.rel_rlx.stderr | 3 ++- .../genmc/fail/data_race/weak_orderings.rlx_acq.stderr | 3 ++- .../genmc/fail/data_race/weak_orderings.rlx_rlx.stderr | 3 ++- src/tools/miri/tests/genmc/fail/loom/buggy_inc.stderr | 3 ++- .../tests/genmc/fail/loom/store_buffering.genmc.stderr | 3 ++- .../tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr | 3 ++- .../tests/genmc/fail/simple/2w2w_weak.release4.stderr | 3 ++- .../tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr | 3 ++- 10 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/tools/miri/src/concurrency/genmc/run.rs b/src/tools/miri/src/concurrency/genmc/run.rs index bc2f5f7b79ec..33c5b6b0a005 100644 --- a/src/tools/miri/src/concurrency/genmc/run.rs +++ b/src/tools/miri/src/concurrency/genmc/run.rs @@ -70,7 +70,7 @@ fn run_genmc_mode_impl<'tcx>( // Execute the program until completion to get the return value, or return if an error happens: let Some(return_code) = eval_entry(genmc_ctx.clone()) else { - genmc_ctx.print_genmc_output(genmc_config); + genmc_ctx.print_genmc_output(genmc_config, tcx); return None; }; @@ -97,7 +97,7 @@ fn run_genmc_mode_impl<'tcx>( // Since we don't have any span information for the error at this point, // we just print GenMC's error string, and the full GenMC output if requested. eprintln!("(GenMC) Error detected: {error}"); - genmc_ctx.print_genmc_output(genmc_config); + genmc_ctx.print_genmc_output(genmc_config, tcx); return None; } } @@ -110,13 +110,13 @@ impl GenmcCtx { /// /// This message can be very verbose and is likely not useful for the average user. /// This function should be called *after* Miri has printed all of its output. - fn print_genmc_output(&self, genmc_config: &GenmcConfig) { + fn print_genmc_output(&self, genmc_config: &GenmcConfig, tcx: TyCtxt<'_>) { if genmc_config.print_genmc_output { eprintln!("GenMC error report:"); eprintln!("{}", self.get_result_message()); } else { - eprintln!( - "(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.)" + tcx.dcx().note( + "add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report" ); } } diff --git a/src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.stderr b/src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.stderr index 00de74009d9b..1ffb55f22e6e 100644 --- a/src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.stderr +++ b/src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.stderr @@ -16,6 +16,7 @@ note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/f LL | f(); | ^^^ -(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rel_rlx.stderr b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rel_rlx.stderr index 501957a90d3a..b0037c211c69 100644 --- a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rel_rlx.stderr +++ b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rel_rlx.stderr @@ -16,6 +16,7 @@ note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/f LL | f(); | ^^^ -(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_acq.stderr b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_acq.stderr index 501957a90d3a..b0037c211c69 100644 --- a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_acq.stderr +++ b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_acq.stderr @@ -16,6 +16,7 @@ note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/f LL | f(); | ^^^ -(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_rlx.stderr b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_rlx.stderr index 501957a90d3a..b0037c211c69 100644 --- a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_rlx.stderr +++ b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_rlx.stderr @@ -16,6 +16,7 @@ note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/f LL | f(); | ^^^ -(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/fail/loom/buggy_inc.stderr b/src/tools/miri/tests/genmc/fail/loom/buggy_inc.stderr index 9c3ba1d4c592..290cdf90a084 100644 --- a/src/tools/miri/tests/genmc/fail/loom/buggy_inc.stderr +++ b/src/tools/miri/tests/genmc/fail/loom/buggy_inc.stderr @@ -10,6 +10,7 @@ LL | std::process::abort(); note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace -(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/fail/loom/store_buffering.genmc.stderr b/src/tools/miri/tests/genmc/fail/loom/store_buffering.genmc.stderr index db92d0573fad..7742790e3330 100644 --- a/src/tools/miri/tests/genmc/fail/loom/store_buffering.genmc.stderr +++ b/src/tools/miri/tests/genmc/fail/loom/store_buffering.genmc.stderr @@ -10,6 +10,7 @@ LL | std::process::abort(); note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace -(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr index 773f86a97596..80ac7206644c 100644 --- a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr +++ b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr @@ -10,6 +10,7 @@ LL | std::process::abort(); note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace -(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.release4.stderr b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.release4.stderr index 773f86a97596..80ac7206644c 100644 --- a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.release4.stderr +++ b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.release4.stderr @@ -10,6 +10,7 @@ LL | std::process::abort(); note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace -(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr index 773f86a97596..80ac7206644c 100644 --- a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr +++ b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr @@ -10,6 +10,7 @@ LL | std::process::abort(); note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace -(Add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report.) +note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report + error: aborting due to 1 previous error From 01cd04f78d86cdc1a60441fd9d6d08aa2fe05a2f Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 19 Sep 2025 04:20:24 +0900 Subject: [PATCH 637/682] fix: Fix one more thing in `in-rust-tree` --- .../rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs index f9d0aa99cf4f..128135d8debe 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs @@ -1,4 +1,4 @@ -pub use ra_ap_rustc_next_trait_solver::solve::inspect::*; +pub use rustc_next_trait_solver::solve::inspect::*; use rustc_ast_ir::try_visit; use rustc_next_trait_solver::{ From eb7abeb2614fb64f4d23afaa83df7380159b72c2 Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Sun, 16 Feb 2025 10:14:53 +0000 Subject: [PATCH 638/682] Specialize `Iterator::eq[_by]` for `TrustedLen` iterators --- library/core/src/iter/traits/iterator.rs | 52 ++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 7fb162a653fb..695f8d1e195e 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -4,6 +4,7 @@ use super::super::{ Product, Rev, Scan, Skip, SkipWhile, StepBy, Sum, Take, TakeWhile, TrustedRandomAccessNoCoerce, Zip, try_process, }; +use super::TrustedLen; use crate::array; use crate::cmp::{self, Ordering}; use crate::num::NonZero; @@ -3816,10 +3817,7 @@ pub trait Iterator { } } - match iter_compare(self, other.into_iter(), compare(eq)) { - ControlFlow::Continue(ord) => ord == Ordering::Equal, - ControlFlow::Break(()) => false, - } + SpecIterEq::spec_iter_eq(self, other.into_iter(), compare(eq)) } /// Determines if the elements of this [`Iterator`] are not equal to those of @@ -4038,6 +4036,42 @@ pub trait Iterator { } } +trait SpecIterEq: Iterator { + fn spec_iter_eq(self, b: B, f: F) -> bool + where + F: FnMut(Self::Item, ::Item) -> ControlFlow<()>; +} + +impl SpecIterEq for A { + #[inline] + default fn spec_iter_eq(self, b: B, f: F) -> bool + where + F: FnMut(Self::Item, ::Item) -> ControlFlow<()>, + { + iter_eq(self, b, f) + } +} + +impl SpecIterEq for A { + #[inline] + fn spec_iter_eq(self, b: B, f: F) -> bool + where + F: FnMut(Self::Item, ::Item) -> ControlFlow<()>, + { + // we *can't* short-circuit if: + match (self.size_hint(), b.size_hint()) { + // ... both iterators have the same length + ((_, Some(a)), (_, Some(b))) if a == b => {} + // ... or both of them are longer than `usize::MAX` (i.e. have an unknown length). + ((_, None), (_, None)) => {} + // otherwise, we can ascertain that they are unequal without actually comparing items + _ => return false, + } + + iter_eq(self, b, f) + } +} + /// Compares two iterators element-wise using the given function. /// /// If `ControlFlow::Continue(())` is returned from the function, the comparison moves on to the next @@ -4078,6 +4112,16 @@ where } } +#[inline] +fn iter_eq(a: A, b: B, f: F) -> bool +where + A: Iterator, + B: Iterator, + F: FnMut(A::Item, B::Item) -> ControlFlow<()>, +{ + iter_compare(a, b, f).continue_value().is_some_and(|ord| ord == Ordering::Equal) +} + /// Implements `Iterator` for mutable references to iterators, such as those produced by [`Iterator::by_ref`]. /// /// This implementation passes all method calls on to the original iterator. From f19b560fa96ca8dd4352db6372f662fa32942a9f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 18 Sep 2025 22:02:46 +0200 Subject: [PATCH 639/682] implement sqrt for f16 and f128 --- src/tools/miri/src/intrinsics/math.rs | 16 ++++++++++ src/tools/miri/tests/pass/float.rs | 44 ++++++++++++++++++--------- 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/src/tools/miri/src/intrinsics/math.rs b/src/tools/miri/src/intrinsics/math.rs index 21d4a92e7d28..ca81454a98c0 100644 --- a/src/tools/miri/src/intrinsics/math.rs +++ b/src/tools/miri/src/intrinsics/math.rs @@ -20,6 +20,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match intrinsic_name { // Operations we can do with soft-floats. + "sqrtf16" => { + let [f] = check_intrinsic_arg_count(args)?; + let f = this.read_scalar(f)?.to_f16()?; + // Sqrt is specified to be fully precise. + let res = math::sqrt(f); + let res = this.adjust_nan(res, &[f]); + this.write_scalar(res, dest)?; + } "sqrtf32" => { let [f] = check_intrinsic_arg_count(args)?; let f = this.read_scalar(f)?.to_f32()?; @@ -36,6 +44,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let res = this.adjust_nan(res, &[f]); this.write_scalar(res, dest)?; } + "sqrtf128" => { + let [f] = check_intrinsic_arg_count(args)?; + let f = this.read_scalar(f)?.to_f128()?; + // Sqrt is specified to be fully precise. + let res = math::sqrt(f); + let res = this.adjust_nan(res, &[f]); + this.write_scalar(res, dest)?; + } "fmaf32" => { let [a, b, c] = check_intrinsic_arg_count(args)?; diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs index 9f1b3f612b2d..3ce5ea8356bd 100644 --- a/src/tools/miri/tests/pass/float.rs +++ b/src/tools/miri/tests/pass/float.rs @@ -281,6 +281,35 @@ fn basic() { assert_eq!(34.2f64.abs(), 34.2f64); assert_eq!((-1.0f128).abs(), 1.0f128); assert_eq!(34.2f128.abs(), 34.2f128); + + assert_eq!(64_f16.sqrt(), 8_f16); + assert_eq!(64_f32.sqrt(), 8_f32); + assert_eq!(64_f64.sqrt(), 8_f64); + assert_eq!(64_f128.sqrt(), 8_f128); + assert_eq!(f16::INFINITY.sqrt(), f16::INFINITY); + assert_eq!(f32::INFINITY.sqrt(), f32::INFINITY); + assert_eq!(f64::INFINITY.sqrt(), f64::INFINITY); + assert_eq!(f128::INFINITY.sqrt(), f128::INFINITY); + assert_eq!(0.0_f16.sqrt().total_cmp(&0.0), std::cmp::Ordering::Equal); + assert_eq!(0.0_f32.sqrt().total_cmp(&0.0), std::cmp::Ordering::Equal); + assert_eq!(0.0_f64.sqrt().total_cmp(&0.0), std::cmp::Ordering::Equal); + assert_eq!(0.0_f128.sqrt().total_cmp(&0.0), std::cmp::Ordering::Equal); + assert_eq!((-0.0_f16).sqrt().total_cmp(&-0.0), std::cmp::Ordering::Equal); + assert_eq!((-0.0_f32).sqrt().total_cmp(&-0.0), std::cmp::Ordering::Equal); + assert_eq!((-0.0_f64).sqrt().total_cmp(&-0.0), std::cmp::Ordering::Equal); + assert_eq!((-0.0_f128).sqrt().total_cmp(&-0.0), std::cmp::Ordering::Equal); + assert!((-5.0_f16).sqrt().is_nan()); + assert!((-5.0_f32).sqrt().is_nan()); + assert!((-5.0_f64).sqrt().is_nan()); + assert!((-5.0_f128).sqrt().is_nan()); + assert!(f16::NEG_INFINITY.sqrt().is_nan()); + assert!(f32::NEG_INFINITY.sqrt().is_nan()); + assert!(f64::NEG_INFINITY.sqrt().is_nan()); + assert!(f128::NEG_INFINITY.sqrt().is_nan()); + assert!(f16::NAN.sqrt().is_nan()); + assert!(f32::NAN.sqrt().is_nan()); + assert!(f64::NAN.sqrt().is_nan()); + assert!(f128::NAN.sqrt().is_nan()); } /// Test casts from floats to ints and back @@ -1012,21 +1041,6 @@ pub fn libm() { unsafe { ldexp(a, b) } } - assert_eq!(64_f32.sqrt(), 8_f32); - assert_eq!(64_f64.sqrt(), 8_f64); - assert_eq!(f32::INFINITY.sqrt(), f32::INFINITY); - assert_eq!(f64::INFINITY.sqrt(), f64::INFINITY); - assert_eq!(0.0_f32.sqrt().total_cmp(&0.0), std::cmp::Ordering::Equal); - assert_eq!(0.0_f64.sqrt().total_cmp(&0.0), std::cmp::Ordering::Equal); - assert_eq!((-0.0_f32).sqrt().total_cmp(&-0.0), std::cmp::Ordering::Equal); - assert_eq!((-0.0_f64).sqrt().total_cmp(&-0.0), std::cmp::Ordering::Equal); - assert!((-5.0_f32).sqrt().is_nan()); - assert!((-5.0_f64).sqrt().is_nan()); - assert!(f32::NEG_INFINITY.sqrt().is_nan()); - assert!(f64::NEG_INFINITY.sqrt().is_nan()); - assert!(f32::NAN.sqrt().is_nan()); - assert!(f64::NAN.sqrt().is_nan()); - assert_approx_eq!(25f32.powi(-2), 0.0016f32); assert_approx_eq!(23.2f64.powi(2), 538.24f64); From 77f2d865549e8f2e1ad656c8202de9bfa6d2e354 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 18 Sep 2025 22:13:15 +0200 Subject: [PATCH 640/682] share sqrt implemention across float types --- src/tools/miri/src/intrinsics/math.rs | 52 ++++++++++----------------- src/tools/miri/src/math.rs | 12 +++---- 2 files changed, 25 insertions(+), 39 deletions(-) diff --git a/src/tools/miri/src/intrinsics/math.rs b/src/tools/miri/src/intrinsics/math.rs index ca81454a98c0..b9c99f285946 100644 --- a/src/tools/miri/src/intrinsics/math.rs +++ b/src/tools/miri/src/intrinsics/math.rs @@ -1,5 +1,5 @@ use rand::Rng; -use rustc_apfloat::{self, Float, Round}; +use rustc_apfloat::{self, Float, FloatConvert, Round}; use rustc_middle::mir; use rustc_middle::ty::{self, FloatTy}; @@ -7,6 +7,20 @@ use self::helpers::{ToHost, ToSoft}; use super::check_intrinsic_arg_count; use crate::*; +fn sqrt<'tcx, F: Float + FloatConvert + Into>( + this: &mut MiriInterpCx<'tcx>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, +) -> InterpResult<'tcx> { + let [f] = check_intrinsic_arg_count(args)?; + let f = this.read_scalar(f)?; + let f: F = f.to_float()?; + // Sqrt is specified to be fully precise. + let res = math::sqrt(f); + let res = this.adjust_nan(res, &[f]); + this.write_scalar(res, dest) +} + impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn emulate_math_intrinsic( @@ -20,38 +34,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match intrinsic_name { // Operations we can do with soft-floats. - "sqrtf16" => { - let [f] = check_intrinsic_arg_count(args)?; - let f = this.read_scalar(f)?.to_f16()?; - // Sqrt is specified to be fully precise. - let res = math::sqrt(f); - let res = this.adjust_nan(res, &[f]); - this.write_scalar(res, dest)?; - } - "sqrtf32" => { - let [f] = check_intrinsic_arg_count(args)?; - let f = this.read_scalar(f)?.to_f32()?; - // Sqrt is specified to be fully precise. - let res = math::sqrt(f); - let res = this.adjust_nan(res, &[f]); - this.write_scalar(res, dest)?; - } - "sqrtf64" => { - let [f] = check_intrinsic_arg_count(args)?; - let f = this.read_scalar(f)?.to_f64()?; - // Sqrt is specified to be fully precise. - let res = math::sqrt(f); - let res = this.adjust_nan(res, &[f]); - this.write_scalar(res, dest)?; - } - "sqrtf128" => { - let [f] = check_intrinsic_arg_count(args)?; - let f = this.read_scalar(f)?.to_f128()?; - // Sqrt is specified to be fully precise. - let res = math::sqrt(f); - let res = this.adjust_nan(res, &[f]); - this.write_scalar(res, dest)?; - } + "sqrtf16" => sqrt::(this, args, dest)?, + "sqrtf32" => sqrt::(this, args, dest)?, + "sqrtf64" => sqrt::(this, args, dest)?, + "sqrtf128" => sqrt::(this, args, dest)?, "fmaf32" => { let [a, b, c] = check_intrinsic_arg_count(args)?; diff --git a/src/tools/miri/src/math.rs b/src/tools/miri/src/math.rs index 401e6dd7aab8..50472ed3638e 100644 --- a/src/tools/miri/src/math.rs +++ b/src/tools/miri/src/math.rs @@ -2,7 +2,7 @@ use std::ops::Neg; use std::{f32, f64}; use rand::Rng as _; -use rustc_apfloat::Float as _; +use rustc_apfloat::Float; use rustc_apfloat::ieee::{DoubleS, IeeeFloat, Semantics, SingleS}; use rustc_middle::ty::{self, FloatTy, ScalarInt}; @@ -317,19 +317,19 @@ where } } -pub(crate) fn sqrt(x: IeeeFloat) -> IeeeFloat { +pub(crate) fn sqrt(x: F) -> F { match x.category() { // preserve zero sign rustc_apfloat::Category::Zero => x, // propagate NaN rustc_apfloat::Category::NaN => x, // sqrt of negative number is NaN - _ if x.is_negative() => IeeeFloat::NAN, + _ if x.is_negative() => F::NAN, // sqrt(∞) = ∞ - rustc_apfloat::Category::Infinity => IeeeFloat::INFINITY, + rustc_apfloat::Category::Infinity => F::INFINITY, rustc_apfloat::Category::Normal => { // Floating point precision, excluding the integer bit - let prec = i32::try_from(S::PRECISION).unwrap() - 1; + let prec = i32::try_from(F::PRECISION).unwrap() - 1; // x = 2^(exp - prec) * mant // where mant is an integer with prec+1 bits @@ -394,7 +394,7 @@ pub(crate) fn sqrt(x: IeeeFloat) -> IeeeFl res = (res + 1) >> 1; // Build resulting value with res as mantissa and exp/2 as exponent - IeeeFloat::from_u128(res).value.scalbn(exp / 2 - prec) + F::from_u128(res).value.scalbn(exp / 2 - prec) } } } From 45e5c765c660463179128c5deaf6f56ff24f3578 Mon Sep 17 00:00:00 2001 From: beepster4096 <19316085+beepster4096@users.noreply.github.com> Date: Thu, 18 Sep 2025 13:17:39 -0700 Subject: [PATCH 641/682] fix tidy spellchecking on windows --- src/tools/tidy/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index 2a9c3963d2d8..f7920e3205ab 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -237,7 +237,7 @@ pub fn ensure_version_or_cargo_install( if !cargo_exit_code.success() { return Err(io::Error::other("cargo install failed")); } - let bin_path = tool_bin_dir.join(bin_name); + let bin_path = tool_bin_dir.join(bin_name).with_extension(env::consts::EXE_EXTENSION); assert!( matches!(bin_path.try_exists(), Ok(true)), "cargo install did not produce the expected binary" From 7af6e590a4d1f66770b49698bd01d88cca98f18f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 18 Sep 2025 23:33:38 +0200 Subject: [PATCH 642/682] update lockfile --- Cargo.lock | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d39cfefea0c7..910a03eaffca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -674,7 +674,7 @@ checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" dependencies = [ "serde", "termcolor", - "unicode-width 0.1.14", + "unicode-width 0.2.1", ] [[package]] @@ -937,23 +937,24 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.168" +version = "1.0.185" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aa144b12f11741f0dab5b4182896afad46faa0598b6a061f7b9d17a21837ba7" +checksum = "2f81de88da10862f22b5b3a60f18f6f42bbe7cb8faa24845dd7b1e4e22190e77" dependencies = [ "cc", + "cxx-build", "cxxbridge-cmd", "cxxbridge-flags", "cxxbridge-macro", - "foldhash", + "foldhash 0.2.0", "link-cplusplus", ] [[package]] name = "cxx-build" -version = "1.0.168" +version = "1.0.185" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12d3cbb84fb003242941c231b45ca9417e786e66e94baa39584bd99df3a270b6" +checksum = "5edd58bf75c3fdfc80d79806403af626570662f7b6cc782a7fabe156166bd6d6" dependencies = [ "cc", "codespan-reporting", @@ -966,9 +967,9 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "1.0.168" +version = "1.0.185" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fa36b7b249d43f67a3f54bd65788e35e7afe64bbc671396387a48b3e8aaea94" +checksum = "fd46bf2b541a4e0c2d5abba76607379ee05d68e714868e3cb406dc8d591ce2d2" dependencies = [ "clap", "codespan-reporting", @@ -980,15 +981,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.168" +version = "1.0.185" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77707c70f6563edc5429618ca34a07241b75ebab35bd01d46697c75d58f8ddfe" +checksum = "2c79b68f6a3a8f809d39b38ae8af61305a6113819b19b262643b9c21353b92d9" [[package]] name = "cxxbridge-macro" -version = "1.0.168" +version = "1.0.185" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede6c0fb7e318f0a11799b86ee29dcf17b9be2960bd379a6c38e1a96a6010fff" +checksum = "862b7fdb048ff9ef0779a0d0a03affd09746c4c875543746b640756be9cff2af" dependencies = [ "indexmap", "proc-macro2", @@ -1388,6 +1389,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -1567,7 +1574,7 @@ checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.1.5", "serde", ] @@ -2160,9 +2167,9 @@ dependencies = [ [[package]] name = "link-cplusplus" -version = "1.0.10" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a6f6da007f968f9def0d65a05b187e2960183de70c160204ecfccf0ee330212" +checksum = "7f78c730aaa7d0b9336a299029ea49f9ee53b0ed06e9202e8cb7db9bae7b8c82" dependencies = [ "cc", ] From a08e6499e6a215021684681448ed0f3ce60c827a Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 16 Sep 2025 15:18:42 +0200 Subject: [PATCH 643/682] move `mod canonical` out of `eval_ctxt` --- .../src/{ => canonical}/canonicalizer.rs | 0 .../canonical.rs => canonical/mod.rs} | 226 ++---------------- compiler/rustc_next_trait_solver/src/lib.rs | 2 +- .../src/solve/eval_ctxt/mod.rs | 202 +++++++++++++++- .../src/solve/inspect/build.rs | 2 +- .../src/solve/inspect/mod.rs | 2 - .../rustc_next_trait_solver/src/solve/mod.rs | 19 -- .../src/solve/search_graph.rs | 3 +- .../src/solve/inspect/analyse.rs | 4 +- 9 files changed, 229 insertions(+), 231 deletions(-) rename compiler/rustc_next_trait_solver/src/{ => canonical}/canonicalizer.rs (100%) rename compiler/rustc_next_trait_solver/src/{solve/eval_ctxt/canonical.rs => canonical/mod.rs} (58%) diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs similarity index 100% rename from compiler/rustc_next_trait_solver/src/canonicalizer.rs rename to compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/canonical/mod.rs similarity index 58% rename from compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs rename to compiler/rustc_next_trait_solver/src/canonical/mod.rs index 889588afe61f..35881acf0c75 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/mod.rs @@ -11,27 +11,25 @@ use std::iter; +use canonicalizer::Canonicalizer; use rustc_index::IndexVec; -use rustc_type_ir::data_structures::HashSet; use rustc_type_ir::inherent::*; use rustc_type_ir::relate::solver_relating::RelateExt; -use rustc_type_ir::solve::OpaqueTypesJank; use rustc_type_ir::{ self as ty, Canonical, CanonicalVarKind, CanonicalVarValues, InferCtxtLike, Interner, TypeFoldable, }; -use tracing::{debug, instrument, trace}; +use tracing::instrument; -use crate::canonicalizer::Canonicalizer; use crate::delegate::SolverDelegate; use crate::resolve::eager_resolve_vars; -use crate::solve::eval_ctxt::CurrentGoalKind; use crate::solve::{ CanonicalInput, CanonicalResponse, Certainty, EvalCtxt, ExternalConstraintsData, Goal, - MaybeCause, NestedNormalizationGoals, NoSolution, PredefinedOpaquesData, QueryInput, - QueryResult, Response, inspect, response_no_constraints_raw, + NestedNormalizationGoals, PredefinedOpaquesData, QueryInput, Response, inspect, }; +pub mod canonicalizer; + trait ResponseT { fn var_values(&self) -> CanonicalVarValues; } @@ -78,199 +76,6 @@ where (orig_values, query_input) } - /// To return the constraints of a canonical query to the caller, we canonicalize: - /// - /// - `var_values`: a map from bound variables in the canonical goal to - /// the values inferred while solving the instantiated goal. - /// - `external_constraints`: additional constraints which aren't expressible - /// using simple unification of inference variables. - /// - /// This takes the `shallow_certainty` which represents whether we're confident - /// that the final result of the current goal only depends on the nested goals. - /// - /// In case this is `Certainty::Maybe`, there may still be additional nested goals - /// or inference constraints required for this candidate to be hold. The candidate - /// always requires all already added constraints and nested goals. - #[instrument(level = "trace", skip(self), ret)] - pub(in crate::solve) fn evaluate_added_goals_and_make_canonical_response( - &mut self, - shallow_certainty: Certainty, - ) -> QueryResult { - self.inspect.make_canonical_response(shallow_certainty); - - let goals_certainty = self.try_evaluate_added_goals()?; - assert_eq!( - self.tainted, - Ok(()), - "EvalCtxt is tainted -- nested goals may have been dropped in a \ - previous call to `try_evaluate_added_goals!`" - ); - - // We only check for leaks from universes which were entered inside - // of the query. - self.delegate.leak_check(self.max_input_universe).map_err(|NoSolution| { - trace!("failed the leak check"); - NoSolution - })?; - - let (certainty, normalization_nested_goals) = - match (self.current_goal_kind, shallow_certainty) { - // When normalizing, we've replaced the expected term with an unconstrained - // inference variable. This means that we dropped information which could - // have been important. We handle this by instead returning the nested goals - // to the caller, where they are then handled. We only do so if we do not - // need to recompute the `NormalizesTo` goal afterwards to avoid repeatedly - // uplifting its nested goals. This is the case if the `shallow_certainty` is - // `Certainty::Yes`. - (CurrentGoalKind::NormalizesTo, Certainty::Yes) => { - let goals = std::mem::take(&mut self.nested_goals); - // As we return all ambiguous nested goals, we can ignore the certainty - // returned by `self.try_evaluate_added_goals()`. - if goals.is_empty() { - assert!(matches!(goals_certainty, Certainty::Yes)); - } - ( - Certainty::Yes, - NestedNormalizationGoals( - goals.into_iter().map(|(s, g, _)| (s, g)).collect(), - ), - ) - } - _ => { - let certainty = shallow_certainty.and(goals_certainty); - (certainty, NestedNormalizationGoals::empty()) - } - }; - - if let Certainty::Maybe { - cause: cause @ MaybeCause::Overflow { keep_constraints: false, .. }, - opaque_types_jank, - } = certainty - { - // If we have overflow, it's probable that we're substituting a type - // into itself infinitely and any partial substitutions in the query - // response are probably not useful anyways, so just return an empty - // query response. - // - // This may prevent us from potentially useful inference, e.g. - // 2 candidates, one ambiguous and one overflow, which both - // have the same inference constraints. - // - // Changing this to retain some constraints in the future - // won't be a breaking change, so this is good enough for now. - return Ok(self.make_ambiguous_response_no_constraints(cause, opaque_types_jank)); - } - - let external_constraints = - self.compute_external_query_constraints(certainty, normalization_nested_goals); - let (var_values, mut external_constraints) = - eager_resolve_vars(self.delegate, (self.var_values, external_constraints)); - - // Remove any trivial or duplicated region constraints once we've resolved regions - let mut unique = HashSet::default(); - external_constraints.region_constraints.retain(|outlives| { - outlives.0.as_region().is_none_or(|re| re != outlives.1) && unique.insert(*outlives) - }); - - let canonical = Canonicalizer::canonicalize_response( - self.delegate, - self.max_input_universe, - &mut Default::default(), - Response { - var_values, - certainty, - external_constraints: self.cx().mk_external_constraints(external_constraints), - }, - ); - - // HACK: We bail with overflow if the response would have too many non-region - // inference variables. This tends to only happen if we encounter a lot of - // ambiguous alias types which get replaced with fresh inference variables - // during generalization. This prevents hangs caused by an exponential blowup, - // see tests/ui/traits/next-solver/coherence-alias-hang.rs. - match self.current_goal_kind { - // We don't do so for `NormalizesTo` goals as we erased the expected term and - // bailing with overflow here would prevent us from detecting a type-mismatch, - // causing a coherence error in diesel, see #131969. We still bail with overflow - // when later returning from the parent AliasRelate goal. - CurrentGoalKind::NormalizesTo => {} - CurrentGoalKind::Misc | CurrentGoalKind::CoinductiveTrait => { - let num_non_region_vars = canonical - .variables - .iter() - .filter(|c| !c.is_region() && c.is_existential()) - .count(); - if num_non_region_vars > self.cx().recursion_limit() { - debug!(?num_non_region_vars, "too many inference variables -> overflow"); - return Ok(self.make_ambiguous_response_no_constraints( - MaybeCause::Overflow { - suggest_increasing_limit: true, - keep_constraints: false, - }, - OpaqueTypesJank::AllGood, - )); - } - } - } - - Ok(canonical) - } - - /// Constructs a totally unconstrained, ambiguous response to a goal. - /// - /// Take care when using this, since often it's useful to respond with - /// ambiguity but return constrained variables to guide inference. - pub(in crate::solve) fn make_ambiguous_response_no_constraints( - &self, - cause: MaybeCause, - opaque_types_jank: OpaqueTypesJank, - ) -> CanonicalResponse { - response_no_constraints_raw( - self.cx(), - self.max_input_universe, - self.variables, - Certainty::Maybe { cause, opaque_types_jank }, - ) - } - - /// Computes the region constraints and *new* opaque types registered when - /// proving a goal. - /// - /// If an opaque was already constrained before proving this goal, then the - /// external constraints do not need to record that opaque, since if it is - /// further constrained by inference, that will be passed back in the var - /// values. - #[instrument(level = "trace", skip(self), ret)] - fn compute_external_query_constraints( - &self, - certainty: Certainty, - normalization_nested_goals: NestedNormalizationGoals, - ) -> ExternalConstraintsData { - // We only return region constraints once the certainty is `Yes`. This - // is necessary as we may drop nested goals on ambiguity, which may result - // in unconstrained inference variables in the region constraints. It also - // prevents us from emitting duplicate region constraints, avoiding some - // unnecessary work. This slightly weakens the leak check in case it uses - // region constraints from an ambiguous nested goal. This is tested in both - // `tests/ui/higher-ranked/leak-check/leak-check-in-selection-5-ambig.rs` and - // `tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.rs`. - let region_constraints = if certainty == Certainty::Yes { - self.delegate.make_deduplicated_outlives_constraints() - } else { - Default::default() - }; - - // We only return *newly defined* opaque types from canonical queries. - // - // Constraints for any existing opaque types are already tracked by changes - // to the `var_values`. - let opaque_types = self - .delegate - .clone_opaque_types_added_since(self.initial_opaque_types_storage_num_entries); - - ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals } - } - /// After calling a canonical query, we apply the constraints returned /// by the query using this function. /// @@ -469,7 +274,7 @@ where /// evaluating a goal. The `var_values` not only include the bound variables /// of the query input, but also contain all unconstrained inference vars /// created while evaluating this goal. -pub(in crate::solve) fn make_canonical_state( +pub fn make_canonical_state( delegate: &D, var_values: &[I::GenericArg], max_input_universe: ty::UniverseIndex, @@ -515,3 +320,22 @@ where EvalCtxt::unify_query_var_values(delegate, param_env, orig_values, var_values, span); data } + +pub fn response_no_constraints_raw( + cx: I, + max_universe: ty::UniverseIndex, + variables: I::CanonicalVarKinds, + certainty: Certainty, +) -> CanonicalResponse { + ty::Canonical { + max_universe, + variables, + value: Response { + var_values: ty::CanonicalVarValues::make_identity(cx, variables), + // FIXME: maybe we should store the "no response" version in cx, like + // we do for cx.types and stuff. + external_constraints: cx.mk_external_constraints(ExternalConstraintsData::default()), + certainty, + }, + } +} diff --git a/compiler/rustc_next_trait_solver/src/lib.rs b/compiler/rustc_next_trait_solver/src/lib.rs index d3965e14c68a..5fa29b7d9f81 100644 --- a/compiler/rustc_next_trait_solver/src/lib.rs +++ b/compiler/rustc_next_trait_solver/src/lib.rs @@ -10,7 +10,7 @@ #![allow(rustc::usage_of_type_ir_traits)] // tidy-alphabetical-end -pub mod canonicalizer; +pub mod canonical; pub mod coherence; pub mod delegate; pub mod placeholder; diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 5df7c92d8814..8f9b68dbac4f 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -17,6 +17,8 @@ use rustc_type_ir::{ use tracing::{debug, instrument, trace}; use super::has_only_region_constraints; +use crate::canonical::canonicalizer::Canonicalizer; +use crate::canonical::response_no_constraints_raw; use crate::coherence; use crate::delegate::SolverDelegate; use crate::placeholder::BoundVarReplacer; @@ -24,12 +26,11 @@ use crate::resolve::eager_resolve_vars; use crate::solve::search_graph::SearchGraph; use crate::solve::ty::may_use_unstable_feature; use crate::solve::{ - CanonicalInput, Certainty, FIXPOINT_STEP_LIMIT, Goal, GoalEvaluation, GoalSource, - GoalStalledOn, HasChanged, NestedNormalizationGoals, NoSolution, QueryInput, QueryResult, - inspect, + CanonicalInput, CanonicalResponse, Certainty, ExternalConstraintsData, FIXPOINT_STEP_LIMIT, + Goal, GoalEvaluation, GoalSource, GoalStalledOn, HasChanged, MaybeCause, + NestedNormalizationGoals, NoSolution, QueryInput, QueryResult, Response, inspect, }; -pub(super) mod canonical; mod probe; /// The kind of goal we're currently proving. @@ -1223,6 +1224,199 @@ where vec![] } } + + /// To return the constraints of a canonical query to the caller, we canonicalize: + /// + /// - `var_values`: a map from bound variables in the canonical goal to + /// the values inferred while solving the instantiated goal. + /// - `external_constraints`: additional constraints which aren't expressible + /// using simple unification of inference variables. + /// + /// This takes the `shallow_certainty` which represents whether we're confident + /// that the final result of the current goal only depends on the nested goals. + /// + /// In case this is `Certainty::Maybe`, there may still be additional nested goals + /// or inference constraints required for this candidate to be hold. The candidate + /// always requires all already added constraints and nested goals. + #[instrument(level = "trace", skip(self), ret)] + pub(in crate::solve) fn evaluate_added_goals_and_make_canonical_response( + &mut self, + shallow_certainty: Certainty, + ) -> QueryResult { + self.inspect.make_canonical_response(shallow_certainty); + + let goals_certainty = self.try_evaluate_added_goals()?; + assert_eq!( + self.tainted, + Ok(()), + "EvalCtxt is tainted -- nested goals may have been dropped in a \ + previous call to `try_evaluate_added_goals!`" + ); + + // We only check for leaks from universes which were entered inside + // of the query. + self.delegate.leak_check(self.max_input_universe).map_err(|NoSolution| { + trace!("failed the leak check"); + NoSolution + })?; + + let (certainty, normalization_nested_goals) = + match (self.current_goal_kind, shallow_certainty) { + // When normalizing, we've replaced the expected term with an unconstrained + // inference variable. This means that we dropped information which could + // have been important. We handle this by instead returning the nested goals + // to the caller, where they are then handled. We only do so if we do not + // need to recompute the `NormalizesTo` goal afterwards to avoid repeatedly + // uplifting its nested goals. This is the case if the `shallow_certainty` is + // `Certainty::Yes`. + (CurrentGoalKind::NormalizesTo, Certainty::Yes) => { + let goals = std::mem::take(&mut self.nested_goals); + // As we return all ambiguous nested goals, we can ignore the certainty + // returned by `self.try_evaluate_added_goals()`. + if goals.is_empty() { + assert!(matches!(goals_certainty, Certainty::Yes)); + } + ( + Certainty::Yes, + NestedNormalizationGoals( + goals.into_iter().map(|(s, g, _)| (s, g)).collect(), + ), + ) + } + _ => { + let certainty = shallow_certainty.and(goals_certainty); + (certainty, NestedNormalizationGoals::empty()) + } + }; + + if let Certainty::Maybe { + cause: cause @ MaybeCause::Overflow { keep_constraints: false, .. }, + opaque_types_jank, + } = certainty + { + // If we have overflow, it's probable that we're substituting a type + // into itself infinitely and any partial substitutions in the query + // response are probably not useful anyways, so just return an empty + // query response. + // + // This may prevent us from potentially useful inference, e.g. + // 2 candidates, one ambiguous and one overflow, which both + // have the same inference constraints. + // + // Changing this to retain some constraints in the future + // won't be a breaking change, so this is good enough for now. + return Ok(self.make_ambiguous_response_no_constraints(cause, opaque_types_jank)); + } + + let external_constraints = + self.compute_external_query_constraints(certainty, normalization_nested_goals); + let (var_values, mut external_constraints) = + eager_resolve_vars(self.delegate, (self.var_values, external_constraints)); + + // Remove any trivial or duplicated region constraints once we've resolved regions + let mut unique = HashSet::default(); + external_constraints.region_constraints.retain(|outlives| { + outlives.0.as_region().is_none_or(|re| re != outlives.1) && unique.insert(*outlives) + }); + + let canonical = Canonicalizer::canonicalize_response( + self.delegate, + self.max_input_universe, + &mut Default::default(), + Response { + var_values, + certainty, + external_constraints: self.cx().mk_external_constraints(external_constraints), + }, + ); + + // HACK: We bail with overflow if the response would have too many non-region + // inference variables. This tends to only happen if we encounter a lot of + // ambiguous alias types which get replaced with fresh inference variables + // during generalization. This prevents hangs caused by an exponential blowup, + // see tests/ui/traits/next-solver/coherence-alias-hang.rs. + match self.current_goal_kind { + // We don't do so for `NormalizesTo` goals as we erased the expected term and + // bailing with overflow here would prevent us from detecting a type-mismatch, + // causing a coherence error in diesel, see #131969. We still bail with overflow + // when later returning from the parent AliasRelate goal. + CurrentGoalKind::NormalizesTo => {} + CurrentGoalKind::Misc | CurrentGoalKind::CoinductiveTrait => { + let num_non_region_vars = canonical + .variables + .iter() + .filter(|c| !c.is_region() && c.is_existential()) + .count(); + if num_non_region_vars > self.cx().recursion_limit() { + debug!(?num_non_region_vars, "too many inference variables -> overflow"); + return Ok(self.make_ambiguous_response_no_constraints( + MaybeCause::Overflow { + suggest_increasing_limit: true, + keep_constraints: false, + }, + OpaqueTypesJank::AllGood, + )); + } + } + } + + Ok(canonical) + } + + /// Constructs a totally unconstrained, ambiguous response to a goal. + /// + /// Take care when using this, since often it's useful to respond with + /// ambiguity but return constrained variables to guide inference. + pub(in crate::solve) fn make_ambiguous_response_no_constraints( + &self, + cause: MaybeCause, + opaque_types_jank: OpaqueTypesJank, + ) -> CanonicalResponse { + response_no_constraints_raw( + self.cx(), + self.max_input_universe, + self.variables, + Certainty::Maybe { cause, opaque_types_jank }, + ) + } + + /// Computes the region constraints and *new* opaque types registered when + /// proving a goal. + /// + /// If an opaque was already constrained before proving this goal, then the + /// external constraints do not need to record that opaque, since if it is + /// further constrained by inference, that will be passed back in the var + /// values. + #[instrument(level = "trace", skip(self), ret)] + fn compute_external_query_constraints( + &self, + certainty: Certainty, + normalization_nested_goals: NestedNormalizationGoals, + ) -> ExternalConstraintsData { + // We only return region constraints once the certainty is `Yes`. This + // is necessary as we may drop nested goals on ambiguity, which may result + // in unconstrained inference variables in the region constraints. It also + // prevents us from emitting duplicate region constraints, avoiding some + // unnecessary work. This slightly weakens the leak check in case it uses + // region constraints from an ambiguous nested goal. This is tested in both + // `tests/ui/higher-ranked/leak-check/leak-check-in-selection-5-ambig.rs` and + // `tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.rs`. + let region_constraints = if certainty == Certainty::Yes { + self.delegate.make_deduplicated_outlives_constraints() + } else { + Default::default() + }; + + // We only return *newly defined* opaque types from canonical queries. + // + // Constraints for any existing opaque types are already tracked by changes + // to the `var_values`. + let opaque_types = self + .delegate + .clone_opaque_types_added_since(self.initial_opaque_types_storage_num_entries); + + ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals } + } } /// Eagerly replace aliases with inference variables, emitting `AliasRelate` diff --git a/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs index 2675ed0d0da6..4369148baf91 100644 --- a/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs +++ b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs @@ -10,8 +10,8 @@ use derive_where::derive_where; use rustc_type_ir::inherent::*; use rustc_type_ir::{self as ty, Interner}; +use crate::canonical; use crate::delegate::SolverDelegate; -use crate::solve::eval_ctxt::canonical; use crate::solve::{Certainty, Goal, GoalSource, QueryResult, inspect}; /// We need to know whether to build a prove tree while evaluating. We diff --git a/compiler/rustc_next_trait_solver/src/solve/inspect/mod.rs b/compiler/rustc_next_trait_solver/src/solve/inspect/mod.rs index 0d8c00601269..65f32f1947fe 100644 --- a/compiler/rustc_next_trait_solver/src/solve/inspect/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/inspect/mod.rs @@ -2,5 +2,3 @@ pub use rustc_type_ir::solve::inspect::*; mod build; pub(in crate::solve) use build::*; - -pub use crate::solve::eval_ctxt::canonical::instantiate_canonical_state; diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index fb900b592d13..afb86aaf8ab2 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -380,25 +380,6 @@ where } } -fn response_no_constraints_raw( - cx: I, - max_universe: ty::UniverseIndex, - variables: I::CanonicalVarKinds, - certainty: Certainty, -) -> CanonicalResponse { - ty::Canonical { - max_universe, - variables, - value: Response { - var_values: ty::CanonicalVarValues::make_identity(cx, variables), - // FIXME: maybe we should store the "no response" version in cx, like - // we do for cx.types and stuff. - external_constraints: cx.mk_external_constraints(ExternalConstraintsData::default()), - certainty, - }, - } -} - /// The result of evaluating a goal. pub struct GoalEvaluation { /// The goal we've evaluated. This is the input goal, but potentially with its diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs index 289325d70550..aa9dfc9a9a26 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -6,6 +6,7 @@ use rustc_type_ir::search_graph::{self, PathKind}; use rustc_type_ir::solve::{CanonicalInput, Certainty, NoSolution, QueryResult}; use rustc_type_ir::{Interner, TypingMode}; +use crate::canonical::response_no_constraints_raw; use crate::delegate::SolverDelegate; use crate::solve::{ EvalCtxt, FIXPOINT_STEP_LIMIT, has_no_inference_or_external_constraints, inspect, @@ -127,7 +128,7 @@ fn response_no_constraints( input: CanonicalInput, certainty: Certainty, ) -> QueryResult { - Ok(super::response_no_constraints_raw( + Ok(response_no_constraints_raw( cx, input.canonical.max_universe, input.canonical.variables, diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs index 086a7a44786d..c010add0fc50 100644 --- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -18,9 +18,9 @@ use rustc_middle::traits::ObligationCause; use rustc_middle::traits::solve::{Certainty, Goal, GoalSource, NoSolution, QueryResult}; use rustc_middle::ty::{TyCtxt, VisitorResult, try_visit}; use rustc_middle::{bug, ty}; +use rustc_next_trait_solver::canonical::instantiate_canonical_state; use rustc_next_trait_solver::resolve::eager_resolve_vars; -use rustc_next_trait_solver::solve::inspect::{self, instantiate_canonical_state}; -use rustc_next_trait_solver::solve::{MaybeCause, SolverDelegateEvalExt as _}; +use rustc_next_trait_solver::solve::{MaybeCause, SolverDelegateEvalExt as _, inspect}; use rustc_span::Span; use tracing::instrument; From b83c0f0e94592d1a7eae1124e4e951f331ccf6c8 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 16 Sep 2025 15:30:25 +0200 Subject: [PATCH 644/682] canonical: yeet `EvalCtxt`, mk `Canonicalizer` private --- .../src/canonical/canonicalizer.rs | 7 +- .../src/canonical/mod.rs | 443 +++++++++--------- .../src/solve/eval_ctxt/mod.rs | 18 +- 3 files changed, 245 insertions(+), 223 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs index 4b4ec4956eb8..b25671d676b9 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs @@ -57,7 +57,7 @@ enum CanonicalizeMode { }, } -pub struct Canonicalizer<'a, D: SolverDelegate, I: Interner> { +pub(super) struct Canonicalizer<'a, D: SolverDelegate, I: Interner> { delegate: &'a D, // Immutable field. @@ -83,7 +83,7 @@ pub struct Canonicalizer<'a, D: SolverDelegate, I: Interner> { } impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { - pub fn canonicalize_response>( + pub(super) fn canonicalize_response>( delegate: &'a D, max_input_universe: ty::UniverseIndex, variables: &'a mut Vec, @@ -112,7 +112,6 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { let (max_universe, variables) = canonicalizer.finalize(); Canonical { max_universe, variables, value } } - fn canonicalize_param_env( delegate: &'a D, variables: &'a mut Vec, @@ -195,7 +194,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { /// /// We want to keep the option of canonicalizing `'static` to an existential /// variable in the future by changing the way we detect global where-bounds. - pub fn canonicalize_input>( + pub(super) fn canonicalize_input>( delegate: &'a D, variables: &'a mut Vec, input: QueryInput, diff --git a/compiler/rustc_next_trait_solver/src/canonical/mod.rs b/compiler/rustc_next_trait_solver/src/canonical/mod.rs index 35881acf0c75..e3520e238ed3 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/mod.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/mod.rs @@ -24,7 +24,7 @@ use tracing::instrument; use crate::delegate::SolverDelegate; use crate::resolve::eager_resolve_vars; use crate::solve::{ - CanonicalInput, CanonicalResponse, Certainty, EvalCtxt, ExternalConstraintsData, Goal, + CanonicalInput, CanonicalResponse, Certainty, ExternalConstraintsData, Goal, NestedNormalizationGoals, PredefinedOpaquesData, QueryInput, Response, inspect, }; @@ -46,226 +46,248 @@ impl ResponseT for inspect::State { } } -impl EvalCtxt<'_, D> +/// Canonicalizes the goal remembering the original values +/// for each bound variable. +/// +/// This expects `goal` and `opaque_types` to be eager resolved. +pub(super) fn canonicalize_goal( + delegate: &D, + goal: Goal, + opaque_types: Vec<(ty::OpaqueTypeKey, I::Ty)>, +) -> (Vec, CanonicalInput) where D: SolverDelegate, I: Interner, { - /// Canonicalizes the goal remembering the original values - /// for each bound variable. - /// - /// This expects `goal` and `opaque_types` to be eager resolved. - pub(super) fn canonicalize_goal( - delegate: &D, - goal: Goal, - opaque_types: Vec<(ty::OpaqueTypeKey, I::Ty)>, - ) -> (Vec, CanonicalInput) { - let mut orig_values = Default::default(); - let canonical = Canonicalizer::canonicalize_input( - delegate, - &mut orig_values, - QueryInput { - goal, - predefined_opaques_in_body: delegate - .cx() - .mk_predefined_opaques_in_body(PredefinedOpaquesData { opaque_types }), - }, - ); - let query_input = - ty::CanonicalQueryInput { canonical, typing_mode: delegate.typing_mode() }; - (orig_values, query_input) + let mut orig_values = Default::default(); + let canonical = Canonicalizer::canonicalize_input( + delegate, + &mut orig_values, + QueryInput { + goal, + predefined_opaques_in_body: delegate + .cx() + .mk_predefined_opaques_in_body(PredefinedOpaquesData { opaque_types }), + }, + ); + let query_input = ty::CanonicalQueryInput { canonical, typing_mode: delegate.typing_mode() }; + (orig_values, query_input) +} + +pub(super) fn canonicalize_response( + delegate: &D, + max_input_universe: ty::UniverseIndex, + value: T, +) -> ty::Canonical +where + D: SolverDelegate, + I: Interner, + T: TypeFoldable, +{ + let mut orig_values = Default::default(); + let canonical = + Canonicalizer::canonicalize_response(delegate, max_input_universe, &mut orig_values, value); + canonical +} + +/// After calling a canonical query, we apply the constraints returned +/// by the query using this function. +/// +/// This happens in three steps: +/// - we instantiate the bound variables of the query response +/// - we unify the `var_values` of the response with the `original_values` +/// - we apply the `external_constraints` returned by the query, returning +/// the `normalization_nested_goals` +pub(super) fn instantiate_and_apply_query_response( + delegate: &D, + param_env: I::ParamEnv, + original_values: &[I::GenericArg], + response: CanonicalResponse, + span: I::Span, +) -> (NestedNormalizationGoals, Certainty) +where + D: SolverDelegate, + I: Interner, +{ + let instantiation = + compute_query_response_instantiation_values(delegate, &original_values, &response, span); + + let Response { var_values, external_constraints, certainty } = + delegate.instantiate_canonical(response, instantiation); + + unify_query_var_values(delegate, param_env, &original_values, var_values, span); + + let ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals } = + &*external_constraints; + + register_region_constraints(delegate, region_constraints, span); + register_new_opaque_types(delegate, opaque_types, span); + + (normalization_nested_goals.clone(), certainty) +} + +/// This returns the canonical variable values to instantiate the bound variables of +/// the canonical response. This depends on the `original_values` for the +/// bound variables. +fn compute_query_response_instantiation_values( + delegate: &D, + original_values: &[I::GenericArg], + response: &Canonical, + span: I::Span, +) -> CanonicalVarValues +where + D: SolverDelegate, + I: Interner, + T: ResponseT, +{ + // FIXME: Longterm canonical queries should deal with all placeholders + // created inside of the query directly instead of returning them to the + // caller. + let prev_universe = delegate.universe(); + let universes_created_in_query = response.max_universe.index(); + for _ in 0..universes_created_in_query { + delegate.create_next_universe(); } - /// After calling a canonical query, we apply the constraints returned - /// by the query using this function. - /// - /// This happens in three steps: - /// - we instantiate the bound variables of the query response - /// - we unify the `var_values` of the response with the `original_values` - /// - we apply the `external_constraints` returned by the query, returning - /// the `normalization_nested_goals` - pub(super) fn instantiate_and_apply_query_response( - delegate: &D, - param_env: I::ParamEnv, - original_values: &[I::GenericArg], - response: CanonicalResponse, - span: I::Span, - ) -> (NestedNormalizationGoals, Certainty) { - let instantiation = Self::compute_query_response_instantiation_values( - delegate, - &original_values, - &response, - span, - ); + let var_values = response.value.var_values(); + assert_eq!(original_values.len(), var_values.len()); - let Response { var_values, external_constraints, certainty } = - delegate.instantiate_canonical(response, instantiation); - - Self::unify_query_var_values(delegate, param_env, &original_values, var_values, span); - - let ExternalConstraintsData { - region_constraints, - opaque_types, - normalization_nested_goals, - } = &*external_constraints; - - Self::register_region_constraints(delegate, region_constraints, span); - Self::register_new_opaque_types(delegate, opaque_types, span); - - (normalization_nested_goals.clone(), certainty) - } - - /// This returns the canonical variable values to instantiate the bound variables of - /// the canonical response. This depends on the `original_values` for the - /// bound variables. - fn compute_query_response_instantiation_values>( - delegate: &D, - original_values: &[I::GenericArg], - response: &Canonical, - span: I::Span, - ) -> CanonicalVarValues { - // FIXME: Longterm canonical queries should deal with all placeholders - // created inside of the query directly instead of returning them to the - // caller. - let prev_universe = delegate.universe(); - let universes_created_in_query = response.max_universe.index(); - for _ in 0..universes_created_in_query { - delegate.create_next_universe(); - } - - let var_values = response.value.var_values(); - assert_eq!(original_values.len(), var_values.len()); - - // If the query did not make progress with constraining inference variables, - // we would normally create a new inference variables for bound existential variables - // only then unify this new inference variable with the inference variable from - // the input. - // - // We therefore instantiate the existential variable in the canonical response with the - // inference variable of the input right away, which is more performant. - let mut opt_values = IndexVec::from_elem_n(None, response.variables.len()); - for (original_value, result_value) in - iter::zip(original_values, var_values.var_values.iter()) - { - match result_value.kind() { - ty::GenericArgKind::Type(t) => { - // We disable the instantiation guess for inference variables - // and only use it for placeholders. We need to handle the - // `sub_root` of type inference variables which would make this - // more involved. They are also a lot rarer than region variables. - if let ty::Bound(debruijn, b) = t.kind() - && !matches!( - response.variables.get(b.var().as_usize()).unwrap(), - CanonicalVarKind::Ty { .. } - ) - { - assert_eq!(debruijn, ty::INNERMOST); - opt_values[b.var()] = Some(*original_value); - } + // If the query did not make progress with constraining inference variables, + // we would normally create a new inference variables for bound existential variables + // only then unify this new inference variable with the inference variable from + // the input. + // + // We therefore instantiate the existential variable in the canonical response with the + // inference variable of the input right away, which is more performant. + let mut opt_values = IndexVec::from_elem_n(None, response.variables.len()); + for (original_value, result_value) in iter::zip(original_values, var_values.var_values.iter()) { + match result_value.kind() { + ty::GenericArgKind::Type(t) => { + // We disable the instantiation guess for inference variables + // and only use it for placeholders. We need to handle the + // `sub_root` of type inference variables which would make this + // more involved. They are also a lot rarer than region variables. + if let ty::Bound(debruijn, b) = t.kind() + && !matches!( + response.variables.get(b.var().as_usize()).unwrap(), + CanonicalVarKind::Ty { .. } + ) + { + assert_eq!(debruijn, ty::INNERMOST); + opt_values[b.var()] = Some(*original_value); } - ty::GenericArgKind::Lifetime(r) => { - if let ty::ReBound(debruijn, br) = r.kind() { - assert_eq!(debruijn, ty::INNERMOST); - opt_values[br.var()] = Some(*original_value); - } + } + ty::GenericArgKind::Lifetime(r) => { + if let ty::ReBound(debruijn, br) = r.kind() { + assert_eq!(debruijn, ty::INNERMOST); + opt_values[br.var()] = Some(*original_value); } - ty::GenericArgKind::Const(c) => { - if let ty::ConstKind::Bound(debruijn, bv) = c.kind() { - assert_eq!(debruijn, ty::INNERMOST); - opt_values[bv.var()] = Some(*original_value); - } + } + ty::GenericArgKind::Const(c) => { + if let ty::ConstKind::Bound(debruijn, bv) = c.kind() { + assert_eq!(debruijn, ty::INNERMOST); + opt_values[bv.var()] = Some(*original_value); } } } - CanonicalVarValues::instantiate(delegate.cx(), response.variables, |var_values, kind| { - if kind.universe() != ty::UniverseIndex::ROOT { - // A variable from inside a binder of the query. While ideally these shouldn't - // exist at all (see the FIXME at the start of this method), we have to deal with - // them for now. - delegate.instantiate_canonical_var(kind, span, &var_values, |idx| { - prev_universe + idx.index() - }) - } else if kind.is_existential() { - // As an optimization we sometimes avoid creating a new inference variable here. - // - // All new inference variables we create start out in the current universe of the caller. - // This is conceptually wrong as these inference variables would be able to name - // more placeholders then they should be able to. However the inference variables have - // to "come from somewhere", so by equating them with the original values of the caller - // later on, we pull them down into their correct universe again. - if let Some(v) = opt_values[ty::BoundVar::from_usize(var_values.len())] { - v - } else { - delegate.instantiate_canonical_var(kind, span, &var_values, |_| prev_universe) - } - } else { - // For placeholders which were already part of the input, we simply map this - // universal bound variable back the placeholder of the input. - original_values[kind.expect_placeholder_index()] - } - }) } - - /// Unify the `original_values` with the `var_values` returned by the canonical query.. - /// - /// This assumes that this unification will always succeed. This is the case when - /// applying a query response right away. However, calling a canonical query, doing any - /// other kind of trait solving, and only then instantiating the result of the query - /// can cause the instantiation to fail. This is not supported and we ICE in this case. - /// - /// We always structurally instantiate aliases. Relating aliases needs to be different - /// depending on whether the alias is *rigid* or not. We're only really able to tell - /// whether an alias is rigid by using the trait solver. When instantiating a response - /// from the solver we assume that the solver correctly handled aliases and therefore - /// always relate them structurally here. - #[instrument(level = "trace", skip(delegate))] - fn unify_query_var_values( - delegate: &D, - param_env: I::ParamEnv, - original_values: &[I::GenericArg], - var_values: CanonicalVarValues, - span: I::Span, - ) { - assert_eq!(original_values.len(), var_values.len()); - - for (&orig, response) in iter::zip(original_values, var_values.var_values.iter()) { - let goals = - delegate.eq_structurally_relating_aliases(param_env, orig, response, span).unwrap(); - assert!(goals.is_empty()); - } - } - - fn register_region_constraints( - delegate: &D, - outlives: &[ty::OutlivesPredicate], - span: I::Span, - ) { - for &ty::OutlivesPredicate(lhs, rhs) in outlives { - match lhs.kind() { - ty::GenericArgKind::Lifetime(lhs) => delegate.sub_regions(rhs, lhs, span), - ty::GenericArgKind::Type(lhs) => delegate.register_ty_outlives(lhs, rhs, span), - ty::GenericArgKind::Const(_) => panic!("const outlives: {lhs:?}: {rhs:?}"), - } - } - } - - fn register_new_opaque_types( - delegate: &D, - opaque_types: &[(ty::OpaqueTypeKey, I::Ty)], - span: I::Span, - ) { - for &(key, ty) in opaque_types { - let prev = delegate.register_hidden_type_in_storage(key, ty, span); - // We eagerly resolve inference variables when computing the query response. - // This can cause previously distinct opaque type keys to now be structurally equal. + CanonicalVarValues::instantiate(delegate.cx(), response.variables, |var_values, kind| { + if kind.universe() != ty::UniverseIndex::ROOT { + // A variable from inside a binder of the query. While ideally these shouldn't + // exist at all (see the FIXME at the start of this method), we have to deal with + // them for now. + delegate.instantiate_canonical_var(kind, span, &var_values, |idx| { + prev_universe + idx.index() + }) + } else if kind.is_existential() { + // As an optimization we sometimes avoid creating a new inference variable here. // - // To handle this, we store any duplicate entries in a separate list to check them - // at the end of typeck/borrowck. We could alternatively eagerly equate the hidden - // types here. However, doing so is difficult as it may result in nested goals and - // any errors may make it harder to track the control flow for diagnostics. - if let Some(prev) = prev { - delegate.add_duplicate_opaque_type(key, prev, span); + // All new inference variables we create start out in the current universe of the caller. + // This is conceptually wrong as these inference variables would be able to name + // more placeholders then they should be able to. However the inference variables have + // to "come from somewhere", so by equating them with the original values of the caller + // later on, we pull them down into their correct universe again. + if let Some(v) = opt_values[ty::BoundVar::from_usize(var_values.len())] { + v + } else { + delegate.instantiate_canonical_var(kind, span, &var_values, |_| prev_universe) } + } else { + // For placeholders which were already part of the input, we simply map this + // universal bound variable back the placeholder of the input. + original_values[kind.expect_placeholder_index()] + } + }) +} + +/// Unify the `original_values` with the `var_values` returned by the canonical query.. +/// +/// This assumes that this unification will always succeed. This is the case when +/// applying a query response right away. However, calling a canonical query, doing any +/// other kind of trait solving, and only then instantiating the result of the query +/// can cause the instantiation to fail. This is not supported and we ICE in this case. +/// +/// We always structurally instantiate aliases. Relating aliases needs to be different +/// depending on whether the alias is *rigid* or not. We're only really able to tell +/// whether an alias is rigid by using the trait solver. When instantiating a response +/// from the solver we assume that the solver correctly handled aliases and therefore +/// always relate them structurally here. +#[instrument(level = "trace", skip(delegate))] +fn unify_query_var_values( + delegate: &D, + param_env: I::ParamEnv, + original_values: &[I::GenericArg], + var_values: CanonicalVarValues, + span: I::Span, +) where + D: SolverDelegate, + I: Interner, +{ + assert_eq!(original_values.len(), var_values.len()); + + for (&orig, response) in iter::zip(original_values, var_values.var_values.iter()) { + let goals = + delegate.eq_structurally_relating_aliases(param_env, orig, response, span).unwrap(); + assert!(goals.is_empty()); + } +} + +fn register_region_constraints( + delegate: &D, + outlives: &[ty::OutlivesPredicate], + span: I::Span, +) where + D: SolverDelegate, + I: Interner, +{ + for &ty::OutlivesPredicate(lhs, rhs) in outlives { + match lhs.kind() { + ty::GenericArgKind::Lifetime(lhs) => delegate.sub_regions(rhs, lhs, span), + ty::GenericArgKind::Type(lhs) => delegate.register_ty_outlives(lhs, rhs, span), + ty::GenericArgKind::Const(_) => panic!("const outlives: {lhs:?}: {rhs:?}"), + } + } +} + +fn register_new_opaque_types( + delegate: &D, + opaque_types: &[(ty::OpaqueTypeKey, I::Ty)], + span: I::Span, +) where + D: SolverDelegate, + I: Interner, +{ + for &(key, ty) in opaque_types { + let prev = delegate.register_hidden_type_in_storage(key, ty, span); + // We eagerly resolve inference variables when computing the query response. + // This can cause previously distinct opaque type keys to now be structurally equal. + // + // To handle this, we store any duplicate entries in a separate list to check them + // at the end of typeck/borrowck. We could alternatively eagerly equate the hidden + // types here. However, doing so is difficult as it may result in nested goals and + // any errors may make it harder to track the control flow for diagnostics. + if let Some(prev) = prev { + delegate.add_duplicate_opaque_type(key, prev, span); } } } @@ -274,7 +296,7 @@ where /// evaluating a goal. The `var_values` not only include the bound variables /// of the query input, but also contain all unconstrained inference vars /// created while evaluating this goal. -pub fn make_canonical_state( +pub fn make_canonical_state( delegate: &D, var_values: &[I::GenericArg], max_input_universe: ty::UniverseIndex, @@ -293,7 +315,7 @@ where // FIXME: needs to be pub to be accessed by downstream // `rustc_trait_selection::solve::inspect::analyse`. -pub fn instantiate_canonical_state>( +pub fn instantiate_canonical_state( delegate: &D, span: I::Span, param_env: I::ParamEnv, @@ -303,6 +325,7 @@ pub fn instantiate_canonical_state>( where D: SolverDelegate, I: Interner, + T: TypeFoldable, { // In case any fresh inference variables have been created between `state` // and the previous instantiation, extend `orig_values` for it. @@ -313,11 +336,11 @@ where ); let instantiation = - EvalCtxt::compute_query_response_instantiation_values(delegate, orig_values, &state, span); + compute_query_response_instantiation_values(delegate, orig_values, &state, span); let inspect::State { var_values, data } = delegate.instantiate_canonical(state, instantiation); - EvalCtxt::unify_query_var_values(delegate, param_env, orig_values, var_values, span); + unify_query_var_values(delegate, param_env, orig_values, var_values, span); data } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 8f9b68dbac4f..bb86357a85f8 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -17,8 +17,10 @@ use rustc_type_ir::{ use tracing::{debug, instrument, trace}; use super::has_only_region_constraints; -use crate::canonical::canonicalizer::Canonicalizer; -use crate::canonical::response_no_constraints_raw; +use crate::canonical::{ + canonicalize_goal, canonicalize_response, instantiate_and_apply_query_response, + response_no_constraints_raw, +}; use crate::coherence; use crate::delegate::SolverDelegate; use crate::placeholder::BoundVarReplacer; @@ -465,8 +467,7 @@ where let opaque_types = self.delegate.clone_opaque_types_lookup_table(); let (goal, opaque_types) = eager_resolve_vars(self.delegate, (goal, opaque_types)); - let (orig_values, canonical_goal) = - Self::canonicalize_goal(self.delegate, goal, opaque_types); + let (orig_values, canonical_goal) = canonicalize_goal(self.delegate, goal, opaque_types); let canonical_result = self.search_graph.evaluate_goal( self.cx(), canonical_goal, @@ -481,7 +482,7 @@ where let has_changed = if !has_only_region_constraints(response) { HasChanged::Yes } else { HasChanged::No }; - let (normalization_nested_goals, certainty) = Self::instantiate_and_apply_query_response( + let (normalization_nested_goals, certainty) = instantiate_and_apply_query_response( self.delegate, goal.param_env, &orig_values, @@ -1319,10 +1320,9 @@ where outlives.0.as_region().is_none_or(|re| re != outlives.1) && unique.insert(*outlives) }); - let canonical = Canonicalizer::canonicalize_response( + let canonical = canonicalize_response( self.delegate, self.max_input_universe, - &mut Default::default(), Response { var_values, certainty, @@ -1557,7 +1557,7 @@ pub(super) fn evaluate_root_goal_for_proof_tree, let opaque_types = delegate.clone_opaque_types_lookup_table(); let (goal, opaque_types) = eager_resolve_vars(delegate, (goal, opaque_types)); - let (orig_values, canonical_goal) = EvalCtxt::canonicalize_goal(delegate, goal, opaque_types); + let (orig_values, canonical_goal) = canonicalize_goal(delegate, goal, opaque_types); let (canonical_result, final_revision) = delegate.cx().evaluate_root_goal_for_proof_tree_raw(canonical_goal); @@ -1574,7 +1574,7 @@ pub(super) fn evaluate_root_goal_for_proof_tree, Ok(response) => response, }; - let (normalization_nested_goals, _certainty) = EvalCtxt::instantiate_and_apply_query_response( + let (normalization_nested_goals, _certainty) = instantiate_and_apply_query_response( delegate, goal.param_env, &proof_tree.orig_values, From 47d6936c7d37898a2de5549dcba8b50b25be21dd Mon Sep 17 00:00:00 2001 From: Oblarg Date: Thu, 18 Sep 2025 18:15:19 -0400 Subject: [PATCH 645/682] fix negative const generic integer literals --- .../rust-analyzer/crates/hir-ty/src/lower.rs | 23 +++++++++++++++++++ .../crates/ide/src/hover/tests.rs | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 79f78c545e59..4d5172fd4f24 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -299,6 +299,29 @@ impl<'a> TyLoweringContext<'a> { const_type, self.resolver.krate(), ), + hir_def::hir::Expr::UnaryOp { expr: inner_expr, op: hir_def::hir::UnaryOp::Neg } => { + if let hir_def::hir::Expr::Literal(literal) = &self.store[*inner_expr] { + // Only handle negation for signed integers and floats + match literal { + hir_def::hir::Literal::Int(_, _) | hir_def::hir::Literal::Float(_, _) => { + if let Some(negated_literal) = literal.clone().negate() { + intern_const_ref( + self.db, + &negated_literal.into(), + const_type, + self.resolver.krate(), + ) + } else { + unknown_const(const_type) + } + } + // For unsigned integers, chars, bools, etc., negation is not meaningful + _ => unknown_const(const_type), + } + } else { + unknown_const(const_type) + } + } _ => unknown_const(const_type), } } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 45aec3877475..1ea11a215f83 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -4738,7 +4738,7 @@ fn main() { *value* ```rust - let value: Const<_> + let value: Const<-1> ``` --- From 934ee043feb6601f9dfb5be173a190a921e6d9a9 Mon Sep 17 00:00:00 2001 From: Sidney Cammeresi Date: Sun, 14 Sep 2025 11:14:50 -0700 Subject: [PATCH 646/682] Plumb Allocator generic into `std::vec::PeekMut` --- library/alloc/src/vec/mod.rs | 54 +++++++++++++++---------------- library/alloc/src/vec/peek_mut.rs | 22 ++++++++----- library/alloctests/tests/vec.rs | 19 +++++------ 3 files changed, 51 insertions(+), 44 deletions(-) diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 2e40227a058a..1b80b43abc35 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -760,33 +760,6 @@ impl Vec { unsafe { Self::from_parts_in(ptr, length, capacity, Global) } } - /// Returns a mutable reference to the last item in the vector, or - /// `None` if it is empty. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// #![feature(vec_peek_mut)] - /// let mut vec = Vec::new(); - /// assert!(vec.peek_mut().is_none()); - /// - /// vec.push(1); - /// vec.push(5); - /// vec.push(2); - /// assert_eq!(vec.last(), Some(&2)); - /// if let Some(mut val) = vec.peek_mut() { - /// *val = 0; - /// } - /// assert_eq!(vec.last(), Some(&0)); - /// ``` - #[inline] - #[unstable(feature = "vec_peek_mut", issue = "122742")] - pub fn peek_mut(&mut self) -> Option> { - PeekMut::new(self) - } - /// Decomposes a `Vec` into its raw components: `(pointer, length, capacity)`. /// /// Returns the raw pointer to the underlying data, the length of @@ -2747,6 +2720,33 @@ impl Vec { if predicate(last) { self.pop() } else { None } } + /// Returns a mutable reference to the last item in the vector, or + /// `None` if it is empty. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(vec_peek_mut)] + /// let mut vec = Vec::new(); + /// assert!(vec.peek_mut().is_none()); + /// + /// vec.push(1); + /// vec.push(5); + /// vec.push(2); + /// assert_eq!(vec.last(), Some(&2)); + /// if let Some(mut val) = vec.peek_mut() { + /// *val = 0; + /// } + /// assert_eq!(vec.last(), Some(&0)); + /// ``` + #[inline] + #[unstable(feature = "vec_peek_mut", issue = "122742")] + pub fn peek_mut(&mut self) -> Option> { + PeekMut::new(self) + } + /// Moves all the elements of `other` into `self`, leaving `other` empty. /// /// # Panics diff --git a/library/alloc/src/vec/peek_mut.rs b/library/alloc/src/vec/peek_mut.rs index caeaf2799d73..979bcaa1111d 100644 --- a/library/alloc/src/vec/peek_mut.rs +++ b/library/alloc/src/vec/peek_mut.rs @@ -1,6 +1,7 @@ use core::ops::{Deref, DerefMut}; use super::Vec; +use crate::alloc::{Allocator, Global}; use crate::fmt; /// Structure wrapping a mutable reference to the last item in a @@ -11,19 +12,23 @@ use crate::fmt; /// /// [`peek_mut`]: Vec::peek_mut #[unstable(feature = "vec_peek_mut", issue = "122742")] -pub struct PeekMut<'a, T> { - vec: &'a mut Vec, +pub struct PeekMut< + 'a, + T, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global, +> { + vec: &'a mut Vec, } #[unstable(feature = "vec_peek_mut", issue = "122742")] -impl fmt::Debug for PeekMut<'_, T> { +impl fmt::Debug for PeekMut<'_, T, A> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("PeekMut").field(self.deref()).finish() } } -impl<'a, T> PeekMut<'a, T> { - pub(crate) fn new(vec: &'a mut Vec) -> Option { +impl<'a, T, A: Allocator> PeekMut<'a, T, A> { + pub(super) fn new(vec: &'a mut Vec) -> Option { if vec.is_empty() { None } else { Some(Self { vec }) } } @@ -36,17 +41,18 @@ impl<'a, T> PeekMut<'a, T> { } #[unstable(feature = "vec_peek_mut", issue = "122742")] -impl<'a, T> Deref for PeekMut<'a, T> { +impl<'a, T, A: Allocator> Deref for PeekMut<'a, T, A> { type Target = T; fn deref(&self) -> &Self::Target { + let idx = self.vec.len() - 1; // SAFETY: PeekMut is only constructed if the vec is non-empty - unsafe { self.vec.get_unchecked(self.vec.len() - 1) } + unsafe { self.vec.get_unchecked(idx) } } } #[unstable(feature = "vec_peek_mut", issue = "122742")] -impl<'a, T> DerefMut for PeekMut<'a, T> { +impl<'a, T, A: Allocator> DerefMut for PeekMut<'a, T, A> { fn deref_mut(&mut self) -> &mut Self::Target { let idx = self.vec.len() - 1; // SAFETY: PeekMut is only constructed if the vec is non-empty diff --git a/library/alloctests/tests/vec.rs b/library/alloctests/tests/vec.rs index 404eb49e1ea9..33a34daccbfd 100644 --- a/library/alloctests/tests/vec.rs +++ b/library/alloctests/tests/vec.rs @@ -2643,15 +2643,16 @@ fn test_peek_mut() { assert!(vec.peek_mut().is_none()); vec.push(1); vec.push(2); - if let Some(mut p) = vec.peek_mut() { - assert_eq!(*p, 2); - *p = 0; - assert_eq!(*p, 0); - PeekMut::pop(p); - assert_eq!(vec.len(), 1); - } else { - unreachable!() - } + let mut p = vec.peek_mut().unwrap(); + assert_eq!(*p, 2); + *p = 0; + assert_eq!(*p, 0); + drop(p); + assert_eq!(vec, vec![1, 0]); + let p = vec.peek_mut().unwrap(); + let p = PeekMut::pop(p); + assert_eq!(p, 0); + assert_eq!(vec, vec![1]); } /// This assortment of tests, in combination with miri, verifies we handle UB on fishy arguments From 7ce81d145359aa230f8fc2d7de12650091508329 Mon Sep 17 00:00:00 2001 From: beepster4096 <19316085+beepster4096@users.noreply.github.com> Date: Tue, 26 Aug 2025 01:12:20 -0700 Subject: [PATCH 647/682] deref related cleanups in ref_prop --- compiler/rustc_mir_transform/src/ref_prop.rs | 33 ++++++++------------ 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_mir_transform/src/ref_prop.rs b/compiler/rustc_mir_transform/src/ref_prop.rs index 6f61215cee2d..b9d6e74ecae8 100644 --- a/compiler/rustc_mir_transform/src/ref_prop.rs +++ b/compiler/rustc_mir_transform/src/ref_prop.rs @@ -195,10 +195,10 @@ fn compute_replacement<'tcx>( // including DEF. This violates the DEF dominates USE condition, and so is impossible. let is_constant_place = |place: Place<'_>| { // We only allow `Deref` as the first projection, to avoid surprises. - if place.projection.first() == Some(&PlaceElem::Deref) { + if let Some((&PlaceElem::Deref, rest)) = place.projection.split_first() { // `place == (*some_local).xxx`, it is constant only if `some_local` is constant. // We approximate constness using SSAness. - ssa.is_ssa(place.local) && place.projection[1..].iter().all(PlaceElem::is_stable_offset) + ssa.is_ssa(place.local) && rest.iter().all(PlaceElem::is_stable_offset) } else { storage_live.has_single_storage(place.local) && place.projection[..].iter().all(PlaceElem::is_stable_offset) @@ -206,7 +206,7 @@ fn compute_replacement<'tcx>( }; let mut can_perform_opt = |target: Place<'tcx>, loc: Location| { - if target.projection.first() == Some(&PlaceElem::Deref) { + if target.is_indirect_first_projection() { // We are creating a reborrow. As `place.local` is a reference, removing the storage // statements should not make it much harder for LLVM to optimize. storage_to_remove.insert(target.local); @@ -266,7 +266,7 @@ fn compute_replacement<'tcx>( Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) => { let mut place = *place; // Try to see through `place` in order to collapse reborrow chains. - if place.projection.first() == Some(&PlaceElem::Deref) + if let Some((&PlaceElem::Deref, rest)) = place.projection.split_first() && let Value::Pointer(target, inner_needs_unique) = targets[place.local] // Only see through immutable reference and pointers, as we do not know yet if // mutable references are fully replaced. @@ -274,7 +274,7 @@ fn compute_replacement<'tcx>( // Only collapse chain if the pointee is definitely live. && can_perform_opt(target, location) { - place = target.project_deeper(&place.projection[1..], tcx); + place = target.project_deeper(rest, tcx); } assert_ne!(place.local, local); if is_constant_place(place) { @@ -323,7 +323,7 @@ fn compute_replacement<'tcx>( return; } - if place.projection.first() != Some(&PlaceElem::Deref) { + if !place.is_indirect_first_projection() { // This is not a dereference, nothing to do. return; } @@ -392,20 +392,15 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> { } fn visit_var_debug_info(&mut self, debuginfo: &mut VarDebugInfo<'tcx>) { - // If the debuginfo is a pointer to another place: - // - if it's a reborrow, see through it; - // - if it's a direct borrow, increase `debuginfo.references`. + // If the debuginfo is a pointer to another place + // and it's a reborrow: see through it while let VarDebugInfoContents::Place(ref mut place) = debuginfo.value && place.projection.is_empty() && let Value::Pointer(target, _) = self.targets[place.local] - && target.projection.iter().all(|p| p.can_use_in_debuginfo()) + && let &[PlaceElem::Deref] = &target.projection[..] { - if let Some((&PlaceElem::Deref, rest)) = target.projection.split_last() { - *place = Place::from(target.local).project_deeper(rest, self.tcx); - self.any_replacement = true; - } else { - break; - } + *place = Place::from(target.local); + self.any_replacement = true; } // Simplify eventual projections left inside `debuginfo`. @@ -414,9 +409,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> { fn visit_place(&mut self, place: &mut Place<'tcx>, ctxt: PlaceContext, loc: Location) { loop { - if place.projection.first() != Some(&PlaceElem::Deref) { - return; - } + let Some((&PlaceElem::Deref, rest)) = place.projection.split_first() else { return }; let Value::Pointer(target, _) = self.targets[place.local] else { return }; @@ -432,7 +425,7 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> { return; } - *place = target.project_deeper(&place.projection[1..], self.tcx); + *place = target.project_deeper(rest, self.tcx); self.any_replacement = true; } } From 6870e24fdbd9dc1770101457ebdcf9d5837f0b23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Fri, 19 Sep 2025 08:08:23 +0300 Subject: [PATCH 648/682] Set WithCachedTypeInfo::stable_hash when in-tree --- src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs | 2 ++ .../rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs | 2 ++ src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs | 2 ++ 3 files changed, 6 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs index 0b3582051bc0..7ebefa76ed02 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs @@ -36,6 +36,8 @@ impl<'db> Const<'db> { internee: kind, flags: flags.flags, outer_exclusive_binder: flags.outer_exclusive_binder, + #[cfg(feature = "in-rust-tree")] + stable_hash: ena::fingerprint::Fingerprint::ZERO, }; Const::new_(interner.db(), InternedWrapperNoDebug(cached)) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs index 99b1354b6335..86545415009a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs @@ -227,6 +227,8 @@ impl<'db> Predicate<'db> { internee: kind, flags: flags.flags, outer_exclusive_binder: flags.outer_exclusive_binder, + #[cfg(feature = "in-rust-tree")] + stable_hash: ena::fingerprint::Fingerprint::ZERO, }; Predicate::new_(interner.db(), InternedWrapperNoDebug(cached)) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index 70139e866694..c7a747ade3e7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -60,6 +60,8 @@ impl<'db> Ty<'db> { internee: kind, flags: flags.flags, outer_exclusive_binder: flags.outer_exclusive_binder, + #[cfg(feature = "in-rust-tree")] + stable_hash: ena::fingerprint::Fingerprint::ZERO, }; Ty::new_(interner.db(), InternedWrapperNoDebug(cached)) } From f69da330493233c9f5485919cc7c421c35a83bbc Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 19 Sep 2025 13:39:45 +0800 Subject: [PATCH 649/682] Add `#[track_caller]` for check_assist_by_label --- src/tools/rust-analyzer/crates/ide-assists/src/tests.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs index 3e422ae1b8f6..c0637a7470f3 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs @@ -180,6 +180,7 @@ pub(crate) fn check_assist_import_one( // There is no way to choose what assist within a group you want to test against, // so this is here to allow you choose. +#[track_caller] pub(crate) fn check_assist_by_label( assist: Handler, #[rust_analyzer::rust_fixture] ra_fixture_before: &str, From 1db74d4845ed18b63e0c683f7a13714c3653a268 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 19 Sep 2025 09:02:03 +0200 Subject: [PATCH 650/682] fix miri bootstrap build --- src/tools/miri/src/clock.rs | 16 +++++++++++++++- src/tools/miri/src/lib.rs | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/tools/miri/src/clock.rs b/src/tools/miri/src/clock.rs index 47608f873a48..dbbe741a0714 100644 --- a/src/tools/miri/src/clock.rs +++ b/src/tools/miri/src/clock.rs @@ -46,7 +46,21 @@ impl Instant { InstantKind::Virtual { nanoseconds: earlier }, ) => { let duration = nanoseconds.saturating_sub(earlier); - Duration::from_nanos_u128(duration) + cfg_select! { + bootstrap => { + // `Duration` does not provide a nice constructor from a `u128` of nanoseconds, + // so we have to implement this ourselves. + // It is possible for second to overflow because u64::MAX < (u128::MAX / 1e9). + // It will be saturated to u64::MAX seconds if the value after division exceeds u64::MAX. + let seconds = u64::try_from(duration / 1_000_000_000).unwrap_or(u64::MAX); + // It is impossible for nanosecond to overflow because u32::MAX > 1e9. + let nanosecond = u32::try_from(duration.wrapping_rem(1_000_000_000)).unwrap(); + Duration::new(seconds, nanosecond) + } + _ => { + Duration::from_nanos_u128(duration) + } + } } _ => panic!("all `Instant` must be of the same kind"), } diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index dbc2a99593d2..7f5f25b9f66b 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -18,7 +18,7 @@ #![feature(derive_coerce_pointee)] #![feature(arbitrary_self_types)] #![feature(iter_advance_by)] -#![feature(duration_from_nanos_u128)] +#![cfg_attr(not(bootstrap), feature(duration_from_nanos_u128))] // Configure clippy and other lints #![allow( clippy::collapsible_else_if, From be01d87ba2c3ae88ed8a5f73c7440b02dbf9d80b Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Fri, 19 Sep 2025 10:52:53 +0200 Subject: [PATCH 651/682] Allow running remote-test-server on Apple simulators --- src/tools/remote-test-server/src/main.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/tools/remote-test-server/src/main.rs b/src/tools/remote-test-server/src/main.rs index 67a7ad6f3b45..5ec5e6e28982 100644 --- a/src/tools/remote-test-server/src/main.rs +++ b/src/tools/remote-test-server/src/main.rs @@ -53,6 +53,10 @@ impl Config { batch: false, bind: if cfg!(target_os = "android") || cfg!(windows) { ([0, 0, 0, 0], 12345).into() + } else if cfg!(target_env = "sim") { + // iOS/tvOS/watchOS/visionOS simulators share network device + // with the host machine. + ([127, 0, 0, 1], 12345).into() } else { ([10, 0, 2, 15], 12345).into() }, @@ -262,10 +266,17 @@ fn handle_run(socket: TcpStream, work: &Path, tmp: &Path, lock: &Mutex<()>, conf cmd.args(args); cmd.envs(env); - // On windows, libraries are just searched in the executable directory, - // system directories, PWD, and PATH, in that order. PATH is the only one - // we can change for this. - let library_path = if cfg!(windows) { "PATH" } else { "LD_LIBRARY_PATH" }; + let library_path = if cfg!(windows) { + // On windows, libraries are just searched in the executable directory, + // system directories, PWD, and PATH, in that order. PATH is the only + // one we can change for this. + "PATH" + } else if cfg!(target_vendor = "apple") { + // On Apple platforms, the environment variable is named differently. + "DYLD_LIBRARY_PATH" + } else { + "LD_LIBRARY_PATH" + }; // Support libraries were uploaded to `work` earlier, so make sure that's // in `LD_LIBRARY_PATH`. Also include our own current dir which may have From 09d3120a99fee8d839e502bdb54f8117c0623c5d Mon Sep 17 00:00:00 2001 From: joboet Date: Sun, 14 Sep 2025 15:30:44 +0200 Subject: [PATCH 652/682] std: simplify host lookup --- library/std/src/net/socket_addr.rs | 41 +++++++------ library/std/src/sys/net/connection/sgx.rs | 29 ++-------- .../std/src/sys/net/connection/socket/mod.rs | 58 +++++-------------- .../src/sys/net/connection/socket/tests.rs | 2 +- .../std/src/sys/net/connection/uefi/mod.rs | 22 +------ .../std/src/sys/net/connection/unsupported.rs | 22 +------ library/std/src/sys/net/connection/wasip1.rs | 22 +------ .../std/src/sys/net/connection/xous/dns.rs | 45 +------------- .../std/src/sys/net/connection/xous/mod.rs | 2 +- tests/ui/inference/issue-72616.rs | 1 - tests/ui/inference/issue-72616.stderr | 19 +----- 11 files changed, 51 insertions(+), 212 deletions(-) diff --git a/library/std/src/net/socket_addr.rs b/library/std/src/net/socket_addr.rs index 41e623e79ce2..5b56dd3f7447 100644 --- a/library/std/src/net/socket_addr.rs +++ b/library/std/src/net/socket_addr.rs @@ -6,7 +6,6 @@ mod tests; pub use core::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr}; -use crate::sys::net::LookupHost; use crate::{io, iter, option, slice, vec}; /// A trait for objects which can be converted or resolved to one or more @@ -188,15 +187,9 @@ impl ToSocketAddrs for (Ipv6Addr, u16) { } } -fn resolve_socket_addr(lh: LookupHost) -> io::Result> { - let p = lh.port(); - let v: Vec<_> = lh - .map(|mut a| { - a.set_port(p); - a - }) - .collect(); - Ok(v.into_iter()) +fn lookup_host(host: &str, port: u16) -> io::Result> { + let addrs = crate::sys::net::lookup_host(host, port)?; + Ok(Vec::from_iter(addrs).into_iter()) } #[stable(feature = "rust1", since = "1.0.0")] @@ -205,17 +198,14 @@ impl ToSocketAddrs for (&str, u16) { fn to_socket_addrs(&self) -> io::Result> { let (host, port) = *self; - // try to parse the host as a regular IP address first - if let Ok(addr) = host.parse::() { - let addr = SocketAddrV4::new(addr, port); - return Ok(vec![SocketAddr::V4(addr)].into_iter()); - } - if let Ok(addr) = host.parse::() { - let addr = SocketAddrV6::new(addr, port, 0, 0); - return Ok(vec![SocketAddr::V6(addr)].into_iter()); + // Try to parse the host as a regular IP address first + if let Ok(addr) = host.parse::() { + let addr = SocketAddr::new(addr, port); + return Ok(vec![addr].into_iter()); } - resolve_socket_addr((host, port).try_into()?) + // Otherwise, make the system look it up. + lookup_host(host, port) } } @@ -232,12 +222,21 @@ impl ToSocketAddrs for (String, u16) { impl ToSocketAddrs for str { type Iter = vec::IntoIter; fn to_socket_addrs(&self) -> io::Result> { - // try to parse as a regular SocketAddr first + // Try to parse as a regular SocketAddr first if let Ok(addr) = self.parse() { return Ok(vec![addr].into_iter()); } - resolve_socket_addr(self.try_into()?) + // Otherwise, split the string by ':' and convert the second part to u16... + let Some((host, port_str)) = self.rsplit_once(':') else { + return Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid socket address")); + }; + let Ok(port) = port_str.parse::() else { + return Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid port value")); + }; + + // ... and make the system look up the host. + lookup_host(host, port) } } diff --git a/library/std/src/sys/net/connection/sgx.rs b/library/std/src/sys/net/connection/sgx.rs index 9b54571997d0..8c9c17d3f171 100644 --- a/library/std/src/sys/net/connection/sgx.rs +++ b/library/std/src/sys/net/connection/sgx.rs @@ -499,16 +499,6 @@ impl fmt::Display for NonIpSockAddr { pub struct LookupHost(!); -impl LookupHost { - fn new(host: String) -> io::Result { - Err(io::Error::new(io::ErrorKind::Uncategorized, NonIpSockAddr { host })) - } - - pub fn port(&self) -> u16 { - self.0 - } -} - impl Iterator for LookupHost { type Item = SocketAddr; fn next(&mut self) -> Option { @@ -516,18 +506,9 @@ impl Iterator for LookupHost { } } -impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(v: &str) -> io::Result { - LookupHost::new(v.to_owned()) - } -} - -impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from((host, port): (&'a str, u16)) -> io::Result { - LookupHost::new(format!("{host}:{port}")) - } +pub fn lookup_host(host: &str, port: u16) -> io::Result { + Err(io::Error::new( + io::ErrorKind::Uncategorized, + NonIpSockAddr { host: format!("{host}:{port}") }, + )) } diff --git a/library/std/src/sys/net/connection/socket/mod.rs b/library/std/src/sys/net/connection/socket/mod.rs index 564f2e3a01f3..1dd06e97bbab 100644 --- a/library/std/src/sys/net/connection/socket/mod.rs +++ b/library/std/src/sys/net/connection/socket/mod.rs @@ -258,7 +258,7 @@ fn to_ipv6mr_interface(value: u32) -> crate::ffi::c_uint { } //////////////////////////////////////////////////////////////////////////////// -// get_host_addresses +// lookup_host //////////////////////////////////////////////////////////////////////////////// pub struct LookupHost { @@ -267,12 +267,6 @@ pub struct LookupHost { port: u16, } -impl LookupHost { - pub fn port(&self) -> u16 { - self.port - } -} - impl Iterator for LookupHost { type Item = SocketAddr; fn next(&mut self) -> Option { @@ -281,7 +275,10 @@ impl Iterator for LookupHost { let cur = self.cur.as_ref()?; self.cur = cur.ai_next; match socket_addr_from_c(cur.ai_addr.cast(), cur.ai_addrlen as usize) { - Ok(addr) => return Some(addr), + Ok(mut addr) => { + addr.set_port(self.port); + return Some(addr); + } Err(_) => continue, } } @@ -298,42 +295,17 @@ impl Drop for LookupHost { } } -impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(s: &str) -> io::Result { - macro_rules! try_opt { - ($e:expr, $msg:expr) => { - match $e { - Some(r) => r, - None => return Err(io::const_error!(io::ErrorKind::InvalidInput, $msg)), - } - }; +pub fn lookup_host(host: &str, port: u16) -> io::Result { + init(); + run_with_cstr(host.as_bytes(), &|c_host| { + let mut hints: c::addrinfo = unsafe { mem::zeroed() }; + hints.ai_socktype = c::SOCK_STREAM; + let mut res = ptr::null_mut(); + unsafe { + cvt_gai(c::getaddrinfo(c_host.as_ptr(), ptr::null(), &hints, &mut res)) + .map(|_| LookupHost { original: res, cur: res, port }) } - - // split the string by ':' and convert the second part to u16 - let (host, port_str) = try_opt!(s.rsplit_once(':'), "invalid socket address"); - let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value"); - (host, port).try_into() - } -} - -impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from((host, port): (&'a str, u16)) -> io::Result { - init(); - - run_with_cstr(host.as_bytes(), &|c_host| { - let mut hints: c::addrinfo = unsafe { mem::zeroed() }; - hints.ai_socktype = c::SOCK_STREAM; - let mut res = ptr::null_mut(); - unsafe { - cvt_gai(c::getaddrinfo(c_host.as_ptr(), ptr::null(), &hints, &mut res)) - .map(|_| LookupHost { original: res, cur: res, port }) - } - }) - } + }) } //////////////////////////////////////////////////////////////////////////////// diff --git a/library/std/src/sys/net/connection/socket/tests.rs b/library/std/src/sys/net/connection/socket/tests.rs index fc236b8027b6..049355afca7a 100644 --- a/library/std/src/sys/net/connection/socket/tests.rs +++ b/library/std/src/sys/net/connection/socket/tests.rs @@ -4,7 +4,7 @@ use crate::collections::HashMap; #[test] fn no_lookup_host_duplicates() { let mut addrs = HashMap::new(); - let lh = match LookupHost::try_from(("localhost", 0)) { + let lh = match lookup_host("localhost", 0) { Ok(lh) => lh, Err(e) => panic!("couldn't resolve `localhost`: {e}"), }; diff --git a/library/std/src/sys/net/connection/uefi/mod.rs b/library/std/src/sys/net/connection/uefi/mod.rs index 003680428735..004f6d413a1f 100644 --- a/library/std/src/sys/net/connection/uefi/mod.rs +++ b/library/std/src/sys/net/connection/uefi/mod.rs @@ -333,12 +333,6 @@ impl fmt::Debug for UdpSocket { pub struct LookupHost(!); -impl LookupHost { - pub fn port(&self) -> u16 { - self.0 - } -} - impl Iterator for LookupHost { type Item = SocketAddr; fn next(&mut self) -> Option { @@ -346,18 +340,6 @@ impl Iterator for LookupHost { } } -impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(_v: &str) -> io::Result { - unsupported() - } -} - -impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from(_v: (&'a str, u16)) -> io::Result { - unsupported() - } +pub fn lookup_host(_host: &str, _port: u16) -> io::Result { + unsupported() } diff --git a/library/std/src/sys/net/connection/unsupported.rs b/library/std/src/sys/net/connection/unsupported.rs index fbc86343272f..fb18e8dec557 100644 --- a/library/std/src/sys/net/connection/unsupported.rs +++ b/library/std/src/sys/net/connection/unsupported.rs @@ -304,12 +304,6 @@ impl fmt::Debug for UdpSocket { pub struct LookupHost(!); -impl LookupHost { - pub fn port(&self) -> u16 { - self.0 - } -} - impl Iterator for LookupHost { type Item = SocketAddr; fn next(&mut self) -> Option { @@ -317,18 +311,6 @@ impl Iterator for LookupHost { } } -impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(_v: &str) -> io::Result { - unsupported() - } -} - -impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from(_v: (&'a str, u16)) -> io::Result { - unsupported() - } +pub fn lookup_host(_host: &str, _port: u16) -> io::Result { + unsupported() } diff --git a/library/std/src/sys/net/connection/wasip1.rs b/library/std/src/sys/net/connection/wasip1.rs index cdfa25c8a440..048dafdcd7f7 100644 --- a/library/std/src/sys/net/connection/wasip1.rs +++ b/library/std/src/sys/net/connection/wasip1.rs @@ -477,12 +477,6 @@ impl fmt::Debug for UdpSocket { pub struct LookupHost(!); -impl LookupHost { - pub fn port(&self) -> u16 { - self.0 - } -} - impl Iterator for LookupHost { type Item = SocketAddr; fn next(&mut self) -> Option { @@ -490,18 +484,6 @@ impl Iterator for LookupHost { } } -impl<'a> TryFrom<&'a str> for LookupHost { - type Error = io::Error; - - fn try_from(_v: &'a str) -> io::Result { - unsupported() - } -} - -impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from(_v: (&'a str, u16)) -> io::Result { - unsupported() - } +pub fn lookup_host(_host: &str, _port: u16) -> io::Result { + unsupported() } diff --git a/library/std/src/sys/net/connection/xous/dns.rs b/library/std/src/sys/net/connection/xous/dns.rs index bb29d211fad5..b139376f5976 100644 --- a/library/std/src/sys/net/connection/xous/dns.rs +++ b/library/std/src/sys/net/connection/xous/dns.rs @@ -1,15 +1,8 @@ -use core::convert::{TryFrom, TryInto}; - use crate::io; use crate::net::{Ipv4Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; use crate::os::xous::ffi::lend_mut; use crate::os::xous::services::{DnsLendMut, dns_server}; -pub struct DnsError { - #[allow(dead_code)] - pub code: u8, -} - #[repr(C, align(4096))] struct LookupHostQuery([u8; 4096]); @@ -20,12 +13,6 @@ pub struct LookupHost { count: usize, } -impl LookupHost { - pub fn port(&self) -> u16 { - self.port - } -} - impl Iterator for LookupHost { type Item = SocketAddr; fn next(&mut self) -> Option { @@ -72,7 +59,7 @@ impl Iterator for LookupHost { } } -pub fn lookup(query: &str, port: u16) -> Result { +pub fn lookup_host(query: &str, port: u16) -> io::Result { let mut result = LookupHost { data: LookupHostQuery([0u8; 4096]), offset: 0, count: 0, port }; // Copy the query into the message that gets sent to the DNS server @@ -89,7 +76,7 @@ pub fn lookup(query: &str, port: u16) -> Result { ) .unwrap(); if result.data.0[0] != 0 { - return Err(DnsError { code: result.data.0[1] }); + return Err(io::const_error!(io::ErrorKind::InvalidInput, "DNS failure")); } assert_eq!(result.offset, 0); result.count = result.data.0[1] as usize; @@ -98,31 +85,3 @@ pub fn lookup(query: &str, port: u16) -> Result { result.offset = 2; Ok(result) } - -impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(s: &str) -> io::Result { - macro_rules! try_opt { - ($e:expr, $msg:expr) => { - match $e { - Some(r) => r, - None => return Err(io::const_error!(io::ErrorKind::InvalidInput, &$msg)), - } - }; - } - - // split the string by ':' and convert the second part to u16 - let (host, port_str) = try_opt!(s.rsplit_once(':'), "invalid socket address"); - let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value"); - (host, port).try_into() - } -} - -impl TryFrom<(&str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from(v: (&str, u16)) -> io::Result { - lookup(v.0, v.1).map_err(|_e| io::const_error!(io::ErrorKind::InvalidInput, "DNS failure")) - } -} diff --git a/library/std/src/sys/net/connection/xous/mod.rs b/library/std/src/sys/net/connection/xous/mod.rs index e44a375b9e3c..0f77be5c3fac 100644 --- a/library/std/src/sys/net/connection/xous/mod.rs +++ b/library/std/src/sys/net/connection/xous/mod.rs @@ -45,4 +45,4 @@ pub struct GetAddress { raw: [u8; 4096], } -pub use dns::LookupHost; +pub use dns::lookup_host; diff --git a/tests/ui/inference/issue-72616.rs b/tests/ui/inference/issue-72616.rs index 71e095cc6fc0..ba18575a5715 100644 --- a/tests/ui/inference/issue-72616.rs +++ b/tests/ui/inference/issue-72616.rs @@ -21,7 +21,6 @@ pub fn main() { { if String::from("a") == "a".try_into().unwrap() {} //~^ ERROR type annotations needed - //~| ERROR type annotations needed } { let _: String = match "_".try_into() { diff --git a/tests/ui/inference/issue-72616.stderr b/tests/ui/inference/issue-72616.stderr index a271639996fd..6b47d5688113 100644 --- a/tests/ui/inference/issue-72616.stderr +++ b/tests/ui/inference/issue-72616.stderr @@ -16,23 +16,6 @@ LL - if String::from("a") == "a".try_into().unwrap() {} LL + if String::from("a") == <&str as TryInto>::try_into("a").unwrap() {} | -error[E0283]: type annotations needed - --> $DIR/issue-72616.rs:22:37 - | -LL | if String::from("a") == "a".try_into().unwrap() {} - | ^^^^^^^^ - | - = note: multiple `impl`s satisfying `_: TryFrom<&str>` found in the following crates: `core`, `std`: - - impl TryFrom<&str> for std::sys::net::connection::socket::LookupHost; - - impl TryFrom for T - where U: Into; - = note: required for `&str` to implement `TryInto<_>` -help: try using a fully qualified path to specify the expected types - | -LL - if String::from("a") == "a".try_into().unwrap() {} -LL + if String::from("a") == <&str as TryInto>::try_into("a").unwrap() {} - | - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0283`. From b30c1b53f3fbd79b94f40b1d000067e1c49ea1d2 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Fri, 19 Sep 2025 13:55:02 +0200 Subject: [PATCH 653/682] Document how to test with iOS/tvOS/watchOS/visionOS simulator --- src/doc/rustc-dev-guide/src/tests/running.md | 31 +++++++++++++++++-- .../rustc/src/platform-support/apple-ios.md | 22 ++++++++++--- .../rustc/src/platform-support/apple-tvos.md | 13 ++------ .../src/platform-support/apple-visionos.md | 13 ++------ .../src/platform-support/apple-watchos.md | 13 ++------ 5 files changed, 51 insertions(+), 41 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/tests/running.md b/src/doc/rustc-dev-guide/src/tests/running.md index 317b65f98cd3..482f3c42578a 100644 --- a/src/doc/rustc-dev-guide/src/tests/running.md +++ b/src/doc/rustc-dev-guide/src/tests/running.md @@ -339,9 +339,34 @@ results. The Docker image is set up to launch `remote-test-server` and the build tools use `remote-test-client` to communicate with the server to coordinate running tests (see [src/bootstrap/src/core/build_steps/test.rs]). -> **TODO** -> -> - Is there any support for using an iOS emulator? +To run on the iOS/tvOS/watchOS/visionOS simulator, we can similarly treat it as +a "remote" machine. A curious detail here is that the network is shared between +the simulator instance and the host macOS, so we can use the local loopback +address `127.0.0.1`. Something like the following should work: + +```sh +# Build the test server for the iOS simulator: +./x build src/tools/remote-test-server --target aarch64-apple-ios-sim + +# If you already have a simulator instance open, copy the device UUID from: +xcrun simctl list devices booted +UDID=01234567-89AB-CDEF-0123-456789ABCDEF + +# Alternatively, create and boot a new simulator instance: +xcrun simctl list runtimes +xcrun simctl list devicetypes +UDID=$(xcrun simctl create $CHOSEN_DEVICE_TYPE $CHOSEN_RUNTIME) +xcrun simctl boot $UDID +# See https://nshipster.com/simctl/ for details. + +# Spawn the runner on port 12345: +xcrun simctl spawn $UDID ./build/host/stage2-tools/aarch64-apple-ios-sim/release/remote-test-server -v --bind 127.0.0.1:12345 + +# In a new terminal, run tests via the runner: +export TEST_DEVICE_ADDR="127.0.0.1:12345" +./x test --host='' --target aarch64-apple-ios-sim --skip tests/debuginfo +# FIXME(madsmtm): Allow debuginfo tests to work (maybe needs `.dSYM` folder to be copied to the target?). +``` [armhf-gnu]: https://github.com/rust-lang/rust/tree/master/src/ci/docker/host-x86_64/armhf-gnu/Dockerfile [QEMU]: https://www.qemu.org/ diff --git a/src/doc/rustc/src/platform-support/apple-ios.md b/src/doc/rustc/src/platform-support/apple-ios.md index 586afa652262..3ac147047546 100644 --- a/src/doc/rustc/src/platform-support/apple-ios.md +++ b/src/doc/rustc/src/platform-support/apple-ios.md @@ -66,6 +66,11 @@ Rust programs can be built for these targets by specifying `--target`, if $ rustc --target aarch64-apple-ios your-code.rs ``` +Or if using Cargo and `-Zbuild-std`: +```console +$ cargo +nightly build -Zbuild-std --target armv7s-apple-ios +``` + The simulator variants can be differentiated from the variants running on-device with the `target_env = "sim"` cfg (or `target_abi = "sim"` before Rust CURRENT_RUSTC_VERSION). @@ -73,7 +78,7 @@ Rust CURRENT_RUSTC_VERSION). ```rust if cfg!(all(target_vendor = "apple", target_env = "sim")) { // Do something on the iOS/tvOS/visionOS/watchOS Simulator. -} { +} else { // Everything else, like Windows and non-Simulator iOS. } ``` @@ -82,8 +87,15 @@ This is similar to the `TARGET_OS_SIMULATOR` define in C code. ## Testing -There is no support for running the Rust or standard library testsuite at the -moment. Testing has mostly been done manually with builds of static libraries -embedded into applications called from Xcode or a simulator. +Running and testing your code naturally requires either an actual device +running iOS, or the equivalent Xcode simulator environment. There exists +several tools in the ecosystem for running a Cargo project on one of these. +One of these tools is [`cargo-dinghy`]. [madsmtm/objc2#459] contains a more +exhaustive list. -It hopefully will be possible to improve this in the future. +See also [testing on emulators in the `rustc-dev-guide`][test-sim] for +instructions on running the standard library's test suite. + +[`cargo-dinghy`]: https://github.com/sonos/dinghy +[madsmtm/objc2#459]: https://github.com/madsmtm/objc2/issues/459 +[test-sim]: https://rustc-dev-guide.rust-lang.org/tests/running.html#testing-on-emulators diff --git a/src/doc/rustc/src/platform-support/apple-tvos.md b/src/doc/rustc/src/platform-support/apple-tvos.md index 193d64666121..a952d8e230d4 100644 --- a/src/doc/rustc/src/platform-support/apple-tvos.md +++ b/src/doc/rustc/src/platform-support/apple-tvos.md @@ -65,17 +65,8 @@ Using the unstable `-Zbuild-std` with a nightly Cargo may also work. ## Building Rust programs -Rust programs can be built for these targets by specifying `--target`, if -`rustc` has been built with support for them. For example: - -```console -$ rustc --target aarch64-apple-tvos your-code.rs -``` +See [the instructions for iOS](./apple-ios.md#building-rust-programs). ## Testing -There is no support for running the Rust or standard library testsuite at the -moment. Testing has mostly been done manually with builds of static libraries -embedded into applications called from Xcode or a simulator. - -It hopefully will be possible to improve this in the future. +See [the instructions for iOS](./apple-ios.md#testing). diff --git a/src/doc/rustc/src/platform-support/apple-visionos.md b/src/doc/rustc/src/platform-support/apple-visionos.md index ed96912da7a4..2ac069248ee9 100644 --- a/src/doc/rustc/src/platform-support/apple-visionos.md +++ b/src/doc/rustc/src/platform-support/apple-visionos.md @@ -46,20 +46,11 @@ be fixed in [#124560](https://github.com/rust-lang/rust/pull/124560). ## Building Rust programs -Rust programs can be built for these targets by specifying `--target`, if -`rustc` has been built with support for them. For example: - -```console -$ rustc --target aarch64-apple-visionos-sim your-code.rs -``` +See [the instructions for iOS](./apple-ios.md#building-rust-programs). ## Testing -There is no support for running the Rust or standard library testsuite at the -moment. Testing has mostly been done manually with builds of static libraries -embedded into applications called from Xcode or a simulator. - -It hopefully will be possible to improve this in the future. +See [the instructions for iOS](./apple-ios.md#testing). ## Cross-compilation toolchains and C code diff --git a/src/doc/rustc/src/platform-support/apple-watchos.md b/src/doc/rustc/src/platform-support/apple-watchos.md index 6ac09d0d1e53..c1a009614250 100644 --- a/src/doc/rustc/src/platform-support/apple-watchos.md +++ b/src/doc/rustc/src/platform-support/apple-watchos.md @@ -50,17 +50,8 @@ Using the unstable `-Zbuild-std` with a nightly Cargo may also work. ## Building Rust programs -Rust programs can be built for these targets by specifying `--target`, if -`rustc` has been built with support for them. For example: - -```console -$ rustc --target aarch64-apple-watchos-sim your-code.rs -``` +See [the instructions for iOS](./apple-ios.md#building-rust-programs). ## Testing -There is no support for running the Rust or standard library testsuite at the -moment. Testing has mostly been done manually with builds of static libraries -embedded into applications called from Xcode or a simulator. - -It hopefully will be possible to improve this in the future. +See [the instructions for iOS](./apple-ios.md#testing). From 37be93497e5318fee712dabb70631f9676f07701 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Fri, 19 Sep 2025 09:17:20 +0200 Subject: [PATCH 654/682] Fix test suite in iOS/tvOS/watchOS/visionOS simulator --- library/std/src/os/unix/process.rs | 2 + library/std/src/process.rs | 16 +-- library/std/src/process/tests.rs | 102 ++++++++++++++---- .../std/src/sys/process/unix/unix/tests.rs | 1 + library/std/tests/process_spawning.rs | 1 + tests/ui/backtrace/apple-no-dsymutil.rs | 1 + tests/ui/backtrace/dylib-dep.rs | 4 + tests/ui/backtrace/line-tables-only.rs | 4 + tests/ui/command/command-current-dir.rs | 2 + tests/ui/command/command-exec.rs | 2 + tests/ui/command/command-pre-exec.rs | 2 + tests/ui/command/command-uid-gid.rs | 2 + .../ui/compiletest-self-test/test-aux-bin.rs | 1 + ...xporting-impl-from-root-causes-ice-2472.rs | 5 +- tests/ui/issues/issue-45731.rs | 4 + tests/ui/process/core-run-destroy.rs | 4 + tests/ui/process/env-funky-keys.rs | 19 +++- tests/ui/process/fds-are-cloexec.rs | 11 +- tests/ui/process/println-with-broken-pipe.rs | 4 + tests/ui/process/process-envs.rs | 4 + tests/ui/process/process-panic-after-fork.rs | 2 + tests/ui/process/process-remove-from-env.rs | 4 + tests/ui/process/process-sigpipe.rs | 4 + tests/ui/process/process-spawn-failure.rs | 4 + tests/ui/runtime/backtrace-debuginfo.rs | 4 + .../runtime/on-broken-pipe/child-processes.rs | 1 + tests/ui/runtime/on-broken-pipe/inherit.rs | 1 + 27 files changed, 179 insertions(+), 32 deletions(-) diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index 09429af06e38..5b7b5a8ea803 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -406,8 +406,10 @@ pub trait ChildExt: Sealed { /// use libc::SIGTERM; /// /// fn main() -> io::Result<()> { + /// # if cfg!(not(all(target_vendor = "apple", not(target_os = "macos")))) { /// let child = Command::new("cat").stdin(Stdio::piped()).spawn()?; /// child.send_signal(SIGTERM)?; + /// # } /// Ok(()) /// } /// ``` diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 48265de90c4d..0883e56342c8 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -532,6 +532,7 @@ impl fmt::Debug for ChildStderr { /// to be changed (for example, by adding arguments) prior to spawning: /// /// ``` +/// # if cfg!(not(all(target_vendor = "apple", not(target_os = "macos")))) { /// use std::process::Command; /// /// let output = if cfg!(target_os = "windows") { @@ -548,6 +549,7 @@ impl fmt::Debug for ChildStderr { /// }; /// /// let hello = output.stdout; +/// # } /// ``` /// /// `Command` can be reused to spawn multiple processes. The builder methods @@ -1348,7 +1350,7 @@ impl Output { /// /// ``` /// #![feature(exit_status_error)] - /// # #[cfg(all(unix, not(target_os = "android")))] { + /// # #[cfg(all(unix, not(target_os = "android"), not(all(target_vendor = "apple", not(target_os = "macos")))))] { /// use std::process::Command; /// assert!(Command::new("false").output().unwrap().exit_ok().is_err()); /// # } @@ -1695,7 +1697,7 @@ impl From for Stdio { /// # Ok(()) /// # } /// # - /// # if cfg!(all(unix, not(target_os = "android"))) { + /// # if cfg!(all(unix, not(target_os = "android"), not(all(target_vendor = "apple", not(target_os = "macos"))))) { /// # test().unwrap(); /// # } /// ``` @@ -1724,7 +1726,7 @@ impl From for Stdio { /// # Ok(()) /// # } /// # - /// # if cfg!(all(unix, not(target_os = "android"))) { + /// # if cfg!(all(unix, not(target_os = "android"), not(all(target_vendor = "apple", not(target_os = "macos"))))) { /// # test().unwrap(); /// # } /// ``` @@ -1800,7 +1802,7 @@ impl ExitStatus { /// /// ``` /// #![feature(exit_status_error)] - /// # if cfg!(unix) { + /// # if cfg!(all(unix, not(all(target_vendor = "apple", not(target_os = "macos"))))) { /// use std::process::Command; /// /// let status = Command::new("ls") @@ -1907,7 +1909,7 @@ impl crate::sealed::Sealed for ExitStatusError {} /// /// ``` /// #![feature(exit_status_error)] -/// # if cfg!(all(unix, not(target_os = "android"))) { +/// # if cfg!(all(unix, not(target_os = "android"), not(all(target_vendor = "apple", not(target_os = "macos"))))) { /// use std::process::{Command, ExitStatusError}; /// /// fn run(cmd: &str) -> Result<(), ExitStatusError> { @@ -1950,7 +1952,7 @@ impl ExitStatusError { /// /// ``` /// #![feature(exit_status_error)] - /// # #[cfg(all(unix, not(target_os = "android")))] { + /// # #[cfg(all(unix, not(target_os = "android"), not(all(target_vendor = "apple", not(target_os = "macos")))))] { /// use std::process::Command; /// /// let bad = Command::new("false").status().unwrap().exit_ok().unwrap_err(); @@ -1975,7 +1977,7 @@ impl ExitStatusError { /// ``` /// #![feature(exit_status_error)] /// - /// # if cfg!(all(unix, not(target_os = "android"))) { + /// # if cfg!(all(unix, not(target_os = "android"), not(all(target_vendor = "apple", not(target_os = "macos"))))) { /// use std::num::NonZero; /// use std::process::Command; /// diff --git a/library/std/src/process/tests.rs b/library/std/src/process/tests.rs index 5879914ca206..12c5130defe5 100644 --- a/library/std/src/process/tests.rs +++ b/library/std/src/process/tests.rs @@ -5,7 +5,15 @@ use crate::mem::MaybeUninit; use crate::str; fn known_command() -> Command { - if cfg!(windows) { Command::new("help") } else { Command::new("echo") } + if cfg!(windows) { + Command::new("help") + } else if cfg!(all(target_vendor = "apple", not(target_os = "macos"))) { + // iOS/tvOS/watchOS/visionOS have a very limited set of commandline + // binaries available. + Command::new("log") + } else { + Command::new("echo") + } } #[cfg(target_os = "android")] @@ -19,7 +27,10 @@ fn shell_cmd() -> Command { } #[test] -#[cfg_attr(any(target_os = "vxworks"), ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no shell available" +)] fn smoke() { let p = if cfg!(target_os = "windows") { Command::new("cmd").args(&["/C", "exit 0"]).spawn() @@ -41,7 +52,10 @@ fn smoke_failure() { } #[test] -#[cfg_attr(any(target_os = "vxworks"), ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no shell available" +)] fn exit_reported_right() { let p = if cfg!(target_os = "windows") { Command::new("cmd").args(&["/C", "exit 1"]).spawn() @@ -56,7 +70,10 @@ fn exit_reported_right() { #[test] #[cfg(unix)] -#[cfg_attr(any(target_os = "vxworks"), ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no shell available" +)] fn signal_reported_right() { use crate::os::unix::process::ExitStatusExt; @@ -80,7 +97,10 @@ pub fn run_output(mut cmd: Command) -> String { } #[test] -#[cfg_attr(any(target_os = "vxworks"), ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no shell available" +)] fn stdout_works() { if cfg!(target_os = "windows") { let mut cmd = Command::new("cmd"); @@ -94,7 +114,11 @@ fn stdout_works() { } #[test] -#[cfg_attr(any(windows, target_os = "vxworks"), ignore)] +#[cfg_attr(windows, ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no shell available" +)] fn set_current_dir_works() { // On many Unix platforms this will use the posix_spawn path. let mut cmd = shell_cmd(); @@ -116,7 +140,11 @@ fn set_current_dir_works() { } #[test] -#[cfg_attr(any(windows, target_os = "vxworks"), ignore)] +#[cfg_attr(windows, ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no shell available" +)] fn stdin_works() { let mut p = shell_cmd() .arg("-c") @@ -134,7 +162,10 @@ fn stdin_works() { } #[test] -#[cfg_attr(any(target_os = "vxworks"), ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no shell available" +)] fn child_stdout_read_buf() { let mut cmd = if cfg!(target_os = "windows") { let mut cmd = Command::new("cmd"); @@ -165,7 +196,10 @@ fn child_stdout_read_buf() { } #[test] -#[cfg_attr(any(target_os = "vxworks"), ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no shell available" +)] fn test_process_status() { let mut status = if cfg!(target_os = "windows") { Command::new("cmd").args(&["/C", "exit 1"]).status().unwrap() @@ -191,7 +225,10 @@ fn test_process_output_fail_to_start() { } #[test] -#[cfg_attr(any(target_os = "vxworks"), ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no shell available" +)] fn test_process_output_output() { let Output { status, stdout, stderr } = if cfg!(target_os = "windows") { Command::new("cmd").args(&["/C", "echo hello"]).output().unwrap() @@ -206,7 +243,10 @@ fn test_process_output_output() { } #[test] -#[cfg_attr(any(target_os = "vxworks"), ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no shell available" +)] fn test_process_output_error() { let Output { status, stdout, stderr } = if cfg!(target_os = "windows") { Command::new("cmd").args(&["/C", "mkdir ."]).output().unwrap() @@ -221,7 +261,10 @@ fn test_process_output_error() { } #[test] -#[cfg_attr(any(target_os = "vxworks"), ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no shell available" +)] fn test_finish_once() { let mut prog = if cfg!(target_os = "windows") { Command::new("cmd").args(&["/C", "exit 1"]).spawn().unwrap() @@ -232,7 +275,10 @@ fn test_finish_once() { } #[test] -#[cfg_attr(any(target_os = "vxworks"), ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no shell available" +)] fn test_finish_twice() { let mut prog = if cfg!(target_os = "windows") { Command::new("cmd").args(&["/C", "exit 1"]).spawn().unwrap() @@ -244,7 +290,10 @@ fn test_finish_twice() { } #[test] -#[cfg_attr(any(target_os = "vxworks"), ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no shell available" +)] fn test_wait_with_output_once() { let prog = if cfg!(target_os = "windows") { Command::new("cmd").args(&["/C", "echo hello"]).stdout(Stdio::piped()).spawn().unwrap() @@ -279,7 +328,10 @@ pub fn env_cmd() -> Command { } #[test] -#[cfg_attr(target_os = "vxworks", ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no shell available" +)] fn test_override_env() { use crate::env; @@ -302,7 +354,10 @@ fn test_override_env() { } #[test] -#[cfg_attr(target_os = "vxworks", ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no shell available" +)] fn test_add_to_env() { let result = env_cmd().env("RUN_TEST_NEW_ENV", "123").output().unwrap(); let output = String::from_utf8_lossy(&result.stdout).to_string(); @@ -314,7 +369,10 @@ fn test_add_to_env() { } #[test] -#[cfg_attr(target_os = "vxworks", ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no shell available" +)] fn test_capture_env_at_spawn() { use crate::env; @@ -378,7 +436,10 @@ fn test_interior_nul_in_current_dir_is_error() { // Regression tests for #30862. #[test] -#[cfg_attr(target_os = "vxworks", ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no `env` cmd available" +)] fn test_interior_nul_in_env_key_is_error() { match env_cmd().env("has-some-\0\0s-inside", "value").spawn() { Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput), @@ -387,7 +448,10 @@ fn test_interior_nul_in_env_key_is_error() { } #[test] -#[cfg_attr(target_os = "vxworks", ignore)] +#[cfg_attr( + any(target_os = "vxworks", all(target_vendor = "apple", not(target_os = "macos"))), + ignore = "no `env` cmd available" +)] fn test_interior_nul_in_env_value_is_error() { match env_cmd().env("key", "has-some-\0\0s-inside").spawn() { Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput), diff --git a/library/std/src/sys/process/unix/unix/tests.rs b/library/std/src/sys/process/unix/unix/tests.rs index f4d6ac6b4e34..663ba61f966c 100644 --- a/library/std/src/sys/process/unix/unix/tests.rs +++ b/library/std/src/sys/process/unix/unix/tests.rs @@ -51,6 +51,7 @@ fn exitstatus_display_tests() { #[test] #[cfg_attr(target_os = "emscripten", ignore)] +#[cfg_attr(any(target_os = "tvos", target_os = "watchos"), ignore = "fork is prohibited")] fn test_command_fork_no_unwind() { let got = catch_unwind(|| { let mut c = Command::new("echo"); diff --git a/library/std/tests/process_spawning.rs b/library/std/tests/process_spawning.rs index 43b45cb2d2b5..93f73ccad3ea 100644 --- a/library/std/tests/process_spawning.rs +++ b/library/std/tests/process_spawning.rs @@ -7,6 +7,7 @@ mod common; #[test] // Process spawning not supported by Miri, Emscripten and wasi #[cfg_attr(any(miri, target_os = "emscripten", target_os = "wasi"), ignore)] +#[cfg_attr(any(target_os = "tvos", target_os = "watchos"), ignore = "fork is prohibited")] fn issue_15149() { // If we're the parent, copy our own binary to a new directory. let my_path = env::current_exe().unwrap(); diff --git a/tests/ui/backtrace/apple-no-dsymutil.rs b/tests/ui/backtrace/apple-no-dsymutil.rs index e5aeced25ca3..00c8349d1293 100644 --- a/tests/ui/backtrace/apple-no-dsymutil.rs +++ b/tests/ui/backtrace/apple-no-dsymutil.rs @@ -3,6 +3,7 @@ //@ compile-flags:-Cstrip=none //@ compile-flags:-g -Csplit-debuginfo=unpacked //@ only-apple +//@ ignore-remote needs the compiler-produced `.o` file to be copied to the device use std::process::Command; use std::str; diff --git a/tests/ui/backtrace/dylib-dep.rs b/tests/ui/backtrace/dylib-dep.rs index 05fdb9afef80..cf420ec8d069 100644 --- a/tests/ui/backtrace/dylib-dep.rs +++ b/tests/ui/backtrace/dylib-dep.rs @@ -7,6 +7,10 @@ //@ ignore-android FIXME #17520 //@ ignore-fuchsia Backtraces not symbolized //@ ignore-musl musl doesn't support dynamic libraries (at least when the original test was written). +//@ ignore-ios needs the `.dSYM` files to be moved to the device +//@ ignore-tvos needs the `.dSYM` files to be moved to the device +//@ ignore-watchos needs the `.dSYM` files to be moved to the device +//@ ignore-visionos needs the `.dSYM` files to be moved to the device //@ needs-unwind //@ ignore-backends: gcc //@ compile-flags: -g -Copt-level=0 -Cstrip=none -Cforce-frame-pointers=yes diff --git a/tests/ui/backtrace/line-tables-only.rs b/tests/ui/backtrace/line-tables-only.rs index 6624c71e184b..5863cc1d17d4 100644 --- a/tests/ui/backtrace/line-tables-only.rs +++ b/tests/ui/backtrace/line-tables-only.rs @@ -11,6 +11,10 @@ //@ ignore-android FIXME #17520 //@ ignore-fuchsia Backtraces not symbolized //@ ignore-emscripten Requires custom symbolization code +//@ ignore-ios needs the `.dSYM` files to be moved to the device +//@ ignore-tvos needs the `.dSYM` files to be moved to the device +//@ ignore-watchos needs the `.dSYM` files to be moved to the device +//@ ignore-visionos needs the `.dSYM` files to be moved to the device //@ needs-unwind //@ aux-build: line-tables-only-helper.rs diff --git a/tests/ui/command/command-current-dir.rs b/tests/ui/command/command-current-dir.rs index e264cbe4d704..a6b51df5f176 100644 --- a/tests/ui/command/command-current-dir.rs +++ b/tests/ui/command/command-current-dir.rs @@ -2,6 +2,8 @@ //@ no-prefer-dynamic We move the binary around, so do not depend dynamically on libstd //@ needs-subprocess //@ ignore-fuchsia Needs directory creation privilege +//@ ignore-tvos `Command::current_dir` requires fork, which is prohibited +//@ ignore-watchos `Command::current_dir` requires fork, which is prohibited use std::env; use std::fs; diff --git a/tests/ui/command/command-exec.rs b/tests/ui/command/command-exec.rs index 77336377e889..870f8b047b9a 100644 --- a/tests/ui/command/command-exec.rs +++ b/tests/ui/command/command-exec.rs @@ -3,6 +3,8 @@ //@ only-unix (this is a unix-specific test) //@ needs-subprocess //@ ignore-fuchsia no execvp syscall provided +//@ ignore-tvos execvp is prohibited +//@ ignore-watchos execvp is prohibited use std::env; use std::os::unix::process::CommandExt; diff --git a/tests/ui/command/command-pre-exec.rs b/tests/ui/command/command-pre-exec.rs index 7299f357bd07..a62ab0b5ed6a 100644 --- a/tests/ui/command/command-pre-exec.rs +++ b/tests/ui/command/command-pre-exec.rs @@ -2,6 +2,8 @@ //@ only-unix (this is a unix-specific test) //@ needs-subprocess //@ ignore-fuchsia no execvp syscall +//@ ignore-tvos execvp is prohibited +//@ ignore-watchos execvp is prohibited #![feature(rustc_private)] diff --git a/tests/ui/command/command-uid-gid.rs b/tests/ui/command/command-uid-gid.rs index f54a0f50708b..ef0653eb2cb4 100644 --- a/tests/ui/command/command-uid-gid.rs +++ b/tests/ui/command/command-uid-gid.rs @@ -1,6 +1,8 @@ //@ run-pass //@ ignore-android //@ ignore-fuchsia no '/bin/sh', '/bin/ls' +//@ ignore-tvos `Command::uid/gid` requires fork, which is prohibited +//@ ignore-watchos `Command::uid/gid` requires fork, which is prohibited //@ needs-subprocess #![feature(rustc_private)] diff --git a/tests/ui/compiletest-self-test/test-aux-bin.rs b/tests/ui/compiletest-self-test/test-aux-bin.rs index c1c28e12b3b5..9ac17e6e1461 100644 --- a/tests/ui/compiletest-self-test/test-aux-bin.rs +++ b/tests/ui/compiletest-self-test/test-aux-bin.rs @@ -1,4 +1,5 @@ //@ ignore-cross-compile because aux-bin does not yet support it +//@ ignore-remote because aux-bin does not yet support it //@ aux-bin: print-it-works.rs //@ run-pass diff --git a/tests/ui/cross-crate/exporting-impl-from-root-causes-ice-2472.rs b/tests/ui/cross-crate/exporting-impl-from-root-causes-ice-2472.rs index 86d637b579dc..c6cfae1e5452 100644 --- a/tests/ui/cross-crate/exporting-impl-from-root-causes-ice-2472.rs +++ b/tests/ui/cross-crate/exporting-impl-from-root-causes-ice-2472.rs @@ -1,6 +1,9 @@ //@ run-pass //@ aux-build:exporting-impl-from-root-causes-ice-2472-b.rs - +//@ ignore-ios FIXME(madsmtm): For some reason the necessary dylib isn't copied to the remote? +//@ ignore-tvos FIXME(madsmtm): For some reason the necessary dylib isn't copied to the remote? +//@ ignore-watchos FIXME(madsmtm): For some reason the necessary dylib isn't copied to the remote? +//@ ignore-visionos FIXME(madsmtm): For some reason the necessary dylib isn't copied to the remote? extern crate exporting_impl_from_root_causes_ice_2472_b as lib; diff --git a/tests/ui/issues/issue-45731.rs b/tests/ui/issues/issue-45731.rs index 49335362dd0f..db11d1dbef1c 100644 --- a/tests/ui/issues/issue-45731.rs +++ b/tests/ui/issues/issue-45731.rs @@ -1,6 +1,10 @@ //@ run-pass #![allow(unused_variables)] //@ compile-flags:--test -g +//@ ignore-ios needs the `.dSYM` files to be moved to the device +//@ ignore-tvos needs the `.dSYM` files to be moved to the device +//@ ignore-watchos needs the `.dSYM` files to be moved to the device +//@ ignore-visionos needs the `.dSYM` files to be moved to the device #[cfg(target_vendor = "apple")] #[test] diff --git a/tests/ui/process/core-run-destroy.rs b/tests/ui/process/core-run-destroy.rs index f4be54da8fe6..f381997ef793 100644 --- a/tests/ui/process/core-run-destroy.rs +++ b/tests/ui/process/core-run-destroy.rs @@ -8,6 +8,10 @@ //@ needs-subprocess //@ ignore-vxworks no 'cat' and 'sleep' //@ ignore-fuchsia no 'cat' +//@ ignore-ios no 'cat' and 'sleep' +//@ ignore-tvos no 'cat' and 'sleep' +//@ ignore-watchos no 'cat' and 'sleep' +//@ ignore-visionos no 'cat' and 'sleep' // N.B., these tests kill child processes. Valgrind sees these children as leaking // memory, which makes for some *confusing* logs. That's why these are here diff --git a/tests/ui/process/env-funky-keys.rs b/tests/ui/process/env-funky-keys.rs index a4a71c94020c..193659bea293 100644 --- a/tests/ui/process/env-funky-keys.rs +++ b/tests/ui/process/env-funky-keys.rs @@ -1,8 +1,7 @@ //@ run-pass //@ edition: 2021 -// Ignore this test on Android, because it segfaults there. -//@ ignore-android +//@ ignore-android segfaults //@ ignore-windows //@ ignore-wasm32 no execve //@ ignore-sgx no execve @@ -24,6 +23,9 @@ use std::ptr; fn main() { if env::args_os().count() == 2 { for (key, value) in env::vars_os() { + if key == "DYLD_ROOT_PATH" { + continue; + } panic!("found env value {:?} {:?}", key, value); } return; @@ -35,7 +37,18 @@ fn main() { .as_bytes()).unwrap(); let filename: *const c_char = current_exe.as_ptr(); let argv: &[*const c_char] = &[filename, filename, ptr::null()]; - let envp: &[*const c_char] = &[c"FOOBAR".as_ptr(), ptr::null()]; + + let root; + let envp: &[*const c_char] = if cfg!(all(target_vendor = "apple", target_env = "sim")) { + // Workaround: iOS/tvOS/watchOS/visionOS simulators need the root path + // from the current process. + root = format!("DYLD_ROOT_PATH={}\0", std::env::var("DYLD_ROOT_PATH").unwrap()); + &[c"FOOBAR".as_ptr(), root.as_ptr().cast(), ptr::null()] + } else { + // Try to set an environment variable without a value. + &[c"FOOBAR".as_ptr(), ptr::null()] + }; + unsafe { execve(filename, &argv[0], &envp[0]); } diff --git a/tests/ui/process/fds-are-cloexec.rs b/tests/ui/process/fds-are-cloexec.rs index f6678379dd67..0fae7c2b5024 100644 --- a/tests/ui/process/fds-are-cloexec.rs +++ b/tests/ui/process/fds-are-cloexec.rs @@ -74,8 +74,15 @@ fn child(args: &[String]) { let fd: libc::c_int = arg.parse().unwrap(); unsafe { assert_eq!(libc::read(fd, b.as_mut_ptr() as *mut _, 2), -1); - assert_eq!(io::Error::last_os_error().raw_os_error(), - Some(libc::EBADF)); + let raw = io::Error::last_os_error().raw_os_error(); + if cfg!(all(target_vendor = "apple", not(target_os = "macos"))) { + // Workaround: iOS/tvOS/watchOS/visionOS seems to treat `tcp6` + // as a directory? + if raw == Some(libc::EISDIR) { + continue; + } + } + assert_eq!(raw, Some(libc::EBADF)); } } } diff --git a/tests/ui/process/println-with-broken-pipe.rs b/tests/ui/process/println-with-broken-pipe.rs index 58b83a2dd9a2..e87e20773702 100644 --- a/tests/ui/process/println-with-broken-pipe.rs +++ b/tests/ui/process/println-with-broken-pipe.rs @@ -5,6 +5,10 @@ //@ ignore-fuchsia //@ ignore-horizon //@ ignore-android +//@ ignore-ios no 'head' +//@ ignore-tvos no 'head' +//@ ignore-watchos no 'head' +//@ ignore-visionos no 'head' //@ ignore-backends: gcc //@ normalize-stderr: ".rs:\d+:\d+" -> ".rs:LL:CC" //@ compile-flags: -Zon-broken-pipe=error diff --git a/tests/ui/process/process-envs.rs b/tests/ui/process/process-envs.rs index 98052f1d3a5d..cbe16704a8e1 100644 --- a/tests/ui/process/process-envs.rs +++ b/tests/ui/process/process-envs.rs @@ -2,6 +2,10 @@ //@ needs-subprocess //@ ignore-vxworks no 'env' //@ ignore-fuchsia no 'env' +//@ ignore-ios no 'env' +//@ ignore-tvos no 'env' +//@ ignore-watchos no 'env' +//@ ignore-visionos no 'env' use std::process::Command; use std::env; diff --git a/tests/ui/process/process-panic-after-fork.rs b/tests/ui/process/process-panic-after-fork.rs index 6e0267e0a54f..653ff6ce314d 100644 --- a/tests/ui/process/process-panic-after-fork.rs +++ b/tests/ui/process/process-panic-after-fork.rs @@ -3,6 +3,8 @@ //@ only-unix //@ needs-subprocess //@ ignore-fuchsia no fork +//@ ignore-tvos fork is prohibited +//@ ignore-watchos fork is prohibited #![feature(rustc_private)] #![feature(never_type)] diff --git a/tests/ui/process/process-remove-from-env.rs b/tests/ui/process/process-remove-from-env.rs index c1a2b2daf5b6..68c3909b15ae 100644 --- a/tests/ui/process/process-remove-from-env.rs +++ b/tests/ui/process/process-remove-from-env.rs @@ -2,6 +2,10 @@ //@ needs-subprocess //@ ignore-vxworks no 'env' //@ ignore-fuchsia no 'env' +//@ ignore-ios no 'env' +//@ ignore-tvos no 'env' +//@ ignore-watchos no 'env' +//@ ignore-visionos no 'env' use std::process::Command; use std::env; diff --git a/tests/ui/process/process-sigpipe.rs b/tests/ui/process/process-sigpipe.rs index 3ecf271599d2..574d79ee1dda 100644 --- a/tests/ui/process/process-sigpipe.rs +++ b/tests/ui/process/process-sigpipe.rs @@ -15,6 +15,10 @@ //@ ignore-vxworks no 'sh' //@ ignore-fuchsia no 'sh' +//@ ignore-ios no 'sh' +//@ ignore-tvos no 'sh' +//@ ignore-watchos no 'sh' +//@ ignore-visionos no 'sh' //@ needs-threads //@ only-unix SIGPIPE is a unix feature diff --git a/tests/ui/process/process-spawn-failure.rs b/tests/ui/process/process-spawn-failure.rs index 0950b044c97c..ac2c34bc7836 100644 --- a/tests/ui/process/process-spawn-failure.rs +++ b/tests/ui/process/process-spawn-failure.rs @@ -9,6 +9,10 @@ //@ ignore-vxworks no 'ps' //@ ignore-fuchsia no 'ps' //@ ignore-nto no 'ps' +//@ ignore-ios no 'ps' +//@ ignore-tvos no 'ps' +//@ ignore-watchos no 'ps' +//@ ignore-visionos no 'ps' #![feature(rustc_private)] diff --git a/tests/ui/runtime/backtrace-debuginfo.rs b/tests/ui/runtime/backtrace-debuginfo.rs index 5fb9943d6c3c..5e91f22aec02 100644 --- a/tests/ui/runtime/backtrace-debuginfo.rs +++ b/tests/ui/runtime/backtrace-debuginfo.rs @@ -11,6 +11,10 @@ //@ compile-flags:-Cstrip=none //@ needs-subprocess //@ ignore-fuchsia Backtrace not symbolized, trace different line alignment +//@ ignore-ios needs the `.dSYM` files to be moved to the device +//@ ignore-tvos needs the `.dSYM` files to be moved to the device +//@ ignore-watchos needs the `.dSYM` files to be moved to the device +//@ ignore-visionos needs the `.dSYM` files to be moved to the device // FIXME(#117097): backtrace (possibly unwinding mechanism) seems to be different on at least // `i686-mingw` (32-bit windows-gnu)? cc #128911. diff --git a/tests/ui/runtime/on-broken-pipe/child-processes.rs b/tests/ui/runtime/on-broken-pipe/child-processes.rs index c0c8ad4e2f5e..b7022e1b09d9 100644 --- a/tests/ui/runtime/on-broken-pipe/child-processes.rs +++ b/tests/ui/runtime/on-broken-pipe/child-processes.rs @@ -1,5 +1,6 @@ //@ revisions: default error kill inherit //@ ignore-cross-compile because aux-bin does not yet support it +//@ ignore-remote because aux-bin does not yet support it //@ only-unix because SIGPIPE is a unix thing //@ ignore-backends: gcc //@ run-pass diff --git a/tests/ui/runtime/on-broken-pipe/inherit.rs b/tests/ui/runtime/on-broken-pipe/inherit.rs index f3c8140eaaef..e99c7c7a0fe4 100644 --- a/tests/ui/runtime/on-broken-pipe/inherit.rs +++ b/tests/ui/runtime/on-broken-pipe/inherit.rs @@ -1,4 +1,5 @@ //@ ignore-cross-compile because aux-bin does not yet support it +//@ ignore-remote because aux-bin does not yet support it //@ only-unix because SIGPIPE is a unix thing //@ aux-bin: assert-inherit-sig_dfl.rs //@ aux-bin: assert-inherit-sig_ign.rs From bf5fe6e44c2cce2877b25686dd1c0bb3c5d64d80 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 19 Sep 2025 22:35:46 +0900 Subject: [PATCH 655/682] minor: Get rid of unused deps `chalk-solve` and `chalk-recursive` --- src/tools/rust-analyzer/Cargo.lock | 88 ++++--------------- src/tools/rust-analyzer/Cargo.toml | 4 - .../rust-analyzer/crates/hir-ty/Cargo.toml | 2 - .../rust-analyzer/crates/hir-ty/src/traits.rs | 10 --- 4 files changed, 16 insertions(+), 88 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 97c4f06dd596..097bb81f6676 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -279,35 +279,6 @@ dependencies = [ "chalk-derive", ] -[[package]] -name = "chalk-recursive" -version = "0.104.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "882959c242558cc686de7ff0aa59860295598d119e84a4b100215f44c3d606c4" -dependencies = [ - "chalk-derive", - "chalk-ir", - "chalk-solve", - "rustc-hash 1.1.0", - "tracing", -] - -[[package]] -name = "chalk-solve" -version = "0.104.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72860086494ccfa05bbd3779a74babb8ace27da9a0cbabffa1315223c7290927" -dependencies = [ - "chalk-derive", - "chalk-ir", - "ena", - "indexmap", - "itertools 0.12.1", - "petgraph", - "rustc-hash 1.1.0", - "tracing", -] - [[package]] name = "clap" version = "4.5.42" @@ -574,12 +545,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - [[package]] name = "flate2" version = "1.1.2" @@ -706,7 +671,7 @@ dependencies = [ "hir-ty", "indexmap", "intern", - "itertools 0.14.0", + "itertools", "ra-ap-rustc_type_ir", "rustc-hash 2.1.1", "smallvec", @@ -739,7 +704,7 @@ dependencies = [ "hir-expand", "indexmap", "intern", - "itertools 0.14.0", + "itertools", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "mbe", "query-group-macro", @@ -773,7 +738,7 @@ dependencies = [ "either", "expect-test", "intern", - "itertools 0.14.0", + "itertools", "mbe", "parser", "query-group-macro", @@ -799,8 +764,6 @@ dependencies = [ "bitflags 2.9.1", "chalk-derive", "chalk-ir", - "chalk-recursive", - "chalk-solve", "cov-mark", "either", "ena", @@ -809,7 +772,7 @@ dependencies = [ "hir-expand", "indexmap", "intern", - "itertools 0.14.0", + "itertools", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "oorandom", "project-model", @@ -949,7 +912,7 @@ dependencies = [ "ide-db", "ide-diagnostics", "ide-ssr", - "itertools 0.14.0", + "itertools", "nohash-hasher", "oorandom", "profile", @@ -977,7 +940,7 @@ dependencies = [ "expect-test", "hir", "ide-db", - "itertools 0.14.0", + "itertools", "smallvec", "stdx", "syntax", @@ -995,7 +958,7 @@ dependencies = [ "expect-test", "hir", "ide-db", - "itertools 0.14.0", + "itertools", "smallvec", "stdx", "syntax", @@ -1018,7 +981,7 @@ dependencies = [ "fst", "hir", "indexmap", - "itertools 0.14.0", + "itertools", "line-index 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "memchr", "nohash-hasher", @@ -1049,7 +1012,7 @@ dependencies = [ "expect-test", "hir", "ide-db", - "itertools 0.14.0", + "itertools", "paths", "serde_json", "stdx", @@ -1067,7 +1030,7 @@ dependencies = [ "expect-test", "hir", "ide-db", - "itertools 0.14.0", + "itertools", "parser", "syntax", "test-fixture", @@ -1146,15 +1109,6 @@ dependencies = [ "memoffset", ] -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.14.0" @@ -1283,7 +1237,7 @@ dependencies = [ "hir-expand", "ide-db", "intern", - "itertools 0.14.0", + "itertools", "proc-macro-api", "project-model", "span", @@ -1640,16 +1594,6 @@ dependencies = [ "libc", ] -[[package]] -name = "petgraph" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" -dependencies = [ - "fixedbitset", - "indexmap", -] - [[package]] name = "pin-project-lite" version = "0.2.16" @@ -1784,7 +1728,7 @@ dependencies = [ "cfg", "expect-test", "intern", - "itertools 0.14.0", + "itertools", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "paths", "rustc-hash 2.1.1", @@ -2062,7 +2006,7 @@ dependencies = [ "ide-ssr", "indexmap", "intern", - "itertools 0.14.0", + "itertools", "load-cargo", "lsp-server 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", "lsp-types", @@ -2400,7 +2344,7 @@ dependencies = [ "backtrace", "crossbeam-channel", "crossbeam-utils", - "itertools 0.14.0", + "itertools", "jod-thread", "libc", "miow", @@ -2436,7 +2380,7 @@ version = "0.0.0" dependencies = [ "either", "expect-test", - "itertools 0.14.0", + "itertools", "parser", "rayon", "rowan", @@ -3285,7 +3229,7 @@ dependencies = [ "edition", "either", "flate2", - "itertools 0.14.0", + "itertools", "proc-macro2", "quote", "stdx", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 513af11a87aa..cfbd8a56b76f 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -37,9 +37,7 @@ debug = 2 [patch.'crates-io'] # rowan = { path = "../rowan" } -# chalk-solve = { path = "../chalk/chalk-solve" } # chalk-ir = { path = "../chalk/chalk-ir" } -# chalk-recursive = { path = "../chalk/chalk-recursive" } # chalk-derive = { path = "../chalk/chalk-derive" } # line-index = { path = "lib/line-index" } # la-arena = { path = "lib/la-arena" } @@ -111,9 +109,7 @@ arrayvec = "0.7.6" bitflags = "2.9.1" cargo_metadata = "0.21.0" camino = "1.1.10" -chalk-solve = { version = "0.104.0", default-features = false } chalk-ir = "0.104.0" -chalk-recursive = { version = "0.104.0", default-features = false } chalk-derive = "0.104.0" crossbeam-channel = "0.5.15" dissimilar = "1.0.10" diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml index 64182110e186..138d02e5a610 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml @@ -24,9 +24,7 @@ oorandom = "11.1.5" tracing.workspace = true rustc-hash.workspace = true scoped-tls = "1.0.1" -chalk-solve.workspace = true chalk-ir.workspace = true -chalk-recursive.workspace = true chalk-derive.workspace = true la-arena.workspace = true triomphe.workspace = true diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs index 8b6836c0e90b..8095d702be48 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -4,7 +4,6 @@ use core::fmt; use std::hash::Hash; use chalk_ir::{DebruijnIndex, GoalData, fold::TypeFoldable}; -use chalk_solve::rust_ir; use base_db::Crate; use hir_def::{BlockId, TraitId, lang_item::LangItem}; @@ -405,15 +404,6 @@ impl FnTrait { } } - pub const fn to_chalk_ir(self) -> rust_ir::ClosureKind { - // Chalk doesn't support async fn traits. - match self { - FnTrait::AsyncFnOnce | FnTrait::FnOnce => rust_ir::ClosureKind::FnOnce, - FnTrait::AsyncFnMut | FnTrait::FnMut => rust_ir::ClosureKind::FnMut, - FnTrait::AsyncFn | FnTrait::Fn => rust_ir::ClosureKind::Fn, - } - } - pub fn method_name(self) -> Name { match self { FnTrait::FnOnce => Name::new_symbol_root(sym::call_once), From 9ba9869b335b56823753a6ce6614f8dffee4fcf3 Mon Sep 17 00:00:00 2001 From: Oblarg Date: Fri, 19 Sep 2025 10:03:14 -0400 Subject: [PATCH 656/682] address review feedback --- .../crates/hir-ty/src/lower_nextsolver.rs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs index b8e0599dba28..0076446a958b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -285,6 +285,29 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { const_type, self.resolver.krate(), ), + hir_def::hir::Expr::UnaryOp { expr: inner_expr, op: hir_def::hir::UnaryOp::Neg } => { + if let hir_def::hir::Expr::Literal(literal) = &self.store[*inner_expr] { + // Only handle negation for signed integers and floats + match literal { + hir_def::hir::Literal::Int(_, _) | hir_def::hir::Literal::Float(_, _) => { + if let Some(negated_literal) = literal.clone().negate() { + intern_const_ref( + self.db, + &negated_literal.into(), + const_type, + self.resolver.krate(), + ) + } else { + unknown_const(const_type) + } + } + // For unsigned integers, chars, bools, etc., negation is not meaningful + _ => unknown_const(const_type), + } + } else { + unknown_const(const_type) + } + } _ => unknown_const(const_type), } } From 58814bce6cfb8faf62979c7d043347e0526d7e7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 19 Sep 2025 16:21:34 +0200 Subject: [PATCH 657/682] Allow running `x ` from a different directory --- src/bootstrap/src/core/builder/mod.rs | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 75c8ee365284..8226b4325b6b 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -590,18 +590,30 @@ impl StepDescription { // Attempt to resolve paths to be relative to the builder source directory. let mut paths: Vec = paths .iter() - .map(|p| { + .map(|original_path| { + let mut path = original_path.clone(); + + // Someone could run `x ` from a different repository than the source + // directory. + // In that case, we should not try to resolve the paths relative to the working + // directory, but rather relative to the source directory. + // So we forcefully "relocate" the path to the source directory here. + if !path.is_absolute() { + path = builder.src.join(path); + } + // If the path does not exist, it may represent the name of a Step, such as `tidy` in `x test tidy` - if !p.exists() { - return p.clone(); + if !path.exists() { + // Use the original path here + return original_path.clone(); } // Make the path absolute, strip the prefix, and convert to a PathBuf. - match std::path::absolute(p) { + match std::path::absolute(&path) { Ok(p) => p.strip_prefix(&builder.src).unwrap_or(&p).to_path_buf(), Err(e) => { eprintln!("ERROR: {e:?}"); - panic!("Due to the above error, failed to resolve path: {p:?}"); + panic!("Due to the above error, failed to resolve path: {path:?}"); } } }) From e500dd820bc703b5b727e691e89a8f879522a90f Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Fri, 19 Sep 2025 14:46:12 +0000 Subject: [PATCH 658/682] fixes for numerous clippy warnings --- .../rustc_hir_typeck/src/method/suggest.rs | 42 ++++++++----------- compiler/rustc_hir_typeck/src/op.rs | 8 ++-- 2 files changed, 22 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index a39ac0fcb6e3..024b9ee08c22 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -2194,7 +2194,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn suggest_associated_call_syntax( &self, err: &mut Diag<'_>, - static_candidates: &Vec, + static_candidates: &[CandidateSource], rcvr_ty: Ty<'tcx>, source: SelfSource<'tcx>, item_name: Ident, @@ -2422,7 +2422,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let span_included = match parent_expr.kind { hir::ExprKind::Struct(_, eps, _) => { - eps.len() > 0 && eps.last().is_some_and(|ep| ep.span.contains(span)) + eps.last().is_some_and(|ep| ep.span.contains(span)) } // `..=` desugars into `::std::ops::RangeInclusive::new(...)`. hir::ExprKind::Call(func, ..) => func.span.contains(span), @@ -2484,7 +2484,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { simplify_type(tcx, ty, TreatParams::InstantiateWithInfer) .and_then(|simp| { tcx.incoherent_impls(simp) - .into_iter() + .iter() .find_map(|&id| self.associated_value(id, item_name)) }) .is_some() @@ -2617,7 +2617,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Node::Expr(call_expr) = self.tcx.parent_hir_node(seg1.hir_id) && let ControlFlow::Break(Some(expr)) = - (LetVisitor { ident_name: seg1.ident.name }).visit_body(&body) + (LetVisitor { ident_name: seg1.ident.name }).visit_body(body) && let Some(self_ty) = self.node_ty_opt(expr.hir_id) { let probe = self.lookup_probe_for_diagnostic( @@ -2960,14 +2960,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect::>() .into(); for pred in &local_preds { - match pred.self_ty().kind() { - ty::Adt(def, _) => { - local_spans.push_span_label( - self.tcx.def_span(def.did()), - format!("must implement `{}`", pred.trait_ref.print_trait_sugared()), - ); - } - _ => {} + if let ty::Adt(def, _) = pred.self_ty().kind() { + local_spans.push_span_label( + self.tcx.def_span(def.did()), + format!("must implement `{}`", pred.trait_ref.print_trait_sugared()), + ); } } if local_spans.primary_span().is_some() { @@ -3006,14 +3003,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect::>() .into(); for pred in &foreign_preds { - match pred.self_ty().kind() { - ty::Adt(def, _) => { - foreign_spans.push_span_label( - self.tcx.def_span(def.did()), - format!("not implement `{}`", pred.trait_ref.print_trait_sugared()), - ); - } - _ => {} + if let ty::Adt(def, _) = pred.self_ty().kind() { + foreign_spans.push_span_label( + self.tcx.def_span(def.did()), + format!("not implement `{}`", pred.trait_ref.print_trait_sugared()), + ); } } if foreign_spans.primary_span().is_some() { @@ -3595,7 +3589,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // would take care of them. && !skippable.contains(&Some(pick.item.container_id(self.tcx))) // Do not suggest pinning when the method is directly on `Pin`. - && pick.item.impl_container(self.tcx).map_or(true, |did| { + && pick.item.impl_container(self.tcx).is_none_or(|did| { match self.tcx.type_of(did).skip_binder().kind() { ty::Adt(def, _) => Some(def.did()) != self.tcx.lang_items().pin_type(), _ => true, @@ -3653,7 +3647,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { vec![ ( rcvr.span.shrink_to_lo(), - format!("let mut pinned = std::pin::pin!("), + "let mut pinned = std::pin::pin!(".to_string(), ), ( rcvr.span.shrink_to_hi(), @@ -4128,7 +4122,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); let trait_span = self.tcx.def_span(trait_def_id); let mut multi_span: MultiSpan = trait_span.into(); - multi_span.push_span_label(trait_span, format!("this is the trait that is needed")); + multi_span.push_span_label(trait_span, "this is the trait that is needed".to_string()); let descr = self.tcx.associated_item(item_def_id).descr(); let rcvr_ty = rcvr_ty.map(|t| format!("`{t}`")).unwrap_or_else(|| "the receiver".to_string()); @@ -4146,7 +4140,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } multi_span.push_span_label( self.tcx.def_span(def_id), - format!("this is the trait that was imported"), + "this is the trait that was imported".to_string(), ); } err.span_note(multi_span, msg); diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 054435379434..a8e8582c51c4 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -566,7 +566,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs_ty, lhs_expr, lhs_ty, - |lhs_ty, rhs_ty| is_compatible_after_call(lhs_ty, rhs_ty), + is_compatible_after_call, ) { // Cool } @@ -1170,8 +1170,8 @@ fn deref_ty_if_possible(ty: Ty<'_>) -> Ty<'_> { } } -/// Returns `true` if this is a built-in arithmetic operation (e.g., u32 -/// + u32, i16x4 == i16x4) and false if these types would have to be +/// Returns `true` if this is a built-in arithmetic operation (e.g., +/// u32 + u32, i16x4 == i16x4) and false if these types would have to be /// overloaded to be legal. There are two reasons that we distinguish /// builtin operations from overloaded ones (vs trying to drive /// everything uniformly through the trait system and intrinsics or @@ -1191,7 +1191,7 @@ fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, category: BinOpCategory) // (See https://github.com/rust-lang/rust/issues/57447.) let (lhs, rhs) = (deref_ty_if_possible(lhs), deref_ty_if_possible(rhs)); - match category.into() { + match category { BinOpCategory::Shortcircuit => true, BinOpCategory::Shift => { lhs.references_error() From 04f6b8f99242eb61756fe294dd9d46536c1c8571 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 19 Sep 2025 16:48:05 +0200 Subject: [PATCH 659/682] fix ./x readdir logic when CDPATH is set --- x | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x b/x index 551cfe6efbf3..4fce0be219e7 100755 --- a/x +++ b/x @@ -15,7 +15,8 @@ realpath() { if [ -L "$path" ]; then readlink -f "$path" elif [ -d "$path" ]; then - (cd -P "$path" && pwd) + # "cd" is not always silent (e.g. when CDPATH is set), so discard its output. + (cd -P "$path" >/dev/null && pwd) else echo "$(realpath "$(dirname "$path")")/$(basename "$path")" fi From 3ab89abac41443b125f2440b8f525f56536d8ffc Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Fri, 19 Sep 2025 23:06:44 +0800 Subject: [PATCH 660/682] mbe: Fix feature gate for `macro_derive` --- compiler/rustc_expand/src/mbe/macro_rules.rs | 2 +- tests/ui/feature-gates/feature-gate-macro-derive.stderr | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 946265eba8bd..1d147a0385c6 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -702,7 +702,7 @@ pub fn compile_declarative_macro( kinds |= MacroKinds::DERIVE; let derive_keyword_span = p.prev_token.span; if !features.macro_derive() { - feature_err(sess, sym::macro_attr, span, "`macro_rules!` derives are unstable") + feature_err(sess, sym::macro_derive, span, "`macro_rules!` derives are unstable") .emit(); } if let Some(guar) = check_no_eof(sess, &p, "expected `()` after `derive`") { diff --git a/tests/ui/feature-gates/feature-gate-macro-derive.stderr b/tests/ui/feature-gates/feature-gate-macro-derive.stderr index b7ca6717bd51..177518edafb3 100644 --- a/tests/ui/feature-gates/feature-gate-macro-derive.stderr +++ b/tests/ui/feature-gates/feature-gate-macro-derive.stderr @@ -4,8 +4,8 @@ error[E0658]: `macro_rules!` derives are unstable LL | macro_rules! MyDerive { derive() {} => {} } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: see issue #83527 for more information - = help: add `#![feature(macro_attr)]` to the crate attributes to enable + = note: see issue #143549 for more information + = help: add `#![feature(macro_derive)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: aborting due to 1 previous error From e2de670558111dc6ce46d764aed6cb3dc4222794 Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Fri, 19 Sep 2025 17:21:55 +0000 Subject: [PATCH 661/682] btree: safety comments for init and new --- library/alloc/src/collections/btree/node.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/library/alloc/src/collections/btree/node.rs b/library/alloc/src/collections/btree/node.rs index 37f784a322ca..b233e1740b7b 100644 --- a/library/alloc/src/collections/btree/node.rs +++ b/library/alloc/src/collections/btree/node.rs @@ -67,6 +67,10 @@ struct LeafNode { impl LeafNode { /// Initializes a new `LeafNode` in-place. + /// + /// # Safety + /// + /// The caller must ensure that `this` points to a (possibly uninitialized) `LeafNode` unsafe fn init(this: *mut Self) { // As a general policy, we leave fields uninitialized if they can be, as this should // be both slightly faster and easier to track in Valgrind. @@ -79,9 +83,11 @@ impl LeafNode { /// Creates a new boxed `LeafNode`. fn new(alloc: A) -> Box { + let mut leaf = Box::new_uninit_in(alloc); unsafe { - let mut leaf = Box::new_uninit_in(alloc); + // SAFETY: `leaf` points to a `LeafNode` LeafNode::init(leaf.as_mut_ptr()); + // SAFETY: `leaf` was just initialized leaf.assume_init() } } From 4b755141bc297444401397b09995bb2baafb3fc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 19 Sep 2025 16:00:05 +0200 Subject: [PATCH 662/682] Simplify default value of `download-ci-llvm` --- src/bootstrap/src/core/config/config.rs | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index efb7ad916997..3dd1b0160a97 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -2134,15 +2134,7 @@ pub fn parse_download_ci_llvm<'a>( asserts: bool, ) -> bool { let dwn_ctx = dwn_ctx.as_ref(); - - // We don't ever want to use `true` on CI, as we should not - // download upstream artifacts if there are any local modifications. - let default = if dwn_ctx.is_running_on_ci { - StringOrBool::String("if-unchanged".to_string()) - } else { - StringOrBool::Bool(true) - }; - let download_ci_llvm = download_ci_llvm.unwrap_or(default); + let download_ci_llvm = download_ci_llvm.unwrap_or(StringOrBool::Bool(true)); let if_unchanged = || { if rust_info.is_from_tarball() { @@ -2174,8 +2166,9 @@ pub fn parse_download_ci_llvm<'a>( ); } - if b && dwn_ctx.is_running_on_ci { - // On CI, we must always rebuild LLVM if there were any modifications to it + #[cfg(not(test))] + if b && dwn_ctx.is_running_on_ci && CiEnv::is_rust_lang_managed_ci_job() { + // On rust-lang CI, we must always rebuild LLVM if there were any modifications to it panic!( "`llvm.download-ci-llvm` cannot be set to `true` on CI. Use `if-unchanged` instead." ); From b27942853efdb2fde2b6f73023c38ebfec3fcb8f Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Fri, 19 Sep 2025 21:53:06 +0200 Subject: [PATCH 663/682] naked_asm: emit a label starting with `func_end` The `cargo asm` tool pattern matches on such labels to figure out where functions end: normal functions generated by LLVM always do have such a label. We don't guarantee that naked functions emit such a label, but having `cargo asm` work is convenient --- compiler/rustc_codegen_ssa/src/mir/naked_asm.rs | 6 ++++++ tests/assembly-llvm/naked-functions/wasm32.rs | 1 + 2 files changed, 7 insertions(+) diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 31784cabf4af..e7239ebfecf6 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -228,6 +228,9 @@ fn prefix_and_suffix<'tcx>( writeln!(begin, "{asm_name}:").unwrap(); writeln!(end).unwrap(); + // emit a label starting with `func_end` for `cargo asm` and other tooling that might + // pattern match on assembly generated by LLVM. + writeln!(end, ".Lfunc_end_{asm_name}:").unwrap(); writeln!(end, ".size {asm_name}, . - {asm_name}").unwrap(); writeln!(end, ".popsection").unwrap(); if !arch_suffix.is_empty() { @@ -246,6 +249,7 @@ fn prefix_and_suffix<'tcx>( writeln!(begin, "{asm_name}:").unwrap(); writeln!(end).unwrap(); + writeln!(end, ".Lfunc_end_{asm_name}:").unwrap(); writeln!(end, ".popsection").unwrap(); if !arch_suffix.is_empty() { writeln!(end, "{}", arch_suffix).unwrap(); @@ -263,6 +267,7 @@ fn prefix_and_suffix<'tcx>( writeln!(begin, "{asm_name}:").unwrap(); writeln!(end).unwrap(); + writeln!(end, ".Lfunc_end_{asm_name}:").unwrap(); writeln!(end, ".popsection").unwrap(); if !arch_suffix.is_empty() { writeln!(end, "{}", arch_suffix).unwrap(); @@ -287,6 +292,7 @@ fn prefix_and_suffix<'tcx>( writeln!(end).unwrap(); // .size is ignored for function symbols, so we can skip it writeln!(end, "end_function").unwrap(); + writeln!(end, ".Lfunc_end_{asm_name}:").unwrap(); } BinaryFormat::Xcoff => { // the LLVM XCOFFAsmParser is extremely incomplete and does not implement many of the diff --git a/tests/assembly-llvm/naked-functions/wasm32.rs b/tests/assembly-llvm/naked-functions/wasm32.rs index 77547e82041f..4bf04dd39234 100644 --- a/tests/assembly-llvm/naked-functions/wasm32.rs +++ b/tests/assembly-llvm/naked-functions/wasm32.rs @@ -21,6 +21,7 @@ use minicore::*; // CHECK: .functype nop () -> () // CHECK-NOT: .size // CHECK: end_function +// CHECK-LABEL: .Lfunc_end_nop: #[no_mangle] #[unsafe(naked)] extern "C" fn nop() { From 54b15a66d89926c4e3e719f26a46b32653d1343f Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Fri, 19 Sep 2025 14:00:29 +0000 Subject: [PATCH 664/682] fixes for numerous clippy warnings --- compiler/rustc_driver_impl/src/lib.rs | 21 ++++++++++----------- compiler/rustc_driver_impl/src/print.rs | 2 +- compiler/rustc_hir_typeck/src/lib.rs | 4 ++-- compiler/rustc_hir_typeck/src/upvar.rs | 17 +++++++---------- compiler/rustc_hir_typeck/src/writeback.rs | 6 +++--- compiler/rustc_interface/src/passes.rs | 12 ++++++------ compiler/rustc_interface/src/queries.rs | 2 +- compiler/rustc_interface/src/util.rs | 4 ++-- 8 files changed, 32 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 4f875cf99ec2..8dab520cf36b 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -206,7 +206,7 @@ impl Callbacks for TimePassesCallbacks { // time because it will mess up the --print output. See #64339. // self.time_passes = (config.opts.prints.is_empty() && config.opts.unstable_opts.time_passes) - .then(|| config.opts.unstable_opts.time_passes_format); + .then_some(config.opts.unstable_opts.time_passes_format); config.opts.trimmed_def_paths = true; } } @@ -439,8 +439,9 @@ fn make_input(early_dcx: &EarlyDiagCtxt, free_matches: &[String]) -> Option() + .expect("UNSTABLE_RUSTDOC_TEST_LINE needs to be a number"); FileName::doc_test_source_code(PathBuf::from(path), line) } Err(_) => FileName::anon_source_code(&input), @@ -474,8 +475,7 @@ fn handle_explain(early_dcx: &EarlyDiagCtxt, registry: Registry, code: &str, col let mut text = String::new(); // Slice off the leading newline and print. for line in description.lines() { - let indent_level = - line.find(|c: char| !c.is_whitespace()).unwrap_or_else(|| line.len()); + let indent_level = line.find(|c: char| !c.is_whitespace()).unwrap_or(line.len()); let dedented_line = &line[indent_level..]; if dedented_line.starts_with("```") { is_in_code_block = !is_in_code_block; @@ -547,7 +547,7 @@ fn show_md_content_with_pager(content: &str, color: ColorConfig) { // The pager failed. Try to print pretty output to stdout. if let Some((bufwtr, mdbuf)) = &pretty_data - && bufwtr.print(&mdbuf).is_ok() + && bufwtr.print(mdbuf).is_ok() { return; } @@ -598,8 +598,7 @@ fn process_rlink(sess: &Session, compiler: &interface::Compiler) { fn list_metadata(sess: &Session, metadata_loader: &dyn MetadataLoader) { match sess.io.input { - Input::File(ref ifile) => { - let path = &(*ifile); + Input::File(ref path) => { let mut v = Vec::new(); locator::list_file_metadata( &sess.target, @@ -833,7 +832,7 @@ fn print_crate_info( SupportedCrateTypes => { let supported_crate_types = CRATE_TYPES .iter() - .filter(|(_, crate_type)| !invalid_output_for_target(&sess, *crate_type)) + .filter(|(_, crate_type)| !invalid_output_for_target(sess, *crate_type)) .filter(|(_, crate_type)| *crate_type != CrateType::Sdylib) .map(|(crate_type_sym, _)| *crate_type_sym) .collect::>(); @@ -1434,7 +1433,7 @@ pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&DiagCtxt)) eprintln!(); if let Some(ice_path) = ice_path() - && let Ok(mut out) = File::options().create(true).append(true).open(&ice_path) + && let Ok(mut out) = File::options().create(true).append(true).open(ice_path) { // The current implementation always returns `Some`. let location = info.location().unwrap(); @@ -1510,7 +1509,7 @@ fn report_ice( let file = if let Some(path) = ice_path() { // Create the ICE dump target file. - match crate::fs::File::options().create(true).append(true).open(&path) { + match crate::fs::File::options().create(true).append(true).open(path) { Ok(mut file) => { dcx.emit_note(session_diagnostics::IcePath { path: path.clone() }); if FIRST_PANIC.swap(false, Ordering::SeqCst) { diff --git a/compiler/rustc_driver_impl/src/print.rs b/compiler/rustc_driver_impl/src/print.rs index 70de55320f7a..3f107eb7a61a 100644 --- a/compiler/rustc_driver_impl/src/print.rs +++ b/compiler/rustc_driver_impl/src/print.rs @@ -14,7 +14,7 @@ macro_rules! safe_println { } pub(crate) fn print(args: fmt::Arguments<'_>) { - if let Err(_) = io::stdout().write_fmt(args) { + if io::stdout().write_fmt(args).is_err() { rustc_errors::FatalError.raise(); } } diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 7f5397a7926d..43a23822fd1e 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -254,7 +254,7 @@ fn typeck_with_inspect<'tcx>( } fcx.select_obligations_where_possible(|_| {}); - if let None = fcx.infcx.tainted_by_errors() { + if fcx.infcx.tainted_by_errors().is_none() { fcx.report_ambiguity_errors(); } @@ -295,7 +295,7 @@ fn infer_type_if_missing<'tcx>(fcx: &FnCtxt<'_, 'tcx>, node: Node<'tcx>) -> Opti } else if let Node::AnonConst(_) = node { let id = tcx.local_def_id_to_hir_id(def_id); match tcx.parent_hir_node(id) { - Node::Ty(&hir::Ty { kind: hir::TyKind::Typeof(ref anon_const), span, .. }) + Node::Ty(&hir::Ty { kind: hir::TyKind::Typeof(anon_const), span, .. }) if anon_const.hir_id == id => { Some(fcx.next_ty_var(span)) diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index 06acff06a51a..0a7b458f3fb3 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -421,7 +421,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Not all upvars are captured by ref, so use // `apply_capture_kind_on_capture_ty` to ensure that we // compute the right captured type. - return apply_capture_kind_on_capture_ty( + apply_capture_kind_on_capture_ty( self.tcx, upvar_ty, capture, @@ -430,7 +430,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { self.tcx.lifetimes.re_erased }, - ); + ) }, ), ); @@ -529,11 +529,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // process any deferred resolutions. let deferred_call_resolutions = self.remove_deferred_call_resolutions(closure_def_id); for deferred_call_resolution in deferred_call_resolutions { - deferred_call_resolution.resolve(&mut FnCtxt::new( - self, - self.param_env, - closure_def_id, - )); + deferred_call_resolution.resolve(&FnCtxt::new(self, self.param_env, closure_def_id)); } } @@ -1493,7 +1489,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Notation: /// - Ty(place): Type of place /// - `(a, b)`: Represents the function parameters `base_path_ty` and `captured_by_move_projs` - /// respectively. + /// respectively. /// ```ignore (illustrative) /// (Ty(w), [ &[p, x], &[c] ]) /// // | @@ -2179,9 +2175,10 @@ fn restrict_precision_for_unsafe( (place, curr_mode) } -/// Truncate projections so that following rules are obeyed by the captured `place`: +/// Truncate projections so that the following rules are obeyed by the captured `place`: /// - No Index projections are captured, since arrays are captured completely. -/// - No unsafe block is required to capture `place` +/// - No unsafe block is required to capture `place`. +/// /// Returns the truncated place and updated capture mode. fn restrict_capture_precision( place: Place<'_>, diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 90301d1b391a..6192420898f6 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -220,7 +220,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { // to use builtin indexing because the index type is known to be // usize-ish fn fix_index_builtin_expr(&mut self, e: &hir::Expr<'_>) { - if let hir::ExprKind::Index(ref base, ref index, _) = e.kind { + if let hir::ExprKind::Index(base, index, _) = e.kind { // All valid indexing looks like this; might encounter non-valid indexes at this point. let base_ty = self.typeck_results.expr_ty_adjusted(base); if let ty::Ref(_, base_ty_inner, _) = *base_ty.kind() { @@ -583,7 +583,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } if let Err(err) = opaque_type_has_defining_use_args( - &self.fcx, + self.fcx, opaque_type_key, hidden_type.span, DefiningScopeKind::HirTypeck, @@ -792,7 +792,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { fn visit_potentially_region_dependent_goals(&mut self) { let obligations = self.fcx.take_hir_typeck_potentially_region_dependent_goals(); - if let None = self.fcx.tainted_by_errors() { + if self.fcx.tainted_by_errors().is_none() { for obligation in obligations { let (predicate, mut cause) = self.fcx.resolve_vars_if_possible((obligation.predicate, obligation.cause)); diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index d39219bfd660..6cefe8875306 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -192,7 +192,7 @@ fn configure_and_expand( unsafe { env::set_var( "PATH", - &env::join_paths( + env::join_paths( new_path.iter().filter(|p| env::join_paths(iter::once(p)).is_ok()), ) .unwrap(), @@ -446,7 +446,7 @@ fn early_lint_checks(tcx: TyCtxt<'_>, (): ()) { let prev_source = sess.psess.source_map().span_to_prev_source(first_span); let ferris_fix = prev_source .map_or(FerrisFix::SnakeCase, |source| { - let mut source_before_ferris = source.trim_end().split_whitespace().rev(); + let mut source_before_ferris = source.split_whitespace().rev(); match source_before_ferris.next() { Some("struct" | "trait" | "mod" | "union" | "type" | "enum") => { FerrisFix::PascalCase @@ -500,7 +500,7 @@ fn env_var_os<'tcx>(tcx: TyCtxt<'tcx>, key: &'tcx OsStr) -> Option<&'tcx OsStr> // properly change-tracked. tcx.sess.psess.env_depinfo.borrow_mut().insert(( Symbol::intern(&key.to_string_lossy()), - value.as_ref().and_then(|value| value.to_str()).map(|value| Symbol::intern(&value)), + value.as_ref().and_then(|value| value.to_str()).map(|value| Symbol::intern(value)), )); value_tcx @@ -824,7 +824,7 @@ pub fn write_dep_info(tcx: TyCtxt<'_>) { let outputs = tcx.output_filenames(()); let output_paths = - generated_output_paths(tcx, &outputs, sess.io.output_file.is_some(), crate_name); + generated_output_paths(tcx, outputs, sess.io.output_file.is_some(), crate_name); // Ensure the source file isn't accidentally overwritten during compilation. if let Some(input_path) = sess.io.input.opt_path() { @@ -847,7 +847,7 @@ pub fn write_dep_info(tcx: TyCtxt<'_>) { } } - write_out_deps(tcx, &outputs, &output_paths); + write_out_deps(tcx, outputs, &output_paths); let only_dep_info = sess.opts.output_types.contains_key(&OutputType::DepInfo) && sess.opts.output_types.len() == 1; @@ -1303,7 +1303,7 @@ pub(crate) fn parse_crate_name( let rustc_hir::Attribute::Parsed(AttributeKind::CrateName { name, name_span, .. }) = AttributeParser::parse_limited_should_emit( sess, - &attrs, + attrs, sym::crate_name, DUMMY_SP, rustc_ast::node_id::CRATE_NODE_ID, diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 370e886c525f..280214ab4183 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -94,7 +94,7 @@ impl Linker { &rlink_file, &codegen_results, &self.metadata, - &*self.output_filenames, + &self.output_filenames, ) .unwrap_or_else(|error| { sess.dcx().emit_fatal(FailedWritingFile { path: &rlink_file, error }) diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 061c764e619a..26e09c95e769 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -243,7 +243,7 @@ pub(crate) fn run_in_thread_pool_with_globals< let query_map = rustc_span::set_session_globals_then(unsafe { &*(session_globals as *const SessionGlobals) }, || { // Ensure there was no errors collecting all active jobs. // We need the complete map to ensure we find a cycle to break. - QueryCtxt::new(tcx).collect_active_jobs().ok().expect("failed to collect active queries in deadlock handler") + QueryCtxt::new(tcx).collect_active_jobs().expect("failed to collect active queries in deadlock handler") }); break_query_cycles(query_map, ®istry); }) @@ -561,7 +561,7 @@ pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> Outpu } Some(out_file.clone()) }; - if sess.io.output_dir != None { + if sess.io.output_dir.is_some() { sess.dcx().emit_warn(errors::IgnoringOutDir); } From 4841d8c5ffb6a7bd025c14d8e9d2c23e6c458792 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Fri, 19 Sep 2025 22:14:50 +0000 Subject: [PATCH 665/682] generate list of all variants with `target_spec_enum` This helps us avoid the hardcoded lists elsewhere. --- compiler/rustc_codegen_llvm/src/lib.rs | 16 +++------------- compiler/rustc_session/src/config/cfg.rs | 6 ++++-- compiler/rustc_target/src/lib.rs | 23 ++++++++++++----------- compiler/rustc_target/src/spec/mod.rs | 16 ---------------- 4 files changed, 19 insertions(+), 42 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 6fb23d098433..13bdb7cb1a27 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -45,6 +45,7 @@ use rustc_middle::util::Providers; use rustc_session::Session; use rustc_session::config::{OptLevel, OutputFilenames, PrintKind, PrintRequest}; use rustc_span::Symbol; +use rustc_target::spec::{RelocModel, TlsModel}; mod abi; mod allocator; @@ -244,16 +245,7 @@ impl CodegenBackend for LlvmCodegenBackend { match req.kind { PrintKind::RelocationModels => { writeln!(out, "Available relocation models:").unwrap(); - for name in &[ - "static", - "pic", - "pie", - "dynamic-no-pic", - "ropi", - "rwpi", - "ropi-rwpi", - "default", - ] { + for name in RelocModel::ALL.iter().map(RelocModel::desc).chain(["default"]) { writeln!(out, " {name}").unwrap(); } writeln!(out).unwrap(); @@ -267,9 +259,7 @@ impl CodegenBackend for LlvmCodegenBackend { } PrintKind::TlsModels => { writeln!(out, "Available TLS models:").unwrap(); - for name in - &["global-dynamic", "local-dynamic", "initial-exec", "local-exec", "emulated"] - { + for name in TlsModel::ALL.iter().map(TlsModel::desc) { writeln!(out, " {name}").unwrap(); } writeln!(out).unwrap(); diff --git a/compiler/rustc_session/src/config/cfg.rs b/compiler/rustc_session/src/config/cfg.rs index 8f63ce6f0ae8..7e970461ab73 100644 --- a/compiler/rustc_session/src/config/cfg.rs +++ b/compiler/rustc_session/src/config/cfg.rs @@ -374,11 +374,13 @@ impl CheckCfg { ins!(sym::overflow_checks, no_values); - ins!(sym::panic, empty_values).extend(&PanicStrategy::all()); + ins!(sym::panic, empty_values) + .extend(PanicStrategy::ALL.iter().map(PanicStrategy::desc_symbol)); ins!(sym::proc_macro, no_values); - ins!(sym::relocation_model, empty_values).extend(RelocModel::all()); + ins!(sym::relocation_model, empty_values) + .extend(RelocModel::ALL.iter().map(RelocModel::desc_symbol)); let sanitize_values = SanitizerSet::all() .into_iter() diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs index b3fe1fffcceb..8c6a77cba8ba 100644 --- a/compiler/rustc_target/src/lib.rs +++ b/compiler/rustc_target/src/lib.rs @@ -76,10 +76,10 @@ fn find_relative_libdir(sysroot: &Path) -> std::borrow::Cow<'static, str> { macro_rules! target_spec_enum { ( $( #[$attr:meta] )* - pub enum $name:ident { + pub enum $Name:ident { $( $( #[$variant_attr:meta] )* - $variant:ident = $string:literal, + $Variant:ident = $string:literal, )* } parse_error_type = $parse_error_type:literal; @@ -87,20 +87,20 @@ macro_rules! target_spec_enum { $( #[$attr] )* #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] #[derive(schemars::JsonSchema)] - pub enum $name { + pub enum $Name { $( $( #[$variant_attr] )* #[serde(rename = $string)] // for JSON schema generation only - $variant, + $Variant, )* } - impl FromStr for $name { + impl FromStr for $Name { type Err = String; fn from_str(s: &str) -> Result { Ok(match s { - $( $string => Self::$variant, )* + $( $string => Self::$Variant, )* _ => { let all = [$( concat!("'", $string, "'") ),*].join(", "); return Err(format!("invalid {}: '{s}'. allowed values: {all}", $parse_error_type)); @@ -109,24 +109,25 @@ macro_rules! target_spec_enum { } } - impl $name { + impl $Name { + pub const ALL: &'static [$Name] = &[ $( $Name::$Variant, )* ]; pub fn desc(&self) -> &'static str { match self { - $( Self::$variant => $string, )* + $( Self::$Variant => $string, )* } } } - impl crate::json::ToJson for $name { + impl crate::json::ToJson for $Name { fn to_json(&self) -> crate::json::Json { self.desc().to_json() } } - crate::json::serde_deserialize_from_str!($name); + crate::json::serde_deserialize_from_str!($Name); - impl std::fmt::Display for $name { + impl std::fmt::Display for $Name { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(self.desc()) } diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 07fb1ce63f7c..f705af52bd86 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -854,10 +854,6 @@ impl PanicStrategy { PanicStrategy::Abort => sym::abort, } } - - pub const fn all() -> [Symbol; 2] { - [Self::Abort.desc_symbol(), Self::Unwind.desc_symbol()] - } } crate::target_spec_enum! { @@ -974,18 +970,6 @@ impl RelocModel { RelocModel::RopiRwpi => sym::ropi_rwpi, } } - - pub const fn all() -> [Symbol; 7] { - [ - RelocModel::Static.desc_symbol(), - RelocModel::Pic.desc_symbol(), - RelocModel::Pie.desc_symbol(), - RelocModel::DynamicNoPic.desc_symbol(), - RelocModel::Ropi.desc_symbol(), - RelocModel::Rwpi.desc_symbol(), - RelocModel::RopiRwpi.desc_symbol(), - ] - } } crate::target_spec_enum! { From 6a5838105d8496b9db5dc61b0bd12dda70f7bbe1 Mon Sep 17 00:00:00 2001 From: Thalia Archibald Date: Fri, 19 Sep 2025 18:13:21 -0600 Subject: [PATCH 666/682] Fix unsupported std::sys::thread Fixes building std for any platform with an unsupported thread abstraction. This includes {aarch64,armv7,x86_64}-unknown-trusty and riscv32im-risc0-zkvm-elf, which explicitly include the unsupported module, and platforms with no PAL. Bug fix for PR 145177 (std: move thread into sys). --- library/std/src/sys/pal/trusty/mod.rs | 2 -- library/std/src/sys/pal/unsupported/mod.rs | 1 - library/std/src/sys/pal/zkvm/mod.rs | 2 -- library/std/src/sys/process/unix/vxworks.rs | 3 +-- 4 files changed, 1 insertion(+), 7 deletions(-) diff --git a/library/std/src/sys/pal/trusty/mod.rs b/library/std/src/sys/pal/trusty/mod.rs index 275f60624633..cf0c098f8a2f 100644 --- a/library/std/src/sys/pal/trusty/mod.rs +++ b/library/std/src/sys/pal/trusty/mod.rs @@ -7,8 +7,6 @@ mod common; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -#[path = "../unsupported/thread.rs"] -pub mod thread; #[path = "../unsupported/time.rs"] pub mod time; diff --git a/library/std/src/sys/pal/unsupported/mod.rs b/library/std/src/sys/pal/unsupported/mod.rs index 5e3295b1331a..e64bbc7c6169 100644 --- a/library/std/src/sys/pal/unsupported/mod.rs +++ b/library/std/src/sys/pal/unsupported/mod.rs @@ -2,7 +2,6 @@ pub mod os; pub mod pipe; -pub mod thread; pub mod time; mod common; diff --git a/library/std/src/sys/pal/zkvm/mod.rs b/library/std/src/sys/pal/zkvm/mod.rs index e1efa2406858..9069c8d12fa1 100644 --- a/library/std/src/sys/pal/zkvm/mod.rs +++ b/library/std/src/sys/pal/zkvm/mod.rs @@ -14,8 +14,6 @@ pub mod abi; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -#[path = "../unsupported/thread.rs"] -pub mod thread; #[path = "../unsupported/time.rs"] pub mod time; diff --git a/library/std/src/sys/process/unix/vxworks.rs b/library/std/src/sys/process/unix/vxworks.rs index 2275cbb946a9..b9298f5fa44c 100644 --- a/library/std/src/sys/process/unix/vxworks.rs +++ b/library/std/src/sys/process/unix/vxworks.rs @@ -4,8 +4,7 @@ use libc::{self, RTP_ID, c_char, c_int}; use super::common::*; use crate::io::{self, ErrorKind}; use crate::num::NonZero; -use crate::sys::cvt; -use crate::sys::pal::thread; +use crate::sys::{cvt, thread}; use crate::{fmt, sys}; //////////////////////////////////////////////////////////////////////////////// From 776c199c7bb7a26f2578087c28ee7be4d8941097 Mon Sep 17 00:00:00 2001 From: Thalia Archibald Date: Fri, 19 Sep 2025 18:30:18 -0600 Subject: [PATCH 667/682] Fix std build for xtensa --- library/core/src/ffi/va_list.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/core/src/ffi/va_list.rs b/library/core/src/ffi/va_list.rs index 643bd95df846..0d4ccb5aeb28 100644 --- a/library/core/src/ffi/va_list.rs +++ b/library/core/src/ffi/va_list.rs @@ -2,6 +2,7 @@ //! //! Better known as "varargs". +#[cfg(not(target_arch = "xtensa"))] use crate::ffi::c_void; #[allow(unused_imports)] use crate::fmt; From db4d4eff56a38c7c1096e0cf76ee3bd523bade05 Mon Sep 17 00:00:00 2001 From: Thalia Archibald Date: Fri, 19 Sep 2025 18:57:49 -0600 Subject: [PATCH 668/682] Update cfg_if! to cfg_select! The macro is now builtin. --- library/std/src/sys/env_consts.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/sys/env_consts.rs b/library/std/src/sys/env_consts.rs index 9683fd47cf96..711ba0a5f8a9 100644 --- a/library/std/src/sys/env_consts.rs +++ b/library/std/src/sys/env_consts.rs @@ -2,7 +2,7 @@ // Replaces the #[else] gate with #[cfg(not(any(…)))] of all the other gates. // This ensures that they must be mutually exclusive and do not have precedence -// like cfg_if!. +// like cfg_select!. macro cfg_unordered( $(#[cfg($cfg:meta)] $os:item)* #[else] $fallback:item From 42b38e3781ee3c34cf3df4dea4cd836a814cfb60 Mon Sep 17 00:00:00 2001 From: Sidney Cammeresi Date: Fri, 19 Sep 2025 21:17:21 -0700 Subject: [PATCH 669/682] Add unstable attribute to BTreeMap-related allocator generics Although these types aren't directly constructable externally, since they're pub, I think this omission was an oversight. --- library/alloc/src/collections/btree/map.rs | 6 +++++- library/alloc/src/collections/btree/map/entry.rs | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 98f11e2ea57f..cebd25c6039c 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -546,7 +546,11 @@ impl fmt::Debug for ValuesMut<'_, K, V> { /// [`into_keys`]: BTreeMap::into_keys #[must_use = "iterators are lazy and do nothing unless consumed"] #[stable(feature = "map_into_keys_values", since = "1.54.0")] -pub struct IntoKeys { +pub struct IntoKeys< + K, + V, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator + Clone = Global, +> { inner: IntoIter, } diff --git a/library/alloc/src/collections/btree/map/entry.rs b/library/alloc/src/collections/btree/map/entry.rs index 21486651b0b0..df51be3de54b 100644 --- a/library/alloc/src/collections/btree/map/entry.rs +++ b/library/alloc/src/collections/btree/map/entry.rs @@ -99,7 +99,12 @@ impl Debug for OccupiedEntry<'_, /// /// Contains the occupied entry, and the value that was not inserted. #[unstable(feature = "map_try_insert", issue = "82766")] -pub struct OccupiedError<'a, K: 'a, V: 'a, A: Allocator + Clone = Global> { +pub struct OccupiedError< + 'a, + K: 'a, + V: 'a, + #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator + Clone = Global, +> { /// The entry in the map that was already occupied. pub entry: OccupiedEntry<'a, K, V, A>, /// The value which was not inserted, because the entry was already occupied. From 4d8655e8d0b69cd1a424ccadd4c9043124a627f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Sat, 20 Sep 2025 09:38:53 +0300 Subject: [PATCH 670/682] Bump rustc crates once more --- src/tools/rust-analyzer/Cargo.lock | 44 +++++++++---------- src/tools/rust-analyzer/Cargo.toml | 16 +++---- .../crates/hir-ty/src/next_solver/inspect.rs | 6 +-- 3 files changed, 32 insertions(+), 34 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 097bb81f6676..17dea1ba4cde 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1807,9 +1807,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_abi" -version = "0.131.0" +version = "0.132.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "016c05852e89655395fbf7d5e729e31cd58b2690480b3a8468eb2b38c91f3756" +checksum = "597bb303548ddcca3a2eb05af254508aaf39cf334d4350bb5da51de1eb728859" dependencies = [ "bitflags 2.9.1", "ra-ap-rustc_hashes", @@ -1819,24 +1819,24 @@ dependencies = [ [[package]] name = "ra-ap-rustc_ast_ir" -version = "0.131.0" +version = "0.132.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5bff48bd0a26f17a4e2e8610bc393296c3d002e221f5c6c4d2875e6a64a3b4b" +checksum = "78982b4e4432ee4b938e47bb5c8f1a5a5a88c27c782f193aefcc12a3250bd2e2" [[package]] name = "ra-ap-rustc_hashes" -version = "0.131.0" +version = "0.132.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d15e7571f6f31f6112fd2fcbc3450a6ef477cc6bfe51643a2b110436a7455f0" +checksum = "2f7f33a422f724cc1ab43972cdd76a556b17fc256f301d23be620adfc8351df7" dependencies = [ "rustc-stable-hash", ] [[package]] name = "ra-ap-rustc_index" -version = "0.131.0" +version = "0.132.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a30f0a15682f1194e5812cc3544266f3494ca8fb3c5033e8b9c08f4f8b8c6d" +checksum = "8a6006023c8be18c3ac225d69c1b42f55b3f597f3db03fb40764b4cf1454fd13" dependencies = [ "ra-ap-rustc_index_macros", "smallvec", @@ -1844,9 +1844,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index_macros" -version = "0.131.0" +version = "0.132.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2f82c9176b964591e1657a9f0fae2850525542d39075c49c2459afc291f4804" +checksum = "9217c29f7fcc30d07ed13a62262144f665410ef1460202599ae924f9ae47ad78" dependencies = [ "proc-macro2", "quote", @@ -1855,9 +1855,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_lexer" -version = "0.131.0" +version = "0.132.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c71ff7da5ca1e8a0c65d5e7cec7ab2a7e9e45b6859543bd2d0be21bee631c" +checksum = "573ad4f5da620e8ba1849d8862866abd7bc765c3d81cb2488c3ecbef33ce2c69" dependencies = [ "memchr", "unicode-properties", @@ -1866,9 +1866,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_next_trait_solver" -version = "0.131.0" +version = "0.132.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "839f521a6cd97c71b2b6681604ace38a9c6737f7a223d072fa8f909879f9dc4b" +checksum = "0d42b095b99e988aeb94622ae62ebda4b7de55d7d98846eec352b8a5a2b8a858" dependencies = [ "derive-where", "ra-ap-rustc_index", @@ -1879,9 +1879,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_parse_format" -version = "0.131.0" +version = "0.132.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83e7872a4fa0620937b60fc6270aa8c31864cb324357553e2be5b2bdc25fa89a" +checksum = "a21b4e95cb45f840c172493c05f5b9471cf44adb2eccf95d76a0d76e88007870" dependencies = [ "ra-ap-rustc_lexer", "rustc-literal-escaper 0.0.5", @@ -1889,9 +1889,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_pattern_analysis" -version = "0.131.0" +version = "0.132.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ecd7f9b960c8cf1e9d02a25297f52a520134132164cfedd3e69fee4f294d41c" +checksum = "b6aeacef1248066f7b67e7296ef135eeab6446d5d2a5c7f02b8d7b747b41e39b" dependencies = [ "ra-ap-rustc_index", "rustc-hash 2.1.1", @@ -1902,9 +1902,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_type_ir" -version = "0.131.0" +version = "0.132.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9494498f8f7c57a5b4ea4e35a6c554cca8d5323adb534d13dc6cd117a7e00e4d" +checksum = "52e35ee9e052406035016b8e6d54ca202bc39ccba1702780b33b2d5fb10d1da8" dependencies = [ "arrayvec", "bitflags 2.9.1", @@ -1922,9 +1922,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_type_ir_macros" -version = "0.131.0" +version = "0.132.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28ed893760b86529080af59438c28dfa9ca976b821cfd0a3b0eb9591932847e6" +checksum = "9b934c956b0c88df8176803416b69d85d2c392a69c8aa794a4c338f22c527d38" dependencies = [ "proc-macro2", "quote", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index cfbd8a56b76f..0401367f7864 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -87,14 +87,14 @@ vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" } edition = { path = "./crates/edition", version = "0.0.0" } -ra-ap-rustc_lexer = { version = "0.131", default-features = false } -ra-ap-rustc_parse_format = { version = "0.131", default-features = false } -ra-ap-rustc_index = { version = "0.131", default-features = false } -ra-ap-rustc_abi = { version = "0.131", default-features = false } -ra-ap-rustc_pattern_analysis = { version = "0.131", default-features = false } -ra-ap-rustc_ast_ir = { version = "0.131", default-features = false } -ra-ap-rustc_type_ir = { version = "0.131", default-features = false } -ra-ap-rustc_next_trait_solver = { version = "0.131", default-features = false } +ra-ap-rustc_lexer = { version = "0.132", default-features = false } +ra-ap-rustc_parse_format = { version = "0.132", default-features = false } +ra-ap-rustc_index = { version = "0.132", default-features = false } +ra-ap-rustc_abi = { version = "0.132", default-features = false } +ra-ap-rustc_pattern_analysis = { version = "0.132", default-features = false } +ra-ap-rustc_ast_ir = { version = "0.132", default-features = false } +ra-ap-rustc_type_ir = { version = "0.132", default-features = false } +ra-ap-rustc_next_trait_solver = { version = "0.132", default-features = false } # local crates that aren't published to crates.io. These should not have versions. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs index 128135d8debe..bc19d51d23e3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs @@ -2,11 +2,9 @@ pub use rustc_next_trait_solver::solve::inspect::*; use rustc_ast_ir::try_visit; use rustc_next_trait_solver::{ + canonical::instantiate_canonical_state, resolve::eager_resolve_vars, - solve::{ - SolverDelegateEvalExt, - inspect::{self, instantiate_canonical_state}, - }, + solve::{SolverDelegateEvalExt, inspect}, }; use rustc_type_ir::{ VisitorResult, From ac3c480388d27ee9ebeb23d308bd37289d37bc41 Mon Sep 17 00:00:00 2001 From: tk <49250442+tkr-sh@users.noreply.github.com> Date: Wed, 14 May 2025 00:54:47 +0200 Subject: [PATCH 671/682] docs: improve doc of some methods w/ ranges --- library/alloc/src/collections/vec_deque/mod.rs | 12 ++++++------ library/alloc/src/string.rs | 12 ++++++------ library/alloc/src/vec/mod.rs | 8 ++++---- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 2fce5c3e737e..d589860524b8 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -1486,8 +1486,8 @@ impl VecDeque { /// /// # Panics /// - /// Panics if the starting point is greater than the end point or if - /// the end point is greater than the length of the deque. + /// Panics if the range has `start_bound > end_bound`, or, if the range is + /// bounded on either end and past the length of the deque. /// /// # Examples /// @@ -1522,8 +1522,8 @@ impl VecDeque { /// /// # Panics /// - /// Panics if the starting point is greater than the end point or if - /// the end point is greater than the length of the deque. + /// Panics if the range has `start_bound > end_bound`, or, if the range is + /// bounded on either end and past the length of the deque. /// /// # Examples /// @@ -1568,8 +1568,8 @@ impl VecDeque { /// /// # Panics /// - /// Panics if the starting point is greater than the end point or if - /// the end point is greater than the length of the deque. + /// Panics if the range has `start_bound > end_bound`, or, if the range is + /// bounded on either end and past the length of the deque. /// /// # Leaking /// diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 1d0dd4be1b66..e669c4708ad0 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -1117,8 +1117,8 @@ impl String { /// /// # Panics /// - /// Panics if the starting point or end point do not lie on a [`char`] - /// boundary, or if they're out of bounds. + /// Panics if the range has `start_bound > end_bound`, or, if the range is + /// bounded on either end and does not lie on a [`char`] boundary. /// /// # Examples /// @@ -1939,8 +1939,8 @@ impl String { /// /// # Panics /// - /// Panics if the starting point or end point do not lie on a [`char`] - /// boundary, or if they're out of bounds. + /// Panics if the range has `start_bound > end_bound`, or, if the range is + /// bounded on either end and does not lie on a [`char`] boundary. /// /// # Leaking /// @@ -2050,8 +2050,8 @@ impl String { /// /// # Panics /// - /// Panics if the starting point or end point do not lie on a [`char`] - /// boundary, or if they're out of bounds. + /// Panics if the range has `start_bound > end_bound`, or, if the range is + /// bounded on either end and does not lie on a [`char`] boundary. /// /// # Examples /// diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 1b80b43abc35..10c7ee4f6c89 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -2796,8 +2796,8 @@ impl Vec { /// /// # Panics /// - /// Panics if the starting point is greater than the end point or if - /// the end point is greater than the length of the vector. + /// Panics if the range has `start_bound > end_bound`, or, if the range is + /// bounded on either end and past the length of the vector. /// /// # Leaking /// @@ -3860,8 +3860,8 @@ impl Vec { /// /// # Panics /// - /// Panics if the starting point is greater than the end point or if - /// the end point is greater than the length of the vector. + /// Panics if the range has `start_bound > end_bound`, or, if the range is + /// bounded on either end and past the length of the vector. /// /// # Examples /// From fd7f8b2b25d195d47682bbddb9de38b01ae9f6da Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 20 Sep 2025 17:58:32 +0200 Subject: [PATCH 672/682] [GCC backend] Ignore failing ui abi tests --- tests/ui/abi/abi-sysv64-arg-passing.rs | 1 + tests/ui/abi/abi-sysv64-register-usage.rs | 1 + tests/ui/abi/issue-28676.rs | 2 ++ tests/ui/abi/large-byval-align.rs | 1 + tests/ui/abi/numbers-arithmetic/float-struct.rs | 1 + tests/ui/abi/stack-probes-lto.rs | 1 + tests/ui/abi/stack-probes.rs | 1 + tests/ui/abi/stack-protector.rs | 1 + tests/ui/abi/struct-enums/struct-return.rs | 2 ++ tests/ui/abi/variadic-ffi.rs | 2 ++ tests/ui/abi/x86stdcall.rs | 1 + 11 files changed, 14 insertions(+) diff --git a/tests/ui/abi/abi-sysv64-arg-passing.rs b/tests/ui/abi/abi-sysv64-arg-passing.rs index c18752418a1d..362a1862f9d4 100644 --- a/tests/ui/abi/abi-sysv64-arg-passing.rs +++ b/tests/ui/abi/abi-sysv64-arg-passing.rs @@ -28,6 +28,7 @@ //@ ignore-arm //@ ignore-aarch64 //@ ignore-windows +//@ ignore-backends: gcc // Windows is ignored because bootstrap doesn't yet know to compile rust_test_helpers with // the sysv64 ABI on Windows. diff --git a/tests/ui/abi/abi-sysv64-register-usage.rs b/tests/ui/abi/abi-sysv64-register-usage.rs index d2fb2ae53ac7..cf1620db6f7b 100644 --- a/tests/ui/abi/abi-sysv64-register-usage.rs +++ b/tests/ui/abi/abi-sysv64-register-usage.rs @@ -6,6 +6,7 @@ //@ ignore-arm //@ ignore-aarch64 //@ needs-asm-support +//@ ignore-backends: gcc #[cfg(target_arch = "x86_64")] pub extern "sysv64" fn all_the_registers( diff --git a/tests/ui/abi/issue-28676.rs b/tests/ui/abi/issue-28676.rs index 8640f5aad21c..2abb4ce52b3b 100644 --- a/tests/ui/abi/issue-28676.rs +++ b/tests/ui/abi/issue-28676.rs @@ -1,4 +1,6 @@ //@ run-pass +//@ ignore-backends: gcc + #![allow(dead_code)] #![allow(improper_ctypes)] diff --git a/tests/ui/abi/large-byval-align.rs b/tests/ui/abi/large-byval-align.rs index c1de841178fc..69418e1cbc7b 100644 --- a/tests/ui/abi/large-byval-align.rs +++ b/tests/ui/abi/large-byval-align.rs @@ -1,6 +1,7 @@ //@ compile-flags: -Copt-level=0 //@ only-x86_64 //@ build-pass +//@ ignore-backends: gcc #[repr(align(536870912))] pub struct A(i64); diff --git a/tests/ui/abi/numbers-arithmetic/float-struct.rs b/tests/ui/abi/numbers-arithmetic/float-struct.rs index a958dc272722..dc4c536a14e2 100644 --- a/tests/ui/abi/numbers-arithmetic/float-struct.rs +++ b/tests/ui/abi/numbers-arithmetic/float-struct.rs @@ -1,4 +1,5 @@ //@ run-pass +//@ ignore-backends: gcc use std::fmt::Debug; use std::hint::black_box; diff --git a/tests/ui/abi/stack-probes-lto.rs b/tests/ui/abi/stack-probes-lto.rs index c6e5bea5f422..6203be90590d 100644 --- a/tests/ui/abi/stack-probes-lto.rs +++ b/tests/ui/abi/stack-probes-lto.rs @@ -13,5 +13,6 @@ //@ ignore-tvos Stack probes are enabled, but the SIGSEGV handler isn't //@ ignore-watchos Stack probes are enabled, but the SIGSEGV handler isn't //@ ignore-visionos Stack probes are enabled, but the SIGSEGV handler isn't +//@ ignore-backends: gcc include!("stack-probes.rs"); diff --git a/tests/ui/abi/stack-probes.rs b/tests/ui/abi/stack-probes.rs index f0fbd80d2e73..4d0301411e0f 100644 --- a/tests/ui/abi/stack-probes.rs +++ b/tests/ui/abi/stack-probes.rs @@ -10,6 +10,7 @@ //@ ignore-tvos Stack probes are enabled, but the SIGSEGV handler isn't //@ ignore-watchos Stack probes are enabled, but the SIGSEGV handler isn't //@ ignore-visionos Stack probes are enabled, but the SIGSEGV handler isn't +//@ ignore-backends: gcc use std::env; use std::mem::MaybeUninit; diff --git a/tests/ui/abi/stack-protector.rs b/tests/ui/abi/stack-protector.rs index 29332861977b..928b49c0eea5 100644 --- a/tests/ui/abi/stack-protector.rs +++ b/tests/ui/abi/stack-protector.rs @@ -4,6 +4,7 @@ //@ [ssp] compile-flags: -Z stack-protector=all //@ compile-flags: -C opt-level=2 //@ compile-flags: -g +//@ ignore-backends: gcc use std::env; use std::process::{Command, ExitStatus}; diff --git a/tests/ui/abi/struct-enums/struct-return.rs b/tests/ui/abi/struct-enums/struct-return.rs index 5b39c0de4546..5fe80d5f670b 100644 --- a/tests/ui/abi/struct-enums/struct-return.rs +++ b/tests/ui/abi/struct-enums/struct-return.rs @@ -1,4 +1,6 @@ //@ run-pass +//@ ignore-backends: gcc + #![allow(dead_code)] #[repr(C)] diff --git a/tests/ui/abi/variadic-ffi.rs b/tests/ui/abi/variadic-ffi.rs index 6cfae0f2a320..dfdbff33264b 100644 --- a/tests/ui/abi/variadic-ffi.rs +++ b/tests/ui/abi/variadic-ffi.rs @@ -1,4 +1,6 @@ //@ run-pass +//@ ignore-backends: gcc + #![feature(c_variadic)] use std::ffi::VaList; diff --git a/tests/ui/abi/x86stdcall.rs b/tests/ui/abi/x86stdcall.rs index c1bd35b80d26..77866c30f0e4 100644 --- a/tests/ui/abi/x86stdcall.rs +++ b/tests/ui/abi/x86stdcall.rs @@ -1,5 +1,6 @@ //@ run-pass //@ only-windows +//@ ignore-backends: gcc // GetLastError doesn't seem to work with stack switching #[cfg(windows)] From 5c0cb3af59e03b4248ae58ca4e65f14307320195 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sat, 20 Sep 2025 18:27:33 -0400 Subject: [PATCH 673/682] Fix old typo in lang_start_internal comment --- library/std/src/rt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs index b3f3b301e3db..8d95cc1fb574 100644 --- a/library/std/src/rt.rs +++ b/library/std/src/rt.rs @@ -161,7 +161,7 @@ fn lang_start_internal( // mechanism itself. // // There are a couple of instances where unwinding can begin. First is inside of the - // `rt::init`, `rt::cleanup` and similar functions controlled by bstd. In those instances a + // `rt::init`, `rt::cleanup` and similar functions controlled by std. In those instances a // panic is a std implementation bug. A quite likely one too, as there isn't any way to // prevent std from accidentally introducing a panic to these functions. Another is from // user code from `main` or, more nefariously, as described in e.g. issue #86030. From 3934fc9eb29061168327640ba7b9ca8f79ba01e2 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Tue, 16 Sep 2025 02:33:24 +0000 Subject: [PATCH 674/682] Consider errors in MIR as impossible predicates. --- .../src/impossible_predicates.rs | 33 ++++++----- tests/crashes/122904-2.rs | 17 ------ tests/crashes/139556.rs | 13 ----- tests/ui/consts/promoted_const_call2.rs | 1 - tests/ui/consts/promoted_const_call2.stderr | 17 ++---- .../122630.rs => ui/coroutine/moved-twice.rs} | 11 +++- tests/ui/coroutine/moved-twice.stderr | 24 ++++++++ .../recursive-drop-elaboration-2.rs | 19 ++++++ .../recursive-drop-elaboration-2.stderr | 30 ++++++++++ .../recursive-drop-elaboration.rs | 24 ++++++++ .../recursive-drop-elaboration.stderr | 58 +++++++++++++++++++ .../type-error-drop-elaboration.rs} | 3 +- .../type-error-drop-elaboration.stderr | 12 ++++ 13 files changed, 201 insertions(+), 61 deletions(-) delete mode 100644 tests/crashes/122904-2.rs delete mode 100644 tests/crashes/139556.rs rename tests/{crashes/122630.rs => ui/coroutine/moved-twice.rs} (62%) create mode 100644 tests/ui/coroutine/moved-twice.stderr create mode 100644 tests/ui/type-alias-impl-trait/recursive-drop-elaboration-2.rs create mode 100644 tests/ui/type-alias-impl-trait/recursive-drop-elaboration-2.stderr create mode 100644 tests/ui/type-alias-impl-trait/recursive-drop-elaboration.rs create mode 100644 tests/ui/type-alias-impl-trait/recursive-drop-elaboration.stderr rename tests/{crashes/125185.rs => ui/type-alias-impl-trait/type-error-drop-elaboration.rs} (78%) create mode 100644 tests/ui/type-alias-impl-trait/type-error-drop-elaboration.stderr diff --git a/compiler/rustc_mir_transform/src/impossible_predicates.rs b/compiler/rustc_mir_transform/src/impossible_predicates.rs index b03518de00a9..883ee32bdec9 100644 --- a/compiler/rustc_mir_transform/src/impossible_predicates.rs +++ b/compiler/rustc_mir_transform/src/impossible_predicates.rs @@ -28,6 +28,7 @@ use rustc_middle::mir::{Body, START_BLOCK, TerminatorKind}; use rustc_middle::ty::{TyCtxt, TypeFlags, TypeVisitableExt}; +use rustc_span::def_id::DefId; use rustc_trait_selection::traits; use tracing::trace; @@ -35,23 +36,29 @@ use crate::pass_manager::MirPass; pub(crate) struct ImpossiblePredicates; +fn has_impossible_predicates(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + let predicates = tcx.predicates_of(def_id).instantiate_identity(tcx); + tracing::trace!(?predicates); + let predicates = predicates.predicates.into_iter().filter(|p| { + !p.has_type_flags( + // Only consider global clauses to simplify. + TypeFlags::HAS_FREE_LOCAL_NAMES + // Clauses that refer to unevaluated constants as they cause cycles. + | TypeFlags::HAS_CT_PROJECTION, + ) + }); + let predicates: Vec<_> = traits::elaborate(tcx, predicates).collect(); + tracing::trace!(?predicates); + predicates.references_error() || traits::impossible_predicates(tcx, predicates) +} + impl<'tcx> MirPass<'tcx> for ImpossiblePredicates { #[tracing::instrument(level = "trace", skip(self, tcx, body))] fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { tracing::trace!(def_id = ?body.source.def_id()); - let predicates = tcx.predicates_of(body.source.def_id()).instantiate_identity(tcx); - tracing::trace!(?predicates); - let predicates = predicates.predicates.into_iter().filter(|p| { - !p.has_type_flags( - // Only consider global clauses to simplify. - TypeFlags::HAS_FREE_LOCAL_NAMES - // Clauses that refer to unevaluated constants as they cause cycles. - | TypeFlags::HAS_CT_PROJECTION, - ) - }); - let predicates: Vec<_> = traits::elaborate(tcx, predicates).collect(); - tracing::trace!(?predicates); - if predicates.references_error() || traits::impossible_predicates(tcx, predicates) { + let impossible = body.tainted_by_errors.is_some() + || has_impossible_predicates(tcx, body.source.def_id()); + if impossible { trace!("found unsatisfiable predicates"); // Clear the body to only contain a single `unreachable` statement. let bbs = body.basic_blocks.as_mut(); diff --git a/tests/crashes/122904-2.rs b/tests/crashes/122904-2.rs deleted file mode 100644 index db66b8625db0..000000000000 --- a/tests/crashes/122904-2.rs +++ /dev/null @@ -1,17 +0,0 @@ -//@ known-bug: #122904 -trait T {} - -type Alias<'a> = impl T; - -struct S; -impl<'a> T for &'a S {} - -#[define_opaque(Alias)] -fn with_positive(fun: impl Fn(Alias<'_>)) { - with_positive(|&n| ()); -} - -#[define_opaque(Alias)] -fn main(Alias<'_>) { - with_positive(|&a| ()); -} diff --git a/tests/crashes/139556.rs b/tests/crashes/139556.rs deleted file mode 100644 index 60dc8d7c3afc..000000000000 --- a/tests/crashes/139556.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@ known-bug: #139556 - -trait T {} - -type Alias<'a> = impl T; - -struct S; -impl<'a> T for &'a S {} - -#[define_opaque(Alias)] -fn with_positive(fun: impl Fn(Alias<'_>)) { - with_positive(|&n| ()); -} diff --git a/tests/ui/consts/promoted_const_call2.rs b/tests/ui/consts/promoted_const_call2.rs index f332cd18cea3..62391f098e5d 100644 --- a/tests/ui/consts/promoted_const_call2.rs +++ b/tests/ui/consts/promoted_const_call2.rs @@ -4,7 +4,6 @@ pub const C: () = { let _: &'static _ = &id(&String::new()); //~^ ERROR: temporary value dropped while borrowed //~| ERROR: temporary value dropped while borrowed - //~| ERROR: destructor of `String` cannot be evaluated at compile-time }; fn main() { diff --git a/tests/ui/consts/promoted_const_call2.stderr b/tests/ui/consts/promoted_const_call2.stderr index bdb43385d203..e62458d1a6ae 100644 --- a/tests/ui/consts/promoted_const_call2.stderr +++ b/tests/ui/consts/promoted_const_call2.stderr @@ -18,16 +18,8 @@ LL | let _: &'static _ = &id(&String::new()); | | creates a temporary value which is freed while still in use | type annotation requires that borrow lasts for `'static` -error[E0493]: destructor of `String` cannot be evaluated at compile-time - --> $DIR/promoted_const_call2.rs:4:30 - | -LL | let _: &'static _ = &id(&String::new()); - | ^^^^^^^^^^^^^ - value is dropped here - | | - | the destructor for this type cannot be evaluated in constants - error[E0716]: temporary value dropped while borrowed - --> $DIR/promoted_const_call2.rs:11:26 + --> $DIR/promoted_const_call2.rs:10:26 | LL | let _: &'static _ = &id(&String::new()); | ---------- ^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use @@ -38,7 +30,7 @@ LL | } | - temporary value is freed at the end of this statement error[E0716]: temporary value dropped while borrowed - --> $DIR/promoted_const_call2.rs:11:30 + --> $DIR/promoted_const_call2.rs:10:30 | LL | let _: &'static _ = &id(&String::new()); | ---------- ^^^^^^^^^^^^^ - temporary value is freed at the end of this statement @@ -46,7 +38,6 @@ LL | let _: &'static _ = &id(&String::new()); | | creates a temporary value which is freed while still in use | type annotation requires that borrow lasts for `'static` -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors -Some errors have detailed explanations: E0493, E0716. -For more information about an error, try `rustc --explain E0493`. +For more information about this error, try `rustc --explain E0716`. diff --git a/tests/crashes/122630.rs b/tests/ui/coroutine/moved-twice.rs similarity index 62% rename from tests/crashes/122630.rs rename to tests/ui/coroutine/moved-twice.rs index e66624431c51..72b83e274c92 100644 --- a/tests/crashes/122630.rs +++ b/tests/ui/coroutine/moved-twice.rs @@ -1,22 +1,27 @@ -//@ known-bug: #122630 +//! Regression test for #122630 //@ compile-flags: -Zvalidate-mir +#![feature(coroutines, coroutine_trait, yield_expr)] + use std::ops::Coroutine; const FOO_SIZE: usize = 1024; struct Foo([u8; FOO_SIZE]); impl Drop for Foo { - fn move_before_yield_with_noop() -> impl Coroutine {} + fn drop(&mut self) {} } fn overlap_move_points() -> impl Coroutine { - static || { + #[coroutine] static || { let first = Foo([0; FOO_SIZE]); yield; let second = first; yield; let second = first; + //~^ ERROR: use of moved value: `first` [E0382] yield; } } + +fn main() {} diff --git a/tests/ui/coroutine/moved-twice.stderr b/tests/ui/coroutine/moved-twice.stderr new file mode 100644 index 000000000000..2b21f6c59f0b --- /dev/null +++ b/tests/ui/coroutine/moved-twice.stderr @@ -0,0 +1,24 @@ +error[E0382]: use of moved value: `first` + --> $DIR/moved-twice.rs:21:22 + | +LL | let first = Foo([0; FOO_SIZE]); + | ----- move occurs because `first` has type `Foo`, which does not implement the `Copy` trait +LL | yield; +LL | let second = first; + | ----- value moved here +LL | yield; +LL | let second = first; + | ^^^^^ value used here after move + | +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/moved-twice.rs:9:1 + | +LL | struct Foo([u8; FOO_SIZE]); + | ^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | let second = first; + | ----- you could clone this value + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/type-alias-impl-trait/recursive-drop-elaboration-2.rs b/tests/ui/type-alias-impl-trait/recursive-drop-elaboration-2.rs new file mode 100644 index 000000000000..5541c5267f39 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/recursive-drop-elaboration-2.rs @@ -0,0 +1,19 @@ +//! Regression test for ICE #139556 + +#![feature(type_alias_impl_trait)] + +trait T {} + +type Alias<'a> = impl T; + +struct S; +impl<'a> T for &'a S {} + +#[define_opaque(Alias)] +fn with_positive(fun: impl Fn(Alias<'_>)) { +//~^ WARN: function cannot return without recursing + with_positive(|&n| ()); + //~^ ERROR: cannot move out of a shared reference +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/recursive-drop-elaboration-2.stderr b/tests/ui/type-alias-impl-trait/recursive-drop-elaboration-2.stderr new file mode 100644 index 000000000000..e1fdd222ee14 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/recursive-drop-elaboration-2.stderr @@ -0,0 +1,30 @@ +warning: function cannot return without recursing + --> $DIR/recursive-drop-elaboration-2.rs:13:1 + | +LL | fn with_positive(fun: impl Fn(Alias<'_>)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing +LL | +LL | with_positive(|&n| ()); + | ---------------------- recursive call site + | + = help: a `loop` may express intention better if this is on purpose + = note: `#[warn(unconditional_recursion)]` on by default + +error[E0507]: cannot move out of a shared reference + --> $DIR/recursive-drop-elaboration-2.rs:15:20 + | +LL | with_positive(|&n| ()); + | ^- + | | + | data moved here + | move occurs because `n` has type `S`, which does not implement the `Copy` trait + | +help: consider removing the borrow + | +LL - with_positive(|&n| ()); +LL + with_positive(|n| ()); + | + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0507`. diff --git a/tests/ui/type-alias-impl-trait/recursive-drop-elaboration.rs b/tests/ui/type-alias-impl-trait/recursive-drop-elaboration.rs new file mode 100644 index 000000000000..dd28732ebb28 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/recursive-drop-elaboration.rs @@ -0,0 +1,24 @@ +//! Regression test for #122904. + +#![feature(type_alias_impl_trait)] + +trait T {} + +type Alias<'a> = impl T; + +struct S; +impl<'a> T for &'a S {} + +#[define_opaque(Alias)] +fn with_positive(fun: impl Fn(Alias<'_>)) { +//~^ WARN: function cannot return without recursing + with_positive(|&n| ()); + //~^ ERROR: cannot move out of a shared reference +} + +#[define_opaque(Alias)] +fn main(_: Alias<'_>) { +//~^ ERROR: `main` function has wrong type [E0580] + with_positive(|&a| ()); + //~^ ERROR: cannot move out of a shared reference +} diff --git a/tests/ui/type-alias-impl-trait/recursive-drop-elaboration.stderr b/tests/ui/type-alias-impl-trait/recursive-drop-elaboration.stderr new file mode 100644 index 000000000000..8b5dc950afdd --- /dev/null +++ b/tests/ui/type-alias-impl-trait/recursive-drop-elaboration.stderr @@ -0,0 +1,58 @@ +warning: function cannot return without recursing + --> $DIR/recursive-drop-elaboration.rs:13:1 + | +LL | fn with_positive(fun: impl Fn(Alias<'_>)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing +LL | +LL | with_positive(|&n| ()); + | ---------------------- recursive call site + | + = help: a `loop` may express intention better if this is on purpose + = note: `#[warn(unconditional_recursion)]` on by default + +error[E0507]: cannot move out of a shared reference + --> $DIR/recursive-drop-elaboration.rs:15:20 + | +LL | with_positive(|&n| ()); + | ^- + | | + | data moved here + | move occurs because `n` has type `S`, which does not implement the `Copy` trait + | +help: consider removing the borrow + | +LL - with_positive(|&n| ()); +LL + with_positive(|n| ()); + | + +error[E0507]: cannot move out of a shared reference + --> $DIR/recursive-drop-elaboration.rs:22:20 + | +LL | with_positive(|&a| ()); + | ^- + | | + | data moved here + | move occurs because `a` has type `S`, which does not implement the `Copy` trait + | +help: consider removing the borrow + | +LL - with_positive(|&a| ()); +LL + with_positive(|a| ()); + | + +error[E0580]: `main` function has wrong type + --> $DIR/recursive-drop-elaboration.rs:20:1 + | +LL | type Alias<'a> = impl T; + | ------ the found opaque type +... +LL | fn main(_: Alias<'_>) { + | ^^^^^^^^^^^^^^^^^^^^^ incorrect number of function parameters + | + = note: expected signature `fn()` + found signature `for<'a> fn(Alias<'a>)` + +error: aborting due to 3 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0507, E0580. +For more information about an error, try `rustc --explain E0507`. diff --git a/tests/crashes/125185.rs b/tests/ui/type-alias-impl-trait/type-error-drop-elaboration.rs similarity index 78% rename from tests/crashes/125185.rs rename to tests/ui/type-alias-impl-trait/type-error-drop-elaboration.rs index e77666ca73d3..c0fb9007865a 100644 --- a/tests/crashes/125185.rs +++ b/tests/ui/type-alias-impl-trait/type-error-drop-elaboration.rs @@ -1,4 +1,4 @@ -//@ known-bug: rust-lang/rust#125185 +//! Regression test for #125185 //@ compile-flags: -Zvalidate-mir #![feature(type_alias_impl_trait)] @@ -10,6 +10,7 @@ struct A; #[define_opaque(Foo)] const fn foo() -> Foo { value() + //~^ ERROR: cannot find function `value` in this scope } const VALUE: Foo = foo(); diff --git a/tests/ui/type-alias-impl-trait/type-error-drop-elaboration.stderr b/tests/ui/type-alias-impl-trait/type-error-drop-elaboration.stderr new file mode 100644 index 000000000000..1cb33eabd902 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/type-error-drop-elaboration.stderr @@ -0,0 +1,12 @@ +error[E0425]: cannot find function `value` in this scope + --> $DIR/type-error-drop-elaboration.rs:12:5 + | +LL | value() + | ^^^^^ help: a constant with a similar name exists: `VALUE` +... +LL | const VALUE: Foo = foo(); + | ------------------------- similarly named constant `VALUE` defined here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0425`. From b0010dd5aeb3b9837fdf92c121ebfdd1a07bce6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 15 Sep 2025 13:16:25 +0200 Subject: [PATCH 675/682] Add diagnostics context and migrate the `style` check to it --- src/tools/tidy/src/diagnostics.rs | 102 +++++++++++++++++++++++ src/tools/tidy/src/lib.rs | 8 +- src/tools/tidy/src/main.rs | 129 +++++++++++++++--------------- src/tools/tidy/src/style.rs | 37 ++++----- 4 files changed, 187 insertions(+), 89 deletions(-) create mode 100644 src/tools/tidy/src/diagnostics.rs diff --git a/src/tools/tidy/src/diagnostics.rs b/src/tools/tidy/src/diagnostics.rs new file mode 100644 index 000000000000..a410938400ee --- /dev/null +++ b/src/tools/tidy/src/diagnostics.rs @@ -0,0 +1,102 @@ +use std::collections::HashSet; +use std::fmt::Display; +use std::sync::{Arc, Mutex}; + +use crate::tidy_error; + +/// Collects diagnostics from all tidy steps, and contains shared information +/// that determines how should message and logs be presented. +/// +/// Since checks are executed in parallel, the context is internally synchronized, to avoid +/// all checks to lock it explicitly. +#[derive(Clone)] +pub struct DiagCtx(Arc>); + +impl DiagCtx { + pub fn new(verbose: bool) -> Self { + Self(Arc::new(Mutex::new(DiagCtxInner { + running_checks: Default::default(), + finished_checks: Default::default(), + verbose, + }))) + } + + pub fn start_check(&self, name: T) -> RunningCheck { + let name = name.to_string(); + + let mut ctx = self.0.lock().unwrap(); + ctx.start_check(&name); + RunningCheck { name, bad: false, ctx: self.0.clone() } + } + + pub fn into_conclusion(self) -> bool { + let ctx = self.0.lock().unwrap(); + assert!(ctx.running_checks.is_empty(), "Some checks are still running"); + ctx.finished_checks.iter().any(|c| c.bad) + } +} + +struct DiagCtxInner { + running_checks: HashSet, + finished_checks: HashSet, + verbose: bool, +} + +impl DiagCtxInner { + fn start_check(&mut self, name: &str) { + if self.has_check(name) { + panic!("Starting a check named {name} for the second time"); + } + self.running_checks.insert(name.to_string()); + } + + fn finish_check(&mut self, check: FinishedCheck) { + assert!( + self.running_checks.remove(&check.name), + "Finishing check {} that was not started", + check.name + ); + self.finished_checks.insert(check); + } + + fn has_check(&self, name: &str) -> bool { + self.running_checks + .iter() + .chain(self.finished_checks.iter().map(|c| &c.name)) + .any(|c| c == name) + } +} + +#[derive(PartialEq, Eq, Hash, Debug)] +struct FinishedCheck { + name: String, + bad: bool, +} + +/// Represents a single tidy check, identified by its `name`, running. +pub struct RunningCheck { + name: String, + bad: bool, + ctx: Arc>, +} + +impl RunningCheck { + /// Immediately output an error and mark the check as failed. + pub fn error(&mut self, t: T) { + self.mark_as_bad(); + tidy_error(&t.to_string()).expect("failed to output error"); + } + + fn mark_as_bad(&mut self) { + self.bad = true; + } +} + +impl Drop for RunningCheck { + fn drop(&mut self) { + self.ctx + .lock() + .unwrap() + .finish_check(FinishedCheck { name: self.name.clone(), bad: self.bad }) + } +} diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index f7920e3205ab..953d69919906 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -50,13 +50,6 @@ macro_rules! tidy_error { }); } -macro_rules! tidy_error_ext { - ($tidy_error:path, $bad:expr, $($fmt:tt)*) => ({ - $tidy_error(&format_args!($($fmt)*).to_string()).expect("failed to output error"); - *$bad = true; - }); -} - fn tidy_error(args: &str) -> std::io::Result<()> { use std::io::Write; @@ -250,6 +243,7 @@ pub mod alphabetical; pub mod bins; pub mod debug_artifacts; pub mod deps; +pub mod diagnostics; pub mod edition; pub mod error_codes; pub mod extdeps; diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index f9e82341b7aa..708daffe657a 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -9,9 +9,11 @@ use std::num::NonZeroUsize; use std::path::PathBuf; use std::str::FromStr; use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Arc, Mutex}; use std::thread::{self, ScopedJoinHandle, scope}; use std::{env, process}; +use tidy::diagnostics::DiagCtx; use tidy::*; fn main() { @@ -54,6 +56,8 @@ fn main() { let ci_info = CiInfo::new(&mut bad); let bad = std::sync::Arc::new(AtomicBool::new(bad)); + let mut diag_ctx = DiagCtx::new(verbose); + let drain_handles = |handles: &mut VecDeque>| { // poll all threads for completion before awaiting the oldest one for i in (0..handles.len()).rev() { @@ -87,76 +91,73 @@ fn main() { (@ $p:ident, name=$name:expr $(, $args:expr)* ) => { drain_handles(&mut handles); + let diag_ctx = diag_ctx.clone(); let handle = thread::Builder::new().name($name).spawn_scoped(s, || { - let mut flag = false; - $p::check($($args, )* &mut flag); - if (flag) { - bad.store(true, Ordering::Relaxed); - } + $p::check($($args, )* diag_ctx); }).unwrap(); handles.push_back(handle); } } - check!(target_specific_tests, &tests_path); + // check!(target_specific_tests, &tests_path); // Checks that are done on the cargo workspace. - check!(deps, &root_path, &cargo, bless); - check!(extdeps, &root_path); + // check!(deps, &root_path, &cargo, bless); + // check!(extdeps, &root_path); // Checks over tests. - check!(tests_placement, &root_path); - check!(tests_revision_unpaired_stdout_stderr, &tests_path); - check!(debug_artifacts, &tests_path); - check!(ui_tests, &root_path, bless); - check!(mir_opt_tests, &tests_path, bless); - check!(rustdoc_gui_tests, &tests_path); - check!(rustdoc_css_themes, &librustdoc_path); - check!(rustdoc_templates, &librustdoc_path); - check!(rustdoc_json, &src_path, &ci_info); - check!(known_bug, &crashes_path); - check!(unknown_revision, &tests_path); + // check!(tests_placement, &root_path); + // check!(tests_revision_unpaired_stdout_stderr, &tests_path); + // check!(debug_artifacts, &tests_path); + // check!(ui_tests, &root_path, bless); + // check!(mir_opt_tests, &tests_path, bless); + // check!(rustdoc_gui_tests, &tests_path); + // check!(rustdoc_css_themes, &librustdoc_path); + // check!(rustdoc_templates, &librustdoc_path); + // check!(rustdoc_json, &src_path, &ci_info); + // check!(known_bug, &crashes_path); + // check!(unknown_revision, &tests_path); // Checks that only make sense for the compiler. - check!(error_codes, &root_path, &[&compiler_path, &librustdoc_path], verbose, &ci_info); - check!(fluent_alphabetical, &compiler_path, bless); - check!(fluent_period, &compiler_path); - check!(fluent_lowercase, &compiler_path); - check!(target_policy, &root_path); - check!(gcc_submodule, &root_path, &compiler_path); + // check!(error_codes, &root_path, &[&compiler_path, &librustdoc_path], verbose, &ci_info); + // check!(fluent_alphabetical, &compiler_path, bless); + // check!(fluent_period, &compiler_path); + // check!(fluent_lowercase, &compiler_path); + // check!(target_policy, &root_path); + // check!(gcc_submodule, &root_path, &compiler_path); // Checks that only make sense for the std libs. - check!(pal, &library_path); + // check!(pal, &library_path); // Checks that need to be done for both the compiler and std libraries. - check!(unit_tests, &src_path, false); - check!(unit_tests, &compiler_path, false); - check!(unit_tests, &library_path, true); - - if bins::check_filesystem_support(&[&root_path], &output_directory) { - check!(bins, &root_path); - } + // check!(unit_tests, &src_path, false); + // check!(unit_tests, &compiler_path, false); + // check!(unit_tests, &library_path, true); + // + // if bins::check_filesystem_support(&[&root_path], &output_directory) { + // check!(bins, &root_path); + // } check!(style, &src_path); check!(style, &tests_path); check!(style, &compiler_path); check!(style, &library_path); - check!(edition, &src_path); - check!(edition, &compiler_path); - check!(edition, &library_path); - - check!(alphabetical, &root_manifest); - check!(alphabetical, &src_path); - check!(alphabetical, &tests_path); - check!(alphabetical, &compiler_path); - check!(alphabetical, &library_path); - - check!(x_version, &root_path, &cargo); - - check!(triagebot, &root_path); - - check!(filenames, &root_path); + // check!(edition, &src_path); + // check!(edition, &compiler_path); + // check!(edition, &library_path); + // + // check!(alphabetical, &root_manifest); + // check!(alphabetical, &src_path); + // check!(alphabetical, &tests_path); + // check!(alphabetical, &compiler_path); + // check!(alphabetical, &library_path); + // + // check!(x_version, &root_path, &cargo); + // + // check!(triagebot, &root_path); + // + // check!(filenames, &root_path); let collected = { drain_handles(&mut handles); @@ -175,24 +176,24 @@ fn main() { } r }; - check!(unstable_book, &src_path, collected); - - check!( - extra_checks, - &root_path, - &output_directory, - &ci_info, - &librustdoc_path, - &tools_path, - &npm, - &cargo, - bless, - extra_checks, - pos_args - ); + // check!(unstable_book, &src_path, collected); + // + // check!( + // extra_checks, + // &root_path, + // &output_directory, + // &ci_info, + // &librustdoc_path, + // &tools_path, + // &npm, + // &cargo, + // bless, + // extra_checks, + // pos_args + // ); }); - if bad.load(Ordering::Relaxed) { + if diag_ctx.into_conclusion() { eprintln!("some tidy checks failed"); process::exit(1); } diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs index fca097c091b7..7d6ebed397fb 100644 --- a/src/tools/tidy/src/style.rs +++ b/src/tools/tidy/src/style.rs @@ -24,6 +24,7 @@ use std::sync::LazyLock; use regex::RegexSetBuilder; use rustc_hash::FxHashMap; +use crate::diagnostics::DiagCtx; use crate::walk::{filter_dirs, walk}; #[cfg(test)] @@ -338,7 +339,9 @@ fn is_unexplained_ignore(extension: &str, line: &str) -> bool { true } -pub fn check(path: &Path, bad: &mut bool) { +pub fn check(path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(format!("style {}", path.display())); + fn skip(path: &Path, is_dir: bool) -> bool { if path.file_name().is_some_and(|name| name.to_string_lossy().starts_with(".#")) { // vim or emacs temporary file @@ -391,7 +394,7 @@ pub fn check(path: &Path, bad: &mut bool) { }); if contents.is_empty() { - tidy_error!(bad, "{}: empty file", file.display()); + check.error(format!("{}: empty file", file.display())); } let extension = file.extension().unwrap().to_string_lossy(); @@ -467,7 +470,7 @@ pub fn check(path: &Path, bad: &mut bool) { } let mut err = |msg: &str| { - tidy_error!(bad, "{}:{}: {}", file.display(), i + 1, msg); + check.error(format!("{}:{}: {msg}", file.display(), i + 1)); }; if trimmed.contains("dbg!") @@ -611,7 +614,7 @@ pub fn check(path: &Path, bad: &mut bool) { && backtick_count % 2 == 1 { let mut err = |msg: &str| { - tidy_error!(bad, "{}:{start_line}: {msg}", file.display()); + check.error(format!("{}:{start_line}: {msg}", file.display())); }; let block_len = (i + 1) - start_line; if block_len == 1 { @@ -632,12 +635,12 @@ pub fn check(path: &Path, bad: &mut bool) { } if leading_new_lines { let mut err = |_| { - tidy_error!(bad, "{}: leading newline", file.display()); + check.error(format!("{}: leading newline", file.display())); }; suppressible_tidy_err!(err, skip_leading_newlines, "missing leading newline"); } let mut err = |msg: &str| { - tidy_error!(bad, "{}: {}", file.display(), msg); + check.error(format!("{}: {}", file.display(), msg)); }; match trailing_new_lines { 0 => suppressible_tidy_err!(err, skip_trailing_newlines, "missing trailing newline"), @@ -650,38 +653,36 @@ pub fn check(path: &Path, bad: &mut bool) { }; if lines > LINES { let mut err = |_| { - tidy_error!( - bad, - "{}: too many lines ({}) (add `// \ + check.error(format!( + "{}: too many lines ({lines}) (add `// \ ignore-tidy-filelength` to the file to suppress this error)", file.display(), - lines - ); + )); }; suppressible_tidy_err!(err, skip_file_length, ""); } if let Directive::Ignore(false) = skip_cr { - tidy_error!(bad, "{}: ignoring CR characters unnecessarily", file.display()); + check.error(format!("{}: ignoring CR characters unnecessarily", file.display())); } if let Directive::Ignore(false) = skip_tab { - tidy_error!(bad, "{}: ignoring tab characters unnecessarily", file.display()); + check.error(format!("{}: ignoring tab characters unnecessarily", file.display())); } if let Directive::Ignore(false) = skip_end_whitespace { - tidy_error!(bad, "{}: ignoring trailing whitespace unnecessarily", file.display()); + check.error(format!("{}: ignoring trailing whitespace unnecessarily", file.display())); } if let Directive::Ignore(false) = skip_trailing_newlines { - tidy_error!(bad, "{}: ignoring trailing newlines unnecessarily", file.display()); + check.error(format!("{}: ignoring trailing newlines unnecessarily", file.display())); } if let Directive::Ignore(false) = skip_leading_newlines { - tidy_error!(bad, "{}: ignoring leading newlines unnecessarily", file.display()); + check.error(format!("{}: ignoring leading newlines unnecessarily", file.display())); } if let Directive::Ignore(false) = skip_copyright { - tidy_error!(bad, "{}: ignoring copyright unnecessarily", file.display()); + check.error(format!("{}: ignoring copyright unnecessarily", file.display())); } // We deliberately do not warn about these being unnecessary, // that would just lead to annoying churn. let _unused = skip_line_length; let _unused = skip_file_length; - }) + }); } From c36faff900bc03c5c4ee5772dcac4f3630fa2dff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 15 Sep 2025 13:24:46 +0200 Subject: [PATCH 676/682] Add `CheckId`, migrate the `alphabetical` check to diagnostics --- src/tools/tidy/src/alphabetical.rs | 31 +++++++-------- src/tools/tidy/src/diagnostics.rs | 63 ++++++++++++++++++++---------- src/tools/tidy/src/main.rs | 10 ++--- src/tools/tidy/src/style.rs | 4 +- 4 files changed, 63 insertions(+), 45 deletions(-) diff --git a/src/tools/tidy/src/alphabetical.rs b/src/tools/tidy/src/alphabetical.rs index 9ddce7251069..f93d3b561130 100644 --- a/src/tools/tidy/src/alphabetical.rs +++ b/src/tools/tidy/src/alphabetical.rs @@ -24,6 +24,7 @@ use std::fmt::Display; use std::iter::Peekable; use std::path::Path; +use crate::diagnostics::{CheckId, DiagCtx, RunningCheck}; use crate::walk::{filter_dirs, walk}; #[cfg(test)] @@ -43,8 +44,7 @@ const END_MARKER: &str = "tidy-alphabetical-end"; fn check_section<'a>( file: impl Display, lines: impl Iterator, - err: &mut dyn FnMut(&str) -> std::io::Result<()>, - bad: &mut bool, + check: &mut RunningCheck, ) { let mut prev_line = String::new(); let mut first_indent = None; @@ -56,12 +56,10 @@ fn check_section<'a>( } if line.contains(START_MARKER) { - tidy_error_ext!( - err, - bad, + check.error(format!( "{file}:{} found `{START_MARKER}` expecting `{END_MARKER}`", idx + 1 - ); + )); return; } @@ -104,45 +102,44 @@ fn check_section<'a>( let prev_line_trimmed_lowercase = prev_line.trim_start_matches(' '); if version_sort(trimmed_line, prev_line_trimmed_lowercase).is_lt() { - tidy_error_ext!(err, bad, "{file}:{}: line not in alphabetical order", idx + 1); + check.error(format!("{file}:{}: line not in alphabetical order", idx + 1)); } prev_line = line; } - tidy_error_ext!(err, bad, "{file}: reached end of file expecting `{END_MARKER}`") + check.error(format!("{file}: reached end of file expecting `{END_MARKER}`")); } fn check_lines<'a>( file: &impl Display, mut lines: impl Iterator, - err: &mut dyn FnMut(&str) -> std::io::Result<()>, - bad: &mut bool, + check: &mut RunningCheck, ) { while let Some((idx, line)) = lines.next() { if line.contains(END_MARKER) { - tidy_error_ext!( - err, - bad, + check.error(format!( "{file}:{} found `{END_MARKER}` expecting `{START_MARKER}`", idx + 1 - ) + )); } if line.contains(START_MARKER) { - check_section(file, &mut lines, err, bad); + check_section(file, &mut lines, check); } } } -pub fn check(path: &Path, bad: &mut bool) { +pub fn check(path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("alphabetical").path(path)); + let skip = |path: &_, _is_dir| filter_dirs(path) || path.ends_with("tidy/src/alphabetical/tests.rs"); walk(path, skip, &mut |entry, contents| { let file = &entry.path().display(); let lines = contents.lines().enumerate(); - check_lines(file, lines, &mut crate::tidy_error, bad) + check_lines(file, lines, &mut check) }); } diff --git a/src/tools/tidy/src/diagnostics.rs b/src/tools/tidy/src/diagnostics.rs index a410938400ee..ae20f92715c0 100644 --- a/src/tools/tidy/src/diagnostics.rs +++ b/src/tools/tidy/src/diagnostics.rs @@ -1,5 +1,6 @@ use std::collections::HashSet; use std::fmt::Display; +use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; use crate::tidy_error; @@ -21,12 +22,12 @@ impl DiagCtx { }))) } - pub fn start_check(&self, name: T) -> RunningCheck { - let name = name.to_string(); + pub fn start_check>(&self, id: Id) -> RunningCheck { + let id = id.into(); let mut ctx = self.0.lock().unwrap(); - ctx.start_check(&name); - RunningCheck { name, bad: false, ctx: self.0.clone() } + ctx.start_check(id.clone()); + RunningCheck { id, bad: false, ctx: self.0.clone() } } pub fn into_conclusion(self) -> bool { @@ -37,45 +38,68 @@ impl DiagCtx { } struct DiagCtxInner { - running_checks: HashSet, + running_checks: HashSet, finished_checks: HashSet, verbose: bool, } impl DiagCtxInner { - fn start_check(&mut self, name: &str) { - if self.has_check(name) { - panic!("Starting a check named {name} for the second time"); + fn start_check(&mut self, id: CheckId) { + if self.has_check_id(&id) { + panic!("Starting a check named `{id:?}` for the second time"); } - self.running_checks.insert(name.to_string()); + self.running_checks.insert(id); } fn finish_check(&mut self, check: FinishedCheck) { assert!( - self.running_checks.remove(&check.name), - "Finishing check {} that was not started", - check.name + self.running_checks.remove(&check.id), + "Finishing check `{:?}` that was not started", + check.id ); self.finished_checks.insert(check); } - fn has_check(&self, name: &str) -> bool { + fn has_check_id(&self, id: &CheckId) -> bool { self.running_checks .iter() - .chain(self.finished_checks.iter().map(|c| &c.name)) - .any(|c| c == name) + .chain(self.finished_checks.iter().map(|c| &c.id)) + .any(|c| c == id) + } +} + +/// Identifies a single step +#[derive(PartialEq, Eq, Hash, Clone, Debug)] +pub struct CheckId { + name: String, + path: Option, +} + +impl CheckId { + pub fn new(name: &'static str) -> Self { + Self { name: name.to_string(), path: None } + } + + pub fn path(self, path: &Path) -> Self { + Self { path: Some(path.to_path_buf()), ..self } + } +} + +impl From<&'static str> for CheckId { + fn from(name: &'static str) -> Self { + Self::new(name) } } #[derive(PartialEq, Eq, Hash, Debug)] struct FinishedCheck { - name: String, + id: CheckId, bad: bool, } /// Represents a single tidy check, identified by its `name`, running. pub struct RunningCheck { - name: String, + id: CheckId, bad: bool, ctx: Arc>, } @@ -94,9 +118,6 @@ impl RunningCheck { impl Drop for RunningCheck { fn drop(&mut self) { - self.ctx - .lock() - .unwrap() - .finish_check(FinishedCheck { name: self.name.clone(), bad: self.bad }) + self.ctx.lock().unwrap().finish_check(FinishedCheck { id: self.id.clone(), bad: self.bad }) } } diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index 708daffe657a..7d42102bbb9d 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -147,11 +147,11 @@ fn main() { // check!(edition, &compiler_path); // check!(edition, &library_path); // - // check!(alphabetical, &root_manifest); - // check!(alphabetical, &src_path); - // check!(alphabetical, &tests_path); - // check!(alphabetical, &compiler_path); - // check!(alphabetical, &library_path); + check!(alphabetical, &root_manifest); + check!(alphabetical, &src_path); + check!(alphabetical, &tests_path); + check!(alphabetical, &compiler_path); + check!(alphabetical, &library_path); // // check!(x_version, &root_path, &cargo); // diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs index 7d6ebed397fb..d17278edc849 100644 --- a/src/tools/tidy/src/style.rs +++ b/src/tools/tidy/src/style.rs @@ -24,7 +24,7 @@ use std::sync::LazyLock; use regex::RegexSetBuilder; use rustc_hash::FxHashMap; -use crate::diagnostics::DiagCtx; +use crate::diagnostics::{CheckId, DiagCtx}; use crate::walk::{filter_dirs, walk}; #[cfg(test)] @@ -340,7 +340,7 @@ fn is_unexplained_ignore(extension: &str, line: &str) -> bool { } pub fn check(path: &Path, diag_ctx: DiagCtx) { - let mut check = diag_ctx.start_check(format!("style {}", path.display())); + let mut check = diag_ctx.start_check(CheckId::new("style").path(path)); fn skip(path: &Path, is_dir: bool) -> bool { if path.file_name().is_some_and(|name| name.to_string_lossy().starts_with(".#")) { From 352fa3960a00046ff81bd2e0528c51371e5c6c1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 15 Sep 2025 13:26:29 +0200 Subject: [PATCH 677/682] Migrate the remaining tidy checks to diagnostics --- src/tools/tidy/src/bins.rs | 14 +- src/tools/tidy/src/debug_artifacts.rs | 15 +- src/tools/tidy/src/deps.rs | 115 +++++++-------- src/tools/tidy/src/diagnostics.rs | 44 +++++- src/tools/tidy/src/edition.rs | 9 +- src/tools/tidy/src/error_codes.rs | 124 +++++++--------- src/tools/tidy/src/extdeps.rs | 9 +- src/tools/tidy/src/extra_checks/mod.rs | 7 +- src/tools/tidy/src/features.rs | 132 ++++++++---------- src/tools/tidy/src/filenames.rs | 20 +-- src/tools/tidy/src/fluent_alphabetical.rs | 36 +++-- src/tools/tidy/src/fluent_lowercase.rs | 13 +- src/tools/tidy/src/fluent_period.rs | 13 +- src/tools/tidy/src/fluent_used.rs | 7 +- src/tools/tidy/src/gcc_submodule.rs | 13 +- src/tools/tidy/src/known_bug.rs | 9 +- src/tools/tidy/src/lib.rs | 43 ++---- src/tools/tidy/src/main.rs | 128 ++++++++--------- src/tools/tidy/src/mir_opt_tests.rs | 23 +-- src/tools/tidy/src/pal.rs | 11 +- src/tools/tidy/src/rustdoc_css_themes.rs | 37 ++--- src/tools/tidy/src/rustdoc_gui_tests.rs | 11 +- src/tools/tidy/src/rustdoc_json.rs | 42 +++--- src/tools/tidy/src/rustdoc_templates.rs | 15 +- src/tools/tidy/src/target_policy.rs | 7 +- src/tools/tidy/src/target_specific_tests.rs | 23 ++- src/tools/tidy/src/tests_placement.rs | 17 ++- .../tests_revision_unpaired_stdout_stderr.rs | 16 ++- src/tools/tidy/src/triagebot.rs | 38 +++-- src/tools/tidy/src/ui_tests.rs | 53 ++++--- src/tools/tidy/src/unit_tests.rs | 14 +- src/tools/tidy/src/unknown_revision.rs | 17 +-- src/tools/tidy/src/unstable_book.rs | 40 +++--- src/tools/tidy/src/x_version.rs | 17 ++- 34 files changed, 560 insertions(+), 572 deletions(-) diff --git a/src/tools/tidy/src/bins.rs b/src/tools/tidy/src/bins.rs index a18f549844b8..10b618699716 100644 --- a/src/tools/tidy/src/bins.rs +++ b/src/tools/tidy/src/bins.rs @@ -12,11 +12,13 @@ pub use os_impl::*; mod os_impl { use std::path::Path; + use crate::diagnostics::DiagCtx; + pub fn check_filesystem_support(_sources: &[&Path], _output: &Path) -> bool { return false; } - pub fn check(_path: &Path, _bad: &mut bool) {} + pub fn check(_path: &Path, _diag_ctx: DiagCtx) {} } #[cfg(unix)] @@ -36,6 +38,8 @@ mod os_impl { use FilesystemSupport::*; + use crate::diagnostics::DiagCtx; + fn is_executable(path: &Path) -> std::io::Result { Ok(path.metadata()?.mode() & 0o111 != 0) } @@ -106,14 +110,16 @@ mod os_impl { } #[cfg(unix)] - pub fn check(path: &Path, bad: &mut bool) { + pub fn check(path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check("bins"); + use std::ffi::OsStr; const ALLOWED: &[&str] = &["configure", "x"]; for p in RI_EXCLUSION_LIST { if !path.join(Path::new(p)).exists() { - tidy_error!(bad, "rust-installer test bins missed: {p}"); + check.error(format!("rust-installer test bins missed: {p}")); } } @@ -153,7 +159,7 @@ mod os_impl { }); let path_bytes = rel_path.as_os_str().as_bytes(); if output.status.success() && output.stdout.starts_with(path_bytes) { - tidy_error!(bad, "binary checked into source: {}", file.display()); + check.error(format!("binary checked into source: {}", file.display())); } } }, diff --git a/src/tools/tidy/src/debug_artifacts.rs b/src/tools/tidy/src/debug_artifacts.rs index 645534cc8273..19effaede817 100644 --- a/src/tools/tidy/src/debug_artifacts.rs +++ b/src/tools/tidy/src/debug_artifacts.rs @@ -2,24 +2,25 @@ use std::path::Path; +use crate::diagnostics::{CheckId, DiagCtx}; use crate::walk::{filter_dirs, filter_not_rust, walk}; const GRAPHVIZ_POSTFLOW_MSG: &str = "`borrowck_graphviz_postflow` attribute in test"; -pub fn check(test_dir: &Path, bad: &mut bool) { +pub fn check(test_dir: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("debug_artifacts").path(test_dir)); + walk( test_dir, |path, _is_dir| filter_dirs(path) || filter_not_rust(path), &mut |entry, contents| { for (i, line) in contents.lines().enumerate() { if line.contains("borrowck_graphviz_postflow") { - tidy_error!( - bad, - "{}:{}: {}", + check.error(format!( + "{}:{}: {GRAPHVIZ_POSTFLOW_MSG}", entry.path().display(), - i + 1, - GRAPHVIZ_POSTFLOW_MSG - ); + i + 1 + )); } } }, diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 568ec0c11989..a9d07c753157 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -9,6 +9,8 @@ use build_helper::ci::CiEnv; use cargo_metadata::semver::Version; use cargo_metadata::{Metadata, Package, PackageId}; +use crate::diagnostics::{CheckId, DiagCtx, RunningCheck}; + #[path = "../../../bootstrap/src/utils/proc_macro_deps.rs"] mod proc_macro_deps; @@ -661,10 +663,12 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[ /// /// `root` is path to the directory with the root `Cargo.toml` (for the workspace). `cargo` is path /// to the cargo executable. -pub fn check(root: &Path, cargo: &Path, bless: bool, bad: &mut bool) { +pub fn check(root: &Path, cargo: &Path, bless: bool, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("deps").path(root)); + let mut checked_runtime_licenses = false; - check_proc_macro_dep_list(root, cargo, bless, bad); + check_proc_macro_dep_list(root, cargo, bless, &mut check); for &WorkspaceInfo { path, exceptions, crates_and_deps, submodules } in WORKSPACES { if has_missing_submodule(root, submodules) { @@ -672,7 +676,7 @@ pub fn check(root: &Path, cargo: &Path, bless: bool, bad: &mut bool) { } if !root.join(path).join("Cargo.lock").exists() { - tidy_error!(bad, "the `{path}` workspace doesn't have a Cargo.lock"); + check.error(format!("the `{path}` workspace doesn't have a Cargo.lock")); continue; } @@ -683,16 +687,23 @@ pub fn check(root: &Path, cargo: &Path, bless: bool, bad: &mut bool) { .other_options(vec!["--locked".to_owned()]); let metadata = t!(cmd.exec()); - check_license_exceptions(&metadata, path, exceptions, bad); + check_license_exceptions(&metadata, path, exceptions, &mut check); if let Some((crates, permitted_deps, location)) = crates_and_deps { let descr = crates.get(0).unwrap_or(&path); - check_permitted_dependencies(&metadata, descr, permitted_deps, crates, location, bad); + check_permitted_dependencies( + &metadata, + descr, + permitted_deps, + crates, + location, + &mut check, + ); } if path == "library" { - check_runtime_license_exceptions(&metadata, bad); - check_runtime_no_duplicate_dependencies(&metadata, bad); - check_runtime_no_proc_macros(&metadata, bad); + check_runtime_license_exceptions(&metadata, &mut check); + check_runtime_no_duplicate_dependencies(&metadata, &mut check); + check_runtime_no_proc_macros(&metadata, &mut check); checked_runtime_licenses = true; } } @@ -703,7 +714,7 @@ pub fn check(root: &Path, cargo: &Path, bless: bool, bad: &mut bool) { } /// Ensure the list of proc-macro crate transitive dependencies is up to date -fn check_proc_macro_dep_list(root: &Path, cargo: &Path, bless: bool, bad: &mut bool) { +fn check_proc_macro_dep_list(root: &Path, cargo: &Path, bless: bool, check: &mut RunningCheck) { let mut cmd = cargo_metadata::MetadataCommand::new(); cmd.cargo_path(cargo) .manifest_path(root.join("Cargo.toml")) @@ -750,22 +761,22 @@ pub static CRATES: &[&str] = &[ ) .unwrap(); } else { - let old_bad = *bad; + let mut error_found = false; for missing in proc_macro_deps.difference(&expected) { - tidy_error!( - bad, + error_found = true; + check.error(format!( "proc-macro crate dependency `{missing}` is not registered in `src/bootstrap/src/utils/proc_macro_deps.rs`", - ); + )); } for extra in expected.difference(&proc_macro_deps) { - tidy_error!( - bad, + error_found = true; + check.error(format!( "`{extra}` is registered in `src/bootstrap/src/utils/proc_macro_deps.rs`, but is not a proc-macro crate dependency", - ); + )); } - if *bad != old_bad { - eprintln!("Run `./x.py test tidy --bless` to regenerate the list"); + if error_found { + check.message("Run `./x.py test tidy --bless` to regenerate the list"); } } } @@ -787,7 +798,7 @@ pub fn has_missing_submodule(root: &Path, submodules: &[&str]) -> bool { /// /// Unlike for tools we don't allow exceptions to the `LICENSES` list for the runtime with the sole /// exception of `fortanix-sgx-abi` which is only used on x86_64-fortanix-unknown-sgx. -fn check_runtime_license_exceptions(metadata: &Metadata, bad: &mut bool) { +fn check_runtime_license_exceptions(metadata: &Metadata, check: &mut RunningCheck) { for pkg in &metadata.packages { if pkg.source.is_none() { // No need to check local packages. @@ -796,7 +807,8 @@ fn check_runtime_license_exceptions(metadata: &Metadata, bad: &mut bool) { let license = match &pkg.license { Some(license) => license, None => { - tidy_error!(bad, "dependency `{}` does not define a license expression", pkg.id); + check + .error(format!("dependency `{}` does not define a license expression", pkg.id)); continue; } }; @@ -809,7 +821,7 @@ fn check_runtime_license_exceptions(metadata: &Metadata, bad: &mut bool) { continue; } - tidy_error!(bad, "invalid license `{}` in `{}`", license, pkg.id); + check.error(format!("invalid license `{}` in `{}`", license, pkg.id)); } } } @@ -821,37 +833,32 @@ fn check_license_exceptions( metadata: &Metadata, workspace: &str, exceptions: &[(&str, &str)], - bad: &mut bool, + check: &mut RunningCheck, ) { // Validate the EXCEPTIONS list hasn't changed. for (name, license) in exceptions { // Check that the package actually exists. if !metadata.packages.iter().any(|p| *p.name == *name) { - tidy_error!( - bad, - "could not find exception package `{}` in workspace `{workspace}`\n\ + check.error(format!( + "could not find exception package `{name}` in workspace `{workspace}`\n\ Remove from EXCEPTIONS list if it is no longer used.", - name - ); + )); } // Check that the license hasn't changed. for pkg in metadata.packages.iter().filter(|p| *p.name == *name) { match &pkg.license { None => { - tidy_error!( - bad, + check.error(format!( "dependency exception `{}` in workspace `{workspace}` does not declare a license expression", pkg.id - ); + )); } Some(pkg_license) => { if pkg_license.as_str() != *license { - println!( - "dependency exception `{name}` license in workspace `{workspace}` has changed" - ); - println!(" previously `{license}` now `{pkg_license}`"); - println!(" update EXCEPTIONS for the new license"); - *bad = true; + check.error(format!(r#"dependency exception `{name}` license in workspace `{workspace}` has changed + previously `{license}` now `{pkg_license}` + update EXCEPTIONS for the new license +"#)); } } } @@ -872,26 +879,23 @@ fn check_license_exceptions( let license = match &pkg.license { Some(license) => license, None => { - tidy_error!( - bad, + check.error(format!( "dependency `{}` in workspace `{workspace}` does not define a license expression", pkg.id - ); + )); continue; } }; if !LICENSES.contains(&license.as_str()) { - tidy_error!( - bad, + check.error(format!( "invalid license `{}` for package `{}` in workspace `{workspace}`", - license, - pkg.id - ); + license, pkg.id + )); } } } -fn check_runtime_no_duplicate_dependencies(metadata: &Metadata, bad: &mut bool) { +fn check_runtime_no_duplicate_dependencies(metadata: &Metadata, check: &mut RunningCheck) { let mut seen_pkgs = HashSet::new(); for pkg in &metadata.packages { if pkg.source.is_none() { @@ -902,25 +906,23 @@ fn check_runtime_no_duplicate_dependencies(metadata: &Metadata, bad: &mut bool) // depends on two version of (one for the `wasm32-wasip1` target and // another for the `wasm32-wasip2` target). if pkg.name.to_string() != "wasi" && !seen_pkgs.insert(&*pkg.name) { - tidy_error!( - bad, + check.error(format!( "duplicate package `{}` is not allowed for the standard library", pkg.name - ); + )); } } } -fn check_runtime_no_proc_macros(metadata: &Metadata, bad: &mut bool) { +fn check_runtime_no_proc_macros(metadata: &Metadata, check: &mut RunningCheck) { for pkg in &metadata.packages { if pkg.targets.iter().any(|target| target.is_proc_macro()) { - tidy_error!( - bad, + check.error(format!( "proc macro `{}` is not allowed as standard library dependency.\n\ Using proc macros in the standard library would break cross-compilation \ as proc-macros don't get shipped for the host tuple.", pkg.name - ); + )); } } } @@ -935,7 +937,7 @@ fn check_permitted_dependencies( permitted_dependencies: &[&'static str], restricted_dependency_crates: &[&'static str], permitted_location: ListLocation, - bad: &mut bool, + check: &mut RunningCheck, ) { let mut has_permitted_dep_error = false; let mut deps = HashSet::new(); @@ -957,11 +959,10 @@ fn check_permitted_dependencies( } } if !deps.iter().any(|dep_id| compare(pkg_from_id(metadata, dep_id), permitted)) { - tidy_error!( - bad, + check.error(format!( "could not find allowed package `{permitted}`\n\ Remove from PERMITTED_DEPENDENCIES list if it is no longer used.", - ); + )); has_permitted_dep_error = true; } } @@ -988,7 +989,7 @@ fn check_permitted_dependencies( false }; if !is_eq { - tidy_error!(bad, "Dependency for {descr} not explicitly permitted: {}", dep.id); + check.error(format!("Dependency for {descr} not explicitly permitted: {}", dep.id)); has_permitted_dep_error = true; } } diff --git a/src/tools/tidy/src/diagnostics.rs b/src/tools/tidy/src/diagnostics.rs index ae20f92715c0..d7889f925ea9 100644 --- a/src/tools/tidy/src/diagnostics.rs +++ b/src/tools/tidy/src/diagnostics.rs @@ -3,7 +3,7 @@ use std::fmt::Display; use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; -use crate::tidy_error; +use termcolor::WriteColor; /// Collects diagnostics from all tidy steps, and contains shared information /// that determines how should message and logs be presented. @@ -111,6 +111,33 @@ impl RunningCheck { tidy_error(&t.to_string()).expect("failed to output error"); } + /// Immediately output a warning. + pub fn warning(&mut self, t: T) { + eprintln!("WARNING: {t}"); + } + + /// Output an informational message + pub fn message(&mut self, t: T) { + eprintln!("{t}"); + } + + /// Output a message only if verbose output is enabled. + pub fn verbose_msg(&mut self, t: T) { + if self.is_verbose_enabled() { + self.message(t); + } + } + + /// Has an error already occured for this check? + pub fn is_bad(&self) -> bool { + self.bad + } + + /// Is verbose output enabled? + pub fn is_verbose_enabled(&self) -> bool { + self.ctx.lock().unwrap().verbose + } + fn mark_as_bad(&mut self) { self.bad = true; } @@ -121,3 +148,18 @@ impl Drop for RunningCheck { self.ctx.lock().unwrap().finish_check(FinishedCheck { id: self.id.clone(), bad: self.bad }) } } + +fn tidy_error(args: &str) -> std::io::Result<()> { + use std::io::Write; + + use termcolor::{Color, ColorChoice, ColorSpec, StandardStream}; + + let mut stderr = StandardStream::stdout(ColorChoice::Auto); + stderr.set_color(ColorSpec::new().set_fg(Some(Color::Red)))?; + + write!(&mut stderr, "tidy error")?; + stderr.set_color(&ColorSpec::new())?; + + writeln!(&mut stderr, ": {args}")?; + Ok(()) +} diff --git a/src/tools/tidy/src/edition.rs b/src/tools/tidy/src/edition.rs index 08f6a3909f80..448e0b0e0a8c 100644 --- a/src/tools/tidy/src/edition.rs +++ b/src/tools/tidy/src/edition.rs @@ -2,9 +2,11 @@ use std::path::Path; +use crate::diagnostics::{CheckId, DiagCtx}; use crate::walk::{filter_dirs, walk}; -pub fn check(path: &Path, bad: &mut bool) { +pub fn check(path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("edition").path(path)); walk(path, |path, _is_dir| filter_dirs(path), &mut |entry, contents| { let file = entry.path(); let filename = file.file_name().unwrap(); @@ -23,11 +25,10 @@ pub fn check(path: &Path, bad: &mut bool) { // Check that all packages use the 2021 edition. Virtual workspaces don't allow setting an // edition, so these shouldn't be checked. if is_package && !is_current_edition { - tidy_error!( - bad, + check.error(format!( "{} doesn't have `edition = \"2021\"` or `edition = \"2024\"` on a separate line", file.display() - ); + )); } }); } diff --git a/src/tools/tidy/src/error_codes.rs b/src/tools/tidy/src/error_codes.rs index 65aa89fe8016..83fbefa43d97 100644 --- a/src/tools/tidy/src/error_codes.rs +++ b/src/tools/tidy/src/error_codes.rs @@ -22,6 +22,7 @@ use std::path::Path; use regex::Regex; +use crate::diagnostics::{DiagCtx, RunningCheck}; use crate::walk::{filter_dirs, walk, walk_many}; const ERROR_CODES_PATH: &str = "compiler/rustc_error_codes/src/lib.rs"; @@ -35,71 +36,50 @@ const IGNORE_DOCTEST_CHECK: &[&str] = &["E0464", "E0570", "E0601", "E0602", "E07 const IGNORE_UI_TEST_CHECK: &[&str] = &["E0461", "E0465", "E0514", "E0554", "E0640", "E0717", "E0729"]; -macro_rules! verbose_print { - ($verbose:expr, $($fmt:tt)*) => { - if $verbose { - println!("{}", format_args!($($fmt)*)); - } - }; -} - -pub fn check( - root_path: &Path, - search_paths: &[&Path], - verbose: bool, - ci_info: &crate::CiInfo, - bad: &mut bool, -) { - let mut errors = Vec::new(); +pub fn check(root_path: &Path, search_paths: &[&Path], ci_info: &crate::CiInfo, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check("error_codes"); // Check that no error code explanation was removed. - check_removed_error_code_explanation(ci_info, bad); + check_removed_error_code_explanation(ci_info, &mut check); // Stage 1: create list - let error_codes = extract_error_codes(root_path, &mut errors); - if verbose { - println!("Found {} error codes", error_codes.len()); - println!("Highest error code: `{}`", error_codes.iter().max().unwrap()); - } + let error_codes = extract_error_codes(root_path, &mut check); + check.verbose_msg(format!("Found {} error codes", error_codes.len())); + check.verbose_msg(format!("Highest error code: `{}`", error_codes.iter().max().unwrap())); // Stage 2: check list has docs - let no_longer_emitted = check_error_codes_docs(root_path, &error_codes, &mut errors, verbose); + let no_longer_emitted = check_error_codes_docs(root_path, &error_codes, &mut check); // Stage 3: check list has UI tests - check_error_codes_tests(root_path, &error_codes, &mut errors, verbose, &no_longer_emitted); + check_error_codes_tests(root_path, &error_codes, &mut check, &no_longer_emitted); // Stage 4: check list is emitted by compiler - check_error_codes_used(search_paths, &error_codes, &mut errors, &no_longer_emitted, verbose); - - // Print any errors. - for error in errors { - tidy_error!(bad, "{}", error); - } + check_error_codes_used(search_paths, &error_codes, &mut check, &no_longer_emitted); } -fn check_removed_error_code_explanation(ci_info: &crate::CiInfo, bad: &mut bool) { +fn check_removed_error_code_explanation(ci_info: &crate::CiInfo, check: &mut RunningCheck) { let Some(base_commit) = &ci_info.base_commit else { - eprintln!("Skipping error code explanation removal check"); + check.verbose_msg("Skipping error code explanation removal check"); return; }; let Some(diff) = crate::git_diff(base_commit, "--name-status") else { - *bad = true; - eprintln!("removed error code explanation tidy check: Failed to run git diff"); + check.error(format!("removed error code explanation: Failed to run git diff")); return; }; if diff.lines().any(|line| { line.starts_with('D') && line.contains("compiler/rustc_error_codes/src/error_codes/") }) { - *bad = true; - eprintln!("tidy check error: Error code explanations should never be removed!"); - eprintln!("Take a look at E0001 to see how to handle it."); + check.error(format!( + r#"Error code explanations should never be removed! +Take a look at E0001 to see how to handle it."# + )); return; } - println!("No error code explanation was removed!"); + check.verbose_msg("No error code explanation was removed!"); } /// Stage 1: Parses a list of error codes from `error_codes.rs`. -fn extract_error_codes(root_path: &Path, errors: &mut Vec) -> Vec { +fn extract_error_codes(root_path: &Path, check: &mut RunningCheck) -> Vec { let path = root_path.join(Path::new(ERROR_CODES_PATH)); let file = fs::read_to_string(&path).unwrap_or_else(|e| panic!("failed to read `{path:?}`: {e}")); @@ -117,7 +97,7 @@ fn extract_error_codes(root_path: &Path, errors: &mut Vec) -> Vec) -> Vec) -> Vec) -> Vec) -> Vec, - verbose: bool, + check: &mut RunningCheck, ) -> Vec { let docs_path = root_path.join(Path::new(ERROR_DOCS_PATH)); @@ -184,7 +164,7 @@ fn check_error_codes_docs( // Error if the file isn't markdown. if path.extension() != Some(OsStr::new("md")) { - errors.push(format!( + check.error(format!( "Found unexpected non-markdown file in error code docs directory: {}", path.display() )); @@ -196,7 +176,7 @@ fn check_error_codes_docs( let err_code = filename.unwrap().0; // `unwrap` is ok because we know the filename is in the correct format. if error_codes.iter().all(|e| e != err_code) { - errors.push(format!( + check.error(format!( "Found valid file `{}` in error code docs directory without corresponding \ entry in `rustc_error_codes/src/lib.rs`", path.display() @@ -208,11 +188,10 @@ fn check_error_codes_docs( check_explanation_has_doctest(contents, err_code); if emit_ignore_warning { - verbose_print!( - verbose, + check.verbose_msg(format!( "warning: Error code `{err_code}` uses the ignore header. This should not be used, add the error code to the \ `IGNORE_DOCTEST_CHECK` constant instead." - ); + )); } if no_longer_emitted { @@ -220,11 +199,10 @@ fn check_error_codes_docs( } if !found_code_example { - verbose_print!( - verbose, + check.verbose_msg(format!( "warning: Error code `{err_code}` doesn't have a code example, all error codes are expected to have one \ (even if untested)." - ); + )); return; } @@ -232,12 +210,12 @@ fn check_error_codes_docs( // Check that the explanation has a doctest, and if it shouldn't, that it doesn't if !found_proper_doctest && !test_ignored { - errors.push(format!( + check.error(format!( "`{}` doesn't use its own error code in compile_fail example", path.display(), )); } else if found_proper_doctest && test_ignored { - errors.push(format!( + check.error(format!( "`{}` has a compile_fail doctest with its own error code, it shouldn't \ be listed in `IGNORE_DOCTEST_CHECK`", path.display(), @@ -289,8 +267,7 @@ fn check_explanation_has_doctest(explanation: &str, err_code: &str) -> (bool, bo fn check_error_codes_tests( root_path: &Path, error_codes: &[String], - errors: &mut Vec, - verbose: bool, + check: &mut RunningCheck, no_longer_emitted: &[String], ) { let tests_path = root_path.join(Path::new(ERROR_TESTS_PATH)); @@ -299,15 +276,14 @@ fn check_error_codes_tests( let test_path = tests_path.join(format!("{code}.stderr")); if !test_path.exists() && !IGNORE_UI_TEST_CHECK.contains(&code.as_str()) { - verbose_print!( - verbose, + check.verbose_msg(format!( "warning: Error code `{code}` needs to have at least one UI test in the `tests/error-codes/` directory`!" - ); + )); continue; } if IGNORE_UI_TEST_CHECK.contains(&code.as_str()) { if test_path.exists() { - errors.push(format!( + check.error(format!( "Error code `{code}` has a UI test in `tests/ui/error-codes/{code}.rs`, it shouldn't be listed in `EXEMPTED_FROM_TEST`!" )); } @@ -317,11 +293,10 @@ fn check_error_codes_tests( let file = match fs::read_to_string(&test_path) { Ok(file) => file, Err(err) => { - verbose_print!( - verbose, + check.verbose_msg(format!( "warning: Failed to read UI test file (`{}`) for `{code}` but the file exists. The test is assumed to work:\n{err}", test_path.display() - ); + )); continue; } }; @@ -343,10 +318,9 @@ fn check_error_codes_tests( } if !found_code { - verbose_print!( - verbose, + check.verbose_msg(format!( "warning: Error code `{code}` has a UI test file, but doesn't contain its own error code!" - ); + )); } } } @@ -355,9 +329,8 @@ fn check_error_codes_tests( fn check_error_codes_used( search_paths: &[&Path], error_codes: &[String], - errors: &mut Vec, + check: &mut RunningCheck, no_longer_emitted: &[String], - verbose: bool, ) { // Search for error codes in the form `E0123`. let regex = Regex::new(r#"\bE\d{4}\b"#).unwrap(); @@ -384,7 +357,7 @@ fn check_error_codes_used( if !error_codes.contains(&error_code) { // This error code isn't properly defined, we must error. - errors.push(format!("Error code `{error_code}` is used in the compiler but not defined and documented in `compiler/rustc_error_codes/src/lib.rs`.")); + check.error(format!("Error code `{error_code}` is used in the compiler but not defined and documented in `compiler/rustc_error_codes/src/lib.rs`.")); continue; } @@ -397,7 +370,7 @@ fn check_error_codes_used( for code in error_codes { if !found_codes.contains(code) && !no_longer_emitted.contains(code) { - errors.push(format!( + check.error(format!( "Error code `{code}` exists, but is not emitted by the compiler!\n\ Please mark the code as no longer emitted by adding the following note to the top of the `EXXXX.md` file:\n\ `#### Note: this error code is no longer emitted by the compiler`\n\ @@ -406,10 +379,9 @@ fn check_error_codes_used( } if found_codes.contains(code) && no_longer_emitted.contains(code) { - verbose_print!( - verbose, + check.verbose_msg(format!( "warning: Error code `{code}` is used when it's marked as \"no longer emitted\"" - ); + )); } } } diff --git a/src/tools/tidy/src/extdeps.rs b/src/tools/tidy/src/extdeps.rs index 2b212cfa67a4..21612980007d 100644 --- a/src/tools/tidy/src/extdeps.rs +++ b/src/tools/tidy/src/extdeps.rs @@ -4,6 +4,7 @@ use std::fs; use std::path::Path; use crate::deps::WorkspaceInfo; +use crate::diagnostics::{CheckId, DiagCtx}; /// List of allowed sources for packages. const ALLOWED_SOURCES: &[&str] = &[ @@ -14,7 +15,9 @@ const ALLOWED_SOURCES: &[&str] = &[ /// Checks for external package sources. `root` is the path to the directory that contains the /// workspace `Cargo.toml`. -pub fn check(root: &Path, bad: &mut bool) { +pub fn check(root: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("extdeps").path(root)); + for &WorkspaceInfo { path, submodules, .. } in crate::deps::WORKSPACES { if crate::deps::has_missing_submodule(root, submodules) { continue; @@ -25,7 +28,7 @@ pub fn check(root: &Path, bad: &mut bool) { let lockfile = root.join(path).join("Cargo.lock"); if !lockfile.exists() { - tidy_error!(bad, "the `{path}` workspace doesn't have a Cargo.lock"); + check.error(format!("the `{path}` workspace doesn't have a Cargo.lock")); continue; } @@ -44,7 +47,7 @@ pub fn check(root: &Path, bad: &mut bool) { // Ensure source is allowed. if !ALLOWED_SOURCES.contains(&source) { - tidy_error!(bad, "invalid source: {}", source); + check.error(format!("invalid source: {}", source)); } } } diff --git a/src/tools/tidy/src/extra_checks/mod.rs b/src/tools/tidy/src/extra_checks/mod.rs index a6b50b5ca470..9f2f2da2e86d 100644 --- a/src/tools/tidy/src/extra_checks/mod.rs +++ b/src/tools/tidy/src/extra_checks/mod.rs @@ -24,6 +24,7 @@ use std::str::FromStr; use std::{fmt, fs, io}; use crate::CiInfo; +use crate::diagnostics::DiagCtx; mod rustdoc_js; @@ -54,8 +55,10 @@ pub fn check( bless: bool, extra_checks: Option<&str>, pos_args: &[String], - bad: &mut bool, + diag_ctx: DiagCtx, ) { + let mut check = diag_ctx.start_check("extra_checks"); + if let Err(e) = check_impl( root_path, outdir, @@ -68,7 +71,7 @@ pub fn check( extra_checks, pos_args, ) { - tidy_error!(bad, "{e}"); + check.error(e); } } diff --git a/src/tools/tidy/src/features.rs b/src/tools/tidy/src/features.rs index 6618ba24be60..0a0ba217c633 100644 --- a/src/tools/tidy/src/features.rs +++ b/src/tools/tidy/src/features.rs @@ -16,6 +16,7 @@ use std::num::NonZeroU32; use std::path::{Path, PathBuf}; use std::{fmt, fs}; +use crate::diagnostics::{DiagCtx, RunningCheck}; use crate::walk::{filter_dirs, filter_not_rust, walk, walk_many}; #[cfg(test)] @@ -91,13 +92,14 @@ pub fn check( tests_path: &Path, compiler_path: &Path, lib_path: &Path, - bad: &mut bool, - verbose: bool, + diag_ctx: DiagCtx, ) -> CollectedFeatures { - let mut features = collect_lang_features(compiler_path, bad); + let mut check = diag_ctx.start_check("features"); + + let mut features = collect_lang_features(compiler_path, &mut check); assert!(!features.is_empty()); - let lib_features = get_and_check_lib_features(lib_path, bad, &features); + let lib_features = get_and_check_lib_features(lib_path, &mut check, &features); assert!(!lib_features.is_empty()); walk_many( @@ -121,7 +123,7 @@ pub fn check( for (i, line) in contents.lines().enumerate() { let mut err = |msg: &str| { - tidy_error!(bad, "{}:{}: {}", file.display(), i + 1, msg); + check.error(format!("{}:{}: {}", file.display(), i + 1, msg)); }; let gate_test_str = "gate-test-"; @@ -175,7 +177,7 @@ pub fn check( } if !gate_untested.is_empty() { - tidy_error!(bad, "Found {} features without a gate test.", gate_untested.len()); + check.error(format!("Found {} features without a gate test.", gate_untested.len())); } let (version, channel) = get_version_and_channel(src_path); @@ -189,39 +191,32 @@ pub fn check( let file = feature.file.display(); let line = feature.line; if since > version && since != Version::CurrentPlaceholder { - tidy_error!( - bad, + check.error(format!( "{file}:{line}: The stabilization version {since} of {kind} feature `{feature_name}` is newer than the current {version}" - ); + )); } if channel == "nightly" && since == version { - tidy_error!( - bad, + check.error(format!( "{file}:{line}: The stabilization version {since} of {kind} feature `{feature_name}` is written out but should be {}", version::VERSION_PLACEHOLDER - ); + )); } if channel != "nightly" && since == Version::CurrentPlaceholder { - tidy_error!( - bad, + check.error(format!( "{file}:{line}: The placeholder use of {kind} feature `{feature_name}` is not allowed on the {channel} channel", - ); + )); } } - if *bad { - return CollectedFeatures { lib: lib_features, lang: features }; - } - - if verbose { + if !check.is_bad() && check.is_verbose_enabled() { let mut lines = Vec::new(); lines.extend(format_features(&features, "lang")); lines.extend(format_features(&lib_features, "lib")); - lines.sort(); - for line in lines { - println!("* {line}"); - } + + check.verbose_msg( + lines.into_iter().map(|l| format!("* {l}")).collect::>().join("\n"), + ); } CollectedFeatures { lib: lib_features, lang: features } @@ -275,15 +270,20 @@ fn test_filen_gate<'f>(filen_underscore: &'f str, features: &mut Features) -> Op None } -pub fn collect_lang_features(base_compiler_path: &Path, bad: &mut bool) -> Features { +pub fn collect_lang_features(base_compiler_path: &Path, check: &mut RunningCheck) -> Features { let mut features = Features::new(); - collect_lang_features_in(&mut features, base_compiler_path, "accepted.rs", bad); - collect_lang_features_in(&mut features, base_compiler_path, "removed.rs", bad); - collect_lang_features_in(&mut features, base_compiler_path, "unstable.rs", bad); + collect_lang_features_in(&mut features, base_compiler_path, "accepted.rs", check); + collect_lang_features_in(&mut features, base_compiler_path, "removed.rs", check); + collect_lang_features_in(&mut features, base_compiler_path, "unstable.rs", check); features } -fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, bad: &mut bool) { +fn collect_lang_features_in( + features: &mut Features, + base: &Path, + file: &str, + check: &mut RunningCheck, +) { let path = base.join("rustc_feature").join("src").join(file); let contents = t!(fs::read_to_string(&path)); @@ -315,13 +315,11 @@ fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, ba if line.starts_with(FEATURE_GROUP_START_PREFIX) { if in_feature_group { - tidy_error!( - bad, - "{}:{}: \ + check.error(format!( + "{}:{line_number}: \ new feature group is started without ending the previous one", - path.display(), - line_number, - ); + path.display() + )); } in_feature_group = true; @@ -353,14 +351,10 @@ fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, ba let since = match since_str.parse() { Ok(since) => Some(since), Err(err) => { - tidy_error!( - bad, - "{}:{}: failed to parse since: {} ({:?})", - path.display(), - line_number, - since_str, - err, - ); + check.error(format!( + "{}:{line_number}: failed to parse since: {since_str} ({err:?})", + path.display() + )); None } }; @@ -371,13 +365,10 @@ fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, ba let correct_index = match prev_names.binary_search(&name) { Ok(_) => { // This only occurs when the feature name has already been declared. - tidy_error!( - bad, - "{}:{}: duplicate feature {}", - path.display(), - line_number, - name, - ); + check.error(format!( + "{}:{line_number}: duplicate feature {name}", + path.display() + )); // skip any additional checks for this line continue; } @@ -398,14 +389,10 @@ fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, ba ) }; - tidy_error!( - bad, - "{}:{}: feature {} is not sorted by feature name (should be {})", + check.error(format!( + "{}:{line_number}: feature {name} is not sorted by feature name (should be {correct_placement})", path.display(), - line_number, - name, - correct_placement, - ); + )); } prev_names.push(name); } @@ -413,13 +400,10 @@ fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, ba let issue_str = parts.next().unwrap().trim(); let tracking_issue = if issue_str.starts_with("None") { if level == Status::Unstable && !next_feature_omits_tracking_issue { - tidy_error!( - bad, - "{}:{}: no tracking issue for feature {}", + check.error(format!( + "{}:{line_number}: no tracking issue for feature {name}", path.display(), - line_number, - name, - ); + )); } None } else { @@ -428,13 +412,11 @@ fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, ba }; match features.entry(name.to_owned()) { Entry::Occupied(e) => { - tidy_error!( - bad, - "{}:{} feature {name} already specified with status '{}'", + check.error(format!( + "{}:{line_number} feature {name} already specified with status '{}'", path.display(), - line_number, e.get().level, - ); + )); } Entry::Vacant(e) => { e.insert(Feature { @@ -458,7 +440,7 @@ fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, ba fn get_and_check_lib_features( base_src_path: &Path, - bad: &mut bool, + check: &mut RunningCheck, lang_features: &Features, ) -> Features { let mut lib_features = Features::new(); @@ -469,16 +451,12 @@ fn get_and_check_lib_features( && f.tracking_issue != s.tracking_issue && f.level != Status::Accepted { - tidy_error!( - bad, - "{}:{}: feature gate {} has inconsistent `issue`: \"{}\" mismatches the {} `issue` of \"{}\"", + check.error(format!( + "{}:{line}: feature gate {name} has inconsistent `issue`: \"{}\" mismatches the {display} `issue` of \"{}\"", file.display(), - line, - name, f.tracking_issue_display(), - display, s.tracking_issue_display(), - ); + )); } }; check_features(&f, lang_features, "corresponding lang feature"); @@ -486,7 +464,7 @@ fn get_and_check_lib_features( lib_features.insert(name.to_owned(), f); } Err(msg) => { - tidy_error!(bad, "{}:{}: {}", file.display(), line, msg); + check.error(format!("{}:{line}: {msg}", file.display())); } }); lib_features diff --git a/src/tools/tidy/src/filenames.rs b/src/tools/tidy/src/filenames.rs index 53115f4eaa41..d52a82297388 100644 --- a/src/tools/tidy/src/filenames.rs +++ b/src/tools/tidy/src/filenames.rs @@ -10,7 +10,10 @@ use std::path::Path; use std::process::Command; -pub fn check(root_path: &Path, bad: &mut bool) { +use crate::diagnostics::{CheckId, DiagCtx}; + +pub fn check(root_path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("filenames").path(root_path)); let stat_output = Command::new("git") .arg("-C") .arg(root_path) @@ -20,20 +23,17 @@ pub fn check(root_path: &Path, bad: &mut bool) { .stdout; for filename in stat_output.split(|&b| b == 0) { match str::from_utf8(filename) { - Err(_) => tidy_error!( - bad, + Err(_) => check.error(format!( r#"non-UTF8 file names are not supported: "{}""#, String::from_utf8_lossy(filename), - ), - Ok(name) if name.chars().any(|c| c.is_control()) => tidy_error!( - bad, + )), + Ok(name) if name.chars().any(|c| c.is_control()) => check.error(format!( r#"control characters are not supported in file names: "{}""#, String::from_utf8_lossy(filename), - ), - Ok(name) if name.contains(':') => tidy_error!( - bad, + )), + Ok(name) if name.contains(':') => check.error(format!( r#"":" is not supported in file names because of Windows compatibility: "{name}""#, - ), + )), _ => (), } } diff --git a/src/tools/tidy/src/fluent_alphabetical.rs b/src/tools/tidy/src/fluent_alphabetical.rs index 48d14a37514b..02b914c21aeb 100644 --- a/src/tools/tidy/src/fluent_alphabetical.rs +++ b/src/tools/tidy/src/fluent_alphabetical.rs @@ -7,6 +7,7 @@ use std::path::Path; use regex::Regex; +use crate::diagnostics::{CheckId, DiagCtx, RunningCheck}; use crate::walk::{filter_dirs, walk}; fn message() -> &'static Regex { @@ -20,19 +21,17 @@ fn is_fluent(path: &Path) -> bool { fn check_alphabetic( filename: &str, fluent: &str, - bad: &mut bool, + check: &mut RunningCheck, all_defined_msgs: &mut HashMap, ) { let mut matches = message().captures_iter(fluent).peekable(); while let Some(m) = matches.next() { let name = m.get(1).unwrap(); if let Some(defined_filename) = all_defined_msgs.get(name.as_str()) { - tidy_error!( - bad, - "{filename}: message `{}` is already defined in {}", + check.error(format!( + "{filename}: message `{}` is already defined in {defined_filename}", name.as_str(), - defined_filename, - ); + )); } all_defined_msgs.insert(name.as_str().to_owned(), filename.to_owned()); @@ -40,13 +39,12 @@ fn check_alphabetic( if let Some(next) = matches.peek() { let next = next.get(1).unwrap(); if name.as_str() > next.as_str() { - tidy_error!( - bad, + check.error(format!( "{filename}: message `{}` appears before `{}`, but is alphabetically later than it run `./x.py test tidy --bless` to sort the file correctly", name.as_str(), next.as_str() - ); + )); } } else { break; @@ -57,7 +55,7 @@ run `./x.py test tidy --bless` to sort the file correctly", fn sort_messages( filename: &str, fluent: &str, - bad: &mut bool, + check: &mut RunningCheck, all_defined_msgs: &mut HashMap, ) -> String { let mut chunks = vec![]; @@ -65,12 +63,10 @@ fn sort_messages( for line in fluent.lines() { if let Some(name) = message().find(line) { if let Some(defined_filename) = all_defined_msgs.get(name.as_str()) { - tidy_error!( - bad, - "{filename}: message `{}` is already defined in {}", + check.error(format!( + "{filename}: message `{}` is already defined in {defined_filename}", name.as_str(), - defined_filename, - ); + )); } all_defined_msgs.insert(name.as_str().to_owned(), filename.to_owned()); @@ -88,7 +84,9 @@ fn sort_messages( out } -pub fn check(path: &Path, bless: bool, bad: &mut bool) { +pub fn check(path: &Path, bless: bool, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("fluent_alphabetical").path(path)); + let mut all_defined_msgs = HashMap::new(); walk( path, @@ -98,7 +96,7 @@ pub fn check(path: &Path, bless: bool, bad: &mut bool) { let sorted = sort_messages( ent.path().to_str().unwrap(), contents, - bad, + &mut check, &mut all_defined_msgs, ); if sorted != contents { @@ -110,12 +108,12 @@ pub fn check(path: &Path, bless: bool, bad: &mut bool) { check_alphabetic( ent.path().to_str().unwrap(), contents, - bad, + &mut check, &mut all_defined_msgs, ); } }, ); - crate::fluent_used::check(path, all_defined_msgs, bad); + crate::fluent_used::check(path, all_defined_msgs, diag_ctx); } diff --git a/src/tools/tidy/src/fluent_lowercase.rs b/src/tools/tidy/src/fluent_lowercase.rs index 13f0319909e1..1d80fda8f3b8 100644 --- a/src/tools/tidy/src/fluent_lowercase.rs +++ b/src/tools/tidy/src/fluent_lowercase.rs @@ -4,6 +4,7 @@ use std::path::Path; use fluent_syntax::ast::{Entry, Message, PatternElement}; +use crate::diagnostics::{CheckId, DiagCtx, RunningCheck}; use crate::walk::{filter_dirs, walk}; #[rustfmt::skip] @@ -34,7 +35,7 @@ fn is_allowed_capitalized_word(msg: &str) -> bool { }) } -fn check_lowercase(filename: &str, contents: &str, bad: &mut bool) { +fn check_lowercase(filename: &str, contents: &str, check: &mut RunningCheck) { let (Ok(parse) | Err((parse, _))) = fluent_syntax::parser::parse(contents); for entry in &parse.body { @@ -45,20 +46,20 @@ fn check_lowercase(filename: &str, contents: &str, bad: &mut bool) { && value.chars().next().is_some_and(char::is_uppercase) && !is_allowed_capitalized_word(value) { - tidy_error!( - bad, + check.error(format!( "{filename}: message `{value}` starts with an uppercase letter. Fix it or add it to `ALLOWED_CAPITALIZED_WORDS`" - ); + )); } } } -pub fn check(path: &Path, bad: &mut bool) { +pub fn check(path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("fluent_lowercase").path(path)); walk( path, |path, is_dir| filter_dirs(path) || (!is_dir && filter_fluent(path)), &mut |ent, contents| { - check_lowercase(ent.path().to_str().unwrap(), contents, bad); + check_lowercase(ent.path().to_str().unwrap(), contents, &mut check); }, ); } diff --git a/src/tools/tidy/src/fluent_period.rs b/src/tools/tidy/src/fluent_period.rs index 836b5699289f..c7c760b8d54f 100644 --- a/src/tools/tidy/src/fluent_period.rs +++ b/src/tools/tidy/src/fluent_period.rs @@ -4,6 +4,7 @@ use std::path::Path; use fluent_syntax::ast::{Entry, PatternElement}; +use crate::diagnostics::{CheckId, DiagCtx, RunningCheck}; use crate::walk::{filter_dirs, walk}; fn filter_fluent(path: &Path) -> bool { @@ -20,7 +21,7 @@ const ALLOWLIST: &[&str] = &[ "incremental_corrupt_file", ]; -fn check_period(filename: &str, contents: &str, bad: &mut bool) { +fn check_period(filename: &str, contents: &str, check: &mut RunningCheck) { if filename.contains("codegen") { // FIXME: Too many codegen messages have periods right now... return; @@ -40,7 +41,7 @@ fn check_period(filename: &str, contents: &str, bad: &mut bool) { if value.ends_with(".") && !value.ends_with("...") { let ll = find_line(contents, value); let name = m.id.name; - tidy_error!(bad, "{filename}:{ll}: message `{name}` ends in a period"); + check.error(format!("{filename}:{ll}: message `{name}` ends in a period")); } } @@ -56,7 +57,7 @@ fn check_period(filename: &str, contents: &str, bad: &mut bool) { { let ll = find_line(contents, value); let name = attr.id.name; - tidy_error!(bad, "{filename}:{ll}: attr `{name}` ends in a period"); + check.error(format!("{filename}:{ll}: attr `{name}` ends in a period")); } } } @@ -74,12 +75,14 @@ fn find_line(haystack: &str, needle: &str) -> usize { 1 } -pub fn check(path: &Path, bad: &mut bool) { +pub fn check(path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("fluent_period").path(path)); + walk( path, |path, is_dir| filter_dirs(path) || (!is_dir && filter_fluent(path)), &mut |ent, contents| { - check_period(ent.path().to_str().unwrap(), contents, bad); + check_period(ent.path().to_str().unwrap(), contents, &mut check); }, ); } diff --git a/src/tools/tidy/src/fluent_used.rs b/src/tools/tidy/src/fluent_used.rs index 909bf482ddfc..2047089631b3 100644 --- a/src/tools/tidy/src/fluent_used.rs +++ b/src/tools/tidy/src/fluent_used.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use std::path::Path; +use crate::diagnostics::{CheckId, DiagCtx}; use crate::walk::{filter_dirs, walk}; fn filter_used_messages( @@ -27,13 +28,15 @@ fn filter_used_messages( } } -pub fn check(path: &Path, mut all_defined_msgs: HashMap, bad: &mut bool) { +pub fn check(path: &Path, mut all_defined_msgs: HashMap, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("fluent_used").path(path)); + let mut msgs_appear_only_once = HashMap::new(); walk(path, |path, _| filter_dirs(path), &mut |_, contents| { filter_used_messages(contents, &mut all_defined_msgs, &mut msgs_appear_only_once); }); for (name, filename) in msgs_appear_only_once { - tidy_error!(bad, "{filename}: message `{}` is not used", name,); + check.error(format!("{filename}: message `{name}` is not used")); } } diff --git a/src/tools/tidy/src/gcc_submodule.rs b/src/tools/tidy/src/gcc_submodule.rs index 217eaf1758c4..3a6e3247de60 100644 --- a/src/tools/tidy/src/gcc_submodule.rs +++ b/src/tools/tidy/src/gcc_submodule.rs @@ -4,7 +4,11 @@ use std::path::Path; use std::process::Command; -pub fn check(root_path: &Path, compiler_path: &Path, bad: &mut bool) { +use crate::diagnostics::DiagCtx; + +pub fn check(root_path: &Path, compiler_path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check("gcc_submodule"); + let cg_gcc_version_path = compiler_path.join("rustc_codegen_gcc/libgccjit.version"); let cg_gcc_version = std::fs::read_to_string(&cg_gcc_version_path) .unwrap_or_else(|_| { @@ -26,7 +30,7 @@ pub fn check(root_path: &Path, compiler_path: &Path, bad: &mut bool) { // Git is not available or we are in a tarball if !git_output.status.success() { - eprintln!("Cannot figure out the SHA of the GCC submodule"); + check.message("Cannot figure out the SHA of the GCC submodule"); return; } @@ -43,12 +47,11 @@ pub fn check(root_path: &Path, compiler_path: &Path, bad: &mut bool) { // The SHA can start with + if the submodule is modified or - if it is not checked out. let gcc_submodule_sha = git_output.trim_start_matches(['+', '-']); if gcc_submodule_sha != cg_gcc_version { - *bad = true; - eprintln!( + check.error(format!( r#"Commit SHA of the src/gcc submodule (`{gcc_submodule_sha}`) does not match the required GCC version of the GCC codegen backend (`{cg_gcc_version}`). Make sure to set the src/gcc submodule to commit {cg_gcc_version}. The GCC codegen backend commit is configured at {}."#, cg_gcc_version_path.display(), - ); + )); } } diff --git a/src/tools/tidy/src/known_bug.rs b/src/tools/tidy/src/known_bug.rs index e1921715ab92..d3b75e0cf5b8 100644 --- a/src/tools/tidy/src/known_bug.rs +++ b/src/tools/tidy/src/known_bug.rs @@ -2,9 +2,11 @@ use std::path::Path; +use crate::diagnostics::{CheckId, DiagCtx}; use crate::walk::*; -pub fn check(filepath: &Path, bad: &mut bool) { +pub fn check(filepath: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("known_bug").path(filepath)); walk(filepath, |path, _is_dir| filter_not_rust(path), &mut |entry, contents| { let file: &Path = entry.path(); @@ -19,11 +21,10 @@ pub fn check(filepath: &Path, bad: &mut bool) { [.., "tests", "crashes", "auxiliary", _aux_file_rs] ) && !contents.lines().any(|line| line.starts_with("//@ known-bug: ")) { - tidy_error!( - bad, + check.error(format!( "{} crash/ice test does not have a \"//@ known-bug: \" directive", file.display() - ); + )); } }); } diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index 953d69919906..0bfee93796be 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -11,7 +11,8 @@ use std::{env, io}; use build_helper::ci::CiEnv; use build_helper::git::{GitConfig, get_closest_upstream_commit}; use build_helper::stage0_parser::{Stage0Config, parse_stage0_file}; -use termcolor::WriteColor; + +use crate::diagnostics::{DiagCtx, RunningCheck}; macro_rules! static_regex { ($re:literal) => {{ @@ -43,28 +44,6 @@ macro_rules! t { }; } -macro_rules! tidy_error { - ($bad:expr, $($fmt:tt)*) => ({ - $crate::tidy_error(&format_args!($($fmt)*).to_string()).expect("failed to output error"); - *$bad = true; - }); -} - -fn tidy_error(args: &str) -> std::io::Result<()> { - use std::io::Write; - - use termcolor::{Color, ColorChoice, ColorSpec, StandardStream}; - - let mut stderr = StandardStream::stdout(ColorChoice::Auto); - stderr.set_color(ColorSpec::new().set_fg(Some(Color::Red)))?; - - write!(&mut stderr, "tidy error")?; - stderr.set_color(&ColorSpec::new())?; - - writeln!(&mut stderr, ": {args}")?; - Ok(()) -} - pub struct CiInfo { pub git_merge_commit_email: String, pub nightly_branch: String, @@ -73,7 +52,9 @@ pub struct CiInfo { } impl CiInfo { - pub fn new(bad: &mut bool) -> Self { + pub fn new(diag_ctx: DiagCtx) -> Self { + let mut check = diag_ctx.start_check("CI history"); + let stage0 = parse_stage0_file(); let Stage0Config { nightly_branch, git_merge_commit_email, .. } = stage0.config; @@ -86,11 +67,14 @@ impl CiInfo { let base_commit = match get_closest_upstream_commit(None, &info.git_config(), info.ci_env) { Ok(Some(commit)) => Some(commit), Ok(None) => { - info.error_if_in_ci("no base commit found", bad); + info.error_if_in_ci("no base commit found", &mut check); None } Err(error) => { - info.error_if_in_ci(&format!("failed to retrieve base commit: {error}"), bad); + info.error_if_in_ci( + &format!("failed to retrieve base commit: {error}"), + &mut check, + ); None } }; @@ -105,12 +89,11 @@ impl CiInfo { } } - pub fn error_if_in_ci(&self, msg: &str, bad: &mut bool) { + pub fn error_if_in_ci(&self, msg: &str, check: &mut RunningCheck) { if self.ci_env.is_running_in_ci() { - *bad = true; - eprintln!("tidy check error: {msg}"); + check.error(msg); } else { - eprintln!("tidy check warning: {msg}. Some checks will be skipped."); + check.warning(format!("{msg}. Some checks will be skipped.")); } } } diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index 7d42102bbb9d..73ff183868a2 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -8,8 +8,6 @@ use std::collections::VecDeque; use std::num::NonZeroUsize; use std::path::PathBuf; use std::str::FromStr; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::{Arc, Mutex}; use std::thread::{self, ScopedJoinHandle, scope}; use std::{env, process}; @@ -52,11 +50,8 @@ fn main() { let extra_checks = cfg_args.iter().find(|s| s.starts_with("--extra-checks=")).map(String::as_str); - let mut bad = false; - let ci_info = CiInfo::new(&mut bad); - let bad = std::sync::Arc::new(AtomicBool::new(bad)); - - let mut diag_ctx = DiagCtx::new(verbose); + let diag_ctx = DiagCtx::new(verbose); + let ci_info = CiInfo::new(diag_ctx.clone()); let drain_handles = |handles: &mut VecDeque>| { // poll all threads for completion before awaiting the oldest one @@ -99,98 +94,85 @@ fn main() { } } - // check!(target_specific_tests, &tests_path); + check!(target_specific_tests, &tests_path); // Checks that are done on the cargo workspace. - // check!(deps, &root_path, &cargo, bless); - // check!(extdeps, &root_path); + check!(deps, &root_path, &cargo, bless); + check!(extdeps, &root_path); // Checks over tests. - // check!(tests_placement, &root_path); - // check!(tests_revision_unpaired_stdout_stderr, &tests_path); - // check!(debug_artifacts, &tests_path); - // check!(ui_tests, &root_path, bless); - // check!(mir_opt_tests, &tests_path, bless); - // check!(rustdoc_gui_tests, &tests_path); - // check!(rustdoc_css_themes, &librustdoc_path); - // check!(rustdoc_templates, &librustdoc_path); - // check!(rustdoc_json, &src_path, &ci_info); - // check!(known_bug, &crashes_path); - // check!(unknown_revision, &tests_path); + check!(tests_placement, &root_path); + check!(tests_revision_unpaired_stdout_stderr, &tests_path); + check!(debug_artifacts, &tests_path); + check!(ui_tests, &root_path, bless); + check!(mir_opt_tests, &tests_path, bless); + check!(rustdoc_gui_tests, &tests_path); + check!(rustdoc_css_themes, &librustdoc_path); + check!(rustdoc_templates, &librustdoc_path); + check!(rustdoc_json, &src_path, &ci_info); + check!(known_bug, &crashes_path); + check!(unknown_revision, &tests_path); // Checks that only make sense for the compiler. - // check!(error_codes, &root_path, &[&compiler_path, &librustdoc_path], verbose, &ci_info); - // check!(fluent_alphabetical, &compiler_path, bless); - // check!(fluent_period, &compiler_path); - // check!(fluent_lowercase, &compiler_path); - // check!(target_policy, &root_path); - // check!(gcc_submodule, &root_path, &compiler_path); + check!(error_codes, &root_path, &[&compiler_path, &librustdoc_path], &ci_info); + check!(fluent_alphabetical, &compiler_path, bless); + check!(fluent_period, &compiler_path); + check!(fluent_lowercase, &compiler_path); + check!(target_policy, &root_path); + check!(gcc_submodule, &root_path, &compiler_path); // Checks that only make sense for the std libs. - // check!(pal, &library_path); + check!(pal, &library_path); // Checks that need to be done for both the compiler and std libraries. - // check!(unit_tests, &src_path, false); - // check!(unit_tests, &compiler_path, false); - // check!(unit_tests, &library_path, true); - // - // if bins::check_filesystem_support(&[&root_path], &output_directory) { - // check!(bins, &root_path); - // } + check!(unit_tests, &src_path, false); + check!(unit_tests, &compiler_path, false); + check!(unit_tests, &library_path, true); + + if bins::check_filesystem_support(&[&root_path], &output_directory) { + check!(bins, &root_path); + } check!(style, &src_path); check!(style, &tests_path); check!(style, &compiler_path); check!(style, &library_path); - // check!(edition, &src_path); - // check!(edition, &compiler_path); - // check!(edition, &library_path); - // + check!(edition, &src_path); + check!(edition, &compiler_path); + check!(edition, &library_path); + check!(alphabetical, &root_manifest); check!(alphabetical, &src_path); check!(alphabetical, &tests_path); check!(alphabetical, &compiler_path); check!(alphabetical, &library_path); - // - // check!(x_version, &root_path, &cargo); - // - // check!(triagebot, &root_path); - // - // check!(filenames, &root_path); + + check!(x_version, &root_path, &cargo); + + check!(triagebot, &root_path); + check!(filenames, &root_path); let collected = { drain_handles(&mut handles); - let mut flag = false; - let r = features::check( - &src_path, - &tests_path, - &compiler_path, - &library_path, - &mut flag, - verbose, - ); - if flag { - bad.store(true, Ordering::Relaxed); - } - r + features::check(&src_path, &tests_path, &compiler_path, &library_path, diag_ctx.clone()) }; - // check!(unstable_book, &src_path, collected); - // - // check!( - // extra_checks, - // &root_path, - // &output_directory, - // &ci_info, - // &librustdoc_path, - // &tools_path, - // &npm, - // &cargo, - // bless, - // extra_checks, - // pos_args - // ); + check!(unstable_book, &src_path, collected); + + check!( + extra_checks, + &root_path, + &output_directory, + &ci_info, + &librustdoc_path, + &tools_path, + &npm, + &cargo, + bless, + extra_checks, + pos_args + ); }); if diag_ctx.into_conclusion() { diff --git a/src/tools/tidy/src/mir_opt_tests.rs b/src/tools/tidy/src/mir_opt_tests.rs index 6119eb58383e..0f9fab51d096 100644 --- a/src/tools/tidy/src/mir_opt_tests.rs +++ b/src/tools/tidy/src/mir_opt_tests.rs @@ -5,9 +5,10 @@ use std::path::{Path, PathBuf}; use miropt_test_tools::PanicStrategy; +use crate::diagnostics::{CheckId, DiagCtx, RunningCheck}; use crate::walk::walk_no_read; -fn check_unused_files(path: &Path, bless: bool, bad: &mut bool) { +fn check_unused_files(path: &Path, bless: bool, check: &mut RunningCheck) { let mut rs_files = Vec::::new(); let mut output_files = HashSet::::new(); @@ -37,18 +38,17 @@ fn check_unused_files(path: &Path, bless: bool, bad: &mut bool) { for extra in output_files { if !bless { - tidy_error!( - bad, + check.error(format!( "the following output file is not associated with any mir-opt test, you can remove it: {}", extra.display() - ); + )); } else { let _ = std::fs::remove_file(extra); } } } -fn check_dash_files(path: &Path, bless: bool, bad: &mut bool) { +fn check_dash_files(path: &Path, bless: bool, check: &mut RunningCheck) { for file in walkdir::WalkDir::new(path.join("mir-opt")) .into_iter() .filter_map(Result::ok) @@ -60,11 +60,10 @@ fn check_dash_files(path: &Path, bless: bool, bad: &mut bool) { && name.contains('-') { if !bless { - tidy_error!( - bad, + check.error(format!( "mir-opt test files should not have dashes in them: {}", path.display() - ); + )); } else { let new_name = name.replace('-', "_"); let mut new_path = path.to_owned(); @@ -75,7 +74,9 @@ fn check_dash_files(path: &Path, bless: bool, bad: &mut bool) { } } -pub fn check(path: &Path, bless: bool, bad: &mut bool) { - check_unused_files(path, bless, bad); - check_dash_files(path, bless, bad); +pub fn check(path: &Path, bless: bool, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("mir_opt_tests").path(path)); + + check_unused_files(path, bless, &mut check); + check_dash_files(path, bless, &mut check); } diff --git a/src/tools/tidy/src/pal.rs b/src/tools/tidy/src/pal.rs index 991ad55809cc..cefad7d9596a 100644 --- a/src/tools/tidy/src/pal.rs +++ b/src/tools/tidy/src/pal.rs @@ -32,6 +32,7 @@ use std::path::Path; +use crate::diagnostics::{CheckId, DiagCtx, RunningCheck}; use crate::walk::{filter_dirs, walk}; // Paths that may contain platform-specific code. @@ -67,7 +68,9 @@ const EXCEPTION_PATHS: &[&str] = &[ "library/std/src/io/error.rs", // Repr unpacked needed for UEFI ]; -pub fn check(path: &Path, bad: &mut bool) { +pub fn check(path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("pal").path(path)); + // Sanity check that the complex parsing here works. let mut saw_target_arch = false; let mut saw_cfg_bang = false; @@ -88,7 +91,7 @@ pub fn check(path: &Path, bad: &mut bool) { return; } - check_cfgs(contents, file, bad, &mut saw_target_arch, &mut saw_cfg_bang); + check_cfgs(contents, file, &mut check, &mut saw_target_arch, &mut saw_cfg_bang); }); assert!(saw_target_arch); @@ -98,7 +101,7 @@ pub fn check(path: &Path, bad: &mut bool) { fn check_cfgs( contents: &str, file: &Path, - bad: &mut bool, + check: &mut RunningCheck, saw_target_arch: &mut bool, saw_cfg_bang: &mut bool, ) { @@ -115,7 +118,7 @@ fn check_cfgs( Ok(_) => unreachable!(), Err(i) => i + 1, }; - tidy_error!(bad, "{}:{}: platform-specific cfg: {}", file.display(), line, cfg); + check.error(format!("{}:{line}: platform-specific cfg: {cfg}", file.display())); }; for (idx, cfg) in cfgs { diff --git a/src/tools/tidy/src/rustdoc_css_themes.rs b/src/tools/tidy/src/rustdoc_css_themes.rs index af36f9ba58e0..8d4af7a3bd56 100644 --- a/src/tools/tidy/src/rustdoc_css_themes.rs +++ b/src/tools/tidy/src/rustdoc_css_themes.rs @@ -3,7 +3,11 @@ use std::path::Path; -pub fn check(librustdoc_path: &Path, bad: &mut bool) { +use crate::diagnostics::{CheckId, DiagCtx, RunningCheck}; + +pub fn check(librustdoc_path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("rustdoc_css_themes").path(librustdoc_path)); + let rustdoc_css = "html/static/css/rustdoc.css"; let noscript_css = "html/static/css/noscript.css"; let rustdoc_css_contents = std::fs::read_to_string(librustdoc_path.join(rustdoc_css)) @@ -14,13 +18,13 @@ pub fn check(librustdoc_path: &Path, bad: &mut bool) { "light", rustdoc_css_contents.lines().enumerate().map(|(i, l)| (i + 1, l.trim())), noscript_css_contents.lines().enumerate().map(|(i, l)| (i + 1, l.trim())), - bad, + &mut check, ); compare_themes_from_files( "dark", rustdoc_css_contents.lines().enumerate(), noscript_css_contents.lines().enumerate(), - bad, + &mut check, ); } @@ -28,7 +32,7 @@ fn compare_themes_from_files<'a>( name: &str, mut rustdoc_css_lines: impl Iterator, mut noscript_css_lines: impl Iterator, - bad: &mut bool, + check: &mut RunningCheck, ) { let begin_theme_pat = format!("/* Begin theme: {name}"); let mut found_theme = None; @@ -38,10 +42,9 @@ fn compare_themes_from_files<'a>( continue; } if let Some(found_theme) = found_theme { - tidy_error!( - bad, + check.error(format!( "rustdoc.css contains two {name} themes on lines {rustdoc_css_line_number} and {found_theme}", - ); + )); return; } found_theme = Some(rustdoc_css_line_number); @@ -50,14 +53,13 @@ fn compare_themes_from_files<'a>( continue; } if let Some(found_theme_noscript) = found_theme_noscript { - tidy_error!( - bad, + check.error(format!( "noscript.css contains two {name} themes on lines {noscript_css_line_number} and {found_theme_noscript}", - ); + )); return; } found_theme_noscript = Some(noscript_css_line_number); - compare_themes(name, &mut rustdoc_css_lines, &mut noscript_css_lines, bad); + compare_themes(name, &mut rustdoc_css_lines, &mut noscript_css_lines, check); } } } @@ -66,7 +68,7 @@ fn compare_themes<'a>( name: &str, rustdoc_css_lines: impl Iterator, noscript_css_lines: impl Iterator, - bad: &mut bool, + check: &mut RunningCheck, ) { let end_theme_pat = format!("/* End theme: {name}"); for ( @@ -90,12 +92,11 @@ fn compare_themes<'a>( break; } if rustdoc_css_line != noscript_css_line { - tidy_error!( - bad, - "noscript.css:{noscript_css_line_number} and rustdoc.css:{rustdoc_css_line_number} contain copies of {name} theme that are not the same", - ); - eprintln!("- {noscript_css_line}"); - eprintln!("+ {rustdoc_css_line}"); + check.error(format!( + r#"noscript.css:{noscript_css_line_number} and rustdoc.css:{rustdoc_css_line_number} contain copies of {name} theme that are not the same +- {noscript_css_line} ++ {rustdoc_css_line}"#, + )); return; } } diff --git a/src/tools/tidy/src/rustdoc_gui_tests.rs b/src/tools/tidy/src/rustdoc_gui_tests.rs index 3b995f219d26..8ec300c42ce9 100644 --- a/src/tools/tidy/src/rustdoc_gui_tests.rs +++ b/src/tools/tidy/src/rustdoc_gui_tests.rs @@ -2,18 +2,21 @@ use std::path::Path; -pub fn check(path: &Path, bad: &mut bool) { +use crate::diagnostics::{CheckId, DiagCtx}; + +pub fn check(path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("rustdoc_gui_tests").path(path)); + crate::walk::walk( &path.join("rustdoc-gui"), |p, is_dir| !is_dir && p.extension().is_none_or(|e| e != "goml"), &mut |entry, content| { for line in content.lines() { if !line.starts_with("// ") { - tidy_error!( - bad, + check.error(format!( "{}: rustdoc-gui tests must start with a small description", entry.path().display(), - ); + )); return; } else if line.starts_with("// ") { let parts = line[2..].trim(); diff --git a/src/tools/tidy/src/rustdoc_json.rs b/src/tools/tidy/src/rustdoc_json.rs index 722e1ebd0cad..7a53c08737f6 100644 --- a/src/tools/tidy/src/rustdoc_json.rs +++ b/src/tools/tidy/src/rustdoc_json.rs @@ -4,19 +4,22 @@ use std::path::Path; use std::str::FromStr; +use crate::diagnostics::{CheckId, DiagCtx}; + const RUSTDOC_JSON_TYPES: &str = "src/rustdoc-json-types"; -pub fn check(src_path: &Path, ci_info: &crate::CiInfo, bad: &mut bool) { - println!("Checking tidy rustdoc_json..."); +pub fn check(src_path: &Path, ci_info: &crate::CiInfo, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("rustdoc_json").path(src_path)); + let Some(base_commit) = &ci_info.base_commit else { - eprintln!("No base commit, skipping rustdoc_json check"); + check.verbose_msg("No base commit, skipping rustdoc_json check"); return; }; // First we check that `src/rustdoc-json-types` was modified. if !crate::files_modified(ci_info, |p| p == RUSTDOC_JSON_TYPES) { // `rustdoc-json-types` was not modified so nothing more to check here. - println!("`rustdoc-json-types` was not modified."); + check.verbose_msg("`rustdoc-json-types` was not modified."); return; } // Then we check that if `FORMAT_VERSION` was updated, the `Latest feature:` was also updated. @@ -45,34 +48,29 @@ pub fn check(src_path: &Path, ci_info: &crate::CiInfo, bad: &mut bool) { } } if format_version_updated != latest_feature_comment_updated { - *bad = true; - if latest_feature_comment_updated { - eprintln!( - "error in `rustdoc_json` tidy check: `Latest feature` comment was updated \ - whereas `FORMAT_VERSION` wasn't in `{RUSTDOC_JSON_TYPES}/lib.rs`" - ); + let msg = if latest_feature_comment_updated { + format!( + "`Latest feature` comment was updated whereas `FORMAT_VERSION` wasn't in `{RUSTDOC_JSON_TYPES}/lib.rs`" + ) } else { - eprintln!( - "error in `rustdoc_json` tidy check: `Latest feature` comment was not \ - updated whereas `FORMAT_VERSION` was in `{RUSTDOC_JSON_TYPES}/lib.rs`" - ); - } + format!( + "`Latest feature` comment was not updated whereas `FORMAT_VERSION` was in `{RUSTDOC_JSON_TYPES}/lib.rs`" + ) + }; + check.error(msg); } match (new_version, old_version) { (Some(new_version), Some(old_version)) if new_version != old_version + 1 => { - *bad = true; - eprintln!( - "error in `rustdoc_json` tidy check: invalid `FORMAT_VERSION` increase in \ - `{RUSTDOC_JSON_TYPES}/lib.rs`, should be `{}`, found `{new_version}`", + check.error(format!( + "invalid `FORMAT_VERSION` increase in `{RUSTDOC_JSON_TYPES}/lib.rs`, should be `{}`, found `{new_version}`", old_version + 1, - ); + )); } _ => {} } } None => { - *bad = true; - eprintln!("error: failed to run `git diff` in rustdoc_json check"); + check.error("failed to run `git diff` in rustdoc_json check"); } } } diff --git a/src/tools/tidy/src/rustdoc_templates.rs b/src/tools/tidy/src/rustdoc_templates.rs index 597290a6a9a8..4e5b9988d539 100644 --- a/src/tools/tidy/src/rustdoc_templates.rs +++ b/src/tools/tidy/src/rustdoc_templates.rs @@ -6,12 +6,15 @@ use std::path::Path; use ignore::DirEntry; +use crate::diagnostics::{CheckId, DiagCtx}; use crate::walk::walk; // Array containing `("beginning of tag", "end of tag")`. const TAGS: &[(&str, &str)] = &[("{#", "#}"), ("{%", "%}"), ("{{", "}}")]; -pub fn check(librustdoc_path: &Path, bad: &mut bool) { +pub fn check(librustdoc_path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("rustdoc_templates").path(librustdoc_path)); + walk( &librustdoc_path.join("html/templates"), |path, is_dir| is_dir || path.extension().is_none_or(|ext| ext != OsStr::new("html")), @@ -46,12 +49,11 @@ pub fn check(librustdoc_path: &Path, bad: &mut bool) { }) { // It seems like ending this line with a jinja tag is not needed after all. - tidy_error!( - bad, + check.error(format!( "`{}` at line {}: unneeded `{{# #}}` tag at the end of the line", path.path().display(), pos + 1, - ); + )); } continue; } @@ -67,12 +69,11 @@ pub fn check(librustdoc_path: &Path, bad: &mut bool) { }) { None => { // No it's not, let's error. - tidy_error!( - bad, + check.error(format!( "`{}` at line {}: missing `{{# #}}` at the end of the line", path.path().display(), pos + 1, - ); + )); } Some(end_tag) => { // We skip the tag. diff --git a/src/tools/tidy/src/target_policy.rs b/src/tools/tidy/src/target_policy.rs index 550932dbfdc3..cfcfcaf2435b 100644 --- a/src/tools/tidy/src/target_policy.rs +++ b/src/tools/tidy/src/target_policy.rs @@ -5,6 +5,7 @@ use std::collections::HashSet; use std::path::Path; +use crate::diagnostics::DiagCtx; use crate::walk::{filter_not_rust, walk}; const TARGET_DEFINITIONS_PATH: &str = "compiler/rustc_target/src/spec/targets/"; @@ -23,7 +24,9 @@ const EXCEPTIONS: &[&str] = &[ "xtensa_esp32s3_espidf", ]; -pub fn check(root_path: &Path, bad: &mut bool) { +pub fn check(root_path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check("target_policy"); + let mut targets_to_find = HashSet::new(); let definitions_path = root_path.join(TARGET_DEFINITIONS_PATH); @@ -55,7 +58,7 @@ pub fn check(root_path: &Path, bad: &mut bool) { for target in targets_to_find { if !EXCEPTIONS.contains(&target.as_str()) { - tidy_error!(bad, "{ASSEMBLY_LLVM_TEST_PATH}: missing assembly test for {target}") + check.error(format!("{ASSEMBLY_LLVM_TEST_PATH}: missing assembly test for {target}")); } } } diff --git a/src/tools/tidy/src/target_specific_tests.rs b/src/tools/tidy/src/target_specific_tests.rs index b2d5f259eb2d..c1db3874ad54 100644 --- a/src/tools/tidy/src/target_specific_tests.rs +++ b/src/tools/tidy/src/target_specific_tests.rs @@ -4,6 +4,7 @@ use std::collections::BTreeMap; use std::path::Path; +use crate::diagnostics::{CheckId, DiagCtx}; use crate::iter_header::{HeaderLine, iter_header}; use crate::walk::filter_not_rust; @@ -16,7 +17,9 @@ struct RevisionInfo<'a> { llvm_components: Option>, } -pub fn check(tests_path: &Path, bad: &mut bool) { +pub fn check(tests_path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("target-specific-tests").path(tests_path)); + crate::walk::walk(tests_path, |path, _is_dir| filter_not_rust(path), &mut |entry, content| { if content.contains("// ignore-tidy-target-specific-tests") { return; @@ -44,8 +47,7 @@ pub fn check(tests_path: &Path, bad: &mut bool) { } else if let Some((arch, _)) = v.split_once("-") { info.target_arch.replace(Some(arch)); } else { - eprintln!("{file}: seems to have a malformed --target value"); - *bad = true; + check.error(format!("{file}: seems to have a malformed --target value")); } } }); @@ -62,25 +64,22 @@ pub fn check(tests_path: &Path, bad: &mut bool) { (Some(target_arch), None) => { let llvm_component = target_arch.map_or_else(|| "".to_string(), arch_to_llvm_component); - eprintln!( + check.error(format!( "{file}: revision {rev} should specify `{LLVM_COMPONENTS_HEADER} {llvm_component}` as it has `--target` set" - ); - *bad = true; + )); } (None, Some(_)) => { - eprintln!( + check.error(format!( "{file}: revision {rev} should not specify `{LLVM_COMPONENTS_HEADER}` as it doesn't need `--target`" - ); - *bad = true; + )); } (Some(target_arch), Some(llvm_components)) => { if let Some(target_arch) = target_arch { let llvm_component = arch_to_llvm_component(target_arch); if !llvm_components.contains(&llvm_component.as_str()) { - eprintln!( + check.error(format!( "{file}: revision {rev} should specify `{LLVM_COMPONENTS_HEADER} {llvm_component}` as it has `--target` set" - ); - *bad = true; + )); } } } diff --git a/src/tools/tidy/src/tests_placement.rs b/src/tools/tidy/src/tests_placement.rs index 9d0057df8bcd..e978e1c453c1 100644 --- a/src/tools/tidy/src/tests_placement.rs +++ b/src/tools/tidy/src/tests_placement.rs @@ -1,15 +1,18 @@ use std::path::Path; +use crate::diagnostics::{CheckId, DiagCtx}; + const FORBIDDEN_PATH: &str = "src/test"; const ALLOWED_PATH: &str = "tests"; -pub fn check(root_path: impl AsRef, bad: &mut bool) { - if root_path.as_ref().join(FORBIDDEN_PATH).exists() { - tidy_error!( - bad, +pub fn check(root_path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("tests-placement").path(root_path)); + + if root_path.join(FORBIDDEN_PATH).exists() { + check.error(format!( "Tests have been moved, please move them from {} to {}", - root_path.as_ref().join(FORBIDDEN_PATH).display(), - root_path.as_ref().join(ALLOWED_PATH).display() - ) + root_path.join(FORBIDDEN_PATH).display(), + root_path.join(ALLOWED_PATH).display() + )); } } diff --git a/src/tools/tidy/src/tests_revision_unpaired_stdout_stderr.rs b/src/tools/tidy/src/tests_revision_unpaired_stdout_stderr.rs index 02412b6f190e..1738088a3a0c 100644 --- a/src/tools/tidy/src/tests_revision_unpaired_stdout_stderr.rs +++ b/src/tools/tidy/src/tests_revision_unpaired_stdout_stderr.rs @@ -4,6 +4,7 @@ use std::collections::{BTreeMap, BTreeSet}; use std::ffi::OsStr; use std::path::Path; +use crate::diagnostics::{CheckId, DiagCtx}; use crate::iter_header::*; use crate::walk::*; @@ -21,7 +22,10 @@ const IGNORES: &[&str] = &[ const EXTENSIONS: &[&str] = &["stdout", "stderr"]; const SPECIAL_TEST: &str = "tests/ui/command/need-crate-arg-ignore-tidy.x.rs"; -pub fn check(tests_path: impl AsRef, bad: &mut bool) { +pub fn check(tests_path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx + .start_check(CheckId::new("tests_revision_unpaired_stdout_stderr").path(tests_path)); + // Recurse over subdirectories under `tests/` walk_dir(tests_path.as_ref(), filter, &mut |entry| { // We are inspecting a folder. Collect the paths to interesting files `.rs`, `.stderr`, @@ -122,12 +126,11 @@ pub fn check(tests_path: impl AsRef, bad: &mut bool) { [] | [_] => return, [_, _] if !expected_revisions.is_empty() => { // Found unrevisioned output files for a revisioned test. - tidy_error!( - bad, + check.error(format!( "found unrevisioned output file `{}` for a revisioned test `{}`", sibling.display(), test_path.display(), - ); + )); } [_, _] => return, [_, found_revision, .., extension] => { @@ -138,13 +141,12 @@ pub fn check(tests_path: impl AsRef, bad: &mut bool) { { // Found some unexpected revision-esque component that is not a known // compare-mode or expected revision. - tidy_error!( - bad, + check.error(format!( "found output file `{}` for unexpected revision `{}` of test `{}`", sibling.display(), found_revision, test_path.display() - ); + )); } } } diff --git a/src/tools/tidy/src/triagebot.rs b/src/tools/tidy/src/triagebot.rs index 6f25ed616fac..9eccef29f2e2 100644 --- a/src/tools/tidy/src/triagebot.rs +++ b/src/tools/tidy/src/triagebot.rs @@ -4,7 +4,10 @@ use std::path::Path; use toml::Value; -pub fn check(path: &Path, bad: &mut bool) { +use crate::diagnostics::{CheckId, DiagCtx}; + +pub fn check(path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("triagebot").path(path)); let triagebot_path = path.join("triagebot.toml"); // This check is mostly to catch broken path filters *within* `triagebot.toml`, and not enforce @@ -30,17 +33,14 @@ pub fn check(path: &Path, bad: &mut bool) { let full_path = path.join(clean_path); if !full_path.exists() { - tidy_error!( - bad, - "triagebot.toml [mentions.*] contains path '{}' which doesn't exist", - clean_path - ); + check.error(format!( + "triagebot.toml [mentions.*] contains path '{clean_path}' which doesn't exist" + )); } } } else { - tidy_error!( - bad, - "triagebot.toml missing [mentions.*] section, this wrong for rust-lang/rust repo." + check.error( + "triagebot.toml missing [mentions.*] section, this wrong for rust-lang/rust repo.", ); } @@ -55,16 +55,13 @@ pub fn check(path: &Path, bad: &mut bool) { let full_path = path.join(clean_path); if !full_path.exists() { - tidy_error!( - bad, - "triagebot.toml [assign.owners] contains path '{}' which doesn't exist", - clean_path - ); + check.error(format!( + "triagebot.toml [assign.owners] contains path '{clean_path}' which doesn't exist" + )); } } } else { - tidy_error!( - bad, + check.error( "triagebot.toml missing [assign.owners] section, this wrong for rust-lang/rust repo." ); } @@ -86,12 +83,9 @@ pub fn check(path: &Path, bad: &mut bool) { // Handle both file and directory paths if !full_path.exists() { - tidy_error!( - bad, - "triagebot.toml [autolabel.{}] contains trigger_files path '{}' which doesn't exist", - label, - file_str - ); + check.error(format!( + "triagebot.toml [autolabel.{label}] contains trigger_files path '{file_str}' which doesn't exist", + )); } } } diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index 5bf966b658c6..12eca47c171d 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -7,13 +7,16 @@ use std::fs; use std::io::Write; use std::path::{Path, PathBuf}; +use crate::diagnostics::{CheckId, DiagCtx, RunningCheck}; + const ISSUES_TXT_HEADER: &str = r#"============================================================ ⚠️⚠️⚠️NOTHING SHOULD EVER BE ADDED TO THIS LIST⚠️⚠️⚠️ ============================================================ "#; -pub fn check(root_path: &Path, bless: bool, bad: &mut bool) { +pub fn check(root_path: &Path, bless: bool, diag_ctx: DiagCtx) { let path = &root_path.join("tests"); + let mut check = diag_ctx.start_check(CheckId::new("ui_tests").path(path)); // the list of files in ui tests that are allowed to start with `issue-XXXX` // BTreeSet because we would like a stable ordering so --bless works @@ -33,16 +36,15 @@ pub fn check(root_path: &Path, bless: bool, bad: &mut bool) { .collect(); if !is_sorted && !bless { - tidy_error!( - bad, + check.error( "`src/tools/tidy/src/issues.txt` is not in order, mostly because you modified it manually, please only update it with command `x test tidy --bless`" ); } - deny_new_top_level_ui_tests(bad, &path.join("ui")); + deny_new_top_level_ui_tests(&mut check, &path.join("ui")); - let remaining_issue_names = recursively_check_ui_tests(bad, path, &allowed_issue_names); + let remaining_issue_names = recursively_check_ui_tests(&mut check, path, &allowed_issue_names); // if there are any file names remaining, they were moved on the fs. // our data must remain up to date, so it must be removed from issues.txt @@ -64,16 +66,15 @@ pub fn check(root_path: &Path, bless: bool, bad: &mut bool) { for file_name in remaining_issue_names { let mut p = PathBuf::from(path); p.push(file_name); - tidy_error!( - bad, + check.error(format!( "file `{}` no longer exists and should be removed from the exclusions in `src/tools/tidy/src/issues.txt`", p.display() - ); + )); } } } -fn deny_new_top_level_ui_tests(bad: &mut bool, tests_path: &Path) { +fn deny_new_top_level_ui_tests(check: &mut RunningCheck, tests_path: &Path) { // See where we propose banning adding // new ui tests *directly* under `tests/ui/`. For more context, see: // @@ -93,16 +94,15 @@ fn deny_new_top_level_ui_tests(bad: &mut bool, tests_path: &Path) { }) .filter(|e| !e.file_type().is_dir()); for entry in top_level_ui_tests { - tidy_error!( - bad, + check.error(format!( "ui tests should be added under meaningful subdirectories: `{}`", entry.path().display() - ) + )); } } fn recursively_check_ui_tests<'issues>( - bad: &mut bool, + check: &mut RunningCheck, path: &Path, allowed_issue_names: &'issues BTreeSet<&'issues str>, ) -> BTreeSet<&'issues str> { @@ -113,19 +113,19 @@ fn recursively_check_ui_tests<'issues>( crate::walk::walk_no_read(&paths, |_, _| false, &mut |entry| { let file_path = entry.path(); if let Some(ext) = file_path.extension().and_then(OsStr::to_str) { - check_unexpected_extension(bad, file_path, ext); + check_unexpected_extension(check, file_path, ext); // NB: We do not use file_stem() as some file names have multiple `.`s and we // must strip all of them. let testname = file_path.file_name().unwrap().to_str().unwrap().split_once('.').unwrap().0; if ext == "stderr" || ext == "stdout" || ext == "fixed" { - check_stray_output_snapshot(bad, file_path, testname); - check_empty_output_snapshot(bad, file_path); + check_stray_output_snapshot(check, file_path, testname); + check_empty_output_snapshot(check, file_path); } deny_new_nondescriptive_test_names( - bad, + check, path, &mut remaining_issue_names, file_path, @@ -137,7 +137,7 @@ fn recursively_check_ui_tests<'issues>( remaining_issue_names } -fn check_unexpected_extension(bad: &mut bool, file_path: &Path, ext: &str) { +fn check_unexpected_extension(check: &mut RunningCheck, file_path: &Path, ext: &str) { const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[ "rs", // test source files "stderr", // expected stderr file, corresponds to a rs file @@ -178,11 +178,11 @@ fn check_unexpected_extension(bad: &mut bool, file_path: &Path, ext: &str) { if !(EXPECTED_TEST_FILE_EXTENSIONS.contains(&ext) || EXTENSION_EXCEPTION_PATHS.iter().any(|path| file_path.ends_with(path))) { - tidy_error!(bad, "file {} has unexpected extension {}", file_path.display(), ext); + check.error(format!("file {} has unexpected extension {}", file_path.display(), ext)); } } -fn check_stray_output_snapshot(bad: &mut bool, file_path: &Path, testname: &str) { +fn check_stray_output_snapshot(check: &mut RunningCheck, file_path: &Path, testname: &str) { // Test output filenames have one of the formats: // ``` // $testname.stderr @@ -197,20 +197,20 @@ fn check_stray_output_snapshot(bad: &mut bool, file_path: &Path, testname: &str) if !file_path.with_file_name(testname).with_extension("rs").exists() && !testname.contains("ignore-tidy") { - tidy_error!(bad, "Stray file with UI testing output: {:?}", file_path); + check.error(format!("Stray file with UI testing output: {:?}", file_path)); } } -fn check_empty_output_snapshot(bad: &mut bool, file_path: &Path) { +fn check_empty_output_snapshot(check: &mut RunningCheck, file_path: &Path) { if let Ok(metadata) = fs::metadata(file_path) && metadata.len() == 0 { - tidy_error!(bad, "Empty file with UI testing output: {:?}", file_path); + check.error(format!("Empty file with UI testing output: {:?}", file_path)); } } fn deny_new_nondescriptive_test_names( - bad: &mut bool, + check: &mut RunningCheck, path: &Path, remaining_issue_names: &mut BTreeSet<&str>, file_path: &Path, @@ -231,11 +231,10 @@ fn deny_new_nondescriptive_test_names( if !remaining_issue_names.remove(stripped_path.as_str()) && !stripped_path.starts_with("ui/issues/") { - tidy_error!( - bad, + check.error(format!( "file `tests/{stripped_path}` must begin with a descriptive name, consider `{{reason}}-issue-{issue_n}.rs`", issue_n = &test_name[1], - ); + )); } } } diff --git a/src/tools/tidy/src/unit_tests.rs b/src/tools/tidy/src/unit_tests.rs index 7396310ed37c..cab445ac63a1 100644 --- a/src/tools/tidy/src/unit_tests.rs +++ b/src/tools/tidy/src/unit_tests.rs @@ -11,9 +11,12 @@ use std::path::Path; +use crate::diagnostics::{CheckId, DiagCtx}; use crate::walk::{filter_dirs, walk}; -pub fn check(root_path: &Path, stdlib: bool, bad: &mut bool) { +pub fn check(root_path: &Path, stdlib: bool, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("unit_tests").path(root_path)); + let skip = move |path: &Path, is_dir| { let file_name = path.file_name().unwrap_or_default(); @@ -92,14 +95,11 @@ pub fn check(root_path: &Path, stdlib: bool, bad: &mut bool) { .to_owned() }; let name = if is_test() { "test" } else { "bench" }; - tidy_error!( - bad, - "`{}:{}` contains `#[{}]`; {}", + check.error(format!( + "`{}:{}` contains `#[{name}]`; {explanation}", path.display(), i + 1, - name, - explanation, - ); + )); return; } } diff --git a/src/tools/tidy/src/unknown_revision.rs b/src/tools/tidy/src/unknown_revision.rs index 0ba05c80a791..776d45e25de0 100644 --- a/src/tools/tidy/src/unknown_revision.rs +++ b/src/tools/tidy/src/unknown_revision.rs @@ -12,12 +12,14 @@ use std::sync::OnceLock; use ignore::DirEntry; use regex::Regex; +use crate::diagnostics::{CheckId, DiagCtx, RunningCheck}; use crate::iter_header::{HeaderLine, iter_header}; use crate::walk::{filter_dirs, filter_not_rust, walk}; -pub fn check(tests_path: impl AsRef, bad: &mut bool) { +pub fn check(tests_path: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("unknown_revision").path(tests_path)); walk( - tests_path.as_ref(), + tests_path, |path, is_dir| { filter_dirs(path) || filter_not_rust(path) || { // Auxiliary source files for incremental tests can refer to revisions @@ -25,11 +27,11 @@ pub fn check(tests_path: impl AsRef, bad: &mut bool) { is_dir && path.file_name().is_some_and(|name| name == "auxiliary") } }, - &mut |entry, contents| visit_test_file(entry, contents, bad), + &mut |entry, contents| visit_test_file(entry, contents, &mut check), ); } -fn visit_test_file(entry: &DirEntry, contents: &str, bad: &mut bool) { +fn visit_test_file(entry: &DirEntry, contents: &str, check: &mut RunningCheck) { let mut revisions = HashSet::new(); let mut unused_revision_names = HashSet::new(); @@ -68,10 +70,9 @@ fn visit_test_file(entry: &DirEntry, contents: &str, bad: &mut bool) { // Fail if any revision names appear in both places, since that's probably a mistake. for rev in revisions.intersection(&unused_revision_names).copied().collect::>() { - tidy_error!( - bad, + check.error(format!( "revision name [{rev}] appears in both `revisions` and `unused-revision-names` in {path}" - ); + )); } // Compute the set of revisions that were mentioned but not declared, @@ -84,7 +85,7 @@ fn visit_test_file(entry: &DirEntry, contents: &str, bad: &mut bool) { bad_revisions.sort(); for (line_number, rev) in bad_revisions { - tidy_error!(bad, "unknown revision [{rev}] at {path}:{line_number}"); + check.error(format!("unknown revision [{rev}] at {path}:{line_number}")); } } diff --git a/src/tools/tidy/src/unstable_book.rs b/src/tools/tidy/src/unstable_book.rs index 0ed954d48deb..bab294abee02 100644 --- a/src/tools/tidy/src/unstable_book.rs +++ b/src/tools/tidy/src/unstable_book.rs @@ -2,6 +2,7 @@ use std::collections::BTreeSet; use std::fs; use std::path::{Path, PathBuf}; +use crate::diagnostics::{DiagCtx, RunningCheck}; use crate::features::{CollectedFeatures, Features, Status}; pub const PATH_STR: &str = "doc/unstable-book"; @@ -75,19 +76,18 @@ fn collect_unstable_book_lib_features_section_file_names(base_src_path: &Path) - } /// Would switching underscores for dashes work? -fn maybe_suggest_dashes(names: &BTreeSet, feature_name: &str, bad: &mut bool) { +fn maybe_suggest_dashes(names: &BTreeSet, feature_name: &str, check: &mut RunningCheck) { let with_dashes = feature_name.replace('_', "-"); if names.contains(&with_dashes) { - tidy_error!( - bad, - "the file `{}.md` contains underscores; use dashes instead: `{}.md`", - feature_name, - with_dashes, - ); + check.error(format!( + "the file `{feature_name}.md` contains underscores; use dashes instead: `{with_dashes}.md`", + )); } } -pub fn check(path: &Path, features: CollectedFeatures, bad: &mut bool) { +pub fn check(path: &Path, features: CollectedFeatures, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check("unstable_book"); + let lang_features = features.lang; let lib_features = features .lib @@ -108,26 +108,22 @@ pub fn check(path: &Path, features: CollectedFeatures, bad: &mut bool) { // Check for Unstable Book sections that don't have a corresponding unstable feature for feature_name in &unstable_book_lib_features_section_file_names - &unstable_lib_feature_names { - tidy_error!( - bad, - "The Unstable Book has a 'library feature' section '{}' which doesn't \ - correspond to an unstable library feature", - feature_name - ); - maybe_suggest_dashes(&unstable_lib_feature_names, &feature_name, bad); + check.error(format!( + "The Unstable Book has a 'library feature' section '{feature_name}' which doesn't \ + correspond to an unstable library feature" + )); + maybe_suggest_dashes(&unstable_lib_feature_names, &feature_name, &mut check); } // Check for Unstable Book sections that don't have a corresponding unstable feature. for feature_name in &unstable_book_lang_features_section_file_names - &unstable_lang_feature_names { - tidy_error!( - bad, - "The Unstable Book has a 'language feature' section '{}' which doesn't \ - correspond to an unstable language feature", - feature_name - ); - maybe_suggest_dashes(&unstable_lang_feature_names, &feature_name, bad); + check.error(format!( + "The Unstable Book has a 'language feature' section '{feature_name}' which doesn't \ + correspond to an unstable language feature" + )); + maybe_suggest_dashes(&unstable_lang_feature_names, &feature_name, &mut check); } // List unstable features that don't have Unstable Book sections. diff --git a/src/tools/tidy/src/x_version.rs b/src/tools/tidy/src/x_version.rs index 9f7f43c4000b..b3e322d9403f 100644 --- a/src/tools/tidy/src/x_version.rs +++ b/src/tools/tidy/src/x_version.rs @@ -3,12 +3,18 @@ use std::process::{Command, Stdio}; use semver::Version; -pub fn check(root: &Path, cargo: &Path, bad: &mut bool) { +use crate::diagnostics::{CheckId, DiagCtx}; + +pub fn check(root: &Path, cargo: &Path, diag_ctx: DiagCtx) { + let mut check = diag_ctx.start_check(CheckId::new("x_version").path(root)); let cargo_list = Command::new(cargo).args(["install", "--list"]).stdout(Stdio::piped()).spawn(); let child = match cargo_list { Ok(child) => child, - Err(e) => return tidy_error!(bad, "failed to run `cargo`: {}", e), + Err(e) => { + check.error(format!("failed to run `cargo`: {e}")); + return; + } }; let cargo_list = child.wait_with_output().unwrap(); @@ -47,13 +53,10 @@ pub fn check(root: &Path, cargo: &Path, bad: &mut bool) { ) } } else { - tidy_error!( - bad, - "Unable to parse the latest version of `x` at `src/tools/x/Cargo.toml`" - ) + check.error("Unable to parse the latest version of `x` at `src/tools/x/Cargo.toml`") } } else { - tidy_error!(bad, "failed to check version of `x`: {}", cargo_list.status) + check.error(format!("failed to check version of `x`: {}", cargo_list.status)) } } From 4c208f5c64c3a86e52f5ebff2792ed1e3d8ca5b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 15 Sep 2025 15:24:56 +0200 Subject: [PATCH 678/682] Implement output of colored messages with optional check context --- src/tools/features-status-dump/src/main.rs | 3 +- src/tools/tidy/src/alphabetical/tests.rs | 27 +++-- src/tools/tidy/src/deps.rs | 4 +- src/tools/tidy/src/diagnostics.rs | 132 ++++++++++++++++----- src/tools/tidy/src/extdeps.rs | 4 +- src/tools/tidy/src/filenames.rs | 4 +- src/tools/tidy/src/main.rs | 22 +++- src/tools/tidy/src/tests_placement.rs | 4 +- src/tools/tidy/src/triagebot.rs | 4 +- src/tools/unstable-book-gen/src/main.rs | 3 +- 10 files changed, 152 insertions(+), 55 deletions(-) diff --git a/src/tools/features-status-dump/src/main.rs b/src/tools/features-status-dump/src/main.rs index 1ce98d1506d1..a4f88362ab81 100644 --- a/src/tools/features-status-dump/src/main.rs +++ b/src/tools/features-status-dump/src/main.rs @@ -5,6 +5,7 @@ use std::path::PathBuf; use anyhow::{Context, Result}; use clap::Parser; +use tidy::diagnostics::RunningCheck; use tidy::features::{Feature, collect_lang_features, collect_lib_features}; #[derive(Debug, Parser)] @@ -29,7 +30,7 @@ struct FeaturesStatus { fn main() -> Result<()> { let Cli { compiler_path, library_path, output_path } = Cli::parse(); - let lang_features_status = collect_lang_features(&compiler_path, &mut false); + let lang_features_status = collect_lang_features(&compiler_path, &mut RunningCheck::new_noop()); let lib_features_status = collect_lib_features(&library_path) .into_iter() .filter(|&(ref name, _)| !lang_features_status.contains_key(name)) diff --git a/src/tools/tidy/src/alphabetical/tests.rs b/src/tools/tidy/src/alphabetical/tests.rs index 4d05bc33cedc..b181ab8f744f 100644 --- a/src/tools/tidy/src/alphabetical/tests.rs +++ b/src/tools/tidy/src/alphabetical/tests.rs @@ -1,19 +1,22 @@ -use std::io::Write; -use std::str::from_utf8; +use std::path::Path; -use super::*; +use crate::alphabetical::check_lines; +use crate::diagnostics::DiagCtx; #[track_caller] fn test(lines: &str, name: &str, expected_msg: &str, expected_bad: bool) { - let mut actual_msg = Vec::new(); - let mut actual_bad = false; - let mut err = |args: &_| { - write!(&mut actual_msg, "{args}")?; - Ok(()) - }; - check_lines(&name, lines.lines().enumerate(), &mut err, &mut actual_bad); - assert_eq!(expected_msg, from_utf8(&actual_msg).unwrap()); - assert_eq!(expected_bad, actual_bad); + let diag_ctx = DiagCtx::new(Path::new("/"), false); + let mut check = diag_ctx.start_check("alphabetical-test"); + check_lines(&name, lines.lines().enumerate(), &mut check); + + assert_eq!(expected_bad, check.is_bad()); + let errors = check.get_errors(); + if expected_bad { + assert_eq!(errors.len(), 1); + assert_eq!(expected_msg, errors[0]); + } else { + assert!(errors.is_empty()); + } } #[track_caller] diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index a9d07c753157..e275d3042cbb 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -9,7 +9,7 @@ use build_helper::ci::CiEnv; use cargo_metadata::semver::Version; use cargo_metadata::{Metadata, Package, PackageId}; -use crate::diagnostics::{CheckId, DiagCtx, RunningCheck}; +use crate::diagnostics::{DiagCtx, RunningCheck}; #[path = "../../../bootstrap/src/utils/proc_macro_deps.rs"] mod proc_macro_deps; @@ -664,7 +664,7 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[ /// `root` is path to the directory with the root `Cargo.toml` (for the workspace). `cargo` is path /// to the cargo executable. pub fn check(root: &Path, cargo: &Path, bless: bool, diag_ctx: DiagCtx) { - let mut check = diag_ctx.start_check(CheckId::new("deps").path(root)); + let mut check = diag_ctx.start_check("deps"); let mut checked_runtime_licenses = false; diff --git a/src/tools/tidy/src/diagnostics.rs b/src/tools/tidy/src/diagnostics.rs index d7889f925ea9..6e95f97d0104 100644 --- a/src/tools/tidy/src/diagnostics.rs +++ b/src/tools/tidy/src/diagnostics.rs @@ -1,9 +1,9 @@ use std::collections::HashSet; -use std::fmt::Display; +use std::fmt::{Display, Formatter}; use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; -use termcolor::WriteColor; +use termcolor::{Color, WriteColor}; /// Collects diagnostics from all tidy steps, and contains shared information /// that determines how should message and logs be presented. @@ -14,26 +14,40 @@ use termcolor::WriteColor; pub struct DiagCtx(Arc>); impl DiagCtx { - pub fn new(verbose: bool) -> Self { + pub fn new(root_path: &Path, verbose: bool) -> Self { Self(Arc::new(Mutex::new(DiagCtxInner { running_checks: Default::default(), finished_checks: Default::default(), + root_path: root_path.to_path_buf(), verbose, }))) } pub fn start_check>(&self, id: Id) -> RunningCheck { - let id = id.into(); + let mut id = id.into(); let mut ctx = self.0.lock().unwrap(); + + // Shorten path for shorter diagnostics + id.path = match id.path { + Some(path) => Some(path.strip_prefix(&ctx.root_path).unwrap_or(&path).to_path_buf()), + None => None, + }; + ctx.start_check(id.clone()); - RunningCheck { id, bad: false, ctx: self.0.clone() } + RunningCheck { + id, + bad: false, + ctx: self.0.clone(), + #[cfg(test)] + errors: vec![], + } } - pub fn into_conclusion(self) -> bool { - let ctx = self.0.lock().unwrap(); + pub fn into_failed_checks(self) -> Vec { + let ctx = Arc::into_inner(self.0).unwrap().into_inner().unwrap(); assert!(ctx.running_checks.is_empty(), "Some checks are still running"); - ctx.finished_checks.iter().any(|c| c.bad) + ctx.finished_checks.into_iter().filter(|c| c.bad).collect() } } @@ -41,6 +55,7 @@ struct DiagCtxInner { running_checks: HashSet, finished_checks: HashSet, verbose: bool, + root_path: PathBuf, } impl DiagCtxInner { @@ -48,6 +63,7 @@ impl DiagCtxInner { if self.has_check_id(&id) { panic!("Starting a check named `{id:?}` for the second time"); } + self.running_checks.insert(id); } @@ -57,6 +73,13 @@ impl DiagCtxInner { "Finishing check `{:?}` that was not started", check.id ); + + if check.bad { + output_message("FAIL", Some(&check.id), Some(COLOR_ERROR)); + } else if self.verbose { + output_message("OK", Some(&check.id), Some(COLOR_SUCCESS)); + } + self.finished_checks.insert(check); } @@ -71,8 +94,8 @@ impl DiagCtxInner { /// Identifies a single step #[derive(PartialEq, Eq, Hash, Clone, Debug)] pub struct CheckId { - name: String, - path: Option, + pub name: String, + pub path: Option, } impl CheckId { @@ -91,40 +114,70 @@ impl From<&'static str> for CheckId { } } +impl Display for CheckId { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.name)?; + if let Some(path) = &self.path { + write!(f, " ({})", path.display())?; + } + Ok(()) + } +} + #[derive(PartialEq, Eq, Hash, Debug)] -struct FinishedCheck { +pub struct FinishedCheck { id: CheckId, bad: bool, } +impl FinishedCheck { + pub fn id(&self) -> &CheckId { + &self.id + } +} + /// Represents a single tidy check, identified by its `name`, running. pub struct RunningCheck { id: CheckId, bad: bool, ctx: Arc>, + #[cfg(test)] + errors: Vec, } impl RunningCheck { + /// Creates a new instance of a running check without going through the diag + /// context. + /// Useful if you want to run some functions from tidy without configuring + /// diagnostics. + pub fn new_noop() -> Self { + let ctx = DiagCtx::new(Path::new(""), false); + ctx.start_check("noop") + } + /// Immediately output an error and mark the check as failed. - pub fn error(&mut self, t: T) { + pub fn error(&mut self, msg: T) { self.mark_as_bad(); - tidy_error(&t.to_string()).expect("failed to output error"); + let msg = msg.to_string(); + output_message(&msg, Some(&self.id), Some(COLOR_ERROR)); + #[cfg(test)] + self.errors.push(msg); } /// Immediately output a warning. - pub fn warning(&mut self, t: T) { - eprintln!("WARNING: {t}"); + pub fn warning(&mut self, msg: T) { + output_message(&msg.to_string(), Some(&self.id), Some(COLOR_WARNING)); } /// Output an informational message - pub fn message(&mut self, t: T) { - eprintln!("{t}"); + pub fn message(&mut self, msg: T) { + output_message(&msg.to_string(), Some(&self.id), None); } /// Output a message only if verbose output is enabled. - pub fn verbose_msg(&mut self, t: T) { + pub fn verbose_msg(&mut self, msg: T) { if self.is_verbose_enabled() { - self.message(t); + self.message(msg); } } @@ -138,6 +191,11 @@ impl RunningCheck { self.ctx.lock().unwrap().verbose } + #[cfg(test)] + pub fn get_errors(&self) -> Vec { + self.errors.clone() + } + fn mark_as_bad(&mut self) { self.bad = true; } @@ -149,17 +207,37 @@ impl Drop for RunningCheck { } } -fn tidy_error(args: &str) -> std::io::Result<()> { +pub const COLOR_SUCCESS: Color = Color::Green; +pub const COLOR_ERROR: Color = Color::Red; +pub const COLOR_WARNING: Color = Color::Yellow; + +/// Output a message to stderr. +/// The message can be optionally scoped to a certain check, and it can also have a certain color. +pub fn output_message(msg: &str, id: Option<&CheckId>, color: Option) { use std::io::Write; - use termcolor::{Color, ColorChoice, ColorSpec, StandardStream}; + use termcolor::{ColorChoice, ColorSpec, StandardStream}; - let mut stderr = StandardStream::stdout(ColorChoice::Auto); - stderr.set_color(ColorSpec::new().set_fg(Some(Color::Red)))?; + let mut stderr = StandardStream::stderr(ColorChoice::Auto); + if let Some(color) = &color { + stderr.set_color(ColorSpec::new().set_fg(Some(*color))).unwrap(); + } - write!(&mut stderr, "tidy error")?; - stderr.set_color(&ColorSpec::new())?; + match id { + Some(id) => { + write!(&mut stderr, "tidy [{}", id.name).unwrap(); + if let Some(path) = &id.path { + write!(&mut stderr, " ({})", path.display()).unwrap(); + } + write!(&mut stderr, "]").unwrap(); + } + None => { + write!(&mut stderr, "tidy").unwrap(); + } + } + if color.is_some() { + stderr.set_color(&ColorSpec::new()).unwrap(); + } - writeln!(&mut stderr, ": {args}")?; - Ok(()) + writeln!(&mut stderr, ": {msg}").unwrap(); } diff --git a/src/tools/tidy/src/extdeps.rs b/src/tools/tidy/src/extdeps.rs index 21612980007d..f75de13b45ce 100644 --- a/src/tools/tidy/src/extdeps.rs +++ b/src/tools/tidy/src/extdeps.rs @@ -4,7 +4,7 @@ use std::fs; use std::path::Path; use crate::deps::WorkspaceInfo; -use crate::diagnostics::{CheckId, DiagCtx}; +use crate::diagnostics::DiagCtx; /// List of allowed sources for packages. const ALLOWED_SOURCES: &[&str] = &[ @@ -16,7 +16,7 @@ const ALLOWED_SOURCES: &[&str] = &[ /// Checks for external package sources. `root` is the path to the directory that contains the /// workspace `Cargo.toml`. pub fn check(root: &Path, diag_ctx: DiagCtx) { - let mut check = diag_ctx.start_check(CheckId::new("extdeps").path(root)); + let mut check = diag_ctx.start_check("extdeps"); for &WorkspaceInfo { path, submodules, .. } in crate::deps::WORKSPACES { if crate::deps::has_missing_submodule(root, submodules) { diff --git a/src/tools/tidy/src/filenames.rs b/src/tools/tidy/src/filenames.rs index d52a82297388..835cbefbf691 100644 --- a/src/tools/tidy/src/filenames.rs +++ b/src/tools/tidy/src/filenames.rs @@ -10,10 +10,10 @@ use std::path::Path; use std::process::Command; -use crate::diagnostics::{CheckId, DiagCtx}; +use crate::diagnostics::DiagCtx; pub fn check(root_path: &Path, diag_ctx: DiagCtx) { - let mut check = diag_ctx.start_check(CheckId::new("filenames").path(root_path)); + let mut check = diag_ctx.start_check("filenames"); let stat_output = Command::new("git") .arg("-C") .arg(root_path) diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index 73ff183868a2..93bc16111992 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -11,7 +11,7 @@ use std::str::FromStr; use std::thread::{self, ScopedJoinHandle, scope}; use std::{env, process}; -use tidy::diagnostics::DiagCtx; +use tidy::diagnostics::{COLOR_ERROR, COLOR_SUCCESS, DiagCtx, output_message}; use tidy::*; fn main() { @@ -50,7 +50,7 @@ fn main() { let extra_checks = cfg_args.iter().find(|s| s.starts_with("--extra-checks=")).map(String::as_str); - let diag_ctx = DiagCtx::new(verbose); + let diag_ctx = DiagCtx::new(&root_path, verbose); let ci_info = CiInfo::new(diag_ctx.clone()); let drain_handles = |handles: &mut VecDeque>| { @@ -175,8 +175,22 @@ fn main() { ); }); - if diag_ctx.into_conclusion() { - eprintln!("some tidy checks failed"); + let failed_checks = diag_ctx.into_failed_checks(); + if !failed_checks.is_empty() { + let mut failed: Vec = + failed_checks.into_iter().map(|c| c.id().to_string()).collect(); + failed.sort(); + output_message( + &format!( + "The following check{} failed: {}", + if failed.len() > 1 { "s" } else { "" }, + failed.join(", ") + ), + None, + Some(COLOR_ERROR), + ); process::exit(1); + } else { + output_message("All tidy checks succeeded", None, Some(COLOR_SUCCESS)); } } diff --git a/src/tools/tidy/src/tests_placement.rs b/src/tools/tidy/src/tests_placement.rs index e978e1c453c1..8ba8cf552bd7 100644 --- a/src/tools/tidy/src/tests_placement.rs +++ b/src/tools/tidy/src/tests_placement.rs @@ -1,12 +1,12 @@ use std::path::Path; -use crate::diagnostics::{CheckId, DiagCtx}; +use crate::diagnostics::DiagCtx; const FORBIDDEN_PATH: &str = "src/test"; const ALLOWED_PATH: &str = "tests"; pub fn check(root_path: &Path, diag_ctx: DiagCtx) { - let mut check = diag_ctx.start_check(CheckId::new("tests-placement").path(root_path)); + let mut check = diag_ctx.start_check("tests_placement"); if root_path.join(FORBIDDEN_PATH).exists() { check.error(format!( diff --git a/src/tools/tidy/src/triagebot.rs b/src/tools/tidy/src/triagebot.rs index 9eccef29f2e2..41d61dcd1411 100644 --- a/src/tools/tidy/src/triagebot.rs +++ b/src/tools/tidy/src/triagebot.rs @@ -4,10 +4,10 @@ use std::path::Path; use toml::Value; -use crate::diagnostics::{CheckId, DiagCtx}; +use crate::diagnostics::DiagCtx; pub fn check(path: &Path, diag_ctx: DiagCtx) { - let mut check = diag_ctx.start_check(CheckId::new("triagebot").path(path)); + let mut check = diag_ctx.start_check("triagebot"); let triagebot_path = path.join("triagebot.toml"); // This check is mostly to catch broken path filters *within* `triagebot.toml`, and not enforce diff --git a/src/tools/unstable-book-gen/src/main.rs b/src/tools/unstable-book-gen/src/main.rs index a7c6173d88c4..16550f83003d 100644 --- a/src/tools/unstable-book-gen/src/main.rs +++ b/src/tools/unstable-book-gen/src/main.rs @@ -5,6 +5,7 @@ use std::env; use std::fs::{self, write}; use std::path::Path; +use tidy::diagnostics::RunningCheck; use tidy::features::{Features, collect_env_vars, collect_lang_features, collect_lib_features}; use tidy::t; use tidy::unstable_book::{ @@ -122,7 +123,7 @@ fn main() { let src_path = Path::new(&src_path_str); let dest_path = Path::new(&dest_path_str); - let lang_features = collect_lang_features(compiler_path, &mut false); + let lang_features = collect_lang_features(compiler_path, &mut RunningCheck::new_noop()); let lib_features = collect_lib_features(library_path) .into_iter() .filter(|&(ref name, _)| !lang_features.contains_key(name)) From 6abcadc235c24b0541e7bb3b6f6444d397d32f35 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Sun, 21 Sep 2025 10:20:56 -0400 Subject: [PATCH 679/682] Port #[macro_export] to the new attribute parsing infrastructure Co-authored-by: Anne Stijns --- .../src/attributes/macro_attrs.rs | 63 +++++++++++++++++++ compiler/rustc_attr_parsing/src/context.rs | 3 +- compiler/rustc_attr_parsing/src/lints.rs | 12 ++++ compiler/rustc_expand/src/base.rs | 6 +- .../rustc_hir/src/attrs/data_structures.rs | 3 + .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_hir/src/lints.rs | 35 +++++++++-- compiler/rustc_lint/src/non_local_def.rs | 10 ++- compiler/rustc_lint_defs/src/builtin.rs | 7 ++- compiler/rustc_passes/messages.ftl | 7 --- compiler/rustc_passes/src/check_attr.rs | 60 ++++++------------ compiler/rustc_passes/src/errors.rs | 9 --- src/librustdoc/html/render/search_index.rs | 23 ++++--- src/librustdoc/json/conversions.rs | 9 +-- src/librustdoc/passes/strip_hidden.rs | 5 +- src/librustdoc/visit_ast.rs | 10 +-- .../src/macro_metavars_in_unsafe.rs | 7 ++- 17 files changed, 177 insertions(+), 93 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs index 180130c7be4f..849141c34f7d 100644 --- a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs @@ -1,3 +1,4 @@ +use rustc_ast::AttrStyle; use rustc_errors::DiagArgValue; use rustc_hir::attrs::MacroUseArgs; @@ -133,3 +134,65 @@ impl NoArgsAttributeParser for AllowInternalUnsafeParser { ]); const CREATE: fn(Span) -> AttributeKind = |span| AttributeKind::AllowInternalUnsafe(span); } + +pub(crate) struct MacroExportParser; + +impl SingleAttributeParser for MacroExportParser { + const PATH: &[Symbol] = &[sym::macro_export]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; + const TEMPLATE: AttributeTemplate = template!(Word, List: &["local_inner_macros"]); + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[ + Allow(Target::MacroDef), + Error(Target::WherePredicate), + Error(Target::Crate), + ]); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + let suggestions = || { + >::TEMPLATE + .suggestions(AttrStyle::Inner, "macro_export") + }; + let local_inner_macros = match args { + ArgParser::NoArgs => false, + ArgParser::List(list) => { + let Some(l) = list.single() else { + let span = cx.attr_span; + cx.emit_lint( + AttributeLintKind::InvalidMacroExportArguments { + suggestions: suggestions(), + }, + span, + ); + return None; + }; + match l.meta_item().and_then(|i| i.path().word_sym()) { + Some(sym::local_inner_macros) => true, + _ => { + let span = cx.attr_span; + cx.emit_lint( + AttributeLintKind::InvalidMacroExportArguments { + suggestions: suggestions(), + }, + span, + ); + return None; + } + } + } + ArgParser::NameValue(_) => { + let span = cx.attr_span; + let suggestions = suggestions(); + cx.emit_err(IllFormedAttributeInputLint { + num_suggestions: suggestions.len(), + suggestions: DiagArgValue::StrListSepByAnd( + suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), + ), + span, + }); + return None; + } + }; + Some(AttributeKind::MacroExport { span: cx.attr_span, local_inner_macros }) + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index ee5b7322b024..4c32bb87a242 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -40,7 +40,7 @@ use crate::attributes::lint_helpers::{ }; use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser}; use crate::attributes::macro_attrs::{ - AllowInternalUnsafeParser, MacroEscapeParser, MacroUseParser, + AllowInternalUnsafeParser, MacroEscapeParser, MacroExportParser, MacroUseParser, }; use crate::attributes::must_use::MustUseParser; use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser; @@ -183,6 +183,7 @@ attribute_parsers!( Single, Single, Single, + Single, Single, Single, Single, diff --git a/compiler/rustc_attr_parsing/src/lints.rs b/compiler/rustc_attr_parsing/src/lints.rs index b1a971eec324..ab8ba0daf1f1 100644 --- a/compiler/rustc_attr_parsing/src/lints.rs +++ b/compiler/rustc_attr_parsing/src/lints.rs @@ -31,6 +31,18 @@ pub fn emit_attribute_lint(lint: &AttributeLint, lint_emi }, ); } + AttributeLintKind::InvalidMacroExportArguments { suggestions } => lint_emitter + .emit_node_span_lint( + rustc_session::lint::builtin::INVALID_MACRO_EXPORT_ARGUMENTS, + *id, + *span, + session_diagnostics::IllFormedAttributeInput { + num_suggestions: suggestions.len(), + suggestions: DiagArgValue::StrListSepByAnd( + suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), + ), + }, + ), AttributeLintKind::EmptyAttribute { first_span } => lint_emitter.emit_node_span_lint( rustc_session::lint::builtin::UNUSED_ATTRIBUTES, *id, diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 88f88f30a8c6..3956125bace0 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -942,9 +942,9 @@ impl SyntaxExtension { .unwrap_or_default(); let allow_internal_unsafe = find_attr!(attrs, AttributeKind::AllowInternalUnsafe(_)); - let local_inner_macros = ast::attr::find_by_name(attrs, sym::macro_export) - .and_then(|macro_export| macro_export.meta_item_list()) - .is_some_and(|l| ast::attr::list_contains_name(&l, sym::local_inner_macros)); + let local_inner_macros = + *find_attr!(attrs, AttributeKind::MacroExport {local_inner_macros: l, ..} => l) + .unwrap_or(&false); let collapse_debuginfo = Self::get_collapse_debuginfo(sess, attrs, !is_local); tracing::debug!(?name, ?local_inner_macros, ?collapse_debuginfo, ?allow_internal_unsafe); diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 0784675b177a..8ab43ff2582c 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -551,6 +551,9 @@ pub enum AttributeKind { /// Represents `#[macro_escape]`. MacroEscape(Span), + /// Represents [`#[macro_export]`](https://doc.rust-lang.org/reference/macros-by-example.html#r-macro.decl.scope.path). + MacroExport { span: Span, local_inner_macros: bool }, + /// Represents `#[rustc_macro_transparency]`. MacroTransparency(Transparency), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 563e7a58c6d5..8e4434050747 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -56,6 +56,7 @@ impl AttributeKind { Linkage(..) => No, LoopMatch(..) => No, MacroEscape(..) => No, + MacroExport { .. } => Yes, MacroTransparency(..) => Yes, MacroUse { .. } => No, Marker(..) => No, diff --git a/compiler/rustc_hir/src/lints.rs b/compiler/rustc_hir/src/lints.rs index 0b24052b453e..b7a0a6a0c197 100644 --- a/compiler/rustc_hir/src/lints.rs +++ b/compiler/rustc_hir/src/lints.rs @@ -31,9 +31,34 @@ pub struct AttributeLint { #[derive(Clone, Debug, HashStable_Generic)] pub enum AttributeLintKind { - UnusedDuplicate { this: Span, other: Span, warning: bool }, - IllFormedAttributeInput { suggestions: Vec }, - EmptyAttribute { first_span: Span }, - InvalidTarget { name: AttrPath, target: Target, applied: Vec, only: &'static str }, - InvalidStyle { name: AttrPath, is_used_as_inner: bool, target: Target, target_span: Span }, + UnusedDuplicate { + this: Span, + other: Span, + warning: bool, + }, + IllFormedAttributeInput { + suggestions: Vec, + }, + EmptyAttribute { + first_span: Span, + }, + + /// Copy of `IllFormedAttributeInput` + /// specifically for the `invalid_macro_export_arguments` lint until that is removed, + /// see + InvalidMacroExportArguments { + suggestions: Vec, + }, + InvalidTarget { + name: AttrPath, + target: Target, + applied: Vec, + only: &'static str, + }, + InvalidStyle { + name: AttrPath, + is_used_as_inner: bool, + target: Target, + target_span: Span, + }, } diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index dca22b986ffa..b10be22dc389 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -1,11 +1,12 @@ use rustc_errors::MultiSpan; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{self, Visitor, VisitorExt}; -use rustc_hir::{Body, HirId, Item, ItemKind, Node, Path, TyKind}; +use rustc_hir::{Body, HirId, Item, ItemKind, Node, Path, TyKind, find_attr}; use rustc_middle::ty::TyCtxt; use rustc_session::{declare_lint, impl_lint_pass}; use rustc_span::def_id::{DefId, LOCAL_CRATE}; -use rustc_span::{ExpnKind, Span, kw, sym}; +use rustc_span::{ExpnKind, Span, kw}; use crate::lints::{NonLocalDefinitionsCargoUpdateNote, NonLocalDefinitionsDiag}; use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent}; @@ -241,7 +242,10 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { ) } ItemKind::Macro(_, _macro, _kinds) - if cx.tcx.has_attr(item.owner_id.def_id, sym::macro_export) => + if find_attr!( + cx.tcx.get_all_attrs(item.owner_id.def_id), + AttributeKind::MacroExport { .. } + ) => { cx.emit_span_lint( NON_LOCAL_DEFINITIONS, diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index b79075ac09b9..3d0974d5d280 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -4191,8 +4191,13 @@ declare_lint! { /// You can't have multiple arguments in a `#[macro_export(..)]`, or mention arguments other than `local_inner_macros`. /// pub INVALID_MACRO_EXPORT_ARGUMENTS, - Warn, + Deny, "\"invalid_parameter\" isn't a valid argument for `#[macro_export]`", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::FutureReleaseError, + reference: "issue #57571 ", + report_in_deps: true, + }; } declare_lint! { diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 75537caa8946..6cd68380e46f 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -349,10 +349,6 @@ passes_invalid_attr_at_crate_level = passes_invalid_attr_at_crate_level_item = the inner attribute doesn't annotate this {$kind} -passes_invalid_macro_export_arguments = invalid `#[macro_export]` argument - -passes_invalid_macro_export_arguments_too_many_items = `#[macro_export]` can only take 1 or 0 arguments - passes_lang_item_fn = {$name -> [panic_impl] `#[panic_handler]` *[other] `{$name}` lang item @@ -392,9 +388,6 @@ passes_loop_match_attr = `#[loop_match]` should be applied to a loop .label = not a loop -passes_macro_export = - `#[macro_export]` only has an effect on macro definitions - passes_macro_export_on_decl_macro = `#[macro_export]` has no effect on declarative macro definitions .note = declarative macros follow the same exporting rules as regular items diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 4d5a8447695b..94c9e71ce770 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -38,8 +38,8 @@ use rustc_middle::{bug, span_bug}; use rustc_session::config::CrateType; use rustc_session::lint; use rustc_session::lint::builtin::{ - CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, INVALID_MACRO_EXPORT_ARGUMENTS, - MALFORMED_DIAGNOSTIC_ATTRIBUTES, MISPLACED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES, + CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, MALFORMED_DIAGNOSTIC_ATTRIBUTES, + MISPLACED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES, }; use rustc_session::parse::feature_err; use rustc_span::edition::Edition; @@ -217,7 +217,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> { }, Attribute::Parsed(AttributeKind::Link(_, attr_span)) => { self.check_link(hir_id, *attr_span, span, target) - } + }, + Attribute::Parsed(AttributeKind::MacroExport { span, .. }) => { + self.check_macro_export(hir_id, *span, target) + }, Attribute::Parsed( AttributeKind::BodyStability { .. } | AttributeKind::ConstStabilityIndirect @@ -331,7 +334,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::rustc_has_incoherent_inherent_impls, ..] => { self.check_has_incoherent_inherent_impls(attr, span, target) } - [sym::macro_export, ..] => self.check_macro_export(hir_id, attr, target), [sym::autodiff_forward, ..] | [sym::autodiff_reverse, ..] => { self.check_autodiff(hir_id, attr, span, target) } @@ -1850,45 +1852,22 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - fn check_macro_export(&self, hir_id: HirId, attr: &Attribute, target: Target) { + fn check_macro_export(&self, hir_id: HirId, attr_span: Span, target: Target) { if target != Target::MacroDef { + return; + } + + // special case when `#[macro_export]` is applied to a macro 2.0 + let (_, macro_definition, _) = self.tcx.hir_node(hir_id).expect_item().expect_macro(); + let is_decl_macro = !macro_definition.macro_rules; + + if is_decl_macro { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span(), - errors::MacroExport::Normal, + attr_span, + errors::MacroExport::OnDeclMacro, ); - } else if let Some(meta_item_list) = attr.meta_item_list() - && !meta_item_list.is_empty() - { - if meta_item_list.len() > 1 { - self.tcx.emit_node_span_lint( - INVALID_MACRO_EXPORT_ARGUMENTS, - hir_id, - attr.span(), - errors::MacroExport::TooManyItems, - ); - } else if !meta_item_list[0].has_name(sym::local_inner_macros) { - self.tcx.emit_node_span_lint( - INVALID_MACRO_EXPORT_ARGUMENTS, - hir_id, - meta_item_list[0].span(), - errors::MacroExport::InvalidArgument, - ); - } - } else { - // special case when `#[macro_export]` is applied to a macro 2.0 - let (_, macro_definition, _) = self.tcx.hir_node(hir_id).expect_item().expect_macro(); - let is_decl_macro = !macro_definition.macro_rules; - - if is_decl_macro { - self.tcx.emit_node_span_lint( - UNUSED_ATTRIBUTES, - hir_id, - attr.span(), - errors::MacroExport::OnDeclMacro, - ); - } } } @@ -2253,7 +2232,9 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { // In the long run, the checks should be harmonized. if let ItemKind::Macro(_, macro_def, _) = item.kind { let def_id = item.owner_id.to_def_id(); - if macro_def.macro_rules && !self.tcx.has_attr(def_id, sym::macro_export) { + if macro_def.macro_rules + && !find_attr!(self.tcx.get_all_attrs(def_id), AttributeKind::MacroExport { .. }) + { check_non_exported_macro_for_invalid_attrs(self.tcx, item); } } @@ -2384,7 +2365,6 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) { // which were unsuccessfully resolved due to cannot determine // resolution for the attribute macro error. const ATTRS_TO_CHECK: &[Symbol] = &[ - sym::macro_export, sym::rustc_main, sym::derive, sym::test, diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 2da4b6f52cf2..cd8935f6b2f0 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -530,18 +530,9 @@ pub(crate) struct RustcForceInlineCoro { #[derive(LintDiagnostic)] pub(crate) enum MacroExport { - #[diag(passes_macro_export)] - Normal, - #[diag(passes_macro_export_on_decl_macro)] #[note] OnDeclMacro, - - #[diag(passes_invalid_macro_export_arguments)] - InvalidArgument, - - #[diag(passes_invalid_macro_export_arguments_too_many_items)] - TooManyItems, } #[derive(Subdiagnostic)] diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index 2984f3ab50ee..3ffce61f7c61 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -6,6 +6,8 @@ use std::path::Path; use rustc_ast::join_path_syms; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::find_attr; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::DefId; use rustc_span::sym; @@ -1458,16 +1460,17 @@ pub(crate) fn build_index( if fqp.last() != Some(&item.name) { return None; } - let path = - if item.ty == ItemType::Macro && tcx.has_attr(defid, sym::macro_export) { - // `#[macro_export]` always exports to the crate root. - vec![tcx.crate_name(defid.krate)] - } else { - if fqp.len() < 2 { - return None; - } - fqp[..fqp.len() - 1].to_vec() - }; + let path = if item.ty == ItemType::Macro + && find_attr!(tcx.get_all_attrs(defid), AttributeKind::MacroExport { .. }) + { + // `#[macro_export]` always exports to the crate root. + vec![tcx.crate_name(defid.krate)] + } else { + if fqp.len() < 2 { + return None; + } + fqp[..fqp.len() - 1].to_vec() + }; if path == item.module_path { return None; } diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 6fe94f9d291b..779e26c7b0f7 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -912,12 +912,8 @@ fn maybe_from_hir_attr( hir::Attribute::Parsed(kind) => kind, hir::Attribute::Unparsed(_) => { - return Some(if attr.has_name(sym::macro_export) { - Attribute::MacroExport - // FIXME: We should handle `#[doc(hidden)]`. - } else { - other_attr(tcx, attr) - }); + // FIXME: We should handle `#[doc(hidden)]`. + return Some(other_attr(tcx, attr)); } }; @@ -925,6 +921,7 @@ fn maybe_from_hir_attr( AK::Deprecation { .. } => return None, // Handled separately into Item::deprecation. AK::DocComment { .. } => unreachable!("doc comments stripped out earlier"), + AK::MacroExport { .. } => Attribute::MacroExport, AK::MustUse { reason, span: _ } => { Attribute::MustUse { reason: reason.map(|s| s.to_string()) } } diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs index 3388ae46f056..525d05b6a986 100644 --- a/src/librustdoc/passes/strip_hidden.rs +++ b/src/librustdoc/passes/strip_hidden.rs @@ -2,9 +2,10 @@ use std::mem; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId}; +use rustc_hir::find_attr; use rustc_middle::ty::TyCtxt; -use rustc_span::symbol::sym; use tracing::debug; use crate::clean::utils::inherits_doc_hidden; @@ -114,7 +115,7 @@ impl DocFolder for Stripper<'_, '_> { // If the macro has the `#[macro_export]` attribute, it means it's accessible at the // crate level so it should be handled differently. clean::MacroItem(..) => { - i.attrs.other_attrs.iter().any(|attr| attr.has_name(sym::macro_export)) + find_attr!(&i.attrs.other_attrs, AttributeKind::MacroExport { .. }) } _ => false, }; diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index b2e4b5943750..cd28322f5901 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -5,10 +5,11 @@ use std::mem; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_hir as hir; +use rustc_hir::attrs::AttributeKind; use rustc_hir::def::{DefKind, MacroKinds, Res}; use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId, LocalDefIdSet}; use rustc_hir::intravisit::{Visitor, walk_body, walk_item}; -use rustc_hir::{CRATE_HIR_ID, Node}; +use rustc_hir::{CRATE_HIR_ID, Node, find_attr}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; use rustc_span::Span; @@ -166,7 +167,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { if !child.reexport_chain.is_empty() && let Res::Def(DefKind::Macro(_), def_id) = child.res && let Some(local_def_id) = def_id.as_local() - && self.cx.tcx.has_attr(def_id, sym::macro_export) + && find_attr!(self.cx.tcx.get_all_attrs(def_id), AttributeKind::MacroExport { .. }) && inserted.insert(def_id) { let item = self.cx.tcx.hir_expect_item(local_def_id); @@ -406,7 +407,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { || match item.kind { hir::ItemKind::Impl(..) => true, hir::ItemKind::Macro(_, _, _) => { - self.cx.tcx.has_attr(item.owner_id.def_id, sym::macro_export) + find_attr!(self.cx.tcx.get_all_attrs(item.owner_id.def_id), AttributeKind::MacroExport{..}) } _ => false, } @@ -524,7 +525,8 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { let def_id = item.owner_id.to_def_id(); let is_macro_2_0 = !macro_def.macro_rules; - let nonexported = !tcx.has_attr(def_id, sym::macro_export); + let nonexported = + !find_attr!(tcx.get_all_attrs(def_id), AttributeKind::MacroExport { .. }); if is_macro_2_0 || nonexported || self.inlining { self.add_to_current_mod(item, renamed, import_id); diff --git a/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs b/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs index 9071c9c95f9d..c5acaf099933 100644 --- a/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs +++ b/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs @@ -5,10 +5,12 @@ use itertools::Itertools; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; use rustc_hir::{BlockCheckMode, Expr, ExprKind, HirId, Stmt, UnsafeSource}; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::find_attr; use rustc_lint::{LateContext, LateLintPass, Level, LintContext}; use rustc_middle::lint::LevelAndSource; use rustc_session::impl_lint_pass; -use rustc_span::{Span, SyntaxContext, sym}; +use rustc_span::{Span, SyntaxContext}; use std::collections::BTreeMap; use std::collections::btree_map::Entry; @@ -146,7 +148,8 @@ struct BodyVisitor<'a, 'tcx> { } fn is_public_macro(cx: &LateContext<'_>, def_id: LocalDefId) -> bool { - (cx.effective_visibilities.is_exported(def_id) || cx.tcx.has_attr(def_id, sym::macro_export)) + ( cx.effective_visibilities.is_exported(def_id) || + find_attr!(cx.tcx.get_all_attrs(def_id), AttributeKind::MacroExport{..}) ) && !cx.tcx.is_doc_hidden(def_id) } From f7fa83ec626b91c360067e78eae94d9199cf43dc Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Sun, 21 Sep 2025 10:21:11 -0400 Subject: [PATCH 680/682] Changes to uitests for macro_export port Co-authored-by: Anne Stijns --- ...invalid_macro_export_argument.allow.stderr | 40 ++ .../invalid_macro_export_argument.deny.stderr | 93 ++++- .../invalid_macro_export_argument.rs | 12 +- tests/ui/attributes/malformed-attrs.rs | 2 +- tests/ui/attributes/malformed-attrs.stderr | 22 +- ...sue-43106-gating-of-builtin-attrs-error.rs | 3 +- ...43106-gating-of-builtin-attrs-error.stderr | 79 ++-- .../issue-43106-gating-of-builtin-attrs.rs | 30 +- ...issue-43106-gating-of-builtin-attrs.stderr | 389 +++++++++--------- .../lint/unused/unused-attr-duplicate.stderr | 24 +- 10 files changed, 417 insertions(+), 277 deletions(-) create mode 100644 tests/ui/attributes/invalid_macro_export_argument.allow.stderr diff --git a/tests/ui/attributes/invalid_macro_export_argument.allow.stderr b/tests/ui/attributes/invalid_macro_export_argument.allow.stderr new file mode 100644 index 000000000000..162b315b072a --- /dev/null +++ b/tests/ui/attributes/invalid_macro_export_argument.allow.stderr @@ -0,0 +1,40 @@ +Future incompatibility report: Future breakage diagnostic: +warning: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]` + --> $DIR/invalid_macro_export_argument.rs:7:1 + | +LL | #[macro_export(hello, world)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 + +Future breakage diagnostic: +warning: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]` + --> $DIR/invalid_macro_export_argument.rs:14:1 + | +LL | #[macro_export(not_local_inner_macros)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 + +Future breakage diagnostic: +warning: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]` + --> $DIR/invalid_macro_export_argument.rs:31:1 + | +LL | #[macro_export()] + | ^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 + +Future breakage diagnostic: +warning: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]` + --> $DIR/invalid_macro_export_argument.rs:38:1 + | +LL | #[macro_export("blah")] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 + diff --git a/tests/ui/attributes/invalid_macro_export_argument.deny.stderr b/tests/ui/attributes/invalid_macro_export_argument.deny.stderr index 9d44bd162c7b..ad225ae187b1 100644 --- a/tests/ui/attributes/invalid_macro_export_argument.deny.stderr +++ b/tests/ui/attributes/invalid_macro_export_argument.deny.stderr @@ -1,26 +1,103 @@ -error: `#[macro_export]` can only take 1 or 0 arguments +error: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]` --> $DIR/invalid_macro_export_argument.rs:7:1 | LL | #[macro_export(hello, world)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 note: the lint level is defined here --> $DIR/invalid_macro_export_argument.rs:4:24 | LL | #![cfg_attr(deny, deny(invalid_macro_export_arguments))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: invalid `#[macro_export]` argument - --> $DIR/invalid_macro_export_argument.rs:13:16 +error: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]` + --> $DIR/invalid_macro_export_argument.rs:14:1 | LL | #[macro_export(not_local_inner_macros)] - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 -error: invalid `#[macro_export]` argument - --> $DIR/invalid_macro_export_argument.rs:33:16 +error: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]` + --> $DIR/invalid_macro_export_argument.rs:31:1 + | +LL | #[macro_export()] + | ^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 + +error: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]` + --> $DIR/invalid_macro_export_argument.rs:38:1 | LL | #[macro_export("blah")] - | ^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors + +Future incompatibility report: Future breakage diagnostic: +error: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]` + --> $DIR/invalid_macro_export_argument.rs:7:1 + | +LL | #[macro_export(hello, world)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 +note: the lint level is defined here + --> $DIR/invalid_macro_export_argument.rs:4:24 + | +LL | #![cfg_attr(deny, deny(invalid_macro_export_arguments))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Future breakage diagnostic: +error: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]` + --> $DIR/invalid_macro_export_argument.rs:14:1 + | +LL | #[macro_export(not_local_inner_macros)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 +note: the lint level is defined here + --> $DIR/invalid_macro_export_argument.rs:4:24 + | +LL | #![cfg_attr(deny, deny(invalid_macro_export_arguments))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Future breakage diagnostic: +error: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]` + --> $DIR/invalid_macro_export_argument.rs:31:1 + | +LL | #[macro_export()] + | ^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 +note: the lint level is defined here + --> $DIR/invalid_macro_export_argument.rs:4:24 + | +LL | #![cfg_attr(deny, deny(invalid_macro_export_arguments))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Future breakage diagnostic: +error: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]` + --> $DIR/invalid_macro_export_argument.rs:38:1 + | +LL | #[macro_export("blah")] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 +note: the lint level is defined here + --> $DIR/invalid_macro_export_argument.rs:4:24 + | +LL | #![cfg_attr(deny, deny(invalid_macro_export_arguments))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/attributes/invalid_macro_export_argument.rs b/tests/ui/attributes/invalid_macro_export_argument.rs index c5fe39d062a4..05a40913434e 100644 --- a/tests/ui/attributes/invalid_macro_export_argument.rs +++ b/tests/ui/attributes/invalid_macro_export_argument.rs @@ -5,13 +5,15 @@ #![cfg_attr(allow, allow(invalid_macro_export_arguments))] #[macro_export(hello, world)] -//[deny]~^ ERROR `#[macro_export]` can only take 1 or 0 arguments +//[deny]~^ ERROR valid forms for the attribute are +//[deny]~| WARN this was previously accepted macro_rules! a { () => () } #[macro_export(not_local_inner_macros)] -//[deny]~^ ERROR invalid `#[macro_export]` argument +//[deny]~^ ERROR valid forms for the attribute are +//[deny]~| WARN this was previously accepted macro_rules! b { () => () } @@ -20,18 +22,22 @@ macro_rules! b { macro_rules! c { () => () } + #[macro_export(local_inner_macros)] macro_rules! d { () => () } #[macro_export()] +//[deny]~^ ERROR valid forms for the attribute are +//[deny]~| WARN this was previously accepted macro_rules! e { () => () } #[macro_export("blah")] -//[deny]~^ ERROR invalid `#[macro_export]` argument +//[deny]~^ ERROR valid forms for the attribute are +//[deny]~| WARN this was previously accepted macro_rules! f { () => () } diff --git a/tests/ui/attributes/malformed-attrs.rs b/tests/ui/attributes/malformed-attrs.rs index 932aaf7a9e2f..e30479b03b11 100644 --- a/tests/ui/attributes/malformed-attrs.rs +++ b/tests/ui/attributes/malformed-attrs.rs @@ -211,7 +211,7 @@ extern crate wloop; //~^ ERROR can't find crate for `wloop` [E0463] #[macro_export = 18] -//~^ ERROR malformed `macro_export` attribute input +//~^ ERROR valid forms for the attribute are #[allow_internal_unsafe = 1] //~^ ERROR malformed //~| ERROR allow_internal_unsafe side-steps the unsafe_code lint diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index b85864b401e3..246029ecf80f 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -178,22 +178,6 @@ LL | #[no_link()] | = note: for more information, visit -error: malformed `macro_export` attribute input - --> $DIR/malformed-attrs.rs:213:1 - | -LL | #[macro_export = 18] - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: for more information, visit -help: the following are the possible correct uses - | -LL - #[macro_export = 18] -LL + #[macro_export(local_inner_macros)] - | -LL - #[macro_export = 18] -LL + #[macro_export] - | - error: the `#[proc_macro]` attribute is only usable with crates of the `proc-macro` crate type --> $DIR/malformed-attrs.rs:98:1 | @@ -725,6 +709,12 @@ error: valid forms for the attribute are `#[macro_use(name1, name2, ...)]` and ` LL | #[macro_use = 1] | ^^^^^^^^^^^^^^^^ +error: valid forms for the attribute are `#![macro_export(local_inner_macros)]` and `#![macro_export]` + --> $DIR/malformed-attrs.rs:213:1 + | +LL | #[macro_export = 18] + | ^^^^^^^^^^^^^^^^^^^^ + error[E0565]: malformed `allow_internal_unsafe` attribute input --> $DIR/malformed-attrs.rs:215:1 | diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs index f391cf92fb63..a1a78df8d537 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.rs @@ -8,7 +8,7 @@ #![macro_export] -//~^ ERROR: `macro_export` attribute cannot be used at crate level +//~^ ERROR: `#[macro_export]` attribute cannot be used on crates #![rustc_main] //~^ ERROR: `rustc_main` attribute cannot be used at crate level //~| ERROR: use of an internal attribute [E0658] @@ -32,7 +32,6 @@ mod inline { //~^ NOTE the inner attribute doesn't annotate this module //~| NOTE the inner attribute doesn't annotate this module - //~| NOTE the inner attribute doesn't annotate this module mod inner { #![inline] } //~^ ERROR attribute cannot be used on diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr index 3b010c3e3127..4f4edeef420f 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr @@ -8,6 +8,14 @@ LL | #![rustc_main] = note: the `#[rustc_main]` attribute is an internal implementation detail that will never be stable = note: the `#[rustc_main]` attribute is used internally to specify test entry point function +error: `#[macro_export]` attribute cannot be used on crates + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:10:1 + | +LL | #![macro_export] + | ^^^^^^^^^^^^^^^^ + | + = help: `#[macro_export]` can only be applied to macro defs + error: `#[path]` attribute cannot be used on crates --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:19:1 | @@ -49,7 +57,7 @@ LL | #[inline] = help: `#[inline]` can only be applied to functions error: `#[inline]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:37:17 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:36:17 | LL | mod inner { #![inline] } | ^^^^^^^^^^ @@ -57,7 +65,7 @@ LL | mod inner { #![inline] } = help: `#[inline]` can only be applied to functions error: `#[inline]` attribute cannot be used on structs - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:46:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:45:5 | LL | #[inline] struct S; | ^^^^^^^^^ @@ -65,7 +73,7 @@ LL | #[inline] struct S; = help: `#[inline]` can only be applied to functions error: `#[inline]` attribute cannot be used on type aliases - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:49:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:48:5 | LL | #[inline] type T = S; | ^^^^^^^^^ @@ -73,7 +81,7 @@ LL | #[inline] type T = S; = help: `#[inline]` can only be applied to functions error: `#[inline]` attribute cannot be used on inherent impl blocks - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:52:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:51:5 | LL | #[inline] impl S { } | ^^^^^^^^^ @@ -81,7 +89,7 @@ LL | #[inline] impl S { } = help: `#[inline]` can only be applied to functions error: `#[export_name]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:82:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:81:1 | LL | #[export_name = "2200"] | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -89,7 +97,7 @@ LL | #[export_name = "2200"] = help: `#[export_name]` can be applied to functions and statics error: `#[export_name]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:85:17 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:84:17 | LL | mod inner { #![export_name="2200"] } | ^^^^^^^^^^^^^^^^^^^^^^ @@ -97,7 +105,7 @@ LL | mod inner { #![export_name="2200"] } = help: `#[export_name]` can be applied to functions and statics error: `#[export_name]` attribute cannot be used on structs - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:90:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:89:5 | LL | #[export_name = "2200"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -105,7 +113,7 @@ LL | #[export_name = "2200"] struct S; = help: `#[export_name]` can be applied to functions and statics error: `#[export_name]` attribute cannot be used on type aliases - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:93:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:92:5 | LL | #[export_name = "2200"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -113,7 +121,7 @@ LL | #[export_name = "2200"] type T = S; = help: `#[export_name]` can be applied to functions and statics error: `#[export_name]` attribute cannot be used on inherent impl blocks - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:96:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:95:5 | LL | #[export_name = "2200"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -121,7 +129,7 @@ LL | #[export_name = "2200"] impl S { } = help: `#[export_name]` can be applied to functions and statics error: `#[export_name]` attribute cannot be used on required trait methods - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:100:9 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:99:9 | LL | #[export_name = "2200"] fn foo(); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -129,7 +137,7 @@ LL | #[export_name = "2200"] fn foo(); = help: `#[export_name]` can be applied to statics, functions, inherent methods, provided trait methods, and trait methods in impl blocks error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:56:1 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:55:1 | LL | #[no_link] | ^^^^^^^^^^ @@ -143,7 +151,7 @@ LL | | } | |_- not an `extern crate` item error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:107:8 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:106:8 | LL | #[repr(C)] | ^ @@ -156,7 +164,7 @@ LL | | } | |_- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:131:8 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:130:8 | LL | #[repr(Rust)] | ^^^^ @@ -174,21 +182,6 @@ error: attribute should be applied to an `extern crate` item LL | #![no_link] | ^^^^^^^^^^^ not an `extern crate` item -error: `macro_export` attribute cannot be used at crate level - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:10:1 - | -LL | #![macro_export] - | ^^^^^^^^^^^^^^^^ -... -LL | mod inline { - | ------ the inner attribute doesn't annotate this module - | -help: perhaps you meant to use an outer attribute - | -LL - #![macro_export] -LL + #[macro_export] - | - error: `rustc_main` attribute cannot be used at crate level --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:12:1 | @@ -220,85 +213,85 @@ LL + #[repr()] | error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:61:17 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:60:17 | LL | mod inner { #![no_link] } | ------------^^^^^^^^^^^-- not an `extern crate` item error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:65:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:64:5 | LL | #[no_link] fn f() { } | ^^^^^^^^^^ ---------- not an `extern crate` item error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:69:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:68:5 | LL | #[no_link] struct S; | ^^^^^^^^^^ --------- not an `extern crate` item error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:73:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:72:5 | LL | #[no_link]type T = S; | ^^^^^^^^^^----------- not an `extern crate` item error: attribute should be applied to an `extern crate` item - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:77:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:76:5 | LL | #[no_link] impl S { } | ^^^^^^^^^^ ---------- not an `extern crate` item error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:111:25 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:110:25 | LL | mod inner { #![repr(C)] } | --------------------^---- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:115:12 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:114:12 | LL | #[repr(C)] fn f() { } | ^ ---------- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:121:12 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:120:12 | LL | #[repr(C)] type T = S; | ^ ----------- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:125:12 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:124:12 | LL | #[repr(C)] impl S { } | ^ ---------- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:135:25 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:134:25 | LL | mod inner { #![repr(Rust)] } | --------------------^^^^---- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:139:12 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:138:12 | LL | #[repr(Rust)] fn f() { } | ^^^^ ---------- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:145:12 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:144:12 | LL | #[repr(Rust)] type T = S; | ^^^^ ----------- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:149:12 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:148:12 | LL | #[repr(Rust)] impl S { } | ^^^^ ---------- not a struct, enum, or union error: valid forms for the attribute are `#[inline(always)]`, `#[inline(never)]`, and `#[inline]` - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:40:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:39:5 | LL | #[inline = "2100"] fn f() { } | ^^^^^^^^^^^^^^^^^^ @@ -313,7 +306,7 @@ Some errors have detailed explanations: E0517, E0658. For more information about an error, try `rustc --explain E0517`. Future incompatibility report: Future breakage diagnostic: error: valid forms for the attribute are `#[inline(always)]`, `#[inline(never)]`, and `#[inline]` - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:40:5 + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:39:5 | LL | #[inline = "2100"] fn f() { } | ^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs index 546aa4052d33..aa5aab41e725 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs @@ -214,22 +214,40 @@ mod macro_use { } #[macro_export] -//~^ WARN `#[macro_export]` only has an effect on macro definitions +//~^ WARN `#[macro_export]` attribute cannot be used on modules [unused_attributes] +//~| WARN previously accepted +//~| HELP can only be applied to +//~| HELP remove the attribute mod macro_export { mod inner { #![macro_export] } - //~^ WARN `#[macro_export]` only has an effect on macro definitions + //~^ WARN `#[macro_export]` attribute cannot be used on modules + //~| WARN previously accepted + //~| HELP can only be applied to + //~| HELP remove the attribute #[macro_export] fn f() { } - //~^ WARN `#[macro_export]` only has an effect on macro definitions + //~^ WARN `#[macro_export]` attribute cannot be used on function + //~| WARN previously accepted + //~| HELP can only be applied to + //~| HELP remove the attribute #[macro_export] struct S; - //~^ WARN `#[macro_export]` only has an effect on macro definitions + //~^ WARN `#[macro_export]` attribute cannot be used on structs + //~| WARN previously accepted + //~| HELP can only be applied to + //~| HELP remove the attribute #[macro_export] type T = S; - //~^ WARN `#[macro_export]` only has an effect on macro definitions + //~^ WARN `#[macro_export]` attribute cannot be used on type aliases + //~| WARN previously accepted + //~| HELP can only be applied to + //~| HELP remove the attribute #[macro_export] impl S { } - //~^ WARN `#[macro_export]` only has an effect on macro definitions + //~^ WARN `#[macro_export]` attribute cannot be used on inherent impl blocks + //~| WARN previously accepted + //~| HELP can only be applied to + //~| HELP remove the attribute } // At time of unit test authorship, if compiling without `--test` then diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr index 3c835be5cffa..5e2029c45165 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr @@ -1,5 +1,5 @@ warning: `#[macro_escape]` is a deprecated synonym for `#[macro_use]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:511:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:529:17 | LL | mod inner { #![macro_escape] } | ^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | mod inner { #![macro_escape] } = help: try an outer attribute: `#[macro_use]` warning: `#[macro_escape]` is a deprecated synonym for `#[macro_use]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:508:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:526:1 | LL | #[macro_escape] | ^^^^^^^^^^^^^^^ @@ -186,31 +186,24 @@ warning: unknown lint: `x5100` LL | #[deny(x5100)] impl S { } | ^^^^^ -warning: `#[macro_export]` only has an effect on macro definitions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:216:1 +warning: crate-level attribute should be an inner attribute + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:501:1 | -LL | #[macro_export] - | ^^^^^^^^^^^^^^^ +LL | #[reexport_test_harness_main = "2900"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here --> $DIR/issue-43106-gating-of-builtin-attrs.rs:37:9 | LL | #![warn(unused_attributes, unknown_lints)] | ^^^^^^^^^^^^^^^^^ - -warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:483:1 - | -LL | #[reexport_test_harness_main = "2900"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | help: add a `!` | LL | #![reexport_test_harness_main = "2900"] | + warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:695:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:713:1 | LL | #[link(name = "x")] | ^^^^^^^^^^^^^^^^^^^ @@ -226,7 +219,7 @@ LL | | } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:771:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:789:1 | LL | #[windows_subsystem = "windows"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -237,7 +230,7 @@ LL | #![windows_subsystem = "windows"] | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:821:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:839:1 | LL | #[crate_type = "0800"] | ^^^^^^^^^^^^^^^^^^^^^^ @@ -248,7 +241,7 @@ LL | #![crate_type = "0800"] | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:845:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:863:1 | LL | #[feature(x0600)] | ^^^^^^^^^^^^^^^^^ @@ -259,7 +252,7 @@ LL | #![feature(x0600)] | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:870:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:888:1 | LL | #[no_main] | ^^^^^^^^^^ @@ -270,7 +263,7 @@ LL | #![no_main] | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:894:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:912:1 | LL | #[no_builtins] | ^^^^^^^^^^^^^^ @@ -296,44 +289,14 @@ LL | #![feature(rust1)] | = note: `#[warn(stable_features)]` on by default -warning: `#[macro_export]` only has an effect on macro definitions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:219:17 - | -LL | mod inner { #![macro_export] } - | ^^^^^^^^^^^^^^^^ - -warning: `#[macro_export]` only has an effect on macro definitions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:222:5 - | -LL | #[macro_export] fn f() { } - | ^^^^^^^^^^^^^^^ - -warning: `#[macro_export]` only has an effect on macro definitions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:225:5 - | -LL | #[macro_export] struct S; - | ^^^^^^^^^^^^^^^ - -warning: `#[macro_export]` only has an effect on macro definitions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:228:5 - | -LL | #[macro_export] type T = S; - | ^^^^^^^^^^^^^^^ - -warning: `#[macro_export]` only has an effect on macro definitions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:231:5 - | -LL | #[macro_export] impl S { } - | ^^^^^^^^^^^^^^^ - warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:487:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:505:17 | LL | mod inner { #![reexport_test_harness_main="2900"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:490:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:508:5 | LL | #[reexport_test_harness_main = "2900"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -344,7 +307,7 @@ LL | #![reexport_test_harness_main = "2900"] fn f() { } | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:494:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:512:5 | LL | #[reexport_test_harness_main = "2900"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -355,7 +318,7 @@ LL | #![reexport_test_harness_main = "2900"] struct S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:498:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:516:5 | LL | #[reexport_test_harness_main = "2900"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -366,7 +329,7 @@ LL | #![reexport_test_harness_main = "2900"] type T = S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:502:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:520:5 | LL | #[reexport_test_harness_main = "2900"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -377,7 +340,7 @@ LL | #![reexport_test_harness_main = "2900"] impl S { } | + warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:701:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:719:17 | LL | mod inner { #![link(name = "x")] } | ------------^^^^^^^^^^^^^^^^^^^^-- not an `extern` block @@ -385,7 +348,7 @@ LL | mod inner { #![link(name = "x")] } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:706:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:724:5 | LL | #[link(name = "x")] fn f() { } | ^^^^^^^^^^^^^^^^^^^ ---------- not an `extern` block @@ -393,7 +356,7 @@ LL | #[link(name = "x")] fn f() { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:711:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:729:5 | LL | #[link(name = "x")] struct S; | ^^^^^^^^^^^^^^^^^^^ --------- not an `extern` block @@ -401,7 +364,7 @@ LL | #[link(name = "x")] struct S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:716:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:734:5 | LL | #[link(name = "x")] type T = S; | ^^^^^^^^^^^^^^^^^^^ ----------- not an `extern` block @@ -409,7 +372,7 @@ LL | #[link(name = "x")] type T = S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:721:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:739:5 | LL | #[link(name = "x")] impl S { } | ^^^^^^^^^^^^^^^^^^^ ---------- not an `extern` block @@ -417,7 +380,7 @@ LL | #[link(name = "x")] impl S { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:726:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:744:5 | LL | #[link(name = "x")] extern "Rust" {} | ^^^^^^^^^^^^^^^^^^^ @@ -425,13 +388,13 @@ LL | #[link(name = "x")] extern "Rust" {} = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:775:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:793:17 | LL | mod inner { #![windows_subsystem="windows"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:778:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:796:5 | LL | #[windows_subsystem = "windows"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -442,7 +405,7 @@ LL | #![windows_subsystem = "windows"] fn f() { } | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:782:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:800:5 | LL | #[windows_subsystem = "windows"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -453,7 +416,7 @@ LL | #![windows_subsystem = "windows"] struct S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:786:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:804:5 | LL | #[windows_subsystem = "windows"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -464,7 +427,7 @@ LL | #![windows_subsystem = "windows"] type T = S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:790:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:808:5 | LL | #[windows_subsystem = "windows"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -475,13 +438,13 @@ LL | #![windows_subsystem = "windows"] impl S { } | + warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:825:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:843:17 | LL | mod inner { #![crate_type="0800"] } | ^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:828:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:846:5 | LL | #[crate_type = "0800"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^ @@ -492,7 +455,7 @@ LL | #![crate_type = "0800"] fn f() { } | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:832:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:850:5 | LL | #[crate_type = "0800"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -503,7 +466,7 @@ LL | #![crate_type = "0800"] struct S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:836:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:854:5 | LL | #[crate_type = "0800"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -514,7 +477,7 @@ LL | #![crate_type = "0800"] type T = S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:840:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:858:5 | LL | #[crate_type = "0800"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^ @@ -525,13 +488,13 @@ LL | #![crate_type = "0800"] impl S { } | + warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:849:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:867:17 | LL | mod inner { #![feature(x0600)] } | ^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:852:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:870:5 | LL | #[feature(x0600)] fn f() { } | ^^^^^^^^^^^^^^^^^ @@ -542,7 +505,7 @@ LL | #![feature(x0600)] fn f() { } | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:856:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:874:5 | LL | #[feature(x0600)] struct S; | ^^^^^^^^^^^^^^^^^ @@ -553,7 +516,7 @@ LL | #![feature(x0600)] struct S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:860:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:878:5 | LL | #[feature(x0600)] type T = S; | ^^^^^^^^^^^^^^^^^ @@ -564,7 +527,7 @@ LL | #![feature(x0600)] type T = S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:864:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:882:5 | LL | #[feature(x0600)] impl S { } | ^^^^^^^^^^^^^^^^^ @@ -575,13 +538,13 @@ LL | #![feature(x0600)] impl S { } | + warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:874:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:892:17 | LL | mod inner { #![no_main] } | ^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:877:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:895:5 | LL | #[no_main] fn f() { } | ^^^^^^^^^^ @@ -592,7 +555,7 @@ LL | #![no_main] fn f() { } | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:881:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:899:5 | LL | #[no_main] struct S; | ^^^^^^^^^^ @@ -603,7 +566,7 @@ LL | #![no_main] struct S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:885:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:903:5 | LL | #[no_main] type T = S; | ^^^^^^^^^^ @@ -614,7 +577,7 @@ LL | #![no_main] type T = S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:889:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:907:5 | LL | #[no_main] impl S { } | ^^^^^^^^^^ @@ -625,13 +588,13 @@ LL | #![no_main] impl S { } | + warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:898:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:916:17 | LL | mod inner { #![no_builtins] } | ^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:901:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:919:5 | LL | #[no_builtins] fn f() { } | ^^^^^^^^^^^^^^ @@ -642,7 +605,7 @@ LL | #![no_builtins] fn f() { } | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:905:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:923:5 | LL | #[no_builtins] struct S; | ^^^^^^^^^^^^^^ @@ -653,7 +616,7 @@ LL | #![no_builtins] struct S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:909:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:927:5 | LL | #[no_builtins] type T = S; | ^^^^^^^^^^^^^^ @@ -664,7 +627,7 @@ LL | #![no_builtins] type T = S; | + warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:913:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:931:5 | LL | #[no_builtins] impl S { } | ^^^^^^^^^^^^^^ @@ -710,8 +673,62 @@ LL | #[macro_use] impl S { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = help: `#[macro_use]` can be applied to modules, extern crates, and crates +warning: `#[macro_export]` attribute cannot be used on modules + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:216:1 + | +LL | #[macro_export] + | ^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = help: `#[macro_export]` can only be applied to macro defs + +warning: `#[macro_export]` attribute cannot be used on modules + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:222:17 + | +LL | mod inner { #![macro_export] } + | ^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = help: `#[macro_export]` can only be applied to macro defs + +warning: `#[macro_export]` attribute cannot be used on functions + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:228:5 + | +LL | #[macro_export] fn f() { } + | ^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = help: `#[macro_export]` can only be applied to macro defs + +warning: `#[macro_export]` attribute cannot be used on structs + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:234:5 + | +LL | #[macro_export] struct S; + | ^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = help: `#[macro_export]` can only be applied to macro defs + +warning: `#[macro_export]` attribute cannot be used on type aliases + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:240:5 + | +LL | #[macro_export] type T = S; + | ^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = help: `#[macro_export]` can only be applied to macro defs + +warning: `#[macro_export]` attribute cannot be used on inherent impl blocks + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:246:5 + | +LL | #[macro_export] impl S { } + | ^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = help: `#[macro_export]` can only be applied to macro defs + warning: `#[path]` attribute cannot be used on functions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:271:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:289:5 | LL | #[path = "3800"] fn f() { } | ^^^^^^^^^^^^^^^^ @@ -720,7 +737,7 @@ LL | #[path = "3800"] fn f() { } = help: `#[path]` can only be applied to modules warning: `#[path]` attribute cannot be used on structs - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:277:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:295:5 | LL | #[path = "3800"] struct S; | ^^^^^^^^^^^^^^^^ @@ -729,7 +746,7 @@ LL | #[path = "3800"] struct S; = help: `#[path]` can only be applied to modules warning: `#[path]` attribute cannot be used on type aliases - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:283:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:301:5 | LL | #[path = "3800"] type T = S; | ^^^^^^^^^^^^^^^^ @@ -738,7 +755,7 @@ LL | #[path = "3800"] type T = S; = help: `#[path]` can only be applied to modules warning: `#[path]` attribute cannot be used on inherent impl blocks - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:289:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:307:5 | LL | #[path = "3800"] impl S { } | ^^^^^^^^^^^^^^^^ @@ -747,7 +764,7 @@ LL | #[path = "3800"] impl S { } = help: `#[path]` can only be applied to modules warning: `#[automatically_derived]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:296:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:314:1 | LL | #[automatically_derived] | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -756,7 +773,7 @@ LL | #[automatically_derived] = help: `#[automatically_derived]` can only be applied to trait impl blocks warning: `#[automatically_derived]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:302:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:320:17 | LL | mod inner { #![automatically_derived] } | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -765,7 +782,7 @@ LL | mod inner { #![automatically_derived] } = help: `#[automatically_derived]` can only be applied to trait impl blocks warning: `#[automatically_derived]` attribute cannot be used on functions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:308:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:326:5 | LL | #[automatically_derived] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -774,7 +791,7 @@ LL | #[automatically_derived] fn f() { } = help: `#[automatically_derived]` can only be applied to trait impl blocks warning: `#[automatically_derived]` attribute cannot be used on structs - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:314:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:332:5 | LL | #[automatically_derived] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -783,7 +800,7 @@ LL | #[automatically_derived] struct S; = help: `#[automatically_derived]` can only be applied to trait impl blocks warning: `#[automatically_derived]` attribute cannot be used on type aliases - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:320:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:338:5 | LL | #[automatically_derived] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -792,7 +809,7 @@ LL | #[automatically_derived] type T = S; = help: `#[automatically_derived]` can only be applied to trait impl blocks warning: `#[automatically_derived]` attribute cannot be used on traits - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:326:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:344:5 | LL | #[automatically_derived] trait W { } | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -801,7 +818,7 @@ LL | #[automatically_derived] trait W { } = help: `#[automatically_derived]` can only be applied to trait impl blocks warning: `#[automatically_derived]` attribute cannot be used on inherent impl blocks - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:332:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:350:5 | LL | #[automatically_derived] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -810,7 +827,7 @@ LL | #[automatically_derived] impl S { } = help: `#[automatically_derived]` can only be applied to trait impl blocks warning: `#[no_mangle]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:341:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:359:1 | LL | #[no_mangle] | ^^^^^^^^^^^^ @@ -819,7 +836,7 @@ LL | #[no_mangle] = help: `#[no_mangle]` can be applied to functions and statics warning: `#[no_mangle]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:347:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:365:17 | LL | mod inner { #![no_mangle] } | ^^^^^^^^^^^^^ @@ -828,7 +845,7 @@ LL | mod inner { #![no_mangle] } = help: `#[no_mangle]` can be applied to functions and statics warning: `#[no_mangle]` attribute cannot be used on structs - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:355:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:373:5 | LL | #[no_mangle] struct S; | ^^^^^^^^^^^^ @@ -837,7 +854,7 @@ LL | #[no_mangle] struct S; = help: `#[no_mangle]` can be applied to functions and statics warning: `#[no_mangle]` attribute cannot be used on type aliases - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:361:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:379:5 | LL | #[no_mangle] type T = S; | ^^^^^^^^^^^^ @@ -846,7 +863,7 @@ LL | #[no_mangle] type T = S; = help: `#[no_mangle]` can be applied to functions and statics warning: `#[no_mangle]` attribute cannot be used on inherent impl blocks - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:367:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:385:5 | LL | #[no_mangle] impl S { } | ^^^^^^^^^^^^ @@ -855,7 +872,7 @@ LL | #[no_mangle] impl S { } = help: `#[no_mangle]` can be applied to functions and statics warning: `#[no_mangle]` attribute cannot be used on required trait methods - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:374:9 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:392:9 | LL | #[no_mangle] fn foo(); | ^^^^^^^^^^^^ @@ -864,7 +881,7 @@ LL | #[no_mangle] fn foo(); = help: `#[no_mangle]` can be applied to functions, statics, inherent methods, and trait methods in impl blocks warning: `#[no_mangle]` attribute cannot be used on provided trait methods - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:380:9 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:398:9 | LL | #[no_mangle] fn bar() {} | ^^^^^^^^^^^^ @@ -873,7 +890,7 @@ LL | #[no_mangle] fn bar() {} = help: `#[no_mangle]` can be applied to functions, statics, inherent methods, and trait methods in impl blocks warning: `#[should_panic]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:388:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:406:1 | LL | #[should_panic] | ^^^^^^^^^^^^^^^ @@ -882,7 +899,7 @@ LL | #[should_panic] = help: `#[should_panic]` can only be applied to functions warning: `#[should_panic]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:394:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:412:17 | LL | mod inner { #![should_panic] } | ^^^^^^^^^^^^^^^^ @@ -891,7 +908,7 @@ LL | mod inner { #![should_panic] } = help: `#[should_panic]` can only be applied to functions warning: `#[should_panic]` attribute cannot be used on structs - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:402:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:420:5 | LL | #[should_panic] struct S; | ^^^^^^^^^^^^^^^ @@ -900,7 +917,7 @@ LL | #[should_panic] struct S; = help: `#[should_panic]` can only be applied to functions warning: `#[should_panic]` attribute cannot be used on type aliases - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:408:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:426:5 | LL | #[should_panic] type T = S; | ^^^^^^^^^^^^^^^ @@ -909,7 +926,7 @@ LL | #[should_panic] type T = S; = help: `#[should_panic]` can only be applied to functions warning: `#[should_panic]` attribute cannot be used on inherent impl blocks - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:414:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:432:5 | LL | #[should_panic] impl S { } | ^^^^^^^^^^^^^^^ @@ -918,7 +935,7 @@ LL | #[should_panic] impl S { } = help: `#[should_panic]` can only be applied to functions warning: `#[ignore]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:421:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:439:1 | LL | #[ignore] | ^^^^^^^^^ @@ -927,7 +944,7 @@ LL | #[ignore] = help: `#[ignore]` can only be applied to functions warning: `#[ignore]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:427:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:445:17 | LL | mod inner { #![ignore] } | ^^^^^^^^^^ @@ -936,7 +953,7 @@ LL | mod inner { #![ignore] } = help: `#[ignore]` can only be applied to functions warning: `#[ignore]` attribute cannot be used on structs - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:435:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:453:5 | LL | #[ignore] struct S; | ^^^^^^^^^ @@ -945,7 +962,7 @@ LL | #[ignore] struct S; = help: `#[ignore]` can only be applied to functions warning: `#[ignore]` attribute cannot be used on type aliases - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:441:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:459:5 | LL | #[ignore] type T = S; | ^^^^^^^^^ @@ -954,7 +971,7 @@ LL | #[ignore] type T = S; = help: `#[ignore]` can only be applied to functions warning: `#[ignore]` attribute cannot be used on inherent impl blocks - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:447:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:465:5 | LL | #[ignore] impl S { } | ^^^^^^^^^ @@ -963,7 +980,7 @@ LL | #[ignore] impl S { } = help: `#[ignore]` can only be applied to functions warning: `#[no_implicit_prelude]` attribute cannot be used on functions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:458:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:476:5 | LL | #[no_implicit_prelude] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^ @@ -972,7 +989,7 @@ LL | #[no_implicit_prelude] fn f() { } = help: `#[no_implicit_prelude]` can be applied to modules and crates warning: `#[no_implicit_prelude]` attribute cannot be used on structs - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:464:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:482:5 | LL | #[no_implicit_prelude] struct S; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -981,7 +998,7 @@ LL | #[no_implicit_prelude] struct S; = help: `#[no_implicit_prelude]` can be applied to modules and crates warning: `#[no_implicit_prelude]` attribute cannot be used on type aliases - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:470:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:488:5 | LL | #[no_implicit_prelude] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -990,7 +1007,7 @@ LL | #[no_implicit_prelude] type T = S; = help: `#[no_implicit_prelude]` can be applied to modules and crates warning: `#[no_implicit_prelude]` attribute cannot be used on inherent impl blocks - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:476:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:494:5 | LL | #[no_implicit_prelude] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^ @@ -999,7 +1016,7 @@ LL | #[no_implicit_prelude] impl S { } = help: `#[no_implicit_prelude]` can be applied to modules and crates warning: `#[macro_escape]` attribute cannot be used on functions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:515:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:533:5 | LL | #[macro_escape] fn f() { } | ^^^^^^^^^^^^^^^ @@ -1008,7 +1025,7 @@ LL | #[macro_escape] fn f() { } = help: `#[macro_escape]` can be applied to modules, extern crates, and crates warning: `#[macro_escape]` attribute cannot be used on structs - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:521:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:539:5 | LL | #[macro_escape] struct S; | ^^^^^^^^^^^^^^^ @@ -1017,7 +1034,7 @@ LL | #[macro_escape] struct S; = help: `#[macro_escape]` can be applied to modules, extern crates, and crates warning: `#[macro_escape]` attribute cannot be used on type aliases - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:527:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:545:5 | LL | #[macro_escape] type T = S; | ^^^^^^^^^^^^^^^ @@ -1026,7 +1043,7 @@ LL | #[macro_escape] type T = S; = help: `#[macro_escape]` can be applied to modules, extern crates, and crates warning: `#[macro_escape]` attribute cannot be used on inherent impl blocks - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:533:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:551:5 | LL | #[macro_escape] impl S { } | ^^^^^^^^^^^^^^^ @@ -1035,13 +1052,13 @@ LL | #[macro_escape] impl S { } = help: `#[macro_escape]` can be applied to modules, extern crates, and crates warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![no_std]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:540:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:558:1 | LL | #[no_std] | ^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:542:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:560:1 | LL | / mod no_std { LL | | @@ -1051,61 +1068,61 @@ LL | | } | |_^ warning: the `#![no_std]` attribute can only be used at the crate root - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:544:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:562:17 | LL | mod inner { #![no_std] } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![no_std]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:547:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:565:5 | LL | #[no_std] fn f() { } | ^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this function - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:547:15 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:565:15 | LL | #[no_std] fn f() { } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![no_std]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:551:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:569:5 | LL | #[no_std] struct S; | ^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this struct - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:551:15 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:569:15 | LL | #[no_std] struct S; | ^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![no_std]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:555:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:573:5 | LL | #[no_std] type T = S; | ^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this type alias - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:555:15 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:573:15 | LL | #[no_std] type T = S; | ^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![no_std]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:559:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:577:5 | LL | #[no_std] impl S { } | ^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this implementation block - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:559:15 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:577:15 | LL | #[no_std] impl S { } | ^^^^^^^^^^ warning: `#[cold]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:581:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:599:1 | LL | #[cold] | ^^^^^^^ @@ -1114,7 +1131,7 @@ LL | #[cold] = help: `#[cold]` can only be applied to functions warning: `#[cold]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:588:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:606:17 | LL | mod inner { #![cold] } | ^^^^^^^^ @@ -1123,7 +1140,7 @@ LL | mod inner { #![cold] } = help: `#[cold]` can only be applied to functions warning: `#[cold]` attribute cannot be used on structs - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:596:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:614:5 | LL | #[cold] struct S; | ^^^^^^^ @@ -1132,7 +1149,7 @@ LL | #[cold] struct S; = help: `#[cold]` can only be applied to functions warning: `#[cold]` attribute cannot be used on type aliases - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:602:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:620:5 | LL | #[cold] type T = S; | ^^^^^^^ @@ -1141,7 +1158,7 @@ LL | #[cold] type T = S; = help: `#[cold]` can only be applied to functions warning: `#[cold]` attribute cannot be used on inherent impl blocks - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:608:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:626:5 | LL | #[cold] impl S { } | ^^^^^^^ @@ -1150,7 +1167,7 @@ LL | #[cold] impl S { } = help: `#[cold]` can only be applied to functions warning: `#[link_name]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:615:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:633:1 | LL | #[link_name = "1900"] | ^^^^^^^^^^^^^^^^^^^^^ @@ -1159,7 +1176,7 @@ LL | #[link_name = "1900"] = help: `#[link_name]` can be applied to foreign functions and foreign statics warning: `#[link_name]` attribute cannot be used on foreign modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:621:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:639:5 | LL | #[link_name = "1900"] | ^^^^^^^^^^^^^^^^^^^^^ @@ -1168,7 +1185,7 @@ LL | #[link_name = "1900"] = help: `#[link_name]` can be applied to foreign functions and foreign statics warning: `#[link_name]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:628:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:646:17 | LL | mod inner { #![link_name="1900"] } | ^^^^^^^^^^^^^^^^^^^^ @@ -1177,7 +1194,7 @@ LL | mod inner { #![link_name="1900"] } = help: `#[link_name]` can be applied to foreign functions and foreign statics warning: `#[link_name]` attribute cannot be used on functions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:634:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:652:5 | LL | #[link_name = "1900"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^ @@ -1186,7 +1203,7 @@ LL | #[link_name = "1900"] fn f() { } = help: `#[link_name]` can be applied to foreign functions and foreign statics warning: `#[link_name]` attribute cannot be used on structs - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:640:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:658:5 | LL | #[link_name = "1900"] struct S; | ^^^^^^^^^^^^^^^^^^^^^ @@ -1195,7 +1212,7 @@ LL | #[link_name = "1900"] struct S; = help: `#[link_name]` can be applied to foreign functions and foreign statics warning: `#[link_name]` attribute cannot be used on type aliases - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:646:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:664:5 | LL | #[link_name = "1900"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^ @@ -1204,7 +1221,7 @@ LL | #[link_name = "1900"] type T = S; = help: `#[link_name]` can be applied to foreign functions and foreign statics warning: `#[link_name]` attribute cannot be used on inherent impl blocks - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:652:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:670:5 | LL | #[link_name = "1900"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^ @@ -1213,7 +1230,7 @@ LL | #[link_name = "1900"] impl S { } = help: `#[link_name]` can be applied to foreign functions and foreign statics warning: `#[link_section]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:659:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:677:1 | LL | #[link_section = "1800"] | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1222,7 +1239,7 @@ LL | #[link_section = "1800"] = help: `#[link_section]` can be applied to statics and functions warning: `#[link_section]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:665:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:683:17 | LL | mod inner { #![link_section="1800"] } | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -1231,7 +1248,7 @@ LL | mod inner { #![link_section="1800"] } = help: `#[link_section]` can be applied to statics and functions warning: `#[link_section]` attribute cannot be used on structs - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:673:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:691:5 | LL | #[link_section = "1800"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1240,7 +1257,7 @@ LL | #[link_section = "1800"] struct S; = help: `#[link_section]` can be applied to statics and functions warning: `#[link_section]` attribute cannot be used on type aliases - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:679:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:697:5 | LL | #[link_section = "1800"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1249,7 +1266,7 @@ LL | #[link_section = "1800"] type T = S; = help: `#[link_section]` can be applied to statics and functions warning: `#[link_section]` attribute cannot be used on inherent impl blocks - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:685:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:703:5 | LL | #[link_section = "1800"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1258,7 +1275,7 @@ LL | #[link_section = "1800"] impl S { } = help: `#[link_section]` can be applied to statics and functions warning: `#[must_use]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:746:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:764:1 | LL | #[must_use] | ^^^^^^^^^^^ @@ -1267,7 +1284,7 @@ LL | #[must_use] = help: `#[must_use]` can be applied to functions, data types, unions, and traits warning: `#[must_use]` attribute cannot be used on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:751:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:769:17 | LL | mod inner { #![must_use] } | ^^^^^^^^^^^^ @@ -1276,7 +1293,7 @@ LL | mod inner { #![must_use] } = help: `#[must_use]` can be applied to functions, data types, unions, and traits warning: `#[must_use]` attribute cannot be used on type aliases - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:760:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:778:5 | LL | #[must_use] type T = S; | ^^^^^^^^^^^ @@ -1285,7 +1302,7 @@ LL | #[must_use] type T = S; = help: `#[must_use]` can be applied to functions, data types, unions, and traits warning: `#[must_use]` attribute cannot be used on inherent impl blocks - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:765:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:783:5 | LL | #[must_use] impl S { } | ^^^^^^^^^^^ @@ -1294,13 +1311,13 @@ LL | #[must_use] impl S { } = help: `#[must_use]` can be applied to functions, data types, unions, and traits warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![crate_name]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:797:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:815:1 | LL | #[crate_name = "0900"] | ^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:799:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:817:1 | LL | / mod crate_name { LL | | @@ -1310,67 +1327,67 @@ LL | | } | |_^ warning: the `#![crate_name]` attribute can only be used at the crate root - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:801:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:819:17 | LL | mod inner { #![crate_name="0900"] } | ^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![crate_name]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:804:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:822:5 | LL | #[crate_name = "0900"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this function - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:804:28 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:822:28 | LL | #[crate_name = "0900"] fn f() { } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![crate_name]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:808:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:826:5 | LL | #[crate_name = "0900"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this struct - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:808:28 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:826:28 | LL | #[crate_name = "0900"] struct S; | ^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![crate_name]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:812:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:830:5 | LL | #[crate_name = "0900"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this type alias - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:812:28 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:830:28 | LL | #[crate_name = "0900"] type T = S; | ^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![crate_name]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:816:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:834:5 | LL | #[crate_name = "0900"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this implementation block - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:816:28 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:834:28 | LL | #[crate_name = "0900"] impl S { } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:918:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:936:1 | LL | #[recursion_limit="0200"] | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:920:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:938:1 | LL | / mod recursion_limit { LL | | @@ -1380,67 +1397,67 @@ LL | | } | |_^ warning: the `#![recursion_limit]` attribute can only be used at the crate root - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:922:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:940:17 | LL | mod inner { #![recursion_limit="0200"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:925:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:943:5 | LL | #[recursion_limit="0200"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this function - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:925:31 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:943:31 | LL | #[recursion_limit="0200"] fn f() { } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:929:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:947:5 | LL | #[recursion_limit="0200"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this struct - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:929:31 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:947:31 | LL | #[recursion_limit="0200"] struct S; | ^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:933:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:951:5 | LL | #[recursion_limit="0200"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this type alias - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:933:31 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:951:31 | LL | #[recursion_limit="0200"] type T = S; | ^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:937:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:955:5 | LL | #[recursion_limit="0200"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this implementation block - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:937:31 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:955:31 | LL | #[recursion_limit="0200"] impl S { } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![type_length_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:942:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:960:1 | LL | #[type_length_limit="0100"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:944:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:962:1 | LL | / mod type_length_limit { LL | | @@ -1450,55 +1467,55 @@ LL | | } | |_^ warning: the `#![type_length_limit]` attribute can only be used at the crate root - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:946:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:964:17 | LL | mod inner { #![type_length_limit="0100"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![type_length_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:949:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:967:5 | LL | #[type_length_limit="0100"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this function - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:949:33 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:967:33 | LL | #[type_length_limit="0100"] fn f() { } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![type_length_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:953:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:971:5 | LL | #[type_length_limit="0100"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this struct - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:953:33 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:971:33 | LL | #[type_length_limit="0100"] struct S; | ^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![type_length_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:957:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:975:5 | LL | #[type_length_limit="0100"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this type alias - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:957:33 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:975:33 | LL | #[type_length_limit="0100"] type T = S; | ^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![type_length_limit]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:961:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:979:5 | LL | #[type_length_limit="0100"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: This attribute does not have an `!`, which means it is applied to this implementation block - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:961:33 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:979:33 | LL | #[type_length_limit="0100"] impl S { } | ^^^^^^^^^^ diff --git a/tests/ui/lint/unused/unused-attr-duplicate.stderr b/tests/ui/lint/unused/unused-attr-duplicate.stderr index 3a3b450f3c55..fa2c9e59a418 100644 --- a/tests/ui/lint/unused/unused-attr-duplicate.stderr +++ b/tests/ui/lint/unused/unused-attr-duplicate.stderr @@ -53,18 +53,6 @@ note: attribute also specified here LL | #![no_builtins] | ^^^^^^^^^^^^^^^ -error: unused attribute - --> $DIR/unused-attr-duplicate.rs:44:5 - | -LL | #[macro_export] - | ^^^^^^^^^^^^^^^ help: remove this attribute - | -note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:43:5 - | -LL | #[macro_export] - | ^^^^^^^^^^^^^^^ - error: unused attribute --> $DIR/unused-attr-duplicate.rs:41:1 | @@ -77,6 +65,18 @@ note: attribute also specified here LL | #[macro_use] | ^^^^^^^^^^^^ +error: unused attribute + --> $DIR/unused-attr-duplicate.rs:44:5 + | +LL | #[macro_export] + | ^^^^^^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/unused-attr-duplicate.rs:43:5 + | +LL | #[macro_export] + | ^^^^^^^^^^^^^^^ + error: unused attribute --> $DIR/unused-attr-duplicate.rs:51:1 | From 87a00f67ba4c8148e6aa2e1768025b9d51a0fa8b Mon Sep 17 00:00:00 2001 From: joboet Date: Tue, 16 Sep 2025 15:48:43 +0200 Subject: [PATCH 681/682] std: merge definitions of `StdioPipes` All platforms define this structure the same way, so we can just put it in the `process` module directly. --- library/std/src/process.rs | 13 +++++++++++-- library/std/src/sys/process/mod.rs | 2 +- library/std/src/sys/process/uefi.rs | 9 +-------- library/std/src/sys/process/unix/common.rs | 9 +-------- library/std/src/sys/process/unix/fuchsia.rs | 1 + library/std/src/sys/process/unix/mod.rs | 2 +- library/std/src/sys/process/unix/unix.rs | 1 + library/std/src/sys/process/unix/unsupported.rs | 1 + library/std/src/sys/process/unix/vxworks.rs | 1 + library/std/src/sys/process/unsupported.rs | 9 +-------- library/std/src/sys/process/windows.rs | 7 +------ 11 files changed, 21 insertions(+), 34 deletions(-) diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 0883e56342c8..5c0ac526a36c 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -268,8 +268,8 @@ impl AsInner for Child { } } -impl FromInner<(imp::Process, imp::StdioPipes)> for Child { - fn from_inner((handle, io): (imp::Process, imp::StdioPipes)) -> Child { +impl FromInner<(imp::Process, StdioPipes)> for Child { + fn from_inner((handle, io): (imp::Process, StdioPipes)) -> Child { Child { handle, stdin: io.stdin.map(ChildStdin::from_inner), @@ -296,6 +296,15 @@ impl fmt::Debug for Child { } } +/// The pipes connected to a spawned process. +/// +/// Used to pass pipe handles between this module and [`imp`]. +pub(crate) struct StdioPipes { + pub stdin: Option, + pub stdout: Option, + pub stderr: Option, +} + /// A handle to a child process's standard input (stdin). /// /// This struct is used in the [`stdin`] field on [`Child`]. diff --git a/library/std/src/sys/process/mod.rs b/library/std/src/sys/process/mod.rs index 9ef5496e57a0..a1ed0cd2cdd2 100644 --- a/library/std/src/sys/process/mod.rs +++ b/library/std/src/sys/process/mod.rs @@ -24,7 +24,7 @@ mod env; pub use env::CommandEnvs; pub use imp::{ - Command, CommandArgs, EnvKey, ExitCode, ExitStatus, ExitStatusError, Process, Stdio, StdioPipes, + Command, CommandArgs, EnvKey, ExitCode, ExitStatus, ExitStatusError, Process, Stdio, }; #[cfg(any( diff --git a/library/std/src/sys/process/uefi.rs b/library/std/src/sys/process/uefi.rs index 4864c5869881..11c8b682bb9b 100644 --- a/library/std/src/sys/process/uefi.rs +++ b/library/std/src/sys/process/uefi.rs @@ -6,6 +6,7 @@ pub use crate::ffi::OsString as EnvKey; use crate::ffi::{OsStr, OsString}; use crate::num::{NonZero, NonZeroI32}; use crate::path::Path; +use crate::process::StdioPipes; use crate::sys::fs::File; use crate::sys::pal::helpers; use crate::sys::pal::os::error_string; @@ -27,14 +28,6 @@ pub struct Command { env: CommandEnv, } -// passed back to std::process with the pipes connected to the child, if any -// were requested -pub struct StdioPipes { - pub stdin: Option, - pub stdout: Option, - pub stderr: Option, -} - #[derive(Copy, Clone, Debug)] pub enum Stdio { Inherit, diff --git a/library/std/src/sys/process/unix/common.rs b/library/std/src/sys/process/unix/common.rs index ea45b08e90a3..1d5909e99bac 100644 --- a/library/std/src/sys/process/unix/common.rs +++ b/library/std/src/sys/process/unix/common.rs @@ -9,6 +9,7 @@ use crate::collections::BTreeMap; use crate::ffi::{CStr, CString, OsStr, OsString}; use crate::os::unix::prelude::*; use crate::path::Path; +use crate::process::StdioPipes; use crate::sys::fd::FileDesc; use crate::sys::fs::File; #[cfg(not(target_os = "fuchsia"))] @@ -104,14 +105,6 @@ pub struct Command { setsid: bool, } -// passed back to std::process with the pipes connected to the child, if any -// were requested -pub struct StdioPipes { - pub stdin: Option, - pub stdout: Option, - pub stderr: Option, -} - // passed to do_exec() with configuration of what the child stdio should look // like #[cfg_attr(target_os = "vita", allow(dead_code))] diff --git a/library/std/src/sys/process/unix/fuchsia.rs b/library/std/src/sys/process/unix/fuchsia.rs index d71be510b6af..3fae5ec1468b 100644 --- a/library/std/src/sys/process/unix/fuchsia.rs +++ b/library/std/src/sys/process/unix/fuchsia.rs @@ -2,6 +2,7 @@ use libc::{c_int, size_t}; use super::common::*; use crate::num::NonZero; +use crate::process::StdioPipes; use crate::sys::pal::fuchsia::*; use crate::{fmt, io, mem, ptr}; diff --git a/library/std/src/sys/process/unix/mod.rs b/library/std/src/sys/process/unix/mod.rs index b4cf060fba9b..cda1bf74f1ca 100644 --- a/library/std/src/sys/process/unix/mod.rs +++ b/library/std/src/sys/process/unix/mod.rs @@ -23,5 +23,5 @@ cfg_select! { pub use imp::{ExitStatus, ExitStatusError, Process}; -pub use self::common::{Command, CommandArgs, ExitCode, Stdio, StdioPipes}; +pub use self::common::{Command, CommandArgs, ExitCode, Stdio}; pub use crate::ffi::OsString as EnvKey; diff --git a/library/std/src/sys/process/unix/unix.rs b/library/std/src/sys/process/unix/unix.rs index 11d48878727b..7d944f2f7eef 100644 --- a/library/std/src/sys/process/unix/unix.rs +++ b/library/std/src/sys/process/unix/unix.rs @@ -13,6 +13,7 @@ use libc::{gid_t, uid_t}; use super::common::*; use crate::io::{self, Error, ErrorKind}; use crate::num::NonZero; +use crate::process::StdioPipes; use crate::sys::cvt; #[cfg(target_os = "linux")] use crate::sys::pal::linux::pidfd::PidFd; diff --git a/library/std/src/sys/process/unix/unsupported.rs b/library/std/src/sys/process/unix/unsupported.rs index 87403cd50f82..9bda394f2465 100644 --- a/library/std/src/sys/process/unix/unsupported.rs +++ b/library/std/src/sys/process/unix/unsupported.rs @@ -3,6 +3,7 @@ use libc::{c_int, pid_t}; use super::common::*; use crate::io; use crate::num::NonZero; +use crate::process::StdioPipes; use crate::sys::pal::unsupported::*; //////////////////////////////////////////////////////////////////////////////// diff --git a/library/std/src/sys/process/unix/vxworks.rs b/library/std/src/sys/process/unix/vxworks.rs index b9298f5fa44c..346ca6d74c9b 100644 --- a/library/std/src/sys/process/unix/vxworks.rs +++ b/library/std/src/sys/process/unix/vxworks.rs @@ -4,6 +4,7 @@ use libc::{self, RTP_ID, c_char, c_int}; use super::common::*; use crate::io::{self, ErrorKind}; use crate::num::NonZero; +use crate::process::StdioPipes; use crate::sys::{cvt, thread}; use crate::{fmt, sys}; diff --git a/library/std/src/sys/process/unsupported.rs b/library/std/src/sys/process/unsupported.rs index 469922c78aca..636465b68e54 100644 --- a/library/std/src/sys/process/unsupported.rs +++ b/library/std/src/sys/process/unsupported.rs @@ -3,6 +3,7 @@ pub use crate::ffi::OsString as EnvKey; use crate::ffi::{OsStr, OsString}; use crate::num::NonZero; use crate::path::Path; +use crate::process::StdioPipes; use crate::sys::fs::File; use crate::sys::pipe::AnonPipe; use crate::sys::unsupported; @@ -23,14 +24,6 @@ pub struct Command { stderr: Option, } -// passed back to std::process with the pipes connected to the child, if any -// were requested -pub struct StdioPipes { - pub stdin: Option, - pub stdout: Option, - pub stderr: Option, -} - #[derive(Debug)] pub enum Stdio { Inherit, diff --git a/library/std/src/sys/process/windows.rs b/library/std/src/sys/process/windows.rs index f9e15b824757..1f2001bdc204 100644 --- a/library/std/src/sys/process/windows.rs +++ b/library/std/src/sys/process/windows.rs @@ -15,6 +15,7 @@ use crate::os::windows::ffi::{OsStrExt, OsStringExt}; use crate::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle}; use crate::os::windows::process::ProcThreadAttributeList; use crate::path::{Path, PathBuf}; +use crate::process::StdioPipes; use crate::sync::Mutex; use crate::sys::args::{self, Arg}; use crate::sys::c::{self, EXIT_FAILURE, EXIT_SUCCESS}; @@ -169,12 +170,6 @@ pub enum Stdio { Handle(Handle), } -pub struct StdioPipes { - pub stdin: Option, - pub stdout: Option, - pub stderr: Option, -} - impl Command { pub fn new(program: &OsStr) -> Command { Command { From 5658b39397d2667ed8c51ef6b5c52f562af4415e Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Mon, 22 Sep 2025 04:13:50 +0000 Subject: [PATCH 682/682] Prepare for merging from rust-lang/rust This updates the rust-version file to 9f32ccf35fb877270bc44a86a126440f04d676d0. --- 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 df2bac877b66..0dc9ce843e9c 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -2f3f27bf79ec147fec9d2e7980605307a74067f4 +9f32ccf35fb877270bc44a86a126440f04d676d0

( &self, tcx: DbInterner<'db>, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/type_variable.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/type_variable.rs index b640039af625..29e7b883c93b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/type_variable.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/type_variable.rs @@ -15,7 +15,7 @@ use tracing::debug; use crate::next_solver::SolverDefId; use crate::next_solver::Ty; -use crate::next_solver::infer::InferCtxtUndoLogs; +use crate::next_solver::infer::{InferCtxtUndoLogs, iter_idx_range}; /// Represents a single undo-able action that affects a type inference variable. #[derive(Clone)] @@ -59,7 +59,7 @@ impl<'tcx> Rollback> for TypeVariableStorage<'tcx> { } } -#[derive(Clone, Default)] +#[derive(Debug, Clone, Default)] pub(crate) struct TypeVariableStorage<'db> { /// The origins of each type variable. values: IndexVec, @@ -102,7 +102,7 @@ pub struct TypeVariableOrigin { pub param_def_id: Option, } -#[derive(Clone)] +#[derive(Debug, Clone)] pub(crate) struct TypeVariableData { origin: TypeVariableOrigin, } @@ -267,6 +267,15 @@ impl<'db> TypeVariableTable<'_, 'db> { self.storage.sub_unification_table.with_log(self.undo_log) } + /// Returns a range of the type variables created during the snapshot. + pub(crate) fn vars_since_snapshot( + &mut self, + value_count: usize, + ) -> (Range, Vec) { + let range = TyVid::from_usize(value_count)..TyVid::from_usize(self.num_vars()); + (range.clone(), iter_idx_range(range).map(|index| self.var_origin(index)).collect()) + } + /// Returns indices of all variables that are not yet /// instantiated. pub(crate) fn unresolved_variables(&mut self) -> Vec { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs new file mode 100644 index 000000000000..cab3c69118f6 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/inspect.rs @@ -0,0 +1,501 @@ +pub use ra_ap_rustc_next_trait_solver::solve::inspect::*; + +use rustc_ast_ir::try_visit; +use rustc_next_trait_solver::{ + resolve::eager_resolve_vars, + solve::{ + SolverDelegateEvalExt, + inspect::{self, instantiate_canonical_state}, + }, +}; +use rustc_type_ir::{ + VisitorResult, + inherent::{IntoKind, Span as _}, + solve::{Certainty, GoalSource, MaybeCause, NoSolution}, +}; + +use crate::next_solver::{ + DbInterner, GenericArg, GenericArgs, Goal, NormalizesTo, ParamEnv, Predicate, PredicateKind, + QueryResult, SolverContext, Span, Term, + fulfill::NextSolverError, + infer::{ + InferCtxt, + traits::{Obligation, ObligationCause}, + }, + obligation_ctxt::ObligationCtxt, +}; + +pub struct InspectConfig { + pub max_depth: usize, +} + +pub struct InspectGoal<'a, 'db> { + infcx: &'a SolverContext<'db>, + depth: usize, + orig_values: Vec>, + goal: Goal<'db, Predicate<'db>>, + result: Result, + final_revision: inspect::Probe>, + normalizes_to_term_hack: Option>, + source: GoalSource, +} + +impl<'a, 'db> std::fmt::Debug for InspectGoal<'a, 'db> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("InspectGoal") + .field("depth", &self.depth) + .field("orig_values", &self.orig_values) + .field("goal", &self.goal) + .field("result", &self.result) + .field("final_revision", &self.final_revision) + .field("normalizes_to_term_hack", &self.normalizes_to_term_hack) + .field("source", &self.source) + .finish() + } +} + +impl<'a, 'db> std::fmt::Debug for InspectCandidate<'a, 'db> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("InspectCandidate") + .field("kind", &self.kind) + .field("steps", &self.steps) + .field("final_state", &self.final_state) + .field("result", &self.result) + .field("shallow_certainty", &self.shallow_certainty) + .finish() + } +} + +/// The expected term of a `NormalizesTo` goal gets replaced +/// with an unconstrained inference variable when computing +/// `NormalizesTo` goals and we return the nested goals to the +/// caller, who also equates the actual term with the expected. +/// +/// This is an implementation detail of the trait solver and +/// not something we want to leak to users. We therefore +/// treat `NormalizesTo` goals as if they apply the expected +/// type at the end of each candidate. +#[derive(Debug, Copy, Clone)] +struct NormalizesToTermHack<'db> { + term: Term<'db>, + unconstrained_term: Term<'db>, +} + +impl<'db> NormalizesToTermHack<'db> { + /// Relate the `term` with the new `unconstrained_term` created + /// when computing the proof tree for this `NormalizesTo` goals. + /// This handles nested obligations. + fn constrain_and( + &self, + infcx: &InferCtxt<'db>, + param_env: ParamEnv<'db>, + f: impl FnOnce(&mut ObligationCtxt<'_, 'db>), + ) -> Result { + let mut ocx = ObligationCtxt::new(infcx); + ocx.eq(&ObligationCause::dummy(), param_env, self.term, self.unconstrained_term)?; + f(&mut ocx); + let errors = ocx.select_all_or_error(); + if errors.is_empty() { + Ok(Certainty::Yes) + } else if errors.iter().all(|e| !matches!(e, NextSolverError::TrueError(_))) { + Ok(Certainty::AMBIGUOUS) + } else { + Err(NoSolution) + } + } +} + +pub struct InspectCandidate<'a, 'db> { + goal: &'a InspectGoal<'a, 'db>, + kind: inspect::ProbeKind>, + steps: Vec<&'a inspect::ProbeStep>>, + final_state: inspect::CanonicalState, ()>, + result: QueryResult<'db>, + shallow_certainty: Certainty, +} + +impl<'a, 'db> InspectCandidate<'a, 'db> { + pub fn kind(&self) -> inspect::ProbeKind> { + self.kind + } + + pub fn result(&self) -> Result { + self.result.map(|c| c.value.certainty) + } + + pub fn goal(&self) -> &'a InspectGoal<'a, 'db> { + self.goal + } + + /// Certainty passed into `evaluate_added_goals_and_make_canonical_response`. + /// + /// If this certainty is `Yes`, then we must be confident that the candidate + /// must hold iff it's nested goals hold. This is not true if the certainty is + /// `Maybe(..)`, which suggests we forced ambiguity instead. + /// + /// This is *not* the certainty of the candidate's full nested evaluation, which + /// can be accessed with [`Self::result`] instead. + pub fn shallow_certainty(&self) -> Certainty { + self.shallow_certainty + } + + /// Visit all nested goals of this candidate without rolling + /// back their inference constraints. This function modifies + /// the state of the `infcx`. + pub fn visit_nested_no_probe>(&self, visitor: &mut V) -> V::Result { + for goal in self.instantiate_nested_goals() { + try_visit!(goal.visit_with(visitor)); + } + + V::Result::output() + } + + /// Instantiate the nested goals for the candidate without rolling back their + /// inference constraints. This function modifies the state of the `infcx`. + /// + /// See [`Self::instantiate_impl_args`] if you need the impl args too. + pub fn instantiate_nested_goals(&self) -> Vec> { + let infcx = self.goal.infcx; + let param_env = self.goal.goal.param_env; + let mut orig_values = self.goal.orig_values.to_vec(); + + let mut instantiated_goals = vec![]; + for step in &self.steps { + match **step { + inspect::ProbeStep::AddGoal(source, goal) => instantiated_goals.push(( + source, + instantiate_canonical_state( + infcx, + Span::dummy(), + param_env, + &mut orig_values, + goal, + ), + )), + inspect::ProbeStep::RecordImplArgs { .. } => {} + inspect::ProbeStep::MakeCanonicalResponse { .. } + | inspect::ProbeStep::NestedProbe(_) => unreachable!(), + } + } + + let () = instantiate_canonical_state( + infcx, + Span::dummy(), + param_env, + &mut orig_values, + self.final_state, + ); + + if let Some(term_hack) = &self.goal.normalizes_to_term_hack { + // FIXME: We ignore the expected term of `NormalizesTo` goals + // when computing the result of its candidates. This is + // scuffed. + let _ = term_hack.constrain_and(infcx, param_env, |_| {}); + } + + instantiated_goals + .into_iter() + .map(|(source, goal)| self.instantiate_proof_tree_for_nested_goal(source, goal)) + .collect() + } + + /// Instantiate the args of an impl if this candidate came from a + /// `CandidateSource::Impl`. This function modifies the state of the + /// `infcx`. + pub fn instantiate_impl_args(&self) -> GenericArgs<'db> { + let infcx = self.goal.infcx; + let param_env = self.goal.goal.param_env; + let mut orig_values = self.goal.orig_values.to_vec(); + + for step in &self.steps { + match **step { + inspect::ProbeStep::RecordImplArgs { impl_args } => { + let impl_args = instantiate_canonical_state( + infcx, + Span::dummy(), + param_env, + &mut orig_values, + impl_args, + ); + + let () = instantiate_canonical_state( + infcx, + Span::dummy(), + param_env, + &mut orig_values, + self.final_state, + ); + + // No reason we couldn't support this, but we don't need to for select. + assert!( + self.goal.normalizes_to_term_hack.is_none(), + "cannot use `instantiate_impl_args` with a `NormalizesTo` goal" + ); + + return eager_resolve_vars(infcx, impl_args); + } + inspect::ProbeStep::AddGoal(..) => {} + inspect::ProbeStep::MakeCanonicalResponse { .. } + | inspect::ProbeStep::NestedProbe(_) => unreachable!(), + } + } + + panic!("expected impl args probe step for `instantiate_impl_args`"); + } + + pub fn instantiate_proof_tree_for_nested_goal( + &self, + source: GoalSource, + goal: Goal<'db, Predicate<'db>>, + ) -> InspectGoal<'a, 'db> { + let infcx = self.goal.infcx; + match goal.predicate.kind().no_bound_vars() { + Some(PredicateKind::NormalizesTo(NormalizesTo { alias, term })) => { + let unconstrained_term = infcx.next_term_var_of_kind(term); + let goal = + goal.with(infcx.interner, NormalizesTo { alias, term: unconstrained_term }); + // We have to use a `probe` here as evaluating a `NormalizesTo` can constrain the + // expected term. This means that candidates which only fail due to nested goals + // and which normalize to a different term then the final result could ICE: when + // building their proof tree, the expected term was unconstrained, but when + // instantiating the candidate it is already constrained to the result of another + // candidate. + let normalizes_to_term_hack = NormalizesToTermHack { term, unconstrained_term }; + let (proof_tree, nested_goals_result) = infcx.probe(|_| { + // Here, if we have any nested goals, then we make sure to apply them + // considering the constrained RHS, and pass the resulting certainty to + // `InspectGoal::new` so that the goal has the right result (and maintains + // the impression that we don't do this normalizes-to infer hack at all). + let (nested, proof_tree) = + infcx.evaluate_root_goal_for_proof_tree(goal, Span::dummy()); + let nested_goals_result = nested.and_then(|nested| { + normalizes_to_term_hack.constrain_and( + infcx, + proof_tree.uncanonicalized_goal.param_env, + |ocx| { + ocx.register_obligations(nested.0.into_iter().map(|(_, goal)| { + Obligation::new( + infcx.interner, + ObligationCause::dummy(), + goal.param_env, + goal.predicate, + ) + })); + }, + ) + }); + (proof_tree, nested_goals_result) + }); + InspectGoal::new( + infcx, + self.goal.depth + 1, + proof_tree, + Some((normalizes_to_term_hack, nested_goals_result)), + source, + ) + } + _ => { + // We're using a probe here as evaluating a goal could constrain + // inference variables by choosing one candidate. If we then recurse + // into another candidate who ends up with different inference + // constraints, we get an ICE if we already applied the constraints + // from the chosen candidate. + let proof_tree = + infcx.probe(|_| infcx.evaluate_root_goal_for_proof_tree(goal, Span::dummy()).1); + InspectGoal::new(infcx, self.goal.depth + 1, proof_tree, None, source) + } + } + } + + /// Visit all nested goals of this candidate, rolling back + /// all inference constraints. + pub fn visit_nested_in_probe>(&self, visitor: &mut V) -> V::Result { + self.goal.infcx.probe(|_| self.visit_nested_no_probe(visitor)) + } +} + +impl<'a, 'db> InspectGoal<'a, 'db> { + pub fn infcx(&self) -> &'a InferCtxt<'db> { + self.infcx + } + + pub fn goal(&self) -> Goal<'db, Predicate<'db>> { + self.goal + } + + pub fn result(&self) -> Result { + self.result + } + + pub fn source(&self) -> GoalSource { + self.source + } + + pub fn depth(&self) -> usize { + self.depth + } + + fn candidates_recur( + &'a self, + candidates: &mut Vec>, + steps: &mut Vec<&'a inspect::ProbeStep>>, + probe: &'a inspect::Probe>, + ) { + let mut shallow_certainty = None; + for step in &probe.steps { + match *step { + inspect::ProbeStep::AddGoal(..) | inspect::ProbeStep::RecordImplArgs { .. } => { + steps.push(step) + } + inspect::ProbeStep::MakeCanonicalResponse { shallow_certainty: c } => { + assert!(matches!( + shallow_certainty.replace(c), + None | Some(Certainty::Maybe(MaybeCause::Ambiguity)) + )); + } + inspect::ProbeStep::NestedProbe(ref probe) => { + match probe.kind { + // These never assemble candidates for the goal we're trying to solve. + inspect::ProbeKind::ProjectionCompatibility + | inspect::ProbeKind::ShadowedEnvProbing => continue, + + inspect::ProbeKind::NormalizedSelfTyAssembly + | inspect::ProbeKind::UnsizeAssembly + | inspect::ProbeKind::Root { .. } + | inspect::ProbeKind::TraitCandidate { .. } + | inspect::ProbeKind::OpaqueTypeStorageLookup { .. } + | inspect::ProbeKind::RigidAlias { .. } => { + // Nested probes have to prove goals added in their parent + // but do not leak them, so we truncate the added goals + // afterwards. + let num_steps = steps.len(); + self.candidates_recur(candidates, steps, probe); + steps.truncate(num_steps); + } + } + } + } + } + + match probe.kind { + inspect::ProbeKind::ProjectionCompatibility + | inspect::ProbeKind::ShadowedEnvProbing => { + panic!() + } + + inspect::ProbeKind::NormalizedSelfTyAssembly | inspect::ProbeKind::UnsizeAssembly => {} + + // We add a candidate even for the root evaluation if there + // is only one way to prove a given goal, e.g. for `WellFormed`. + inspect::ProbeKind::Root { result } + | inspect::ProbeKind::TraitCandidate { source: _, result } + | inspect::ProbeKind::OpaqueTypeStorageLookup { result } + | inspect::ProbeKind::RigidAlias { result } => { + // We only add a candidate if `shallow_certainty` was set, which means + // that we ended up calling `evaluate_added_goals_and_make_canonical_response`. + if let Some(shallow_certainty) = shallow_certainty { + candidates.push(InspectCandidate { + goal: self, + kind: probe.kind, + steps: steps.clone(), + final_state: probe.final_state, + shallow_certainty, + result, + }); + } + } + } + } + + pub fn candidates(&'a self) -> Vec> { + let mut candidates = vec![]; + let mut nested_goals = vec![]; + self.candidates_recur(&mut candidates, &mut nested_goals, &self.final_revision); + candidates + } + + /// Returns the single candidate applicable for the current goal, if it exists. + /// + /// Returns `None` if there are either no or multiple applicable candidates. + pub fn unique_applicable_candidate(&'a self) -> Option> { + // FIXME(-Znext-solver): This does not handle impl candidates + // hidden by env candidates. + let mut candidates = self.candidates(); + candidates.retain(|c| c.result().is_ok()); + candidates.pop().filter(|_| candidates.is_empty()) + } + + fn new( + infcx: &'a InferCtxt<'db>, + depth: usize, + root: inspect::GoalEvaluation>, + term_hack_and_nested_certainty: Option<( + NormalizesToTermHack<'db>, + Result, + )>, + source: GoalSource, + ) -> Self { + let infcx = <&SolverContext<'db>>::from(infcx); + + let inspect::GoalEvaluation { uncanonicalized_goal, orig_values, final_revision, result } = + root; + // If there's a normalizes-to goal, AND the evaluation result with the result of + // constraining the normalizes-to RHS and computing the nested goals. + let result = result.and_then(|ok| { + let nested_goals_certainty = + term_hack_and_nested_certainty.map_or(Ok(Certainty::Yes), |(_, c)| c)?; + Ok(ok.value.certainty.and(nested_goals_certainty)) + }); + + InspectGoal { + infcx, + depth, + orig_values, + goal: eager_resolve_vars(infcx, uncanonicalized_goal), + result, + final_revision, + normalizes_to_term_hack: term_hack_and_nested_certainty.map(|(n, _)| n), + source, + } + } + + pub(crate) fn visit_with>(&self, visitor: &mut V) -> V::Result { + if self.depth < visitor.config().max_depth { + try_visit!(visitor.visit_goal(self)); + } + + V::Result::output() + } +} + +/// The public API to interact with proof trees. +pub trait ProofTreeVisitor<'db> { + type Result: VisitorResult; + + fn config(&self) -> InspectConfig { + InspectConfig { max_depth: 10 } + } + + fn visit_goal(&mut self, goal: &InspectGoal<'_, 'db>) -> Self::Result; +} + +impl<'db> InferCtxt<'db> { + pub(crate) fn visit_proof_tree>( + &self, + goal: Goal<'db, Predicate<'db>>, + visitor: &mut V, + ) -> V::Result { + self.visit_proof_tree_at_depth(goal, 0, visitor) + } + + pub(crate) fn visit_proof_tree_at_depth>( + &self, + goal: Goal<'db, Predicate<'db>>, + depth: usize, + visitor: &mut V, + ) -> V::Result { + let (_, proof_tree) = <&SolverContext<'db>>::from(self) + .evaluate_root_goal_for_proof_tree(goal, Span::dummy()); + visitor.visit_goal(&InspectGoal::new(self, depth, proof_tree, None, GoalSource::Misc)) + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 7b6e8a1073d7..8f731b06fa19 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -48,8 +48,8 @@ use crate::next_solver::infer::InferCtxt; use crate::next_solver::util::{ContainsTypeErrors, explicit_item_bounds, for_trait_impls}; use crate::next_solver::{ AdtIdWrapper, BoundConst, CallableIdWrapper, CanonicalVarKind, ClosureIdWrapper, - CoroutineIdWrapper, FxIndexMap, ImplIdWrapper, InternedWrapperNoDebug, RegionAssumptions, - SolverContext, SolverDefIds, TraitIdWrapper, TypeAliasIdWrapper, + CoroutineIdWrapper, Ctor, FnSig, FxIndexMap, ImplIdWrapper, InternedWrapperNoDebug, + RegionAssumptions, SolverContext, SolverDefIds, TraitIdWrapper, TypeAliasIdWrapper, }; use crate::{ConstScalar, FnAbi, Interner, db::HirDatabase}; @@ -1055,60 +1055,62 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { } fn variances_of(self, def_id: Self::DefId) -> Self::VariancesOf { - match def_id { - SolverDefId::FunctionId(def_id) => VariancesOf::new_from_iter( - self, - self.db() - .variances_of(hir_def::GenericDefId::FunctionId(def_id)) - .as_deref() - .unwrap_or_default() - .iter() - .map(|v| v.to_nextsolver(self)), - ), - SolverDefId::AdtId(def_id) => VariancesOf::new_from_iter( - self, - self.db() - .variances_of(hir_def::GenericDefId::AdtId(def_id)) - .as_deref() - .unwrap_or_default() - .iter() - .map(|v| v.to_nextsolver(self)), - ), + let generic_def = match def_id { + SolverDefId::FunctionId(def_id) => def_id.into(), + SolverDefId::AdtId(def_id) => def_id.into(), + SolverDefId::Ctor(Ctor::Struct(def_id)) => def_id.into(), + SolverDefId::Ctor(Ctor::Enum(def_id)) => def_id.loc(self.db).parent.into(), SolverDefId::InternedOpaqueTyId(_def_id) => { // FIXME(next-solver): track variances // // We compute them based on the only `Ty` level info in rustc, // move `variances_of_opaque` into `rustc_next_trait_solver` for reuse. - VariancesOf::new_from_iter( + return VariancesOf::new_from_iter( self, (0..self.generics_of(def_id).count()).map(|_| Variance::Invariant), - ) + ); } - _ => VariancesOf::new_from_iter(self, []), - } + _ => return VariancesOf::new_from_iter(self, []), + }; + VariancesOf::new_from_iter( + self, + self.db() + .variances_of(generic_def) + .as_deref() + .unwrap_or_default() + .iter() + .map(|v| v.to_nextsolver(self)), + ) } fn type_of(self, def_id: Self::DefId) -> EarlyBinder { - let def_id = match def_id { + match def_id { SolverDefId::TypeAliasId(id) => { use hir_def::Lookup; match id.lookup(self.db()).container { ItemContainerId::ImplId(it) => it, _ => panic!("assoc ty value should be in impl"), }; - crate::TyDefId::TypeAliasId(id) + self.db().ty_ns(id.into()) } - SolverDefId::AdtId(id) => crate::TyDefId::AdtId(id), + SolverDefId::AdtId(id) => self.db().ty_ns(id.into()), // FIXME(next-solver): This uses the types of `query mir_borrowck` in rustc. // // We currently always use the type from HIR typeck which ignores regions. This // should be fine. - SolverDefId::InternedOpaqueTyId(_) => { - return self.type_of_opaque_hir_typeck(def_id); + SolverDefId::InternedOpaqueTyId(_) => self.type_of_opaque_hir_typeck(def_id), + SolverDefId::FunctionId(id) => self.db.value_ty_ns(id.into()).unwrap(), + SolverDefId::Ctor(id) => { + let id = match id { + Ctor::Struct(id) => id.into(), + Ctor::Enum(id) => id.into(), + }; + self.db + .value_ty_ns(id) + .expect("`SolverDefId::Ctor` should have a function-like ctor") } _ => panic!("Unexpected def_id `{def_id:?}` provided for `type_of`"), - }; - self.db().ty_ns(def_id) + } } fn adt_def(self, def_id: Self::AdtId) -> Self::AdtDef { @@ -1998,6 +2000,28 @@ impl<'db> DbInterner<'db> { ) -> T { self.replace_escaping_bound_vars_uncached(value.skip_binder(), delegate) } + + pub fn mk_fn_sig( + self, + inputs: I, + output: Ty<'db>, + c_variadic: bool, + safety: Safety, + abi: FnAbi, + ) -> FnSig<'db> + where + I: IntoIterator>, + { + FnSig { + inputs_and_output: Tys::new_from_iter( + self, + inputs.into_iter().chain(std::iter::once(output)), + ), + c_variadic, + safety, + abi, + } + } } macro_rules! TrivialTypeTraversalImpls { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index eb2cc69167d0..d8a86ea083e0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -147,6 +147,24 @@ pub trait NextSolverToChalk<'db, Out> { fn to_chalk(self, interner: DbInterner<'db>) -> Out; } +impl NextSolverToChalk<'_, chalk_ir::Mutability> for rustc_ast_ir::Mutability { + fn to_chalk(self, interner: DbInterner<'_>) -> chalk_ir::Mutability { + match self { + rustc_ast_ir::Mutability::Not => chalk_ir::Mutability::Not, + rustc_ast_ir::Mutability::Mut => chalk_ir::Mutability::Mut, + } + } +} + +impl NextSolverToChalk<'_, chalk_ir::Safety> for crate::next_solver::abi::Safety { + fn to_chalk(self, interner: DbInterner<'_>) -> chalk_ir::Safety { + match self { + crate::next_solver::abi::Safety::Unsafe => chalk_ir::Safety::Unsafe, + crate::next_solver::abi::Safety::Safe => chalk_ir::Safety::Safe, + } + } +} + impl<'db> ChalkToNextSolver<'db, Ty<'db>> for chalk_ir::Ty { fn to_nextsolver(&self, interner: DbInterner<'db>) -> Ty<'db> { Ty::new( @@ -617,6 +635,15 @@ impl<'db> ChalkToNextSolver<'db, Tys<'db>> for chalk_ir::Substitution } } +impl<'db> NextSolverToChalk<'db, crate::Substitution> for Tys<'db> { + fn to_chalk(self, interner: DbInterner<'db>) -> crate::Substitution { + Substitution::from_iter( + Interner, + self.inner().iter().map(|ty| ty.to_chalk(interner).cast(Interner)), + ) + } +} + impl<'db> ChalkToNextSolver<'db, rustc_type_ir::DebruijnIndex> for chalk_ir::DebruijnIndex { fn to_nextsolver(&self, _interner: DbInterner<'db>) -> rustc_type_ir::DebruijnIndex { rustc_type_ir::DebruijnIndex::from_u32(self.depth()) @@ -859,6 +886,16 @@ impl<'db> NextSolverToChalk<'db, chalk_ir::Goal> for Predicate<'db> { } } +impl<'db> NextSolverToChalk<'db, crate::ProjectionTy> for crate::next_solver::AliasTy<'db> { + fn to_chalk(self, interner: DbInterner<'db>) -> crate::ProjectionTy { + let SolverDefId::TypeAliasId(assoc_id) = self.def_id else { unreachable!() }; + crate::ProjectionTy { + associated_ty_id: to_assoc_type_id(assoc_id), + substitution: self.args.to_chalk(interner), + } + } +} + impl<'db> ChalkToNextSolver<'db, ParamEnv<'db>> for chalk_ir::Environment { fn to_nextsolver(&self, interner: DbInterner<'db>) -> ParamEnv<'db> { let clauses = Clauses::new_from_iter( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project/solve_normalize.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/normalize.rs similarity index 87% rename from src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project/solve_normalize.rs rename to src/tools/rust-analyzer/crates/hir-ty/src/next_solver/normalize.rs index 42c238febf6f..41cb4884404f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project/solve_normalize.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/normalize.rs @@ -1,35 +1,23 @@ -//! Normalization within a next-solver infer context. - -use std::fmt::Debug; - use rustc_next_trait_solver::placeholder::BoundVarReplacer; use rustc_type_ir::{ AliasRelationDirection, FallibleTypeFolder, Flags, Interner, TermKind, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, UniverseIndex, - inherent::{IntoKind, Span as _, Term as _}, + inherent::{IntoKind, Term as _}, }; +use crate::next_solver::SolverDefId; use crate::next_solver::{ - Binder, Const, ConstKind, DbInterner, Goal, ParamEnv, Predicate, PredicateKind, Span, Term, Ty, - TyKind, TypingMode, + Binder, Const, ConstKind, DbInterner, Goal, ParamEnv, Predicate, PredicateKind, Term, Ty, + TyKind, fulfill::{FulfillmentCtxt, NextSolverError}, infer::{ - DbInternerInferExt, InferCtxt, + InferCtxt, at::At, traits::{Obligation, ObligationCause}, }, util::PlaceholderReplacer, }; -pub fn normalize<'db, T>(interner: DbInterner<'db>, param_env: ParamEnv<'db>, value: T) -> T -where - T: TypeFoldable>, -{ - let infer_ctxt = interner.infer_ctxt().build(TypingMode::non_body_analysis()); - let cause = ObligationCause::dummy(); - deeply_normalize(infer_ctxt.at(&cause, param_env), value.clone()).unwrap_or(value) -} - /// Deeply normalize all aliases in `value`. This does not handle inference and expects /// its input to be already fully resolved. pub fn deeply_normalize<'db, T>(at: At<'_, 'db>, value: T) -> Result>> @@ -81,10 +69,16 @@ where T: TypeFoldable>, { let fulfill_cx = FulfillmentCtxt::new(at.infcx); - let mut folder = NormalizationFolder { at, fulfill_cx, depth: 0, universes }; + let mut folder = NormalizationFolder { + at, + fulfill_cx, + depth: 0, + universes, + stalled_coroutine_goals: vec![], + }; let value = value.try_fold_with(&mut folder)?; let errors = folder.fulfill_cx.select_all_or_error(at.infcx); - if errors.is_empty() { Ok((value, vec![])) } else { Err(errors) } + if errors.is_empty() { Ok((value, folder.stalled_coroutine_goals)) } else { Err(errors) } } struct NormalizationFolder<'me, 'db> { @@ -92,6 +86,7 @@ struct NormalizationFolder<'me, 'db> { fulfill_cx: FulfillmentCtxt<'db>, depth: usize, universes: Vec>, + stalled_coroutine_goals: Vec>>, } impl<'db> NormalizationFolder<'_, 'db> { @@ -100,22 +95,30 @@ impl<'db> NormalizationFolder<'_, 'db> { alias_term: Term<'db>, ) -> Result, Vec>> { let infcx = self.at.infcx; - let tcx = infcx.interner; - let recursion_limit = tcx.recursion_limit(); - if self.depth > recursion_limit { - return Err(vec![]); - } + let interner = infcx.interner; + let recursion_limit = interner.recursion_limit(); self.depth += 1; let infer_term = infcx.next_term_var_of_kind(alias_term); let obligation = Obligation::new( - tcx, + interner, self.at.cause.clone(), self.at.param_env, PredicateKind::AliasRelate(alias_term, infer_term, AliasRelationDirection::Equate), ); + if self.depth > recursion_limit { + // let term = alias_term.to_alias_term().unwrap(); + // self.at.infcx.err_ctxt().report_overflow_error( + // OverflowCause::DeeplyNormalize(term), + // self.at.cause.span, + // true, + // |_| {}, + // ); + return Err(vec![NextSolverError::Overflow(obligation)]); + } + self.fulfill_cx.register_predicate_obligation(infcx, obligation); self.select_all_and_stall_coroutine_predicates()?; @@ -140,6 +143,13 @@ impl<'db> NormalizationFolder<'_, 'db> { return Err(errors); } + self.stalled_coroutine_goals.extend( + self.fulfill_cx + .drain_stalled_obligations_for_coroutines(self.at.infcx) + .into_iter() + .map(|obl| obl.as_goal()), + ); + let errors = self.fulfill_cx.collect_remaining_errors(self.at.infcx); if !errors.is_empty() { return Err(errors); @@ -166,7 +176,6 @@ impl<'db> FallibleTypeFolder> for NormalizationFolder<'_, 'db> { Ok(t) } - #[tracing::instrument(level = "trace", skip(self), ret)] fn try_fold_ty(&mut self, ty: Ty<'db>) -> Result, Self::Error> { let infcx = self.at.infcx; debug_assert_eq!(ty, infcx.shallow_resolve(ty)); @@ -193,7 +202,6 @@ impl<'db> FallibleTypeFolder> for NormalizationFolder<'_, 'db> { } } - #[tracing::instrument(level = "trace", skip(self), ret)] fn try_fold_const(&mut self, ct: Const<'db>) -> Result, Self::Error> { let infcx = self.at.infcx; debug_assert_eq!(ct, infcx.shallow_resolve_const(ct)); @@ -232,8 +240,8 @@ pub(crate) fn deeply_normalize_for_diagnostics<'db, T: TypeFoldable { - at: At<'a, 'db>, +struct DeeplyNormalizeForDiagnosticsFolder<'a, 'tcx> { + at: At<'a, 'tcx>, } impl<'db> TypeFolder> for DeeplyNormalizeForDiagnosticsFolder<'_, 'db> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/obligation_ctxt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/obligation_ctxt.rs new file mode 100644 index 000000000000..8e2dc0dec4ed --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/obligation_ctxt.rs @@ -0,0 +1,203 @@ +use hir_def::TraitId; +use rustc_type_ir::relate::Relate; +use rustc_type_ir::{TypeFoldable, Upcast, Variance}; + +use crate::next_solver::fulfill::{FulfillmentCtxt, NextSolverError}; +use crate::next_solver::infer::at::ToTrace; +use crate::next_solver::infer::traits::{ + Obligation, ObligationCause, PredicateObligation, PredicateObligations, +}; +use crate::next_solver::infer::{DefineOpaqueTypes, InferCtxt, InferOk, TypeTrace}; +use crate::next_solver::{Const, DbInterner, ParamEnv, Term, TraitRef, Ty, TypeError}; + +/// Used if you want to have pleasant experience when dealing +/// with obligations outside of hir or mir typeck. +pub struct ObligationCtxt<'a, 'db> { + pub infcx: &'a InferCtxt<'db>, + engine: FulfillmentCtxt<'db>, +} + +impl<'a, 'db> ObligationCtxt<'a, 'db> { + pub fn new(infcx: &'a InferCtxt<'db>) -> Self { + Self { infcx, engine: FulfillmentCtxt::new(infcx) } + } +} + +impl<'a, 'db> ObligationCtxt<'a, 'db> { + pub fn register_obligation(&mut self, obligation: PredicateObligation<'db>) { + self.engine.register_predicate_obligation(self.infcx, obligation); + } + + pub fn register_obligations( + &mut self, + obligations: impl IntoIterator>, + ) { + self.engine.register_predicate_obligations(self.infcx, obligations); + } + + pub fn register_infer_ok_obligations(&mut self, infer_ok: InferOk<'db, T>) -> T { + let InferOk { value, obligations } = infer_ok; + self.register_obligations(obligations); + value + } + + /// Requires that `ty` must implement the trait with `def_id` in + /// the given environment. This trait must not have any type + /// parameters (except for `Self`). + pub fn register_bound( + &mut self, + cause: ObligationCause, + param_env: ParamEnv<'db>, + ty: Ty<'db>, + def_id: TraitId, + ) { + let trait_ref = TraitRef::new(self.infcx.interner, def_id.into(), [ty]); + self.register_obligation(Obligation { + cause, + recursion_depth: 0, + param_env, + predicate: trait_ref.upcast(self.infcx.interner), + }); + } + + pub fn eq>( + &mut self, + cause: &ObligationCause, + param_env: ParamEnv<'db>, + expected: T, + actual: T, + ) -> Result<(), TypeError<'db>> { + self.infcx + .at(cause, param_env) + .eq(DefineOpaqueTypes::Yes, expected, actual) + .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) + } + + pub fn eq_trace>>( + &mut self, + cause: &ObligationCause, + param_env: ParamEnv<'db>, + trace: TypeTrace<'db>, + expected: T, + actual: T, + ) -> Result<(), TypeError<'db>> { + self.infcx + .at(cause, param_env) + .eq_trace(DefineOpaqueTypes::Yes, trace, expected, actual) + .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) + } + + /// Checks whether `expected` is a subtype of `actual`: `expected <: actual`. + pub fn sub>( + &mut self, + cause: &ObligationCause, + param_env: ParamEnv<'db>, + expected: T, + actual: T, + ) -> Result<(), TypeError<'db>> { + self.infcx + .at(cause, param_env) + .sub(DefineOpaqueTypes::Yes, expected, actual) + .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) + } + + pub fn relate>( + &mut self, + cause: &ObligationCause, + param_env: ParamEnv<'db>, + variance: Variance, + expected: T, + actual: T, + ) -> Result<(), TypeError<'db>> { + self.infcx + .at(cause, param_env) + .relate(DefineOpaqueTypes::Yes, expected, variance, actual) + .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) + } + + /// Checks whether `expected` is a supertype of `actual`: `expected :> actual`. + pub fn sup>( + &mut self, + cause: &ObligationCause, + param_env: ParamEnv<'db>, + expected: T, + actual: T, + ) -> Result<(), TypeError<'db>> { + self.infcx + .at(cause, param_env) + .sup(DefineOpaqueTypes::Yes, expected, actual) + .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) + } + + /// Computes the least-upper-bound, or mutual supertype, of two values. + pub fn lub>( + &mut self, + cause: &ObligationCause, + param_env: ParamEnv<'db>, + expected: T, + actual: T, + ) -> Result> { + self.infcx + .at(cause, param_env) + .lub(expected, actual) + .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) + } + + #[must_use] + pub fn select_where_possible(&mut self) -> Vec> { + self.engine.select_where_possible(self.infcx) + } + + #[must_use] + pub fn select_all_or_error(&mut self) -> Vec> { + self.engine.select_all_or_error(self.infcx) + } + + /// Returns the not-yet-processed and stalled obligations from the + /// `ObligationCtxt`. + /// + /// Takes ownership of the context as doing operations such as + /// [`ObligationCtxt::eq`] afterwards will result in other obligations + /// getting ignored. You can make a new `ObligationCtxt` if this + /// needs to be done in a loop, for example. + #[must_use] + pub fn into_pending_obligations(self) -> PredicateObligations<'db> { + self.engine.pending_obligations() + } + + pub fn deeply_normalize>>( + &self, + cause: &ObligationCause, + param_env: ParamEnv<'db>, + value: T, + ) -> Result>> { + self.infcx.at(cause, param_env).deeply_normalize(value) + } + + pub fn structurally_normalize_ty( + &mut self, + cause: &ObligationCause, + param_env: ParamEnv<'db>, + value: Ty<'db>, + ) -> Result, Vec>> { + self.infcx.at(cause, param_env).structurally_normalize_ty(value, &mut self.engine) + } + + pub fn structurally_normalize_const( + &mut self, + cause: &ObligationCause, + param_env: ParamEnv<'db>, + value: Const<'db>, + ) -> Result, Vec>> { + self.infcx.at(cause, param_env).structurally_normalize_const(value, &mut self.engine) + } + + pub fn structurally_normalize_term( + &mut self, + cause: &ObligationCause, + param_env: ParamEnv<'db>, + value: Term<'db>, + ) -> Result, Vec>> { + self.infcx.at(cause, param_env).structurally_normalize_term(value, &mut self.engine) + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs index c86d3a4aadb2..99b1354b6335 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs @@ -262,28 +262,6 @@ impl<'db> Predicate<'db> { Some(Predicate::new(DbInterner::conjure(), kind)) } - - pub fn as_trait_clause(self) -> Option> { - let predicate = self.kind(); - match predicate.skip_binder() { - PredicateKind::Clause(ClauseKind::Trait(t)) => Some(predicate.rebind(t)), - PredicateKind::Clause(ClauseKind::Projection(..)) - | PredicateKind::Clause(ClauseKind::HostEffect(..)) - | PredicateKind::Clause(ClauseKind::ConstArgHasType(..)) - | PredicateKind::Clause(ClauseKind::UnstableFeature(_)) - | PredicateKind::NormalizesTo(..) - | PredicateKind::AliasRelate(..) - | PredicateKind::Subtype(..) - | PredicateKind::Coerce(..) - | PredicateKind::Clause(ClauseKind::RegionOutlives(..)) - | PredicateKind::Clause(ClauseKind::WellFormed(..)) - | PredicateKind::DynCompatible(..) - | PredicateKind::Clause(ClauseKind::TypeOutlives(..)) - | PredicateKind::Clause(ClauseKind::ConstEvaluatable(..)) - | PredicateKind::ConstEquate(..) - | PredicateKind::Ambiguous => None, - } - } } // FIXME: should make a "header" in interned_vec @@ -693,6 +671,12 @@ impl<'db> UpcastFrom, ty::OutlivesPredicate, Reg } } +impl<'db> UpcastFrom, PolyRegionOutlivesPredicate<'db>> for Predicate<'db> { + fn upcast_from(from: PolyRegionOutlivesPredicate<'db>, tcx: DbInterner<'db>) -> Self { + from.map_bound(|p| PredicateKind::Clause(ClauseKind::RegionOutlives(p))).upcast(tcx) + } +} + impl<'db> rustc_type_ir::inherent::Predicate> for Predicate<'db> { fn as_clause(self) -> Option< as rustc_type_ir::Interner>::Clause> { match self.kind().skip_binder() { @@ -730,6 +714,30 @@ impl<'db> rustc_type_ir::inherent::Predicate> for Predicate<'db> } impl<'db> Predicate<'db> { + pub fn as_trait_clause(self) -> Option> { + let predicate = self.kind(); + match predicate.skip_binder() { + PredicateKind::Clause(ClauseKind::Trait(t)) => Some(predicate.rebind(t)), + _ => None, + } + } + + pub fn as_projection_clause(self) -> Option> { + let predicate = self.kind(); + match predicate.skip_binder() { + PredicateKind::Clause(ClauseKind::Projection(t)) => Some(predicate.rebind(t)), + _ => None, + } + } + + /// Matches a `PredicateKind::Clause` and turns it into a `Clause`, otherwise returns `None`. + pub fn as_clause(self) -> Option> { + match self.kind().skip_binder() { + PredicateKind::Clause(..) => Some(self.expect_clause()), + _ => None, + } + } + /// Assert that the predicate is a clause. pub fn expect_clause(self) -> Clause<'db> { match self.kind().skip_binder() { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project.rs deleted file mode 100644 index e57808728ae9..000000000000 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project.rs +++ /dev/null @@ -1,3 +0,0 @@ -//! Projection code for next-solver. - -pub(crate) mod solve_normalize; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs index c7591c0c7796..946e57e6cb74 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs @@ -2,6 +2,7 @@ use hir_def::{AssocItemId, GeneralConstId, TypeAliasId}; use rustc_next_trait_solver::delegate::SolverDelegate; +use rustc_type_ir::GenericArgKind; use rustc_type_ir::lang_items::SolverTraitLangItem; use rustc_type_ir::{ InferCtxtLike, Interner, PredicatePolarity, TypeFlags, TypeVisitableExt, UniverseIndex, @@ -65,12 +66,12 @@ impl<'db> SolverDelegate for SolverContext<'db> { (SolverContext(infcx), value, vars) } - fn fresh_var_for_kind_with_span( - &self, - arg: ::GenericArg, - span: ::Span, - ) -> ::GenericArg { - unimplemented!() + fn fresh_var_for_kind_with_span(&self, arg: GenericArg<'db>, span: Span) -> GenericArg<'db> { + match arg.kind() { + GenericArgKind::Lifetime(_) => self.next_region_var().into(), + GenericArgKind::Type(_) => self.next_ty_var().into(), + GenericArgKind::Const(_) => self.next_const_var().into(), + } } fn leak_check( @@ -92,7 +93,8 @@ impl<'db> SolverDelegate for SolverContext<'db> { >, >, > { - unimplemented!() + // FIXME(next-solver): + None } fn make_deduplicated_outlives_constraints( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/structural_normalize.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/structural_normalize.rs new file mode 100644 index 000000000000..18859d8b7970 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/structural_normalize.rs @@ -0,0 +1,57 @@ +use rustc_type_ir::{AliasRelationDirection, inherent::Term as _}; + +use crate::next_solver::{ + Const, PredicateKind, Term, Ty, + fulfill::{FulfillmentCtxt, NextSolverError}, + infer::{at::At, traits::Obligation}, +}; + +impl<'db> At<'_, 'db> { + pub(crate) fn structurally_normalize_ty( + &self, + ty: Ty<'db>, + fulfill_cx: &mut FulfillmentCtxt<'db>, + ) -> Result, Vec>> { + self.structurally_normalize_term(ty.into(), fulfill_cx).map(|term| term.expect_type()) + } + + pub(crate) fn structurally_normalize_const( + &self, + ct: Const<'db>, + fulfill_cx: &mut FulfillmentCtxt<'db>, + ) -> Result, Vec>> { + self.structurally_normalize_term(ct.into(), fulfill_cx).map(|term| term.expect_const()) + } + + pub(crate) fn structurally_normalize_term( + &self, + term: Term<'db>, + fulfill_cx: &mut FulfillmentCtxt<'db>, + ) -> Result, Vec>> { + assert!(!term.is_infer(), "should have resolved vars before calling"); + + if term.to_alias_term().is_none() { + return Ok(term); + } + + let new_infer = self.infcx.next_term_var_of_kind(term); + + // We simply emit an `alias-eq` goal here, since that will take care of + // normalizing the LHS of the projection until it is a rigid projection + // (or a not-yet-defined opaque in scope). + let obligation = Obligation::new( + self.infcx.interner, + self.cause.clone(), + self.param_env, + PredicateKind::AliasRelate(term, new_infer, AliasRelationDirection::Equate), + ); + + fulfill_cx.register_predicate_obligation(self.infcx, obligation); + let errors = fulfill_cx.select_where_possible(self.infcx); + if !errors.is_empty() { + return Err(errors); + } + + Ok(self.infcx.resolve_vars_if_possible(new_infer)) + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index e1c29160ad1a..e6c444d0d6be 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -1,17 +1,19 @@ //! Things related to tys in the next-trait-solver. +use std::iter; +use std::ops::ControlFlow; + use hir_def::{GenericDefId, TypeOrConstParamId, TypeParamId}; use intern::{Interned, Symbol, sym}; use rustc_abi::{Float, Integer, Size}; use rustc_ast_ir::{Mutability, try_visit, visit::VisitorResult}; -use rustc_type_ir::Interner; use rustc_type_ir::{ - BoundVar, ClosureKind, FlagComputation, Flags, FloatTy, FloatVid, InferTy, IntTy, IntVid, - TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, UintTy, - WithCachedTypeInfo, + BoundVar, ClosureKind, CollectAndApply, FlagComputation, Flags, FloatTy, FloatVid, InferTy, + IntTy, IntVid, Interner, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt, TypeVisitor, UintTy, WithCachedTypeInfo, inherent::{ - AdtDef, BoundVarLike, GenericArgs as _, IntoKind, ParamLike, PlaceholderLike, SliceLike, - Ty as _, + Abi, AdtDef, BoundVarLike, Const as _, GenericArgs as _, IntoKind, ParamLike, + PlaceholderLike, Safety as _, SliceLike, Ty as _, }, relate::Relate, solve::SizedTraitKind, @@ -20,13 +22,16 @@ use rustc_type_ir::{ use salsa::plumbing::{AsId, FromId}; use smallvec::SmallVec; -use crate::next_solver::{ - CallableIdWrapper, ClosureIdWrapper, CoroutineIdWrapper, GenericArg, TypeAliasIdWrapper, -}; use crate::{ + FnAbi, db::HirDatabase, interner::InternedWrapperNoDebug, - next_solver::util::{CoroutineArgsExt, IntegerTypeExt}, + next_solver::{ + CallableIdWrapper, ClosureIdWrapper, Const, CoroutineIdWrapper, FnSig, GenericArg, + PolyFnSig, TypeAliasIdWrapper, + abi::Safety, + util::{CoroutineArgsExt, IntegerTypeExt}, + }, }; use super::{ @@ -310,6 +315,68 @@ impl<'db> Ty<'db> { | TyKind::Error(_) => false, } } + + #[inline] + pub fn is_never(self) -> bool { + matches!(self.kind(), TyKind::Never) + } + + #[inline] + pub fn is_infer(self) -> bool { + matches!(self.kind(), TyKind::Infer(..)) + } + + #[inline] + pub fn is_str(self) -> bool { + matches!(self.kind(), TyKind::Str) + } + + #[inline] + pub fn is_unit(self) -> bool { + matches!(self.kind(), TyKind::Tuple(tys) if tys.inner().is_empty()) + } + + /// Given a `fn` type, returns an equivalent `unsafe fn` type; + /// that is, a `fn` type that is equivalent in every way for being + /// unsafe. + pub fn safe_to_unsafe_fn_ty(interner: DbInterner<'db>, sig: PolyFnSig<'db>) -> Ty<'db> { + assert!(sig.safety().is_safe()); + Ty::new_fn_ptr(interner, sig.map_bound(|sig| FnSig { safety: Safety::Unsafe, ..sig })) + } + + /// Returns the type of `*ty`. + /// + /// The parameter `explicit` indicates if this is an *explicit* dereference. + /// Some types -- notably raw ptrs -- can only be dereferenced explicitly. + pub fn builtin_deref(self, db: &dyn HirDatabase, explicit: bool) -> Option> { + match self.kind() { + TyKind::Adt(adt, substs) if crate::lang_items::is_box(db, adt.def_id().0) => { + Some(substs.as_slice()[0].expect_ty()) + } + TyKind::Ref(_, ty, _) => Some(ty), + TyKind::RawPtr(ty, _) if explicit => Some(ty), + _ => None, + } + } + + /// Whether the type contains some non-lifetime, aka. type or const, error type. + pub fn references_non_lt_error(self) -> bool { + self.references_error() && self.visit_with(&mut ReferencesNonLifetimeError).is_break() + } +} + +struct ReferencesNonLifetimeError; + +impl<'db> TypeVisitor> for ReferencesNonLifetimeError { + type Result = ControlFlow<()>; + + fn visit_ty(&mut self, ty: Ty<'db>) -> Self::Result { + if ty.is_ty_error() { ControlFlow::Break(()) } else { ty.super_visit_with(self) } + } + + fn visit_const(&mut self, c: Const<'db>) -> Self::Result { + if c.is_ct_error() { ControlFlow::Break(()) } else { c.super_visit_with(self) } + } } impl<'db> std::fmt::Debug for Ty<'db> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs index 97d3ea72c93c..a50f20a565ee 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs @@ -409,8 +409,7 @@ pub(crate) fn for_trait_impls( // Note: Since we're using `impls_for_trait` and `impl_provided_for`, // only impls where the trait can be resolved should ever reach Chalk. // `impl_datum` relies on that and will panic if the trait can't be resolved. - let in_deps = db.trait_impls_in_deps(krate); - let in_self = db.trait_impls_in_crate(krate); + let in_self_and_deps = db.trait_impls_in_deps(krate); let trait_module = trait_id.module(db); let type_module = match self_ty_fp { Some(TyFingerprint::Adt(adt_id)) => Some(adt_id.module(db)), @@ -435,8 +434,7 @@ pub(crate) fn for_trait_impls( }); }) .filter_map(|block_id| db.trait_impls_in_block(block_id)); - f(&in_self)?; - for it in in_deps.iter().map(ops::Deref::deref) { + for it in in_self_and_deps.iter().map(ops::Deref::deref) { f(it)?; } for it in block_impls { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/target_feature.rs b/src/tools/rust-analyzer/crates/hir-ty/src/target_feature.rs index 9d1238701bcf..0a8ed2cf0cab 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/target_feature.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/target_feature.rs @@ -7,7 +7,7 @@ use hir_def::tt; use intern::{Symbol, sym}; use rustc_hash::{FxHashMap, FxHashSet}; -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone)] pub struct TargetFeatures { pub(crate) enabled: FxHashSet, } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs index 7ec5231a7341..1735f550b8ad 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs @@ -177,21 +177,23 @@ fn test(i: i32) { #[test] fn coerce_merge_one_by_one1() { - cov_mark::check!(coerce_merge_fail_fallback); - check( r" fn test() { let t = &mut 1; let x = match 1 { 1 => t as *mut i32, - //^^^^^^^^^^^^^ adjustments: Pointer(MutToConstPointer) + //^ adjustments: Deref(None), Borrow(RawPtr(Mut)) + _ => t as *const i32, + }; + x; + // ^ type: *const i32 + let x = match 1 { + 1 => t as *mut i32, 2 => t as &i32, //^^^^^^^^^ expected *mut i32, got &'? i32 _ => t as *const i32, }; - x; - //^ type: *const i32 } ", @@ -276,17 +278,19 @@ fn test() { fn coerce_autoderef_implication_1() { check_no_mismatches( r" -//- minicore: deref -struct Foo; +//- minicore: deref, phantom_data +use core::marker::PhantomData; + +struct Foo(PhantomData); impl core::ops::Deref for Foo { type Target = (); } fn takes_ref_foo(x: &Foo) {} fn test() { - let foo = Foo; + let foo = Foo(PhantomData); //^^^ type: Foo<{unknown}> takes_ref_foo(&foo); - let foo = Foo; + let foo = Foo(PhantomData); //^^^ type: Foo let _: &() = &foo; }", @@ -297,16 +301,18 @@ fn test() { fn coerce_autoderef_implication_2() { check( r" -//- minicore: deref -struct Foo; +//- minicore: deref, phantom_data +use core::marker::PhantomData; + +struct Foo(PhantomData); impl core::ops::Deref for Foo { type Target = (); } fn takes_ref_foo(x: &Foo) {} fn test() { - let foo = Foo; + let foo = Foo(PhantomData); //^^^ type: Foo<{unknown}> - let _: &u32 = &Foo; - //^^^^ expected &'? u32, got &'? Foo<{unknown}> + let _: &u32 = &Foo(PhantomData); + //^^^^^^^^^^^^^^^^^ expected &'? u32, got &'? Foo<{unknown}> }", ); } @@ -409,8 +415,6 @@ fn test() { #[test] fn coerce_fn_items_in_match_arms() { - cov_mark::check!(coerce_fn_reification); - check_no_mismatches( r" fn foo1(x: u32) -> isize { 1 } @@ -683,9 +687,9 @@ fn coerce_unsize_expected_type_2() { check_no_mismatches( r#" //- minicore: coerce_unsized -struct InFile; +struct InFile(T); impl InFile { - fn with_value(self, value: U) -> InFile { InFile } + fn with_value(self, value: U) -> InFile { InFile(loop {}) } } struct RecordField; trait AstNode {} @@ -694,7 +698,7 @@ impl AstNode for RecordField {} fn takes_dyn(it: InFile<&dyn AstNode>) {} fn test() { - let x: InFile<()> = InFile; + let x: InFile<()> = InFile(()); let n = &RecordField; takes_dyn(x.with_value(n)); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs index 855034117c0d..f257aa1b6e60 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/diagnostics.rs @@ -89,7 +89,6 @@ fn test(x: bool) { //^^^^ expected (), got &'static str } match x { true => true, false => 0 } - //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected (), got bool //^ expected bool, got i32 () } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs index d79639b93727..c0b930e5e123 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs @@ -48,7 +48,6 @@ fn foo() -> i32 { "expr_scopes_shim", "lang_item", "crate_lang_items", - "lang_item", ] "#]], ); @@ -138,7 +137,6 @@ fn baz() -> i32 { "crate_lang_items", "attrs_shim", "attrs_shim", - "lang_item", "infer_shim", "function_signature_shim", "function_signature_with_source_map_shim", @@ -588,8 +586,8 @@ fn main() { "crate_lang_items", "attrs_shim", "attrs_shim", - "return_type_impl_traits_shim", "generic_predicates_ns_shim", + "return_type_impl_traits_shim", "infer_shim", "function_signature_shim", "function_signature_with_source_map_shim", @@ -602,6 +600,7 @@ fn main() { "VariantFields::firewall_", "VariantFields::query_", "lang_item", + "lang_item", "inherent_impls_in_crate_shim", "impl_signature_shim", "impl_signature_with_source_map_shim", @@ -616,7 +615,6 @@ fn main() { "generic_predicates_ns_shim", "value_ty_shim", "generic_predicates_shim", - "lang_item", ] "#]], ); @@ -688,8 +686,8 @@ fn main() { "attrs_shim", "attrs_shim", "attrs_shim", - "return_type_impl_traits_shim", "generic_predicates_ns_shim", + "return_type_impl_traits_shim", "infer_shim", "function_signature_with_source_map_shim", "expr_scopes_shim", diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs index 5d088e40cded..25b938c7078a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs @@ -194,15 +194,15 @@ fn expr_macro_def_expanded_in_various_places() { !0..6 '1isize': isize !0..6 '1isize': isize !0..6 '1isize': isize - 39..442 '{ ...!(); }': () + 39..442 '{ ...!(); }': {unknown} 73..94 'spam!(...am!())': {unknown} 100..119 'for _ ...!() {}': fn into_iter(isize) -> ::IntoIter - 100..119 'for _ ...!() {}': {unknown} + 100..119 'for _ ...!() {}': ::IntoIter 100..119 'for _ ...!() {}': ! - 100..119 'for _ ...!() {}': {unknown} - 100..119 'for _ ...!() {}': &'? mut {unknown} + 100..119 'for _ ...!() {}': ::IntoIter + 100..119 'for _ ...!() {}': &'? mut ::IntoIter 100..119 'for _ ...!() {}': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 100..119 'for _ ...!() {}': Option<{unknown}> + 100..119 'for _ ...!() {}': Option<<{unknown} as Iterator>::Item> 100..119 'for _ ...!() {}': () 100..119 'for _ ...!() {}': () 100..119 'for _ ...!() {}': () @@ -288,15 +288,15 @@ fn expr_macro_rules_expanded_in_various_places() { !0..6 '1isize': isize !0..6 '1isize': isize !0..6 '1isize': isize - 53..456 '{ ...!(); }': () + 53..456 '{ ...!(); }': {unknown} 87..108 'spam!(...am!())': {unknown} 114..133 'for _ ...!() {}': fn into_iter(isize) -> ::IntoIter - 114..133 'for _ ...!() {}': {unknown} + 114..133 'for _ ...!() {}': ::IntoIter 114..133 'for _ ...!() {}': ! - 114..133 'for _ ...!() {}': {unknown} - 114..133 'for _ ...!() {}': &'? mut {unknown} + 114..133 'for _ ...!() {}': ::IntoIter + 114..133 'for _ ...!() {}': &'? mut ::IntoIter 114..133 'for _ ...!() {}': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 114..133 'for _ ...!() {}': Option<{unknown}> + 114..133 'for _ ...!() {}': Option<<{unknown} as Iterator>::Item> 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () @@ -707,7 +707,7 @@ fn infer_builtin_macros_file() { expect![[r#" !0..6 '"file"': &'static str 63..87 '{ ...!(); }': () - 73..74 'x': &'static str + 73..74 'x': &'? str "#]], ); } @@ -745,7 +745,7 @@ fn infer_builtin_macros_concat() { expect![[r#" !0..13 '"helloworld!"': &'static str 65..121 '{ ...")); }': () - 75..76 'x': &'static str + 75..76 'x': &'? str "#]], ); } @@ -822,7 +822,7 @@ macro_rules! include_str {() => {}} fn main() { let a = include_str!("foo.rs"); a; -} //^ &'static str +} //^ &'? str //- /foo.rs hello @@ -849,7 +849,7 @@ macro_rules! m { fn main() { let a = include_str!(m!(".rs")); a; -} //^ &'static str +} //^ &'? str //- /foo.rs hello @@ -964,7 +964,7 @@ fn infer_builtin_macros_concat_with_lazy() { expect![[r#" !0..13 '"helloworld!"': &'static str 103..160 '{ ...")); }': () - 113..114 'x': &'static str + 113..114 'x': &'? str "#]], ); } @@ -979,7 +979,7 @@ fn infer_builtin_macros_env() { fn main() { let x = env!("foo"); - //^ &'static str + //^ &'? str } "#, ); @@ -993,7 +993,7 @@ fn infer_builtin_macros_option_env() { //- /main.rs env:foo=bar fn main() { let x = option_env!("foo"); - //^ Option<&'static str> + //^ Option<&'? str> } "#, ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs index d50daf0f0d5b..b14ce35aa99c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs @@ -578,17 +578,17 @@ fn infer_trait_assoc_method_generics_3() { trait Trait { fn make() -> (Self, T); } - struct S; + struct S(T); impl Trait for S {} fn test() { let a = S::make(); } "#, expect![[r#" - 100..126 '{ ...e(); }': () - 110..111 'a': (S, i64) - 114..121 'S::make': fn make, i64>() -> (S, i64) - 114..123 'S::make()': (S, i64) + 103..129 '{ ...e(); }': () + 113..114 'a': (S, i64) + 117..124 'S::make': fn make, i64>() -> (S, i64) + 117..126 'S::make()': (S, i64) "#]], ); } @@ -600,7 +600,7 @@ fn infer_trait_assoc_method_generics_4() { trait Trait { fn make() -> (Self, T); } - struct S; + struct S(T); impl Trait for S {} impl Trait for S {} fn test() { @@ -609,13 +609,13 @@ fn infer_trait_assoc_method_generics_4() { } "#, expect![[r#" - 130..202 '{ ...e(); }': () - 140..141 'a': (S, i64) - 157..164 'S::make': fn make, i64>() -> (S, i64) - 157..166 'S::make()': (S, i64) - 176..177 'b': (S, i32) - 190..197 'S::make': fn make, i32>() -> (S, i32) - 190..199 'S::make()': (S, i32) + 133..205 '{ ...e(); }': () + 143..144 'a': (S, i64) + 160..167 'S::make': fn make, i64>() -> (S, i64) + 160..169 'S::make()': (S, i64) + 179..180 'b': (S, i32) + 193..200 'S::make': fn make, i32>() -> (S, i32) + 193..202 'S::make()': (S, i32) "#]], ); } @@ -627,7 +627,7 @@ fn infer_trait_assoc_method_generics_5() { trait Trait { fn make() -> (Self, T, U); } - struct S; + struct S(T); impl Trait for S {} fn test() { let a = >::make::(); @@ -635,13 +635,13 @@ fn infer_trait_assoc_method_generics_5() { } "#, expect![[r#" - 106..210 '{ ...>(); }': () - 116..117 'a': (S, i64, u8) - 120..149 '': fn make, i64, u8>() -> (S, i64, u8) - 120..151 '()': (S, i64, u8) - 161..162 'b': (S, i64, u8) - 181..205 'Trait:...::': fn make, i64, u8>() -> (S, i64, u8) - 181..207 'Trait:...()': (S, i64, u8) + 109..213 '{ ...>(); }': () + 119..120 'a': (S, i64, u8) + 123..152 '': fn make, i64, u8>() -> (S, i64, u8) + 123..154 '()': (S, i64, u8) + 164..165 'b': (S, i64, u8) + 184..208 'Trait:...::': fn make, i64, u8>() -> (S, i64, u8) + 184..210 'Trait:...()': (S, i64, u8) "#]], ); } @@ -1107,6 +1107,9 @@ fn method_resolution_slow() { // this can get quite slow if we set the solver size limit too high check_types( r#" +//- minicore: phantom_data +use core::marker::PhantomData; + trait SendX {} struct S1; impl SendX for S1 {} @@ -1115,17 +1118,17 @@ struct U1; trait Trait { fn method(self); } -struct X1 {} +struct X1(PhantomData<(A, B)>); impl SendX for X1 where A: SendX, B: SendX {} -struct S {} +struct S(PhantomData<(B, C)>); trait FnX {} impl Trait for S where C: FnX, B: SendX {} -fn test() { (S {}).method(); } - //^^^^^^^^^^^^^^^ () +fn test() { (S(PhantomData)).method(); } + //^^^^^^^^^^^^^^^^^^^^^^^^^ () "#, ); } @@ -1187,11 +1190,11 @@ fn test() { 89..109 '{ ... }': bool 99..103 'true': bool 123..167 '{ ...o(); }': () - 133..134 's': &'static S - 137..151 'unsafe { f() }': &'static S + 133..134 's': &'? S + 137..151 'unsafe { f() }': &'? S 146..147 'f': fn f() -> &'static S 146..149 'f()': &'static S - 157..158 's': &'static S + 157..158 's': &'? S 157..164 's.foo()': bool "#]], ); @@ -2191,9 +2194,9 @@ impl Receiver for Bar { fn main() { let bar = Bar; let _v1 = bar.foo1(); - //^^^ type: {unknown} + //^^^ type: i32 let _v2 = bar.foo2(); - //^^^ type: {unknown} + //^^^ type: bool } "#, ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs index 6a9135622deb..af5290d72035 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs @@ -14,6 +14,8 @@ fn test() { ); } +// FIXME(next-solver): The never type fallback implemented in r-a no longer works properly because of +// `Coerce` predicates. We should reimplement fallback like rustc. #[test] fn infer_never2() { check_types( @@ -24,7 +26,7 @@ fn test() { let a = gen(); if false { a } else { loop {} }; a; -} //^ ! +} //^ {unknown} "#, ); } @@ -39,7 +41,7 @@ fn test() { let a = gen(); if false { loop {} } else { a }; a; - //^ ! + //^ {unknown} } "#, ); @@ -54,7 +56,7 @@ enum Option { None, Some(T) } fn test() { let a = if true { Option::None } else { Option::Some(return) }; a; -} //^ Option +} //^ Option<{unknown}> "#, ); } @@ -104,7 +106,7 @@ enum Option { None, Some(T) } fn test() { let a = if true { Option::None } else { Option::Some(return) }; a; - //^ Option<&'static str> + //^ Option<&'? str> match 42 { 42 => a, _ => Option::Some("str"), @@ -218,7 +220,7 @@ fn test(a: i32) { _ => loop {}, }; i; -} //^ ! +} //^ {unknown} "#, ); } @@ -362,12 +364,12 @@ fn diverging_expression_3_break() { 140..141 'x': u32 149..175 '{ for ...; }; }': u32 151..172 'for a ...eak; }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 151..172 'for a ...eak; }': {unknown} + 151..172 'for a ...eak; }': <{unknown} as IntoIterator>::IntoIter 151..172 'for a ...eak; }': ! 151..172 'for a ...eak; }': {unknown} 151..172 'for a ...eak; }': &'? mut {unknown} 151..172 'for a ...eak; }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 151..172 'for a ...eak; }': Option<{unknown}> + 151..172 'for a ...eak; }': Option<<{unknown} as Iterator>::Item> 151..172 'for a ...eak; }': () 151..172 'for a ...eak; }': () 151..172 'for a ...eak; }': () @@ -379,12 +381,12 @@ fn diverging_expression_3_break() { 226..227 'x': u32 235..253 '{ for ... {}; }': u32 237..250 'for a in b {}': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 237..250 'for a in b {}': {unknown} + 237..250 'for a in b {}': <{unknown} as IntoIterator>::IntoIter 237..250 'for a in b {}': ! 237..250 'for a in b {}': {unknown} 237..250 'for a in b {}': &'? mut {unknown} 237..250 'for a in b {}': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 237..250 'for a in b {}': Option<{unknown}> + 237..250 'for a in b {}': Option<<{unknown} as Iterator>::Item> 237..250 'for a in b {}': () 237..250 'for a in b {}': () 237..250 'for a in b {}': () @@ -395,12 +397,12 @@ fn diverging_expression_3_break() { 304..305 'x': u32 313..340 '{ for ...; }; }': u32 315..337 'for a ...urn; }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 315..337 'for a ...urn; }': {unknown} + 315..337 'for a ...urn; }': <{unknown} as IntoIterator>::IntoIter 315..337 'for a ...urn; }': ! 315..337 'for a ...urn; }': {unknown} 315..337 'for a ...urn; }': &'? mut {unknown} 315..337 'for a ...urn; }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 315..337 'for a ...urn; }': Option<{unknown}> + 315..337 'for a ...urn; }': Option<<{unknown} as Iterator>::Item> 315..337 'for a ...urn; }': () 315..337 'for a ...urn; }': () 315..337 'for a ...urn; }': () diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs index 256ca7defb78..40e4c28fcc0b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs @@ -158,6 +158,7 @@ static ALIAS: i32 = { 191..193 '_a': impl Trait + ?Sized 205..211 'Struct': Struct 217..218 '5': i32 + 205..211: expected impl Trait + ?Sized, got Struct "#]], ) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs index 02cb03706919..607daada42eb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs @@ -41,19 +41,19 @@ fn infer_pattern() { 47..48 'x': &'? i32 58..59 'a': i32 62..63 'z': i32 - 73..79 '(c, d)': (i32, &'static str) + 73..79 '(c, d)': (i32, &'? str) 74..75 'c': i32 - 77..78 'd': &'static str - 82..94 '(1, "hello")': (i32, &'static str) + 77..78 'd': &'? str + 82..94 '(1, "hello")': (i32, &'? str) 83..84 '1': i32 86..93 '"hello"': &'static str 101..151 'for (e... }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 101..151 'for (e... }': {unknown} + 101..151 'for (e... }': <{unknown} as IntoIterator>::IntoIter 101..151 'for (e... }': ! 101..151 'for (e... }': {unknown} 101..151 'for (e... }': &'? mut {unknown} 101..151 'for (e... }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 101..151 'for (e... }': Option<({unknown}, {unknown})> + 101..151 'for (e... }': Option<<{unknown} as Iterator>::Item> 101..151 'for (e... }': () 101..151 'for (e... }': () 101..151 'for (e... }': () @@ -653,7 +653,7 @@ fn infer_generics_in_patterns() { fn infer_const_pattern() { check( r#" -enum Option { None } +enum Option { None, Some(T) } use Option::None; struct Foo; const Bar: usize = 1; @@ -719,28 +719,28 @@ fn test() { 51..58 'loop {}': ! 56..58 '{}': () 72..171 '{ ... x); }': () - 78..81 'foo': fn foo<&'? (i32, &'static str), i32, impl FnOnce(&'? (i32, &'static str)) -> i32>(&'? (i32, &'static str), impl FnOnce(&'? (i32, &'static str)) -> i32) -> i32 + 78..81 'foo': fn foo<&'? (i32, &'? str), i32, impl FnOnce(&'? (i32, &'? str)) -> i32>(&'? (i32, &'? str), impl FnOnce(&'? (i32, &'? str)) -> i32) -> i32 78..105 'foo(&(...y)| x)': i32 - 82..91 '&(1, "a")': &'? (i32, &'static str) - 83..91 '(1, "a")': (i32, &'static str) + 82..91 '&(1, "a")': &'? (i32, &'? str) + 83..91 '(1, "a")': (i32, &'? str) 84..85 '1': i32 87..90 '"a"': &'static str - 93..104 '|&(x, y)| x': impl FnOnce(&'? (i32, &'static str)) -> i32 - 94..101 '&(x, y)': &'? (i32, &'static str) - 95..101 '(x, y)': (i32, &'static str) + 93..104 '|&(x, y)| x': impl FnOnce(&'? (i32, &'? str)) -> i32 + 94..101 '&(x, y)': &'? (i32, &'? str) + 95..101 '(x, y)': (i32, &'? str) 96..97 'x': i32 - 99..100 'y': &'static str + 99..100 'y': &'? str 103..104 'x': i32 - 142..145 'foo': fn foo<&'? (i32, &'static str), &'? i32, impl FnOnce(&'? (i32, &'static str)) -> &'? i32>(&'? (i32, &'static str), impl FnOnce(&'? (i32, &'static str)) -> &'? i32) -> &'? i32 + 142..145 'foo': fn foo<&'? (i32, &'? str), &'? i32, impl FnOnce(&'? (i32, &'? str)) -> &'? i32>(&'? (i32, &'? str), impl FnOnce(&'? (i32, &'? str)) -> &'? i32) -> &'? i32 142..168 'foo(&(...y)| x)': &'? i32 - 146..155 '&(1, "a")': &'? (i32, &'static str) - 147..155 '(1, "a")': (i32, &'static str) + 146..155 '&(1, "a")': &'? (i32, &'? str) + 147..155 '(1, "a")': (i32, &'? str) 148..149 '1': i32 151..154 '"a"': &'static str - 157..167 '|(x, y)| x': impl FnOnce(&'? (i32, &'static str)) -> &'? i32 - 158..164 '(x, y)': (i32, &'static str) + 157..167 '|(x, y)| x': impl FnOnce(&'? (i32, &'? str)) -> &'? i32 + 158..164 '(x, y)': (i32, &'? str) 159..160 'x': &'? i32 - 162..163 'y': &'? &'static str + 162..163 'y': &'? &'? str 166..167 'x': &'? i32 "#]], ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index 6a3f2286215f..2ba1e2341b29 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -88,6 +88,7 @@ fn bug_651() { #[test] fn recursive_vars() { + // FIXME: This isn't nice, but I guess as long as we don't hang/crash that's fine? check_infer( r#" fn test() { @@ -97,12 +98,12 @@ fn recursive_vars() { "#, expect![[r#" 10..47 '{ ...&y]; }': () - 20..21 'y': {unknown} - 24..31 'unknown': {unknown} - 37..44 '[y, &y]': [{unknown}; 2] - 38..39 'y': {unknown} - 41..43 '&y': &'? {unknown} - 42..43 'y': {unknown} + 20..21 'y': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 24..31 'unknown': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 37..44 '[y, &y]': [&'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown}; 2] + 38..39 'y': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 41..43 '&y': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 42..43 'y': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} "#]], ); } @@ -119,19 +120,19 @@ fn recursive_vars_2() { "#, expect![[r#" 10..79 '{ ...x)]; }': () - 20..21 'x': &'? {unknown} - 24..31 'unknown': &'? {unknown} - 41..42 'y': {unknown} - 45..52 'unknown': {unknown} - 58..76 '[(x, y..., &x)]': [(&'? {unknown}, {unknown}); 2] - 59..65 '(x, y)': (&'? {unknown}, {unknown}) - 60..61 'x': &'? {unknown} - 63..64 'y': {unknown} - 67..75 '(&y, &x)': (&'? {unknown}, {unknown}) - 68..70 '&y': &'? {unknown} - 69..70 'y': {unknown} - 72..74 '&x': &'? &'? {unknown} - 73..74 'x': &'? {unknown} + 20..21 'x': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 24..31 'unknown': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 41..42 'y': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 45..52 'unknown': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 58..76 '[(x, y..., &x)]': [(&'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown}, &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown}); 2] + 59..65 '(x, y)': (&'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown}, &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown}) + 60..61 'x': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 63..64 'y': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 67..75 '(&y, &x)': (&'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown}, &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown}) + 68..70 '&y': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 69..70 'y': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 72..74 '&x': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 73..74 'x': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} "#]], ); } @@ -268,37 +269,37 @@ fn infer_std_crash_5() { expect![[r#" 26..322 '{ ... } }': () 32..320 'for co... }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 32..320 'for co... }': {unknown} + 32..320 'for co... }': <{unknown} as IntoIterator>::IntoIter 32..320 'for co... }': ! 32..320 'for co... }': {unknown} 32..320 'for co... }': &'? mut {unknown} 32..320 'for co... }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 32..320 'for co... }': Option<{unknown}> + 32..320 'for co... }': Option<<{unknown} as Iterator>::Item> 32..320 'for co... }': () 32..320 'for co... }': () 32..320 'for co... }': () 32..320 'for co... }': () - 36..43 'content': {unknown} + 36..43 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} 47..60 'doesnt_matter': {unknown} 61..320 '{ ... }': () - 75..79 'name': &'? {unknown} - 82..166 'if doe... }': &'? {unknown} + 75..79 'name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 82..166 'if doe... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} 85..98 'doesnt_matter': bool - 99..128 '{ ... }': &'? {unknown} - 113..118 'first': &'? {unknown} - 134..166 '{ ... }': &'? {unknown} - 148..156 '&content': &'? {unknown} - 149..156 'content': {unknown} - 181..188 'content': &'? {unknown} - 191..313 'if ICE... }': &'? {unknown} - 194..231 'ICE_RE..._VALUE': {unknown} + 99..128 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 113..118 'first': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 134..166 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 148..156 '&content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 149..156 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 181..188 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 191..313 'if ICE... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 194..231 'ICE_RE..._VALUE': bool 194..247 'ICE_RE...&name)': bool - 241..246 '&name': &'? &'? {unknown} - 242..246 'name': &'? {unknown} - 248..276 '{ ... }': &'? {unknown} - 262..266 'name': &'? {unknown} - 282..313 '{ ... }': {unknown} - 296..303 'content': {unknown} + 241..246 '&name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 242..246 'name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 248..276 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 262..266 'name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 282..313 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 296..303 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} "#]], ); } @@ -394,7 +395,7 @@ fn issue_2669() { r#" trait A {} trait Write {} - struct Response {} + struct Response(T); trait D { fn foo(); @@ -410,13 +411,13 @@ fn issue_2669() { } "#, expect![[r#" - 119..214 '{ ... }': () - 129..132 'end': fn end<{unknown}>() - 129..134 'end()': () - 163..208 '{ ... }': () - 181..183 '_x': ! - 190..197 'loop {}': ! - 195..197 '{}': () + 120..215 '{ ... }': () + 130..133 'end': fn end<{unknown}>() + 130..135 'end()': () + 164..209 '{ ... }': () + 182..184 '_x': ! + 191..198 'loop {}': ! + 196..198 '{}': () "#]], ) } @@ -628,7 +629,7 @@ fn issue_4053_diesel_where_clauses() { 65..69 'self': Self 267..271 'self': Self 466..470 'self': SelectStatement - 488..522 '{ ... }': () + 488..522 '{ ... }': as BoxedDsl>::Output 498..502 'self': SelectStatement 498..508 'self.order': O 498..515 'self.o...into()': dyn QueryFragment + '? @@ -799,7 +800,7 @@ fn issue_4966() { struct Map { f: F } - struct Vec {} + struct Vec { p: *mut T } impl core::ops::Deref for Vec { type Target = [T]; @@ -818,23 +819,23 @@ fn issue_4966() { } "#, expect![[r#" - 225..229 'iter': T - 244..246 '{}': Vec - 258..402 '{ ...r(); }': () - 268..273 'inner': Map f64> - 276..300 'Map { ... 0.0 }': Map f64> - 285..298 '|_: &f64| 0.0': impl Fn(&'? f64) -> f64 - 286..287 '_': &'? f64 - 295..298 '0.0': f64 - 311..317 'repeat': Repeat f64>> - 320..345 'Repeat...nner }': Repeat f64>> - 338..343 'inner': Map f64> - 356..359 'vec': Vec<{unknown}> - 362..371 'from_iter': fn from_iter<{unknown}, Repeat f64>>>(Repeat f64>>) -> Vec<{unknown}> - 362..379 'from_i...epeat)': Vec<{unknown}> - 372..378 'repeat': Repeat f64>> - 386..389 'vec': Vec<{unknown}> - 386..399 'vec.foo_bar()': {unknown} + 236..240 'iter': T + 255..257 '{}': Vec + 269..413 '{ ...r(); }': () + 279..284 'inner': Map f64> + 287..311 'Map { ... 0.0 }': Map f64> + 296..309 '|_: &f64| 0.0': impl Fn(&'? f64) -> f64 + 297..298 '_': &'? f64 + 306..309 '0.0': f64 + 322..328 'repeat': Repeat f64>> + 331..356 'Repeat...nner }': Repeat f64>> + 349..354 'inner': Map f64> + 367..370 'vec': Vec<{unknown}> + 373..382 'from_iter': fn from_iter<{unknown}, Repeat f64>>>(Repeat f64>>) -> Vec<{unknown}> + 373..390 'from_i...epeat)': Vec<{unknown}> + 383..389 'repeat': Repeat f64>> + 397..400 'vec': Vec<{unknown}> + 397..410 'vec.foo_bar()': {unknown} "#]], ); } @@ -843,37 +844,40 @@ fn issue_4966() { fn issue_6628() { check_infer( r#" -//- minicore: fn -struct S(); +//- minicore: fn, phantom_data +use core::marker::PhantomData; + +struct S(PhantomData); impl S { fn f(&self, _t: T) {} fn g(&self, _f: F) {} } fn main() { - let s = S(); + let s = S(PhantomData); s.g(|_x| {}); s.f(10); } "#, expect![[r#" - 40..44 'self': &'? S - 46..48 '_t': T - 53..55 '{}': () - 81..85 'self': &'? S - 87..89 '_f': F - 94..96 '{}': () - 109..160 '{ ...10); }': () - 119..120 's': S - 123..124 'S': fn S() -> S - 123..126 'S()': S - 132..133 's': S - 132..144 's.g(|_x| {})': () - 136..143 '|_x| {}': impl FnOnce(&'? i32) - 137..139 '_x': &'? i32 - 141..143 '{}': () - 150..151 's': S - 150..157 's.f(10)': () - 154..156 '10': i32 + 86..90 'self': &'? S + 92..94 '_t': T + 99..101 '{}': () + 127..131 'self': &'? S + 133..135 '_f': F + 140..142 '{}': () + 155..217 '{ ...10); }': () + 165..166 's': S + 169..170 'S': fn S(PhantomData) -> S + 169..183 'S(PhantomData)': S + 171..182 'PhantomData': PhantomData + 189..190 's': S + 189..201 's.g(|_x| {})': () + 193..200 '|_x| {}': impl FnOnce(&'? i32) + 194..196 '_x': &'? i32 + 198..200 '{}': () + 207..208 's': S + 207..214 's.f(10)': () + 211..213 '10': i32 "#]], ); } @@ -931,7 +935,7 @@ fn lifetime_from_chalk_during_deref() { check_types( r#" //- minicore: deref -struct Box {} +struct Box(T); impl core::ops::Deref for Box { type Target = T; @@ -964,6 +968,9 @@ fn clone_iter(s: Iter) { fn issue_8686() { check_infer( r#" +//- minicore: phantom_data +use core::marker::PhantomData; + pub trait Try: FromResidual { type Output; type Residual; @@ -972,28 +979,32 @@ pub trait FromResidual::Residual> { fn from_residual(residual: R) -> Self; } -struct ControlFlow; +struct ControlFlow(PhantomData<(B, C)>); impl Try for ControlFlow { type Output = C; type Residual = ControlFlow; } impl FromResidual for ControlFlow { - fn from_residual(r: ControlFlow) -> Self { ControlFlow } + fn from_residual(r: ControlFlow) -> Self { ControlFlow(PhantomData) } } fn test() { - ControlFlow::from_residual(ControlFlow::); + ControlFlow::from_residual(ControlFlow::(PhantomData)); } "#, expect![[r#" - 144..152 'residual': R - 365..366 'r': ControlFlow - 395..410 '{ ControlFlow }': ControlFlow - 397..408 'ControlFlow': ControlFlow - 424..482 '{ ...!>); }': () - 430..456 'Contro...sidual': fn from_residual, ControlFlow>(ControlFlow) -> ControlFlow - 430..479 'Contro...2, !>)': ControlFlow - 457..478 'Contro...32, !>': ControlFlow + 176..184 'residual': R + 418..419 'r': ControlFlow + 448..476 '{ Cont...ata) }': ControlFlow + 450..461 'ControlFlow': fn ControlFlow(PhantomData<(B, C)>) -> ControlFlow + 450..474 'Contro...mData)': ControlFlow + 462..473 'PhantomData': PhantomData<(B, C)> + 490..561 '{ ...a)); }': () + 496..522 'Contro...sidual': fn from_residual, ControlFlow>(ControlFlow) -> ControlFlow + 496..558 'Contro...Data))': ControlFlow + 523..544 'Contro...32, !>': fn ControlFlow(PhantomData<(u32, !)>) -> ControlFlow + 523..557 'Contro...mData)': ControlFlow + 545..556 'PhantomData': PhantomData<(u32, !)> "#]], ); } @@ -1052,12 +1063,13 @@ fn impl_trait_in_option_9530() { check_types( r#" //- minicore: sized -struct Option; +struct Option(T); impl Option { fn unwrap(self) -> T { loop {} } } -fn make() -> Option { Option } +fn make() -> Option { Option(()) } trait Copy {} +impl Copy for () {} fn test() { let o = make(); o.unwrap(); @@ -1163,9 +1175,9 @@ pub trait BitView { pub struct Lsb0; -pub struct BitArray { } +pub struct BitArray(V); -pub struct BitSlice { } +pub struct BitSlice(T); impl core::ops::Deref for BitArray { type Target = BitSlice; @@ -1243,12 +1255,12 @@ fn test() { expect![[r#" 10..68 '{ ... } }': () 16..66 'for _ ... }': fn into_iter<()>(()) -> <() as IntoIterator>::IntoIter - 16..66 'for _ ... }': {unknown} + 16..66 'for _ ... }': <() as IntoIterator>::IntoIter 16..66 'for _ ... }': ! - 16..66 'for _ ... }': {unknown} - 16..66 'for _ ... }': &'? mut {unknown} + 16..66 'for _ ... }': <() as IntoIterator>::IntoIter + 16..66 'for _ ... }': &'? mut <() as IntoIterator>::IntoIter 16..66 'for _ ... }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> - 16..66 'for _ ... }': Option<{unknown}> + 16..66 'for _ ... }': Option<<{unknown} as Iterator>::Item> 16..66 'for _ ... }': () 16..66 'for _ ... }': () 16..66 'for _ ... }': () @@ -1779,7 +1791,7 @@ fn regression_14844() { r#" pub type Ty = Unknown; -pub struct Inner(); +pub struct Inner(T); pub struct Outer { pub inner: Inner, @@ -1787,7 +1799,7 @@ pub struct Outer { fn main() { _ = Outer { - inner: Inner::(), + inner: Inner::(0), }; } "#, @@ -1939,7 +1951,7 @@ fn main() { Alias::Braced; //^^^^^^^^^^^^^ {unknown} let Alias::Braced = loop {}; - //^^^^^^^^^^^^^ ! + //^^^^^^^^^^^^^ {unknown} let Alias::Braced(..) = loop {}; //^^^^^^^^^^^^^^^^^ Enum @@ -2017,12 +2029,12 @@ fn tait_async_stack_overflow_17199() { fn lifetime_params_move_param_defaults() { check_types( r#" -pub struct Thing<'s, T = u32>; +pub struct Thing<'s, T = u32>(&'s T); impl <'s> Thing<'s> { pub fn new() -> Thing<'s> { - Thing - //^^^^^ Thing<'?, u32> + Thing(&0) + //^^^^^^^^^ Thing<'?, u32> } } @@ -2042,11 +2054,11 @@ fn issue_17734() { r#" fn test() { let x = S::foo::<'static, &()>(&S); - // ^ Wrap<'static, ()> + // ^ Wrap<'?, ()> let x = S::foo::<&()>(&S); // ^ Wrap<'?, ()> let x = S.foo::<'static, &()>(); - // ^ Wrap<'static, ()> + // ^ Wrap<'?, ()> let x = S.foo::<&()>(); // ^ Wrap<'?, ()> } @@ -2059,7 +2071,7 @@ impl S { } } -struct Wrap<'a, T>(T); +struct Wrap<'a, T>(&'a T); trait Trait<'a> { type Proj; } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs index 82d670cef2b0..ead79a8f5b90 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -307,3 +307,114 @@ where "#]], ) } + +#[test] +fn fn_coercion() { + check_no_mismatches( + r#" +fn foo() { + let _is_suffix_start: fn(&(usize, char)) -> bool = match true { + true => |(_, c)| *c == ' ', + _ => |(_, c)| *c == 'v', + }; +} + "#, + ); +} + +#[test] +fn coercion_with_errors() { + check_no_mismatches( + r#" +//- minicore: unsize, coerce_unsized +fn foo(_v: i32) -> [u8; _] { loop {} } +fn bar(_v: &[u8]) {} + +fn main() { + bar(&foo()); +} + "#, + ); +} + +#[test] +fn another_20654_case() { + check_no_mismatches( + r#" +//- minicore: sized, unsize, coerce_unsized, dispatch_from_dyn, fn +struct Region<'db>(&'db ()); + +trait TypeFoldable {} + +trait Interner { + type Region; + type GenericArg; +} + +struct DbInterner<'db>(&'db ()); +impl<'db> Interner for DbInterner<'db> { + type Region = Region<'db>; + type GenericArg = GenericArg<'db>; +} + +trait GenericArgExt> { + fn expect_region(&self) -> I::Region { + loop {} + } +} +impl<'db> GenericArgExt> for GenericArg<'db> {} + +enum GenericArg<'db> { + Region(Region<'db>), +} + +fn foo<'db, T: TypeFoldable>>(arg: GenericArg<'db>) { + let regions = &mut || arg.expect_region(); + let f: &'_ mut (dyn FnMut() -> Region<'db> + '_) = regions; +} + "#, + ); +} + +#[test] +fn trait_solving_with_error() { + check_infer( + r#" +//- minicore: size_of +struct Vec(T); + +trait Foo { + type Item; + fn to_vec(self) -> Vec { + loop {} + } +} + +impl<'a, T, const N: usize> Foo for &'a [T; N] { + type Item = T; +} + +fn to_bytes() -> [u8; _] { + loop {} +} + +fn foo() { + let _x = to_bytes().to_vec(); +} + "#, + expect![[r#" + 60..64 'self': Self + 85..108 '{ ... }': Vec<::Item> + 95..102 'loop {}': ! + 100..102 '{}': () + 208..223 '{ loop {} }': [u8; _] + 214..221 'loop {}': ! + 219..221 '{}': () + 234..271 '{ ...c(); }': () + 244..246 '_x': {unknown} + 249..257 'to_bytes': fn to_bytes() -> [u8; _] + 249..259 'to_bytes()': [u8; _] + 249..268 'to_byt..._vec()': Vec<<[u8; _] as Foo>::Item> + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index 60ad0f49c6ab..9d02a44c37c9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -439,11 +439,11 @@ h"; 256..260 'true': bool 274..370 'r#" ... "#': &'static str 384..394 'br#"yolo"#': &'static [u8; 4] - 412..413 'a': &'static [u8; 4] + 412..413 'a': &'? [u8; 4] 416..440 'b"a\x2... c"': &'static [u8; 4] - 458..459 'b': &'static [u8; 4] + 458..459 'b': &'? [u8; 4] 462..470 'br"g\ h"': &'static [u8; 4] - 488..489 'c': &'static [u8; 6] + 488..489 'c': &'? [u8; 6] 492..504 'br#"x"\"yb"#': &'static [u8; 6] "##]], ); @@ -1124,13 +1124,13 @@ fn infer_tuple() { 116..122 '(c, x)': ((isize, &'? str), &'? str) 117..118 'c': (isize, &'? str) 120..121 'x': &'? str - 132..133 'e': (i32, &'static str) - 136..144 '(1, "e")': (i32, &'static str) + 132..133 'e': (i32, &'? str) + 136..144 '(1, "e")': (i32, &'? str) 137..138 '1': i32 140..143 '"e"': &'static str - 154..155 'f': ((i32, &'static str), &'static str) - 158..166 '(e, "d")': ((i32, &'static str), &'static str) - 159..160 'e': (i32, &'static str) + 154..155 'f': ((i32, &'? str), &'? str) + 158..166 '(e, "d")': ((i32, &'? str), &'? str) + 159..160 'e': (i32, &'? str) 162..165 '"d"': &'static str "#]], ); @@ -1201,8 +1201,8 @@ fn infer_array() { 209..215 '[1, 2]': [i32; 2] 210..211 '1': i32 213..214 '2': i32 - 225..226 'i': [&'static str; 2] - 229..239 '["a", "b"]': [&'static str; 2] + 225..226 'i': [&'? str; 2] + 229..239 '["a", "b"]': [&'? str; 2] 230..233 '"a"': &'static str 235..238 '"b"': &'static str 250..251 'b': [[&'? str; 1]; 2] @@ -1283,11 +1283,11 @@ fn infer_tuple_struct_generics() { 92..93 'A': fn A(u128) -> A 92..101 'A(42u128)': A 94..100 '42u128': u128 - 107..111 'Some': fn Some<&'static str>(&'static str) -> Option<&'static str> - 107..116 'Some("x")': Option<&'static str> + 107..111 'Some': fn Some<&'? str>(&'? str) -> Option<&'? str> + 107..116 'Some("x")': Option<&'? str> 112..115 '"x"': &'static str - 122..134 'Option::Some': fn Some<&'static str>(&'static str) -> Option<&'static str> - 122..139 'Option...e("x")': Option<&'static str> + 122..134 'Option::Some': fn Some<&'? str>(&'? str) -> Option<&'? str> + 122..139 'Option...e("x")': Option<&'? str> 135..138 '"x"': &'static str 145..149 'None': Option<{unknown}> 159..160 'x': Option @@ -1946,9 +1946,9 @@ fn closure_return_inferred() { "#, expect![[r#" 16..46 '{ ..." }; }': u32 - 26..27 'x': impl Fn() -> &'static str - 30..43 '|| { "test" }': impl Fn() -> &'static str - 33..43 '{ "test" }': &'static str + 26..27 'x': impl Fn() -> &'? str + 30..43 '|| { "test" }': impl Fn() -> &'? str + 33..43 '{ "test" }': &'? str 35..41 '"test"': &'static str "#]], ); @@ -1983,10 +1983,10 @@ fn test() { 70..71 'v': i64 78..80 '{}': () 91..362 '{ ... } }': () - 101..106 'mut g': |usize| yields i64 -> &'static str - 109..218 '|r| { ... }': |usize| yields i64 -> &'static str + 101..106 'mut g': |usize| yields i64 -> &'? str + 109..218 '|r| { ... }': |usize| yields i64 -> &'? str 110..111 'r': usize - 113..218 '{ ... }': &'static str + 113..218 '{ ... }': &'? str 127..128 'a': usize 131..138 'yield 0': usize 137..138 '0': i64 @@ -1998,20 +1998,20 @@ fn test() { 187..188 '2': i64 198..212 '"return value"': &'static str 225..360 'match ... }': () - 231..239 'Pin::new': fn new<&'? mut |usize| yields i64 -> &'static str>(&'? mut |usize| yields i64 -> &'static str) -> Pin<&'? mut |usize| yields i64 -> &'static str> - 231..247 'Pin::n...mut g)': Pin<&'? mut |usize| yields i64 -> &'static str> - 231..262 'Pin::n...usize)': CoroutineState - 240..246 '&mut g': &'? mut |usize| yields i64 -> &'static str - 245..246 'g': |usize| yields i64 -> &'static str + 231..239 'Pin::new': fn new<&'? mut |usize| yields i64 -> &'? str>(&'? mut |usize| yields i64 -> &'? str) -> Pin<&'? mut |usize| yields i64 -> &'? str> + 231..247 'Pin::n...mut g)': Pin<&'? mut |usize| yields i64 -> &'? str> + 231..262 'Pin::n...usize)': CoroutineState + 240..246 '&mut g': &'? mut |usize| yields i64 -> &'? str + 245..246 'g': |usize| yields i64 -> &'? str 255..261 '0usize': usize - 273..299 'Corout...ded(y)': CoroutineState + 273..299 'Corout...ded(y)': CoroutineState 297..298 'y': i64 303..312 '{ f(y); }': () 305..306 'f': fn f(i64) 305..309 'f(y)': () 307..308 'y': i64 - 321..348 'Corout...ete(r)': CoroutineState - 346..347 'r': &'static str + 321..348 'Corout...ete(r)': CoroutineState + 346..347 'r': &'? str 352..354 '{}': () "#]], ); @@ -2707,11 +2707,11 @@ unsafe impl Allocator for Global {} #[lang = "owned_box"] #[fundamental] -pub struct Box; +pub struct Box(T, A); impl, U: ?Sized, A: Allocator> CoerceUnsized> for Box {} -pub struct Vec {} +pub struct Vec(T, A); #[lang = "slice"] impl [T] {} @@ -2734,22 +2734,22 @@ struct Astruct; impl B for Astruct {} "#, expect![[r#" - 604..608 'self': Box<[T], A> - 637..669 '{ ... }': Vec - 683..853 '{ ...])); }': () - 693..696 'vec': Vec - 699..714 '<[_]>::into_vec': fn into_vec(Box<[i32], Global>) -> Vec - 699..745 '<[_]>:...i32]))': Vec - 715..744 '#[rust...1i32])': Box<[i32; 1], Global> - 737..743 '[1i32]': [i32; 1] - 738..742 '1i32': i32 - 755..756 'v': Vec, Global> - 776..793 '<[_]> ...to_vec': fn into_vec, Global>(Box<[Box], Global>) -> Vec, Global> - 776..850 '<[_]> ...ct)]))': Vec, Global> - 794..849 '#[rust...uct)])': Box<[Box; 1], Global> - 816..848 '[#[rus...ruct)]': [Box; 1] - 817..847 '#[rust...truct)': Box - 839..846 'Astruct': Astruct + 614..618 'self': Box<[T], A> + 647..679 '{ ... }': Vec + 693..863 '{ ...])); }': () + 703..706 'vec': Vec + 709..724 '<[_]>::into_vec': fn into_vec(Box<[i32], Global>) -> Vec + 709..755 '<[_]>:...i32]))': Vec + 725..754 '#[rust...1i32])': Box<[i32; 1], Global> + 747..753 '[1i32]': [i32; 1] + 748..752 '1i32': i32 + 765..766 'v': Vec, Global> + 786..803 '<[_]> ...to_vec': fn into_vec, Global>(Box<[Box], Global>) -> Vec, Global> + 786..860 '<[_]> ...ct)]))': Vec, Global> + 804..859 '#[rust...uct)])': Box<[Box; 1], Global> + 826..858 '[#[rus...ruct)]': [Box; 1] + 827..857 '#[rust...truct)': Box + 849..856 'Astruct': Astruct "#]], ) } @@ -3889,9 +3889,9 @@ fn main() { 74..75 'f': F 80..82 '{}': () 94..191 '{ ... }); }': () - 100..113 'async_closure': fn async_closure impl Future>(impl AsyncFnOnce(i32) -> impl Future) + 100..113 'async_closure': fn async_closure(impl FnOnce(i32)) 100..147 'async_... })': () - 114..146 'async ... }': impl AsyncFnOnce(i32) -> impl Future + 114..146 'async ... }': impl FnOnce(i32) 121..124 'arg': i32 126..146 '{ ... }': () 136..139 'arg': i32 @@ -3924,7 +3924,7 @@ fn foo() { expect![[r#" 110..127 '{ ...z(); }': () 116..122 'T::baz': fn baz() -> <{unknown} as Foo>::Gat<'?> - 116..124 'T::baz()': {unknown} + 116..124 'T::baz()': <{unknown} as Foo>::Gat<'?> "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index 22332fdc2b8a..41f8d4ed555f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -85,6 +85,7 @@ async fn test() { } #[test] +#[ignore = "FIXME(next-solver): fix async closures"] fn infer_async_closure() { check_types( r#" @@ -164,16 +165,16 @@ unsafe impl Allocator for Global {} #[lang = "owned_box"] #[fundamental] -pub struct Box(T); +pub struct Box(T, A); impl, U: ?Sized, A: Allocator> CoerceUnsized> for Box {} fn send() -> Box + Send + 'static>{ - Box(async move {}) + Box(async move {}, Global) } fn not_send() -> Box + 'static> { - Box(async move {}) + Box(async move {}, Global) } "#, ); @@ -248,15 +249,15 @@ fn test() { v.push("foo"); for x in v { x; - } //^ &'static str + } //^ &'? str } //- /alloc.rs crate:alloc #![no_std] pub mod collections { - pub struct Vec {} + pub struct Vec { p: *const T } impl Vec { - pub fn new() -> Self { Vec {} } + pub fn new() -> Self { Vec { p: 0 as _ } } pub fn push(&mut self, t: T) { } } @@ -722,8 +723,8 @@ fn deref_trait_with_inference_var() { check_types( r#" //- minicore: deref -struct Arc; -fn new_arc() -> Arc { Arc } +struct Arc(T); +fn new_arc() -> Arc { loop {} } impl core::ops::Deref for Arc { type Target = T; } @@ -785,13 +786,15 @@ fn test(s: Arc) { fn deref_trait_with_implicit_sized_requirement_on_inference_var() { check_types( r#" -//- minicore: deref -struct Foo; +//- minicore: deref, phantom_data +use core::marker::PhantomData; + +struct Foo(PhantomData); impl core::ops::Deref for Foo { type Target = (); } fn test() { - let foo = Foo; + let foo = Foo(PhantomData); *foo; //^^^^ () let _: Foo = foo; @@ -1456,7 +1459,7 @@ trait Trait { fn foo2(&self) -> i64; } -struct Box {} +struct Box(*const T); impl core::ops::Deref for Box { type Target = T; } @@ -1477,27 +1480,27 @@ fn test(x: Box>, y: &dyn Trait) { expect![[r#" 29..33 'self': &'? Self 54..58 'self': &'? Self - 198..200 '{}': Box + '?> - 210..211 'x': Box + '?> - 234..235 'y': &'? (dyn Trait + '?) - 254..371 '{ ...2(); }': () - 260..261 'x': Box + '?> - 267..268 'y': &'? (dyn Trait + '?) - 278..279 'z': Box + '?> - 282..285 'bar': fn bar() -> Box + '?> - 282..287 'bar()': Box + '?> - 293..294 'x': Box + '?> - 293..300 'x.foo()': u64 - 306..307 'y': &'? (dyn Trait + '?) - 306..313 'y.foo()': u64 - 319..320 'z': Box + '?> - 319..326 'z.foo()': u64 - 332..333 'x': Box + '?> - 332..340 'x.foo2()': i64 - 346..347 'y': &'? (dyn Trait + '?) - 346..354 'y.foo2()': i64 - 360..361 'z': Box + '?> - 360..368 'z.foo2()': i64 + 206..208 '{}': Box + '?> + 218..219 'x': Box + '?> + 242..243 'y': &'? (dyn Trait + '?) + 262..379 '{ ...2(); }': () + 268..269 'x': Box + '?> + 275..276 'y': &'? (dyn Trait + '?) + 286..287 'z': Box + '?> + 290..293 'bar': fn bar() -> Box + '?> + 290..295 'bar()': Box + '?> + 301..302 'x': Box + '?> + 301..308 'x.foo()': u64 + 314..315 'y': &'? (dyn Trait + '?) + 314..321 'y.foo()': u64 + 327..328 'z': Box + '?> + 327..334 'z.foo()': u64 + 340..341 'x': Box + '?> + 340..348 'x.foo2()': i64 + 354..355 'y': &'? (dyn Trait + '?) + 354..362 'y.foo2()': i64 + 368..369 'z': Box + '?> + 368..376 'z.foo2()': i64 "#]], ); } @@ -1674,7 +1677,9 @@ fn test(x: (impl Trait + UnknownTrait)) { fn assoc_type_bindings() { check_infer( r#" -//- minicore: sized +//- minicore: sized, phantom_data +use core::marker::PhantomData; + trait Trait { type Type; } @@ -1683,7 +1688,7 @@ fn get(t: T) -> ::Type {} fn get2>(t: T) -> U {} fn set>(t: T) -> T {t} -struct S; +struct S(PhantomData); impl Trait for S { type Type = T; } fn test>(x: T, y: impl Trait) { @@ -1691,46 +1696,52 @@ fn test>(x: T, y: impl Trait) { get2(x); get(y); get2(y); - get(set(S)); - get2(set(S)); - get2(S::); + get(set(S(PhantomData))); + get2(set(S(PhantomData))); + get2(S::(PhantomData)); }"#, expect![[r#" - 49..50 't': T - 77..79 '{}': ::Type - 111..112 't': T - 122..124 '{}': U - 154..155 't': T - 165..168 '{t}': T - 166..167 't': T - 256..257 'x': T - 262..263 'y': impl Trait - 289..399 '{ ...e>); }': () - 295..298 'get': fn get(T) -> ::Type - 295..301 'get(x)': u32 - 299..300 'x': T - 307..311 'get2': fn get2(T) -> u32 - 307..314 'get2(x)': u32 - 312..313 'x': T - 320..323 'get': fn get>(impl Trait) -> as Trait>::Type - 320..326 'get(y)': i64 - 324..325 'y': impl Trait - 332..336 'get2': fn get2>(impl Trait) -> i64 - 332..339 'get2(y)': i64 - 337..338 'y': impl Trait - 345..348 'get': fn get>(S) -> as Trait>::Type - 345..356 'get(set(S))': u64 - 349..352 'set': fn set>(S) -> S - 349..355 'set(S)': S - 353..354 'S': S - 362..366 'get2': fn get2>(S) -> u64 - 362..374 'get2(set(S))': u64 - 367..370 'set': fn set>(S) -> S - 367..373 'set(S)': S - 371..372 'S': S - 380..384 'get2': fn get2>(S) -> usize - 380..396 'get2(S...size>)': usize - 385..395 'S::': S + 81..82 't': T + 109..111 '{}': ::Type + 143..144 't': T + 154..156 '{}': U + 186..187 't': T + 197..200 '{t}': T + 198..199 't': T + 304..305 'x': T + 310..311 'y': impl Trait + 337..486 '{ ...a)); }': () + 343..346 'get': fn get(T) -> ::Type + 343..349 'get(x)': u32 + 347..348 'x': T + 355..359 'get2': fn get2(T) -> u32 + 355..362 'get2(x)': u32 + 360..361 'x': T + 368..371 'get': fn get>(impl Trait) -> as Trait>::Type + 368..374 'get(y)': i64 + 372..373 'y': impl Trait + 380..384 'get2': fn get2>(impl Trait) -> i64 + 380..387 'get2(y)': i64 + 385..386 'y': impl Trait + 393..396 'get': fn get>(S) -> as Trait>::Type + 393..417 'get(se...ata)))': u64 + 397..400 'set': fn set>(S) -> S + 397..416 'set(S(...Data))': S + 401..402 'S': fn S(PhantomData) -> S + 401..415 'S(PhantomData)': S + 403..414 'PhantomData': PhantomData + 423..427 'get2': fn get2>(S) -> u64 + 423..448 'get2(s...ata)))': u64 + 428..431 'set': fn set>(S) -> S + 428..447 'set(S(...Data))': S + 432..433 'S': fn S(PhantomData) -> S + 432..446 'S(PhantomData)': S + 434..445 'PhantomData': PhantomData + 454..458 'get2': fn get2>(S) -> usize + 454..483 'get2(S...Data))': usize + 459..469 'S::': fn S(PhantomData) -> S + 459..482 'S:: + 470..481 'PhantomData': PhantomData "#]], ); } @@ -1747,7 +1758,7 @@ pub enum RustLanguage {} impl Language for RustLanguage { type Kind = SyntaxKind; } -struct SyntaxNode {} +struct SyntaxNode(L); fn foo() -> impl Iterator> {} trait Clone { @@ -1886,31 +1897,36 @@ fn super_trait_cycle() { fn super_trait_assoc_type_bounds() { check_infer( r#" +//- minicore: phantom_data +use core::marker::PhantomData; + trait SuperTrait { type Type; } trait Trait where Self: SuperTrait {} fn get2>(t: T) -> U {} fn set>(t: T) -> T {t} -struct S; +struct S(PhantomData); impl SuperTrait for S { type Type = T; } impl Trait for S {} fn test() { - get2(set(S)); + get2(set(S(PhantomData))); }"#, expect![[r#" - 102..103 't': T - 113..115 '{}': U - 145..146 't': T - 156..159 '{t}': T - 157..158 't': T - 258..279 '{ ...S)); }': () - 264..268 'get2': fn get2>(S) -> u64 - 264..276 'get2(set(S))': u64 - 269..272 'set': fn set>(S) -> S - 269..275 'set(S)': S - 273..274 'S': S + 134..135 't': T + 145..147 '{}': U + 177..178 't': T + 188..191 '{t}': T + 189..190 't': T + 306..340 '{ ...))); }': () + 312..316 'get2': fn get2>(S) -> u64 + 312..337 'get2(s...ata)))': u64 + 317..320 'set': fn set>(S) -> S + 317..336 'set(S(...Data))': S + 321..322 'S': fn S(PhantomData) -> S + 321..335 'S(PhantomData)': S + 323..334 'PhantomData': PhantomData "#]], ); } @@ -2000,7 +2016,7 @@ impl Foo { fn foo(&self) -> usize {} } -struct Lazy T>(F); +struct Lazy T>(T, F); impl Lazy { pub fn new(f: F) -> Lazy {} @@ -2014,7 +2030,7 @@ fn test() { let lazy1: Lazy = Lazy::new(|| Foo); let r1 = lazy1.foo(); - fn make_foo_fn() -> Foo {} +fn make_foo_fn() -> Foo {} let make_foo_fn_ptr: fn() -> Foo = make_foo_fn; let lazy2: Lazy = Lazy::new(make_foo_fn_ptr); let r2 = lazy2.foo(); @@ -2022,27 +2038,27 @@ fn test() { expect![[r#" 36..40 'self': &'? Foo 51..53 '{}': usize - 131..132 'f': F - 151..153 '{}': Lazy - 251..497 '{ ...o(); }': () - 261..266 'lazy1': Lazy Foo> - 283..292 'Lazy::new': fn new Foo>(impl Fn() -> Foo) -> Lazy Foo> - 283..300 'Lazy::...| Foo)': Lazy Foo> - 293..299 '|| Foo': impl Fn() -> Foo - 296..299 'Foo': Foo - 310..312 'r1': usize - 315..320 'lazy1': Lazy Foo> - 315..326 'lazy1.foo()': usize - 368..383 'make_foo_fn_ptr': fn() -> Foo - 399..410 'make_foo_fn': fn make_foo_fn() -> Foo - 420..425 'lazy2': Lazy Foo> - 442..451 'Lazy::new': fn new Foo>(fn() -> Foo) -> Lazy Foo> - 442..468 'Lazy::...n_ptr)': Lazy Foo> - 452..467 'make_foo_fn_ptr': fn() -> Foo - 478..480 'r2': usize - 483..488 'lazy2': Lazy Foo> - 483..494 'lazy2.foo()': usize - 357..359 '{}': Foo + 134..135 'f': F + 154..156 '{}': Lazy + 254..496 '{ ...o(); }': () + 264..269 'lazy1': Lazy Foo> + 286..295 'Lazy::new': fn new Foo>(impl Fn() -> Foo) -> Lazy Foo> + 286..303 'Lazy::...| Foo)': Lazy Foo> + 296..302 '|| Foo': impl Fn() -> Foo + 299..302 'Foo': Foo + 313..315 'r1': usize + 318..323 'lazy1': Lazy Foo> + 318..329 'lazy1.foo()': usize + 367..382 'make_foo_fn_ptr': fn() -> Foo + 398..409 'make_foo_fn': fn make_foo_fn() -> Foo + 419..424 'lazy2': Lazy Foo> + 441..450 'Lazy::new': fn new Foo>(fn() -> Foo) -> Lazy Foo> + 441..467 'Lazy::...n_ptr)': Lazy Foo> + 451..466 'make_foo_fn_ptr': fn() -> Foo + 477..479 'r2': usize + 482..487 'lazy2': Lazy Foo> + 482..493 'lazy2.foo()': usize + 356..358 '{}': Foo "#]], ); } @@ -2341,7 +2357,7 @@ trait Fold { type Result; } -struct Ty {} +struct Ty(I); impl Fold for Ty { type Result = Ty; } @@ -2383,17 +2399,20 @@ fn test() { fn trait_impl_self_ty_cycle() { check_types( r#" +//- minicore: phantom_data +use core::marker::PhantomData; + trait Trait { fn foo(&self); } -struct S; +struct S(T); impl Trait for S {} fn test() { - S.foo(); -} //^^^^^^^ {unknown} + S(PhantomData).foo(); +} //^^^^^^^^^^^^^^^^^^^^ {unknown} "#, ); } @@ -2743,7 +2762,7 @@ fn dyn_trait_through_chalk() { check_types( r#" //- minicore: deref, unsize, dispatch_from_dyn -struct Box {} +struct Box(*const T); impl core::ops::Deref for Box { type Target = T; } @@ -2802,7 +2821,7 @@ pub trait IntoIterator { fn into_iter(self) -> Self::IntoIter; } -pub struct FilterMap { } +pub struct FilterMap(I, F); impl Iterator for FilterMap where F: FnMut(I::Item) -> Option, @@ -2820,7 +2839,7 @@ impl IntoIterator for I { } } -struct Vec {} +struct Vec(T); impl Vec { fn new() -> Self { loop {} } } @@ -2830,7 +2849,7 @@ impl IntoIterator for Vec { type IntoIter = IntoIter; } -pub struct IntoIter { } +pub struct IntoIter(T); impl Iterator for IntoIter { type Item = T; } @@ -2852,35 +2871,35 @@ fn main() { 242..249 'loop {}': ! 247..249 '{}': () 360..364 'self': Self - 689..693 'self': I - 700..720 '{ ... }': I - 710..714 'self': I - 779..790 '{ loop {} }': Vec - 781..788 'loop {}': ! - 786..788 '{}': () - 977..1104 '{ ... }); }': () - 983..998 'Vec::::new': fn new() -> Vec - 983..1000 'Vec::<...:new()': Vec - 983..1012 'Vec::<...iter()': IntoIter - 983..1075 'Vec::<...one })': FilterMap, impl FnMut(i32) -> Option> - 983..1101 'Vec::<... y; })': () - 1029..1074 '|x| if...None }': impl FnMut(i32) -> Option - 1030..1031 'x': i32 - 1033..1074 'if x >...None }': Option - 1036..1037 'x': i32 - 1036..1041 'x > 0': bool - 1040..1041 '0': i32 - 1042..1060 '{ Some...u32) }': Option - 1044..1048 'Some': fn Some(u32) -> Option - 1044..1058 'Some(x as u32)': Option - 1049..1050 'x': i32 - 1049..1057 'x as u32': u32 - 1066..1074 '{ None }': Option - 1068..1072 'None': Option - 1090..1100 '|y| { y; }': impl FnMut(u32) - 1091..1092 'y': u32 - 1094..1100 '{ y; }': () - 1096..1097 'y': u32 + 692..696 'self': I + 703..723 '{ ... }': I + 713..717 'self': I + 783..794 '{ loop {} }': Vec + 785..792 'loop {}': ! + 790..792 '{}': () + 981..1108 '{ ... }); }': () + 987..1002 'Vec::::new': fn new() -> Vec + 987..1004 'Vec::<...:new()': Vec + 987..1016 'Vec::<...iter()': IntoIter + 987..1079 'Vec::<...one })': FilterMap, impl FnMut(i32) -> Option> + 987..1105 'Vec::<... y; })': () + 1033..1078 '|x| if...None }': impl FnMut(i32) -> Option + 1034..1035 'x': i32 + 1037..1078 'if x >...None }': Option + 1040..1041 'x': i32 + 1040..1045 'x > 0': bool + 1044..1045 '0': i32 + 1046..1064 '{ Some...u32) }': Option + 1048..1052 'Some': fn Some(u32) -> Option + 1048..1062 'Some(x as u32)': Option + 1053..1054 'x': i32 + 1053..1061 'x as u32': u32 + 1070..1078 '{ None }': Option + 1072..1076 'None': Option + 1094..1104 '|y| { y; }': impl FnMut(u32) + 1095..1096 'y': u32 + 1098..1104 '{ y; }': () + 1100..1101 'y': u32 "#]], ); } @@ -3134,7 +3153,6 @@ fn foo() { #[test] fn dyn_fn_param_informs_call_site_closure_signature() { - cov_mark::check!(dyn_fn_param_informs_call_site_closure_signature); check_types( r#" //- minicore: fn, coerce_unsized, dispatch_from_dyn @@ -3617,7 +3635,7 @@ impl Add<&i32> for i32 { type Output = i32 } impl Add for u32 { type Output = u32 } impl Add<&u32> for u32 { type Output = u32 } -struct V; +struct V(T); impl V { fn default() -> Self { loop {} } fn get(&self, _: &T) -> &T { loop {} } @@ -3646,7 +3664,7 @@ impl Add<&i32> for i32 { type Output = i32; } // fallback to integer type variable for `42`. impl Add<&()> for i32 { type Output = (); } -struct V; +struct V(T); impl V { fn default() -> Self { loop {} } fn get(&self) -> &T { loop {} } @@ -4213,21 +4231,21 @@ fn f(v: impl Trait) { } fn g<'a, T: 'a>(v: impl Trait = &'a T>) { let a = v.get::(); - //^ &'a T + //^ &'? T let a = v.get::<()>(); //^ = &'a T> as Trait>::Assoc<()> } fn h<'a>(v: impl Trait = &'a i32> + Trait = &'a i64>) { let a = v.get::(); - //^ &'a i32 + //^ &'? i32 let a = v.get::(); - //^ &'a i64 + //^ &'? i64 } fn i<'a>(v: impl Trait = &'a i32, Assoc = &'a i64>) { let a = v.get::(); - //^ &'a i32 + //^ &'? i32 let a = v.get::(); - //^ &'a i64 + //^ &'? i64 } "#, ); @@ -4257,8 +4275,8 @@ fn f<'a>(v: &dyn Trait = &'a i32>) { 127..128 'v': &'? (dyn Trait = &'a i32> + '?) 164..195 '{ ...f(); }': () 170..171 'v': &'? (dyn Trait = &'a i32> + '?) - 170..184 'v.get::()': {unknown} - 170..192 'v.get:...eref()': &'? {unknown} + 170..184 'v.get::()': = &'a i32> + '? as Trait>::Assoc + 170..192 'v.get:...eref()': {unknown} "#]], ); } @@ -4485,7 +4503,9 @@ impl Trait for () { fn derive_macro_bounds() { check_types( r#" - //- minicore: clone, derive + //- minicore: clone, derive, phantom_data + use core::marker::PhantomData; + #[derive(Clone)] struct Copy; struct NotCopy; @@ -4508,7 +4528,7 @@ fn derive_macro_bounds() { struct AssocGeneric3(Generic); #[derive(Clone)] - struct Vec(); + struct Vec(PhantomData); #[derive(Clone)] struct R1(Vec); @@ -4532,9 +4552,9 @@ fn derive_macro_bounds() { let x: &AssocGeneric3 = &AssocGeneric3(Generic(NotCopy)); let x = x.clone(); //^ &'? AssocGeneric3 - let x = (&R1(Vec())).clone(); + let x = (&R1(Vec(PhantomData))).clone(); //^ R1 - let x = (&R2(R1(Vec()))).clone(); + let x = (&R2(R1(Vec(PhantomData)))).clone(); //^ R2 } "#, @@ -4624,8 +4644,10 @@ fn ttt() { fn infer_borrow() { check_types( r#" -//- minicore: index -pub struct SomeMap; +//- minicore: index, phantom_data +use core::marker::PhantomData; + +pub struct SomeMap(PhantomData); pub trait Borrow { fn borrow(&self) -> &Borrowed; @@ -4658,7 +4680,7 @@ impl core::ops::IndexMut for SomeMap { } fn foo() { - let mut map = SomeMap; + let mut map = SomeMap(PhantomData); map["a"] = (); map; //^^^ SomeMap<&'static str> @@ -4908,6 +4930,7 @@ fn main() { #[test] fn async_fn_return_type() { + // FIXME(next-solver): Async closures are lowered as closures currently. We should fix that. check_infer( r#" //- minicore: async_fn @@ -4925,9 +4948,9 @@ fn main() { 46..53 'loop {}': ! 51..53 '{}': () 67..97 '{ ...()); }': () - 73..76 'foo': fn foo impl Future, ()>(impl AsyncFn() -> impl Future) + 73..76 'foo': fn foo(impl Fn()) 73..94 'foo(as...|| ())': () - 77..93 'async ... || ()': impl AsyncFn() -> impl Future + 77..93 'async ... || ()': impl Fn() 91..93 '()': () "#]], ); @@ -5020,7 +5043,7 @@ fn main() { 223..227 'iter': Box + 'static> 273..280 'loop {}': ! 278..280 '{}': () - 290..291 '_': Box + 'static> + 290..291 '_': Box + '?> 294..298 'iter': Box + 'static> 294..310 'iter.i...iter()': Box + 'static> 152..156 'self': &'? mut Box diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs index a6377243ed98..d73d7da4b9b9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -428,9 +428,4 @@ impl FnTrait { pub fn get_id(self, db: &dyn HirDatabase, krate: Crate) -> Option { self.lang_item().resolve_trait(db, krate) } - - #[inline] - pub(crate) fn is_async(self) -> bool { - matches!(self, FnTrait::AsyncFn | FnTrait::AsyncFnMut | FnTrait::AsyncFnOnce) - } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs index 07679d2a119d..427c4bb68423 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs @@ -20,14 +20,14 @@ use hir_expand::name::Name; use intern::sym; use rustc_abi::TargetDataLayout; use rustc_hash::FxHashSet; -use rustc_type_ir::inherent::{IntoKind, SliceLike}; +use rustc_type_ir::inherent::{GenericArgs, IntoKind, SliceLike}; use smallvec::{SmallVec, smallvec}; use span::Edition; -use stdx::never; +use crate::next_solver::mapping::NextSolverToChalk; use crate::{ - ChalkTraitId, Const, ConstScalar, GenericArg, Interner, Substitution, TargetFeatures, TraitRef, - TraitRefExt, Ty, WhereClause, + ChalkTraitId, Const, ConstScalar, Interner, Substitution, TargetFeatures, TraitRef, + TraitRefExt, Ty, consteval::unknown_const, db::HirDatabase, layout::{Layout, TagEncoding}, @@ -120,52 +120,6 @@ impl Iterator for SuperTraits<'_> { } } -pub(super) fn elaborate_clause_supertraits( - db: &dyn HirDatabase, - clauses: impl Iterator, -) -> ClauseElaborator<'_> { - let mut elaborator = ClauseElaborator { db, stack: Vec::new(), seen: FxHashSet::default() }; - elaborator.extend_deduped(clauses); - - elaborator -} - -pub(super) struct ClauseElaborator<'a> { - db: &'a dyn HirDatabase, - stack: Vec, - seen: FxHashSet, -} - -impl ClauseElaborator<'_> { - fn extend_deduped(&mut self, clauses: impl IntoIterator) { - self.stack.extend(clauses.into_iter().filter(|c| self.seen.insert(c.clone()))) - } - - fn elaborate_supertrait(&mut self, clause: &WhereClause) { - if let WhereClause::Implemented(trait_ref) = clause { - direct_super_trait_refs(self.db, trait_ref, |t| { - let clause = WhereClause::Implemented(t); - if self.seen.insert(clause.clone()) { - self.stack.push(clause); - } - }); - } - } -} - -impl Iterator for ClauseElaborator<'_> { - type Item = WhereClause; - - fn next(&mut self) -> Option { - if let Some(next) = self.stack.pop() { - self.elaborate_supertrait(&next); - Some(next) - } else { - None - } - } -} - fn direct_super_traits_cb(db: &dyn DefDatabase, trait_: TraitId, cb: impl FnMut(TraitId)) { let resolver = LazyCell::new(|| trait_.resolver(db)); let (generic_params, store) = db.generic_params_and_store(trait_.into()); @@ -239,34 +193,25 @@ pub(super) fn associated_type_by_name_including_super_traits( }) } -/// It is a bit different from the rustc equivalent. Currently it stores: -/// - 0..n-1: generics of the parent -/// - n: the function signature, encoded as a function pointer type -/// -/// and it doesn't store the closure types and fields. -/// -/// Codes should not assume this ordering, and should always use methods available -/// on this struct for retrieving, and `TyBuilder::substs_for_closure` for creating. pub(crate) struct ClosureSubst<'a>(pub(crate) &'a Substitution); impl<'a> ClosureSubst<'a> { - pub(crate) fn parent_subst(&self) -> &'a [GenericArg] { - match self.0.as_slice(Interner) { - [x @ .., _] => x, - _ => { - never!("Closure missing parameter"); - &[] - } - } + pub(crate) fn parent_subst(&self, db: &dyn HirDatabase) -> Substitution { + let interner = DbInterner::new_with(db, None, None); + let subst = + >>::to_nextsolver( + self.0, interner, + ); + subst.split_closure_args().parent_args.to_chalk(interner) } - pub(crate) fn sig_ty(&self) -> &'a Ty { - match self.0.as_slice(Interner) { - [.., x] => x.assert_ty_ref(Interner), - _ => { - unreachable!("Closure missing sig_ty parameter"); - } - } + pub(crate) fn sig_ty(&self, db: &dyn HirDatabase) -> Ty { + let interner = DbInterner::new_with(db, None, None); + let subst = + >>::to_nextsolver( + self.0, interner, + ); + subst.split_closure_args_untupled().closure_sig_as_fn_ptr_ty.to_chalk(interner) } } @@ -278,8 +223,17 @@ pub enum Unsafety { DeprecatedSafe2024, } -pub fn target_feature_is_safe_in_target(target: &TargetData) -> bool { - matches!(target.arch, target::Arch::Wasm32 | target::Arch::Wasm64) +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum TargetFeatureIsSafeInTarget { + No, + Yes, +} + +pub fn target_feature_is_safe_in_target(target: &TargetData) -> TargetFeatureIsSafeInTarget { + match target.arch { + target::Arch::Wasm32 | target::Arch::Wasm64 => TargetFeatureIsSafeInTarget::Yes, + _ => TargetFeatureIsSafeInTarget::No, + } } pub fn is_fn_unsafe_to_call( @@ -287,14 +241,14 @@ pub fn is_fn_unsafe_to_call( func: FunctionId, caller_target_features: &TargetFeatures, call_edition: Edition, - target_feature_is_safe: bool, + target_feature_is_safe: TargetFeatureIsSafeInTarget, ) -> Unsafety { let data = db.function_signature(func); if data.is_unsafe() { return Unsafety::Unsafe; } - if data.has_target_feature() && !target_feature_is_safe { + if data.has_target_feature() && target_feature_is_safe == TargetFeatureIsSafeInTarget::No { // RFC 2396 . let callee_target_features = TargetFeatures::from_attrs_no_implications(&db.attrs(func.into())); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs index a1ebff04bbd4..8593dba301b8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs @@ -49,7 +49,23 @@ pub(crate) fn variances_of(db: &dyn HirDatabase, def: GenericDefId) -> Option { } "#, expect![[r#" - Hello['a: bivariant] - Other['a: bivariant] + Hello['a: invariant] + Other['a: invariant] "#]], ); } @@ -601,7 +618,7 @@ struct Foo { //~ ERROR [T: o] } "#, expect![[r#" - Foo[T: bivariant] + Foo[T: invariant] "#]], ); } @@ -683,9 +700,9 @@ struct TestBox+Setter> { //~ ERROR [U: *, T: +] get[Self: contravariant, T: covariant] get[Self: contravariant, T: contravariant] TestStruct[U: covariant, T: covariant] - TestEnum[U: bivariant, T: covariant] - TestContraStruct[U: bivariant, T: covariant] - TestBox[U: bivariant, T: covariant] + TestEnum[U: invariant, T: covariant] + TestContraStruct[U: invariant, T: covariant] + TestBox[U: invariant, T: covariant] "#]], ); } @@ -805,8 +822,8 @@ enum SomeEnum<'a> { Nothing } //~ ERROR parameter `'a` is never used trait SomeTrait<'a> { fn foo(&self); } // OK on traits. "#, expect![[r#" - SomeStruct['a: bivariant] - SomeEnum['a: bivariant] + SomeStruct['a: invariant] + SomeEnum['a: invariant] foo[Self: contravariant, 'a: invariant] "#]], ); @@ -834,14 +851,14 @@ struct DoubleNothing { "#, expect![[r#" - SomeStruct[A: bivariant] - SomeEnum[A: bivariant] - ListCell[T: bivariant] - SelfTyAlias[T: bivariant] - WithBounds[T: bivariant] - WithWhereBounds[T: bivariant] - WithOutlivesBounds[T: bivariant] - DoubleNothing[T: bivariant] + SomeStruct[A: invariant] + SomeEnum[A: invariant] + ListCell[T: invariant] + SelfTyAlias[T: invariant] + WithBounds[T: invariant] + WithWhereBounds[T: invariant] + WithOutlivesBounds[T: invariant] + DoubleNothing[T: invariant] "#]], ); } @@ -952,7 +969,7 @@ struct S3(S); "#, expect![[r#" S[T: covariant] - S2[T: bivariant] + S2[T: invariant] S3[T: covariant] "#]], ); @@ -965,7 +982,7 @@ struct S3(S); struct FixedPoint(&'static FixedPoint<(), T, U>, V); "#, expect![[r#" - FixedPoint[T: bivariant, U: bivariant, V: bivariant] + FixedPoint[T: invariant, U: invariant, V: invariant] "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index bd9360f30f16..3083b5651562 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -2549,11 +2549,13 @@ impl Function { let target_feature_is_safe_in_target = match &caller.krate(db).id.workspace_data(db).target { Ok(target) => hir_ty::target_feature_is_safe_in_target(target), - Err(_) => false, + Err(_) => hir_ty::TargetFeatureIsSafeInTarget::No, }; (target_features, target_feature_is_safe_in_target) }) - .unwrap_or_else(|| (hir_ty::TargetFeatures::default(), false)); + .unwrap_or_else(|| { + (hir_ty::TargetFeatures::default(), hir_ty::TargetFeatureIsSafeInTarget::No) + }); matches!( hir_ty::is_fn_unsafe_to_call( db, @@ -4660,7 +4662,7 @@ impl Closure { .iter() .map(|capture| Type { env: db.trait_environment_for_body(owner), - ty: capture.ty(&self.subst), + ty: capture.ty(db, &self.subst), _pd: PhantomCovariantLifetime::new(), }) .collect() diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs index 5f526ec89940..3dd435d9423b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs @@ -805,6 +805,7 @@ impl A { ); } + #[ignore = "FIXME(next-solver): Fix async closures"] #[test] fn replaces_async_closure_with_async_fn() { check_assist( @@ -1066,7 +1067,7 @@ fn foo() { r#" fn foo() { let (mut a, b) = (0.1, "abc"); - fn closure(p1: i32, p2: &mut bool, a: &mut f64, b: &&'static str) { + fn closure(p1: i32, p2: &mut bool, a: &mut f64, b: &&str) { *a = 1.2; let c = *b; } @@ -1098,7 +1099,7 @@ fn foo() { r#" fn foo() { let (mut a, b) = (0.1, "abc"); - fn closure(p1: i32, p2: &mut bool, a: &mut f64, b: &&'static str) { + fn closure(p1: i32, p2: &mut bool, a: &mut f64, b: &&str) { let _: &mut bool = p2; *a = 1.2; let c = *b; @@ -1136,7 +1137,7 @@ fn foo() { r#" fn foo() { let (mut a, b) = (0.1, "abc"); - fn closure(p1: i32, p2: &mut bool, a: &mut f64, b: &&'static str) { + fn closure(p1: i32, p2: &mut bool, a: &mut f64, b: &&str) { let _: &mut bool = p2; *a = 1.2; let c = *b; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs index d88e3311bd7b..3685cc904e94 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs @@ -5043,7 +5043,7 @@ fn main() { fun_name(bar); } -fn $0fun_name(bar: &'static str) { +fn $0fun_name(bar: &str) { m!(bar); } "#, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index b9ef68cc2d2a..8b4f315ac573 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -1501,7 +1501,9 @@ fn main() { bar.$0 } "#, - expect![[r#""#]], + expect![[r#" + me foo() fn(self: Bar) + "#]], ); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index 79137104b568..5cc72ef845bf 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -2011,7 +2011,7 @@ fn main() { en Enum Enum fn function() fn() fn main() fn() - lc variable &'static str + lc variable &str ma helper!(…) macro_rules! helper ma m!(…) macro_rules! m ma makro!(…) macro_rules! makro @@ -2486,6 +2486,7 @@ fn bar() { md rust_2024 (use core::prelude::rust_2024) tt Clone tt Copy + tt FromIterator tt IntoIterator tt Iterator ta Result (use core::fmt::Result) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs index 8d4277026905..542ac215f1cc 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs @@ -152,7 +152,7 @@ fn main() { fn main() { let mut x = t(); x = _; - //^ 💡 error: invalid `_` expression, expected type `&'static str` + //^ 💡 error: invalid `_` expression, expected type `&str` x = ""; } fn t() -> T { loop {} } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs index dcca85d4db33..bd5d134348e2 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs @@ -268,7 +268,7 @@ impl A { } fn main() { let a = A {a: 0, b: ""}; - A::::foo(); + A::::foo(); } "#, ); @@ -351,4 +351,26 @@ fn foo() { "#, ); } + + #[test] + fn iter_collect() { + check_diagnostics( + r#" +//- minicore: unsize, coerce_unsized, iterator, iterators, sized +struct Map(K, V); +impl FromIterator<(K, V)> for Map { + fn from_iter>(_iter: T) -> Self { + loop {} + } +} + +fn foo() -> Map { + [ + (123, &["abc", "def"] as _), + (456, &["ghi"] as _), + ].into_iter().collect() +} + "#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index a88c0c9866e5..b623e51ee45e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -7195,7 +7195,7 @@ fn foo() { "#, expect![[r#" ```rust - &'static str + &str ```"#]], ); } @@ -8459,7 +8459,7 @@ format_args!("{aaaaa$0}"); *aaaaa* ```rust - let aaaaa: &'static str + let aaaaa: &str ``` "#]], ); @@ -8479,7 +8479,7 @@ format_args!("{$0aaaaa}"); *aaaaa* ```rust - let aaaaa: &'static str + let aaaaa: &str ``` "#]], ); @@ -8499,7 +8499,7 @@ format_args!(r"{$0aaaaa}"); *aaaaa* ```rust - let aaaaa: &'static str + let aaaaa: &str ``` "#]], ); @@ -8524,7 +8524,7 @@ foo!(r"{$0aaaaa}"); *aaaaa* ```rust - let aaaaa: &'static str + let aaaaa: &str ``` "#]], ); @@ -10168,7 +10168,7 @@ fn baz() { --- - `U` = `i32`, `T` = `&'static str` + `U` = `i32`, `T` = `&str` "#]], ); } @@ -10261,7 +10261,7 @@ fn bar() { --- - `T` = `i8`, `U` = `&'static str` + `T` = `i8`, `U` = `&str` "#]], ); } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs index 7a4af4f7549c..104740cbbf74 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs @@ -378,9 +378,9 @@ fn main() { let foo = foo3(); // ^^^ impl Fn(f64, f64) -> u32 let foo = foo4(); - // ^^^ &'static dyn Fn(f64, f64) -> u32 + // ^^^ &dyn Fn(f64, f64) -> u32 let foo = foo5(); - // ^^^ &'static dyn Fn(&dyn Fn(f64, f64) -> u32, f64) -> u32 + // ^^^ &dyn Fn(&dyn Fn(f64, f64) -> u32, f64) -> u32 let foo = foo6(); // ^^^ impl Fn(f64, f64) -> u32 let foo = foo7(); @@ -411,7 +411,7 @@ fn main() { let foo = foo3(); // ^^^ impl Fn(f64, f64) -> u32 let foo = foo4(); - // ^^^ &'static dyn Fn(f64, f64) -> u32 + // ^^^ &dyn Fn(f64, f64) -> u32 let foo = foo5(); let foo = foo6(); let foo = foo7(); @@ -526,7 +526,7 @@ fn main() { //^^^^ i32 let _ = 22; let test = "test"; - //^^^^ &'static str + //^^^^ &str let test = InnerStruct {}; //^^^^ InnerStruct @@ -616,12 +616,12 @@ impl Iterator for IntoIter { fn main() { let mut data = Vec::new(); - //^^^^ Vec<&'static str> + //^^^^ Vec<&str> data.push("foo"); for i in data { - //^ &'static str + //^ &str let z = i; - //^ &'static str + //^ &str } } "#, @@ -1015,7 +1015,7 @@ fn test(t: T) { "#, expect![[r#" fn test(t: T) { - let f = |a: i32, b: &'static str, c: T| {}; + let f = |a: i32, b: &str, c: T| {}; let result: () = f(42, "", t); } "#]], diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index 983ed3684a4f..1db4f8ecd6ba 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -437,6 +437,7 @@ define_symbols! { rustc_safe_intrinsic, rustc_skip_array_during_method_dispatch, rustc_skip_during_method_dispatch, + rustc_force_inline, semitransparent, shl_assign, shl, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs index 1b940c70da66..8a04bc7798f8 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs @@ -1061,7 +1061,7 @@ fn main() { ), work_done_progress_params: Default::default(), }); - assert!(res.to_string().contains("&'static str")); + assert!(res.to_string().contains("&str")); let res = server.send_request::(HoverParams { text_document_position_params: TextDocumentPositionParams::new( @@ -1070,7 +1070,7 @@ fn main() { ), work_done_progress_params: Default::default(), }); - assert!(res.to_string().contains("&'static str")); + assert!(res.to_string().contains("&str")); server.request::( GotoDefinitionParams { diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index 7c3e7fea1bde..696928b522f9 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -169,6 +169,17 @@ pub mod marker { // region:phantom_data #[lang = "phantom_data"] pub struct PhantomData; + + // region:clone + impl Clone for PhantomData { + fn clone(&self) -> Self { Self } + } + // endregion:clone + + // region:copy + impl Copy for PhantomData {} + // endregion:copy + // endregion:phantom_data // region:discriminant @@ -1147,7 +1158,7 @@ pub mod fmt { pub struct Error; pub type Result = crate::result::Result<(), Error>; - pub struct Formatter<'a>; + pub struct Formatter<'a>(&'a ()); pub struct DebugTuple; pub struct DebugStruct; impl Formatter<'_> { @@ -1620,6 +1631,12 @@ pub mod iter { { loop {} } + fn collect>(self) -> B + where + Self: Sized, + { + loop {} + } // endregion:iterators } impl Iterator for &mut I { @@ -1689,10 +1706,13 @@ pub mod iter { loop {} } } + pub trait FromIterator: Sized { + fn from_iter>(iter: T) -> Self; + } } - pub use self::collect::IntoIterator; + pub use self::collect::{IntoIterator, FromIterator}; } - pub use self::traits::{IntoIterator, Iterator}; + pub use self::traits::{IntoIterator, FromIterator, Iterator}; } // endregion:iterator @@ -1988,7 +2008,7 @@ pub mod prelude { convert::AsRef, // :as_ref convert::{From, Into, TryFrom, TryInto}, // :from default::Default, // :default - iter::{IntoIterator, Iterator}, // :iterator + iter::{IntoIterator, Iterator, FromIterator}, // :iterator macros::builtin::{derive, derive_const}, // :derive marker::Copy, // :copy marker::Send, // :send diff --git a/src/tools/rust-analyzer/xtask/src/tidy.rs b/src/tools/rust-analyzer/xtask/src/tidy.rs index f91192b0076b..0462835f0675 100644 --- a/src/tools/rust-analyzer/xtask/src/tidy.rs +++ b/src/tools/rust-analyzer/xtask/src/tidy.rs @@ -235,6 +235,10 @@ impl TidyDocs { return; } + if is_ported_from_rustc(path, &["crates/hir-ty/src/next_solver"]) { + return; + } + let first_line = match text.lines().next() { Some(it) => it, None => return, @@ -290,6 +294,11 @@ fn is_exclude_dir(p: &Path, dirs_to_exclude: &[&str]) -> bool { .any(|it| dirs_to_exclude.contains(&it)) } +fn is_ported_from_rustc(p: &Path, dirs_to_exclude: &[&str]) -> bool { + let p = p.strip_prefix(project_root()).unwrap(); + dirs_to_exclude.iter().any(|exclude| p.starts_with(exclude)) +} + #[derive(Default)] struct TidyMarks { hits: HashSet, From bf73aac8cbdfeff983dce44b883993be2ae89151 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Mon, 15 Sep 2025 18:34:02 +0200 Subject: [PATCH 535/682] compiletest: Make `./x test --test-args ...` work again It accidentally broke with a48c8e337d1. The intention of that commit was to keep existing behavior if `--exact` is not used, but it had a bug. This commit fixes that bug. --- src/tools/compiletest/src/executor.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/tools/compiletest/src/executor.rs b/src/tools/compiletest/src/executor.rs index 37cc17351d5f..c8e13d445738 100644 --- a/src/tools/compiletest/src/executor.rs +++ b/src/tools/compiletest/src/executor.rs @@ -295,11 +295,14 @@ fn filter_tests(opts: &Config, tests: Vec) -> Vec let mut filtered = tests; let matches_filter = |test: &CollectedTest, filter_str: &str| { - let filterable_path = test.desc.filterable_path.as_str(); if opts.filter_exact { - filterable_path == filter_str + // When `--exact` is used we must use `filterable_path` to get + // reasonable filtering behavior. + test.desc.filterable_path.as_str() == filter_str } else { - filterable_path.contains(filter_str) + // For compatibility we use the name (which includes the full path) + // if `--exact` is not used. + test.desc.name.contains(filter_str) } }; From 487cdbc07d4bc73b8c52c7a35a99d2e0a64c6f55 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Tue, 16 Sep 2025 01:44:00 +0900 Subject: [PATCH 536/682] fix: More precise clause filtering for `explicit_*_predicates_of` --- .../crates/hir-ty/src/next_solver/interner.rs | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 7b6e8a1073d7..76d10f7dcc50 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -1330,16 +1330,23 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { self, def_id: Self::TraitId, ) -> EarlyBinder> { + let is_self = |ty: Ty<'db>| match ty.kind() { + rustc_type_ir::TyKind::Param(param) => param.index == 0, + _ => false, + }; + let predicates: Vec<(Clause<'db>, Span)> = self .db() .generic_predicates_ns(def_id.0.into()) .iter() .filter(|p| match p.kind().skip_binder() { - rustc_type_ir::ClauseKind::Trait(tr) => match tr.self_ty().kind() { - rustc_type_ir::TyKind::Param(param) => param.index == 0, - _ => false, - }, - _ => true, + // rustc has the following assertion: + // https://github.com/rust-lang/rust/blob/52618eb338609df44978b0ca4451ab7941fd1c7a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs#L525-L608 + rustc_type_ir::ClauseKind::Trait(it) => is_self(it.self_ty()), + rustc_type_ir::ClauseKind::TypeOutlives(it) => is_self(it.0), + rustc_type_ir::ClauseKind::Projection(it) => is_self(it.self_ty()), + rustc_type_ir::ClauseKind::HostEffect(it) => is_self(it.self_ty()), + _ => false, }) .cloned() .map(|p| (p, Span::dummy())) @@ -1367,7 +1374,14 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { .generic_predicates_ns(def_id.try_into().unwrap()) .iter() .filter(|p| match p.kind().skip_binder() { - rustc_type_ir::ClauseKind::Trait(tr) => is_self_or_assoc(tr.self_ty()), + rustc_type_ir::ClauseKind::Trait(it) => is_self_or_assoc(it.self_ty()), + rustc_type_ir::ClauseKind::TypeOutlives(it) => is_self_or_assoc(it.0), + rustc_type_ir::ClauseKind::Projection(it) => is_self_or_assoc(it.self_ty()), + rustc_type_ir::ClauseKind::HostEffect(it) => is_self_or_assoc(it.self_ty()), + // FIXME: Not sure is this correct to allow other clauses but we might replace + // `generic_predicates_ns` query here with something closer to rustc's + // `implied_bounds_with_filter`, which is more granular lowering than this + // "lower at once and then filter" implementation. _ => true, }) .cloned() From c4da39cca328e1aaabb513f20d32c55bbbc49057 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 15 Sep 2025 18:48:57 +0200 Subject: [PATCH 537/682] Recognize canonical `?` pattern with `Result` --- clippy_lints/src/question_mark.rs | 21 +++++-- tests/ui/question_mark.fixed | 14 ++++- tests/ui/question_mark.rs | 26 ++++++++- tests/ui/question_mark.stderr | 92 ++++++++++++++++++++----------- 4 files changed, 113 insertions(+), 40 deletions(-) diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index de12a25b03df..4aa100a50e05 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -8,9 +8,9 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::{ - eq_expr_value, higher, is_else_clause, is_in_const_context, is_lint_allowed, is_path_lang_item, is_res_lang_ctor, - pat_and_expr_can_be_question_mark, path_res, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt, - span_contains_cfg, span_contains_comment, sym, + eq_expr_value, fn_def_id_with_node_args, higher, is_else_clause, is_in_const_context, is_lint_allowed, + is_path_lang_item, is_res_lang_ctor, pat_and_expr_can_be_question_mark, path_res, path_to_local, path_to_local_id, + peel_blocks, peel_blocks_with_stmt, span_contains_cfg, span_contains_comment, sym, }; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk}; @@ -393,8 +393,8 @@ fn check_arm_is_none_or_err<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &A && let ExprKind::Ret(Some(wrapped_ret_expr)) = arm_body.kind && let ExprKind::Call(ok_ctor, [ret_expr]) = wrapped_ret_expr.kind && is_res_lang_ctor(cx, path_res(cx, ok_ctor), ResultErr) - // check `...` is `val` from binding - && path_to_local_id(ret_expr, ok_val) + // check if `...` is `val` from binding or `val.into()` + && is_local_or_local_into(cx, ret_expr, ok_val) { true } else { @@ -417,6 +417,17 @@ fn check_arm_is_none_or_err<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &A } } +/// Check if `expr` is `val` or `val.into()` +fn is_local_or_local_into(cx: &LateContext<'_>, expr: &Expr<'_>, val: HirId) -> bool { + let is_into_call = fn_def_id_with_node_args(cx, expr) + .and_then(|(fn_def_id, _)| cx.tcx.trait_of_assoc(fn_def_id)) + .is_some_and(|trait_def_id| cx.tcx.is_diagnostic_item(sym::Into, trait_def_id)); + match expr.kind { + ExprKind::MethodCall(_, recv, [], _) | ExprKind::Call(_, [recv]) => is_into_call && path_to_local_id(recv, val), + _ => path_to_local_id(expr, val), + } +} + fn check_arms_are_try<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm1: &Arm<'tcx>, arm2: &Arm<'tcx>) -> bool { (check_arm_is_some_or_ok(cx, mode, arm1) && check_arm_is_none_or_err(cx, mode, arm2)) || (check_arm_is_some_or_ok(cx, mode, arm2) && check_arm_is_none_or_err(cx, mode, arm1)) diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed index 8d6f5fbadca5..ac81b324c204 100644 --- a/tests/ui/question_mark.fixed +++ b/tests/ui/question_mark.fixed @@ -1,6 +1,4 @@ #![feature(try_blocks)] -#![allow(unreachable_code)] -#![allow(dead_code)] #![allow(clippy::unnecessary_wraps)] use std::sync::MutexGuard; @@ -465,3 +463,15 @@ fn issue_13642(x: Option) -> Option<()> { None } + +fn issue_15679() -> Result { + let some_result: Result = todo!(); + + some_result?; + + some_result?; + + some_result?; + + Ok(0) +} diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index f13eee29c113..b5866dac6b8f 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -1,6 +1,4 @@ #![feature(try_blocks)] -#![allow(unreachable_code)] -#![allow(dead_code)] #![allow(clippy::unnecessary_wraps)] use std::sync::MutexGuard; @@ -561,3 +559,27 @@ fn issue_13642(x: Option) -> Option<()> { None } + +fn issue_15679() -> Result { + let some_result: Result = todo!(); + + match some_result { + //~^ question_mark + Ok(val) => val, + Err(err) => return Err(err.into()), + }; + + match some_result { + //~^ question_mark + Ok(val) => val, + Err(err) => return Err(Into::into(err)), + }; + + match some_result { + //~^ question_mark + Ok(val) => val, + Err(err) => return Err(<&str as Into>::into(err)), + }; + + Ok(0) +} diff --git a/tests/ui/question_mark.stderr b/tests/ui/question_mark.stderr index d8ce4420aeeb..1ecd936292e5 100644 --- a/tests/ui/question_mark.stderr +++ b/tests/ui/question_mark.stderr @@ -1,5 +1,5 @@ error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:9:5 + --> tests/ui/question_mark.rs:7:5 | LL | / if a.is_none() { LL | | @@ -11,7 +11,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::question_mark)]` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:55:9 + --> tests/ui/question_mark.rs:53:9 | LL | / if (self.opt).is_none() { LL | | @@ -20,7 +20,7 @@ LL | | } | |_________^ help: replace it with: `(self.opt)?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:60:9 + --> tests/ui/question_mark.rs:58:9 | LL | / if self.opt.is_none() { LL | | @@ -29,7 +29,7 @@ LL | | } | |_________^ help: replace it with: `self.opt?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:65:17 + --> tests/ui/question_mark.rs:63:17 | LL | let _ = if self.opt.is_none() { | _________________^ @@ -41,7 +41,7 @@ LL | | }; | |_________^ help: replace it with: `Some(self.opt?)` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:72:17 + --> tests/ui/question_mark.rs:70:17 | LL | let _ = if let Some(x) = self.opt { | _________________^ @@ -53,7 +53,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:90:9 + --> tests/ui/question_mark.rs:88:9 | LL | / if self.opt.is_none() { LL | | @@ -62,7 +62,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:99:9 + --> tests/ui/question_mark.rs:97:9 | LL | / if self.opt.is_none() { LL | | @@ -71,7 +71,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:108:9 + --> tests/ui/question_mark.rs:106:9 | LL | / if self.opt.is_none() { LL | | @@ -80,7 +80,7 @@ LL | | } | |_________^ help: replace it with: `self.opt.as_ref()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:116:26 + --> tests/ui/question_mark.rs:114:26 | LL | let v: &Vec<_> = if let Some(ref v) = self.opt { | __________________________^ @@ -92,7 +92,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt.as_ref()?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:127:17 + --> tests/ui/question_mark.rs:125:17 | LL | let v = if let Some(v) = self.opt { | _________________^ @@ -104,7 +104,7 @@ LL | | }; | |_________^ help: replace it with: `self.opt?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:149:5 + --> tests/ui/question_mark.rs:147:5 | LL | / if f().is_none() { LL | | @@ -113,7 +113,7 @@ LL | | } | |_____^ help: replace it with: `f()?;` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:154:16 + --> tests/ui/question_mark.rs:152:16 | LL | let _val = match f() { | ________________^ @@ -124,7 +124,7 @@ LL | | }; | |_____^ help: try instead: `f()?` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:165:5 + --> tests/ui/question_mark.rs:163:5 | LL | / match f() { LL | | @@ -134,7 +134,7 @@ LL | | }; | |_____^ help: try instead: `f()?` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:171:5 + --> tests/ui/question_mark.rs:169:5 | LL | / match opt_none!() { LL | | @@ -144,13 +144,13 @@ LL | | }; | |_____^ help: try instead: `opt_none!()?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:198:13 + --> tests/ui/question_mark.rs:196:13 | LL | let _ = if let Ok(x) = x { x } else { return x }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:201:5 + --> tests/ui/question_mark.rs:199:5 | LL | / if x.is_err() { LL | | @@ -159,7 +159,7 @@ LL | | } | |_____^ help: replace it with: `x?;` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:206:16 + --> tests/ui/question_mark.rs:204:16 | LL | let _val = match func_returning_result() { | ________________^ @@ -170,7 +170,7 @@ LL | | }; | |_____^ help: try instead: `func_returning_result()?` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:212:5 + --> tests/ui/question_mark.rs:210:5 | LL | / match func_returning_result() { LL | | @@ -180,7 +180,7 @@ LL | | }; | |_____^ help: try instead: `func_returning_result()?` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:304:5 + --> tests/ui/question_mark.rs:302:5 | LL | / if let Err(err) = func_returning_result() { LL | | @@ -189,7 +189,7 @@ LL | | } | |_____^ help: replace it with: `func_returning_result()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:312:5 + --> tests/ui/question_mark.rs:310:5 | LL | / if let Err(err) = func_returning_result() { LL | | @@ -198,7 +198,7 @@ LL | | } | |_____^ help: replace it with: `func_returning_result()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:395:13 + --> tests/ui/question_mark.rs:393:13 | LL | / if a.is_none() { LL | | @@ -208,7 +208,7 @@ LL | | } | |_____________^ help: replace it with: `a?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:456:5 + --> tests/ui/question_mark.rs:454:5 | LL | / let Some(v) = bar.foo.owned.clone() else { LL | | return None; @@ -216,7 +216,7 @@ LL | | }; | |______^ help: replace it with: `let v = bar.foo.owned.clone()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:471:5 + --> tests/ui/question_mark.rs:469:5 | LL | / let Some(ref x) = foo.opt_x else { LL | | return None; @@ -224,7 +224,7 @@ LL | | }; | |______^ help: replace it with: `let x = foo.opt_x.as_ref()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:481:5 + --> tests/ui/question_mark.rs:479:5 | LL | / let Some(ref mut x) = foo.opt_x else { LL | | return None; @@ -232,7 +232,7 @@ LL | | }; | |______^ help: replace it with: `let x = foo.opt_x.as_mut()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:492:5 + --> tests/ui/question_mark.rs:490:5 | LL | / let Some(ref x @ ref y) = foo.opt_x else { LL | | return None; @@ -240,7 +240,7 @@ LL | | }; | |______^ help: replace it with: `let x @ y = foo.opt_x.as_ref()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:496:5 + --> tests/ui/question_mark.rs:494:5 | LL | / let Some(ref x @ WrapperStructWithString(_)) = bar else { LL | | return None; @@ -248,7 +248,7 @@ LL | | }; | |______^ help: replace it with: `let x @ &WrapperStructWithString(_) = bar.as_ref()?;` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:500:5 + --> tests/ui/question_mark.rs:498:5 | LL | / let Some(ref mut x @ WrapperStructWithString(_)) = bar else { LL | | return None; @@ -256,7 +256,7 @@ LL | | }; | |______^ help: replace it with: `let x @ &mut WrapperStructWithString(_) = bar.as_mut()?;` error: this block may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:522:5 + --> tests/ui/question_mark.rs:520:5 | LL | / if arg.is_none() { LL | | @@ -265,7 +265,7 @@ LL | | } | |_____^ help: replace it with: `arg?;` error: this `match` expression can be replaced with `?` - --> tests/ui/question_mark.rs:526:15 + --> tests/ui/question_mark.rs:524:15 | LL | let val = match arg { | _______________^ @@ -276,12 +276,42 @@ LL | | }; | |_____^ help: try instead: `arg?` error: this `let...else` may be rewritten with the `?` operator - --> tests/ui/question_mark.rs:536:5 + --> tests/ui/question_mark.rs:534:5 | LL | / let Some(a) = *a else { LL | | return None; LL | | }; | |______^ help: replace it with: `let a = (*a)?;` -error: aborting due to 30 previous errors +error: this `match` expression can be replaced with `?` + --> tests/ui/question_mark.rs:566:5 + | +LL | / match some_result { +LL | | +LL | | Ok(val) => val, +LL | | Err(err) => return Err(err.into()), +LL | | }; + | |_____^ help: try instead: `some_result?` + +error: this `match` expression can be replaced with `?` + --> tests/ui/question_mark.rs:572:5 + | +LL | / match some_result { +LL | | +LL | | Ok(val) => val, +LL | | Err(err) => return Err(Into::into(err)), +LL | | }; + | |_____^ help: try instead: `some_result?` + +error: this `match` expression can be replaced with `?` + --> tests/ui/question_mark.rs:578:5 + | +LL | / match some_result { +LL | | +LL | | Ok(val) => val, +LL | | Err(err) => return Err(<&str as Into>::into(err)), +LL | | }; + | |_____^ help: try instead: `some_result?` + +error: aborting due to 33 previous errors From 2adaa5dae2b9b63a50fa0200650aeee918cb76fb Mon Sep 17 00:00:00 2001 From: Jynn Nelson Date: Mon, 15 Sep 2025 10:33:39 -0700 Subject: [PATCH 538/682] Bump rustfix 0.8.1 -> 0.8.7 This commit can be replicated by running `cargo update -p rustfix --precise 0.8.7 && x test ui --bless`. --- The reasons this affects UI tests is as follows: - The UI test suite runs rustc with `-Z deduplicate-diagnostics=no --error-format=json`, which means that rustc emits multiple errors containing identical suggestions. That caused the weird-looking code that had multiple `X: Copy` suggestions. - Those suggestions are interpreted not by rustc itself, but by the `rustfix` library, maintained by cargo but published as a separate crates.io library and used by compiletest. - Sometime between rustfix 0.8.1 and 0.8.7 (probably in cargo 14747, but it's hard to tell because rustfix's versioning doesn't match cargo's), rustfix got smarter and stopped applying duplicate suggestions. Update rustfix to match cargo's behavior. Ideally, we would always share a version of rustfix between cargo and rustc (perhaps with a path dependency?), to make sure we are testing the behavior we ship. But for now, just manually update it to match. Note that the latest version of rustfix published to crates.io is 0.9.1, not 0.8.7. But 0.9.1 is not the version used in cargo, which is 0.9.3. Rather than trying to match versions exactly, I just updated rustfix to the latest in the 0.8 branch. --- Cargo.lock | 4 ++-- .../associated-types/associated-types-for-unimpl-trait.fixed | 2 +- tests/ui/lifetimes/issue-105507.fixed | 2 +- tests/ui/parser/expr-as-stmt.fixed | 4 ++-- .../ui/suggestions/missing-bound-in-derive-copy-impl-2.fixed | 2 +- .../ui/suggestions/missing-bound-in-derive-copy-impl-3.fixed | 2 +- tests/ui/suggestions/trait-impl-bound-suggestions.fixed | 4 ++-- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4677d34d2a63..d39cfefea0c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4817,9 +4817,9 @@ dependencies = [ [[package]] name = "rustfix" -version = "0.8.1" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81864b097046da5df3758fdc6e4822bbb70afa06317e8ca45ea1b51cb8c5e5a4" +checksum = "82fa69b198d894d84e23afde8e9ab2af4400b2cba20d6bf2b428a8b01c222c5a" dependencies = [ "serde", "serde_json", diff --git a/tests/ui/associated-types/associated-types-for-unimpl-trait.fixed b/tests/ui/associated-types/associated-types-for-unimpl-trait.fixed index bce6148f9e19..ae4d0107aee5 100644 --- a/tests/ui/associated-types/associated-types-for-unimpl-trait.fixed +++ b/tests/ui/associated-types/associated-types-for-unimpl-trait.fixed @@ -8,7 +8,7 @@ trait Get { } trait Other { - fn uhoh(&self, foo: U, bar: ::Value) where Self: Sized, Self: Get, Self: Get {} + fn uhoh(&self, foo: U, bar: ::Value) where Self: Sized, Self: Get {} //~^ ERROR the trait bound `Self: Get` is not satisfied //~| ERROR the trait bound `Self: Get` is not satisfied } diff --git a/tests/ui/lifetimes/issue-105507.fixed b/tests/ui/lifetimes/issue-105507.fixed index 46d4f14a2457..a3c4e5784b87 100644 --- a/tests/ui/lifetimes/issue-105507.fixed +++ b/tests/ui/lifetimes/issue-105507.fixed @@ -31,7 +31,7 @@ impl ProjectedMyTrait for T fn require_trait(_: T) {} -fn foo(wrap: Wrapper<'_, Option>, wrap1: Wrapper<'_, Option>) { +fn foo(wrap: Wrapper<'_, Option>, wrap1: Wrapper<'_, Option>) { //~^ HELP consider restricting the type parameter to the `'static` lifetime //~| HELP consider restricting the type parameter to the `'static` lifetime require_trait(wrap); diff --git a/tests/ui/parser/expr-as-stmt.fixed b/tests/ui/parser/expr-as-stmt.fixed index bfae55047ed1..b3a491200ed1 100644 --- a/tests/ui/parser/expr-as-stmt.fixed +++ b/tests/ui/parser/expr-as-stmt.fixed @@ -66,7 +66,7 @@ fn asteroids() -> impl FnOnce() -> bool { // https://github.com/rust-lang/rust/issues/105179 fn r#match() -> i32 { - ((match () { () => 1 })) + match () { () => 1 } //~ ERROR expected expression, found `+` + (match () { () => 1 }) + match () { () => 1 } //~ ERROR expected expression, found `+` //~^ ERROR mismatched types } @@ -82,7 +82,7 @@ fn matches() -> bool { (match () { _ => true }) && match () { _ => true }; //~ ERROR mismatched types //~^ ERROR expected `;`, found keyword `match` (match () { _ => true }) && true; //~ ERROR mismatched types - ((match () { _ => true })) && true //~ ERROR mismatched types + (match () { _ => true }) && true //~ ERROR mismatched types //~^ ERROR mismatched types } fn main() {} diff --git a/tests/ui/suggestions/missing-bound-in-derive-copy-impl-2.fixed b/tests/ui/suggestions/missing-bound-in-derive-copy-impl-2.fixed index 99433f733204..8a2be310e0d8 100644 --- a/tests/ui/suggestions/missing-bound-in-derive-copy-impl-2.fixed +++ b/tests/ui/suggestions/missing-bound-in-derive-copy-impl-2.fixed @@ -8,7 +8,7 @@ pub struct Vector2 { } #[derive(Debug, Copy, Clone)] -pub struct AABB { +pub struct AABB { pub loc: Vector2, //~ ERROR the trait bound `K: Copy` is not satisfied //~^ ERROR the trait bound `K: Copy` is not satisfied //~| ERROR the trait bound `K: Copy` is not satisfied diff --git a/tests/ui/suggestions/missing-bound-in-derive-copy-impl-3.fixed b/tests/ui/suggestions/missing-bound-in-derive-copy-impl-3.fixed index 6da3e351ffbd..74df1d7c7cfd 100644 --- a/tests/ui/suggestions/missing-bound-in-derive-copy-impl-3.fixed +++ b/tests/ui/suggestions/missing-bound-in-derive-copy-impl-3.fixed @@ -8,7 +8,7 @@ pub struct Vector2{ } #[derive(Debug, Copy, Clone)] //~ ERROR the trait `Copy` cannot be implemented for this type -pub struct AABB{ +pub struct AABB{ pub loc: Vector2, //~ ERROR `K` doesn't implement `Debug` //~^ ERROR `K` doesn't implement `Debug` pub size: Vector2 //~ ERROR `K` doesn't implement `Debug` diff --git a/tests/ui/suggestions/trait-impl-bound-suggestions.fixed b/tests/ui/suggestions/trait-impl-bound-suggestions.fixed index 9d3168f5acd6..49793b4b6f47 100644 --- a/tests/ui/suggestions/trait-impl-bound-suggestions.fixed +++ b/tests/ui/suggestions/trait-impl-bound-suggestions.fixed @@ -10,7 +10,7 @@ struct ConstrainedStruct { } #[allow(dead_code)] -trait InsufficientlyConstrainedGeneric where Self: Sized, X: std::marker::Copy, X: std::marker::Copy { +trait InsufficientlyConstrainedGeneric where Self: Sized, X: std::marker::Copy { fn return_the_constrained_type(&self, x: X) -> ConstrainedStruct { //~^ ERROR the trait bound `X: Copy` is not satisfied ConstrainedStruct { x } @@ -20,7 +20,7 @@ trait InsufficientlyConstrainedGeneric where Self: Sized, X: std::marker:: // Regression test for #120838 #[allow(dead_code)] -trait InsufficientlyConstrainedGenericWithEmptyWhere where Self: Sized, X: std::marker::Copy, X: std::marker::Copy { +trait InsufficientlyConstrainedGenericWithEmptyWhere where Self: Sized, X: std::marker::Copy { fn return_the_constrained_type(&self, x: X) -> ConstrainedStruct { //~^ ERROR the trait bound `X: Copy` is not satisfied ConstrainedStruct { x } From b8cb80cf80572e9d287445af16608b68ec436099 Mon Sep 17 00:00:00 2001 From: binarycat Date: Mon, 15 Sep 2025 13:38:04 -0500 Subject: [PATCH 539/682] improve internal bootstrap docs --- src/bootstrap/README.md | 15 +++++++++++++++ src/bootstrap/src/core/build_steps/tool.rs | 5 +++++ 2 files changed, 20 insertions(+) diff --git a/src/bootstrap/README.md b/src/bootstrap/README.md index 2965174b45bc..12e09cb07dbb 100644 --- a/src/bootstrap/README.md +++ b/src/bootstrap/README.md @@ -63,6 +63,21 @@ build/ debug/ release/ + # Build directory for various tools like `typos` that are only ever + # built for the host system, and always with stage0 cargo. + misc-tools/ + bin/ + target/ + + # Directory where js dependencies like tsc and eslint are stored. + node_modules/ + .bin/ + + # Copy of package.json and package-lock.json, because npm requires these + # to be in the same directory as `node_modules`. + package.json + package-lock.json + # Output of the dist-related steps like dist-std, dist-rustc, and dist-docs dist/ diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index dcc4898cae16..4f096d50ea5c 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -394,6 +394,9 @@ macro_rules! bootstrap_tool { } impl<'a> Builder<'a> { + /// Ensure a tool is built, then get the path to its executable. + /// + /// The actual building, if any, will be handled via [`ToolBuild`]. pub fn tool_exe(&self, tool: Tool) -> PathBuf { match tool { $(Tool::$name => @@ -1552,6 +1555,8 @@ pub const TEST_FLOAT_PARSE_ALLOW_FEATURES: &str = "f16,cfg_target_has_reliable_f impl Builder<'_> { /// Gets a `BootstrapCommand` which is ready to run `tool` in `stage` built for /// `host`. + /// + /// This also ensures that the given tool is built (using [`ToolBuild`]). pub fn tool_cmd(&self, tool: Tool) -> BootstrapCommand { let mut cmd = command(self.tool_exe(tool)); let compiler = self.compiler(0, self.config.host_target); From 29ca09be09a88f359adc7879e2592f36f6c74a64 Mon Sep 17 00:00:00 2001 From: binarycat Date: Mon, 15 Sep 2025 14:42:44 -0500 Subject: [PATCH 540/682] bootstrap: lower verbosity of cargo to one less than bootstrap's the main thing this does is eliminate the "Fresh ..." output when `--verbose` is only passed once. --- src/bootstrap/bootstrap.py | 3 ++- src/bootstrap/src/core/build_steps/test.rs | 3 --- src/bootstrap/src/core/builder/cargo.rs | 7 ++++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 2ece53eb0cc9..41c8ef17f0b5 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -1145,7 +1145,8 @@ class RustBuild(object): os.path.join(self.rust_root, "src/bootstrap/Cargo.toml"), "-Zroot-dir=" + self.rust_root, ] - args.extend("--verbose" for _ in range(self.verbose)) + # verbose cargo output is very noisy, so only enable it with -vv + args.extend("--verbose" for _ in range(self.verbose - 1)) if "BOOTSTRAP_TRACING" in env: args.append("--features=tracing") diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 723ba80eaf82..e7f5879b5f51 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -3418,9 +3418,6 @@ impl Step for TierCheck { ); cargo.arg(builder.src.join("src/doc/rustc/src/platform-support.md")); cargo.arg(builder.rustc(self.test_compiler)); - if builder.is_verbose() { - cargo.arg("--verbose"); - } let _guard = builder.msg_test( "platform support check", diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 924bb4adb42d..8e65ec7ce50a 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -1133,7 +1133,7 @@ impl Builder<'_> { cargo.env("RUSTC_BACKTRACE_ON_ICE", "1"); } - if self.is_verbose() { + if self.is_verbose_than(1) { // This provides very useful logs especially when debugging build cache-related stuff. cargo.env("CARGO_LOG", "cargo::core::compiler::fingerprint=info"); } @@ -1275,8 +1275,9 @@ impl Builder<'_> { cargo.env("WINAPI_NO_BUNDLED_LIBRARIES", "1"); } - for _ in 0..self.verbosity { - cargo.arg("-v"); + // verbose cargo output is very noisy, so only enable it with -vv + for _ in 0..self.verbosity.saturating_sub(1) { + cargo.arg("--verbose"); } match (mode, self.config.rust_codegen_units_std, self.config.rust_codegen_units) { From fa7e474f9ca755ac102debcf6cd0ce77313ee0bf Mon Sep 17 00:00:00 2001 From: ash Date: Mon, 15 Sep 2025 13:55:20 -0600 Subject: [PATCH 541/682] remove FIXME from `has_significant_drop`, replaced with checking non_region_infer --- compiler/rustc_middle/src/ty/util.rs | 9 +-------- tests/ui/type-inference/has_sigdrop.rs | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 8 deletions(-) create mode 100644 tests/ui/type-inference/has_sigdrop.rs diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 029586a9c554..b79b67c5927b 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -1359,6 +1359,7 @@ impl<'tcx> Ty<'tcx> { /// 2229 drop reorder migration analysis. #[inline] pub fn has_significant_drop(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> bool { + assert!(!self.has_non_region_infer()); // Avoid querying in simple cases. match needs_drop_components(tcx, self) { Err(AlwaysRequiresDrop) => true, @@ -1371,14 +1372,6 @@ impl<'tcx> Ty<'tcx> { _ => self, }; - // FIXME(#86868): We should be canonicalizing, or else moving this to a method of inference - // context, or *something* like that, but for now just avoid passing inference - // variables to queries that can't cope with them. Instead, conservatively - // return "true" (may change drop order). - if query_ty.has_infer() { - return true; - } - // This doesn't depend on regions, so try to minimize distinct // query keys used. let erased = tcx.normalize_erasing_regions(typing_env, query_ty); diff --git a/tests/ui/type-inference/has_sigdrop.rs b/tests/ui/type-inference/has_sigdrop.rs new file mode 100644 index 000000000000..c3d835cfe16f --- /dev/null +++ b/tests/ui/type-inference/has_sigdrop.rs @@ -0,0 +1,18 @@ +//@ run-pass +// Inference, canonicalization, and significant drops should work nicely together. +// Related issue: #86868 + +#[clippy::has_significant_drop] +struct DropGuy {} + +fn creator() -> DropGuy { + DropGuy {} +} + +fn dropper() { + let _ = creator(); +} + +fn main() { + dropper(); +} From d58061e6131f6141985faadb6794af540f81b1db Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Tue, 16 Sep 2025 01:22:10 +0000 Subject: [PATCH 542/682] Restrict simple assignment condition. --- compiler/rustc_mir_transform/src/dest_prop.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index b9ab0a9feee6..c57483a68114 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -596,8 +596,14 @@ fn save_as_intervals<'tcx>( // as behaving so by default. // We make an exception for simple assignments `_a.stuff = {copy|move} _b.stuff`, // as marking `_b` live here would prevent unification. - let is_simple_assignment = - matches!(stmt.kind, StatementKind::Assign(box (_, Rvalue::Use(_)))); + let is_simple_assignment = match stmt.kind { + StatementKind::Assign(box ( + lhs, + Rvalue::CopyForDeref(rhs) + | Rvalue::Use(Operand::Copy(rhs) | Operand::Move(rhs)), + )) => lhs.projection == rhs.projection, + _ => false, + }; VisitPlacesWith(|place: Place<'tcx>, ctxt| { if let Some(relevant) = relevant.shrink[place.local] { match DefUse::for_place(place, ctxt) { From 181bd119d07022db19b184ac23a796daa0e45246 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 5 Sep 2025 08:25:28 +0200 Subject: [PATCH 543/682] fix: Fix expand macro recursively not working correctly for nested macro calls --- .../crates/ide/src/expand_macro.rs | 95 ++++++++++--------- 1 file changed, 49 insertions(+), 46 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs index ad84eacfb3e8..094a4a7036c4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs +++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs @@ -1,5 +1,5 @@ use hir::db::ExpandDatabase; -use hir::{ExpandResult, InFile, InRealFile, Semantics}; +use hir::{ExpandResult, InFile, Semantics}; use ide_db::{ FileId, RootDatabase, base_db::Crate, helpers::pick_best_token, syntax_helpers::prettify_macro_expansion, @@ -87,52 +87,55 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< return derive; } - let mut anc = sema - .descend_token_into_include_expansion(InRealFile::new(file_id, tok)) - .value - .parent_ancestors(); - let mut span_map = SpanMap::empty(); - let mut error = String::new(); - let (name, expanded, kind) = loop { - let node = anc.next()?; + let syntax_token = sema.descend_into_macros_exact(tok); + 'tokens: for syntax_token in syntax_token { + let mut anc = syntax_token.parent_ancestors(); + let mut span_map = SpanMap::empty(); + let mut error = String::new(); + let (name, expanded, kind) = loop { + let Some(node) = anc.next() else { + continue 'tokens; + }; - if let Some(item) = ast::Item::cast(node.clone()) - && let Some(def) = sema.resolve_attr_macro_call(&item) - { - break ( - def.name(db).display(db, file_id.edition(db)).to_string(), - expand_macro_recur(&sema, &item, &mut error, &mut span_map, TextSize::new(0))?, - SyntaxKind::MACRO_ITEMS, - ); + if let Some(item) = ast::Item::cast(node.clone()) + && let Some(def) = sema.resolve_attr_macro_call(&item) + { + break ( + def.name(db).display(db, file_id.edition(db)).to_string(), + expand_macro_recur(&sema, &item, &mut error, &mut span_map, TextSize::new(0))?, + SyntaxKind::MACRO_ITEMS, + ); + } + if let Some(mac) = ast::MacroCall::cast(node) { + let mut name = mac.path()?.segment()?.name_ref()?.to_string(); + name.push('!'); + let syntax_kind = + mac.syntax().parent().map(|it| it.kind()).unwrap_or(SyntaxKind::MACRO_ITEMS); + break ( + name, + expand_macro_recur( + &sema, + &ast::Item::MacroCall(mac), + &mut error, + &mut span_map, + TextSize::new(0), + )?, + syntax_kind, + ); + } + }; + + // FIXME: + // macro expansion may lose all white space information + // But we hope someday we can use ra_fmt for that + let mut expansion = format(db, kind, position.file_id, expanded, &span_map, krate); + + if !error.is_empty() { + expansion.insert_str(0, &format!("Expansion had errors:{error}\n\n")); } - if let Some(mac) = ast::MacroCall::cast(node) { - let mut name = mac.path()?.segment()?.name_ref()?.to_string(); - name.push('!'); - let syntax_kind = - mac.syntax().parent().map(|it| it.kind()).unwrap_or(SyntaxKind::MACRO_ITEMS); - break ( - name, - expand_macro_recur( - &sema, - &ast::Item::MacroCall(mac), - &mut error, - &mut span_map, - TextSize::new(0), - )?, - syntax_kind, - ); - } - }; - - // FIXME: - // macro expansion may lose all white space information - // But we hope someday we can use ra_fmt for that - let mut expansion = format(db, kind, position.file_id, expanded, &span_map, krate); - - if !error.is_empty() { - expansion.insert_str(0, &format!("Expansion had errors:{error}\n\n")); + return Some(ExpandedMacro { name, expansion }); } - Some(ExpandedMacro { name, expansion }) + None } fn expand_macro_recur( @@ -752,8 +755,8 @@ fn test() { } "#, expect![[r#" - my_concat! - "<>hi""#]], + concat! + "<>""#]], ); } From 8a9ec5dd0dbf81e6337fb272aa0b90cf6e8d5655 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 23 Aug 2025 09:00:35 +0200 Subject: [PATCH 544/682] fix: Only compute unstable paths on nightly toolchains for IDE features --- .../crates/hir-def/src/find_path.rs | 8 ++-- .../rust-analyzer/crates/hir-def/src/lib.rs | 2 +- .../crates/hir-ty/src/display.rs | 4 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 6 +-- .../rust-analyzer/crates/hir/src/semantics.rs | 7 ++++ .../crates/hir/src/term_search/expr.rs | 10 ++--- .../crates/ide-assists/src/assist_config.rs | 18 +++++++-- .../src/handlers/add_missing_match_arms.rs | 7 ++-- .../src/handlers/convert_bool_to_enum.rs | 37 ++++++++++--------- .../src/handlers/convert_into_to_from.rs | 2 +- .../convert_tuple_return_type_to_struct.rs | 3 +- .../handlers/destructure_struct_binding.rs | 3 +- .../src/handlers/extract_function.rs | 3 +- .../extract_struct_from_enum_variant.rs | 3 +- .../src/handlers/generate_deref.rs | 8 ++-- .../ide-assists/src/handlers/generate_new.rs | 3 +- .../generate_single_field_struct_from.rs | 7 +--- .../src/handlers/qualify_method_call.rs | 3 +- .../replace_derive_with_manual_impl.rs | 3 +- .../replace_qualified_name_with_use.rs | 9 ++--- .../ide-assists/src/handlers/term_search.rs | 2 +- .../src/handlers/toggle_async_sugar.rs | 7 +--- .../crates/ide-completion/src/completions.rs | 2 +- .../ide-completion/src/completions/expr.rs | 4 +- .../src/completions/flyimport.rs | 6 +-- .../ide-completion/src/completions/postfix.rs | 2 +- .../crates/ide-completion/src/config.rs | 19 ++++++++-- .../crates/ide-completion/src/render.rs | 2 +- .../crates/ide-completion/src/snippet.rs | 2 +- .../ide-db/src/imports/import_assets.rs | 21 ++++++++++- .../crates/ide-db/src/path_transform.rs | 10 ++--- .../src/handlers/json_is_not_rust.rs | 4 +- .../src/handlers/missing_fields.rs | 4 +- .../src/handlers/typed_hole.rs | 4 +- .../crates/ide-ssr/src/matching.rs | 4 +- .../rust-analyzer/src/cli/analysis_stats.rs | 6 +-- 36 files changed, 144 insertions(+), 101 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs index faa0ef8ceec7..e8a6ebcffa0a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs @@ -12,7 +12,7 @@ use intern::sym; use rustc_hash::FxHashSet; use crate::{ - ImportPathConfig, ModuleDefId, ModuleId, + FindPathConfig, ModuleDefId, ModuleId, db::DefDatabase, item_scope::ItemInNs, nameres::DefMap, @@ -27,7 +27,7 @@ pub fn find_path( from: ModuleId, mut prefix_kind: PrefixKind, ignore_local_imports: bool, - mut cfg: ImportPathConfig, + mut cfg: FindPathConfig, ) -> Option { let _p = tracing::info_span!("find_path").entered(); @@ -96,7 +96,7 @@ impl PrefixKind { struct FindPathCtx<'db> { db: &'db dyn DefDatabase, prefix: PrefixKind, - cfg: ImportPathConfig, + cfg: FindPathConfig, ignore_local_imports: bool, is_std_item: bool, from: ModuleId, @@ -718,7 +718,7 @@ mod tests { module, prefix, ignore_local_imports, - ImportPathConfig { prefer_no_std, prefer_prelude, prefer_absolute, allow_unstable }, + FindPathConfig { prefer_no_std, prefer_prelude, prefer_absolute, allow_unstable }, ); format_to!( res, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index 1f5b1e023702..301d4cca0666 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -101,7 +101,7 @@ use crate::{ type FxIndexMap = indexmap::IndexMap; /// A wrapper around three booleans #[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)] -pub struct ImportPathConfig { +pub struct FindPathConfig { /// If true, prefer to unconditionally use imports of the `core` and `alloc` crate /// over the std. pub prefer_no_std: bool, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index ae0113fcbd7f..f3691b2e4f7e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -11,7 +11,7 @@ use base_db::Crate; use chalk_ir::{BoundVar, Safety, TyKind}; use either::Either; use hir_def::{ - GeneralConstId, GenericDefId, HasModule, ImportPathConfig, LocalFieldId, Lookup, ModuleDefId, + FindPathConfig, GeneralConstId, GenericDefId, HasModule, LocalFieldId, Lookup, ModuleDefId, ModuleId, TraitId, db::DefDatabase, expr_store::{ExpressionStore, path::Path}, @@ -1433,7 +1433,7 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { PrefixKind::Plain, false, // FIXME: no_std Cfg? - ImportPathConfig { + FindPathConfig { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index f03f542e5bce..4d1d8e2b15a9 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -130,7 +130,7 @@ pub use { cfg::{CfgAtom, CfgExpr, CfgOptions}, hir_def::{ Complete, - ImportPathConfig, + FindPathConfig, attr::{AttrSourceMap, Attrs, AttrsWithOwner}, find_path::PrefixKind, import_map, @@ -957,7 +957,7 @@ impl Module { self, db: &dyn DefDatabase, item: impl Into, - cfg: ImportPathConfig, + cfg: FindPathConfig, ) -> Option { hir_def::find_path::find_path( db, @@ -976,7 +976,7 @@ impl Module { db: &dyn DefDatabase, item: impl Into, prefix_kind: PrefixKind, - cfg: ImportPathConfig, + cfg: FindPathConfig, ) -> Option { hir_def::find_path::find_path(db, item.into().into(), self.into(), prefix_kind, true, cfg) } diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 6d6e08100b04..84f01da72f06 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -302,6 +302,13 @@ impl Semantics<'_, DB> { self.imp.hir_file_to_module_defs(file.into()) } + pub fn is_nightly(&self, krate: Crate) -> bool { + let toolchain = self.db.toolchain_channel(krate.into()); + // `toolchain == None` means we're in some detached files. Since we have no information on + // the toolchain being used, let's just allow unstable items to be listed. + matches!(toolchain, Some(base_db::ReleaseChannel::Nightly) | None) + } + pub fn to_adt_def(&self, a: &ast::Adt) -> Option { self.imp.to_def(a) } diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs b/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs index 78f534d014b9..e56f9e91e3f3 100644 --- a/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs +++ b/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs @@ -1,6 +1,6 @@ //! Type tree for term search -use hir_def::ImportPathConfig; +use hir_def::FindPathConfig; use hir_expand::mod_path::ModPath; use hir_ty::{ db::HirDatabase, @@ -18,7 +18,7 @@ use crate::{ fn mod_item_path( sema_scope: &SemanticsScope<'_>, def: &ModuleDef, - cfg: ImportPathConfig, + cfg: FindPathConfig, ) -> Option { let db = sema_scope.db; let m = sema_scope.module(); @@ -29,7 +29,7 @@ fn mod_item_path( fn mod_item_path_str( sema_scope: &SemanticsScope<'_>, def: &ModuleDef, - cfg: ImportPathConfig, + cfg: FindPathConfig, edition: Edition, ) -> Result { let path = mod_item_path(sema_scope, def, cfg); @@ -103,7 +103,7 @@ impl<'db> Expr<'db> { &self, sema_scope: &SemanticsScope<'db>, many_formatter: &mut dyn FnMut(&Type<'db>) -> String, - cfg: ImportPathConfig, + cfg: FindPathConfig, display_target: DisplayTarget, ) -> Result { let db = sema_scope.db; @@ -380,7 +380,7 @@ impl<'db> Expr<'db> { fn container_name( container: AssocItemContainer, sema_scope: &SemanticsScope<'_>, - cfg: ImportPathConfig, + cfg: FindPathConfig, edition: Edition, display_target: DisplayTarget, ) -> Result { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs b/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs index 57ced8d8534b..597d035ebd85 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs @@ -4,8 +4,12 @@ //! module, and we use to statically check that we only produce snippet //! assists if we are allowed to. -use hir::ImportPathConfig; -use ide_db::{SnippetCap, assists::ExprFillDefaultMode, imports::insert_use::InsertUseConfig}; +use hir::FindPathConfig; +use ide_db::{ + SnippetCap, + assists::ExprFillDefaultMode, + imports::{import_assets::ImportPathConfig, insert_use::InsertUseConfig}, +}; use crate::AssistKind; @@ -31,7 +35,15 @@ impl AssistConfig { prefer_no_std: self.prefer_no_std, prefer_prelude: self.prefer_prelude, prefer_absolute: self.prefer_absolute, - allow_unstable: true, + } + } + + pub fn find_path_confg(&self, allow_unstable: bool) -> FindPathConfig { + FindPathConfig { + prefer_no_std: self.prefer_no_std, + prefer_prelude: self.prefer_prelude, + prefer_absolute: self.prefer_absolute, + allow_unstable, } } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs index 1ece7ddab101..4d3212c515f2 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs @@ -1,7 +1,7 @@ use std::iter::{self, Peekable}; use either::Either; -use hir::{Adt, AsAssocItem, Crate, HasAttrs, ImportPathConfig, ModuleDef, Semantics, sym}; +use hir::{Adt, AsAssocItem, Crate, FindPathConfig, HasAttrs, ModuleDef, Semantics, sym}; use ide_db::RootDatabase; use ide_db::assists::ExprFillDefaultMode; use ide_db::syntax_helpers::suggest_name; @@ -76,12 +76,11 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) .filter(|pat| !matches!(pat, Pat::WildcardPat(_))) .collect(); - let cfg = ctx.config.import_path_config(); - let make = SyntaxFactory::with_mappings(); let scope = ctx.sema.scope(expr.syntax())?; let module = scope.module(); + let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(scope.krate())); let self_ty = if ctx.config.prefer_self_ty { scope .containing_function() @@ -498,7 +497,7 @@ fn build_pat( make: &SyntaxFactory, module: hir::Module, var: ExtendedVariant, - cfg: ImportPathConfig, + cfg: FindPathConfig, ) -> Option { let db = ctx.db(); match var { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs index c208c8bf789a..80445578fcef 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_to_enum.rs @@ -329,8 +329,6 @@ fn augment_references_with_imports( ) -> Vec { let mut visited_modules = FxHashSet::default(); - let cfg = ctx.config.import_path_config(); - let edition = target_module.krate().edition(ctx.db()); references .into_iter() @@ -345,22 +343,27 @@ fn augment_references_with_imports( { visited_modules.insert(ref_module); - let import_scope = ImportScope::find_insert_use_container(name.syntax(), &ctx.sema); - let path = ref_module - .find_use_path( - ctx.sema.db, - ModuleDef::Module(*target_module), - ctx.config.insert_use.prefix_kind, - cfg, - ) - .map(|mod_path| { - make::path_concat( - mod_path_to_ast(&mod_path, edition), - make::path_from_text("Bool"), - ) - }); + ImportScope::find_insert_use_container(name.syntax(), &ctx.sema).and_then( + |import_scope| { + let cfg = + ctx.config.find_path_confg(ctx.sema.is_nightly(target_module.krate())); + let path = ref_module + .find_use_path( + ctx.sema.db, + ModuleDef::Module(*target_module), + ctx.config.insert_use.prefix_kind, + cfg, + ) + .map(|mod_path| { + make::path_concat( + mod_path_to_ast(&mod_path, edition), + make::path_from_text("Bool"), + ) + })?; - import_scope.zip(path) + Some((import_scope, path)) + }, + ) } else { None }; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs index 3d9cde0e0a67..3a464a3dc6aa 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs @@ -43,7 +43,7 @@ pub(crate) fn convert_into_to_from(acc: &mut Assists, ctx: &AssistContext<'_>) - return None; } - let cfg = ctx.config.import_path_config(); + let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(module.krate())); let src_type_path = { let src_type_path = src_type.syntax().descendants().find_map(ast::Path::cast)?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs index 247c1011589b..80ffb4db3e84 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs @@ -184,8 +184,6 @@ fn augment_references_with_imports( ) -> Vec<(ast::NameLike, Option<(ImportScope, ast::Path)>)> { let mut visited_modules = FxHashSet::default(); - let cfg = ctx.config.import_path_config(); - references .iter() .filter_map(|FileReference { name, .. }| { @@ -201,6 +199,7 @@ fn augment_references_with_imports( { visited_modules.insert(ref_module); + let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(ref_module.krate())); let import_scope = ImportScope::find_insert_use_container(new_name.syntax(), &ctx.sema); let path = ref_module diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs index b8c647ac8b71..397327cb4ff8 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs @@ -86,9 +86,8 @@ fn collect_data(ident_pat: ast::IdentPat, ctx: &AssistContext<'_>) -> Option) -> Op FamousDefs(&ctx.sema, module.krate()).core_ops_ControlFlow(); if let Some(control_flow_enum) = control_flow_enum { + let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(module.krate())); let mod_path = module.find_use_path( ctx.sema.db, ModuleDef::from(control_flow_enum), ctx.config.insert_use.prefix_kind, - ctx.config.import_path_config(), + cfg, ); if let Some(mod_path) = mod_path { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index c56d0b3de5d6..a5467e760bf3 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -400,11 +400,12 @@ fn process_references( let segment = builder.make_mut(segment); let scope_node = builder.make_syntax_mut(scope_node); if !visited_modules.contains(&module) { + let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(module.krate())); let mod_path = module.find_use_path( ctx.sema.db, *enum_module_def, ctx.config.insert_use.prefix_kind, - ctx.config.import_path_config(), + cfg, ); if let Some(mut mod_path) = mod_path { mod_path.pop_segment(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs index 55a09c5d775d..a1fc2c602359 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs @@ -57,9 +57,9 @@ fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( }; let module = ctx.sema.to_def(&strukt)?.module(ctx.db()); + let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(module.krate())); let trait_ = deref_type_to_generate.to_trait(&ctx.sema, module.krate())?; - let trait_path = - module.find_path(ctx.db(), ModuleDef::Trait(trait_), ctx.config.import_path_config())?; + let trait_path = module.find_path(ctx.db(), ModuleDef::Trait(trait_), cfg)?; let field_type = field.ty()?; let field_name = field.name()?; @@ -99,9 +99,9 @@ fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<() }; let module = ctx.sema.to_def(&strukt)?.module(ctx.db()); + let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(module.krate())); let trait_ = deref_type_to_generate.to_trait(&ctx.sema, module.krate())?; - let trait_path = - module.find_path(ctx.db(), ModuleDef::Trait(trait_), ctx.config.import_path_config())?; + let trait_path = module.find_path(ctx.db(), ModuleDef::Trait(trait_), cfg)?; let field_type = field.ty()?; let target = field.syntax().text_range(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs index ac4a54a89ef1..9760fd62aab4 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs @@ -77,10 +77,11 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option let item_in_ns = hir::ItemInNs::from(hir::ModuleDef::from(ty.as_adt()?)); + let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(current_module.krate())); let type_path = current_module.find_path( ctx.sema.db, item_for_path_search(ctx.sema.db, item_in_ns)?, - ctx.config.import_path_config(), + cfg, )?; let edition = current_module.krate().edition(ctx.db()); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs index 943795d40d55..cad14d929648 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs @@ -169,6 +169,7 @@ fn make_constructors( types: &[ast::Type], ) -> Vec> { let (db, sema) = (ctx.db(), &ctx.sema); + let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(module.krate())); types .iter() .map(|ty| { @@ -179,11 +180,7 @@ fn make_constructors( let item_in_ns = ModuleDef::Adt(ty.as_adt()?).into(); let edition = module.krate().edition(db); - let ty_path = module.find_path( - db, - item_for_path_search(db, item_in_ns)?, - ctx.config.import_path_config(), - )?; + let ty_path = module.find_path(db, item_for_path_search(db, item_in_ns)?, cfg)?; use_trivial_constructor(db, mod_path_to_ast(&ty_path, edition), &ty, edition) }) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs index 985121780b1a..e4494f0492ec 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs @@ -45,10 +45,11 @@ pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> let current_edition = current_module.krate().edition(ctx.db()); let target_module_def = ModuleDef::from(resolved_call); let item_in_ns = ItemInNs::from(target_module_def); + let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(current_module.krate())); let receiver_path = current_module.find_path( ctx.sema.db, item_for_path_search(ctx.sema.db, item_in_ns)?, - ctx.config.import_path_config(), + cfg, )?; let qualify_candidate = QualifyCandidate::ImplMethod(ctx.sema.db, call, resolved_call); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs index 175f26131705..25c5593007bc 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs @@ -71,6 +71,7 @@ pub(crate) fn replace_derive_with_manual_impl( let current_module = ctx.sema.scope(adt.syntax())?.module(); let current_crate = current_module.krate(); let current_edition = current_crate.edition(ctx.db()); + let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(current_crate)); let found_traits = items_locator::items_with_name( ctx.db(), @@ -84,7 +85,7 @@ pub(crate) fn replace_derive_with_manual_impl( }) .flat_map(|trait_| { current_module - .find_path(ctx.sema.db, hir::ModuleDef::Trait(trait_), ctx.config.import_path_config()) + .find_path(ctx.sema.db, hir::ModuleDef::Trait(trait_), cfg) .as_ref() .map(|path| mod_path_to_ast(path, current_edition)) .zip(Some(trait_)) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs index 9f742131e5cb..5fc4d7a6170d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs @@ -63,12 +63,9 @@ pub(crate) fn replace_qualified_name_with_use( ); let path_to_qualifier = starts_with_name_ref .then(|| { - ctx.sema.scope(original_path.syntax())?.module().find_use_path( - ctx.sema.db, - module, - ctx.config.insert_use.prefix_kind, - ctx.config.import_path_config(), - ) + let mod_ = ctx.sema.scope(original_path.syntax())?.module(); + let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(mod_.krate())); + mod_.find_use_path(ctx.sema.db, module, ctx.config.insert_use.prefix_kind, cfg) }) .flatten(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs index 6527d3706e21..209d3f08eb88 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs @@ -55,7 +55,7 @@ pub(crate) fn term_search(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< path.gen_source_code( &scope, &mut formatter, - ctx.config.import_path_config(), + ctx.config.find_path_confg(ctx.sema.is_nightly(scope.module().krate())), scope.krate().to_display_target(ctx.db()), ) .ok() diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_async_sugar.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_async_sugar.rs index eed070cb07dd..aed66d3d8344 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_async_sugar.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_async_sugar.rs @@ -132,12 +132,9 @@ pub(crate) fn desugar_async_into_impl_future( let scope = ctx.sema.scope(function.syntax())?; let module = scope.module(); + let cfg = ctx.config.find_path_confg(ctx.sema.is_nightly(module.krate())); let future_trait = FamousDefs(&ctx.sema, scope.krate()).core_future_Future()?; - let trait_path = module.find_path( - ctx.db(), - ModuleDef::Trait(future_trait), - ctx.config.import_path_config(), - )?; + let trait_path = module.find_path(ctx.db(), ModuleDef::Trait(future_trait), cfg)?; let edition = scope.krate().edition(ctx.db()); let trait_path = trait_path.display(ctx.db(), edition); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs index 11d26228ba20..e36e0e570458 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs @@ -654,7 +654,7 @@ fn enum_variants_with_paths( if let Some(path) = ctx.module.find_path( ctx.db, hir::ModuleDef::from(variant), - ctx.config.import_path_config(ctx.is_nightly), + ctx.config.find_path_config(ctx.is_nightly), ) { // Variants with trivial paths are already added by the existing completion logic, // so we should avoid adding these twice diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs index 1972f166134a..a5b8a0b1ceb6 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs @@ -254,7 +254,7 @@ pub(crate) fn complete_expr_path( .find_path( ctx.db, hir::ModuleDef::from(strukt), - ctx.config.import_path_config(ctx.is_nightly), + ctx.config.find_path_config(ctx.is_nightly), ) .filter(|it| it.len() > 1); @@ -276,7 +276,7 @@ pub(crate) fn complete_expr_path( .find_path( ctx.db, hir::ModuleDef::from(un), - ctx.config.import_path_config(ctx.is_nightly), + ctx.config.find_path_config(ctx.is_nightly), ) .filter(|it| it.len() > 1); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs index dad8a76de87d..d1e05a4359f1 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs @@ -257,7 +257,7 @@ fn import_on_the_fly( }; let user_input_lowercased = potential_import_name.to_lowercase(); - let import_cfg = ctx.config.import_path_config(ctx.is_nightly); + let import_cfg = ctx.config.import_path_config(); import_assets .search_for_imports(&ctx.sema, import_cfg, ctx.config.insert_use.prefix_kind) @@ -304,7 +304,7 @@ fn import_on_the_fly_pat_( ItemInNs::Values(def) => matches!(def, hir::ModuleDef::Const(_)), }; let user_input_lowercased = potential_import_name.to_lowercase(); - let cfg = ctx.config.import_path_config(ctx.is_nightly); + let cfg = ctx.config.import_path_config(); import_assets .search_for_imports(&ctx.sema, cfg, ctx.config.insert_use.prefix_kind) @@ -346,7 +346,7 @@ fn import_on_the_fly_method( let user_input_lowercased = potential_import_name.to_lowercase(); - let cfg = ctx.config.import_path_config(ctx.is_nightly); + let cfg = ctx.config.import_path_config(); import_assets .search_for_imports(&ctx.sema, cfg, ctx.config.insert_use.prefix_kind) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs index 0058611a6153..d355fdbe0739 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs @@ -63,7 +63,7 @@ pub(crate) fn complete_postfix( None => return, }; - let cfg = ctx.config.import_path_config(ctx.is_nightly); + let cfg = ctx.config.find_path_config(ctx.is_nightly); if let Some(drop_trait) = ctx.famous_defs().core_ops_Drop() && receiver_ty.impls_trait(ctx.db, drop_trait, &[]) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs index 844fce5ef801..b7367cb62f09 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs @@ -4,8 +4,11 @@ //! module, and we use to statically check that we only produce snippet //! completions if we are allowed to. -use hir::ImportPathConfig; -use ide_db::{SnippetCap, imports::insert_use::InsertUseConfig}; +use hir::FindPathConfig; +use ide_db::{ + SnippetCap, + imports::{import_assets::ImportPathConfig, insert_use::InsertUseConfig}, +}; use crate::{CompletionFieldsToResolve, snippet::Snippet}; @@ -59,12 +62,20 @@ impl CompletionConfig<'_> { .flat_map(|snip| snip.prefix_triggers.iter().map(move |trigger| (&**trigger, snip))) } - pub fn import_path_config(&self, allow_unstable: bool) -> ImportPathConfig { - ImportPathConfig { + pub fn find_path_config(&self, allow_unstable: bool) -> FindPathConfig { + FindPathConfig { prefer_no_std: self.prefer_no_std, prefer_prelude: self.prefer_prelude, prefer_absolute: self.prefer_absolute, allow_unstable, } } + + pub fn import_path_config(&self) -> ImportPathConfig { + ImportPathConfig { + prefer_no_std: self.prefer_no_std, + prefer_prelude: self.prefer_prelude, + prefer_absolute: self.prefer_absolute, + } + } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index 7d23f9d14c66..dbf68dbe33af 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -299,7 +299,7 @@ pub(crate) fn render_expr( .unwrap_or_else(|| String::from("...")) }; - let cfg = ctx.config.import_path_config(ctx.is_nightly); + let cfg = ctx.config.find_path_config(ctx.is_nightly); let label = expr.gen_source_code(&ctx.scope, &mut label_formatter, cfg, ctx.display_target).ok()?; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs index 9dc0c0234dc5..d326098f9407 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs @@ -164,7 +164,7 @@ impl Snippet { } fn import_edits(ctx: &CompletionContext<'_>, requires: &[ModPath]) -> Option> { - let import_cfg = ctx.config.import_path_config(ctx.is_nightly); + let import_cfg = ctx.config.find_path_config(ctx.is_nightly); let resolve = |import| { let item = ctx.scope.resolve_mod_path(import).next()?; diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs index 1012f993344f..0c235c8d9a57 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs @@ -3,7 +3,7 @@ use std::ops::ControlFlow; use hir::{ - AsAssocItem, AssocItem, AssocItemContainer, Complete, Crate, HasCrate, ImportPathConfig, + AsAssocItem, AssocItem, AssocItemContainer, Complete, Crate, FindPathConfig, HasCrate, ItemInNs, ModPath, Module, ModuleDef, Name, PathResolution, PrefixKind, ScopeDef, Semantics, SemanticsScope, Trait, TyFingerprint, Type, db::HirDatabase, }; @@ -19,6 +19,17 @@ use crate::{ items_locator::{self, AssocSearchMode, DEFAULT_QUERY_SEARCH_LIMIT}, }; +#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)] +pub struct ImportPathConfig { + /// If true, prefer to unconditionally use imports of the `core` and `alloc` crate + /// over the std. + pub prefer_no_std: bool, + /// If true, prefer import paths containing a prelude module. + pub prefer_prelude: bool, + /// If true, prefer abs path (starting with `::`) where it is available. + pub prefer_absolute: bool, +} + /// A candidate for import, derived during various IDE activities: /// * completion with imports on the fly proposals /// * completion edit resolve requests @@ -296,6 +307,12 @@ impl<'db> ImportAssets<'db> { Some(it) => it, None => return >::default().into_iter(), }; + let cfg = FindPathConfig { + prefer_no_std: cfg.prefer_no_std, + prefer_prelude: cfg.prefer_prelude, + prefer_absolute: cfg.prefer_absolute, + allow_unstable: sema.is_nightly(scope.krate()), + }; let db = sema.db; let krate = self.module_with_candidate.krate(); let scope_definitions = self.scope_definitions(sema); @@ -698,7 +715,7 @@ fn get_mod_path( item_to_search: ItemInNs, module_with_candidate: &Module, prefixed: Option, - cfg: ImportPathConfig, + cfg: FindPathConfig, ) -> Option { if let Some(prefix_kind) = prefixed { module_with_candidate.find_use_path(db, item_to_search, prefix_kind, cfg) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs index a76a0551dd8d..4a27035afd09 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs @@ -3,7 +3,7 @@ use crate::helpers::mod_path_to_ast; use either::Either; use hir::{ - AsAssocItem, HirDisplay, HirFileId, ImportPathConfig, ModuleDef, SemanticsScope, + AsAssocItem, FindPathConfig, HirDisplay, HirFileId, ModuleDef, SemanticsScope, prettify_macro_expansion, }; use itertools::Itertools; @@ -392,7 +392,7 @@ impl Ctx<'_> { parent.segment()?.name_ref()?, ) .and_then(|trait_ref| { - let cfg = ImportPathConfig { + let cfg = FindPathConfig { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, @@ -452,7 +452,7 @@ impl Ctx<'_> { return None; } - let cfg = ImportPathConfig { + let cfg = FindPathConfig { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, @@ -501,7 +501,7 @@ impl Ctx<'_> { if let Some(adt) = ty.as_adt() && let ast::Type::PathType(path_ty) = &ast_ty { - let cfg = ImportPathConfig { + let cfg = FindPathConfig { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, @@ -546,7 +546,7 @@ impl Ctx<'_> { match resolution { hir::PathResolution::Def(def) if def.as_assoc_item(self.source_scope.db).is_none() => { - let cfg = ImportPathConfig { + let cfg = FindPathConfig { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs index 742d614bc567..2dfc2bb56595 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs @@ -1,7 +1,7 @@ //! This diagnostic provides an assist for creating a struct definition from a JSON //! example. -use hir::{ImportPathConfig, PathResolution, Semantics}; +use hir::{FindPathConfig, PathResolution, Semantics}; use ide_db::text_edit::TextEdit; use ide_db::{ EditionedFileId, FileRange, FxHashMap, RootDatabase, @@ -141,7 +141,7 @@ pub(crate) fn json_in_items( let scope = scb.make_import_scope_mut(import_scope); let current_module = semantics_scope.module(); - let cfg = ImportPathConfig { + let cfg = FindPathConfig { prefer_no_std: config.prefer_no_std, prefer_prelude: config.prefer_prelude, prefer_absolute: config.prefer_absolute, diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs index 893bfca6a129..49f925e2e0c9 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs @@ -1,6 +1,6 @@ use either::Either; use hir::{ - AssocItem, HirDisplay, ImportPathConfig, InFile, Type, + AssocItem, FindPathConfig, HirDisplay, InFile, Type, db::{ExpandDatabase, HirDatabase}, sym, }; @@ -132,7 +132,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option, d: &hir::TypedHole<'_>) -> Option Date: Thu, 7 Aug 2025 15:24:39 +0200 Subject: [PATCH 545/682] Workaround lsp-types typo --- .../crates/rust-analyzer/src/bin/main.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs index ab045e0bf9ff..9bcf835b4cca 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs @@ -208,13 +208,24 @@ fn run_server() -> anyhow::Result<()> { tracing::info!("InitializeParams: {}", initialize_params); let lsp_types::InitializeParams { root_uri, - capabilities, + mut capabilities, workspace_folders, initialization_options, client_info, .. } = from_json::("InitializeParams", &initialize_params)?; + // lsp-types has a typo in the `/capabilities/workspace/diagnostics` field, its typoed as `diagnostic` + if let Some(val) = initialize_params.pointer("/capabilities/workspace/diagnostics") { + if let Ok(diag_caps) = from_json::( + "DiagnosticWorkspaceClientCapabilities", + val, + ) { + tracing::info!("Patching lsp-types workspace diagnostics capabilities: {diag_caps:#?}"); + capabilities.workspace.get_or_insert_default().diagnostic.get_or_insert(diag_caps); + } + } + let root_path = match root_uri .and_then(|it| it.to_file_path().ok()) .map(patch_path_prefix) From fd28cc8a1b2496ac9717d7185a40af123aaefafb Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 7 Aug 2025 15:24:39 +0200 Subject: [PATCH 546/682] Add more workaround hacks for incorrect startup diagnostics --- .../crates/rust-analyzer/src/bin/main.rs | 12 ++++++------ .../crates/rust-analyzer/src/global_state.rs | 5 +++++ .../crates/rust-analyzer/src/handlers/dispatch.rs | 2 +- .../rust-analyzer/crates/rust-analyzer/src/reload.rs | 5 ++++- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs index 9bcf835b4cca..e80f2953505f 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs @@ -216,14 +216,14 @@ fn run_server() -> anyhow::Result<()> { } = from_json::("InitializeParams", &initialize_params)?; // lsp-types has a typo in the `/capabilities/workspace/diagnostics` field, its typoed as `diagnostic` - if let Some(val) = initialize_params.pointer("/capabilities/workspace/diagnostics") { - if let Ok(diag_caps) = from_json::( + if let Some(val) = initialize_params.pointer("/capabilities/workspace/diagnostics") + && let Ok(diag_caps) = from_json::( "DiagnosticWorkspaceClientCapabilities", val, - ) { - tracing::info!("Patching lsp-types workspace diagnostics capabilities: {diag_caps:#?}"); - capabilities.workspace.get_or_insert_default().diagnostic.get_or_insert(diag_caps); - } + ) + { + tracing::info!("Patching lsp-types workspace diagnostics capabilities: {diag_caps:#?}"); + capabilities.workspace.get_or_insert_default().diagnostic.get_or_insert(diag_caps); } let root_path = match root_uri diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index 2f1afba3634e..89d6fb8edc2e 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -183,6 +183,10 @@ pub(crate) struct GlobalState { /// this queue should run only *after* [`GlobalState::process_changes`] has /// been called. pub(crate) deferred_task_queue: TaskQueue, + /// HACK: Workaround for https://github.com/rust-lang/rust-analyzer/issues/19709 + /// This is marked true if we failed to load a crate root file at crate graph creation, + /// which will usually end up causing a bunch of incorrect diagnostics on startup. + pub(crate) incomplete_crate_graph: bool, } /// An immutable snapshot of the world's state at a point in time. @@ -298,6 +302,7 @@ impl GlobalState { discover_workspace_queue: OpQueue::default(), deferred_task_queue: task_queue, + incomplete_crate_graph: false, }; // Apply any required database inputs from the config. this.update_configuration(config); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs index b25245dd884a..10bbb0bb31d9 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs @@ -141,7 +141,7 @@ impl RequestDispatcher<'_> { Result: Serialize, > + 'static, { - if !self.global_state.vfs_done { + if !self.global_state.vfs_done || self.global_state.incomplete_crate_graph { if let Some(lsp_server::Request { id, .. }) = self.req.take_if(|it| it.method == R::METHOD) { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index a8a54930c6e5..c15496b3f6cd 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -741,13 +741,16 @@ impl GlobalState { }) .collect(); + self.incomplete_crate_graph = false; let (crate_graph, proc_macro_paths) = { // Create crate graph from all the workspaces let vfs = &self.vfs.read().0; let load = |path: &AbsPath| { let vfs_path = vfs::VfsPath::from(path.to_path_buf()); self.crate_graph_file_dependencies.insert(vfs_path.clone()); - vfs.file_id(&vfs_path).and_then(|(file_id, excluded)| { + let file_id = vfs.file_id(&vfs_path); + self.incomplete_crate_graph |= file_id.is_none(); + file_id.and_then(|(file_id, excluded)| { (excluded == vfs::FileExcluded::No).then_some(file_id) }) }; From d66fb4910fb7e87b7e17477ae147854adf76b0dd Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 5 Sep 2025 21:35:58 -0500 Subject: [PATCH 547/682] Suggest removing Box::new --- .../src/fn_ctxt/suggestions.rs | 22 +++++++++++++++++++ tests/ui/coercion/coerce-block-tail.stderr | 7 +++--- .../ui/coercion/coerce-box-new-to-unboxed.rs | 4 ++++ .../coercion/coerce-box-new-to-unboxed.stderr | 19 ++++++++++++++++ 4 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 tests/ui/coercion/coerce-box-new-to-unboxed.rs create mode 100644 tests/ui/coercion/coerce-box-new-to-unboxed.stderr diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index aca3840712e7..a12bc0579343 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -2955,6 +2955,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { let deref_kind = if checked_ty.is_box() { + // detect Box::new(..) + if let ExprKind::Call(box_new, [_]) = expr.kind + && let ExprKind::Path(qpath) = &box_new.kind + && let Res::Def(DefKind::AssocFn, fn_id) = + self.typeck_results.borrow().qpath_res(qpath, box_new.hir_id) + && let Some(impl_id) = self.tcx.inherent_impl_of_assoc(fn_id) + && self.tcx.type_of(impl_id).skip_binder().is_box() + && self.tcx.item_name(fn_id) == sym::new + { + let l_paren = self.tcx.sess.source_map().next_point(box_new.span); + let r_paren = self.tcx.sess.source_map().end_point(expr.span); + return Some(( + vec![ + (box_new.span.to(l_paren), String::new()), + (r_paren, String::new()), + ], + "consider removing the Box".to_string(), + Applicability::MachineApplicable, + false, + false, + )); + } "unboxing the value" } else if checked_ty.is_ref() { "dereferencing the borrow" diff --git a/tests/ui/coercion/coerce-block-tail.stderr b/tests/ui/coercion/coerce-block-tail.stderr index 1301f3b7813e..b358401b7062 100644 --- a/tests/ui/coercion/coerce-block-tail.stderr +++ b/tests/ui/coercion/coerce-block-tail.stderr @@ -6,10 +6,11 @@ LL | let _: &i32 = & { Box::new(1i32) }; | = note: expected type `i32` found struct `Box` -help: consider unboxing the value +help: consider removing the Box + | +LL - let _: &i32 = & { Box::new(1i32) }; +LL + let _: &i32 = & { 1i32 }; | -LL | let _: &i32 = & { *Box::new(1i32) }; - | + error: aborting due to 1 previous error diff --git a/tests/ui/coercion/coerce-box-new-to-unboxed.rs b/tests/ui/coercion/coerce-box-new-to-unboxed.rs new file mode 100644 index 000000000000..63ff0fc4e238 --- /dev/null +++ b/tests/ui/coercion/coerce-box-new-to-unboxed.rs @@ -0,0 +1,4 @@ +fn main() { + let _: String = Box::new(String::new()); + //~^ ERROR mismatched types +} diff --git a/tests/ui/coercion/coerce-box-new-to-unboxed.stderr b/tests/ui/coercion/coerce-box-new-to-unboxed.stderr new file mode 100644 index 000000000000..bdd962686e4b --- /dev/null +++ b/tests/ui/coercion/coerce-box-new-to-unboxed.stderr @@ -0,0 +1,19 @@ +error[E0308]: mismatched types + --> $DIR/coerce-box-new-to-unboxed.rs:2:21 + | +LL | let _: String = Box::new(String::new()); + | ------ ^^^^^^^^^^^^^^^^^^^^^^^ expected `String`, found `Box` + | | + | expected due to this + | + = note: expected struct `String` + found struct `Box` +help: consider removing the Box + | +LL - let _: String = Box::new(String::new()); +LL + let _: String = String::new(); + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. From 8b752cb37d646118c46f7739ae965b8e9c9a9b03 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 14 Sep 2025 21:14:37 +0200 Subject: [PATCH 548/682] fix issue with `cmse-nonsecure-entry` ABI being both async and c-variadic --- .../src/hir_ty_lowering/cmse.rs | 6 ++ tests/crashes/132142.rs | 3 - .../cmse-nonsecure-entry/c-variadic.rs | 69 +++++++++++++++++++ .../cmse-nonsecure-entry/c-variadic.stderr | 28 ++++++++ .../cmse-nonsecure-entry/generics.rs | 7 +- .../cmse-nonsecure-entry/generics.stderr | 18 +---- 6 files changed, 105 insertions(+), 26 deletions(-) delete mode 100644 tests/crashes/132142.rs create mode 100644 tests/ui/cmse-nonsecure/cmse-nonsecure-entry/c-variadic.rs create mode 100644 tests/ui/cmse-nonsecure/cmse-nonsecure-entry/c-variadic.stderr diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs index 5088c63702e6..7867c1c3b485 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs @@ -85,6 +85,12 @@ pub(crate) fn validate_cmse_abi<'tcx>( return; }; + // An `extern "cmse-nonsecure-entry"` function cannot be c-variadic. We run + // into https://github.com/rust-lang/rust/issues/132142 if we don't explicitly bail. + if decl.c_variadic { + return; + } + match is_valid_cmse_inputs(tcx, fn_sig) { Ok(Ok(())) => {} Ok(Err(index)) => { diff --git a/tests/crashes/132142.rs b/tests/crashes/132142.rs deleted file mode 100644 index 813bf0bf0a8e..000000000000 --- a/tests/crashes/132142.rs +++ /dev/null @@ -1,3 +0,0 @@ -//@ known-bug: #132142 - -async extern "cmse-nonsecure-entry" fn fun(...) {} diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/c-variadic.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/c-variadic.rs new file mode 100644 index 000000000000..9317ab5cd275 --- /dev/null +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/c-variadic.rs @@ -0,0 +1,69 @@ +//@ add-core-stubs +//@ edition: 2018 +//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib +//@ needs-llvm-components: arm +#![feature(cmse_nonsecure_entry, c_variadic, no_core, lang_items)] +#![no_core] + +extern crate minicore; +use minicore::*; + +#[lang = "va_list"] +struct VaList(*mut u8); + +unsafe extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { + //~^ ERROR `...` is not supported for `extern "cmse-nonsecure-entry"` functions +} + +// A regression test for https://github.com/rust-lang/rust/issues/132142 +async unsafe extern "cmse-nonsecure-entry" fn async_and_c_variadic(_: ...) { + //~^ ERROR `...` is not supported for `extern "cmse-nonsecure-entry"` functions + //~| ERROR functions cannot be both `async` and C-variadic +} + +// Below are the lang items that are required for a program that defines an `async` function. +// Without them, the ICE that is tested for here is not reached for this target. For now they are in +// this file, but they may be moved into `minicore` if/when other `#[no_core]` tests want to use +// them. + +// NOTE: in `core` this type uses `NonNull`. +#[lang = "ResumeTy"] +pub struct ResumeTy(*mut Context<'static>); + +#[lang = "future_trait"] +pub trait Future { + /// The type of value produced on completion. + #[lang = "future_output"] + type Output; + + // NOTE: misses the `poll` method. +} + +#[lang = "async_drop"] +pub trait AsyncDrop { + // NOTE: misses the `drop` method. +} + +#[lang = "Poll"] +pub enum Poll { + #[lang = "Ready"] + Ready(T), + + #[lang = "Pending"] + Pending, +} + +#[lang = "Context"] +pub struct Context<'a> { + // NOTE: misses a bunch of fields. + _marker: PhantomData &'a ()>, +} + +#[lang = "get_context"] +pub unsafe fn get_context<'a, 'b>(cx: ResumeTy) -> &'a mut Context<'b> { + // NOTE: the actual implementation looks different. + mem::transmute(cx.0) +} + +#[lang = "pin"] +pub struct Pin(T); diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/c-variadic.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/c-variadic.stderr new file mode 100644 index 000000000000..948f8f5747b0 --- /dev/null +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/c-variadic.stderr @@ -0,0 +1,28 @@ +error: `...` is not supported for `extern "cmse-nonsecure-entry"` functions + --> $DIR/c-variadic.rs:14:60 + | +LL | unsafe extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { + | ----------------------------- ^^^^^^ + | | + | `extern "cmse-nonsecure-entry"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: functions cannot be both `async` and C-variadic + --> $DIR/c-variadic.rs:19:1 + | +LL | async unsafe extern "cmse-nonsecure-entry" fn async_and_c_variadic(_: ...) { + | ^^^^^ `async` because of this ^^^^^^ C-variadic because of this + +error: `...` is not supported for `extern "cmse-nonsecure-entry"` functions + --> $DIR/c-variadic.rs:19:68 + | +LL | async unsafe extern "cmse-nonsecure-entry" fn async_and_c_variadic(_: ...) { + | ----------------------------- ^^^^^^ + | | + | `extern "cmse-nonsecure-entry"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: aborting due to 3 previous errors + diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs index eb6f4a323655..aee01e25ddc4 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs @@ -1,7 +1,7 @@ //@ add-core-stubs //@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib //@ needs-llvm-components: arm -#![feature(cmse_nonsecure_entry, c_variadic, no_core, lang_items)] +#![feature(cmse_nonsecure_entry, no_core, lang_items)] #![no_core] extern crate minicore; @@ -65,8 +65,3 @@ extern "cmse-nonsecure-entry" fn wrapped_trait_object(x: WrapperTransparent) -> //~^ ERROR return value of `"cmse-nonsecure-entry"` function too large to pass via registers [E0798] x } - -unsafe extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { - //~^ ERROR `...` is not supported for `extern "cmse-nonsecure-entry"` functions - //~| ERROR requires `va_list` lang_item -} diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr index 8937def9428c..df1a91070437 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr @@ -1,13 +1,3 @@ -error: `...` is not supported for `extern "cmse-nonsecure-entry"` functions - --> $DIR/generics.rs:69:60 - | -LL | unsafe extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { - | ----------------------------- ^^^^^^ - | | - | `extern "cmse-nonsecure-entry"` because of this - | - = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list - error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type --> $DIR/generics.rs:30:1 | @@ -71,12 +61,6 @@ LL | extern "cmse-nonsecure-entry" fn wrapped_trait_object(x: WrapperTransparent = note: functions with the `"cmse-nonsecure-entry"` ABI must pass their result via the available return registers = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size -error: requires `va_list` lang_item - --> $DIR/generics.rs:69:60 - | -LL | unsafe extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { - | ^^^^^^ - -error: aborting due to 9 previous errors +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0798`. From d23909d4d8aa2431df5ebc2fdeb2d967cd3c286d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 16 Sep 2025 08:48:30 +0200 Subject: [PATCH 549/682] rustup --- src/tools/miri/rust-version | 2 +- src/tools/miri/tests/pass/shims/fs.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 8315eb30f943..f27677cd5038 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -a015919e54c60b1b2bec7a98dec478cfc4a48f4e +9d82de19dfae60e55c291f5f28e28cfc2c1b9630 diff --git a/src/tools/miri/tests/pass/shims/fs.rs b/src/tools/miri/tests/pass/shims/fs.rs index 022dcc5dcbaf..0748007b3c05 100644 --- a/src/tools/miri/tests/pass/shims/fs.rs +++ b/src/tools/miri/tests/pass/shims/fs.rs @@ -28,7 +28,8 @@ fn main() { test_from_raw_os_error(); test_file_clone(); // Windows file handling is very incomplete. - if cfg!(not(windows)) { + // FIXME: read_dir broken on FreeBSD (https://github.com/rust-lang/miri/issues/4587) + if cfg!(not(windows)) && !cfg!(target_os = "freebsd") { test_file_set_len(); test_file_sync(); test_rename(); From 929c9335ddcaf8e59f5b56c986fd58a3310fdac0 Mon Sep 17 00:00:00 2001 From: Haidong Zhang Date: Tue, 9 Sep 2025 18:10:24 +0800 Subject: [PATCH 550/682] Add parallel-frontend-threads to bootstrap.toml and enable multi-threaded parallel compilation --- bootstrap.example.toml | 8 ++++++++ src/bootstrap/configure.py | 5 +++++ src/bootstrap/src/core/builder/cargo.rs | 6 ++++++ src/bootstrap/src/core/config/config.rs | 5 ++++- src/bootstrap/src/core/config/toml/rust.rs | 2 ++ src/bootstrap/src/utils/change_tracker.rs | 5 +++++ 6 files changed, 30 insertions(+), 1 deletion(-) diff --git a/bootstrap.example.toml b/bootstrap.example.toml index eac939577979..51529751dd58 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -859,6 +859,14 @@ # Trigger a `DebugBreak` after an internal compiler error during bootstrap on Windows #rust.break-on-ice = true +# Set the number of threads for the compiler frontend used during compilation of Rust code (passed to `-Zthreads`). +# The valid options are: +# 0 - Set the number of threads according to the detected number of threads of the host system +# 1 - Use a single thread for compilation of Rust code (the default) +# N - Number of threads used for compilation of Rust code +# +#rust.parallel-frontend-threads = 1 + # ============================================================================= # Distribution options # diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index b05a5cc8b818..1915986be28c 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -340,6 +340,11 @@ o( "don't truncate options when printing them in this configure script", ) v("set", None, "set arbitrary key/value pairs in TOML configuration") +v( + "parallel-frontend-threads", + "rust.parallel-frontend-threads", + "number of parallel threads for rustc compilation", +) def p(msg): diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 924bb4adb42d..7c24378f0bb5 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -679,6 +679,12 @@ impl Builder<'_> { // cargo would implicitly add it, it was discover that sometimes bootstrap only use // `rustflags` without `cargo` making it required. rustflags.arg("-Zunstable-options"); + + // Add parallel frontend threads configuration + if let Some(threads) = self.config.rust_parallel_frontend_threads { + rustflags.arg(&format!("-Zthreads={threads}")); + } + for (restricted_mode, name, values) in EXTRA_CHECK_CFGS { if restricted_mode.is_none() || *restricted_mode == Some(mode) { rustflags.arg(&check_cfg_arg(name, *values)); diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 678a9b639522..585bf5f870cb 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -191,7 +191,6 @@ pub struct Config { pub rust_optimize: RustOptimize, pub rust_codegen_units: Option, pub rust_codegen_units_std: Option, - pub rustc_debug_assertions: bool, pub std_debug_assertions: bool, pub tools_debug_assertions: bool, @@ -222,6 +221,8 @@ pub struct Config { pub rust_validate_mir_opts: Option, pub rust_std_features: BTreeSet, pub rust_break_on_ice: bool, + pub rust_parallel_frontend_threads: Option, + pub llvm_profile_use: Option, pub llvm_profile_generate: bool, pub llvm_libunwind_default: Option, @@ -534,6 +535,7 @@ impl Config { backtrace_on_ice: rust_backtrace_on_ice, verify_llvm_ir: rust_verify_llvm_ir, thin_lto_import_instr_limit: rust_thin_lto_import_instr_limit, + parallel_frontend_threads: rust_parallel_frontend_threads, remap_debuginfo: rust_remap_debuginfo, jemalloc: rust_jemalloc, test_compare_mode: rust_test_compare_mode, @@ -1298,6 +1300,7 @@ impl Config { rust_overflow_checks_std: rust_overflow_checks_std .or(rust_overflow_checks) .unwrap_or(rust_debug == Some(true)), + rust_parallel_frontend_threads: rust_parallel_frontend_threads.map(threads_from_config), rust_profile_generate: flags_rust_profile_generate.or(rust_profile_generate), rust_profile_use: flags_rust_profile_use.or(rust_profile_use), rust_randomize_layout: rust_randomize_layout.unwrap_or(false), diff --git a/src/bootstrap/src/core/config/toml/rust.rs b/src/bootstrap/src/core/config/toml/rust.rs index 4832a1d37b77..e5987d7040aa 100644 --- a/src/bootstrap/src/core/config/toml/rust.rs +++ b/src/bootstrap/src/core/config/toml/rust.rs @@ -66,6 +66,7 @@ define_config! { validate_mir_opts: Option = "validate-mir-opts", std_features: Option> = "std-features", break_on_ice: Option = "break-on-ice", + parallel_frontend_threads: Option = "parallel-frontend-threads", } } @@ -357,6 +358,7 @@ pub fn check_incompatible_options_for_ci_rustc( validate_mir_opts: _, frame_pointers: _, break_on_ice: _, + parallel_frontend_threads: _, } = ci_rust_config; // There are two kinds of checks for CI rustc incompatible options: diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 03b39882e30a..2c48cebd2df0 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -546,4 +546,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "The default value of the `gcc.download-ci-gcc` option has been changed to `true`.", }, + ChangeInfo { + change_id: 146458, + severity: ChangeSeverity::Info, + summary: "There is now a bootstrap option called `rust.parallel-frontend-threads`, which can be used to set the number of threads for the compiler frontend used during compilation of Rust code.", + }, ]; From c89b6a955c9e16c7c96714cea7945945caadcc79 Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Wed, 10 Sep 2025 12:40:29 +0000 Subject: [PATCH 551/682] Iterator repeat: no infinite loop for `last` and `count` --- library/core/src/iter/sources/repeat.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/library/core/src/iter/sources/repeat.rs b/library/core/src/iter/sources/repeat.rs index c4f5a483e5c2..4bcd5b16aea6 100644 --- a/library/core/src/iter/sources/repeat.rs +++ b/library/core/src/iter/sources/repeat.rs @@ -9,7 +9,7 @@ use crate::num::NonZero; /// [`Iterator::take()`], in order to make them finite. /// /// Use [`str::repeat()`] instead of this function if you just want to repeat -/// a char/string `n`th times. +/// a char/string `n` times. /// /// If the element type of the iterator you need does not implement `Clone`, /// or if you do not want to keep the repeated element in memory, you can @@ -98,11 +98,12 @@ impl Iterator for Repeat { } fn last(self) -> Option { - loop {} + Some(self.element) } + #[track_caller] fn count(self) -> usize { - loop {} + panic!("iterator is infinite"); } } From 9991ec282f3ff08c895e4a5262259139fe689953 Mon Sep 17 00:00:00 2001 From: Mu001999 Date: Sun, 9 Mar 2025 18:25:57 +0800 Subject: [PATCH 552/682] Keep space if expr follows identifier when lint unused parens --- compiler/rustc_lint/src/unused.rs | 8 +++++ .../ui/lint/unused_parens_follow_ident.fixed | 17 ++++++++++ tests/ui/lint/unused_parens_follow_ident.rs | 17 ++++++++++ .../ui/lint/unused_parens_follow_ident.stderr | 31 +++++++++++++++++++ 4 files changed, 73 insertions(+) create mode 100644 tests/ui/lint/unused_parens_follow_ident.fixed create mode 100644 tests/ui/lint/unused_parens_follow_ident.rs create mode 100644 tests/ui/lint/unused_parens_follow_ident.stderr diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 22d89d246127..edbbfba4f346 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -843,6 +843,10 @@ trait UnusedDelimLint { && !snip.ends_with(' ') { " " + } else if let Ok(snip) = sm.span_to_prev_source(value_span) + && snip.ends_with(|c: char| c.is_alphanumeric()) + { + " " } else { "" }; @@ -852,6 +856,10 @@ trait UnusedDelimLint { && !snip.starts_with(' ') { " " + } else if let Ok(snip) = sm.span_to_prev_source(value_span) + && snip.starts_with(|c: char| c.is_alphanumeric()) + { + " " } else { "" }; diff --git a/tests/ui/lint/unused_parens_follow_ident.fixed b/tests/ui/lint/unused_parens_follow_ident.fixed new file mode 100644 index 000000000000..e61b287e5a64 --- /dev/null +++ b/tests/ui/lint/unused_parens_follow_ident.fixed @@ -0,0 +1,17 @@ +//@ run-rustfix + +#![deny(unused_parens)] + +macro_rules! wrap { + ($name:ident $arg:expr) => { + $name($arg); + }; +} + +fn main() { + wrap!(unary routine()); //~ ERROR unnecessary parentheses around function argument + wrap!(unary routine()); //~ ERROR unnecessary parentheses around function argument +} + +fn unary(_: ()) {} +fn routine() {} diff --git a/tests/ui/lint/unused_parens_follow_ident.rs b/tests/ui/lint/unused_parens_follow_ident.rs new file mode 100644 index 000000000000..32a163345b2c --- /dev/null +++ b/tests/ui/lint/unused_parens_follow_ident.rs @@ -0,0 +1,17 @@ +//@ run-rustfix + +#![deny(unused_parens)] + +macro_rules! wrap { + ($name:ident $arg:expr) => { + $name($arg); + }; +} + +fn main() { + wrap!(unary(routine())); //~ ERROR unnecessary parentheses around function argument + wrap!(unary (routine())); //~ ERROR unnecessary parentheses around function argument +} + +fn unary(_: ()) {} +fn routine() {} diff --git a/tests/ui/lint/unused_parens_follow_ident.stderr b/tests/ui/lint/unused_parens_follow_ident.stderr new file mode 100644 index 000000000000..ce7bb26778c0 --- /dev/null +++ b/tests/ui/lint/unused_parens_follow_ident.stderr @@ -0,0 +1,31 @@ +error: unnecessary parentheses around function argument + --> $DIR/unused_parens_follow_ident.rs:12:16 + | +LL | wrap!(unary(routine())); + | ^ ^ + | +note: the lint level is defined here + --> $DIR/unused_parens_follow_ident.rs:3:9 + | +LL | #![deny(unused_parens)] + | ^^^^^^^^^^^^^ +help: remove these parentheses + | +LL - wrap!(unary(routine())); +LL + wrap!(unary routine()); + | + +error: unnecessary parentheses around function argument + --> $DIR/unused_parens_follow_ident.rs:13:17 + | +LL | wrap!(unary (routine())); + | ^ ^ + | +help: remove these parentheses + | +LL - wrap!(unary (routine())); +LL + wrap!(unary routine()); + | + +error: aborting due to 2 previous errors + From 1a1510816a69844fb3611efdc53acee07a55cebb Mon Sep 17 00:00:00 2001 From: Jeremy Smart Date: Thu, 11 Sep 2025 09:13:44 -0400 Subject: [PATCH 553/682] handle const generics, ?Sized, early bound lifetimes --- compiler/rustc_hir_analysis/src/check/mod.rs | 44 ++++++++++++++----- .../apitit-unimplemented-method.rs | 5 ++- .../apitit-unimplemented-method.stderr | 10 +++-- tests/ui/suggestions/auxiliary/dep.rs | 14 +++++- 4 files changed, 56 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 63d0f400aefc..e70d5505aae3 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -70,6 +70,7 @@ pub mod intrinsic; mod region; pub mod wfcheck; +use std::borrow::Cow; use std::num::NonZero; pub use check::{check_abi, check_custom_abi}; @@ -86,7 +87,7 @@ use rustc_middle::query::Providers; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::print::with_types_for_signature; use rustc_middle::ty::{ - self, GenericArgs, GenericArgsRef, GenericParamDefKind, Ty, TyCtxt, TypingMode, + self, GenericArgs, GenericArgsRef, OutlivesPredicate, Region, Ty, TyCtxt, TypingMode, }; use rustc_middle::{bug, span_bug}; use rustc_session::parse::feature_err; @@ -335,6 +336,7 @@ fn bounds_from_generic_predicates<'tcx>( assoc: ty::AssocItem, ) -> (String, String) { let mut types: FxIndexMap, Vec> = FxIndexMap::default(); + let mut regions: FxIndexMap, Vec>> = FxIndexMap::default(); let mut projections = vec![]; for (predicate, _) in predicates { debug!("predicate {:?}", predicate); @@ -351,20 +353,23 @@ fn bounds_from_generic_predicates<'tcx>( ty::ClauseKind::Projection(projection_pred) => { projections.push(bound_predicate.rebind(projection_pred)); } + ty::ClauseKind::RegionOutlives(OutlivesPredicate(a, b)) => { + regions.entry(a).or_default().push(b); + } _ => {} } } let mut where_clauses = vec![]; let generics = tcx.generics_of(assoc.def_id); - let types_str = generics + let params = generics .own_params .iter() - .filter(|p| matches!(p.kind, GenericParamDefKind::Type { synthetic: false, .. })) - .map(|p| { - // we just checked that it's a type, so the unwrap can't fail - let ty = tcx.mk_param_from_def(p).as_type().unwrap(); - if let Some(bounds) = types.get(&ty) { + .filter(|p| !p.kind.is_synthetic()) + .map(|p| match tcx.mk_param_from_def(p).kind() { + ty::GenericArgKind::Type(ty) => { + let bounds = + types.get(&ty).map(Cow::Borrowed).unwrap_or_else(|| Cow::Owned(Vec::new())); let mut bounds_str = vec![]; for bound in bounds.iter().copied() { let mut projections_str = vec![]; @@ -377,7 +382,11 @@ fn bounds_from_generic_predicates<'tcx>( projections_str.push(format!("{} = {}", name, p.term)); } } - let bound_def_path = tcx.def_path_str(bound); + let bound_def_path = if tcx.is_lang_item(bound, LangItem::MetaSized) { + String::from("?Sized") + } else { + tcx.def_path_str(bound) + }; if projections_str.is_empty() { where_clauses.push(format!("{}: {}", ty, bound_def_path)); } else { @@ -393,8 +402,21 @@ fn bounds_from_generic_predicates<'tcx>( } else { format!("{}: {}", ty, bounds_str.join(" + ")) } - } else { - ty.to_string() + } + ty::GenericArgKind::Const(ct) => { + format!("const {ct}: {}", tcx.type_of(p.def_id).skip_binder()) + } + ty::GenericArgKind::Lifetime(region) => { + if let Some(v) = regions.get(®ion) + && !v.is_empty() + { + format!( + "{region}: {}", + v.into_iter().map(Region::to_string).collect::>().join(" + ") + ) + } else { + region.to_string() + } } }) .collect::>(); @@ -409,7 +431,7 @@ fn bounds_from_generic_predicates<'tcx>( } let generics = - if types_str.is_empty() { "".to_string() } else { format!("<{}>", types_str.join(", ")) }; + if params.is_empty() { "".to_string() } else { format!("<{}>", params.join(", ")) }; let where_clauses = if where_clauses.is_empty() { "".to_string() diff --git a/tests/ui/suggestions/apitit-unimplemented-method.rs b/tests/ui/suggestions/apitit-unimplemented-method.rs index b182e1939b3e..c0cd709e2300 100644 --- a/tests/ui/suggestions/apitit-unimplemented-method.rs +++ b/tests/ui/suggestions/apitit-unimplemented-method.rs @@ -4,9 +4,12 @@ extern crate dep; use dep::*; struct Local; + impl Trait for Local {} //~^ ERROR not all trait items implemented //~| HELP implement the missing item: `fn foo(_: impl Sized) { todo!() }` -//~| HELP implement the missing item: `fn bar(_: impl Sized) { todo!() }` +//~| HELP implement the missing item: `fn bar(_: impl Sized) where Foo: MetaSized { todo!() }` +//~| HELP implement the missing item: `fn baz() { todo!() }` +//~| HELP implement the missing item: `fn quux<'a: 'b, 'b, T>() where T: ?Sized { todo!() }` fn main() {} diff --git a/tests/ui/suggestions/apitit-unimplemented-method.stderr b/tests/ui/suggestions/apitit-unimplemented-method.stderr index b309a64e9582..1f2e0ea2cad9 100644 --- a/tests/ui/suggestions/apitit-unimplemented-method.stderr +++ b/tests/ui/suggestions/apitit-unimplemented-method.stderr @@ -1,11 +1,13 @@ -error[E0046]: not all trait items implemented, missing: `foo`, `bar` - --> $DIR/apitit-unimplemented-method.rs:7:1 +error[E0046]: not all trait items implemented, missing: `foo`, `bar`, `baz`, `quux` + --> $DIR/apitit-unimplemented-method.rs:8:1 | LL | impl Trait for Local {} - | ^^^^^^^^^^^^^^^^^^^^ missing `foo`, `bar` in implementation + | ^^^^^^^^^^^^^^^^^^^^ missing `foo`, `bar`, `baz`, `quux` in implementation | = help: implement the missing item: `fn foo(_: impl Sized) { todo!() }` - = help: implement the missing item: `fn bar(_: impl Sized) { todo!() }` + = help: implement the missing item: `fn bar(_: impl Sized) where Foo: MetaSized { todo!() }` + = help: implement the missing item: `fn baz() { todo!() }` + = help: implement the missing item: `fn quux<'a: 'b, 'b, T>() where T: ?Sized { todo!() }` error: aborting due to 1 previous error diff --git a/tests/ui/suggestions/auxiliary/dep.rs b/tests/ui/suggestions/auxiliary/dep.rs index ac0de418313c..c28c8b8a52f5 100644 --- a/tests/ui/suggestions/auxiliary/dep.rs +++ b/tests/ui/suggestions/auxiliary/dep.rs @@ -1,4 +1,16 @@ +#![feature(sized_hierarchy)] + +use std::marker::MetaSized; + +pub struct Foo { + inner: T, +} + pub trait Trait { fn foo(_: impl Sized); - fn bar(_: impl Sized); + fn bar(_: impl Sized) + where + Foo: MetaSized; + fn baz<'a, const N: usize>(); + fn quux<'a: 'b, 'b, T: ?Sized>(); } From e652f97c6b71d485473c98bb0e5be1328db9bc45 Mon Sep 17 00:00:00 2001 From: Christian Poveda Date: Fri, 12 Sep 2025 11:10:45 -0500 Subject: [PATCH 554/682] Improve `core::ascii` coverage --- library/coretests/tests/ascii.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/library/coretests/tests/ascii.rs b/library/coretests/tests/ascii.rs index ce09ee507f11..297aa114e006 100644 --- a/library/coretests/tests/ascii.rs +++ b/library/coretests/tests/ascii.rs @@ -505,3 +505,10 @@ fn test_escape_ascii_iter() { let _ = it.advance_back_by(4); assert_eq!(it.to_string(), r#"fastpath\xffremainder"#); } + +#[test] +fn test_invalid_u8() { + for c in 128..=255 { + assert_eq!(core::ascii::Char::from_u8(c), None); + } +} From 94d3a8cb913da5823768710c0373c58bc5e1dce7 Mon Sep 17 00:00:00 2001 From: Christian Poveda Date: Fri, 12 Sep 2025 14:26:25 -0500 Subject: [PATCH 555/682] Improve `core::sync::atomic` coverage --- library/coretests/tests/atomic.rs | 248 +++++++++++++++++++++++++++++- 1 file changed, 242 insertions(+), 6 deletions(-) diff --git a/library/coretests/tests/atomic.rs b/library/coretests/tests/atomic.rs index b1ab443aa6e5..d888bd0f55a1 100644 --- a/library/coretests/tests/atomic.rs +++ b/library/coretests/tests/atomic.rs @@ -11,6 +11,38 @@ fn bool_() { assert_eq!(a.compare_exchange(false, true, SeqCst, SeqCst), Ok(false)); } +#[test] +#[should_panic = "there is no such thing as an acquire store"] +fn store_illegal_rt_store_acquire_ordering() { + let a = AtomicBool::new(false); + let ord = Ordering::Acquire; + a.store(true, ord); +} + +#[test] +#[should_panic = "there is no such thing as an acquire-release store"] +fn store_illegal_rt_store_acq_rel_ordering() { + let a = AtomicBool::new(false); + let ord = Ordering::AcqRel; + a.store(true, ord); +} + +#[test] +#[should_panic = "there is no such thing as a release load"] +fn store_illegal_rt_load_release_ordering() { + let a = AtomicBool::new(false); + let ord = Ordering::Release; + a.load(ord); +} + +#[test] +#[should_panic = "there is no such thing as an acquire-release load"] +fn store_illegal_rt_load_acq_rel_ordering() { + let a = AtomicBool::new(false); + let ord = Ordering::AcqRel; + a.load(ord); +} + #[test] fn bool_and() { let a = AtomicBool::new(true); @@ -283,25 +315,229 @@ fn atomic_compare_exchange() { static ATOMIC: AtomicIsize = AtomicIsize::new(0); ATOMIC.compare_exchange(0, 1, Relaxed, Relaxed).ok(); + ATOMIC.compare_exchange(0, 1, Relaxed, Acquire).ok(); + ATOMIC.compare_exchange(0, 1, Relaxed, SeqCst).ok(); ATOMIC.compare_exchange(0, 1, Acquire, Relaxed).ok(); - ATOMIC.compare_exchange(0, 1, Release, Relaxed).ok(); - ATOMIC.compare_exchange(0, 1, AcqRel, Relaxed).ok(); - ATOMIC.compare_exchange(0, 1, SeqCst, Relaxed).ok(); ATOMIC.compare_exchange(0, 1, Acquire, Acquire).ok(); + ATOMIC.compare_exchange(0, 1, Acquire, SeqCst).ok(); + ATOMIC.compare_exchange(0, 1, Release, Relaxed).ok(); + ATOMIC.compare_exchange(0, 1, Release, Acquire).ok(); + ATOMIC.compare_exchange(0, 1, Release, SeqCst).ok(); + ATOMIC.compare_exchange(0, 1, AcqRel, Relaxed).ok(); ATOMIC.compare_exchange(0, 1, AcqRel, Acquire).ok(); + ATOMIC.compare_exchange(0, 1, AcqRel, SeqCst).ok(); + ATOMIC.compare_exchange(0, 1, SeqCst, Relaxed).ok(); ATOMIC.compare_exchange(0, 1, SeqCst, Acquire).ok(); ATOMIC.compare_exchange(0, 1, SeqCst, SeqCst).ok(); ATOMIC.compare_exchange_weak(0, 1, Relaxed, Relaxed).ok(); + ATOMIC.compare_exchange_weak(0, 1, Relaxed, Acquire).ok(); + ATOMIC.compare_exchange_weak(0, 1, Relaxed, SeqCst).ok(); ATOMIC.compare_exchange_weak(0, 1, Acquire, Relaxed).ok(); - ATOMIC.compare_exchange_weak(0, 1, Release, Relaxed).ok(); - ATOMIC.compare_exchange_weak(0, 1, AcqRel, Relaxed).ok(); - ATOMIC.compare_exchange_weak(0, 1, SeqCst, Relaxed).ok(); ATOMIC.compare_exchange_weak(0, 1, Acquire, Acquire).ok(); + ATOMIC.compare_exchange_weak(0, 1, Acquire, SeqCst).ok(); + ATOMIC.compare_exchange_weak(0, 1, Release, Relaxed).ok(); + ATOMIC.compare_exchange_weak(0, 1, Release, Acquire).ok(); + ATOMIC.compare_exchange_weak(0, 1, Release, SeqCst).ok(); + ATOMIC.compare_exchange_weak(0, 1, AcqRel, Relaxed).ok(); ATOMIC.compare_exchange_weak(0, 1, AcqRel, Acquire).ok(); + ATOMIC.compare_exchange_weak(0, 1, AcqRel, SeqCst).ok(); + ATOMIC.compare_exchange_weak(0, 1, SeqCst, Relaxed).ok(); ATOMIC.compare_exchange_weak(0, 1, SeqCst, Acquire).ok(); ATOMIC.compare_exchange_weak(0, 1, SeqCst, SeqCst).ok(); } +#[test] +#[should_panic = "there is no such thing as an acquire-release failure ordering"] +fn atomic_compare_exchange_illegal_acq_rel() { + use Ordering::*; + + static ATOMIC: AtomicIsize = AtomicIsize::new(0); + + let failure = AcqRel; + + ATOMIC.compare_exchange(0, 1, Relaxed, failure).ok(); +} + +#[test] +#[should_panic = "there is no such thing as a release failure ordering"] +fn atomic_compare_exchange_illegal_release() { + use Ordering::*; + + static ATOMIC: AtomicIsize = AtomicIsize::new(0); + + let failure = Release; + + ATOMIC.compare_exchange(0, 1, Relaxed, failure).ok(); +} + +#[test] +#[should_panic = "there is no such thing as an acquire-release failure ordering"] +fn atomic_compare_exchange_weak_illegal_acq_rel() { + use Ordering::*; + + static ATOMIC: AtomicIsize = AtomicIsize::new(0); + + let failure = AcqRel; + + ATOMIC.compare_exchange_weak(0, 1, Relaxed, failure).ok(); +} + +#[test] +#[should_panic = "there is no such thing as a release failure ordering"] +fn atomic_compare_exchange_weak_illegal_release() { + use Ordering::*; + + static ATOMIC: AtomicIsize = AtomicIsize::new(0); + + let failure = Release; + + ATOMIC.compare_exchange_weak(0, 1, Relaxed, failure).ok(); +} + +#[test] +fn atomic_swap() { + use Ordering::*; + + static ATOMIC: AtomicBool = AtomicBool::new(false); + + assert_eq!(ATOMIC.swap(true, Relaxed), false); + assert_eq!(ATOMIC.swap(false, Acquire), true); + assert_eq!(ATOMIC.swap(true, Release), false); + assert_eq!(ATOMIC.swap(false, AcqRel), true); + assert_eq!(ATOMIC.swap(true, SeqCst), false); +} + +#[test] +fn atomic_add() { + use Ordering::*; + + static ATOMIC: AtomicU8 = AtomicU8::new(0); + + assert_eq!(ATOMIC.fetch_add(1, Relaxed), 0); + assert_eq!(ATOMIC.fetch_add(1, Acquire), 1); + assert_eq!(ATOMIC.fetch_add(1, Release), 2); + assert_eq!(ATOMIC.fetch_add(1, AcqRel), 3); + assert_eq!(ATOMIC.fetch_add(1, SeqCst), 4); + assert_eq!(ATOMIC.load(Relaxed), 5); +} + +#[test] +fn atomic_sub() { + use Ordering::*; + + static ATOMIC: AtomicU8 = AtomicU8::new(5); + + assert_eq!(ATOMIC.fetch_sub(1, Relaxed), 5); + assert_eq!(ATOMIC.fetch_sub(1, Acquire), 4); + assert_eq!(ATOMIC.fetch_sub(1, Release), 3); + assert_eq!(ATOMIC.fetch_sub(1, AcqRel), 2); + assert_eq!(ATOMIC.fetch_sub(1, SeqCst), 1); + assert_eq!(ATOMIC.load(Relaxed), 0); +} + +#[test] +fn atomic_and_or() { + use Ordering::*; + + static ATOMIC: AtomicBool = AtomicBool::new(false); + + assert_eq!(ATOMIC.fetch_or(true, Relaxed), false); + assert_eq!(ATOMIC.fetch_and(false, Relaxed), true); + assert_eq!(ATOMIC.fetch_or(true, Acquire), false); + assert_eq!(ATOMIC.fetch_and(false, Acquire), true); + assert_eq!(ATOMIC.fetch_or(true, Release), false); + assert_eq!(ATOMIC.fetch_and(false, Release), true); + assert_eq!(ATOMIC.fetch_or(true, AcqRel), false); + assert_eq!(ATOMIC.fetch_and(false, AcqRel), true); + assert_eq!(ATOMIC.fetch_or(true, SeqCst), false); + assert_eq!(ATOMIC.fetch_and(false, SeqCst), true); + assert_eq!(ATOMIC.load(Relaxed), false); +} + +#[test] +fn atomic_nand() { + use Ordering::*; + + static ATOMIC: AtomicU8 = AtomicU8::new(0x13); + + assert_eq!(ATOMIC.fetch_nand(0x13, Relaxed), 0x13); + assert_eq!(ATOMIC.fetch_nand(0xec, Acquire), 0xec); + assert_eq!(ATOMIC.fetch_nand(0x13, Release), 0x13); + assert_eq!(ATOMIC.fetch_nand(0xec, AcqRel), 0xec); + assert_eq!(ATOMIC.fetch_nand(0x13, SeqCst), 0x13); + assert_eq!(ATOMIC.load(Relaxed), 0xec); +} + +#[test] +fn atomic_xor() { + use Ordering::*; + + static ATOMIC: AtomicBool = AtomicBool::new(false); + + assert_eq!(ATOMIC.fetch_xor(true, Relaxed), false); + assert_eq!(ATOMIC.fetch_xor(true, Acquire), true); + assert_eq!(ATOMIC.fetch_xor(true, Release), false); + assert_eq!(ATOMIC.fetch_xor(true, AcqRel), true); + assert_eq!(ATOMIC.fetch_xor(true, SeqCst), false); + assert_eq!(ATOMIC.load(Relaxed), true); +} + +#[test] +fn atomic_max() { + use Ordering::*; + + static ATOMIC: AtomicI8 = AtomicI8::new(0); + + assert_eq!(ATOMIC.fetch_max(1, Relaxed), 0); + assert_eq!(ATOMIC.fetch_max(2, Acquire), 1); + assert_eq!(ATOMIC.fetch_max(3, Release), 2); + assert_eq!(ATOMIC.fetch_max(4, AcqRel), 3); + assert_eq!(ATOMIC.fetch_max(5, SeqCst), 4); + assert_eq!(ATOMIC.load(Relaxed), 5); +} + +#[test] +fn atomic_umax() { + use Ordering::*; + + static ATOMIC: AtomicU8 = AtomicU8::new(0); + + assert_eq!(ATOMIC.fetch_max(1, Relaxed), 0); + assert_eq!(ATOMIC.fetch_max(2, Acquire), 1); + assert_eq!(ATOMIC.fetch_max(3, Release), 2); + assert_eq!(ATOMIC.fetch_max(4, AcqRel), 3); + assert_eq!(ATOMIC.fetch_max(5, SeqCst), 4); + assert_eq!(ATOMIC.load(Relaxed), 5); +} + +#[test] +fn atomic_min() { + use Ordering::*; + + static ATOMIC: AtomicI8 = AtomicI8::new(5); + + assert_eq!(ATOMIC.fetch_min(4, Relaxed), 5); + assert_eq!(ATOMIC.fetch_min(3, Acquire), 4); + assert_eq!(ATOMIC.fetch_min(2, Release), 3); + assert_eq!(ATOMIC.fetch_min(1, AcqRel), 2); + assert_eq!(ATOMIC.fetch_min(0, SeqCst), 1); + assert_eq!(ATOMIC.load(Relaxed), 0); +} + +#[test] +fn atomic_umin() { + use Ordering::*; + + static ATOMIC: AtomicU8 = AtomicU8::new(5); + + assert_eq!(ATOMIC.fetch_min(4, Relaxed), 5); + assert_eq!(ATOMIC.fetch_min(3, Acquire), 4); + assert_eq!(ATOMIC.fetch_min(2, Release), 3); + assert_eq!(ATOMIC.fetch_min(1, AcqRel), 2); + assert_eq!(ATOMIC.fetch_min(0, SeqCst), 1); + assert_eq!(ATOMIC.load(Relaxed), 0); +} + /* FIXME(#110395) #[test] fn atomic_const_from() { From a535042e80a38196a58c27a8c95552546affe5dc Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 16 Sep 2025 04:42:27 +0200 Subject: [PATCH 556/682] Do not run ui test if options specific to llvm are used when another codegen backend is used --- src/tools/compiletest/src/common.rs | 4 ++++ src/tools/compiletest/src/directives/needs.rs | 13 ++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 143ccdcb9e52..6da102b1b5f1 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -203,6 +203,10 @@ impl CodegenBackend { Self::Llvm => "llvm", } } + + pub fn is_llvm(self) -> bool { + matches!(self, Self::Llvm) + } } /// Configuration for `compiletest` *per invocation*. diff --git a/src/tools/compiletest/src/directives/needs.rs b/src/tools/compiletest/src/directives/needs.rs index ee46f4c70cb8..3b7a9478717f 100644 --- a/src/tools/compiletest/src/directives/needs.rs +++ b/src/tools/compiletest/src/directives/needs.rs @@ -81,8 +81,8 @@ pub(super) fn handle_needs( }, Need { name: "needs-enzyme", - condition: config.has_enzyme, - ignore_reason: "ignored when LLVM Enzyme is disabled", + condition: config.has_enzyme && config.default_codegen_backend.is_llvm(), + ignore_reason: "ignored when LLVM Enzyme is disabled or LLVM is not the default codegen backend", }, Need { name: "needs-run-enabled", @@ -161,8 +161,8 @@ pub(super) fn handle_needs( }, Need { name: "needs-llvm-zstd", - condition: cache.llvm_zstd, - ignore_reason: "ignored if LLVM wasn't build with zstd for ELF section compression", + condition: cache.llvm_zstd && config.default_codegen_backend.is_llvm(), + ignore_reason: "ignored if LLVM wasn't build with zstd for ELF section compression or LLVM is not the default codegen backend", }, Need { name: "needs-rustc-debug-assertions", @@ -279,7 +279,10 @@ pub(super) fn handle_needs( // Handled elsewhere. if name == "needs-llvm-components" { - return IgnoreDecision::Continue; + if config.default_codegen_backend.is_llvm() { + return IgnoreDecision::Continue; + } + return IgnoreDecision::Ignore { reason: "LLVM specific test".into() }; } let mut found_valid = false; From 6912631d3ead427848a559ef5af66ab6f30e79ed Mon Sep 17 00:00:00 2001 From: Tawan Muadmuenwai Date: Mon, 15 Sep 2025 21:24:57 +0700 Subject: [PATCH 557/682] Add span for struct tail recursion limit error --- .../src/type_check/canonical.rs | 16 ++++--- .../src/const_eval/valtrees.rs | 2 + compiler/rustc_hir_typeck/src/expectation.rs | 9 +++- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 1 + compiler/rustc_middle/src/error.rs | 2 + compiler/rustc_middle/src/ty/layout.rs | 2 + compiler/rustc_middle/src/ty/sty.rs | 3 +- compiler/rustc_middle/src/ty/util.rs | 17 +++++-- .../src/traits/project.rs | 1 + compiler/rustc_ty_utils/src/layout.rs | 46 ++++++++++--------- compiler/rustc_ty_utils/src/ty.rs | 1 + .../ui/did_you_mean/recursion_limit_deref.rs | 7 +-- .../did_you_mean/recursion_limit_deref.stderr | 18 ++++++-- ...te-instantiation-struct-tail-ice-114484.rs | 28 +++++------ ...nstantiation-struct-tail-ice-114484.stderr | 8 ++-- tests/ui/infinite/infinite-struct.rs | 3 +- tests/ui/infinite/infinite-struct.stderr | 6 ++- .../invalid/issue-114435-layout-type-err.rs | 3 +- .../solver-cycles/129541-recursive-struct.rs | 3 +- 19 files changed, 111 insertions(+), 65 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index 2627ed899a93..aece0bda3469 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs @@ -230,8 +230,14 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { location: impl NormalizeLocation, ) -> Ty<'tcx> { let tcx = self.tcx(); + let body = self.body; + + let cause = ObligationCause::misc( + location.to_locations().span(body), + body.source.def_id().expect_local(), + ); + if self.infcx.next_trait_solver() { - let body = self.body; let param_env = self.infcx.param_env; // FIXME: Make this into a real type op? self.fully_perform_op( @@ -241,10 +247,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { |ocx| { let structurally_normalize = |ty| { ocx.structurally_normalize_ty( - &ObligationCause::misc( - location.to_locations().span(body), - body.source.def_id().expect_local(), - ), + &cause, param_env, ty, ) @@ -253,6 +256,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let tail = tcx.struct_tail_raw( ty, + &cause, structurally_normalize, || {}, ); @@ -265,7 +269,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { .unwrap_or_else(|guar| Ty::new_error(tcx, guar)) } else { let mut normalize = |ty| self.normalize(ty, location); - let tail = tcx.struct_tail_raw(ty, &mut normalize, || {}); + let tail = tcx.struct_tail_raw(ty, &cause, &mut normalize, || {}); normalize(tail) } } diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index 37c6c4a61d8d..7c41258ebfe5 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -1,6 +1,7 @@ use rustc_abi::{BackendRepr, FieldIdx, VariantIdx}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId, ValTreeCreationError}; +use rustc_middle::traits::ObligationCause; use rustc_middle::ty::layout::{LayoutCx, TyAndLayout}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, mir}; @@ -196,6 +197,7 @@ fn reconstruct_place_meta<'tcx>( // Traverse the type, and update `last_valtree` as we go. let tail = tcx.struct_tail_raw( layout.ty, + &ObligationCause::dummy(), |ty| ty, || { let branches = last_valtree.unwrap_branch(); diff --git a/compiler/rustc_hir_typeck/src/expectation.rs b/compiler/rustc_hir_typeck/src/expectation.rs index 6d95b6917e29..2fbea5b61cfc 100644 --- a/compiler/rustc_hir_typeck/src/expectation.rs +++ b/compiler/rustc_hir_typeck/src/expectation.rs @@ -1,3 +1,4 @@ +use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{self, Ty}; use rustc_span::Span; @@ -74,8 +75,14 @@ impl<'a, 'tcx> Expectation<'tcx> { /// See the test case `test/ui/coerce-expect-unsized.rs` and #20169 /// for examples of where this comes up,. pub(super) fn rvalue_hint(fcx: &FnCtxt<'a, 'tcx>, ty: Ty<'tcx>) -> Expectation<'tcx> { + let span = match ty.kind() { + ty::Adt(adt_def, _) => fcx.tcx.def_span(adt_def.did()), + _ => fcx.tcx.def_span(fcx.body_id), + }; + let cause = ObligationCause::misc(span, fcx.body_id); + // FIXME: This is not right, even in the old solver... - match fcx.tcx.struct_tail_raw(ty, |ty| ty, || {}).kind() { + match fcx.tcx.struct_tail_raw(ty, &cause, |ty| ty, || {}).kind() { ty::Slice(_) | ty::Str | ty::Dynamic(..) => ExpectRvalueLikeUnsized(ty), _ => ExpectHasType(ty), } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 7370124e800d..4563159314d1 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -424,6 +424,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !ty.references_error() { let tail = self.tcx.struct_tail_raw( ty, + &self.misc(span), |ty| { if self.next_trait_solver() { self.try_structurally_resolve_type(span, ty) diff --git a/compiler/rustc_middle/src/error.rs b/compiler/rustc_middle/src/error.rs index dad402ec6961..e3e1393b5f9a 100644 --- a/compiler/rustc_middle/src/error.rs +++ b/compiler/rustc_middle/src/error.rs @@ -71,6 +71,8 @@ pub enum TypeMismatchReason { #[diag(middle_recursion_limit_reached)] #[help] pub(crate) struct RecursionLimitReached<'tcx> { + #[primary_span] + pub span: Span, pub ty: Ty<'tcx>, pub suggested_limit: rustc_hir::limit::Limit, } diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 2114d080dfa4..572d7e037b79 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -22,6 +22,7 @@ use {rustc_abi as abi, rustc_hir as hir}; use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::query::TyCtxtAt; +use crate::traits::ObligationCause; use crate::ty::normalize_erasing_regions::NormalizationError; use crate::ty::{self, CoroutineArgsExt, Ty, TyCtxt, TypeVisitableExt}; @@ -384,6 +385,7 @@ impl<'tcx> SizeSkeleton<'tcx> { let tail = tcx.struct_tail_raw( pointee, + &ObligationCause::dummy(), |ty| match tcx.try_normalize_erasing_regions(typing_env, ty) { Ok(ty) => ty, Err(e) => Ty::new_error_with_message( diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 755fc68d86f3..ea07f5c18dd1 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -23,6 +23,7 @@ use ty::util::IntTypeExt; use super::GenericParamDefKind; use crate::infer::canonical::Canonical; +use crate::traits::ObligationCause; use crate::ty::InferTy::*; use crate::ty::{ self, AdtDef, BoundRegionKind, Discr, GenericArg, GenericArgs, GenericArgsRef, List, ParamEnv, @@ -1640,7 +1641,7 @@ impl<'tcx> Ty<'tcx> { tcx: TyCtxt<'tcx>, normalize: impl FnMut(Ty<'tcx>) -> Ty<'tcx>, ) -> Result, Ty<'tcx>> { - let tail = tcx.struct_tail_raw(self, normalize, || {}); + let tail = tcx.struct_tail_raw(self, &ObligationCause::dummy(), normalize, || {}); match tail.kind() { // Sized types ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 029586a9c554..9e95a2c3cb37 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -24,6 +24,7 @@ use super::TypingEnv; use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::mir; use crate::query::Providers; +use crate::traits::ObligationCause; use crate::ty::layout::{FloatExt, IntegerExt}; use crate::ty::{ self, Asyncness, FallibleTypeFolder, GenericArgKind, GenericArgsRef, Ty, TyCtxt, TypeFoldable, @@ -216,7 +217,12 @@ impl<'tcx> TyCtxt<'tcx> { typing_env: ty::TypingEnv<'tcx>, ) -> Ty<'tcx> { let tcx = self; - tcx.struct_tail_raw(ty, |ty| tcx.normalize_erasing_regions(typing_env, ty), || {}) + tcx.struct_tail_raw( + ty, + &ObligationCause::dummy(), + |ty| tcx.normalize_erasing_regions(typing_env, ty), + || {}, + ) } /// Returns true if a type has metadata. @@ -248,6 +254,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn struct_tail_raw( self, mut ty: Ty<'tcx>, + cause: &ObligationCause<'tcx>, mut normalize: impl FnMut(Ty<'tcx>) -> Ty<'tcx>, // This is currently used to allow us to walk a ValTree // in lockstep with the type in order to get the ValTree branch that @@ -261,9 +268,11 @@ impl<'tcx> TyCtxt<'tcx> { Limit(0) => Limit(2), limit => limit * 2, }; - let reported = self - .dcx() - .emit_err(crate::error::RecursionLimitReached { ty, suggested_limit }); + let reported = self.dcx().emit_err(crate::error::RecursionLimitReached { + span: cause.span, + ty, + suggested_limit, + }); return Ty::new_error(self, reported); } match *ty.kind() { diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 884d53732fe2..042d6def84c3 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1057,6 +1057,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( Some(LangItem::PointeeTrait) => { let tail = selcx.tcx().struct_tail_raw( self_ty, + &obligation.cause, |ty| { // We throw away any obligations we get from this, since we normalize // and confirm these obligations once again during confirmation diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 79f7e228e2ad..c48d16701163 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -10,6 +10,7 @@ use rustc_hashes::Hash64; use rustc_index::IndexVec; use rustc_middle::bug; use rustc_middle::query::Providers; +use rustc_middle::traits::ObligationCause; use rustc_middle::ty::layout::{ FloatExt, HasTyCtxt, IntegerExt, LayoutCx, LayoutError, LayoutOf, TyAndLayout, }; @@ -390,30 +391,31 @@ fn layout_of_uncached<'tcx>( let metadata = if let Some(metadata_def_id) = tcx.lang_items().metadata_type() { let pointee_metadata = Ty::new_projection(tcx, metadata_def_id, [pointee]); - let metadata_ty = - match tcx.try_normalize_erasing_regions(cx.typing_env, pointee_metadata) { - Ok(metadata_ty) => metadata_ty, - Err(mut err) => { - // Usually `::Metadata` can't be normalized because - // its struct tail cannot be normalized either, so try to get a - // more descriptive layout error here, which will lead to less confusing - // diagnostics. - // - // We use the raw struct tail function here to get the first tail - // that is an alias, which is likely the cause of the normalization - // error. - match tcx.try_normalize_erasing_regions( - cx.typing_env, - tcx.struct_tail_raw(pointee, |ty| ty, || {}), - ) { - Ok(_) => {} - Err(better_err) => { - err = better_err; - } + let metadata_ty = match tcx + .try_normalize_erasing_regions(cx.typing_env, pointee_metadata) + { + Ok(metadata_ty) => metadata_ty, + Err(mut err) => { + // Usually `::Metadata` can't be normalized because + // its struct tail cannot be normalized either, so try to get a + // more descriptive layout error here, which will lead to less confusing + // diagnostics. + // + // We use the raw struct tail function here to get the first tail + // that is an alias, which is likely the cause of the normalization + // error. + match tcx.try_normalize_erasing_regions( + cx.typing_env, + tcx.struct_tail_raw(pointee, &ObligationCause::dummy(), |ty| ty, || {}), + ) { + Ok(_) => {} + Err(better_err) => { + err = better_err; } - return Err(error(cx, LayoutError::NormalizationFailure(pointee, err))); } - }; + return Err(error(cx, LayoutError::NormalizationFailure(pointee, err))); + } + }; let metadata_layout = cx.layout_of(metadata_ty)?; // If the metadata is a 1-zst, then the pointer is thin. diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index a5987757dc34..64e62e173876 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -353,6 +353,7 @@ fn impl_self_is_guaranteed_unsized<'tcx>(tcx: TyCtxt<'tcx>, impl_def_id: DefId) let tail = tcx.struct_tail_raw( tcx.type_of(impl_def_id).instantiate_identity(), + &cause, |ty| { ocx.structurally_normalize_ty(&cause, param_env, ty).unwrap_or_else(|_| { Ty::new_error_with_message( diff --git a/tests/ui/did_you_mean/recursion_limit_deref.rs b/tests/ui/did_you_mean/recursion_limit_deref.rs index e53007388af2..3ae956b751d8 100644 --- a/tests/ui/did_you_mean/recursion_limit_deref.rs +++ b/tests/ui/did_you_mean/recursion_limit_deref.rs @@ -1,3 +1,6 @@ +//~ ERROR reached the recursion limit finding the struct tail for `K` +//~| ERROR reached the recursion limit finding the struct tail for `Bottom` + // Test that the recursion limit can be changed and that the compiler // suggests a fix. In this case, we have a long chain of Deref impls // which will cause an overflow during the autoderef loop. @@ -9,6 +12,7 @@ macro_rules! link { ($outer:ident, $inner:ident) => { struct $outer($inner); + //~^ ERROR reached the recursion limit finding the struct tail for `Bottom` impl $outer { fn new() -> $outer { @@ -51,6 +55,3 @@ fn main() { let x: &Bottom = &t; //~ ERROR mismatched types //~^ error recursion limit } - -//~? ERROR reached the recursion limit finding the struct tail for `K` -//~? ERROR reached the recursion limit finding the struct tail for `Bottom` diff --git a/tests/ui/did_you_mean/recursion_limit_deref.stderr b/tests/ui/did_you_mean/recursion_limit_deref.stderr index 23341ec6bdc2..faa85dc5ae98 100644 --- a/tests/ui/did_you_mean/recursion_limit_deref.stderr +++ b/tests/ui/did_you_mean/recursion_limit_deref.stderr @@ -6,8 +6,20 @@ error: reached the recursion limit finding the struct tail for `Bottom` | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "20"]` +error: reached the recursion limit finding the struct tail for `Bottom` + --> $DIR/recursion_limit_deref.rs:14:9 + | +LL | struct $outer($inner); + | ^^^^^^^^^^^^^^^^^^^^^^ +... +LL | link!(A, B); + | ----------- in this macro invocation + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "20"]` + = note: this error originates in the macro `link` (in Nightly builds, run with -Z macro-backtrace for more info) + error[E0055]: reached the recursion limit while auto-dereferencing `J` - --> $DIR/recursion_limit_deref.rs:51:22 + --> $DIR/recursion_limit_deref.rs:55:22 | LL | let x: &Bottom = &t; | ^^ deref recursion limit reached @@ -15,7 +27,7 @@ LL | let x: &Bottom = &t; = help: consider increasing the recursion limit by adding a `#![recursion_limit = "20"]` attribute to your crate (`recursion_limit_deref`) error[E0308]: mismatched types - --> $DIR/recursion_limit_deref.rs:51:22 + --> $DIR/recursion_limit_deref.rs:55:22 | LL | let x: &Bottom = &t; | ------- ^^ expected `&Bottom`, found `&Top` @@ -25,7 +37,7 @@ LL | let x: &Bottom = &t; = note: expected reference `&Bottom` found reference `&Top` -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors Some errors have detailed explanations: E0055, E0308. For more information about an error, try `rustc --explain E0055`. diff --git a/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.rs b/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.rs index f7117368ece7..b8ea353df938 100644 --- a/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.rs +++ b/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.rs @@ -1,4 +1,17 @@ -//~ ERROR reached the recursion limit while instantiating `` +//~| ERROR reached the recursion limit finding the struct tail for `SomeData<256>` +//~| ERROR reached the recursion limit finding the struct tail for `SomeData<256>` +//~| ERROR reached the recursion limit finding the struct tail for `SomeData<256>` +//~| ERROR reached the recursion limit finding the struct tail for `VirtualWrapper, 0>` +//~| ERROR reached the recursion limit finding the struct tail for `VirtualWrapper, 0>` +//~| ERROR reached the recursion limit finding the struct tail for `VirtualWrapper, 0>` +//~| ERROR reached the recursion limit finding the struct tail for `VirtualWrapper, 0>` +//~| ERROR reached the recursion limit while instantiating ` as MyTrait>::virtualize` + //@ build-fail //@ compile-flags: --diagnostic-width=100 -Zwrite-long-types-to-disk=yes @@ -72,16 +85,3 @@ fn main() { let test = SomeData([0; 256]); test.virtualize(); } - -//~? ERROR reached the recursion limit finding the struct tail for `[u8; 256]` -//~? ERROR reached the recursion limit finding the struct tail for `[u8; 256]` -//~? ERROR reached the recursion limit finding the struct tail for `[u8; 256]` -//~? ERROR reached the recursion limit finding the struct tail for `[u8; 256]` -//~? ERROR reached the recursion limit finding the struct tail for `SomeData<256>` -//~? ERROR reached the recursion limit finding the struct tail for `SomeData<256>` -//~? ERROR reached the recursion limit finding the struct tail for `SomeData<256>` -//~? ERROR reached the recursion limit finding the struct tail for `SomeData<256>` -//~? ERROR reached the recursion limit finding the struct tail for `VirtualWrapper, 0>` -//~? ERROR reached the recursion limit finding the struct tail for `VirtualWrapper, 0>` -//~? ERROR reached the recursion limit finding the struct tail for `VirtualWrapper, 0>` -//~? ERROR reached the recursion limit finding the struct tail for `VirtualWrapper, 0>` diff --git a/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.stderr b/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.stderr index faf9cbe2318c..deccc88e64fa 100644 --- a/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.stderr +++ b/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.stderr @@ -18,7 +18,7 @@ error: reached the recursion limit finding the struct tail for `[u8; 256]` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` note: the above error was encountered while instantiating `fn virtualize_my_trait::>` - --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:25:18 + --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:38:18 | LL | unsafe { virtualize_my_trait(L, self) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -46,7 +46,7 @@ error: reached the recursion limit finding the struct tail for `SomeData<256>` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` note: the above error was encountered while instantiating `fn virtualize_my_trait::>` - --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:25:18 + --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:38:18 | LL | unsafe { virtualize_my_trait(L, self) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -74,7 +74,7 @@ error: reached the recursion limit finding the struct tail for `VirtualWrapper>` - --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:25:18 + --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:38:18 | LL | unsafe { virtualize_my_trait(L, self) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -85,7 +85,7 @@ LL | unsafe { virtualize_my_trait(L, self) } error: reached the recursion limit while instantiating ` as MyTrait>::virtualize` | note: ` as MyTrait>::virtualize` defined here - --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:24:5 + --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:37:5 | LL | fn virtualize(&self) -> &dyn MyTrait { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/infinite/infinite-struct.rs b/tests/ui/infinite/infinite-struct.rs index fd47a4ec9cc6..d78445582460 100644 --- a/tests/ui/infinite/infinite-struct.rs +++ b/tests/ui/infinite/infinite-struct.rs @@ -1,6 +1,7 @@ struct Take(Take); //~^ ERROR has infinite size //~| ERROR cycle +//~| ERROR reached the recursion limit finding the struct tail for `Take` // check that we don't hang trying to find the tail of a recursive struct (#79437) fn foo() -> Take { @@ -15,5 +16,3 @@ struct Foo { //~ ERROR has infinite size struct Bar([T; 1]); fn main() {} - -//~? ERROR reached the recursion limit finding the struct tail for `Take` diff --git a/tests/ui/infinite/infinite-struct.stderr b/tests/ui/infinite/infinite-struct.stderr index 5896aec399dc..0d1ec4989aa5 100644 --- a/tests/ui/infinite/infinite-struct.stderr +++ b/tests/ui/infinite/infinite-struct.stderr @@ -10,7 +10,7 @@ LL | struct Take(Box); | ++++ + error[E0072]: recursive type `Foo` has infinite size - --> $DIR/infinite-struct.rs:11:1 + --> $DIR/infinite-struct.rs:12:1 | LL | struct Foo { | ^^^^^^^^^^ @@ -23,6 +23,10 @@ LL | x: Bar>, | ++++ + error: reached the recursion limit finding the struct tail for `Take` + --> $DIR/infinite-struct.rs:1:1 + | +LL | struct Take(Take); + | ^^^^^^^^^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` diff --git a/tests/ui/invalid/issue-114435-layout-type-err.rs b/tests/ui/invalid/issue-114435-layout-type-err.rs index 07f310478d3c..ae04759af5df 100644 --- a/tests/ui/invalid/issue-114435-layout-type-err.rs +++ b/tests/ui/invalid/issue-114435-layout-type-err.rs @@ -1,3 +1,4 @@ +//~ ERROR reached the recursion limit finding the struct tail for `Bottom` //@ check-fail //@ compile-flags: --crate-type lib -Cdebuginfo=2 @@ -40,5 +41,3 @@ link!(J, K); link!(K, Bottom); fn main() {} - -//~? ERROR reached the recursion limit finding the struct tail for `Bottom` diff --git a/tests/ui/traits/solver-cycles/129541-recursive-struct.rs b/tests/ui/traits/solver-cycles/129541-recursive-struct.rs index 723179302e30..c5c2c27b2826 100644 --- a/tests/ui/traits/solver-cycles/129541-recursive-struct.rs +++ b/tests/ui/traits/solver-cycles/129541-recursive-struct.rs @@ -1,3 +1,4 @@ +//~ ERROR reached the recursion limit finding the struct tail for `<[Hello] as Normalize>::Assoc` // Regression test for #129541 //@ revisions: unique_curr unique_next multiple_curr multiple_next @@ -24,5 +25,3 @@ struct Hello { } fn main() {} - -//~? ERROR reached the recursion limit finding the struct tail for `<[Hello] as Normalize>::Assoc` From 9c423796bbbb989bac15524d78cdc6d46641e5da Mon Sep 17 00:00:00 2001 From: binarycat Date: Mon, 15 Sep 2025 16:31:32 -0500 Subject: [PATCH 558/682] bootstrap: emit hint if a config key is used in the wrong section --- src/bootstrap/src/core/config/config.rs | 109 +++++++++++++++++++++--- 1 file changed, 98 insertions(+), 11 deletions(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 678a9b639522..0213047f3a14 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -1853,13 +1853,7 @@ fn load_toml_config( } else { toml_path.clone() }); - ( - get_toml(&toml_path).unwrap_or_else(|e| { - eprintln!("ERROR: Failed to parse '{}': {e}", toml_path.display()); - exit!(2); - }), - path, - ) + (get_toml(&toml_path).unwrap_or_else(|e| bad_config(&toml_path, e)), path) } else { (TomlConfig::default(), None) } @@ -1892,10 +1886,8 @@ fn postprocess_toml( .unwrap() .join(include_path); - let included_toml = get_toml(&include_path).unwrap_or_else(|e| { - eprintln!("ERROR: Failed to parse '{}': {e}", include_path.display()); - exit!(2); - }); + let included_toml = + get_toml(&include_path).unwrap_or_else(|e| bad_config(&include_path, e)); toml.merge( Some(include_path), &mut Default::default(), @@ -2398,3 +2390,98 @@ pub(crate) fn read_file_by_commit<'a>( git.arg("show").arg(format!("{commit}:{}", file.to_str().unwrap())); git.run_capture_stdout(dwn_ctx.exec_ctx).stdout() } + +fn bad_config(toml_path: &Path, e: toml::de::Error) -> ! { + eprintln!("ERROR: Failed to parse '{}': {e}", toml_path.display()); + let e_s = e.to_string(); + if e_s.contains("unknown field") + && let Some(field_name) = e_s.split("`").nth(1) + && let sections = find_correct_section_for_field(field_name) + && !sections.is_empty() + { + if sections.len() == 1 { + match sections[0] { + WouldBeValidFor::TopLevel { is_section } => { + if is_section { + eprintln!( + "hint: section name `{field_name}` used as a key within a section" + ); + } else { + eprintln!("hint: try using `{field_name}` as a top level key"); + } + } + WouldBeValidFor::Section(section) => { + eprintln!("hint: try moving `{field_name}` to the `{section}` section") + } + } + } else { + eprintln!( + "hint: `{field_name}` would be valid {}", + join_oxford_comma(sections.iter(), "or"), + ); + } + } + + exit!(2); +} + +#[derive(Copy, Clone, Debug)] +enum WouldBeValidFor { + TopLevel { is_section: bool }, + Section(&'static str), +} + +fn join_oxford_comma( + mut parts: impl ExactSizeIterator, + conj: &str, +) -> String { + use std::fmt::Write; + let mut out = String::new(); + + assert!(parts.len() > 1); + while let Some(part) = parts.next() { + if parts.len() == 0 { + write!(&mut out, "{conj} {part}") + } else { + write!(&mut out, "{part}, ") + } + .unwrap(); + } + out +} + +impl std::fmt::Display for WouldBeValidFor { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::TopLevel { .. } => write!(f, "at top level"), + Self::Section(section_name) => write!(f, "in section `{section_name}`"), + } + } +} + +fn find_correct_section_for_field(field_name: &str) -> Vec { + let sections = ["build", "install", "llvm", "gcc", "rust", "dist"]; + sections + .iter() + .map(Some) + .chain([None]) + .filter_map(|section_name| { + let dummy_config_str = if let Some(section_name) = section_name { + format!("{section_name}.{field_name} = 0\n") + } else { + format!("{field_name} = 0\n") + }; + let is_unknown_field = toml::from_str::(&dummy_config_str) + .and_then(TomlConfig::deserialize) + .err() + .is_some_and(|e| e.to_string().contains("unknown field")); + if is_unknown_field { + None + } else { + Some(section_name.copied().map(WouldBeValidFor::Section).unwrap_or_else(|| { + WouldBeValidFor::TopLevel { is_section: sections.contains(&field_name) } + })) + } + }) + .collect() +} From 91cf067f7174eac7ec0492e829bcbb844d4471d7 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Mon, 15 Sep 2025 19:57:08 +0200 Subject: [PATCH 559/682] ci: x86_64-gnu-tools: Add `--test-args` regression test --- src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh index ff9fedad6567..14cf63b94d48 100755 --- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh +++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/checktools.sh @@ -30,3 +30,10 @@ cat /tmp/toolstate/toolstates.json python3 "$X_PY" test --stage 2 check-tools python3 "$X_PY" test --stage 2 src/tools/clippy python3 "$X_PY" test --stage 2 src/tools/rustfmt + +# The below is a regression test for https://github.com/rust-lang/rust/pull/146501#issuecomment-3292608398. +# The bug caused 0 tests to run. By grepping on that 1 test is run we prevent regressing. +# Any test can be used. We arbitrarily chose `tests/ui/lint/unused/unused-result.rs`. +python3 "$X_PY" test tests/ui --test-args tests/ui/lint/unused/unused-result.rs --force-rerun | +grep --fixed-strings 'test result: ok. 1 passed; 0 failed; 0 ignored;' || +( echo "ERROR: --test-args functionality is broken" && exit 1 ) From c916e8886b3a1c6f32daf467c1c3d8316e35a6df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 16 Sep 2025 11:08:43 -0700 Subject: [PATCH 560/682] fmt --- .../src/error_reporting/infer/need_type_info.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs index 94772be16be2..75283dc4ffaa 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs @@ -4,11 +4,12 @@ use std::path::PathBuf; use rustc_errors::codes::*; use rustc_errors::{Diag, IntoDiagArg}; -use rustc_hir::{self as hir, PatKind}; use rustc_hir::def::{CtorOf, DefKind, Namespace, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{Body, Closure, Expr, ExprKind, FnRetTy, HirId, LetStmt, LocalSource}; +use rustc_hir::{ + self as hir, Body, Closure, Expr, ExprKind, FnRetTy, HirId, LetStmt, LocalSource, PatKind, +}; use rustc_middle::bug; use rustc_middle::hir::nested_filter; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; From ed85f9846da9abfad09bb5732bc18d6c39fbc9f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 16 Sep 2025 11:21:29 -0700 Subject: [PATCH 561/682] remove redundant test --- tests/ui/closures/varargs-in-closure-isnt-supported.rs | 10 ---------- .../closures/varargs-in-closure-isnt-supported.stderr | 10 ---------- 2 files changed, 20 deletions(-) delete mode 100644 tests/ui/closures/varargs-in-closure-isnt-supported.rs delete mode 100644 tests/ui/closures/varargs-in-closure-isnt-supported.stderr diff --git a/tests/ui/closures/varargs-in-closure-isnt-supported.rs b/tests/ui/closures/varargs-in-closure-isnt-supported.rs deleted file mode 100644 index 4de78bef14d3..000000000000 --- a/tests/ui/closures/varargs-in-closure-isnt-supported.rs +++ /dev/null @@ -1,10 +0,0 @@ -// var-args are not supported in closures, ensure we don't misdirect people (#146489) -#![feature(c_variadic)] - -unsafe extern "C" fn thats_not_a_pattern(mut ap: ...) -> u32 { - let mut lol = |...| (); //~ ERROR: unexpected `...` - unsafe { ap.arg::() } //~^ NOTE: C-variadic type `...` is not allowed here - //~| NOTE: not a valid pattern -} - -fn main() {} diff --git a/tests/ui/closures/varargs-in-closure-isnt-supported.stderr b/tests/ui/closures/varargs-in-closure-isnt-supported.stderr deleted file mode 100644 index a645741a5277..000000000000 --- a/tests/ui/closures/varargs-in-closure-isnt-supported.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error: unexpected `...` - --> $DIR/varargs-in-closure-isnt-supported.rs:5:20 - | -LL | let mut lol = |...| (); - | ^^^ not a valid pattern - | - = note: C-variadic type `...` is not allowed here - -error: aborting due to 1 previous error - From f71f75669563cc9fb74c629937046c1f13d3698e Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Wed, 17 Sep 2025 03:03:00 +0900 Subject: [PATCH 562/682] Fix "sync-from-ra" for `rust-lang/rust` --- src/tools/rust-analyzer/crates/hir-ty/src/lib.rs | 3 +++ src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 02fdbf0ebf0d..37d5347fe7ea 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -39,6 +39,9 @@ extern crate rustc_next_trait_solver; #[cfg(not(feature = "in-rust-tree"))] extern crate ra_ap_rustc_next_trait_solver as rustc_next_trait_solver; +#[cfg(feature = "in-rust-tree")] +extern crate rustc_data_structures as ena; + mod builder; mod chalk_db; mod chalk_ext; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs index 0225deebe4f3..99a501b05341 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs @@ -40,5 +40,8 @@ pub type AliasTy<'db> = rustc_type_ir::AliasTy>; pub type PolyFnSig<'db> = Binder<'db, rustc_type_ir::FnSig>>; pub type TypingMode<'db> = rustc_type_ir::TypingMode>; +#[cfg(feature = "in-rust-tree")] +use rustc_data_structure::sorted_map::index_map as indexmap; + pub type FxIndexMap = indexmap::IndexMap>; From 8306a2f02e6c56447ff05b19b4b73c5c861e34fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 16 Sep 2025 11:24:51 -0700 Subject: [PATCH 563/682] Reword note --- compiler/rustc_parse/messages.ftl | 2 +- tests/ui/c-variadic/no-closure.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 6d9521c7d2be..3a4c9348b327 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -189,7 +189,7 @@ parse_dotdotdot = unexpected token: `...` parse_dotdotdot_rest_pattern = unexpected `...` .label = not a valid pattern .suggestion = for a rest pattern, use `..` instead of `...` - .note = C-variadic type `...` is not allowed here + .note = only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list parse_double_colon_in_bound = expected `:` followed by trait or lifetime .suggestion = use single colon diff --git a/tests/ui/c-variadic/no-closure.stderr b/tests/ui/c-variadic/no-closure.stderr index ad635a29ab48..4b553c215979 100644 --- a/tests/ui/c-variadic/no-closure.stderr +++ b/tests/ui/c-variadic/no-closure.stderr @@ -10,7 +10,7 @@ error: unexpected `...` LL | let f = |...| {}; | ^^^ not a valid pattern | - = note: C-variadic type `...` is not allowed here + = note: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error[E0743]: C-variadic type `...` may not be nested inside another type --> $DIR/no-closure.rs:13:17 From e9270e3cba3da56d4d83ed74f648e53b041cb263 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 16 Sep 2025 11:38:08 -0700 Subject: [PATCH 564/682] Detect top-level `...` in argument type When writing something like the expression `|_: ...| {}`, we now detect the `...` during parsing explicitly instead of relying on the detection in `parse_ty_common` so that we don't talk about "nested `...` are not supported". ``` error: unexpected `...` --> $DIR/no-closure.rs:6:35 | LL | const F: extern "C" fn(...) = |_: ...| {}; | ^^^ | = note: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list ``` --- compiler/rustc_parse/messages.ftl | 3 +++ compiler/rustc_parse/src/errors.rs | 8 ++++++++ compiler/rustc_parse/src/parser/ty.rs | 13 +++++++++++-- tests/ui/c-variadic/no-closure.rs | 8 ++++++-- tests/ui/c-variadic/no-closure.stderr | 13 ++++++++----- 5 files changed, 36 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 3a4c9348b327..f83cf645f829 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -191,6 +191,9 @@ parse_dotdotdot_rest_pattern = unexpected `...` .suggestion = for a rest pattern, use `..` instead of `...` .note = only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list +parse_dotdotdot_rest_type = unexpected `...` + .note = only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + parse_double_colon_in_bound = expected `:` followed by trait or lifetime .suggestion = use single colon diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 2b107fe35d59..1abeee6fe433 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -3032,6 +3032,14 @@ pub(crate) struct NestedCVariadicType { pub span: Span, } +#[derive(Diagnostic)] +#[diag(parse_dotdotdot_rest_type)] +#[note] +pub(crate) struct InvalidCVariadicType { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(parse_invalid_dyn_keyword)] #[help] diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 23aaafac934e..65347496599d 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -15,8 +15,8 @@ use super::{Parser, PathStyle, SeqSep, TokenType, Trailing}; use crate::errors::{ self, AttributeOnEmptyType, AttributeOnType, DynAfterMut, ExpectedFnPathFoundFnKeyword, ExpectedMutOrConstInRawPointerType, FnPtrWithGenerics, FnPtrWithGenericsSugg, - HelpUseLatestEdition, InvalidDynKeyword, LifetimeAfterMut, NeedPlusAfterTraitObjectLifetime, - NestedCVariadicType, ReturnTypesUseThinArrow, + HelpUseLatestEdition, InvalidCVariadicType, InvalidDynKeyword, LifetimeAfterMut, + NeedPlusAfterTraitObjectLifetime, NestedCVariadicType, ReturnTypesUseThinArrow, }; use crate::parser::item::FrontMatterParsingMode; use crate::parser::{FnContext, FnParseMode}; @@ -106,6 +106,15 @@ fn can_begin_dyn_bound_in_edition_2015(t: &Token) -> bool { impl<'a> Parser<'a> { /// Parses a type. pub fn parse_ty(&mut self) -> PResult<'a, Box> { + if self.token == token::DotDotDot { + // We special case this so that we don't talk about "nested C-variadics" in types. + // We still pass in `AllowCVariadic::No` so that `parse_ty_common` can complain about + // things like `Vec<...>`. + let span = self.token.span; + self.bump(); + let kind = TyKind::Err(self.dcx().emit_err(InvalidCVariadicType { span })); + return Ok(self.mk_ty(span, kind)); + } // Make sure deeply nested types don't overflow the stack. ensure_sufficient_stack(|| { self.parse_ty_common( diff --git a/tests/ui/c-variadic/no-closure.rs b/tests/ui/c-variadic/no-closure.rs index a5b791fbca80..830ed962a8c4 100644 --- a/tests/ui/c-variadic/no-closure.rs +++ b/tests/ui/c-variadic/no-closure.rs @@ -4,13 +4,17 @@ // Check that `...` in closures is rejected. const F: extern "C" fn(...) = |_: ...| {}; -//~^ ERROR C-variadic type `...` may not be nested inside another type +//~^ ERROR: unexpected `...` +//~| NOTE: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list fn foo() { let f = |...| {}; //~^ ERROR: unexpected `...` + //~| NOTE: not a valid pattern + //~| NOTE: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list let f = |_: ...| {}; - //~^ ERROR C-variadic type `...` may not be nested inside another type + //~^ ERROR: unexpected `...` + //~| NOTE: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list f(1i64) } diff --git a/tests/ui/c-variadic/no-closure.stderr b/tests/ui/c-variadic/no-closure.stderr index 4b553c215979..0946c4632e6e 100644 --- a/tests/ui/c-variadic/no-closure.stderr +++ b/tests/ui/c-variadic/no-closure.stderr @@ -1,23 +1,26 @@ -error[E0743]: C-variadic type `...` may not be nested inside another type +error: unexpected `...` --> $DIR/no-closure.rs:6:35 | LL | const F: extern "C" fn(...) = |_: ...| {}; | ^^^ + | + = note: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error: unexpected `...` - --> $DIR/no-closure.rs:10:14 + --> $DIR/no-closure.rs:11:14 | LL | let f = |...| {}; | ^^^ not a valid pattern | = note: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list -error[E0743]: C-variadic type `...` may not be nested inside another type - --> $DIR/no-closure.rs:13:17 +error: unexpected `...` + --> $DIR/no-closure.rs:16:17 | LL | let f = |_: ...| {}; | ^^^ + | + = note: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0743`. From 580b4891aa23c0625539bf5ee55270f27af09072 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Thu, 7 Aug 2025 14:29:00 -0700 Subject: [PATCH 565/682] Update the minimum external LLVM to 20 --- compiler/rustc_codegen_llvm/src/builder.rs | 9 +- compiler/rustc_codegen_llvm/src/context.rs | 29 ---- compiler/rustc_codegen_llvm/src/llvm_util.rs | 57 +------ compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 30 +--- .../rustc_codegen_ssa/src/traits/builder.rs | 38 ++++- .../rustc_llvm/llvm-wrapper/PassWrapper.cpp | 140 ++++-------------- src/bootstrap/src/core/build_steps/llvm.rs | 4 +- src/ci/docker/README.md | 8 +- .../Dockerfile | 8 +- .../host-x86_64/x86_64-gnu-llvm-19/Dockerfile | 66 --------- src/ci/github-actions/jobs.yml | 35 +---- .../riscv-soft-abi-with-float-features.rs | 10 +- tests/assembly-llvm/x86_64-bigint-helpers.rs | 1 - tests/assembly-llvm/x86_64-cmp.rs | 78 +++------- .../comparison-operators-2-struct.rs | 1 - .../comparison-operators-2-tuple.rs | 1 - tests/codegen-llvm/enum/enum-aggregate.rs | 1 - .../codegen-llvm/enum/enum-discriminant-eq.rs | 1 - tests/codegen-llvm/integer-cmp.rs | 36 +---- .../intrinsics/three_way_compare.rs | 1 - .../issues/and-masked-comparison-131162.rs | 1 - tests/codegen-llvm/issues/issue-101082.rs | 1 - tests/codegen-llvm/issues/issue-129795.rs | 1 - .../iter-max-no-unwrap-failed-129583.rs | 1 - .../issues/looping-over-ne-bytes-133528.rs | 1 - tests/codegen-llvm/option-niche-eq.rs | 1 - .../slice-last-elements-optimization.rs | 1 - tests/codegen-llvm/swap-small-types.rs | 1 - tests/codegen-llvm/try_question_mark_nop.rs | 31 ++-- tests/codegen-llvm/union-aggregate.rs | 1 - tests/ui/abi/sparcv8plus-llvm19.rs | 42 ------ tests/ui/abi/sparcv8plus-llvm19.sparc.stderr | 8 - .../sparcv8plus-llvm19.sparc_cpu_v9.stderr | 8 - ...-llvm19.sparc_cpu_v9_feature_v8plus.stderr | 8 - ...cv8plus-llvm19.sparc_feature_v8plus.stderr | 8 - .../abi/sparcv8plus-llvm19.sparcv8plus.stderr | 8 - tests/ui/abi/sparcv8plus.rs | 1 - tests/ui/abi/sparcv8plus.sparc.stderr | 2 +- tests/ui/abi/sparcv8plus.sparc_cpu_v9.stderr | 2 +- ...cv8plus.sparc_cpu_v9_feature_v8plus.stderr | 2 +- .../sparcv8plus.sparc_feature_v8plus.stderr | 2 +- tests/ui/abi/sparcv8plus.sparcv8plus.stderr | 2 +- .../bad-reg.loongarch32_ilp32d.stderr | 12 +- .../bad-reg.loongarch32_ilp32s.stderr | 20 +-- .../bad-reg.loongarch64_lp64d.stderr | 12 +- .../bad-reg.loongarch64_lp64s.stderr | 20 +-- tests/ui/asm/loongarch/bad-reg.rs | 1 - 47 files changed, 157 insertions(+), 595 deletions(-) rename src/ci/docker/host-aarch64/{aarch64-gnu-llvm-19 => aarch64-gnu-llvm-20}/Dockerfile (92%) delete mode 100644 src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile delete mode 100644 tests/ui/abi/sparcv8plus-llvm19.rs delete mode 100644 tests/ui/abi/sparcv8plus-llvm19.sparc.stderr delete mode 100644 tests/ui/abi/sparcv8plus-llvm19.sparc_cpu_v9.stderr delete mode 100644 tests/ui/abi/sparcv8plus-llvm19.sparc_cpu_v9_feature_v8plus.stderr delete mode 100644 tests/ui/abi/sparcv8plus-llvm19.sparc_feature_v8plus.stderr delete mode 100644 tests/ui/abi/sparcv8plus-llvm19.sparcv8plus.stderr diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 7d0691366e60..0f17cc9063a8 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -1091,16 +1091,11 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { ty: Ty<'tcx>, lhs: Self::Value, rhs: Self::Value, - ) -> Option { - // FIXME: See comment on the definition of `three_way_compare`. - if crate::llvm_util::get_version() < (20, 0, 0) { - return None; - } - + ) -> Self::Value { let size = ty.primitive_size(self.tcx); let name = if ty.is_signed() { "llvm.scmp" } else { "llvm.ucmp" }; - Some(self.call_intrinsic(name, &[self.type_i8(), self.type_ix(size.bits())], &[lhs, rhs])) + self.call_intrinsic(name, &[self.type_i8(), self.type_ix(size.bits())], &[lhs, rhs]) } /* Miscellaneous instructions */ diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index a69fa54a54a4..5f4385c9c6a2 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -172,35 +172,6 @@ pub(crate) unsafe fn create_module<'ll>( let mut target_data_layout = sess.target.data_layout.to_string(); let llvm_version = llvm_util::get_version(); - if llvm_version < (20, 0, 0) { - if sess.target.arch == "aarch64" || sess.target.arch.starts_with("arm64") { - // LLVM 20 defines three additional address spaces for alternate - // pointer kinds used in Windows. - // See https://github.com/llvm/llvm-project/pull/111879 - target_data_layout = - target_data_layout.replace("-p270:32:32-p271:32:32-p272:64:64", ""); - } - if sess.target.arch.starts_with("sparc") { - // LLVM 20 updates the sparc layout to correctly align 128 bit integers to 128 bit. - // See https://github.com/llvm/llvm-project/pull/106951 - target_data_layout = target_data_layout.replace("-i128:128", ""); - } - if sess.target.arch.starts_with("mips64") { - // LLVM 20 updates the mips64 layout to correctly align 128 bit integers to 128 bit. - // See https://github.com/llvm/llvm-project/pull/112084 - target_data_layout = target_data_layout.replace("-i128:128", ""); - } - if sess.target.arch.starts_with("powerpc64") { - // LLVM 20 updates the powerpc64 layout to correctly align 128 bit integers to 128 bit. - // See https://github.com/llvm/llvm-project/pull/118004 - target_data_layout = target_data_layout.replace("-i128:128", ""); - } - if sess.target.arch.starts_with("wasm32") || sess.target.arch.starts_with("wasm64") { - // LLVM 20 updates the wasm(32|64) layout to correctly align 128 bit integers to 128 bit. - // See https://github.com/llvm/llvm-project/pull/119204 - target_data_layout = target_data_layout.replace("-i128:128", ""); - } - } if llvm_version < (21, 0, 0) { if sess.target.arch == "nvptx64" { // LLVM 21 updated the default layout on nvptx: https://github.com/llvm/llvm-project/pull/124961 diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index d927ffd78c29..8461c8b03d5a 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -246,9 +246,6 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option Some(LLVMFeature::new("perfmon")), ("aarch64", "paca") => Some(LLVMFeature::new("pauth")), ("aarch64", "pacg") => Some(LLVMFeature::new("pauth")), - // Before LLVM 20 those two features were packaged together as b16b16 - ("aarch64", "sve-b16b16") if get_version().0 < 20 => Some(LLVMFeature::new("b16b16")), - ("aarch64", "sme-b16b16") if get_version().0 < 20 => Some(LLVMFeature::new("b16b16")), ("aarch64", "flagm2") => Some(LLVMFeature::new("altnzcv")), // Rust ties fp and neon together. ("aarch64", "neon") => Some(LLVMFeature::with_dependencies( @@ -262,57 +259,17 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option None, // only existed in 18 ("arm", "fp16") => Some(LLVMFeature::new("fullfp16")), - // NVPTX targets added in LLVM 20 - ("nvptx64", "sm_100") if get_version().0 < 20 => None, - ("nvptx64", "sm_100a") if get_version().0 < 20 => None, - ("nvptx64", "sm_101") if get_version().0 < 20 => None, - ("nvptx64", "sm_101a") if get_version().0 < 20 => None, - ("nvptx64", "sm_120") if get_version().0 < 20 => None, - ("nvptx64", "sm_120a") if get_version().0 < 20 => None, - ("nvptx64", "ptx86") if get_version().0 < 20 => None, - ("nvptx64", "ptx87") if get_version().0 < 20 => None, // Filter out features that are not supported by the current LLVM version - ("loongarch64", "div32" | "lam-bh" | "lamcas" | "ld-seq-sa" | "scq") - if get_version().0 < 20 => - { - None - } ("loongarch32" | "loongarch64", "32s") if get_version().0 < 21 => None, - // Filter out features that are not supported by the current LLVM version - ("riscv32" | "riscv64", "zacas" | "rva23u64" | "supm") if get_version().0 < 20 => None, - ( - "s390x", - "message-security-assist-extension12" - | "concurrent-functions" - | "miscellaneous-extensions-4" - | "vector-enhancements-3" - | "vector-packed-decimal-enhancement-3", - ) if get_version().0 < 20 => None, // Enable the evex512 target feature if an avx512 target feature is enabled. ("x86", s) if s.starts_with("avx512") => Some(LLVMFeature::with_dependencies( s, smallvec![TargetFeatureFoldStrength::EnableOnly("evex512")], )), - // Support for `wide-arithmetic` will first land in LLVM 20 as part of - // llvm/llvm-project#111598 - ("wasm32" | "wasm64", "wide-arithmetic") if get_version() < (20, 0, 0) => None, ("sparc", "leoncasa") => Some(LLVMFeature::new("hasleoncasa")), - // In LLVM 19, there is no `v8plus` feature and `v9` means "SPARC-V9 instruction available and SPARC-V8+ ABI used". - // https://github.com/llvm/llvm-project/blob/llvmorg-19.1.0/llvm/lib/Target/Sparc/MCTargetDesc/SparcELFObjectWriter.cpp#L27-L28 - // Before LLVM 19, there was no `v8plus` feature and `v9` means "SPARC-V9 instruction available". - // https://github.com/llvm/llvm-project/blob/llvmorg-18.1.0/llvm/lib/Target/Sparc/MCTargetDesc/SparcELFObjectWriter.cpp#L26 - ("sparc", "v8plus") if get_version().0 == 19 => Some(LLVMFeature::new("v9")), ("powerpc", "power8-crypto") => Some(LLVMFeature::new("crypto")), - // These new `amx` variants and `movrs` were introduced in LLVM20 - ("x86", "amx-avx512" | "amx-fp8" | "amx-movrs" | "amx-tf32" | "amx-transpose") - if get_version().0 < 20 => - { - None - } - ("x86", "movrs") if get_version().0 < 20 => None, ("x86", "avx10.1") => Some(LLVMFeature::new("avx10.1-512")), - ("x86", "avx10.2") if get_version().0 < 20 => None, - ("x86", "avx10.2") if get_version().0 >= 20 => Some(LLVMFeature::new("avx10.2-512")), + ("x86", "avx10.2") => Some(LLVMFeature::new("avx10.2-512")), ("x86", "apxf") => Some(LLVMFeature::with_dependencies( "egpr", smallvec![ @@ -716,17 +673,7 @@ pub(crate) fn global_llvm_features( }; // Features implied by an implicit or explicit `--target`. - features.extend( - sess.target - .features - .split(',') - .filter(|v| !v.is_empty()) - // Drop +v8plus feature introduced in LLVM 20. - // (Hard-coded target features do not go through `to_llvm_feature` since they already - // are LLVM feature names, hence we need a special case here.) - .filter(|v| *v != "+v8plus" || get_version() >= (20, 0, 0)) - .map(String::from), - ); + features.extend(sess.target.features.split(',').filter(|v| !v.is_empty()).map(String::from)); if wants_wasm_eh(sess) && sess.panic_strategy() == PanicStrategy::Unwind { features.push("+exception-handling".into()); diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index f8755874014d..4c9bb7cf8a8f 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -901,36 +901,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } mir::BinOp::Cmp => { - use std::cmp::Ordering; assert!(!is_float); - if let Some(value) = bx.three_way_compare(lhs_ty, lhs, rhs) { - return value; - } - let pred = |op| base::bin_op_to_icmp_predicate(op, is_signed); - if bx.cx().tcx().sess.opts.optimize == OptLevel::No { - // FIXME: This actually generates tighter assembly, and is a classic trick - // - // However, as of 2023-11 it optimizes worse in things like derived - // `PartialOrd`, so only use it in debug for now. Once LLVM can handle it - // better (see ), it'll - // be worth trying it in optimized builds as well. - let is_gt = bx.icmp(pred(mir::BinOp::Gt), lhs, rhs); - let gtext = bx.zext(is_gt, bx.type_i8()); - let is_lt = bx.icmp(pred(mir::BinOp::Lt), lhs, rhs); - let ltext = bx.zext(is_lt, bx.type_i8()); - bx.unchecked_ssub(gtext, ltext) - } else { - // These operations are those expected by `tests/codegen-llvm/integer-cmp.rs`, - // from . - let is_lt = bx.icmp(pred(mir::BinOp::Lt), lhs, rhs); - let is_ne = bx.icmp(pred(mir::BinOp::Ne), lhs, rhs); - let ge = bx.select( - is_ne, - bx.cx().const_i8(Ordering::Greater as i8), - bx.cx().const_i8(Ordering::Equal as i8), - ); - bx.select(is_lt, bx.cx().const_i8(Ordering::Less as i8), ge) - } + bx.three_way_compare(lhs_ty, lhs, rhs) } mir::BinOp::AddWithOverflow | mir::BinOp::SubWithOverflow diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index f417d1a7bf72..422fa715de10 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -3,6 +3,7 @@ use std::ops::Deref; use rustc_abi::{Align, Scalar, Size, WrappingRange}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; +use rustc_middle::mir; use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout}; use rustc_middle::ty::{AtomicOrdering, Instance, Ty}; use rustc_session::config::OptLevel; @@ -405,15 +406,38 @@ pub trait BuilderMethods<'a, 'tcx>: fn fcmp(&mut self, op: RealPredicate, lhs: Self::Value, rhs: Self::Value) -> Self::Value; /// Returns `-1` if `lhs < rhs`, `0` if `lhs == rhs`, and `1` if `lhs > rhs`. - // FIXME: Move the default implementation from `codegen_scalar_binop` into this method and - // remove the `Option` return once LLVM 20 is the minimum version. fn three_way_compare( &mut self, - _ty: Ty<'tcx>, - _lhs: Self::Value, - _rhs: Self::Value, - ) -> Option { - None + ty: Ty<'tcx>, + lhs: Self::Value, + rhs: Self::Value, + ) -> Self::Value { + use std::cmp::Ordering; + let pred = |op| crate::base::bin_op_to_icmp_predicate(op, ty.is_signed()); + if self.cx().sess().opts.optimize == OptLevel::No { + // FIXME: This actually generates tighter assembly, and is a classic trick + // + // However, as of 2023-11 it optimizes worse in things like derived + // `PartialOrd`, so only use it in debug for now. Once LLVM can handle it + // better (see ), it'll + // be worth trying it in optimized builds as well. + let is_gt = self.icmp(pred(mir::BinOp::Gt), lhs, rhs); + let gtext = self.zext(is_gt, self.type_i8()); + let is_lt = self.icmp(pred(mir::BinOp::Lt), lhs, rhs); + let ltext = self.zext(is_lt, self.type_i8()); + self.unchecked_ssub(gtext, ltext) + } else { + // These operations are those expected by `tests/codegen-llvm/integer-cmp.rs`, + // from . + let is_lt = self.icmp(pred(mir::BinOp::Lt), lhs, rhs); + let is_ne = self.icmp(pred(mir::BinOp::Ne), lhs, rhs); + let ge = self.select( + is_ne, + self.cx().const_i8(Ordering::Greater as i8), + self.cx().const_i8(Ordering::Equal as i8), + ); + self.select(is_lt, self.cx().const_i8(Ordering::Less as i8), ge) + } } fn memcpy( diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 91d11ba317a6..ab5d5c03e817 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -344,7 +344,6 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( Options.EmitStackSizeSection = EmitStackSizeSection; if (ArgsCstrBuff != nullptr) { -#if LLVM_VERSION_GE(20, 0) size_t buffer_offset = 0; assert(ArgsCstrBuff[ArgsCstrBuffLen - 1] == '\0'); auto Arg0 = std::string(ArgsCstrBuff); @@ -362,33 +361,6 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( OS.flush(); Options.MCOptions.Argv0 = Arg0; Options.MCOptions.CommandlineArgs = CommandlineArgs; -#else - size_t buffer_offset = 0; - assert(ArgsCstrBuff[ArgsCstrBuffLen - 1] == '\0'); - - const size_t arg0_len = std::strlen(ArgsCstrBuff); - char *arg0 = new char[arg0_len + 1]; - memcpy(arg0, ArgsCstrBuff, arg0_len); - arg0[arg0_len] = '\0'; - buffer_offset += arg0_len + 1; - - const size_t num_cmd_arg_strings = std::count( - &ArgsCstrBuff[buffer_offset], &ArgsCstrBuff[ArgsCstrBuffLen], '\0'); - - std::string *cmd_arg_strings = new std::string[num_cmd_arg_strings]; - for (size_t i = 0; i < num_cmd_arg_strings; ++i) { - assert(buffer_offset < ArgsCstrBuffLen); - const size_t len = std::strlen(ArgsCstrBuff + buffer_offset); - cmd_arg_strings[i] = std::string(&ArgsCstrBuff[buffer_offset], len); - buffer_offset += len + 1; - } - - assert(buffer_offset == ArgsCstrBuffLen); - - Options.MCOptions.Argv0 = arg0; - Options.MCOptions.CommandLineArgs = - llvm::ArrayRef(cmd_arg_strings, num_cmd_arg_strings); -#endif } #if LLVM_VERSION_GE(21, 0) @@ -402,12 +374,6 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( } extern "C" void LLVMRustDisposeTargetMachine(LLVMTargetMachineRef TM) { -#if LLVM_VERSION_LT(20, 0) - MCTargetOptions &MCOptions = unwrap(TM)->Options.MCOptions; - delete[] MCOptions.Argv0; - delete[] MCOptions.CommandLineArgs.data(); -#endif - delete unwrap(TM); } @@ -688,14 +654,9 @@ extern "C" LLVMRustResult LLVMRustOptimize( // the PassBuilder does not create a pipeline. std::vector> PipelineStartEPCallbacks; -#if LLVM_VERSION_GE(20, 0) std::vector> OptimizerLastEPCallbacks; -#else - std::vector> - OptimizerLastEPCallbacks; -#endif if (!IsLinkerPluginLTO && SanitizerOptions && SanitizerOptions->SanitizeCFI && !NoPrepopulatePasses) { @@ -747,12 +708,8 @@ extern "C" LLVMRustResult LLVMRustOptimize( SanitizerOptions->SanitizeDataFlowABIList + SanitizerOptions->SanitizeDataFlowABIListLen); OptimizerLastEPCallbacks.push_back( -#if LLVM_VERSION_GE(20, 0) [ABIListFiles](ModulePassManager &MPM, OptimizationLevel Level, ThinOrFullLTOPhase phase) { -#else - [ABIListFiles](ModulePassManager &MPM, OptimizationLevel Level) { -#endif MPM.addPass(DataFlowSanitizerPass(ABIListFiles)); }); } @@ -763,66 +720,48 @@ extern "C" LLVMRustResult LLVMRustOptimize( SanitizerOptions->SanitizeMemoryRecover, /*CompileKernel=*/false, /*EagerChecks=*/true); - OptimizerLastEPCallbacks.push_back( -#if LLVM_VERSION_GE(20, 0) - [Options](ModulePassManager &MPM, OptimizationLevel Level, - ThinOrFullLTOPhase phase) { -#else - [Options](ModulePassManager &MPM, OptimizationLevel Level) { -#endif - MPM.addPass(MemorySanitizerPass(Options)); - }); + OptimizerLastEPCallbacks.push_back([Options](ModulePassManager &MPM, + OptimizationLevel Level, + ThinOrFullLTOPhase phase) { + MPM.addPass(MemorySanitizerPass(Options)); + }); } if (SanitizerOptions->SanitizeThread) { - OptimizerLastEPCallbacks.push_back( -#if LLVM_VERSION_GE(20, 0) - [](ModulePassManager &MPM, OptimizationLevel Level, - ThinOrFullLTOPhase phase) { -#else - [](ModulePassManager &MPM, OptimizationLevel Level) { -#endif - MPM.addPass(ModuleThreadSanitizerPass()); - MPM.addPass( - createModuleToFunctionPassAdaptor(ThreadSanitizerPass())); - }); + OptimizerLastEPCallbacks.push_back([](ModulePassManager &MPM, + OptimizationLevel Level, + ThinOrFullLTOPhase phase) { + MPM.addPass(ModuleThreadSanitizerPass()); + MPM.addPass(createModuleToFunctionPassAdaptor(ThreadSanitizerPass())); + }); } if (SanitizerOptions->SanitizeAddress || SanitizerOptions->SanitizeKernelAddress) { - OptimizerLastEPCallbacks.push_back( -#if LLVM_VERSION_GE(20, 0) - [SanitizerOptions, TM](ModulePassManager &MPM, - OptimizationLevel Level, - ThinOrFullLTOPhase phase) { -#else - [SanitizerOptions, TM](ModulePassManager &MPM, - OptimizationLevel Level) { -#endif - auto CompileKernel = SanitizerOptions->SanitizeKernelAddress; - AddressSanitizerOptions opts = AddressSanitizerOptions{ - CompileKernel, - SanitizerOptions->SanitizeAddressRecover || - SanitizerOptions->SanitizeKernelAddressRecover, - /*UseAfterScope=*/true, - AsanDetectStackUseAfterReturnMode::Runtime, - }; - MPM.addPass(AddressSanitizerPass( - opts, - /*UseGlobalGC*/ true, - // UseOdrIndicator should be false on windows machines - // https://reviews.llvm.org/D137227 - !TM->getTargetTriple().isOSWindows())); - }); + OptimizerLastEPCallbacks.push_back([SanitizerOptions, + TM](ModulePassManager &MPM, + OptimizationLevel Level, + ThinOrFullLTOPhase phase) { + auto CompileKernel = SanitizerOptions->SanitizeKernelAddress; + AddressSanitizerOptions opts = AddressSanitizerOptions{ + CompileKernel, + SanitizerOptions->SanitizeAddressRecover || + SanitizerOptions->SanitizeKernelAddressRecover, + /*UseAfterScope=*/true, + AsanDetectStackUseAfterReturnMode::Runtime, + }; + MPM.addPass( + AddressSanitizerPass(opts, + /*UseGlobalGC*/ true, + // UseOdrIndicator should be false on windows + // machines https://reviews.llvm.org/D137227 + !TM->getTargetTriple().isOSWindows())); + }); } if (SanitizerOptions->SanitizeHWAddress) { OptimizerLastEPCallbacks.push_back( -#if LLVM_VERSION_GE(20, 0) [SanitizerOptions](ModulePassManager &MPM, OptimizationLevel Level, ThinOrFullLTOPhase phase) { -#else - [SanitizerOptions](ModulePassManager &MPM, OptimizationLevel Level) { -#endif HWAddressSanitizerOptions opts( /*CompileKernel=*/false, SanitizerOptions->SanitizeHWAddressRecover, @@ -904,11 +843,7 @@ extern "C" LLVMRustResult LLVMRustOptimize( for (const auto &C : PipelineStartEPCallbacks) C(MPM, OptLevel); for (const auto &C : OptimizerLastEPCallbacks) -#if LLVM_VERSION_GE(20, 0) C(MPM, OptLevel, ThinOrFullLTOPhase::None); -#else - C(MPM, OptLevel); -#endif } if (ExtraPassesLen) { @@ -1185,11 +1120,7 @@ struct LLVMRustThinLTOData { // Not 100% sure what these are, but they impact what's internalized and // what's inlined across modules, I believe. -#if LLVM_VERSION_GE(20, 0) FunctionImporter::ImportListsTy ImportLists; -#else - DenseMap ImportLists; -#endif DenseMap ExportLists; DenseMap ModuleToDefinedGVSummaries; StringMap> ResolvedODR; @@ -1531,13 +1462,8 @@ extern "C" void LLVMRustComputeLTOCacheKey(RustStringRef KeyOut, const auto &ExportList = Data->ExportLists.lookup(ModId); const auto &ResolvedODR = Data->ResolvedODR.lookup(ModId); const auto &DefinedGlobals = Data->ModuleToDefinedGVSummaries.lookup(ModId); -#if LLVM_VERSION_GE(20, 0) DenseSet CfiFunctionDefs; DenseSet CfiFunctionDecls; -#else - std::set CfiFunctionDefs; - std::set CfiFunctionDecls; -#endif // Based on the 'InProcessThinBackend' constructor in LLVM #if LLVM_VERSION_GE(21, 0) @@ -1556,15 +1482,9 @@ extern "C" void LLVMRustComputeLTOCacheKey(RustStringRef KeyOut, GlobalValue::getGUID(GlobalValue::dropLLVMManglingEscape(Name))); #endif -#if LLVM_VERSION_GE(20, 0) Key = llvm::computeLTOCacheKey(conf, Data->Index, ModId, ImportList, ExportList, ResolvedODR, DefinedGlobals, CfiFunctionDefs, CfiFunctionDecls); -#else - llvm::computeLTOCacheKey(Key, conf, Data->Index, ModId, ImportList, - ExportList, ResolvedODR, DefinedGlobals, - CfiFunctionDefs, CfiFunctionDecls); -#endif auto OS = RawRustStringOstream(KeyOut); OS << Key.str(); diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 83ed7430c397..d43d261ad6c3 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -619,11 +619,11 @@ fn check_llvm_version(builder: &Builder<'_>, llvm_config: &Path) { let version = get_llvm_version(builder, llvm_config); let mut parts = version.split('.').take(2).filter_map(|s| s.parse::().ok()); if let (Some(major), Some(_minor)) = (parts.next(), parts.next()) - && major >= 19 + && major >= 20 { return; } - panic!("\n\nbad LLVM version: {version}, need >=19\n\n") + panic!("\n\nbad LLVM version: {version}, need >=20\n\n") } fn configure_cmake( diff --git a/src/ci/docker/README.md b/src/ci/docker/README.md index 488a6a2bce12..4ee02e9bf005 100644 --- a/src/ci/docker/README.md +++ b/src/ci/docker/README.md @@ -14,9 +14,9 @@ To run a specific CI job locally, you can use the `citool` Rust crate: cargo run --manifest-path src/ci/citool/Cargo.toml run-local ``` -For example, to run the `x86_64-gnu-llvm-19-1` job: +For example, to run the `x86_64-gnu-llvm-20-1` job: ``` -cargo run --manifest-path src/ci/citool/Cargo.toml run-local x86_64-gnu-llvm-19-1 +cargo run --manifest-path src/ci/citool/Cargo.toml run-local x86_64-gnu-llvm-20-1 ``` The job will output artifacts in an `obj/` dir at the root of a repository. Note @@ -27,10 +27,10 @@ Docker image executed in the given CI job. while locally, to the `obj/` directory. This is primarily to prevent strange linker errors when using multiple Docker images. -For some Linux workflows (for example `x86_64-gnu-llvm-19-N`), the process is more involved. You will need to see which script is executed for the given workflow inside the [`jobs.yml`](../github-actions/jobs.yml) file and pass it through the `DOCKER_SCRIPT` environment variable. For example, to reproduce the `x86_64-gnu-llvm-19-3` workflow, you can run the following script: +For some Linux workflows (for example `x86_64-gnu-llvm-20-N`), the process is more involved. You will need to see which script is executed for the given workflow inside the [`jobs.yml`](../github-actions/jobs.yml) file and pass it through the `DOCKER_SCRIPT` environment variable. For example, to reproduce the `x86_64-gnu-llvm-20-3` workflow, you can run the following script: ``` -DOCKER_SCRIPT=x86_64-gnu-llvm3.sh ./src/ci/docker/run.sh x86_64-gnu-llvm-19 +DOCKER_SCRIPT=x86_64-gnu-llvm3.sh ./src/ci/docker/run.sh x86_64-gnu-llvm-20 ``` ## Local Development diff --git a/src/ci/docker/host-aarch64/aarch64-gnu-llvm-19/Dockerfile b/src/ci/docker/host-aarch64/aarch64-gnu-llvm-20/Dockerfile similarity index 92% rename from src/ci/docker/host-aarch64/aarch64-gnu-llvm-19/Dockerfile rename to src/ci/docker/host-aarch64/aarch64-gnu-llvm-20/Dockerfile index e73fbe506f78..adbb1f03378a 100644 --- a/src/ci/docker/host-aarch64/aarch64-gnu-llvm-19/Dockerfile +++ b/src/ci/docker/host-aarch64/aarch64-gnu-llvm-20/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:24.10 +FROM ubuntu:25.04 ARG DEBIAN_FRONTEND=noninteractive @@ -15,8 +15,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ cmake \ sudo \ gdb \ - llvm-19-tools \ - llvm-19-dev \ + llvm-20-tools \ + llvm-20-dev \ libedit-dev \ libssl-dev \ pkg-config \ @@ -43,7 +43,7 @@ ENV EXTERNAL_LLVM 1 # Using llvm-link-shared due to libffi issues -- see #34486 ENV RUST_CONFIGURE_ARGS \ --build=aarch64-unknown-linux-gnu \ - --llvm-root=/usr/lib/llvm-19 \ + --llvm-root=/usr/lib/llvm-20 \ --enable-llvm-link-shared \ --set rust.randomize-layout=true \ --set rust.thin-lto-import-instr-limit=10 diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile deleted file mode 100644 index 5cba7c564f16..000000000000 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile +++ /dev/null @@ -1,66 +0,0 @@ -FROM ubuntu:24.10 - -ARG DEBIAN_FRONTEND=noninteractive - -RUN apt-get update && apt-get install -y --no-install-recommends \ - bzip2 \ - g++ \ - gcc-multilib \ - make \ - ninja-build \ - file \ - curl \ - ca-certificates \ - python3 \ - git \ - cmake \ - sudo \ - gdb \ - llvm-19-tools \ - llvm-19-dev \ - libedit-dev \ - libssl-dev \ - pkg-config \ - zlib1g-dev \ - xz-utils \ - nodejs \ - mingw-w64 \ - # libgccjit dependencies - flex \ - libmpfr-dev \ - libgmp-dev \ - libmpc3 \ - libmpc-dev \ - && rm -rf /var/lib/apt/lists/* - -# Install powershell (universal package) so we can test x.ps1 on Linux -# FIXME: need a "universal" version that supports libicu74, but for now it still works to ignore that dep. -RUN curl -sL "https://github.com/PowerShell/PowerShell/releases/download/v7.3.1/powershell_7.3.1-1.deb_amd64.deb" > powershell.deb && \ - dpkg --ignore-depends=libicu72 -i powershell.deb && \ - rm -f powershell.deb - -COPY scripts/sccache.sh /scripts/ -RUN sh /scripts/sccache.sh - -# We are disabling CI LLVM since this builder is intentionally using a host -# LLVM, rather than the typical src/llvm-project LLVM. -ENV NO_DOWNLOAD_CI_LLVM 1 -ENV EXTERNAL_LLVM 1 - -# Using llvm-link-shared due to libffi issues -- see #34486 -ENV RUST_CONFIGURE_ARGS \ - --build=x86_64-unknown-linux-gnu \ - --llvm-root=/usr/lib/llvm-19 \ - --enable-llvm-link-shared \ - --set rust.randomize-layout=true \ - --set rust.thin-lto-import-instr-limit=10 - -COPY scripts/shared.sh /scripts/ - -COPY scripts/x86_64-gnu-llvm.sh /scripts/ -COPY scripts/x86_64-gnu-llvm2.sh /scripts/ -COPY scripts/x86_64-gnu-llvm3.sh /scripts/ -COPY scripts/stage_2_test_set1.sh /scripts/ -COPY scripts/stage_2_test_set2.sh /scripts/ - -ENV SCRIPT "Must specify DOCKER_SCRIPT for this image" diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 35b9456d37d2..b3e3fe7d96aa 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -122,19 +122,19 @@ pr: # tidy. This speeds up the PR CI job by ~1 minute. SKIP_SUBMODULES: src/gcc <<: *job-linux-4c - - name: x86_64-gnu-llvm-19 + - name: x86_64-gnu-llvm-20 env: ENABLE_GCC_CODEGEN: "1" DOCKER_SCRIPT: x86_64-gnu-llvm.sh <<: *job-linux-4c - - name: aarch64-gnu-llvm-19-1 + - name: aarch64-gnu-llvm-20-1 env: - IMAGE: aarch64-gnu-llvm-19 + IMAGE: aarch64-gnu-llvm-20 DOCKER_SCRIPT: stage_2_test_set1.sh <<: *job-aarch64-linux - - name: aarch64-gnu-llvm-19-2 + - name: aarch64-gnu-llvm-20-2 env: - IMAGE: aarch64-gnu-llvm-19 + IMAGE: aarch64-gnu-llvm-20 DOCKER_SCRIPT: stage_2_test_set2.sh <<: *job-aarch64-linux - name: x86_64-gnu-tools @@ -397,31 +397,6 @@ auto: DOCKER_SCRIPT: x86_64-gnu-llvm3.sh <<: *job-linux-4c - # The x86_64-gnu-llvm-19 job is split into multiple jobs to run tests in parallel. - # x86_64-gnu-llvm-19-1 skips tests that run in x86_64-gnu-llvm-19-{2,3}. - - name: x86_64-gnu-llvm-19-1 - env: - RUST_BACKTRACE: 1 - IMAGE: x86_64-gnu-llvm-19 - DOCKER_SCRIPT: stage_2_test_set2.sh - <<: *job-linux-4c - - # Skip tests that run in x86_64-gnu-llvm-19-{1,3} - - name: x86_64-gnu-llvm-19-2 - env: - RUST_BACKTRACE: 1 - IMAGE: x86_64-gnu-llvm-19 - DOCKER_SCRIPT: x86_64-gnu-llvm2.sh - <<: *job-linux-4c - - # Skip tests that run in x86_64-gnu-llvm-19-{1,2} - - name: x86_64-gnu-llvm-19-3 - env: - RUST_BACKTRACE: 1 - IMAGE: x86_64-gnu-llvm-19 - DOCKER_SCRIPT: x86_64-gnu-llvm3.sh - <<: *job-linux-4c - - name: x86_64-gnu-nopt <<: *job-linux-4c diff --git a/tests/assembly-llvm/riscv-soft-abi-with-float-features.rs b/tests/assembly-llvm/riscv-soft-abi-with-float-features.rs index 72cbd3841c12..085ea9facd06 100644 --- a/tests/assembly-llvm/riscv-soft-abi-with-float-features.rs +++ b/tests/assembly-llvm/riscv-soft-abi-with-float-features.rs @@ -2,9 +2,6 @@ //@ assembly-output: emit-asm //@ compile-flags: --target riscv64imac-unknown-none-elf -Ctarget-feature=+f,+d //@ needs-llvm-components: riscv -//@ revisions: LLVM-PRE-20 LLVM-POST-20 -//@ [LLVM-PRE-20] max-llvm-major-version: 19 -//@ [LLVM-POST-20] min-llvm-version: 20 #![feature(no_core, lang_items, f16)] #![crate_type = "lib"] @@ -28,11 +25,8 @@ pub extern "C" fn read_f16(x: &f16) -> f16 { // CHECK-LABEL: read_f32 #[no_mangle] pub extern "C" fn read_f32(x: &f32) -> f32 { - // LLVM-PRE-20: flw fa5, 0(a0) - // LLVM-PRE-20-NEXT: fmv.x.w a0, fa5 - // LLVM-PRE-20-NEXT: ret - // LLVM-POST-20: lw a0, 0(a0) - // LLVM-POST-20-NEXT: ret + // CHECK: lw a0, 0(a0) + // CHECK-NEXT: ret *x } diff --git a/tests/assembly-llvm/x86_64-bigint-helpers.rs b/tests/assembly-llvm/x86_64-bigint-helpers.rs index c5efda58fd66..64aa02572385 100644 --- a/tests/assembly-llvm/x86_64-bigint-helpers.rs +++ b/tests/assembly-llvm/x86_64-bigint-helpers.rs @@ -2,7 +2,6 @@ //@ assembly-output: emit-asm //@ compile-flags: --crate-type=lib -Copt-level=3 -C target-cpu=x86-64-v4 //@ compile-flags: -C llvm-args=-x86-asm-syntax=intel -//@ min-llvm-version: 20 #![no_std] #![feature(bigint_helper_methods)] diff --git a/tests/assembly-llvm/x86_64-cmp.rs b/tests/assembly-llvm/x86_64-cmp.rs index 26c9013d96fc..7e87171991d4 100644 --- a/tests/assembly-llvm/x86_64-cmp.rs +++ b/tests/assembly-llvm/x86_64-cmp.rs @@ -1,12 +1,6 @@ -//@ revisions: LLVM-PRE-20-DEBUG LLVM-20-DEBUG LLVM-PRE-20-OPTIM LLVM-20-OPTIM -//@ [LLVM-PRE-20-DEBUG] compile-flags: -C opt-level=0 -//@ [LLVM-PRE-20-DEBUG] max-llvm-major-version: 19 -//@ [LLVM-20-DEBUG] compile-flags: -C opt-level=0 -//@ [LLVM-20-DEBUG] min-llvm-version: 20 -//@ [LLVM-PRE-20-OPTIM] compile-flags: -C opt-level=3 -//@ [LLVM-PRE-20-OPTIM] max-llvm-major-version: 19 -//@ [LLVM-20-OPTIM] compile-flags: -C opt-level=3 -//@ [LLVM-20-OPTIM] min-llvm-version: 20 +//@ revisions: DEBUG OPTIM +//@ [DEBUG] compile-flags: -C opt-level=0 +//@ [OPTIM] compile-flags: -C opt-level=3 //@ assembly-output: emit-asm //@ compile-flags: --crate-type=lib -C llvm-args=-x86-asm-syntax=intel //@ only-x86_64 @@ -19,61 +13,31 @@ use std::intrinsics::three_way_compare; #[no_mangle] // CHECK-LABEL: signed_cmp: pub fn signed_cmp(a: i16, b: i16) -> std::cmp::Ordering { - // LLVM-PRE-20-DEBUG: cmp - // LLVM-PRE-20-DEBUG: setg - // LLVM-PRE-20-DEBUG: and - // LLVM-PRE-20-DEBUG: cmp - // LLVM-PRE-20-DEBUG: setl - // LLVM-PRE-20-DEBUG: and - // LLVM-PRE-20-DEBUG: sub + // DEBUG: sub + // DEBUG: setl + // DEBUG: setg + // DEBUG: sub + // DEBUG: ret // - // LLVM-20-DEBUG: sub - // LLVM-20-DEBUG: setl - // LLVM-20-DEBUG: setg - // LLVM-20-DEBUG: sub - // LLVM-20-DEBUG: ret - - // LLVM-PRE-20-OPTIM: xor - // LLVM-PRE-20-OPTIM: cmp - // LLVM-PRE-20-OPTIM: setne - // LLVM-PRE-20-OPTIM: mov - // LLVM-PRE-20-OPTIM: cmovge - // LLVM-PRE-20-OPTIM: ret - // - // LLVM-20-OPTIM: cmp - // LLVM-20-OPTIM: setl - // LLVM-20-OPTIM: setg - // LLVM-20-OPTIM: sub - // LLVM-20-OPTIM: ret + // OPTIM: cmp + // OPTIM: setl + // OPTIM: setg + // OPTIM: sub + // OPTIM: ret three_way_compare(a, b) } #[no_mangle] // CHECK-LABEL: unsigned_cmp: pub fn unsigned_cmp(a: u16, b: u16) -> std::cmp::Ordering { - // LLVM-PRE-20-DEBUG: cmp - // LLVM-PRE-20-DEBUG: seta - // LLVM-PRE-20-DEBUG: and - // LLVM-PRE-20-DEBUG: cmp - // LLVM-PRE-20-DEBUG: setb - // LLVM-PRE-20-DEBUG: and - // LLVM-PRE-20-DEBUG: sub + // DEBUG: sub + // DEBUG: seta + // DEBUG: sbb + // DEBUG: ret // - // LLVM-20-DEBUG: sub - // LLVM-20-DEBUG: seta - // LLVM-20-DEBUG: sbb - // LLVM-20-DEBUG: ret - - // LLVM-PRE-20-OPTIM: xor - // LLVM-PRE-20-OPTIM: cmp - // LLVM-PRE-20-OPTIM: setne - // LLVM-PRE-20-OPTIM: mov - // LLVM-PRE-20-OPTIM: cmovae - // LLVM-PRE-20-OPTIM: ret - // - // LLVM-20-OPTIM: cmp - // LLVM-20-OPTIM: seta - // LLVM-20-OPTIM: sbb - // LLVM-20-OPTIM: ret + // OPTIM: cmp + // OPTIM: seta + // OPTIM: sbb + // OPTIM: ret three_way_compare(a, b) } diff --git a/tests/codegen-llvm/comparison-operators-2-struct.rs b/tests/codegen-llvm/comparison-operators-2-struct.rs index e179066ebfd7..d44f92f511b6 100644 --- a/tests/codegen-llvm/comparison-operators-2-struct.rs +++ b/tests/codegen-llvm/comparison-operators-2-struct.rs @@ -1,5 +1,4 @@ //@ compile-flags: -C opt-level=1 -//@ min-llvm-version: 20 // The `derive(PartialOrd)` for a 2-field type doesn't override `lt`/`le`/`gt`/`ge`. // This double-checks that the `Option` intermediate values used diff --git a/tests/codegen-llvm/comparison-operators-2-tuple.rs b/tests/codegen-llvm/comparison-operators-2-tuple.rs index 6a7e489c82dd..37a7c5dfdaf0 100644 --- a/tests/codegen-llvm/comparison-operators-2-tuple.rs +++ b/tests/codegen-llvm/comparison-operators-2-tuple.rs @@ -1,5 +1,4 @@ //@ compile-flags: -C opt-level=1 -Z merge-functions=disabled -//@ min-llvm-version: 20 #![crate_type = "lib"] diff --git a/tests/codegen-llvm/enum/enum-aggregate.rs b/tests/codegen-llvm/enum/enum-aggregate.rs index f58d7ef12b66..7d450a89e2e3 100644 --- a/tests/codegen-llvm/enum/enum-aggregate.rs +++ b/tests/codegen-llvm/enum/enum-aggregate.rs @@ -1,5 +1,4 @@ //@ compile-flags: -Copt-level=0 -Cno-prepopulate-passes -//@ min-llvm-version: 19 //@ only-64bit #![crate_type = "lib"] diff --git a/tests/codegen-llvm/enum/enum-discriminant-eq.rs b/tests/codegen-llvm/enum/enum-discriminant-eq.rs index a1ab5e5c6e2e..68cd58643e84 100644 --- a/tests/codegen-llvm/enum/enum-discriminant-eq.rs +++ b/tests/codegen-llvm/enum/enum-discriminant-eq.rs @@ -1,5 +1,4 @@ //@ compile-flags: -Copt-level=3 -Zmerge-functions=disabled -//@ min-llvm-version: 20 //@ only-64bit //@ revisions: LLVM20 LLVM21 //@ [LLVM21] min-llvm-version: 21 diff --git a/tests/codegen-llvm/integer-cmp.rs b/tests/codegen-llvm/integer-cmp.rs index 812fa8e4a424..2233a575f8e7 100644 --- a/tests/codegen-llvm/integer-cmp.rs +++ b/tests/codegen-llvm/integer-cmp.rs @@ -1,9 +1,6 @@ // This is test for more optimal Ord implementation for integers. // See for more info. -//@ revisions: llvm-pre-20 llvm-20 -//@ [llvm-20] min-llvm-version: 20 -//@ [llvm-pre-20] max-llvm-major-version: 19 //@ compile-flags: -C opt-level=3 -Zmerge-functions=disabled #![crate_type = "lib"] @@ -13,50 +10,29 @@ use std::cmp::Ordering; // CHECK-LABEL: @cmp_signed #[no_mangle] pub fn cmp_signed(a: i64, b: i64) -> Ordering { - // llvm-20: call{{.*}} i8 @llvm.scmp.i8.i64 - // llvm-pre-20: icmp slt - // llvm-pre-20: icmp ne - // llvm-pre-20: zext i1 - // llvm-pre-20: select i1 + // CHECK: call{{.*}} i8 @llvm.scmp.i8.i64 a.cmp(&b) } // CHECK-LABEL: @cmp_unsigned #[no_mangle] pub fn cmp_unsigned(a: u32, b: u32) -> Ordering { - // llvm-20: call{{.*}} i8 @llvm.ucmp.i8.i32 - // llvm-pre-20: icmp ult - // llvm-pre-20: icmp ne - // llvm-pre-20: zext i1 - // llvm-pre-20: select i1 + // CHECK: call{{.*}} i8 @llvm.ucmp.i8.i32 a.cmp(&b) } // CHECK-LABEL: @cmp_char #[no_mangle] pub fn cmp_char(a: char, b: char) -> Ordering { - // llvm-20: call{{.*}} i8 @llvm.ucmp.i8.i32 - // llvm-pre-20: icmp ult - // llvm-pre-20: icmp ne - // llvm-pre-20: zext i1 - // llvm-pre-20: select i1 + // CHECK: call{{.*}} i8 @llvm.ucmp.i8.i32 a.cmp(&b) } // CHECK-LABEL: @cmp_tuple #[no_mangle] pub fn cmp_tuple(a: (i16, u16), b: (i16, u16)) -> Ordering { - // llvm-20-DAG: call{{.*}} i8 @llvm.ucmp.i8.i16 - // llvm-20-DAG: call{{.*}} i8 @llvm.scmp.i8.i16 - // llvm-20: ret i8 - // llvm-pre-20: icmp slt - // llvm-pre-20: icmp ne - // llvm-pre-20: zext i1 - // llvm-pre-20: select i1 - // llvm-pre-20: icmp ult - // llvm-pre-20: icmp ne - // llvm-pre-20: zext i1 - // llvm-pre-20: select i1 - // llvm-pre-20: select i1 + // CHECK-DAG: call{{.*}} i8 @llvm.ucmp.i8.i16 + // CHECK-DAG: call{{.*}} i8 @llvm.scmp.i8.i16 + // CHECK: ret i8 a.cmp(&b) } diff --git a/tests/codegen-llvm/intrinsics/three_way_compare.rs b/tests/codegen-llvm/intrinsics/three_way_compare.rs index 95fcb636f7ca..89bf69561e9a 100644 --- a/tests/codegen-llvm/intrinsics/three_way_compare.rs +++ b/tests/codegen-llvm/intrinsics/three_way_compare.rs @@ -2,7 +2,6 @@ //@ [DEBUG] compile-flags: -C opt-level=0 //@ [OPTIM] compile-flags: -C opt-level=3 //@ compile-flags: -C no-prepopulate-passes -//@ min-llvm-version: 20 #![crate_type = "lib"] #![feature(core_intrinsics)] diff --git a/tests/codegen-llvm/issues/and-masked-comparison-131162.rs b/tests/codegen-llvm/issues/and-masked-comparison-131162.rs index bdf021092fda..fc4b0341a31b 100644 --- a/tests/codegen-llvm/issues/and-masked-comparison-131162.rs +++ b/tests/codegen-llvm/issues/and-masked-comparison-131162.rs @@ -1,5 +1,4 @@ //@ compile-flags: -Copt-level=3 -//@ min-llvm-version: 20 #![crate_type = "lib"] diff --git a/tests/codegen-llvm/issues/issue-101082.rs b/tests/codegen-llvm/issues/issue-101082.rs index 8d15921ddb4e..0c1f90f951a7 100644 --- a/tests/codegen-llvm/issues/issue-101082.rs +++ b/tests/codegen-llvm/issues/issue-101082.rs @@ -1,6 +1,5 @@ //@ compile-flags: -Copt-level=3 //@ revisions: host x86-64 x86-64-v3 -//@ min-llvm-version: 20 //@[host] ignore-x86_64 diff --git a/tests/codegen-llvm/issues/issue-129795.rs b/tests/codegen-llvm/issues/issue-129795.rs index dc64ee35c97e..7a928389fab2 100644 --- a/tests/codegen-llvm/issues/issue-129795.rs +++ b/tests/codegen-llvm/issues/issue-129795.rs @@ -1,5 +1,4 @@ //@ compile-flags: -Copt-level=3 -//@ min-llvm-version: 20 #![crate_type = "lib"] // Ensure that a modulo operation with an operand that is known to be diff --git a/tests/codegen-llvm/issues/iter-max-no-unwrap-failed-129583.rs b/tests/codegen-llvm/issues/iter-max-no-unwrap-failed-129583.rs index 4d3fa4993ef7..4c4eebeabb5f 100644 --- a/tests/codegen-llvm/issues/iter-max-no-unwrap-failed-129583.rs +++ b/tests/codegen-llvm/issues/iter-max-no-unwrap-failed-129583.rs @@ -3,7 +3,6 @@ // use a larger value to prevent unrolling. //@ compile-flags: -Copt-level=3 -//@ min-llvm-version: 20 #![crate_type = "lib"] diff --git a/tests/codegen-llvm/issues/looping-over-ne-bytes-133528.rs b/tests/codegen-llvm/issues/looping-over-ne-bytes-133528.rs index 35acf765d690..b686f8c4b3ac 100644 --- a/tests/codegen-llvm/issues/looping-over-ne-bytes-133528.rs +++ b/tests/codegen-llvm/issues/looping-over-ne-bytes-133528.rs @@ -1,5 +1,4 @@ //@ compile-flags: -Copt-level=3 -//@ min-llvm-version: 20 #![crate_type = "lib"] /// Ensure the function is properly optimized diff --git a/tests/codegen-llvm/option-niche-eq.rs b/tests/codegen-llvm/option-niche-eq.rs index 3900cb79aa2a..e9c3fa2407e7 100644 --- a/tests/codegen-llvm/option-niche-eq.rs +++ b/tests/codegen-llvm/option-niche-eq.rs @@ -1,5 +1,4 @@ //@ revisions: REGULAR LLVM21 -//@ min-llvm-version: 20 //@ compile-flags: -Copt-level=3 -Zmerge-functions=disabled //@ [LLVM21] min-llvm-version: 21 #![crate_type = "lib"] diff --git a/tests/codegen-llvm/slice-last-elements-optimization.rs b/tests/codegen-llvm/slice-last-elements-optimization.rs index d982cda709d4..77fc1d21cd90 100644 --- a/tests/codegen-llvm/slice-last-elements-optimization.rs +++ b/tests/codegen-llvm/slice-last-elements-optimization.rs @@ -1,5 +1,4 @@ //@ compile-flags: -Copt-level=3 -//@ min-llvm-version: 20 #![crate_type = "lib"] // This test verifies that LLVM 20 properly optimizes the bounds check diff --git a/tests/codegen-llvm/swap-small-types.rs b/tests/codegen-llvm/swap-small-types.rs index 7aa613ae9c22..0799ff76331d 100644 --- a/tests/codegen-llvm/swap-small-types.rs +++ b/tests/codegen-llvm/swap-small-types.rs @@ -1,6 +1,5 @@ //@ compile-flags: -Copt-level=3 -Z merge-functions=disabled //@ only-x86_64 -//@ min-llvm-version: 20 //@ ignore-std-debug-assertions (`ptr::swap_nonoverlapping` has one which blocks some optimizations) #![crate_type = "lib"] diff --git a/tests/codegen-llvm/try_question_mark_nop.rs b/tests/codegen-llvm/try_question_mark_nop.rs index 398c9a580bc3..a09fa0a49019 100644 --- a/tests/codegen-llvm/try_question_mark_nop.rs +++ b/tests/codegen-llvm/try_question_mark_nop.rs @@ -1,9 +1,6 @@ //@ compile-flags: -Copt-level=3 -Z merge-functions=disabled //@ edition: 2021 //@ only-x86_64 -//@ revisions: NINETEEN TWENTY -//@[NINETEEN] exact-llvm-major-version: 19 -//@[TWENTY] min-llvm-version: 20 #![crate_type = "lib"] #![feature(try_blocks)] @@ -17,13 +14,9 @@ pub fn option_nop_match_32(x: Option) -> Option { // CHECK: start: // CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw i32 %0 to i1 - // NINETEEN-NEXT: [[SELECT:%.*]] = select i1 [[TRUNC]], i32 %0, i32 0 - // NINETEEN-NEXT: [[REG2:%.*]] = insertvalue { i32, i32 } poison, i32 [[SELECT]], 0 - // NINETEEN-NEXT: [[REG3:%.*]] = insertvalue { i32, i32 } [[REG2]], i32 %1, 1 - - // TWENTY-NEXT: [[SELECT:%.*]] = select i1 [[TRUNC]], i32 %1, i32 undef - // TWENTY-NEXT: [[REG2:%.*]] = insertvalue { i32, i32 } poison, i32 %0, 0 - // TWENTY-NEXT: [[REG3:%.*]] = insertvalue { i32, i32 } [[REG2]], i32 [[SELECT]], 1 + // CHECK-NEXT: [[SELECT:%.*]] = select i1 [[TRUNC]], i32 %1, i32 undef + // CHECK-NEXT: [[REG2:%.*]] = insertvalue { i32, i32 } poison, i32 %0, 0 + // CHECK-NEXT: [[REG3:%.*]] = insertvalue { i32, i32 } [[REG2]], i32 [[SELECT]], 1 // CHECK-NEXT: ret { i32, i32 } [[REG3]] match x { @@ -36,8 +29,8 @@ pub fn option_nop_match_32(x: Option) -> Option { #[no_mangle] pub fn option_nop_traits_32(x: Option) -> Option { // CHECK: start: - // TWENTY-NEXT: %[[IS_SOME:.+]] = trunc nuw i32 %0 to i1 - // TWENTY-NEXT: select i1 %[[IS_SOME]], i32 %1, i32 undef + // CHECK-NEXT: %[[IS_SOME:.+]] = trunc nuw i32 %0 to i1 + // CHECK-NEXT: select i1 %[[IS_SOME]], i32 %1, i32 undef // CHECK-NEXT: insertvalue { i32, i32 } // CHECK-NEXT: insertvalue { i32, i32 } // CHECK-NEXT: ret { i32, i32 } @@ -96,13 +89,9 @@ pub fn option_nop_match_64(x: Option) -> Option { // CHECK: start: // CHECK-NEXT: [[TRUNC:%.*]] = trunc nuw i64 %0 to i1 - // NINETEEN-NEXT: [[SELECT:%.*]] = select i1 [[TRUNC]], i64 %0, i64 0 - // NINETEEN-NEXT: [[REG2:%.*]] = insertvalue { i64, i64 } poison, i64 [[SELECT]], 0 - // NINETEEN-NEXT: [[REG3:%.*]] = insertvalue { i64, i64 } [[REG2]], i64 %1, 1 - - // TWENTY-NEXT: [[SELECT:%.*]] = select i1 [[TRUNC]], i64 %1, i64 undef - // TWENTY-NEXT: [[REG2:%.*]] = insertvalue { i64, i64 } poison, i64 %0, 0 - // TWENTY-NEXT: [[REG3:%.*]] = insertvalue { i64, i64 } [[REG2]], i64 [[SELECT]], 1 + // CHECK-NEXT: [[SELECT:%.*]] = select i1 [[TRUNC]], i64 %1, i64 undef + // CHECK-NEXT: [[REG2:%.*]] = insertvalue { i64, i64 } poison, i64 %0, 0 + // CHECK-NEXT: [[REG3:%.*]] = insertvalue { i64, i64 } [[REG2]], i64 [[SELECT]], 1 // CHECK-NEXT: ret { i64, i64 } [[REG3]] match x { @@ -115,8 +104,8 @@ pub fn option_nop_match_64(x: Option) -> Option { #[no_mangle] pub fn option_nop_traits_64(x: Option) -> Option { // CHECK: start: - // TWENTY-NEXT: %[[TRUNC:[0-9]+]] = trunc nuw i64 %0 to i1 - // TWENTY-NEXT: %[[SEL:\.[0-9]+]] = select i1 %[[TRUNC]], i64 %1, i64 undef + // CHECK-NEXT: %[[TRUNC:[0-9]+]] = trunc nuw i64 %0 to i1 + // CHECK-NEXT: %[[SEL:\.[0-9]+]] = select i1 %[[TRUNC]], i64 %1, i64 undef // CHECK-NEXT: insertvalue { i64, i64 } // CHECK-NEXT: insertvalue { i64, i64 } // CHECK-NEXT: ret { i64, i64 } diff --git a/tests/codegen-llvm/union-aggregate.rs b/tests/codegen-llvm/union-aggregate.rs index aac66c5dcdd9..7faa66804fea 100644 --- a/tests/codegen-llvm/union-aggregate.rs +++ b/tests/codegen-llvm/union-aggregate.rs @@ -1,5 +1,4 @@ //@ compile-flags: -Copt-level=0 -Cno-prepopulate-passes -//@ min-llvm-version: 19 //@ only-64bit #![crate_type = "lib"] diff --git a/tests/ui/abi/sparcv8plus-llvm19.rs b/tests/ui/abi/sparcv8plus-llvm19.rs deleted file mode 100644 index 3d6d8568b6e5..000000000000 --- a/tests/ui/abi/sparcv8plus-llvm19.rs +++ /dev/null @@ -1,42 +0,0 @@ -//@ add-core-stubs -//@ revisions: sparc sparcv8plus sparc_cpu_v9 sparc_feature_v8plus sparc_cpu_v9_feature_v8plus -//@[sparc] compile-flags: --target sparc-unknown-none-elf -//@[sparc] needs-llvm-components: sparc -//@[sparcv8plus] compile-flags: --target sparc-unknown-linux-gnu -//@[sparcv8plus] needs-llvm-components: sparc -//@[sparc_cpu_v9] compile-flags: --target sparc-unknown-none-elf -C target-cpu=v9 -//@[sparc_cpu_v9] needs-llvm-components: sparc -//@[sparc_feature_v8plus] compile-flags: --target sparc-unknown-none-elf -C target-feature=+v8plus -//@[sparc_feature_v8plus] needs-llvm-components: sparc -//@[sparc_cpu_v9_feature_v8plus] compile-flags: --target sparc-unknown-none-elf -C target-cpu=v9 -C target-feature=+v8plus -//@[sparc_cpu_v9_feature_v8plus] needs-llvm-components: sparc -//@ exact-llvm-major-version: 19 - -#![crate_type = "rlib"] -#![feature(no_core, rustc_attrs, lang_items)] -#![no_core] - -extern crate minicore; -use minicore::*; - -#[rustc_builtin_macro] -macro_rules! compile_error { - () => {}; -} - -#[cfg(all(not(target_feature = "v8plus"), not(target_feature = "v9")))] -compile_error!("-v8plus,-v9"); -//[sparc]~^ ERROR -v8plus,-v9 - -// FIXME: sparc_cpu_v9 should be in "-v8plus,+v9" group (fixed in LLVM 20) -#[cfg(all(target_feature = "v8plus", target_feature = "v9"))] -compile_error!("+v8plus,+v9"); -//[sparcv8plus,sparc_cpu_v9_feature_v8plus,sparc_cpu_v9]~^ ERROR +v8plus,+v9 - -// FIXME: should be rejected -#[cfg(all(target_feature = "v8plus", not(target_feature = "v9")))] -compile_error!("+v8plus,-v9 (FIXME)"); -//[sparc_feature_v8plus]~^ ERROR +v8plus,-v9 (FIXME) - -#[cfg(all(not(target_feature = "v8plus"), target_feature = "v9"))] -compile_error!("-v8plus,+v9"); diff --git a/tests/ui/abi/sparcv8plus-llvm19.sparc.stderr b/tests/ui/abi/sparcv8plus-llvm19.sparc.stderr deleted file mode 100644 index d3462ae87d33..000000000000 --- a/tests/ui/abi/sparcv8plus-llvm19.sparc.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: -v8plus,-v9 - --> $DIR/sparcv8plus-llvm19.rs:28:1 - | -LL | compile_error!("-v8plus,-v9"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/abi/sparcv8plus-llvm19.sparc_cpu_v9.stderr b/tests/ui/abi/sparcv8plus-llvm19.sparc_cpu_v9.stderr deleted file mode 100644 index 9891aec94b86..000000000000 --- a/tests/ui/abi/sparcv8plus-llvm19.sparc_cpu_v9.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: +v8plus,+v9 - --> $DIR/sparcv8plus-llvm19.rs:33:1 - | -LL | compile_error!("+v8plus,+v9"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/abi/sparcv8plus-llvm19.sparc_cpu_v9_feature_v8plus.stderr b/tests/ui/abi/sparcv8plus-llvm19.sparc_cpu_v9_feature_v8plus.stderr deleted file mode 100644 index 9891aec94b86..000000000000 --- a/tests/ui/abi/sparcv8plus-llvm19.sparc_cpu_v9_feature_v8plus.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: +v8plus,+v9 - --> $DIR/sparcv8plus-llvm19.rs:33:1 - | -LL | compile_error!("+v8plus,+v9"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/abi/sparcv8plus-llvm19.sparc_feature_v8plus.stderr b/tests/ui/abi/sparcv8plus-llvm19.sparc_feature_v8plus.stderr deleted file mode 100644 index dbcdb8ed121b..000000000000 --- a/tests/ui/abi/sparcv8plus-llvm19.sparc_feature_v8plus.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: +v8plus,-v9 (FIXME) - --> $DIR/sparcv8plus-llvm19.rs:38:1 - | -LL | compile_error!("+v8plus,-v9 (FIXME)"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/abi/sparcv8plus-llvm19.sparcv8plus.stderr b/tests/ui/abi/sparcv8plus-llvm19.sparcv8plus.stderr deleted file mode 100644 index 9891aec94b86..000000000000 --- a/tests/ui/abi/sparcv8plus-llvm19.sparcv8plus.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: +v8plus,+v9 - --> $DIR/sparcv8plus-llvm19.rs:33:1 - | -LL | compile_error!("+v8plus,+v9"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/abi/sparcv8plus.rs b/tests/ui/abi/sparcv8plus.rs index 6c17f7218386..ba4fb6f7108d 100644 --- a/tests/ui/abi/sparcv8plus.rs +++ b/tests/ui/abi/sparcv8plus.rs @@ -10,7 +10,6 @@ //@[sparc_feature_v8plus] needs-llvm-components: sparc //@[sparc_cpu_v9_feature_v8plus] compile-flags: --target sparc-unknown-none-elf -C target-cpu=v9 -C target-feature=+v8plus //@[sparc_cpu_v9_feature_v8plus] needs-llvm-components: sparc -//@ min-llvm-version: 20 #![crate_type = "rlib"] #![feature(no_core, rustc_attrs, lang_items)] diff --git a/tests/ui/abi/sparcv8plus.sparc.stderr b/tests/ui/abi/sparcv8plus.sparc.stderr index e2aa89a92731..e31dbd344d6f 100644 --- a/tests/ui/abi/sparcv8plus.sparc.stderr +++ b/tests/ui/abi/sparcv8plus.sparc.stderr @@ -1,5 +1,5 @@ error: -v8plus,-v9 - --> $DIR/sparcv8plus.rs:28:1 + --> $DIR/sparcv8plus.rs:27:1 | LL | compile_error!("-v8plus,-v9"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/abi/sparcv8plus.sparc_cpu_v9.stderr b/tests/ui/abi/sparcv8plus.sparc_cpu_v9.stderr index 2c5699f2dec2..a1a8383cbe70 100644 --- a/tests/ui/abi/sparcv8plus.sparc_cpu_v9.stderr +++ b/tests/ui/abi/sparcv8plus.sparc_cpu_v9.stderr @@ -1,5 +1,5 @@ error: -v8plus,+v9 - --> $DIR/sparcv8plus.rs:41:1 + --> $DIR/sparcv8plus.rs:40:1 | LL | compile_error!("-v8plus,+v9"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/abi/sparcv8plus.sparc_cpu_v9_feature_v8plus.stderr b/tests/ui/abi/sparcv8plus.sparc_cpu_v9_feature_v8plus.stderr index 4b96e4421f9f..c633ee26c514 100644 --- a/tests/ui/abi/sparcv8plus.sparc_cpu_v9_feature_v8plus.stderr +++ b/tests/ui/abi/sparcv8plus.sparc_cpu_v9_feature_v8plus.stderr @@ -1,5 +1,5 @@ error: +v8plus,+v9 - --> $DIR/sparcv8plus.rs:32:1 + --> $DIR/sparcv8plus.rs:31:1 | LL | compile_error!("+v8plus,+v9"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/abi/sparcv8plus.sparc_feature_v8plus.stderr b/tests/ui/abi/sparcv8plus.sparc_feature_v8plus.stderr index dfdec88961be..bad8adc15996 100644 --- a/tests/ui/abi/sparcv8plus.sparc_feature_v8plus.stderr +++ b/tests/ui/abi/sparcv8plus.sparc_feature_v8plus.stderr @@ -1,5 +1,5 @@ error: +v8plus,-v9 (FIXME) - --> $DIR/sparcv8plus.rs:37:1 + --> $DIR/sparcv8plus.rs:36:1 | LL | compile_error!("+v8plus,-v9 (FIXME)"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/abi/sparcv8plus.sparcv8plus.stderr b/tests/ui/abi/sparcv8plus.sparcv8plus.stderr index 4b96e4421f9f..c633ee26c514 100644 --- a/tests/ui/abi/sparcv8plus.sparcv8plus.stderr +++ b/tests/ui/abi/sparcv8plus.sparcv8plus.stderr @@ -1,5 +1,5 @@ error: +v8plus,+v9 - --> $DIR/sparcv8plus.rs:32:1 + --> $DIR/sparcv8plus.rs:31:1 | LL | compile_error!("+v8plus,+v9"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32d.stderr b/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32d.stderr index 8742d4bd82cd..c67c913d2a60 100644 --- a/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32d.stderr +++ b/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32d.stderr @@ -1,35 +1,35 @@ error: invalid register `$r0`: constant zero cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:27:18 + --> $DIR/bad-reg.rs:26:18 | LL | asm!("", out("$r0") _); | ^^^^^^^^^^^^ error: invalid register `$tp`: reserved for TLS - --> $DIR/bad-reg.rs:29:18 + --> $DIR/bad-reg.rs:28:18 | LL | asm!("", out("$tp") _); | ^^^^^^^^^^^^ error: invalid register `$sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:31:18 + --> $DIR/bad-reg.rs:30:18 | LL | asm!("", out("$sp") _); | ^^^^^^^^^^^^ error: invalid register `$r21`: reserved by the ABI - --> $DIR/bad-reg.rs:33:18 + --> $DIR/bad-reg.rs:32:18 | LL | asm!("", out("$r21") _); | ^^^^^^^^^^^^^ error: invalid register `$fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:35:18 + --> $DIR/bad-reg.rs:34:18 | LL | asm!("", out("$fp") _); | ^^^^^^^^^^^^ error: invalid register `$r31`: $r31 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:37:18 + --> $DIR/bad-reg.rs:36:18 | LL | asm!("", out("$r31") _); | ^^^^^^^^^^^^^ diff --git a/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32s.stderr b/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32s.stderr index e6cb6e40c701..99c071919acf 100644 --- a/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32s.stderr +++ b/tests/ui/asm/loongarch/bad-reg.loongarch32_ilp32s.stderr @@ -1,59 +1,59 @@ error: invalid register `$r0`: constant zero cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:27:18 + --> $DIR/bad-reg.rs:26:18 | LL | asm!("", out("$r0") _); | ^^^^^^^^^^^^ error: invalid register `$tp`: reserved for TLS - --> $DIR/bad-reg.rs:29:18 + --> $DIR/bad-reg.rs:28:18 | LL | asm!("", out("$tp") _); | ^^^^^^^^^^^^ error: invalid register `$sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:31:18 + --> $DIR/bad-reg.rs:30:18 | LL | asm!("", out("$sp") _); | ^^^^^^^^^^^^ error: invalid register `$r21`: reserved by the ABI - --> $DIR/bad-reg.rs:33:18 + --> $DIR/bad-reg.rs:32:18 | LL | asm!("", out("$r21") _); | ^^^^^^^^^^^^^ error: invalid register `$fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:35:18 + --> $DIR/bad-reg.rs:34:18 | LL | asm!("", out("$fp") _); | ^^^^^^^^^^^^ error: invalid register `$r31`: $r31 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:37:18 + --> $DIR/bad-reg.rs:36:18 | LL | asm!("", out("$r31") _); | ^^^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:41:26 + --> $DIR/bad-reg.rs:40:26 | LL | asm!("/* {} */", in(freg) f); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:43:26 + --> $DIR/bad-reg.rs:42:26 | LL | asm!("/* {} */", out(freg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:45:26 + --> $DIR/bad-reg.rs:44:26 | LL | asm!("/* {} */", in(freg) d); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:47:26 + --> $DIR/bad-reg.rs:46:26 | LL | asm!("/* {} */", out(freg) d); | ^^^^^^^^^^^ diff --git a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr index 8742d4bd82cd..c67c913d2a60 100644 --- a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr +++ b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64d.stderr @@ -1,35 +1,35 @@ error: invalid register `$r0`: constant zero cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:27:18 + --> $DIR/bad-reg.rs:26:18 | LL | asm!("", out("$r0") _); | ^^^^^^^^^^^^ error: invalid register `$tp`: reserved for TLS - --> $DIR/bad-reg.rs:29:18 + --> $DIR/bad-reg.rs:28:18 | LL | asm!("", out("$tp") _); | ^^^^^^^^^^^^ error: invalid register `$sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:31:18 + --> $DIR/bad-reg.rs:30:18 | LL | asm!("", out("$sp") _); | ^^^^^^^^^^^^ error: invalid register `$r21`: reserved by the ABI - --> $DIR/bad-reg.rs:33:18 + --> $DIR/bad-reg.rs:32:18 | LL | asm!("", out("$r21") _); | ^^^^^^^^^^^^^ error: invalid register `$fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:35:18 + --> $DIR/bad-reg.rs:34:18 | LL | asm!("", out("$fp") _); | ^^^^^^^^^^^^ error: invalid register `$r31`: $r31 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:37:18 + --> $DIR/bad-reg.rs:36:18 | LL | asm!("", out("$r31") _); | ^^^^^^^^^^^^^ diff --git a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr index e6cb6e40c701..99c071919acf 100644 --- a/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr +++ b/tests/ui/asm/loongarch/bad-reg.loongarch64_lp64s.stderr @@ -1,59 +1,59 @@ error: invalid register `$r0`: constant zero cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:27:18 + --> $DIR/bad-reg.rs:26:18 | LL | asm!("", out("$r0") _); | ^^^^^^^^^^^^ error: invalid register `$tp`: reserved for TLS - --> $DIR/bad-reg.rs:29:18 + --> $DIR/bad-reg.rs:28:18 | LL | asm!("", out("$tp") _); | ^^^^^^^^^^^^ error: invalid register `$sp`: the stack pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:31:18 + --> $DIR/bad-reg.rs:30:18 | LL | asm!("", out("$sp") _); | ^^^^^^^^^^^^ error: invalid register `$r21`: reserved by the ABI - --> $DIR/bad-reg.rs:33:18 + --> $DIR/bad-reg.rs:32:18 | LL | asm!("", out("$r21") _); | ^^^^^^^^^^^^^ error: invalid register `$fp`: the frame pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:35:18 + --> $DIR/bad-reg.rs:34:18 | LL | asm!("", out("$fp") _); | ^^^^^^^^^^^^ error: invalid register `$r31`: $r31 is used internally by LLVM and cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:37:18 + --> $DIR/bad-reg.rs:36:18 | LL | asm!("", out("$r31") _); | ^^^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:41:26 + --> $DIR/bad-reg.rs:40:26 | LL | asm!("/* {} */", in(freg) f); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:43:26 + --> $DIR/bad-reg.rs:42:26 | LL | asm!("/* {} */", out(freg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:45:26 + --> $DIR/bad-reg.rs:44:26 | LL | asm!("/* {} */", in(freg) d); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:47:26 + --> $DIR/bad-reg.rs:46:26 | LL | asm!("/* {} */", out(freg) d); | ^^^^^^^^^^^ diff --git a/tests/ui/asm/loongarch/bad-reg.rs b/tests/ui/asm/loongarch/bad-reg.rs index 0d3eba07f14d..cca37dd2e8e8 100644 --- a/tests/ui/asm/loongarch/bad-reg.rs +++ b/tests/ui/asm/loongarch/bad-reg.rs @@ -1,7 +1,6 @@ //@ add-core-stubs //@ needs-asm-support //@ revisions: loongarch32_ilp32d loongarch32_ilp32s loongarch64_lp64d loongarch64_lp64s -//@ min-llvm-version: 20 //@[loongarch32_ilp32d] compile-flags: --target loongarch32-unknown-none //@[loongarch32_ilp32d] needs-llvm-components: loongarch //@[loongarch32_ilp32s] compile-flags: --target loongarch32-unknown-none-softfloat From e54602c5bbb6abc272eef6c795dcc01e755ab75f Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 8 Aug 2025 13:59:06 -0700 Subject: [PATCH 566/682] Merge similar output checks in assembly-llvm/x86_64-cmp --- tests/assembly-llvm/x86_64-cmp.rs | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/tests/assembly-llvm/x86_64-cmp.rs b/tests/assembly-llvm/x86_64-cmp.rs index 7e87171991d4..1f1fe7fd0052 100644 --- a/tests/assembly-llvm/x86_64-cmp.rs +++ b/tests/assembly-llvm/x86_64-cmp.rs @@ -14,16 +14,11 @@ use std::intrinsics::three_way_compare; // CHECK-LABEL: signed_cmp: pub fn signed_cmp(a: i16, b: i16) -> std::cmp::Ordering { // DEBUG: sub - // DEBUG: setl - // DEBUG: setg - // DEBUG: sub - // DEBUG: ret - // // OPTIM: cmp - // OPTIM: setl - // OPTIM: setg - // OPTIM: sub - // OPTIM: ret + // CHECK: setl + // CHECK: setg + // CHECK: sub + // CHECK: ret three_way_compare(a, b) } @@ -31,13 +26,9 @@ pub fn signed_cmp(a: i16, b: i16) -> std::cmp::Ordering { // CHECK-LABEL: unsigned_cmp: pub fn unsigned_cmp(a: u16, b: u16) -> std::cmp::Ordering { // DEBUG: sub - // DEBUG: seta - // DEBUG: sbb - // DEBUG: ret - // // OPTIM: cmp - // OPTIM: seta - // OPTIM: sbb - // OPTIM: ret + // CHECK: seta + // CHECK: sbb + // CHECK: ret three_way_compare(a, b) } From 88bef496465112850185fd1c46de1e250b271fa3 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 8 Aug 2025 14:20:56 -0700 Subject: [PATCH 567/682] Update the FIXME comments in the generic three_way_compare --- .../rustc_codegen_ssa/src/traits/builder.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index 422fa715de10..4a5694e97fa2 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -412,23 +412,26 @@ pub trait BuilderMethods<'a, 'tcx>: lhs: Self::Value, rhs: Self::Value, ) -> Self::Value { + // FIXME: This implementation was designed around LLVM's ability to optimize, but `cg_llvm` + // overrides this to just use `@llvm.scmp`/`ucmp` since LLVM 20. This default impl should be + // reevaluated with respect to the remaining backends like cg_gcc, whether they might use + // specialized implementations as well, or continue to use a generic implementation here. use std::cmp::Ordering; let pred = |op| crate::base::bin_op_to_icmp_predicate(op, ty.is_signed()); if self.cx().sess().opts.optimize == OptLevel::No { - // FIXME: This actually generates tighter assembly, and is a classic trick - // - // However, as of 2023-11 it optimizes worse in things like derived - // `PartialOrd`, so only use it in debug for now. Once LLVM can handle it - // better (see ), it'll - // be worth trying it in optimized builds as well. + // This actually generates tighter assembly, and is a classic trick: + // . + // However, as of 2023-11 it optimized worse in LLVM in things like derived + // `PartialOrd`, so we were only using it in debug. Since LLVM now uses its own + // intrinsics, it may be be worth trying it in optimized builds for other backends. let is_gt = self.icmp(pred(mir::BinOp::Gt), lhs, rhs); let gtext = self.zext(is_gt, self.type_i8()); let is_lt = self.icmp(pred(mir::BinOp::Lt), lhs, rhs); let ltext = self.zext(is_lt, self.type_i8()); self.unchecked_ssub(gtext, ltext) } else { - // These operations are those expected by `tests/codegen-llvm/integer-cmp.rs`, - // from . + // These operations were better optimized by LLVM, before `@llvm.scmp`/`ucmp` in 20. + // See . let is_lt = self.icmp(pred(mir::BinOp::Lt), lhs, rhs); let is_ne = self.icmp(pred(mir::BinOp::Ne), lhs, rhs); let ge = self.select( From b79a4bfad66dea3a8b6bc90985cb65378288fe07 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Tue, 16 Sep 2025 21:51:39 +0200 Subject: [PATCH 568/682] Do not use `git -C dir` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Older versions of git (≤ 1.8.5) do not support the `-C dir` global option. Use the `cwd` optional argument when using Python's `subprocess` functionality instead. --- src/bootstrap/bootstrap.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 19e87f9c2931..effd33d288fa 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -1193,8 +1193,6 @@ class RustBuild(object): return "" cmd = [ "git", - "-C", - repo_path, "rev-list", "--author", author_email, @@ -1202,7 +1200,9 @@ class RustBuild(object): "HEAD", ] try: - commit = subprocess.check_output(cmd, universal_newlines=True).strip() + commit = subprocess.check_output( + cmd, universal_newlines=True, cwd=repo_path + ).strip() return commit or "" except subprocess.CalledProcessError: return "" From d81872a97145f35766b9885d6d88e8077d0eaf59 Mon Sep 17 00:00:00 2001 From: T Date: Fri, 12 Sep 2025 01:53:11 +0000 Subject: [PATCH 569/682] add Readme.md to tidy update Readme add info about githooks and bootstrap.toml add info about config and remove linting specific files add link to rustc-dev-guide --- src/doc/rustc-dev-guide/src/tests/intro.md | 4 +- src/tools/tidy/Readme.md | 112 +++++++++++++++++++++ 2 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 src/tools/tidy/Readme.md diff --git a/src/doc/rustc-dev-guide/src/tests/intro.md b/src/doc/rustc-dev-guide/src/tests/intro.md index b90c16d602c3..4fa63b83b17f 100644 --- a/src/doc/rustc-dev-guide/src/tests/intro.md +++ b/src/doc/rustc-dev-guide/src/tests/intro.md @@ -70,10 +70,12 @@ package tests: Tidy is a custom tool used for validating source code style and formatting conventions, such as rejecting long lines. There is more information in the -[section on coding conventions](../conventions.md#formatting). +[section on coding conventions](../conventions.md#formatting) or the [Tidy Readme]. > Examples: `./x test tidy` +[Tidy Readme]: https://github.com/rust-lang/rust/blob/master/src/tools/tidy/Readme.md + ### Formatting diff --git a/src/tools/tidy/Readme.md b/src/tools/tidy/Readme.md new file mode 100644 index 000000000000..fc9dc6350e92 --- /dev/null +++ b/src/tools/tidy/Readme.md @@ -0,0 +1,112 @@ +# Tidy +Tidy is the Rust project's custom internal linter and a crucial part of our testing and continuous integration (CI) infrastructure. It is designed to enforce a consistent style and formatting across the entire codebase, but its role extends beyond simple linting. Tidy also helps with infrastructure, policy, and documentation, ensuring the project remains organized, functional, and... tidy. + +This document will cover how to use tidy, the specific checks tidy performs, and using tidy directives to manage its behavior. By understanding and utilizing tidy, you can help us maintain the high standards of the Rust project. +## Tidy Checks +### Style and Code Quality +These lints focus on enforcing consistent formatting, style, and general code health. +* [`alphabetical`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/alphabetical/index.html): Checks that lists are sorted alphabetically +* [`style`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/style/index.html): Check to enforce various stylistic guidelines on the Rust codebase. +* [`filenames`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/filenames/index.html): Check to prevent invalid characters in file names. +* [`pal`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/pal/index.html): Check to enforce rules about platform-specific code in std. +* [`target_policy`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/target_policy/index.html): Check for target tier policy compliance. +* [`error_codes`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/error_codes/index.html): Check to ensure error codes are properly documented and tested. + +### Infrastructure +These checks focus on the integrity of the project's dependencies, internal tools, and documentation. +* [`bins`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/bins/index.html): Prevent stray binaries from being checked into the source tree. +* [`fluent_alphabetical`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/fluent_alphabetical/index.html) / [`fluent_period`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/fluent_period/index.html) / [`fluent_used`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/fluent_used/index.html): Various checks related to [Fluent](https://github.com/projectfluent) for localization and natural language translation. +* [`deps`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/deps/index.html) / [`extdeps`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/extdeps/index.html): Check for valid licenses and sources for external dependencies. +* [`gcc_submodule`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/gcc_submodule/index.html): Check that the `src/gcc` submodule version is valid. +* [`triagebot`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/triagebot/index.html): Check to ensure paths mentioned in `triagebot.toml` exist in the project. +* [`x_version`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/x_version/index.html): Validates the current version of the `x` tool. + +* [`edition`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/edition/index.html) / [`features`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/features/index.html): Check for a valid Rust edition and proper ordering of unstable features. +* [`rustdoc_css_themes`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/rustdoc_css_themes/index.html) / [`rustdoc_templates`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/rustdoc_templates/index.html): Verify that Rust documentation templates and themes are correct. +* [`unstable_book`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/unstable_book/index.html): Synchronizes the unstable book with unstable features. +### Testing +These checks ensure that tests are correctly structured, cleaned up, and free of common errors. +* [`tests_placement`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/tests_placement/index.html) / [`unit_tests`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/unit_tests/index.html): Verify that tests are located in the correct directories and are not using improper attributes. +* [`known_bug`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/known_bug/index.html) / [`unknown_revision`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/unknown_revision/index.html): Ensure that test directives and annotations are used correctly. +* [`debug_artifacts`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/debug_artifacts/index.html) / [`mir_opt_tests`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/mir_opt_tests/index.html): Prevent unnecessary artifacts and stale files in test directories. +* [`tests_revision_unpaired_stdout_stderr`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/tests_revision_unpaired_stdout_stderr/index.html) / [`ui_tests`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/ui_tests/index.html): Check for unpaired or stray test output files. +* [`target_specific_tests`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/target_specific_tests/index.html): Check to ensure that all target specific tests (those that require a --target flag) also require the pre-requisite LLVM components to run. +* [`rustdoc_gui_tests`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/rustdoc_gui_tests/index.html): Checks that rustdoc gui tests start with a small description +* [`rustdoc_json`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/rustdoc_json/index.html): Verify that `FORMAT_VERSION` is in sync with `rust-json-types`. +## Using Tidy + +Tidy is used in a number of different ways. +* Every time `./x test` is used tidy will run automatically. + +* On every pull request, tidy will run automatically during CI checks. +* Optionally, with the use of git-hooks, tidy can run locally on every push. This can be setup with `./x setup`. See the [rustc-dev-guide](https://rustc-dev-guide.rust-lang.org/building/suggested.html#installing-a-pre-push-hook) for more information. + +You can run tidy manually with: + +`./x test tidy` + +To first run the relevant formatter and then run tidy you can add `--bless`. + +`./x test tidy --bless` +### Extra Checks +[`extra_checks`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/extra_checks/index.html) are optional checks primarily focused on other file types and programming languages. + +Example usage: + +`./x test tidy --extra-checks=py,cpp,js,spellcheck` + +All options for `--extra-checks`: +* `cpp`, `cpp:fmt` +* `py`, `py:lint`, `py:fmt` +* `js`, `js:lint`, `js:fmt`, `js:typecheck` +* `shell`, `shell:lint` +* `spellcheck` + +Default values for tidy's `extra-checks` can be set in `bootstrap.toml`. For example, `build.tidy-extra-checks = "js,py"`. + +Any argument without a suffix (eg. `py` or `js`) will include all possible checks. For example, `--extra-checks=js` is the same as `extra-checks=js:lint,js:fmt,js:typecheck`. + +Any argument can be prefixed with `auto:` to only run if relevant files are modified (eg. `--extra-checks=auto:py`). + +A specific configuration file or folder can be passed to tidy after a double dash (`--extra-checks=py -- foo.py`) + +## Tidy Directives + +Tidy directives are special comments that help tidy operate. + +Tidy directives can be used in the following types of comments: +* `// ` +* `# ` +* `/* {...} */` +* `` + +You might find yourself needing to ignore a specific tidy style check and can do so with: +* `ignore-tidy-cr` +* `ignore-tidy-undocumented-unsafe` +* `ignore-tidy-tab` +* `ignore-tidy-linelength` +* `ignore-tidy-filelength` + +* `ignore-tidy-end-whitespace` +* `ignore-tidy-trailing-newlines` +* `ignore-tidy-leading-newlines` +* `ignore-tidy-copyright` +* `ignore-tidy-dbg` +* `ignore-tidy-odd-backticks` +* `ignore-tidy-todo` + +Some checks, like `alphabetical`, require a tidy directive to use: +``` +// tidy-alphabetical-start +fn aaa() {} +fn eee() {} +fn z() {} +// tidy-alphabetical-end +``` +While not exactly a tidy directive, // TODO will fail tidy and make sure you can't merge a PR with unfinished work. + +### Test Specific Directives + +`target-specific-tests` can be ignored with `// ignore-tidy-target-specific-tests` + +Tidy's `unknown_revision` check can be suppressed by adding the revision name to `//@ unused-revision-names:{revision}` or with `//@ unused-revision-names:*`. From a4e8d6d79d52da36f92d9e10b3fee5e024c55ade Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Tue, 16 Sep 2025 17:25:31 -0400 Subject: [PATCH 570/682] Update cargo submodule --- src/tools/cargo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/cargo b/src/tools/cargo index 24bb93c388fb..966f94733bbc 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 24bb93c388fb8c211a37986539f24a819dc669d3 +Subproject commit 966f94733bbc94ca51ff9f1e4c49ad250ebbdc50 From 44c1a00a2f9929cff1641b18f4c2b5f2d2833e3e Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 30 Apr 2023 20:48:29 +0000 Subject: [PATCH 571/682] Enable DestinationPropagation by default. --- compiler/rustc_mir_transform/src/dest_prop.rs | 10 +- compiler/rustc_mir_transform/src/lib.rs | 4 +- compiler/rustc_mir_transform/src/nrvo.rs | 234 ----------------- ...o.DestinationPropagation.panic-abort.diff} | 31 +-- ....DestinationPropagation.panic-unwind.diff} | 31 +-- .../nrvo_borrowed.rs} | 4 +- .../{ => dest-prop}/nrvo_miscompile_111005.rs | 7 +- ..._111005.wrong.DestinationPropagation.diff} | 7 +- tests/mir-opt/pre-codegen/checked_ops.rs | 3 +- ...b_at_home.PreCodegen.after.panic-abort.mir | 6 +- ..._at_home.PreCodegen.after.panic-unwind.mir | 6 +- .../derived_ord.demo_le.PreCodegen.after.mir | 35 ++- tests/mir-opt/pre-codegen/derived_ord.rs | 5 +- ....{impl#0}-partial_cmp.PreCodegen.after.mir | 32 ++- ...ward_loop.PreCodegen.after.panic-abort.mir | 68 +++-- ...ard_loop.PreCodegen.after.panic-unwind.mir | 68 +++-- ...ated_loop.PreCodegen.after.panic-abort.mir | 240 ++++++++---------- ...ted_loop.PreCodegen.after.panic-unwind.mir | 70 +++-- ...ward_loop.PreCodegen.after.panic-abort.mir | 182 ++++++------- ...ard_loop.PreCodegen.after.panic-unwind.mir | 182 ++++++------- ...ange_loop.PreCodegen.after.panic-abort.mir | 54 ++-- ...nge_loop.PreCodegen.after.panic-unwind.mir | 54 ++-- ...erse_loop.PreCodegen.after.panic-abort.mir | 72 +++--- ...rse_loop.PreCodegen.after.panic-unwind.mir | 72 +++--- ...e_ord.demo_ge_partial.PreCodegen.after.mir | 18 +- ...ple_ord.demo_le_total.PreCodegen.after.mir | 18 +- 26 files changed, 590 insertions(+), 923 deletions(-) delete mode 100644 compiler/rustc_mir_transform/src/nrvo.rs rename tests/mir-opt/{nrvo_simple.nrvo.RenameReturnPlace.panic-abort.diff => dest-prop/nrvo_borrowed.nrvo.DestinationPropagation.panic-abort.diff} (52%) rename tests/mir-opt/{nrvo_simple.nrvo.RenameReturnPlace.panic-unwind.diff => dest-prop/nrvo_borrowed.nrvo.DestinationPropagation.panic-unwind.diff} (52%) rename tests/mir-opt/{nrvo_simple.rs => dest-prop/nrvo_borrowed.rs} (69%) rename tests/mir-opt/{ => dest-prop}/nrvo_miscompile_111005.rs (75%) rename tests/mir-opt/{nrvo_miscompile_111005.wrong.RenameReturnPlace.diff => dest-prop/nrvo_miscompile_111005.wrong.DestinationPropagation.diff} (61%) diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index c57483a68114..abd1cd4e35ac 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -153,15 +153,7 @@ pub(super) struct DestinationPropagation; impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - // For now, only run at MIR opt level 3. Two things need to be changed before this can be - // turned on by default: - // 1. Because of the overeager removal of storage statements, this can cause stack space - // regressions. This opt is not the place to fix this though, it's a more general - // problem in MIR. - // 2. Despite being an overall perf improvement, this still causes a 30% regression in - // keccak. We can temporarily fix this by bounding function size, but in the long term - // we should fix this by being smarter about invalidating analysis results. - sess.mir_opt_level() >= 3 + sess.mir_opt_level() >= 2 } #[tracing::instrument(level = "trace", skip(self, tcx, body))] diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 1663dfa744f4..9ff7e0b55003 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -156,7 +156,6 @@ declare_passes! { mod match_branches : MatchBranchSimplification; mod mentioned_items : MentionedItems; mod multiple_return_terminators : MultipleReturnTerminators; - mod nrvo : RenameReturnPlace; mod post_drop_elaboration : CheckLiveDrops; mod prettify : ReorderBasicBlocks, ReorderLocals; mod promote_consts : PromoteTemps; @@ -715,7 +714,6 @@ pub(crate) fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<' &jump_threading::JumpThreading, &early_otherwise_branch::EarlyOtherwiseBranch, &simplify_comparison_integral::SimplifyComparisonIntegral, - &dest_prop::DestinationPropagation, &o1(simplify_branches::SimplifyConstCondition::Final), &o1(remove_noop_landing_pads::RemoveNoopLandingPads), &o1(simplify::SimplifyCfg::Final), @@ -723,7 +721,7 @@ pub(crate) fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<' &strip_debuginfo::StripDebugInfo, ©_prop::CopyProp, &dead_store_elimination::DeadStoreElimination::Final, - &nrvo::RenameReturnPlace, + &dest_prop::DestinationPropagation, &simplify::SimplifyLocals::Final, &multiple_return_terminators::MultipleReturnTerminators, &large_enums::EnumSizeOpt { discrepancy: 128 }, diff --git a/compiler/rustc_mir_transform/src/nrvo.rs b/compiler/rustc_mir_transform/src/nrvo.rs deleted file mode 100644 index 965002aae04c..000000000000 --- a/compiler/rustc_mir_transform/src/nrvo.rs +++ /dev/null @@ -1,234 +0,0 @@ -//! See the docs for [`RenameReturnPlace`]. - -use rustc_hir::Mutability; -use rustc_index::bit_set::DenseBitSet; -use rustc_middle::bug; -use rustc_middle::mir::visit::{MutVisitor, NonUseContext, PlaceContext, Visitor}; -use rustc_middle::mir::{self, BasicBlock, Local, Location}; -use rustc_middle::ty::TyCtxt; -use tracing::{debug, trace}; - -/// This pass looks for MIR that always copies the same local into the return place and eliminates -/// the copy by renaming all uses of that local to `_0`. -/// -/// This allows LLVM to perform an optimization similar to the named return value optimization -/// (NRVO) that is guaranteed in C++. This avoids a stack allocation and `memcpy` for the -/// relatively common pattern of allocating a buffer on the stack, mutating it, and returning it by -/// value like so: -/// -/// ```rust -/// fn foo(init: fn(&mut [u8; 1024])) -> [u8; 1024] { -/// let mut buf = [0; 1024]; -/// init(&mut buf); -/// buf -/// } -/// ``` -/// -/// For now, this pass is very simple and only capable of eliminating a single copy. A more general -/// version of copy propagation, such as the one based on non-overlapping live ranges in [#47954] and -/// [#71003], could yield even more benefits. -/// -/// [#47954]: https://github.com/rust-lang/rust/pull/47954 -/// [#71003]: https://github.com/rust-lang/rust/pull/71003 -pub(super) struct RenameReturnPlace; - -impl<'tcx> crate::MirPass<'tcx> for RenameReturnPlace { - fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - // unsound: #111005 - sess.mir_opt_level() > 0 && sess.opts.unstable_opts.unsound_mir_opts - } - - fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut mir::Body<'tcx>) { - let def_id = body.source.def_id(); - let Some(returned_local) = local_eligible_for_nrvo(body) else { - debug!("`{:?}` was ineligible for NRVO", def_id); - return; - }; - - debug!( - "`{:?}` was eligible for NRVO, making {:?} the return place", - def_id, returned_local - ); - - RenameToReturnPlace { tcx, to_rename: returned_local }.visit_body_preserves_cfg(body); - - // Clean up the `NOP`s we inserted for statements made useless by our renaming. - for block_data in body.basic_blocks.as_mut_preserves_cfg() { - block_data.statements.retain(|stmt| stmt.kind != mir::StatementKind::Nop); - } - - // Overwrite the debuginfo of `_0` with that of the renamed local. - let (renamed_decl, ret_decl) = - body.local_decls.pick2_mut(returned_local, mir::RETURN_PLACE); - - // Sometimes, the return place is assigned a local of a different but coercible type, for - // example `&mut T` instead of `&T`. Overwriting the `LocalInfo` for the return place means - // its type may no longer match the return type of its function. This doesn't cause a - // problem in codegen because these two types are layout-compatible, but may be unexpected. - debug!("_0: {:?} = {:?}: {:?}", ret_decl.ty, returned_local, renamed_decl.ty); - ret_decl.clone_from(renamed_decl); - - // The return place is always mutable. - ret_decl.mutability = Mutability::Mut; - } - - fn is_required(&self) -> bool { - false - } -} - -/// MIR that is eligible for the NRVO must fulfill two conditions: -/// 1. The return place must not be read prior to the `Return` terminator. -/// 2. A simple assignment of a whole local to the return place (e.g., `_0 = _1`) must be the -/// only definition of the return place reaching the `Return` terminator. -/// -/// If the MIR fulfills both these conditions, this function returns the `Local` that is assigned -/// to the return place along all possible paths through the control-flow graph. -fn local_eligible_for_nrvo(body: &mir::Body<'_>) -> Option { - if IsReturnPlaceRead::run(body) { - return None; - } - - let mut copied_to_return_place = None; - for block in body.basic_blocks.indices() { - // Look for blocks with a `Return` terminator. - if !matches!(body[block].terminator().kind, mir::TerminatorKind::Return) { - continue; - } - - // Look for an assignment of a single local to the return place prior to the `Return`. - let returned_local = find_local_assigned_to_return_place(block, body)?; - match body.local_kind(returned_local) { - // FIXME: Can we do this for arguments as well? - mir::LocalKind::Arg => return None, - - mir::LocalKind::ReturnPointer => bug!("Return place was assigned to itself?"), - mir::LocalKind::Temp => {} - } - - // If multiple different locals are copied to the return place. We can't pick a - // single one to rename. - if copied_to_return_place.is_some_and(|old| old != returned_local) { - return None; - } - - copied_to_return_place = Some(returned_local); - } - - copied_to_return_place -} - -fn find_local_assigned_to_return_place(start: BasicBlock, body: &mir::Body<'_>) -> Option { - let mut block = start; - let mut seen = DenseBitSet::new_empty(body.basic_blocks.len()); - - // Iterate as long as `block` has exactly one predecessor that we have not yet visited. - while seen.insert(block) { - trace!("Looking for assignments to `_0` in {:?}", block); - - let local = body[block].statements.iter().rev().find_map(as_local_assigned_to_return_place); - if local.is_some() { - return local; - } - - match body.basic_blocks.predecessors()[block].as_slice() { - &[pred] => block = pred, - _ => return None, - } - } - - None -} - -// If this statement is an assignment of an unprojected local to the return place, -// return that local. -fn as_local_assigned_to_return_place(stmt: &mir::Statement<'_>) -> Option { - if let mir::StatementKind::Assign(box (lhs, rhs)) = &stmt.kind { - if lhs.as_local() == Some(mir::RETURN_PLACE) { - if let mir::Rvalue::Use(mir::Operand::Copy(rhs) | mir::Operand::Move(rhs)) = rhs { - return rhs.as_local(); - } - } - } - - None -} - -struct RenameToReturnPlace<'tcx> { - to_rename: Local, - tcx: TyCtxt<'tcx>, -} - -/// Replaces all uses of `self.to_rename` with `_0`. -impl<'tcx> MutVisitor<'tcx> for RenameToReturnPlace<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn visit_statement(&mut self, stmt: &mut mir::Statement<'tcx>, loc: Location) { - // Remove assignments of the local being replaced to the return place, since it is now the - // return place: - // _0 = _1 - if as_local_assigned_to_return_place(stmt) == Some(self.to_rename) { - stmt.kind = mir::StatementKind::Nop; - return; - } - - // Remove storage annotations for the local being replaced: - // StorageLive(_1) - if let mir::StatementKind::StorageLive(local) | mir::StatementKind::StorageDead(local) = - stmt.kind - { - if local == self.to_rename { - stmt.kind = mir::StatementKind::Nop; - return; - } - } - - self.super_statement(stmt, loc) - } - - fn visit_terminator(&mut self, terminator: &mut mir::Terminator<'tcx>, loc: Location) { - // Ignore the implicit "use" of the return place in a `Return` statement. - if let mir::TerminatorKind::Return = terminator.kind { - return; - } - - self.super_terminator(terminator, loc); - } - - fn visit_local(&mut self, l: &mut Local, ctxt: PlaceContext, _: Location) { - if *l == mir::RETURN_PLACE { - assert_eq!(ctxt, PlaceContext::NonUse(NonUseContext::VarDebugInfo)); - } else if *l == self.to_rename { - *l = mir::RETURN_PLACE; - } - } -} - -struct IsReturnPlaceRead(bool); - -impl IsReturnPlaceRead { - fn run(body: &mir::Body<'_>) -> bool { - let mut vis = IsReturnPlaceRead(false); - vis.visit_body(body); - vis.0 - } -} - -impl<'tcx> Visitor<'tcx> for IsReturnPlaceRead { - fn visit_local(&mut self, l: Local, ctxt: PlaceContext, _: Location) { - if l == mir::RETURN_PLACE && ctxt.is_use() && !ctxt.is_place_assignment() { - self.0 = true; - } - } - - fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, loc: Location) { - // Ignore the implicit "use" of the return place in a `Return` statement. - if let mir::TerminatorKind::Return = terminator.kind { - return; - } - - self.super_terminator(terminator, loc); - } -} diff --git a/tests/mir-opt/nrvo_simple.nrvo.RenameReturnPlace.panic-abort.diff b/tests/mir-opt/dest-prop/nrvo_borrowed.nrvo.DestinationPropagation.panic-abort.diff similarity index 52% rename from tests/mir-opt/nrvo_simple.nrvo.RenameReturnPlace.panic-abort.diff rename to tests/mir-opt/dest-prop/nrvo_borrowed.nrvo.DestinationPropagation.panic-abort.diff index 6dce3ec5303d..e9fbcf20a722 100644 --- a/tests/mir-opt/nrvo_simple.nrvo.RenameReturnPlace.panic-abort.diff +++ b/tests/mir-opt/dest-prop/nrvo_borrowed.nrvo.DestinationPropagation.panic-abort.diff @@ -1,5 +1,5 @@ -- // MIR for `nrvo` before RenameReturnPlace -+ // MIR for `nrvo` after RenameReturnPlace +- // MIR for `nrvo` before DestinationPropagation ++ // MIR for `nrvo` after DestinationPropagation fn nrvo(_1: for<'a> fn(&'a mut [u8; 1024])) -> [u8; 1024] { debug init => _1; @@ -10,32 +10,33 @@ let mut _5: &mut [u8; 1024]; let mut _6: &mut [u8; 1024]; scope 1 { -- debug buf => _2; -+ debug buf => _0; + debug buf => _2; } bb0: { -- StorageLive(_2); -- _2 = [const 0_u8; 1024]; -+ _0 = [const 0_u8; 1024]; + StorageLive(_2); + _2 = [const 0_u8; 1024]; StorageLive(_3); - StorageLive(_4); - _4 = copy _1; +- StorageLive(_4); +- _4 = copy _1; ++ nop; ++ nop; StorageLive(_5); StorageLive(_6); -- _6 = &mut _2; -+ _6 = &mut _0; + _6 = &mut _2; _5 = &mut (*_6); - _3 = move _4(move _5) -> [return: bb1, unwind unreachable]; +- _3 = move _4(move _5) -> [return: bb1, unwind unreachable]; ++ _3 = move _1(move _5) -> [return: bb1, unwind unreachable]; } bb1: { StorageDead(_5); - StorageDead(_4); +- StorageDead(_4); ++ nop; StorageDead(_6); StorageDead(_3); -- _0 = copy _2; -- StorageDead(_2); + _0 = copy _2; + StorageDead(_2); return; } } diff --git a/tests/mir-opt/nrvo_simple.nrvo.RenameReturnPlace.panic-unwind.diff b/tests/mir-opt/dest-prop/nrvo_borrowed.nrvo.DestinationPropagation.panic-unwind.diff similarity index 52% rename from tests/mir-opt/nrvo_simple.nrvo.RenameReturnPlace.panic-unwind.diff rename to tests/mir-opt/dest-prop/nrvo_borrowed.nrvo.DestinationPropagation.panic-unwind.diff index 54cbe2871f15..95d5fe1b9304 100644 --- a/tests/mir-opt/nrvo_simple.nrvo.RenameReturnPlace.panic-unwind.diff +++ b/tests/mir-opt/dest-prop/nrvo_borrowed.nrvo.DestinationPropagation.panic-unwind.diff @@ -1,5 +1,5 @@ -- // MIR for `nrvo` before RenameReturnPlace -+ // MIR for `nrvo` after RenameReturnPlace +- // MIR for `nrvo` before DestinationPropagation ++ // MIR for `nrvo` after DestinationPropagation fn nrvo(_1: for<'a> fn(&'a mut [u8; 1024])) -> [u8; 1024] { debug init => _1; @@ -10,32 +10,33 @@ let mut _5: &mut [u8; 1024]; let mut _6: &mut [u8; 1024]; scope 1 { -- debug buf => _2; -+ debug buf => _0; + debug buf => _2; } bb0: { -- StorageLive(_2); -- _2 = [const 0_u8; 1024]; -+ _0 = [const 0_u8; 1024]; + StorageLive(_2); + _2 = [const 0_u8; 1024]; StorageLive(_3); - StorageLive(_4); - _4 = copy _1; +- StorageLive(_4); +- _4 = copy _1; ++ nop; ++ nop; StorageLive(_5); StorageLive(_6); -- _6 = &mut _2; -+ _6 = &mut _0; + _6 = &mut _2; _5 = &mut (*_6); - _3 = move _4(move _5) -> [return: bb1, unwind continue]; +- _3 = move _4(move _5) -> [return: bb1, unwind continue]; ++ _3 = move _1(move _5) -> [return: bb1, unwind continue]; } bb1: { StorageDead(_5); - StorageDead(_4); +- StorageDead(_4); ++ nop; StorageDead(_6); StorageDead(_3); -- _0 = copy _2; -- StorageDead(_2); + _0 = copy _2; + StorageDead(_2); return; } } diff --git a/tests/mir-opt/nrvo_simple.rs b/tests/mir-opt/dest-prop/nrvo_borrowed.rs similarity index 69% rename from tests/mir-opt/nrvo_simple.rs rename to tests/mir-opt/dest-prop/nrvo_borrowed.rs index df540472e1cc..6f3076d4c13e 100644 --- a/tests/mir-opt/nrvo_simple.rs +++ b/tests/mir-opt/dest-prop/nrvo_borrowed.rs @@ -1,8 +1,8 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ test-mir-pass: RenameReturnPlace +//@ test-mir-pass: DestinationPropagation -// EMIT_MIR nrvo_simple.nrvo.RenameReturnPlace.diff +// EMIT_MIR nrvo_borrowed.nrvo.DestinationPropagation.diff fn nrvo(init: fn(&mut [u8; 1024])) -> [u8; 1024] { let mut buf = [0; 1024]; init(&mut buf); diff --git a/tests/mir-opt/nrvo_miscompile_111005.rs b/tests/mir-opt/dest-prop/nrvo_miscompile_111005.rs similarity index 75% rename from tests/mir-opt/nrvo_miscompile_111005.rs rename to tests/mir-opt/dest-prop/nrvo_miscompile_111005.rs index 131f7b8f6f99..17e6450278ac 100644 --- a/tests/mir-opt/nrvo_miscompile_111005.rs +++ b/tests/mir-opt/dest-prop/nrvo_miscompile_111005.rs @@ -1,18 +1,17 @@ // This is a miscompilation, #111005 to track -//@ test-mir-pass: RenameReturnPlace +//@ test-mir-pass: DestinationPropagation #![feature(custom_mir, core_intrinsics)] extern crate core; use core::intrinsics::mir::*; -// EMIT_MIR nrvo_miscompile_111005.wrong.RenameReturnPlace.diff +// EMIT_MIR nrvo_miscompile_111005.wrong.DestinationPropagation.diff #[custom_mir(dialect = "runtime", phase = "initial")] pub fn wrong(arg: char) -> char { // CHECK-LABEL: fn wrong( // CHECK: _0 = copy _1; - // FIXME: This is wrong: - // CHECK-NEXT: _0 = const 'b'; + // CHECK-NEXT: _1 = const 'b'; // CHECK-NEXT: return; mir! { { diff --git a/tests/mir-opt/nrvo_miscompile_111005.wrong.RenameReturnPlace.diff b/tests/mir-opt/dest-prop/nrvo_miscompile_111005.wrong.DestinationPropagation.diff similarity index 61% rename from tests/mir-opt/nrvo_miscompile_111005.wrong.RenameReturnPlace.diff rename to tests/mir-opt/dest-prop/nrvo_miscompile_111005.wrong.DestinationPropagation.diff index d248c76f261b..60cf9236f6c2 100644 --- a/tests/mir-opt/nrvo_miscompile_111005.wrong.RenameReturnPlace.diff +++ b/tests/mir-opt/dest-prop/nrvo_miscompile_111005.wrong.DestinationPropagation.diff @@ -1,5 +1,5 @@ -- // MIR for `wrong` before RenameReturnPlace -+ // MIR for `wrong` after RenameReturnPlace +- // MIR for `wrong` before DestinationPropagation ++ // MIR for `wrong` after DestinationPropagation fn wrong(_1: char) -> char { let mut _0: char; @@ -9,8 +9,9 @@ - _2 = copy _1; - _0 = copy _2; - _2 = const 'b'; ++ nop; + _0 = copy _1; -+ _0 = const 'b'; ++ _1 = const 'b'; return; } } diff --git a/tests/mir-opt/pre-codegen/checked_ops.rs b/tests/mir-opt/pre-codegen/checked_ops.rs index 8fd340503f54..dfbc0f4a1108 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.rs +++ b/tests/mir-opt/pre-codegen/checked_ops.rs @@ -44,8 +44,7 @@ pub fn saturating_sub_at_home(lhs: u32, rhs: u32) -> u32 { // CHECK-LABEL: fn saturating_sub_at_home // CHECK: [[DELTA:_[0-9]+]] = SubUnchecked(copy _1, copy _2) // CHECK: [[TEMP1:_.+]] = Option::::Some({{move|copy}} [[DELTA]]); - // CHECK: [[TEMP2:_.+]] = {{move|copy}} (([[TEMP1]] as Some).0: u32); - // CHECK: _0 = {{move|copy}} [[TEMP2]]; + // CHECK: _0 = {{move|copy}} (([[TEMP1]] as Some).0: u32); u32::checked_sub(lhs, rhs).unwrap_or(0) } diff --git a/tests/mir-opt/pre-codegen/checked_ops.saturating_sub_at_home.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/checked_ops.saturating_sub_at_home.PreCodegen.after.panic-abort.mir index 5b4fdeda8573..dc7567b57e70 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.saturating_sub_at_home.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/checked_ops.saturating_sub_at_home.PreCodegen.after.panic-abort.mir @@ -10,7 +10,6 @@ fn saturating_sub_at_home(_1: u32, _2: u32) -> u32 { let mut _4: u32; } scope 2 (inlined Option::::unwrap_or) { - let _6: u32; scope 3 { } } @@ -28,10 +27,7 @@ fn saturating_sub_at_home(_1: u32, _2: u32) -> u32 { _5 = Option::::Some(move _4); StorageDead(_4); StorageDead(_3); - StorageLive(_6); - _6 = move ((_5 as Some).0: u32); - _0 = move _6; - StorageDead(_6); + _0 = move ((_5 as Some).0: u32); goto -> bb3; } diff --git a/tests/mir-opt/pre-codegen/checked_ops.saturating_sub_at_home.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/checked_ops.saturating_sub_at_home.PreCodegen.after.panic-unwind.mir index 5b4fdeda8573..dc7567b57e70 100644 --- a/tests/mir-opt/pre-codegen/checked_ops.saturating_sub_at_home.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/checked_ops.saturating_sub_at_home.PreCodegen.after.panic-unwind.mir @@ -10,7 +10,6 @@ fn saturating_sub_at_home(_1: u32, _2: u32) -> u32 { let mut _4: u32; } scope 2 (inlined Option::::unwrap_or) { - let _6: u32; scope 3 { } } @@ -28,10 +27,7 @@ fn saturating_sub_at_home(_1: u32, _2: u32) -> u32 { _5 = Option::::Some(move _4); StorageDead(_4); StorageDead(_3); - StorageLive(_6); - _6 = move ((_5 as Some).0: u32); - _0 = move _6; - StorageDead(_6); + _0 = move ((_5 as Some).0: u32); goto -> bb3; } diff --git a/tests/mir-opt/pre-codegen/derived_ord.demo_le.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/derived_ord.demo_le.PreCodegen.after.mir index 8746cb089916..e235fa35c023 100644 --- a/tests/mir-opt/pre-codegen/derived_ord.demo_le.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/derived_ord.demo_le.PreCodegen.after.mir @@ -5,8 +5,9 @@ fn demo_le(_1: &MultiField, _2: &MultiField) -> bool { debug b => _2; let mut _0: bool; scope 1 (inlined ::le) { - let mut _11: std::option::Option; + let mut _6: std::option::Option; scope 2 (inlined Option::::is_some_and:: bool {std::cmp::Ordering::is_le}>) { + let mut _11: isize; let _12: std::cmp::Ordering; scope 3 { scope 4 (inlined bool {std::cmp::Ordering::is_le} as FnOnce<(std::cmp::Ordering,)>>::call_once - shim(fn(std::cmp::Ordering) -> bool {std::cmp::Ordering::is_le})) { @@ -19,7 +20,6 @@ fn demo_le(_1: &MultiField, _2: &MultiField) -> bool { } } scope 7 (inlined ::partial_cmp) { - let mut _6: std::option::Option; let mut _7: i8; scope 8 { } @@ -38,9 +38,8 @@ fn demo_le(_1: &MultiField, _2: &MultiField) -> bool { bb0: { StorageLive(_12); - StorageLive(_11); - StorageLive(_5); StorageLive(_6); + StorageLive(_5); StorageLive(_7); StorageLive(_3); _3 = copy ((*_1).0: char); @@ -63,30 +62,44 @@ fn demo_le(_1: &MultiField, _2: &MultiField) -> bool { _10 = Cmp(move _8, move _9); StorageDead(_9); StorageDead(_8); - _11 = Option::::Some(move _10); + _6 = Option::::Some(move _10); StorageDead(_10); StorageDead(_7); - StorageDead(_6); StorageDead(_5); - goto -> bb3; + StorageLive(_11); + goto -> bb4; } bb2: { - _11 = copy _6; StorageDead(_7); - StorageDead(_6); StorageDead(_5); - goto -> bb3; + StorageLive(_11); + _11 = discriminant(_6); + switchInt(move _11) -> [0: bb3, 1: bb4, otherwise: bb6]; } bb3: { - _12 = move ((_11 as Some).0: std::cmp::Ordering); + _0 = const false; + goto -> bb5; + } + + bb4: { + _12 = move ((_6 as Some).0: std::cmp::Ordering); StorageLive(_13); _13 = discriminant(_12); _0 = Le(move _13, const 0_i8); StorageDead(_13); + goto -> bb5; + } + + bb5: { StorageDead(_11); + StorageDead(_6); StorageDead(_12); return; } + + bb6: { + unreachable; + } } diff --git a/tests/mir-opt/pre-codegen/derived_ord.rs b/tests/mir-opt/pre-codegen/derived_ord.rs index 73ae923a6cb9..823e0f6d09c2 100644 --- a/tests/mir-opt/pre-codegen/derived_ord.rs +++ b/tests/mir-opt/pre-codegen/derived_ord.rs @@ -25,7 +25,10 @@ pub fn demo_le(a: &MultiField, b: &MultiField) -> bool { // CHECK: Cmp(move [[A1]], move [[B1]]); // CHECK: [[D1:_[0-9]+]] = discriminant({{.+}}); - // CHECK: _0 = Le(move [[D1]], const 0_i8); + // CHECK: switchInt(move [[D1]]) -> [0: bb{{[0-9]+}}, 1: bb{{[0-9]+}}, otherwise: bb{{[0-9]+}}]; + + // CHECK: [[D2:_[0-9]+]] = discriminant({{.+}}); + // CHECK: _0 = Le(move [[D2]], const 0_i8); *a <= *b } diff --git a/tests/mir-opt/pre-codegen/derived_ord.{impl#0}-partial_cmp.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/derived_ord.{impl#0}-partial_cmp.PreCodegen.after.mir index de25eebee77f..5993bd79d274 100644 --- a/tests/mir-opt/pre-codegen/derived_ord.{impl#0}-partial_cmp.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/derived_ord.{impl#0}-partial_cmp.PreCodegen.after.mir @@ -4,10 +4,9 @@ fn ::partial_cmp(_1: &MultiField, _2: &M debug self => _1; debug other => _2; let mut _0: std::option::Option; - let mut _6: std::option::Option; - let mut _7: i8; + let mut _6: i8; scope 1 { - debug cmp => _6; + debug cmp => _0; } scope 2 (inlined std::cmp::impls::::partial_cmp) { let mut _3: char; @@ -15,9 +14,9 @@ fn ::partial_cmp(_1: &MultiField, _2: &M let mut _5: std::cmp::Ordering; } scope 3 (inlined std::cmp::impls::::partial_cmp) { + let mut _7: i16; let mut _8: i16; - let mut _9: i16; - let mut _10: std::cmp::Ordering; + let mut _9: std::cmp::Ordering; } bb0: { @@ -28,27 +27,26 @@ fn ::partial_cmp(_1: &MultiField, _2: &M _5 = Cmp(move _3, move _4); StorageDead(_4); StorageDead(_3); - _6 = Option::::Some(copy _5); - _7 = discriminant(_5); - switchInt(move _7) -> [0: bb1, otherwise: bb2]; + _0 = Option::::Some(copy _5); + _6 = discriminant(_5); + switchInt(move _6) -> [0: bb1, otherwise: bb2]; } bb1: { - StorageLive(_10); - StorageLive(_8); - _8 = copy ((*_1).1: i16); StorageLive(_9); - _9 = copy ((*_2).1: i16); - _10 = Cmp(move _8, move _9); - StorageDead(_9); + StorageLive(_7); + _7 = copy ((*_1).1: i16); + StorageLive(_8); + _8 = copy ((*_2).1: i16); + _9 = Cmp(move _7, move _8); StorageDead(_8); - _0 = Option::::Some(move _10); - StorageDead(_10); + StorageDead(_7); + _0 = Option::::Some(move _9); + StorageDead(_9); goto -> bb3; } bb2: { - _0 = copy _6; goto -> bb3; } diff --git a/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-abort.mir index dfe618612ab9..03a52b82b49b 100644 --- a/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-abort.mir @@ -5,23 +5,21 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () { debug end => _2; debug f => _3; let mut _0: (); - let mut _4: u32; - let mut _9: std::option::Option; - let mut _11: &impl Fn(u32); - let mut _12: (u32,); - let _13: (); + let mut _7: std::option::Option; + let mut _9: &impl Fn(u32); + let mut _10: (u32,); + let _11: (); scope 1 { - debug ((iter: std::ops::Range).0: u32) => _4; + debug ((iter: std::ops::Range).0: u32) => _1; debug ((iter: std::ops::Range).1: u32) => _2; - let _10: u32; + let _8: u32; scope 2 { - debug x => _10; + debug x => _8; } scope 4 (inlined iter::range::>::next) { scope 5 (inlined as iter::range::RangeIteratorImpl>::spec_next) { - let mut _6: bool; - let _7: u32; - let mut _8: u32; + let mut _5: bool; + let _6: u32; scope 6 { scope 8 (inlined ::forward_unchecked) { scope 9 (inlined #[track_caller] core::num::::unchecked_add) { @@ -33,7 +31,7 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () { } } scope 7 (inlined std::cmp::impls::::lt) { - let mut _5: u32; + let mut _4: u32; } } } @@ -42,25 +40,22 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () { } bb0: { - StorageLive(_4); - _4 = copy _1; goto -> bb1; } bb1: { - StorageLive(_9); - StorageLive(_6); + StorageLive(_7); StorageLive(_5); - _5 = copy _4; - _6 = Lt(move _5, copy _2); - StorageDead(_5); - switchInt(move _6) -> [0: bb2, otherwise: bb4]; + StorageLive(_4); + _4 = copy _1; + _5 = Lt(move _4, copy _2); + StorageDead(_4); + switchInt(move _5) -> [0: bb2, otherwise: bb4]; } bb2: { - StorageDead(_6); - StorageDead(_9); - StorageDead(_4); + StorageDead(_5); + StorageDead(_7); drop(_3) -> [return: bb3, unwind unreachable]; } @@ -69,25 +64,22 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () { } bb4: { - _7 = copy _4; - StorageLive(_8); - _8 = AddUnchecked(copy _7, const 1_u32); - _4 = move _8; - StorageDead(_8); - _9 = Option::::Some(copy _7); - StorageDead(_6); - _10 = copy ((_9 as Some).0: u32); - StorageLive(_11); - _11 = &_3; - StorageLive(_12); - _12 = (copy _10,); - _13 = >::call(move _11, move _12) -> [return: bb5, unwind unreachable]; + _6 = copy _1; + _1 = AddUnchecked(copy _6, const 1_u32); + _7 = Option::::Some(copy _6); + StorageDead(_5); + _8 = copy ((_7 as Some).0: u32); + StorageLive(_9); + _9 = &_3; + StorageLive(_10); + _10 = (copy _8,); + _11 = >::call(move _9, move _10) -> [return: bb5, unwind unreachable]; } bb5: { - StorageDead(_12); - StorageDead(_11); + StorageDead(_10); StorageDead(_9); + StorageDead(_7); goto -> bb1; } } diff --git a/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-unwind.mir index e0fcfcaffc59..3b09f33e7333 100644 --- a/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/range_iter.forward_loop.PreCodegen.after.panic-unwind.mir @@ -5,23 +5,21 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () { debug end => _2; debug f => _3; let mut _0: (); - let mut _4: u32; - let mut _9: std::option::Option; - let mut _11: &impl Fn(u32); - let mut _12: (u32,); - let _13: (); + let mut _7: std::option::Option; + let mut _9: &impl Fn(u32); + let mut _10: (u32,); + let _11: (); scope 1 { - debug ((iter: std::ops::Range).0: u32) => _4; + debug ((iter: std::ops::Range).0: u32) => _1; debug ((iter: std::ops::Range).1: u32) => _2; - let _10: u32; + let _8: u32; scope 2 { - debug x => _10; + debug x => _8; } scope 4 (inlined iter::range::>::next) { scope 5 (inlined as iter::range::RangeIteratorImpl>::spec_next) { - let mut _6: bool; - let _7: u32; - let mut _8: u32; + let mut _5: bool; + let _6: u32; scope 6 { scope 8 (inlined ::forward_unchecked) { scope 9 (inlined #[track_caller] core::num::::unchecked_add) { @@ -33,7 +31,7 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () { } } scope 7 (inlined std::cmp::impls::::lt) { - let mut _5: u32; + let mut _4: u32; } } } @@ -42,25 +40,22 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () { } bb0: { - StorageLive(_4); - _4 = copy _1; goto -> bb1; } bb1: { - StorageLive(_9); - StorageLive(_6); + StorageLive(_7); StorageLive(_5); - _5 = copy _4; - _6 = Lt(move _5, copy _2); - StorageDead(_5); - switchInt(move _6) -> [0: bb2, otherwise: bb4]; + StorageLive(_4); + _4 = copy _1; + _5 = Lt(move _4, copy _2); + StorageDead(_4); + switchInt(move _5) -> [0: bb2, otherwise: bb4]; } bb2: { - StorageDead(_6); - StorageDead(_9); - StorageDead(_4); + StorageDead(_5); + StorageDead(_7); drop(_3) -> [return: bb3, unwind continue]; } @@ -69,25 +64,22 @@ fn forward_loop(_1: u32, _2: u32, _3: impl Fn(u32)) -> () { } bb4: { - _7 = copy _4; - StorageLive(_8); - _8 = AddUnchecked(copy _7, const 1_u32); - _4 = move _8; - StorageDead(_8); - _9 = Option::::Some(copy _7); - StorageDead(_6); - _10 = copy ((_9 as Some).0: u32); - StorageLive(_11); - _11 = &_3; - StorageLive(_12); - _12 = (copy _10,); - _13 = >::call(move _11, move _12) -> [return: bb5, unwind: bb6]; + _6 = copy _1; + _1 = AddUnchecked(copy _6, const 1_u32); + _7 = Option::::Some(copy _6); + StorageDead(_5); + _8 = copy ((_7 as Some).0: u32); + StorageLive(_9); + _9 = &_3; + StorageLive(_10); + _10 = (copy _8,); + _11 = >::call(move _9, move _10) -> [return: bb5, unwind: bb6]; } bb5: { - StorageDead(_12); - StorageDead(_11); + StorageDead(_10); StorageDead(_9); + StorageDead(_7); goto -> bb1; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir index 72b39a7f6a87..104987b0fdda 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-abort.mir @@ -4,30 +4,28 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { debug slice => _1; debug f => _2; let mut _0: (); - let mut _11: std::ptr::NonNull; - let mut _12: *const T; - let mut _13: usize; - let mut _32: std::option::Option<(usize, &T)>; - let mut _35: &impl Fn(usize, &T); - let mut _36: (usize, &T); - let _37: (); + let mut _10: usize; + let mut _28: std::option::Option<(usize, &T)>; + let mut _31: &impl Fn(usize, &T); + let mut _32: (usize, &T); + let _33: (); scope 1 { - debug (((iter: Enumerate>).0: std::slice::Iter<'_, T>).0: std::ptr::NonNull) => _11; - debug (((iter: Enumerate>).0: std::slice::Iter<'_, T>).1: *const T) => _12; + debug (((iter: Enumerate>).0: std::slice::Iter<'_, T>).0: std::ptr::NonNull) => _6; + debug (((iter: Enumerate>).0: std::slice::Iter<'_, T>).1: *const T) => _9; debug (((iter: Enumerate>).0: std::slice::Iter<'_, T>).2: std::marker::PhantomData<&T>) => const ZeroSized: PhantomData<&T>; - debug ((iter: Enumerate>).1: usize) => _13; - let _33: usize; - let _34: &T; + debug ((iter: Enumerate>).1: usize) => _10; + let _29: usize; + let _30: &T; scope 2 { - debug i => _33; - debug x => _34; + debug i => _29; + debug x => _30; } scope 18 (inlined > as Iterator>::next) { - let mut _27: std::option::Option<&T>; - let mut _30: (usize, bool); - let mut _31: (usize, &T); + let mut _23: std::option::Option<&T>; + let mut _26: (usize, bool); + let mut _27: (usize, &T); scope 19 { - let _29: usize; + let _25: usize; scope 24 { } } @@ -42,21 +40,21 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } } scope 25 (inlined as Try>::branch) { - let _28: &T; + let _24: &T; scope 26 { } } scope 28 (inlined as Iterator>::next) { - let _14: std::ptr::NonNull; - let _16: std::ptr::NonNull; - let mut _19: bool; - let mut _22: std::ptr::NonNull; - let mut _24: usize; - let _26: &T; + let mut _6: std::ptr::NonNull; + let _11: std::ptr::NonNull; + let _13: std::ptr::NonNull; + let mut _16: bool; + let mut _20: usize; + let _22: &T; scope 29 { - let _15: *const T; + let _12: *const T; scope 30 { - let _23: usize; + let _19: usize; scope 31 { scope 34 (inlined #[track_caller] core::num::::unchecked_sub) { scope 35 (inlined core::ub_checks::check_language_ub) { @@ -72,21 +70,21 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } } scope 38 (inlined as PartialEq>::eq) { - let mut _17: *mut T; - let mut _18: *mut T; + let mut _14: *mut T; + let mut _15: *mut T; scope 39 (inlined NonNull::::as_ptr) { } scope 40 (inlined NonNull::::as_ptr) { } } scope 41 (inlined NonNull::::add) { - let mut _20: *const T; - let mut _21: *const T; + let mut _17: *const T; + let mut _18: *const T; scope 42 (inlined NonNull::::as_ptr) { } } scope 43 (inlined NonNull::::as_ref::<'_>) { - let _25: *const T; + let _21: *const T; scope 44 (inlined NonNull::::as_ptr) { } scope 45 (inlined std::ptr::mut_ptr::::cast_const) { @@ -102,9 +100,7 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { let _3: usize; let mut _7: *mut T; let mut _8: *mut T; - let mut _10: *const T; scope 5 { - let _6: std::ptr::NonNull; scope 6 { let _9: *const T; scope 7 { @@ -145,7 +141,6 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { _5 = copy _4 as *const T (PtrToPtr); _6 = NonNull:: { pointer: copy _5 }; StorageDead(_5); - StorageLive(_9); switchInt(const ::IS_ZST) -> [0: bb1, otherwise: bb2]; } @@ -166,97 +161,86 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb3: { - _10 = copy _9; - StorageDead(_9); StorageDead(_4); StorageDead(_3); - StorageLive(_11); - StorageLive(_12); - StorageLive(_13); - _11 = copy _6; - _12 = copy _10; - _13 = const 0_usize; + StorageLive(_10); + _10 = const 0_usize; goto -> bb4; } bb4: { - StorageLive(_32); - StorageLive(_29); - StorageLive(_30); - StorageLive(_27); - StorageLive(_14); - StorageLive(_15); - StorageLive(_23); - StorageLive(_24); - StorageLive(_16); + StorageLive(_28); + StorageLive(_25); StorageLive(_26); - _14 = copy _11; - _15 = copy _12; + StorageLive(_23); + StorageLive(_11); + StorageLive(_12); + StorageLive(_19); + StorageLive(_20); + StorageLive(_13); + StorageLive(_22); + _11 = copy _6; + _12 = copy _9; switchInt(const ::IS_ZST) -> [0: bb5, otherwise: bb8]; } bb5: { - StorageLive(_19); - _16 = copy _15 as std::ptr::NonNull (Transmute); - StorageLive(_17); - _17 = copy _14 as *mut T (Transmute); - StorageLive(_18); - _18 = copy _16 as *mut T (Transmute); - _19 = Eq(copy _17, copy _18); - StorageDead(_18); - StorageDead(_17); - switchInt(move _19) -> [0: bb6, otherwise: bb7]; + StorageLive(_16); + _13 = copy _12 as std::ptr::NonNull (Transmute); + StorageLive(_14); + _14 = copy _11 as *mut T (Transmute); + StorageLive(_15); + _15 = copy _13 as *mut T (Transmute); + _16 = Eq(copy _14, copy _15); + StorageDead(_15); + StorageDead(_14); + switchInt(move _16) -> [0: bb6, otherwise: bb7]; } bb6: { - StorageDead(_19); - StorageLive(_22); - StorageLive(_21); - StorageLive(_20); - _20 = copy _14 as *const T (Transmute); - _21 = Offset(copy _20, const 1_usize); - StorageDead(_20); - _22 = NonNull:: { pointer: copy _21 }; - StorageDead(_21); - _11 = move _22; - StorageDead(_22); + StorageDead(_16); + StorageLive(_18); + StorageLive(_17); + _17 = copy _11 as *const T (Transmute); + _18 = Offset(copy _17, const 1_usize); + StorageDead(_17); + _6 = NonNull:: { pointer: copy _18 }; + StorageDead(_18); goto -> bb13; } bb7: { - StorageDead(_19); - StorageDead(_26); StorageDead(_16); - StorageDead(_24); - StorageDead(_23); - StorageDead(_15); - StorageDead(_14); + StorageDead(_22); + StorageDead(_13); + StorageDead(_20); + StorageDead(_19); + StorageDead(_12); + StorageDead(_11); goto -> bb10; } bb8: { - _23 = copy _15 as usize (Transmute); - switchInt(copy _23) -> [0: bb9, otherwise: bb12]; + _19 = copy _12 as usize (Transmute); + switchInt(copy _19) -> [0: bb9, otherwise: bb12]; } bb9: { - StorageDead(_26); - StorageDead(_16); - StorageDead(_24); - StorageDead(_23); - StorageDead(_15); - StorageDead(_14); + StorageDead(_22); + StorageDead(_13); + StorageDead(_20); + StorageDead(_19); + StorageDead(_12); + StorageDead(_11); goto -> bb10; } bb10: { - StorageDead(_27); - StorageDead(_30); - StorageDead(_29); - StorageDead(_32); - StorageDead(_11); - StorageDead(_12); - StorageDead(_13); + StorageDead(_23); + StorageDead(_26); + StorageDead(_25); + StorageDead(_28); + StorageDead(_10); drop(_2) -> [return: bb11, unwind unreachable]; } @@ -265,51 +249,51 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb12: { - _24 = SubUnchecked(copy _23, const 1_usize); - _12 = copy _24 as *const T (Transmute); + _20 = SubUnchecked(copy _19, const 1_usize); + _9 = copy _20 as *const T (Transmute); goto -> bb13; } bb13: { - StorageLive(_25); - _25 = copy _14 as *const T (Transmute); - _26 = &(*_25); - StorageDead(_25); - _27 = Option::<&T>::Some(copy _26); - StorageDead(_26); - StorageDead(_16); - StorageDead(_24); + StorageLive(_21); + _21 = copy _11 as *const T (Transmute); + _22 = &(*_21); + StorageDead(_21); + _23 = Option::<&T>::Some(copy _22); + StorageDead(_22); + StorageDead(_13); + StorageDead(_20); + StorageDead(_19); + StorageDead(_12); + StorageDead(_11); + _24 = copy ((_23 as Some).0: &T); StorageDead(_23); - StorageDead(_15); - StorageDead(_14); - _28 = copy ((_27 as Some).0: &T); - StorageDead(_27); - _29 = copy _13; - _30 = AddWithOverflow(copy _13, const 1_usize); - assert(!move (_30.1: bool), "attempt to compute `{} + {}`, which would overflow", copy _13, const 1_usize) -> [success: bb14, unwind unreachable]; + _25 = copy _10; + _26 = AddWithOverflow(copy _10, const 1_usize); + assert(!move (_26.1: bool), "attempt to compute `{} + {}`, which would overflow", copy _10, const 1_usize) -> [success: bb14, unwind unreachable]; } bb14: { - _13 = move (_30.0: usize); + _10 = move (_26.0: usize); + StorageLive(_27); + _27 = (copy _25, copy _24); + _28 = Option::<(usize, &T)>::Some(move _27); + StorageDead(_27); + StorageDead(_26); + StorageDead(_25); + _29 = copy (((_28 as Some).0: (usize, &T)).0: usize); + _30 = copy (((_28 as Some).0: (usize, &T)).1: &T); StorageLive(_31); - _31 = (copy _29, copy _28); - _32 = Option::<(usize, &T)>::Some(move _31); - StorageDead(_31); - StorageDead(_30); - StorageDead(_29); - _33 = copy (((_32 as Some).0: (usize, &T)).0: usize); - _34 = copy (((_32 as Some).0: (usize, &T)).1: &T); - StorageLive(_35); - _35 = &_2; - StorageLive(_36); - _36 = (copy _33, copy _34); - _37 = >::call(move _35, move _36) -> [return: bb15, unwind unreachable]; + _31 = &_2; + StorageLive(_32); + _32 = (copy _29, copy _30); + _33 = >::call(move _31, move _32) -> [return: bb15, unwind unreachable]; } bb15: { - StorageDead(_36); - StorageDead(_35); StorageDead(_32); + StorageDead(_31); + StorageDead(_28); goto -> bb4; } } diff --git a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir index 42aa152ec99d..28b12cdf3675 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.enumerated_loop.PreCodegen.after.panic-unwind.mir @@ -4,22 +4,22 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { debug slice => _1; debug f => _2; let mut _0: (); - let mut _11: std::slice::Iter<'_, T>; + let mut _10: std::slice::Iter<'_, T>; + let mut _11: std::iter::Enumerate>; let mut _12: std::iter::Enumerate>; - let mut _13: std::iter::Enumerate>; - let mut _14: &mut std::iter::Enumerate>; - let mut _15: std::option::Option<(usize, &T)>; - let mut _16: isize; - let mut _19: &impl Fn(usize, &T); - let mut _20: (usize, &T); - let _21: (); + let mut _13: &mut std::iter::Enumerate>; + let mut _14: std::option::Option<(usize, &T)>; + let mut _15: isize; + let mut _18: &impl Fn(usize, &T); + let mut _19: (usize, &T); + let _20: (); scope 1 { - debug iter => _13; - let _17: usize; - let _18: &T; + debug iter => _12; + let _16: usize; + let _17: &T; scope 2 { - debug i => _17; - debug x => _18; + debug i => _16; + debug x => _17; } } scope 3 (inlined core::slice::::iter) { @@ -27,7 +27,6 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { let _3: usize; let mut _7: *mut T; let mut _8: *mut T; - let mut _10: *const T; scope 5 { let _6: std::ptr::NonNull; scope 6 { @@ -62,9 +61,10 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb0: { - StorageLive(_11); + StorageLive(_10); StorageLive(_3); StorageLive(_6); + StorageLive(_9); StorageLive(_4); _3 = PtrMetadata(copy _1); _4 = &raw const (*_1); @@ -72,7 +72,6 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { _5 = copy _4 as *const T (PtrToPtr); _6 = NonNull:: { pointer: copy _5 }; StorageDead(_5); - StorageLive(_9); switchInt(const ::IS_ZST) -> [0: bb1, otherwise: bb2]; } @@ -93,33 +92,30 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb3: { - StorageLive(_10); - _10 = copy _9; - _11 = std::slice::Iter::<'_, T> { ptr: copy _6, end_or_len: copy _10, _marker: const ZeroSized: PhantomData<&T> }; - StorageDead(_10); - StorageDead(_9); + _10 = std::slice::Iter::<'_, T> { ptr: copy _6, end_or_len: copy _9, _marker: const ZeroSized: PhantomData<&T> }; StorageDead(_4); + StorageDead(_9); StorageDead(_6); StorageDead(_3); - _12 = Enumerate::> { iter: copy _11, count: const 0_usize }; - StorageDead(_11); - StorageLive(_13); - _13 = copy _12; + _11 = Enumerate::> { iter: copy _10, count: const 0_usize }; + StorageDead(_10); + StorageLive(_12); + _12 = copy _11; goto -> bb4; } bb4: { - _14 = &mut _13; - _15 = > as Iterator>::next(move _14) -> [return: bb5, unwind: bb11]; + _13 = &mut _12; + _14 = > as Iterator>::next(move _13) -> [return: bb5, unwind: bb11]; } bb5: { - _16 = discriminant(_15); - switchInt(move _16) -> [0: bb6, 1: bb8, otherwise: bb10]; + _15 = discriminant(_14); + switchInt(move _15) -> [0: bb6, 1: bb8, otherwise: bb10]; } bb6: { - StorageDead(_13); + StorageDead(_12); drop(_2) -> [return: bb7, unwind continue]; } @@ -128,18 +124,18 @@ fn enumerated_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { } bb8: { - _17 = copy (((_15 as Some).0: (usize, &T)).0: usize); - _18 = copy (((_15 as Some).0: (usize, &T)).1: &T); + _16 = copy (((_14 as Some).0: (usize, &T)).0: usize); + _17 = copy (((_14 as Some).0: (usize, &T)).1: &T); + StorageLive(_18); + _18 = &_2; StorageLive(_19); - _19 = &_2; - StorageLive(_20); - _20 = copy ((_15 as Some).0: (usize, &T)); - _21 = >::call(move _19, move _20) -> [return: bb9, unwind: bb11]; + _19 = copy ((_14 as Some).0: (usize, &T)); + _20 = >::call(move _18, move _19) -> [return: bb9, unwind: bb11]; } bb9: { - StorageDead(_20); StorageDead(_19); + StorageDead(_18); goto -> bb4; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir index 0d65640ec9b1..4d0e3548e7d6 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-abort.mir @@ -4,31 +4,29 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { debug slice => _1; debug f => _2; let mut _0: (); - let mut _11: std::ptr::NonNull; - let mut _12: *const T; - let mut _26: std::option::Option<&T>; - let mut _28: &impl Fn(&T); - let mut _29: (&T,); - let _30: (); + let mut _22: std::option::Option<&T>; + let mut _24: &impl Fn(&T); + let mut _25: (&T,); + let _26: (); scope 1 { - debug ((iter: std::slice::Iter<'_, T>).0: std::ptr::NonNull) => _11; - debug ((iter: std::slice::Iter<'_, T>).1: *const T) => _12; + debug ((iter: std::slice::Iter<'_, T>).0: std::ptr::NonNull) => _6; + debug ((iter: std::slice::Iter<'_, T>).1: *const T) => _9; debug ((iter: std::slice::Iter<'_, T>).2: std::marker::PhantomData<&T>) => const ZeroSized: PhantomData<&T>; - let _27: &T; + let _23: &T; scope 2 { - debug x => _27; + debug x => _23; } scope 16 (inlined as Iterator>::next) { - let _13: std::ptr::NonNull; - let _15: std::ptr::NonNull; - let mut _18: bool; - let mut _21: std::ptr::NonNull; - let mut _23: usize; - let _25: &T; + let mut _6: std::ptr::NonNull; + let _10: std::ptr::NonNull; + let _12: std::ptr::NonNull; + let mut _15: bool; + let mut _19: usize; + let _21: &T; scope 17 { - let _14: *const T; + let _11: *const T; scope 18 { - let _22: usize; + let _18: usize; scope 19 { scope 22 (inlined #[track_caller] core::num::::unchecked_sub) { scope 23 (inlined core::ub_checks::check_language_ub) { @@ -44,21 +42,21 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } } scope 26 (inlined as PartialEq>::eq) { - let mut _16: *mut T; - let mut _17: *mut T; + let mut _13: *mut T; + let mut _14: *mut T; scope 27 (inlined NonNull::::as_ptr) { } scope 28 (inlined NonNull::::as_ptr) { } } scope 29 (inlined NonNull::::add) { - let mut _19: *const T; - let mut _20: *const T; + let mut _16: *const T; + let mut _17: *const T; scope 30 (inlined NonNull::::as_ptr) { } } scope 31 (inlined NonNull::::as_ref::<'_>) { - let _24: *const T; + let _20: *const T; scope 32 (inlined NonNull::::as_ptr) { } scope 33 (inlined std::ptr::mut_ptr::::cast_const) { @@ -73,9 +71,7 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { let _3: usize; let mut _7: *mut T; let mut _8: *mut T; - let mut _10: *const T; scope 5 { - let _6: std::ptr::NonNull; scope 6 { let _9: *const T; scope 7 { @@ -112,7 +108,6 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { _5 = copy _4 as *const T (PtrToPtr); _6 = NonNull:: { pointer: copy _5 }; StorageDead(_5); - StorageLive(_9); switchInt(const ::IS_ZST) -> [0: bb1, otherwise: bb2]; } @@ -133,88 +128,77 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb3: { - _10 = copy _9; - StorageDead(_9); StorageDead(_4); StorageDead(_3); - StorageLive(_11); - StorageLive(_12); - _11 = copy _6; - _12 = copy _10; goto -> bb4; } bb4: { - StorageLive(_26); - StorageLive(_13); - StorageLive(_14); StorageLive(_22); - StorageLive(_23); - StorageLive(_15); - StorageLive(_25); - _13 = copy _11; - _14 = copy _12; + StorageLive(_10); + StorageLive(_11); + StorageLive(_18); + StorageLive(_19); + StorageLive(_12); + StorageLive(_21); + _10 = copy _6; + _11 = copy _9; switchInt(const ::IS_ZST) -> [0: bb5, otherwise: bb8]; } bb5: { - StorageLive(_18); - _15 = copy _14 as std::ptr::NonNull (Transmute); - StorageLive(_16); - _16 = copy _13 as *mut T (Transmute); - StorageLive(_17); - _17 = copy _15 as *mut T (Transmute); - _18 = Eq(copy _16, copy _17); - StorageDead(_17); - StorageDead(_16); - switchInt(move _18) -> [0: bb6, otherwise: bb7]; + StorageLive(_15); + _12 = copy _11 as std::ptr::NonNull (Transmute); + StorageLive(_13); + _13 = copy _10 as *mut T (Transmute); + StorageLive(_14); + _14 = copy _12 as *mut T (Transmute); + _15 = Eq(copy _13, copy _14); + StorageDead(_14); + StorageDead(_13); + switchInt(move _15) -> [0: bb6, otherwise: bb7]; } bb6: { - StorageDead(_18); - StorageLive(_21); - StorageLive(_20); - StorageLive(_19); - _19 = copy _13 as *const T (Transmute); - _20 = Offset(copy _19, const 1_usize); - StorageDead(_19); - _21 = NonNull:: { pointer: copy _20 }; - StorageDead(_20); - _11 = move _21; - StorageDead(_21); + StorageDead(_15); + StorageLive(_17); + StorageLive(_16); + _16 = copy _10 as *const T (Transmute); + _17 = Offset(copy _16, const 1_usize); + StorageDead(_16); + _6 = NonNull:: { pointer: copy _17 }; + StorageDead(_17); goto -> bb13; } bb7: { - StorageDead(_18); - StorageDead(_25); StorageDead(_15); - StorageDead(_23); - StorageDead(_22); - StorageDead(_14); - StorageDead(_13); + StorageDead(_21); + StorageDead(_12); + StorageDead(_19); + StorageDead(_18); + StorageDead(_11); + StorageDead(_10); goto -> bb10; } bb8: { - _22 = copy _14 as usize (Transmute); - switchInt(copy _22) -> [0: bb9, otherwise: bb12]; + _18 = copy _11 as usize (Transmute); + switchInt(copy _18) -> [0: bb9, otherwise: bb12]; } bb9: { - StorageDead(_25); - StorageDead(_15); - StorageDead(_23); - StorageDead(_22); - StorageDead(_14); - StorageDead(_13); + StorageDead(_21); + StorageDead(_12); + StorageDead(_19); + StorageDead(_18); + StorageDead(_11); + StorageDead(_10); goto -> bb10; } bb10: { - StorageDead(_26); - StorageDead(_11); - StorageDead(_12); + StorageDead(_22); drop(_2) -> [return: bb11, unwind unreachable]; } @@ -223,35 +207,35 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb12: { - _23 = SubUnchecked(copy _22, const 1_usize); - _12 = copy _23 as *const T (Transmute); + _19 = SubUnchecked(copy _18, const 1_usize); + _9 = copy _19 as *const T (Transmute); goto -> bb13; } bb13: { + StorageLive(_20); + _20 = copy _10 as *const T (Transmute); + _21 = &(*_20); + StorageDead(_20); + _22 = Option::<&T>::Some(copy _21); + StorageDead(_21); + StorageDead(_12); + StorageDead(_19); + StorageDead(_18); + StorageDead(_11); + StorageDead(_10); + _23 = copy ((_22 as Some).0: &T); StorageLive(_24); - _24 = copy _13 as *const T (Transmute); - _25 = &(*_24); - StorageDead(_24); - _26 = Option::<&T>::Some(copy _25); - StorageDead(_25); - StorageDead(_15); - StorageDead(_23); - StorageDead(_22); - StorageDead(_14); - StorageDead(_13); - _27 = copy ((_26 as Some).0: &T); - StorageLive(_28); - _28 = &_2; - StorageLive(_29); - _29 = (copy _27,); - _30 = >::call(move _28, move _29) -> [return: bb14, unwind unreachable]; + _24 = &_2; + StorageLive(_25); + _25 = (copy _23,); + _26 = >::call(move _24, move _25) -> [return: bb14, unwind unreachable]; } bb14: { - StorageDead(_29); - StorageDead(_28); - StorageDead(_26); + StorageDead(_25); + StorageDead(_24); + StorageDead(_22); goto -> bb4; } } diff --git a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir index 02efb193474d..2b5d8c27d710 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.forward_loop.PreCodegen.after.panic-unwind.mir @@ -4,31 +4,29 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { debug slice => _1; debug f => _2; let mut _0: (); - let mut _11: std::ptr::NonNull; - let mut _12: *const T; - let mut _26: std::option::Option<&T>; - let mut _28: &impl Fn(&T); - let mut _29: (&T,); - let _30: (); + let mut _22: std::option::Option<&T>; + let mut _24: &impl Fn(&T); + let mut _25: (&T,); + let _26: (); scope 1 { - debug ((iter: std::slice::Iter<'_, T>).0: std::ptr::NonNull) => _11; - debug ((iter: std::slice::Iter<'_, T>).1: *const T) => _12; + debug ((iter: std::slice::Iter<'_, T>).0: std::ptr::NonNull) => _6; + debug ((iter: std::slice::Iter<'_, T>).1: *const T) => _9; debug ((iter: std::slice::Iter<'_, T>).2: std::marker::PhantomData<&T>) => const ZeroSized: PhantomData<&T>; - let _27: &T; + let _23: &T; scope 2 { - debug x => _27; + debug x => _23; } scope 16 (inlined as Iterator>::next) { - let _13: std::ptr::NonNull; - let _15: std::ptr::NonNull; - let mut _18: bool; - let mut _21: std::ptr::NonNull; - let mut _23: usize; - let _25: &T; + let mut _6: std::ptr::NonNull; + let _10: std::ptr::NonNull; + let _12: std::ptr::NonNull; + let mut _15: bool; + let mut _19: usize; + let _21: &T; scope 17 { - let _14: *const T; + let _11: *const T; scope 18 { - let _22: usize; + let _18: usize; scope 19 { scope 22 (inlined #[track_caller] core::num::::unchecked_sub) { scope 23 (inlined core::ub_checks::check_language_ub) { @@ -44,21 +42,21 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } } scope 26 (inlined as PartialEq>::eq) { - let mut _16: *mut T; - let mut _17: *mut T; + let mut _13: *mut T; + let mut _14: *mut T; scope 27 (inlined NonNull::::as_ptr) { } scope 28 (inlined NonNull::::as_ptr) { } } scope 29 (inlined NonNull::::add) { - let mut _19: *const T; - let mut _20: *const T; + let mut _16: *const T; + let mut _17: *const T; scope 30 (inlined NonNull::::as_ptr) { } } scope 31 (inlined NonNull::::as_ref::<'_>) { - let _24: *const T; + let _20: *const T; scope 32 (inlined NonNull::::as_ptr) { } scope 33 (inlined std::ptr::mut_ptr::::cast_const) { @@ -73,9 +71,7 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { let _3: usize; let mut _7: *mut T; let mut _8: *mut T; - let mut _10: *const T; scope 5 { - let _6: std::ptr::NonNull; scope 6 { let _9: *const T; scope 7 { @@ -112,7 +108,6 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { _5 = copy _4 as *const T (PtrToPtr); _6 = NonNull:: { pointer: copy _5 }; StorageDead(_5); - StorageLive(_9); switchInt(const ::IS_ZST) -> [0: bb1, otherwise: bb2]; } @@ -133,88 +128,77 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb3: { - _10 = copy _9; - StorageDead(_9); StorageDead(_4); StorageDead(_3); - StorageLive(_11); - StorageLive(_12); - _11 = copy _6; - _12 = copy _10; goto -> bb4; } bb4: { - StorageLive(_26); - StorageLive(_13); - StorageLive(_14); StorageLive(_22); - StorageLive(_23); - StorageLive(_15); - StorageLive(_25); - _13 = copy _11; - _14 = copy _12; + StorageLive(_10); + StorageLive(_11); + StorageLive(_18); + StorageLive(_19); + StorageLive(_12); + StorageLive(_21); + _10 = copy _6; + _11 = copy _9; switchInt(const ::IS_ZST) -> [0: bb5, otherwise: bb8]; } bb5: { - StorageLive(_18); - _15 = copy _14 as std::ptr::NonNull (Transmute); - StorageLive(_16); - _16 = copy _13 as *mut T (Transmute); - StorageLive(_17); - _17 = copy _15 as *mut T (Transmute); - _18 = Eq(copy _16, copy _17); - StorageDead(_17); - StorageDead(_16); - switchInt(move _18) -> [0: bb6, otherwise: bb7]; + StorageLive(_15); + _12 = copy _11 as std::ptr::NonNull (Transmute); + StorageLive(_13); + _13 = copy _10 as *mut T (Transmute); + StorageLive(_14); + _14 = copy _12 as *mut T (Transmute); + _15 = Eq(copy _13, copy _14); + StorageDead(_14); + StorageDead(_13); + switchInt(move _15) -> [0: bb6, otherwise: bb7]; } bb6: { - StorageDead(_18); - StorageLive(_21); - StorageLive(_20); - StorageLive(_19); - _19 = copy _13 as *const T (Transmute); - _20 = Offset(copy _19, const 1_usize); - StorageDead(_19); - _21 = NonNull:: { pointer: copy _20 }; - StorageDead(_20); - _11 = move _21; - StorageDead(_21); + StorageDead(_15); + StorageLive(_17); + StorageLive(_16); + _16 = copy _10 as *const T (Transmute); + _17 = Offset(copy _16, const 1_usize); + StorageDead(_16); + _6 = NonNull:: { pointer: copy _17 }; + StorageDead(_17); goto -> bb13; } bb7: { - StorageDead(_18); - StorageDead(_25); StorageDead(_15); - StorageDead(_23); - StorageDead(_22); - StorageDead(_14); - StorageDead(_13); + StorageDead(_21); + StorageDead(_12); + StorageDead(_19); + StorageDead(_18); + StorageDead(_11); + StorageDead(_10); goto -> bb10; } bb8: { - _22 = copy _14 as usize (Transmute); - switchInt(copy _22) -> [0: bb9, otherwise: bb12]; + _18 = copy _11 as usize (Transmute); + switchInt(copy _18) -> [0: bb9, otherwise: bb12]; } bb9: { - StorageDead(_25); - StorageDead(_15); - StorageDead(_23); - StorageDead(_22); - StorageDead(_14); - StorageDead(_13); + StorageDead(_21); + StorageDead(_12); + StorageDead(_19); + StorageDead(_18); + StorageDead(_11); + StorageDead(_10); goto -> bb10; } bb10: { - StorageDead(_26); - StorageDead(_11); - StorageDead(_12); + StorageDead(_22); drop(_2) -> [return: bb11, unwind continue]; } @@ -223,35 +207,35 @@ fn forward_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb12: { - _23 = SubUnchecked(copy _22, const 1_usize); - _12 = copy _23 as *const T (Transmute); + _19 = SubUnchecked(copy _18, const 1_usize); + _9 = copy _19 as *const T (Transmute); goto -> bb13; } bb13: { + StorageLive(_20); + _20 = copy _10 as *const T (Transmute); + _21 = &(*_20); + StorageDead(_20); + _22 = Option::<&T>::Some(copy _21); + StorageDead(_21); + StorageDead(_12); + StorageDead(_19); + StorageDead(_18); + StorageDead(_11); + StorageDead(_10); + _23 = copy ((_22 as Some).0: &T); StorageLive(_24); - _24 = copy _13 as *const T (Transmute); - _25 = &(*_24); - StorageDead(_24); - _26 = Option::<&T>::Some(copy _25); - StorageDead(_25); - StorageDead(_15); - StorageDead(_23); - StorageDead(_22); - StorageDead(_14); - StorageDead(_13); - _27 = copy ((_26 as Some).0: &T); - StorageLive(_28); - _28 = &_2; - StorageLive(_29); - _29 = (copy _27,); - _30 = >::call(move _28, move _29) -> [return: bb14, unwind: bb15]; + _24 = &_2; + StorageLive(_25); + _25 = (copy _23,); + _26 = >::call(move _24, move _25) -> [return: bb14, unwind: bb15]; } bb14: { - StorageDead(_29); - StorageDead(_28); - StorageDead(_26); + StorageDead(_25); + StorageDead(_24); + StorageDead(_22); goto -> bb4; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-abort.mir index 41e273151eca..145375990710 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-abort.mir @@ -5,28 +5,27 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { debug f => _2; let mut _0: (); let mut _3: usize; - let mut _4: usize; - let mut _9: std::option::Option; - let mut _11: bool; - let mut _13: &impl Fn(usize, &T); - let mut _14: (usize, &T); - let _15: (); + let mut _8: std::option::Option; + let mut _10: bool; + let mut _12: &impl Fn(usize, &T); + let mut _13: (usize, &T); + let _14: (); scope 1 { debug ((iter: std::ops::Range).0: usize) => _4; debug ((iter: std::ops::Range).1: usize) => _3; - let _10: usize; + let _9: usize; scope 2 { - debug i => _10; - let _12: &T; + debug i => _9; + let _11: &T; scope 3 { - debug x => _12; + debug x => _11; } } scope 5 (inlined iter::range::>::next) { scope 6 (inlined as iter::range::RangeIteratorImpl>::spec_next) { + let mut _4: usize; let mut _6: bool; let _7: usize; - let mut _8: usize; scope 7 { scope 9 (inlined ::forward_unchecked) { scope 10 (inlined #[track_caller] core::num::::unchecked_add) { @@ -48,13 +47,12 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { bb0: { _3 = PtrMetadata(copy _1); - StorageLive(_4); _4 = const 0_usize; goto -> bb1; } bb1: { - StorageLive(_9); + StorageLive(_8); StorageLive(_6); StorageLive(_5); _5 = copy _4; @@ -65,8 +63,7 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { bb2: { StorageDead(_6); - StorageDead(_9); - StorageDead(_4); + StorageDead(_8); drop(_2) -> [return: bb3, unwind unreachable]; } @@ -76,30 +73,27 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { bb4: { _7 = copy _4; - StorageLive(_8); - _8 = AddUnchecked(copy _7, const 1_usize); - _4 = move _8; - StorageDead(_8); - _9 = Option::::Some(copy _7); + _4 = AddUnchecked(copy _7, const 1_usize); + _8 = Option::::Some(copy _7); StorageDead(_6); - _10 = copy ((_9 as Some).0: usize); - _11 = Lt(copy _10, copy _3); - assert(move _11, "index out of bounds: the length is {} but the index is {}", copy _3, copy _10) -> [success: bb5, unwind unreachable]; + _9 = copy ((_8 as Some).0: usize); + _10 = Lt(copy _9, copy _3); + assert(move _10, "index out of bounds: the length is {} but the index is {}", copy _3, copy _9) -> [success: bb5, unwind unreachable]; } bb5: { - _12 = &(*_1)[_10]; + _11 = &(*_1)[_9]; + StorageLive(_12); + _12 = &_2; StorageLive(_13); - _13 = &_2; - StorageLive(_14); - _14 = (copy _10, copy _12); - _15 = >::call(move _13, move _14) -> [return: bb6, unwind unreachable]; + _13 = (copy _9, copy _11); + _14 = >::call(move _12, move _13) -> [return: bb6, unwind unreachable]; } bb6: { - StorageDead(_14); StorageDead(_13); - StorageDead(_9); + StorageDead(_12); + StorageDead(_8); goto -> bb1; } } diff --git a/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-unwind.mir index ec781c1480c7..8e573ef488fc 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.range_loop.PreCodegen.after.panic-unwind.mir @@ -5,28 +5,27 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { debug f => _2; let mut _0: (); let mut _3: usize; - let mut _4: usize; - let mut _9: std::option::Option; - let mut _11: bool; - let mut _13: &impl Fn(usize, &T); - let mut _14: (usize, &T); - let _15: (); + let mut _8: std::option::Option; + let mut _10: bool; + let mut _12: &impl Fn(usize, &T); + let mut _13: (usize, &T); + let _14: (); scope 1 { debug ((iter: std::ops::Range).0: usize) => _4; debug ((iter: std::ops::Range).1: usize) => _3; - let _10: usize; + let _9: usize; scope 2 { - debug i => _10; - let _12: &T; + debug i => _9; + let _11: &T; scope 3 { - debug x => _12; + debug x => _11; } } scope 5 (inlined iter::range::>::next) { scope 6 (inlined as iter::range::RangeIteratorImpl>::spec_next) { + let mut _4: usize; let mut _6: bool; let _7: usize; - let mut _8: usize; scope 7 { scope 9 (inlined ::forward_unchecked) { scope 10 (inlined #[track_caller] core::num::::unchecked_add) { @@ -48,13 +47,12 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { bb0: { _3 = PtrMetadata(copy _1); - StorageLive(_4); _4 = const 0_usize; goto -> bb1; } bb1: { - StorageLive(_9); + StorageLive(_8); StorageLive(_6); StorageLive(_5); _5 = copy _4; @@ -65,8 +63,7 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { bb2: { StorageDead(_6); - StorageDead(_9); - StorageDead(_4); + StorageDead(_8); drop(_2) -> [return: bb3, unwind continue]; } @@ -76,30 +73,27 @@ fn range_loop(_1: &[T], _2: impl Fn(usize, &T)) -> () { bb4: { _7 = copy _4; - StorageLive(_8); - _8 = AddUnchecked(copy _7, const 1_usize); - _4 = move _8; - StorageDead(_8); - _9 = Option::::Some(copy _7); + _4 = AddUnchecked(copy _7, const 1_usize); + _8 = Option::::Some(copy _7); StorageDead(_6); - _10 = copy ((_9 as Some).0: usize); - _11 = Lt(copy _10, copy _3); - assert(move _11, "index out of bounds: the length is {} but the index is {}", copy _3, copy _10) -> [success: bb5, unwind: bb7]; + _9 = copy ((_8 as Some).0: usize); + _10 = Lt(copy _9, copy _3); + assert(move _10, "index out of bounds: the length is {} but the index is {}", copy _3, copy _9) -> [success: bb5, unwind: bb7]; } bb5: { - _12 = &(*_1)[_10]; + _11 = &(*_1)[_9]; + StorageLive(_12); + _12 = &_2; StorageLive(_13); - _13 = &_2; - StorageLive(_14); - _14 = (copy _10, copy _12); - _15 = >::call(move _13, move _14) -> [return: bb6, unwind: bb7]; + _13 = (copy _9, copy _11); + _14 = >::call(move _12, move _13) -> [return: bb6, unwind: bb7]; } bb6: { - StorageDead(_14); StorageDead(_13); - StorageDead(_9); + StorageDead(_12); + StorageDead(_8); goto -> bb1; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir index ee638be7d7ac..0adf268d766d 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-abort.mir @@ -4,22 +4,22 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { debug slice => _1; debug f => _2; let mut _0: (); - let mut _11: std::slice::Iter<'_, T>; + let mut _10: std::slice::Iter<'_, T>; + let mut _11: std::iter::Rev>; let mut _12: std::iter::Rev>; - let mut _13: std::iter::Rev>; - let mut _15: std::option::Option<&T>; - let mut _16: isize; - let mut _18: &impl Fn(&T); - let mut _19: (&T,); - let _20: (); + let mut _14: std::option::Option<&T>; + let mut _15: isize; + let mut _17: &impl Fn(&T); + let mut _18: (&T,); + let _19: (); scope 1 { - debug iter => _13; - let _17: &T; + debug iter => _12; + let _16: &T; scope 2 { - debug x => _17; + debug x => _16; } scope 18 (inlined > as Iterator>::next) { - let mut _14: &mut std::slice::Iter<'_, T>; + let mut _13: &mut std::slice::Iter<'_, T>; } } scope 3 (inlined core::slice::::iter) { @@ -27,7 +27,6 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { let _3: usize; let mut _7: *mut T; let mut _8: *mut T; - let mut _10: *const T; scope 5 { let _6: std::ptr::NonNull; scope 6 { @@ -62,9 +61,10 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb0: { - StorageLive(_11); + StorageLive(_10); StorageLive(_3); StorageLive(_6); + StorageLive(_9); StorageLive(_4); _3 = PtrMetadata(copy _1); _4 = &raw const (*_1); @@ -72,7 +72,6 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { _5 = copy _4 as *const T (PtrToPtr); _6 = NonNull:: { pointer: copy _5 }; StorageDead(_5); - StorageLive(_9); switchInt(const ::IS_ZST) -> [0: bb1, otherwise: bb2]; } @@ -93,37 +92,34 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb3: { - StorageLive(_10); - _10 = copy _9; - _11 = std::slice::Iter::<'_, T> { ptr: copy _6, end_or_len: copy _10, _marker: const ZeroSized: PhantomData<&T> }; - StorageDead(_10); - StorageDead(_9); + _10 = std::slice::Iter::<'_, T> { ptr: copy _6, end_or_len: copy _9, _marker: const ZeroSized: PhantomData<&T> }; StorageDead(_4); + StorageDead(_9); StorageDead(_6); StorageDead(_3); - _12 = Rev::> { iter: copy _11 }; - StorageDead(_11); - StorageLive(_13); - _13 = copy _12; + _11 = Rev::> { iter: copy _10 }; + StorageDead(_10); + StorageLive(_12); + _12 = copy _11; goto -> bb4; } bb4: { - StorageLive(_15); StorageLive(_14); - _14 = &mut (_13.0: std::slice::Iter<'_, T>); - _15 = as DoubleEndedIterator>::next_back(move _14) -> [return: bb5, unwind unreachable]; + StorageLive(_13); + _13 = &mut (_12.0: std::slice::Iter<'_, T>); + _14 = as DoubleEndedIterator>::next_back(move _13) -> [return: bb5, unwind unreachable]; } bb5: { - StorageDead(_14); - _16 = discriminant(_15); - switchInt(move _16) -> [0: bb6, 1: bb8, otherwise: bb10]; + StorageDead(_13); + _15 = discriminant(_14); + switchInt(move _15) -> [0: bb6, 1: bb8, otherwise: bb10]; } bb6: { - StorageDead(_15); - StorageDead(_13); + StorageDead(_14); + StorageDead(_12); drop(_2) -> [return: bb7, unwind unreachable]; } @@ -132,18 +128,18 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb8: { - _17 = copy ((_15 as Some).0: &T); + _16 = copy ((_14 as Some).0: &T); + StorageLive(_17); + _17 = &_2; StorageLive(_18); - _18 = &_2; - StorageLive(_19); - _19 = (copy _17,); - _20 = >::call(move _18, move _19) -> [return: bb9, unwind unreachable]; + _18 = (copy _16,); + _19 = >::call(move _17, move _18) -> [return: bb9, unwind unreachable]; } bb9: { - StorageDead(_19); StorageDead(_18); - StorageDead(_15); + StorageDead(_17); + StorageDead(_14); goto -> bb4; } diff --git a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir index aee29d4d4fef..cb0d640d445b 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/slice_iter.reverse_loop.PreCodegen.after.panic-unwind.mir @@ -4,22 +4,22 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { debug slice => _1; debug f => _2; let mut _0: (); - let mut _11: std::slice::Iter<'_, T>; + let mut _10: std::slice::Iter<'_, T>; + let mut _11: std::iter::Rev>; let mut _12: std::iter::Rev>; - let mut _13: std::iter::Rev>; - let mut _15: std::option::Option<&T>; - let mut _16: isize; - let mut _18: &impl Fn(&T); - let mut _19: (&T,); - let _20: (); + let mut _14: std::option::Option<&T>; + let mut _15: isize; + let mut _17: &impl Fn(&T); + let mut _18: (&T,); + let _19: (); scope 1 { - debug iter => _13; - let _17: &T; + debug iter => _12; + let _16: &T; scope 2 { - debug x => _17; + debug x => _16; } scope 18 (inlined > as Iterator>::next) { - let mut _14: &mut std::slice::Iter<'_, T>; + let mut _13: &mut std::slice::Iter<'_, T>; } } scope 3 (inlined core::slice::::iter) { @@ -27,7 +27,6 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { let _3: usize; let mut _7: *mut T; let mut _8: *mut T; - let mut _10: *const T; scope 5 { let _6: std::ptr::NonNull; scope 6 { @@ -62,9 +61,10 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb0: { - StorageLive(_11); + StorageLive(_10); StorageLive(_3); StorageLive(_6); + StorageLive(_9); StorageLive(_4); _3 = PtrMetadata(copy _1); _4 = &raw const (*_1); @@ -72,7 +72,6 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { _5 = copy _4 as *const T (PtrToPtr); _6 = NonNull:: { pointer: copy _5 }; StorageDead(_5); - StorageLive(_9); switchInt(const ::IS_ZST) -> [0: bb1, otherwise: bb2]; } @@ -93,37 +92,34 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb3: { - StorageLive(_10); - _10 = copy _9; - _11 = std::slice::Iter::<'_, T> { ptr: copy _6, end_or_len: copy _10, _marker: const ZeroSized: PhantomData<&T> }; - StorageDead(_10); - StorageDead(_9); + _10 = std::slice::Iter::<'_, T> { ptr: copy _6, end_or_len: copy _9, _marker: const ZeroSized: PhantomData<&T> }; StorageDead(_4); + StorageDead(_9); StorageDead(_6); StorageDead(_3); - _12 = Rev::> { iter: copy _11 }; - StorageDead(_11); - StorageLive(_13); - _13 = copy _12; + _11 = Rev::> { iter: copy _10 }; + StorageDead(_10); + StorageLive(_12); + _12 = copy _11; goto -> bb4; } bb4: { - StorageLive(_15); StorageLive(_14); - _14 = &mut (_13.0: std::slice::Iter<'_, T>); - _15 = as DoubleEndedIterator>::next_back(move _14) -> [return: bb5, unwind: bb11]; + StorageLive(_13); + _13 = &mut (_12.0: std::slice::Iter<'_, T>); + _14 = as DoubleEndedIterator>::next_back(move _13) -> [return: bb5, unwind: bb11]; } bb5: { - StorageDead(_14); - _16 = discriminant(_15); - switchInt(move _16) -> [0: bb6, 1: bb8, otherwise: bb10]; + StorageDead(_13); + _15 = discriminant(_14); + switchInt(move _15) -> [0: bb6, 1: bb8, otherwise: bb10]; } bb6: { - StorageDead(_15); - StorageDead(_13); + StorageDead(_14); + StorageDead(_12); drop(_2) -> [return: bb7, unwind continue]; } @@ -132,18 +128,18 @@ fn reverse_loop(_1: &[T], _2: impl Fn(&T)) -> () { } bb8: { - _17 = copy ((_15 as Some).0: &T); + _16 = copy ((_14 as Some).0: &T); + StorageLive(_17); + _17 = &_2; StorageLive(_18); - _18 = &_2; - StorageLive(_19); - _19 = (copy _17,); - _20 = >::call(move _18, move _19) -> [return: bb9, unwind: bb11]; + _18 = (copy _16,); + _19 = >::call(move _17, move _18) -> [return: bb9, unwind: bb11]; } bb9: { - StorageDead(_19); StorageDead(_18); - StorageDead(_15); + StorageDead(_17); + StorageDead(_14); goto -> bb4; } diff --git a/tests/mir-opt/pre-codegen/tuple_ord.demo_ge_partial.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/tuple_ord.demo_ge_partial.PreCodegen.after.mir index c4d0e318b58d..29bfe962974c 100644 --- a/tests/mir-opt/pre-codegen/tuple_ord.demo_ge_partial.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/tuple_ord.demo_ge_partial.PreCodegen.after.mir @@ -7,7 +7,6 @@ fn demo_ge_partial(_1: &(f32, f32), _2: &(f32, f32)) -> bool { scope 1 (inlined std::cmp::impls::::ge) { scope 2 (inlined core::tuple::::ge) { let mut _7: std::ops::ControlFlow; - let _8: bool; scope 3 { } scope 4 (inlined std::cmp::impls::::__chaining_ge) { @@ -19,8 +18,8 @@ fn demo_ge_partial(_1: &(f32, f32), _2: &(f32, f32)) -> bool { } } scope 6 (inlined std::cmp::impls::::ge) { + let mut _8: f32; let mut _9: f32; - let mut _10: f32; } } } @@ -44,10 +43,7 @@ fn demo_ge_partial(_1: &(f32, f32), _2: &(f32, f32)) -> bool { StorageDead(_5); StorageDead(_4); StorageDead(_3); - StorageLive(_8); - _8 = copy ((_7 as Break).0: bool); - _0 = copy _8; - StorageDead(_8); + _0 = copy ((_7 as Break).0: bool); goto -> bb3; } @@ -55,13 +51,13 @@ fn demo_ge_partial(_1: &(f32, f32), _2: &(f32, f32)) -> bool { StorageDead(_5); StorageDead(_4); StorageDead(_3); + StorageLive(_8); + _8 = copy ((*_1).1: f32); StorageLive(_9); - _9 = copy ((*_1).1: f32); - StorageLive(_10); - _10 = copy ((*_2).1: f32); - _0 = Ge(move _9, move _10); - StorageDead(_10); + _9 = copy ((*_2).1: f32); + _0 = Ge(move _8, move _9); StorageDead(_9); + StorageDead(_8); goto -> bb3; } diff --git a/tests/mir-opt/pre-codegen/tuple_ord.demo_le_total.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/tuple_ord.demo_le_total.PreCodegen.after.mir index 44df8b279935..7678c92a1f0c 100644 --- a/tests/mir-opt/pre-codegen/tuple_ord.demo_le_total.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/tuple_ord.demo_le_total.PreCodegen.after.mir @@ -7,7 +7,6 @@ fn demo_le_total(_1: &(u16, i16), _2: &(u16, i16)) -> bool { scope 1 (inlined std::cmp::impls::::le) { scope 2 (inlined core::tuple::::le) { let mut _7: std::ops::ControlFlow; - let _8: bool; scope 3 { } scope 4 (inlined std::cmp::impls::::__chaining_le) { @@ -19,8 +18,8 @@ fn demo_le_total(_1: &(u16, i16), _2: &(u16, i16)) -> bool { } } scope 6 (inlined std::cmp::impls::::le) { + let mut _8: i16; let mut _9: i16; - let mut _10: i16; } } } @@ -44,10 +43,7 @@ fn demo_le_total(_1: &(u16, i16), _2: &(u16, i16)) -> bool { StorageDead(_5); StorageDead(_4); StorageDead(_3); - StorageLive(_8); - _8 = copy ((_7 as Break).0: bool); - _0 = copy _8; - StorageDead(_8); + _0 = copy ((_7 as Break).0: bool); goto -> bb3; } @@ -55,13 +51,13 @@ fn demo_le_total(_1: &(u16, i16), _2: &(u16, i16)) -> bool { StorageDead(_5); StorageDead(_4); StorageDead(_3); + StorageLive(_8); + _8 = copy ((*_1).1: i16); StorageLive(_9); - _9 = copy ((*_1).1: i16); - StorageLive(_10); - _10 = copy ((*_2).1: i16); - _0 = Le(move _9, move _10); - StorageDead(_10); + _9 = copy ((*_2).1: i16); + _0 = Le(move _8, move _9); StorageDead(_9); + StorageDead(_8); goto -> bb3; } From 19e5286c806978e094328559d172f9d7e4e8885f Mon Sep 17 00:00:00 2001 From: galileocap Date: Tue, 16 Sep 2025 19:20:39 -0300 Subject: [PATCH 572/682] Fix `unnecessary_unwrap` false negative when unwrapping a known value inside a macro call Fixes https://github.com/rust-lang/rust-clippy/issues/12295 changelog: [`unnecessary_unwrap`]: Fix false negative when unwrapping a known value inside a macro call --- clippy_lints/src/unwrap.rs | 1 + .../ui/checked_unwrap/simple_conditionals.rs | 22 ++++++++++++ .../checked_unwrap/simple_conditionals.stderr | 36 ++++++++++++++++++- 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index 490da4f1e037..34dfe5b6546f 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -292,6 +292,7 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { // Shouldn't lint when `expr` is in macro. if expr.span.in_external_macro(self.cx.tcx.sess.source_map()) { + walk_expr(self, expr); return; } // Skip checking inside closures since they are visited through `Unwrap::check_fn()` already. diff --git a/tests/ui/checked_unwrap/simple_conditionals.rs b/tests/ui/checked_unwrap/simple_conditionals.rs index 785b2473c053..bba264080b40 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.rs +++ b/tests/ui/checked_unwrap/simple_conditionals.rs @@ -273,6 +273,28 @@ const ISSUE14763: fn(Option) = |x| { } }; +fn issue12295() { + let option = Some(()); + + if option.is_some() { + println!("{:?}", option.unwrap()); + //~^ unnecessary_unwrap + } else { + println!("{:?}", option.unwrap()); + //~^ panicking_unwrap + } + + let result = Ok::<(), ()>(()); + + if result.is_ok() { + println!("{:?}", result.unwrap()); + //~^ unnecessary_unwrap + } else { + println!("{:?}", result.unwrap()); + //~^ panicking_unwrap + } +} + fn check_expect() { let x = Some(()); if x.is_some() { diff --git a/tests/ui/checked_unwrap/simple_conditionals.stderr b/tests/ui/checked_unwrap/simple_conditionals.stderr index 939b509d85c9..2007a8595413 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.stderr +++ b/tests/ui/checked_unwrap/simple_conditionals.stderr @@ -322,6 +322,40 @@ LL | if x.is_some() { LL | _ = x.unwrap(); | ^^^^^^^^^^ +error: called `unwrap` on `option` after checking its variant with `is_some` + --> tests/ui/checked_unwrap/simple_conditionals.rs:280:26 + | +LL | if option.is_some() { + | ------------------- help: try: `if let Some() = option` +LL | println!("{:?}", option.unwrap()); + | ^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:283:26 + | +LL | if option.is_some() { + | ---------------- because of this check +... +LL | println!("{:?}", option.unwrap()); + | ^^^^^^^^^^^^^^^ + +error: called `unwrap` on `result` after checking its variant with `is_ok` + --> tests/ui/checked_unwrap/simple_conditionals.rs:290:26 + | +LL | if result.is_ok() { + | ----------------- help: try: `if let Ok() = result` +LL | println!("{:?}", result.unwrap()); + | ^^^^^^^^^^^^^^^ + +error: this call to `unwrap()` will always panic + --> tests/ui/checked_unwrap/simple_conditionals.rs:293:26 + | +LL | if result.is_ok() { + | -------------- because of this check +... +LL | println!("{:?}", result.unwrap()); + | ^^^^^^^^^^^^^^^ + error: creating a shared reference to mutable static --> tests/ui/checked_unwrap/simple_conditionals.rs:183:12 | @@ -332,5 +366,5 @@ LL | if X.is_some() { = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives = note: `#[deny(static_mut_refs)]` (part of `#[deny(rust_2024_compatibility)]`) on by default -error: aborting due to 36 previous errors +error: aborting due to 40 previous errors From 53b91ea87fe4b430c33bd22dfdaaa6289bf9466a Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sun, 14 Sep 2025 22:29:04 +0000 Subject: [PATCH 573/682] Remove Rvalue::Len. --- compiler/rustc_borrowck/src/lib.rs | 3 +- .../src/polonius/legacy/loan_invalidations.rs | 9 +- compiler/rustc_borrowck/src/type_check/mod.rs | 2 - compiler/rustc_codegen_cranelift/src/base.rs | 6 -- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 25 +----- .../src/check_consts/check.rs | 3 +- .../src/check_consts/qualifs.rs | 4 +- .../src/check_consts/resolver.rs | 1 - .../rustc_const_eval/src/interpret/step.rs | 8 +- compiler/rustc_middle/src/mir/pretty.rs | 1 - compiler/rustc_middle/src/mir/statement.rs | 2 - compiler/rustc_middle/src/mir/syntax.rs | 10 --- compiler/rustc_middle/src/mir/visit.rs | 8 -- .../src/builder/custom/parse/instruction.rs | 6 +- .../src/builder/expr/as_place.rs | 2 +- .../src/builder/matches/test.rs | 3 +- .../src/impls/borrowed_locals.rs | 1 - .../src/move_paths/builder.rs | 1 - .../src/dataflow_const_prop.rs | 38 ++++----- compiler/rustc_mir_transform/src/gvn.rs | 77 +++++------------ .../src/known_panics_lint.rs | 15 ---- .../rustc_mir_transform/src/promote_consts.rs | 4 +- compiler/rustc_mir_transform/src/validate.rs | 8 -- .../src/unstable/convert/stable/mir.rs | 1 - compiler/rustc_span/src/symbol.rs | 1 + library/core/src/intrinsics/mir.rs | 8 +- .../clippy_utils/src/qualify_min_const_fn.rs | 2 +- .../custom/arrays.arrays.built.after.mir | 10 ++- tests/mir-opt/building/custom/arrays.rs | 4 +- ...n.DataflowConstProp.32bit.panic-abort.diff | 77 +++++++++++++++++ ....DataflowConstProp.32bit.panic-unwind.diff | 77 +++++++++++++++++ ...n.DataflowConstProp.64bit.panic-abort.diff | 77 +++++++++++++++++ ....DataflowConstProp.64bit.panic-unwind.diff | 77 +++++++++++++++++ .../mir-opt/dataflow-const-prop/slice_len.rs | 34 ++++++++ .../gvn.array_len.GVN.panic-abort.diff | 6 +- .../gvn.array_len.GVN.panic-unwind.diff | 6 +- ...implify-after-simplifycfg.panic-abort.diff | 71 ++++++++++++++++ ...mplify-after-simplifycfg.panic-unwind.diff | 71 ++++++++++++++++ .../mir-opt/instsimplify/combine_array_len.rs | 15 ++++ ...implifyComparisonIntegral.panic-abort.diff | 32 +++---- ...mplifyComparisonIntegral.panic-unwind.diff | 32 +++---- ...ray_len.array_len_raw.GVN.panic-abort.diff | 6 +- ...ay_len.array_len_raw.GVN.panic-unwind.diff | 6 +- ...en.array_len_reborrow.GVN.panic-abort.diff | 6 +- ...n.array_len_reborrow.GVN.panic-unwind.diff | 6 +- ...e_prop.debuginfo.ReferencePropagation.diff | 85 ++++++++++--------- 46 files changed, 656 insertions(+), 291 deletions(-) create mode 100644 tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-abort.diff create mode 100644 tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-unwind.diff create mode 100644 tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-abort.diff create mode 100644 tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-unwind.diff create mode 100644 tests/mir-opt/dataflow-const-prop/slice_len.rs create mode 100644 tests/mir-opt/instsimplify/combine_array_len.norm2.InstSimplify-after-simplifycfg.panic-abort.diff create mode 100644 tests/mir-opt/instsimplify/combine_array_len.norm2.InstSimplify-after-simplifycfg.panic-unwind.diff create mode 100644 tests/mir-opt/instsimplify/combine_array_len.rs diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 5d2dda8b0e7c..f196bd04d0e8 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1557,9 +1557,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { ); } - &(Rvalue::Len(place) | Rvalue::Discriminant(place)) => { + &Rvalue::Discriminant(place) => { let af = match *rvalue { - Rvalue::Len(..) => Some(ArtificialField::ArrayLength), Rvalue::Discriminant(..) => None, _ => unreachable!(), }; diff --git a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs index 99dd0b2dd466..c2ad6fcb4b79 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs @@ -306,16 +306,11 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> { self.consume_operand(location, op); } - &(Rvalue::Len(place) | Rvalue::Discriminant(place)) => { - let af = match rvalue { - Rvalue::Len(..) => Some(ArtificialField::ArrayLength), - Rvalue::Discriminant(..) => None, - _ => unreachable!(), - }; + &Rvalue::Discriminant(place) => { self.access_place( location, place, - (Shallow(af), Read(ReadKind::Copy)), + (Shallow(None), Read(ReadKind::Copy)), LocalMutationIsAllowed::No, ); } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 8c5447fe1e9e..dcedf0b15322 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1631,7 +1631,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { | Rvalue::BinaryOp(..) | Rvalue::RawPtr(..) | Rvalue::ThreadLocalRef(..) - | Rvalue::Len(..) | Rvalue::Discriminant(..) | Rvalue::NullaryOp(NullOp::OffsetOf(..), _) => {} } @@ -2201,7 +2200,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { | Rvalue::Repeat(..) | Rvalue::Ref(..) | Rvalue::RawPtr(..) - | Rvalue::Len(..) | Rvalue::Cast(..) | Rvalue::ShallowInitBox(..) | Rvalue::BinaryOp(..) diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 3a28dd7e73cb..41e11e1de616 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -834,12 +834,6 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt: fx.bcx.ins().nop(); } } - Rvalue::Len(place) => { - let place = codegen_place(fx, place); - let usize_layout = fx.layout_of(fx.tcx.types.usize); - let len = codegen_array_len(fx, place); - lval.write_cvalue(fx, CValue::by_val(len, usize_layout)); - } Rvalue::ShallowInitBox(ref operand, content_ty) => { let content_ty = fx.monomorphize(content_ty); let box_layout = fx.layout_of(Ty::new_box(fx.tcx, content_ty)); diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index f8755874014d..c30e6a505058 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -7,9 +7,9 @@ use rustc_middle::{bug, mir, span_bug}; use rustc_session::config::OptLevel; use tracing::{debug, instrument}; +use super::FunctionCx; use super::operand::{OperandRef, OperandRefBuilder, OperandValue}; use super::place::{PlaceRef, PlaceValue, codegen_tag_value}; -use super::{FunctionCx, LocalRef}; use crate::common::{IntPredicate, TypeKind}; use crate::traits::*; use crate::{MemFlags, base}; @@ -510,14 +510,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.codegen_place_to_pointer(bx, place, mk_ptr) } - mir::Rvalue::Len(place) => { - let size = self.evaluate_array_len(bx, place); - OperandRef { - val: OperandValue::Immediate(size), - layout: bx.cx().layout_of(bx.tcx().types.usize), - } - } - mir::Rvalue::BinaryOp(op_with_overflow, box (ref lhs, ref rhs)) if let Some(op) = op_with_overflow.overflowing_to_wrapping() => { @@ -749,21 +741,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - fn evaluate_array_len(&mut self, bx: &mut Bx, place: mir::Place<'tcx>) -> Bx::Value { - // ZST are passed as operands and require special handling - // because codegen_place() panics if Local is operand. - if let Some(index) = place.as_local() - && let LocalRef::Operand(op) = self.locals[index] - && let ty::Array(_, n) = op.layout.ty.kind() - { - let n = n.try_to_target_usize(bx.tcx()).expect("expected monomorphic const in codegen"); - return bx.cx().const_usize(n); - } - // use common size calculation for non zero-sized types - let cg_value = self.codegen_place(bx, place.as_ref()); - cg_value.len(bx.cx()) - } - /// Codegen an `Rvalue::RawPtr` or `Rvalue::Ref` fn codegen_place_to_pointer( &mut self, diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index ca707b50d50d..3397bd9a68e4 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -573,8 +573,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { Rvalue::Use(_) | Rvalue::CopyForDeref(..) | Rvalue::Repeat(..) - | Rvalue::Discriminant(..) - | Rvalue::Len(_) => {} + | Rvalue::Discriminant(..) => {} Rvalue::Aggregate(kind, ..) => { if let AggregateKind::Coroutine(def_id, ..) = kind.as_ref() diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs index faf41f1658b7..34d1fdd8c869 100644 --- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs @@ -232,9 +232,7 @@ where Q::in_any_value_of_ty(cx, rvalue.ty(cx.body, cx.tcx)) } - Rvalue::Discriminant(place) | Rvalue::Len(place) => { - in_place::(cx, in_local, place.as_ref()) - } + Rvalue::Discriminant(place) => in_place::(cx, in_local, place.as_ref()), Rvalue::CopyForDeref(place) => in_place::(cx, in_local, place.as_ref()), diff --git a/compiler/rustc_const_eval/src/check_consts/resolver.rs b/compiler/rustc_const_eval/src/check_consts/resolver.rs index d98e5027e4d6..664dda8701a6 100644 --- a/compiler/rustc_const_eval/src/check_consts/resolver.rs +++ b/compiler/rustc_const_eval/src/check_consts/resolver.rs @@ -197,7 +197,6 @@ where | mir::Rvalue::CopyForDeref(..) | mir::Rvalue::ThreadLocalRef(..) | mir::Rvalue::Repeat(..) - | mir::Rvalue::Len(..) | mir::Rvalue::BinaryOp(..) | mir::Rvalue::NullaryOp(..) | mir::Rvalue::UnaryOp(..) diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 46950d60f8c7..923e00ad4cf1 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -17,7 +17,7 @@ use tracing::{info, instrument, trace}; use super::{ FnArg, FnVal, ImmTy, Immediate, InterpCx, InterpResult, Machine, MemPlaceMeta, PlaceTy, - Projectable, Scalar, interp_ok, throw_ub, throw_unsup_format, + Projectable, interp_ok, throw_ub, throw_unsup_format, }; use crate::interpret::EnteredTraceSpan; use crate::{enter_trace_span, util}; @@ -225,12 +225,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.write_repeat(operand, &dest)?; } - Len(place) => { - let src = self.eval_place(place)?; - let len = src.len(self)?; - self.write_scalar(Scalar::from_target_usize(len, self), &dest)?; - } - Ref(_, borrow_kind, place) => { let src = self.eval_place(place)?; let place = self.force_allocation(&src)?; diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index f9d0a5f0a3b6..96148fd5b926 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1062,7 +1062,6 @@ impl<'tcx> Debug for Rvalue<'tcx> { pretty_print_const(b, fmt, false)?; write!(fmt, "]") } - Len(ref a) => write!(fmt, "Len({a:?})"), Cast(ref kind, ref place, ref ty) => { with_no_trimmed_paths!(write!(fmt, "{place:?} as {ty} ({kind:?})")) } diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index ec2a8e86077d..28294b47e90f 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -697,7 +697,6 @@ impl<'tcx> Rvalue<'tcx> { | Rvalue::Ref(_, _, _) | Rvalue::ThreadLocalRef(_) | Rvalue::RawPtr(_, _) - | Rvalue::Len(_) | Rvalue::Cast( CastKind::IntToInt | CastKind::FloatToInt @@ -739,7 +738,6 @@ impl<'tcx> Rvalue<'tcx> { let place_ty = place.ty(local_decls, tcx).ty; Ty::new_ptr(tcx, place_ty, kind.to_mutbl_lossy()) } - Rvalue::Len(..) => tcx.types.usize, Rvalue::Cast(.., ty) => ty, Rvalue::BinaryOp(op, box (ref lhs, ref rhs)) => { let lhs_ty = lhs.ty(local_decls, tcx); diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index d402ea4b04f9..e6c8512564ed 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1407,16 +1407,6 @@ pub enum Rvalue<'tcx> { /// model. RawPtr(RawPtrKind, Place<'tcx>), - /// Yields the length of the place, as a `usize`. - /// - /// If the type of the place is an array, this is the array length. For slices (`[T]`, not - /// `&[T]`) this accesses the place's metadata to determine the length. This rvalue is - /// ill-formed for places of other types. - /// - /// This cannot be a `UnOp(PtrMetadata, _)` because that expects a value, and we only - /// have a place, and `UnOp(PtrMetadata, RawPtr(place))` is not a thing. - Len(Place<'tcx>), - /// Performs essentially all of the casts that can be performed via `as`. /// /// This allows for casts from/to a variety of types. diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index b498b7b8912c..07a15b3cd18a 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -717,14 +717,6 @@ macro_rules! make_mir_visitor { self.visit_place(path, ctx, location); } - Rvalue::Len(path) => { - self.visit_place( - path, - PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect), - location - ); - } - Rvalue::Cast(_cast_kind, operand, ty) => { self.visit_operand(operand, location); self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); diff --git a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs index 41d3aefcbe6b..54490e005090 100644 --- a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs @@ -229,6 +229,11 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { let source = self.parse_operand(args[0])?; Ok(Rvalue::Cast(CastKind::PtrToPtr, source, expr.ty)) }, + @call(mir_cast_unsize, args) => { + let source = self.parse_operand(args[0])?; + let kind = CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, CoercionSource::AsCast); + Ok(Rvalue::Cast(kind, source, expr.ty)) + }, @call(mir_checked, args) => { parse_by_kind!(self, args[0], _, "binary op", ExprKind::Binary { op, lhs, rhs } => { @@ -247,7 +252,6 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { let offset = self.parse_operand(args[1])?; Ok(Rvalue::BinaryOp(BinOp::Offset, Box::new((ptr, offset)))) }, - @call(mir_len, args) => Ok(Rvalue::Len(self.parse_place(args[0])?)), @call(mir_ptr_metadata, args) => Ok(Rvalue::UnaryOp(UnOp::PtrMetadata, self.parse_operand(args[0])?)), @call(mir_copy_for_deref, args) => Ok(Rvalue::CopyForDeref(self.parse_place(args[0])?)), ExprKind::Borrow { borrow_kind, arg } => Ok( diff --git a/compiler/rustc_mir_build/src/builder/expr/as_place.rs b/compiler/rustc_mir_build/src/builder/expr/as_place.rs index 7c851ec465bd..5a6bd2f413c2 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_place.rs @@ -663,7 +663,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// /// For arrays it'll be `Operand::Constant` with the actual length; /// For slices it'll be `Operand::Move` of a local using `PtrMetadata`. - fn len_of_slice_or_array( + pub(in crate::builder) fn len_of_slice_or_array( &mut self, block: BasicBlock, place: Place<'tcx>, diff --git a/compiler/rustc_mir_build/src/builder/matches/test.rs b/compiler/rustc_mir_build/src/builder/matches/test.rs index d03794fe2d58..1b6d96e49f0c 100644 --- a/compiler/rustc_mir_build/src/builder/matches/test.rs +++ b/compiler/rustc_mir_build/src/builder/matches/test.rs @@ -309,7 +309,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let actual = self.temp(usize_ty, test.span); // actual = len(place) - self.cfg.push_assign(block, source_info, actual, Rvalue::Len(place)); + let length_op = self.len_of_slice_or_array(block, place, test.span, source_info); + self.cfg.push_assign(block, source_info, actual, Rvalue::Use(length_op)); // expected = let expected = self.push_usize(block, source_info, len); diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index 9abb83434321..a4e4e30a8bb6 100644 --- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs @@ -91,7 +91,6 @@ where | Rvalue::Use(..) | Rvalue::ThreadLocalRef(..) | Rvalue::Repeat(..) - | Rvalue::Len(..) | Rvalue::BinaryOp(..) | Rvalue::NullaryOp(..) | Rvalue::UnaryOp(..) diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 48718cad597a..f38bb4458240 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -413,7 +413,6 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> { Rvalue::Ref(..) | Rvalue::RawPtr(..) | Rvalue::Discriminant(..) - | Rvalue::Len(..) | Rvalue::NullaryOp( NullOp::SizeOf | NullOp::AlignOf diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index fe53de31f758..5c984984d3cc 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -412,18 +412,6 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { state: &mut State>, ) -> ValueOrPlace> { let val = match rvalue { - Rvalue::Len(place) => { - let place_ty = place.ty(self.local_decls, self.tcx); - if let ty::Array(_, len) = place_ty.ty.kind() { - Const::Ty(self.tcx.types.usize, *len) - .try_eval_scalar(self.tcx, self.typing_env) - .map_or(FlatSet::Top, FlatSet::Elem) - } else if let [ProjectionElem::Deref] = place.projection[..] { - state.get_len(place.local.into(), &self.map) - } else { - FlatSet::Top - } - } Rvalue::Cast(CastKind::IntToInt | CastKind::IntToFloat, operand, ty) => { let Ok(layout) = self.tcx.layout_of(self.typing_env.as_query_input(*ty)) else { return ValueOrPlace::Value(FlatSet::Top); @@ -465,15 +453,23 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { let (val, _overflow) = self.binary_op(state, *op, left, right); val } - Rvalue::UnaryOp(op, operand) => match self.eval_operand(operand, state) { - FlatSet::Elem(value) => self - .ecx - .unary_op(*op, &value) - .discard_err() - .map_or(FlatSet::Top, |val| self.wrap_immediate(*val)), - FlatSet::Bottom => FlatSet::Bottom, - FlatSet::Top => FlatSet::Top, - }, + Rvalue::UnaryOp(op, operand) => { + if let UnOp::PtrMetadata = op + && let Some(place) = operand.place() + && let Some(len) = self.map.find_len(place.as_ref()) + { + return ValueOrPlace::Place(len); + } + match self.eval_operand(operand, state) { + FlatSet::Elem(value) => self + .ecx + .unary_op(*op, &value) + .discard_err() + .map_or(FlatSet::Top, |val| self.wrap_immediate(*val)), + FlatSet::Bottom => FlatSet::Bottom, + FlatSet::Top => FlatSet::Top, + } + } Rvalue::NullaryOp(null_op, ty) => { let Ok(layout) = self.tcx.layout_of(self.typing_env.as_query_input(*ty)) else { return ValueOrPlace::Value(FlatSet::Top); diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index bf6aa800d20f..30e68a3f6505 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -200,8 +200,6 @@ enum Value<'tcx> { Projection(VnIndex, ProjectionElem), /// Discriminant of the given value. Discriminant(VnIndex), - /// Length of an array or slice. - Len(VnIndex), // Operations. NullaryOp(NullOp<'tcx>, Ty<'tcx>), @@ -477,11 +475,6 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { self.ecx.discriminant_for_variant(base.layout.ty, variant).discard_err()?; discr_value.into() } - Len(slice) => { - let slice = self.evaluated[slice].as_ref()?; - let len = slice.len(&self.ecx).discard_err()?; - ImmTy::from_uint(len, ty).into() - } NullaryOp(null_op, arg_ty) => { let arg_layout = self.ecx.layout_of(arg_ty).ok()?; if let NullOp::SizeOf | NullOp::AlignOf = null_op @@ -841,7 +834,6 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { } // Operations. - Rvalue::Len(ref mut place) => return self.simplify_len(place, location), Rvalue::Cast(ref mut kind, ref mut value, to) => { return self.simplify_cast(kind, value, to, location); } @@ -1049,7 +1041,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { if op == UnOp::PtrMetadata { let mut was_updated = false; loop { - match self.get(arg_index) { + arg_index = match self.get(arg_index) { // Pointer casts that preserve metadata, such as // `*const [i32]` <-> `*mut [i32]` <-> `*mut [f32]`. // It's critical that this not eliminate cases like @@ -1061,9 +1053,19 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { Value::Cast { kind: CastKind::PtrToPtr, value: inner } if self.pointers_have_same_metadata(self.ty(*inner), arg_ty) => { - arg_index = *inner; - was_updated = true; - continue; + *inner + } + + // We have an unsizing cast, which assigns the length to wide pointer metadata. + Value::Cast { + kind: CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _), + value: from, + } if let Some(from) = self.ty(*from).builtin_deref(true) + && let ty::Array(_, len) = from.kind() + && let Some(to) = self.ty(arg_index).builtin_deref(true) + && let ty::Slice(..) = to.kind() => + { + return Some(self.insert_constant(Const::Ty(self.tcx.types.usize, *len))); } // `&mut *p`, `&raw *p`, etc don't change metadata. @@ -1072,18 +1074,16 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { place.as_ref() && let Some(local_index) = self.locals[local] => { - arg_index = local_index; - was_updated = true; - continue; + local_index } - _ => { - if was_updated && let Some(op) = self.try_as_operand(arg_index, location) { - *arg_op = op; - } - break; - } - } + _ => break, + }; + was_updated = true; + } + + if was_updated && let Some(op) = self.try_as_operand(arg_index, location) { + *arg_op = op; } } @@ -1407,39 +1407,6 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { Some(self.insert(to, Value::Cast { kind, value })) } - fn simplify_len(&mut self, place: &mut Place<'tcx>, location: Location) -> Option { - // Trivial case: we are fetching a statically known length. - let place_ty = place.ty(self.local_decls, self.tcx).ty; - if let ty::Array(_, len) = place_ty.kind() { - return Some(self.insert_constant(Const::Ty(self.tcx.types.usize, *len))); - } - - let mut inner = self.simplify_place_value(place, location)?; - - // The length information is stored in the wide pointer. - // Reborrowing copies length information from one pointer to the other. - while let Value::Address { place: borrowed, .. } = self.get(inner) - && let [PlaceElem::Deref] = borrowed.projection[..] - && let Some(borrowed) = self.locals[borrowed.local] - { - inner = borrowed; - } - - // We have an unsizing cast, which assigns the length to wide pointer metadata. - if let Value::Cast { kind, value: from } = self.get(inner) - && let CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize, _) = kind - && let Some(from) = self.ty(*from).builtin_deref(true) - && let ty::Array(_, len) = from.kind() - && let Some(to) = self.ty(inner).builtin_deref(true) - && let ty::Slice(..) = to.kind() - { - return Some(self.insert_constant(Const::Ty(self.tcx.types.usize, *len))); - } - - // Fallback: a symbolic `Len`. - Some(self.insert(self.tcx.types.usize, Value::Len(inner))) - } - fn pointers_have_same_metadata(&self, left_ptr_ty: Ty<'tcx>, right_ptr_ty: Ty<'tcx>) -> bool { let left_meta_ty = left_ptr_ty.pointee_metadata_ty_or_projection(self.tcx); let right_meta_ty = right_ptr_ty.pointee_metadata_ty_or_projection(self.tcx); diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index 481c79419092..aaacc5866a2a 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -441,7 +441,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { | Rvalue::Use(..) | Rvalue::CopyForDeref(..) | Rvalue::Repeat(..) - | Rvalue::Len(..) | Rvalue::Cast(..) | Rvalue::ShallowInitBox(..) | Rvalue::Discriminant(..) @@ -604,20 +603,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { return None; } - Len(place) => { - let len = if let ty::Array(_, n) = place.ty(self.local_decls(), self.tcx).ty.kind() - { - n.try_to_target_usize(self.tcx)? - } else { - match self.get_const(place)? { - Value::Immediate(src) => src.len(&self.ecx).discard_err()?, - Value::Aggregate { fields, .. } => fields.len() as u64, - Value::Uninit => return None, - } - }; - ImmTy::from_scalar(Scalar::from_target_usize(len, self), layout).into() - } - Ref(..) | RawPtr(..) => return None, NullaryOp(ref null_op, ty) => { diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 9ea2eb4f25d9..a0b0c8c990f3 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -437,9 +437,7 @@ impl<'tcx> Validator<'_, 'tcx> { self.validate_operand(op)? } - Rvalue::Discriminant(place) | Rvalue::Len(place) => { - self.validate_place(place.as_ref())? - } + Rvalue::Discriminant(place) => self.validate_place(place.as_ref())?, Rvalue::ThreadLocalRef(_) => return Err(Unpromotable), diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 99e4782e4700..c8a9a88dc3fe 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -1064,14 +1064,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } Rvalue::Ref(..) => {} - Rvalue::Len(p) => { - let pty = p.ty(&self.body.local_decls, self.tcx).ty; - check_kinds!( - pty, - "Cannot compute length of non-array type {:?}", - ty::Array(..) | ty::Slice(..) - ); - } Rvalue::BinaryOp(op, vals) => { use BinOp::*; let a = vals.0.ty(&self.body.local_decls, self.tcx); diff --git a/compiler/rustc_public/src/unstable/convert/stable/mir.rs b/compiler/rustc_public/src/unstable/convert/stable/mir.rs index be8ee80bed3c..b10af6526ead 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/mir.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/mir.rs @@ -215,7 +215,6 @@ impl<'tcx> Stable<'tcx> for mir::Rvalue<'tcx> { mutability.stable(tables, cx), place.stable(tables, cx), ), - Len(place) => crate::mir::Rvalue::Len(place.stable(tables, cx)), Cast(cast_kind, op, ty) => crate::mir::Rvalue::Cast( cast_kind.stable(tables, cx), op.stable(tables, cx), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index cdb0b5b58da6..16b8b46f97cf 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1413,6 +1413,7 @@ symbols! { mir_call, mir_cast_ptr_to_ptr, mir_cast_transmute, + mir_cast_unsize, mir_checked, mir_copy_for_deref, mir_debuginfo, diff --git a/library/core/src/intrinsics/mir.rs b/library/core/src/intrinsics/mir.rs index 55dcf7cd47e9..c4bd10e606aa 100644 --- a/library/core/src/intrinsics/mir.rs +++ b/library/core/src/intrinsics/mir.rs @@ -401,7 +401,6 @@ define!("mir_storage_dead", fn StorageDead(local: T)); define!("mir_assume", fn Assume(operand: bool)); define!("mir_deinit", fn Deinit(place: T)); define!("mir_checked", fn Checked(binop: T) -> (T, bool)); -define!("mir_len", fn Len(place: T) -> usize); define!( "mir_ptr_metadata", fn PtrMetadata(place: *const P) ->

( + &self, + tcx: DbInterner<'db>, + value: impl Upcast, P>, + ) -> Obligation<'db, P> { + Obligation::with_depth(tcx, self.cause.clone(), self.recursion_depth, self.param_env, value) + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/type_variable.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/type_variable.rs new file mode 100644 index 000000000000..5217308af473 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/type_variable.rs @@ -0,0 +1,268 @@ +//! Storage for type variables for the infer context the next-trait-solver. + +use std::cmp; +use std::marker::PhantomData; +use std::ops::Range; + +use ena::snapshot_vec as sv; +use ena::undo_log::Rollback; +use ena::unify as ut; +use rustc_index::IndexVec; +use rustc_type_ir::TyVid; +use rustc_type_ir::UniverseIndex; +use rustc_type_ir::inherent::Ty as _; +use tracing::debug; + +use crate::next_solver::SolverDefId; +use crate::next_solver::Ty; +use crate::next_solver::infer::InferCtxtUndoLogs; + +impl<'db> Rollback>>> for TypeVariableStorage<'db> { + fn reverse(&mut self, undo: sv::UndoLog>>) { + self.eq_relations.reverse(undo) + } +} + +#[derive(Clone, Default)] +pub(crate) struct TypeVariableStorage<'db> { + /// The origins of each type variable. + values: IndexVec, + /// Two variables are unified in `eq_relations` when we have a + /// constraint `?X == ?Y`. This table also stores, for each key, + /// the known value. + eq_relations: ut::UnificationTableStorage>, +} + +pub(crate) struct TypeVariableTable<'a, 'db> { + storage: &'a mut TypeVariableStorage<'db>, + + undo_log: &'a mut InferCtxtUndoLogs<'db>, +} + +#[derive(Copy, Clone, Debug)] +pub struct TypeVariableOrigin { + /// `DefId` of the type parameter this was instantiated for, if any. + /// + /// This should only be used for diagnostics. + pub param_def_id: Option, +} + +#[derive(Clone)] +pub(crate) struct TypeVariableData { + origin: TypeVariableOrigin, +} + +#[derive(Clone, Debug)] +pub(crate) enum TypeVariableValue<'db> { + Known { value: Ty<'db> }, + Unknown { universe: UniverseIndex }, +} + +impl<'db> TypeVariableValue<'db> { + /// If this value is known, returns the type it is known to be. + /// Otherwise, `None`. + pub(crate) fn known(&self) -> Option> { + match self { + TypeVariableValue::Unknown { .. } => None, + TypeVariableValue::Known { value } => Some(*value), + } + } + + pub(crate) fn is_unknown(&self) -> bool { + match *self { + TypeVariableValue::Unknown { .. } => true, + TypeVariableValue::Known { .. } => false, + } + } +} + +impl<'db> TypeVariableStorage<'db> { + #[inline] + pub(crate) fn with_log<'a>( + &'a mut self, + undo_log: &'a mut InferCtxtUndoLogs<'db>, + ) -> TypeVariableTable<'a, 'db> { + TypeVariableTable { storage: self, undo_log } + } + + #[inline] + pub(crate) fn eq_relations_ref(&self) -> &ut::UnificationTableStorage> { + &self.eq_relations + } + + pub(super) fn finalize_rollback(&mut self) { + debug_assert!(self.values.len() >= self.eq_relations.len()); + self.values.truncate(self.eq_relations.len()); + } +} + +impl<'db> TypeVariableTable<'_, 'db> { + /// Returns the origin that was given when `vid` was created. + /// + /// Note that this function does not return care whether + /// `vid` has been unified with something else or not. + pub(crate) fn var_origin(&self, vid: TyVid) -> TypeVariableOrigin { + self.storage.values[vid].origin + } + + /// Records that `a == b`, depending on `dir`. + /// + /// Precondition: neither `a` nor `b` are known. + pub(crate) fn equate(&mut self, a: TyVid, b: TyVid) { + debug_assert!(self.probe(a).is_unknown()); + debug_assert!(self.probe(b).is_unknown()); + self.eq_relations().union(a, b); + } + + /// Instantiates `vid` with the type `ty`. + /// + /// Precondition: `vid` must not have been previously instantiated. + pub(crate) fn instantiate(&mut self, vid: TyVid, ty: Ty<'db>) { + let vid = self.root_var(vid); + debug_assert!(!ty.is_ty_var(), "instantiating ty var with var: {vid:?} {ty:?}"); + debug_assert!(self.probe(vid).is_unknown()); + debug_assert!( + self.eq_relations().probe_value(vid).is_unknown(), + "instantiating type variable `{vid:?}` twice: new-value = {ty:?}, old-value={:?}", + self.eq_relations().probe_value(vid) + ); + self.eq_relations().union_value(vid, TypeVariableValue::Known { value: ty }); + } + + /// Creates a new type variable. + /// + /// - `diverging`: indicates if this is a "diverging" type + /// variable, e.g., one created as the type of a `return` + /// expression. The code in this module doesn't care if a + /// variable is diverging, but the main Rust type-checker will + /// sometimes "unify" such variables with the `!` or `()` types. + /// - `origin`: indicates *why* the type variable was created. + /// The code in this module doesn't care, but it can be useful + /// for improving error messages. + pub(crate) fn new_var(&mut self, universe: UniverseIndex, origin: TypeVariableOrigin) -> TyVid { + let eq_key = self.eq_relations().new_key(TypeVariableValue::Unknown { universe }); + let index = self.storage.values.push(TypeVariableData { origin }); + debug_assert_eq!(eq_key.vid, index); + + debug!("new_var(index={:?}, universe={:?}, origin={:?})", eq_key.vid, universe, origin); + + index + } + + /// Returns the number of type variables created thus far. + pub(crate) fn num_vars(&self) -> usize { + self.storage.values.len() + } + + /// Returns the "root" variable of `vid` in the `eq_relations` + /// equivalence table. All type variables that have been equated + /// will yield the same root variable (per the union-find + /// algorithm), so `root_var(a) == root_var(b)` implies that `a == + /// b` (transitively). + pub(crate) fn root_var(&mut self, vid: TyVid) -> TyVid { + self.eq_relations().find(vid).vid + } + + /// Retrieves the type to which `vid` has been instantiated, if + /// any. + pub(crate) fn probe(&mut self, vid: TyVid) -> TypeVariableValue<'db> { + self.inlined_probe(vid) + } + + /// An always-inlined variant of `probe`, for very hot call sites. + #[inline(always)] + pub(crate) fn inlined_probe(&mut self, vid: TyVid) -> TypeVariableValue<'db> { + self.eq_relations().inlined_probe_value(vid) + } + + #[inline] + fn eq_relations(&mut self) -> super::UnificationTable<'_, 'db, TyVidEqKey<'db>> { + self.storage.eq_relations.with_log(self.undo_log) + } + + /// Returns indices of all variables that are not yet + /// instantiated. + pub(crate) fn unresolved_variables(&mut self) -> Vec { + (0..self.num_vars()) + .filter_map(|i| { + let vid = TyVid::from_usize(i); + match self.probe(vid) { + TypeVariableValue::Unknown { .. } => Some(vid), + TypeVariableValue::Known { .. } => None, + } + }) + .collect() + } +} + +/////////////////////////////////////////////////////////////////////////// + +/// These structs (a newtyped TyVid) are used as the unification key +/// for the `eq_relations`; they carry a `TypeVariableValue` along +/// with them. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub(crate) struct TyVidEqKey<'db> { + vid: TyVid, + + // in the table, we map each ty-vid to one of these: + phantom: PhantomData>, +} + +impl<'db> From for TyVidEqKey<'db> { + #[inline] // make this function eligible for inlining - it is quite hot. + fn from(vid: TyVid) -> Self { + TyVidEqKey { vid, phantom: PhantomData } + } +} + +impl<'db> ut::UnifyKey for TyVidEqKey<'db> { + type Value = TypeVariableValue<'db>; + #[inline(always)] + fn index(&self) -> u32 { + self.vid.as_u32() + } + #[inline] + fn from_index(i: u32) -> Self { + TyVidEqKey::from(TyVid::from_u32(i)) + } + fn tag() -> &'static str { + "TyVidEqKey" + } +} + +impl<'db> ut::UnifyValue for TypeVariableValue<'db> { + type Error = ut::NoError; + + fn unify_values(value1: &Self, value2: &Self) -> Result { + match (value1, value2) { + // We never equate two type variables, both of which + // have known types. Instead, we recursively equate + // those types. + (&TypeVariableValue::Known { .. }, &TypeVariableValue::Known { .. }) => { + panic!("equating two type variables, both of which have known types") + } + + // If one side is known, prefer that one. + (&TypeVariableValue::Known { .. }, &TypeVariableValue::Unknown { .. }) => { + Ok(value1.clone()) + } + (&TypeVariableValue::Unknown { .. }, &TypeVariableValue::Known { .. }) => { + Ok(value2.clone()) + } + + // If both sides are *unknown*, it hardly matters, does it? + ( + &TypeVariableValue::Unknown { universe: universe1 }, + &TypeVariableValue::Unknown { universe: universe2 }, + ) => { + // If we unify two unbound variables, ?T and ?U, then whatever + // value they wind up taking (which must be the same value) must + // be nameable by both universes. Therefore, the resulting + // universe is the minimum of the two universes, because that is + // the one which contains the fewest names in scope. + let universe = cmp::min(universe1, universe2); + Ok(TypeVariableValue::Unknown { universe }) + } + } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/unify_key.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/unify_key.rs new file mode 100644 index 000000000000..b9afb45ba89d --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/unify_key.rs @@ -0,0 +1,176 @@ +//! Unification keyes for the infer context the next-trait-solver. + +use std::cmp; +use std::marker::PhantomData; + +use ena::unify::{NoError, UnifyKey, UnifyValue}; +use rustc_type_ir::{ConstVid, RegionKind, RegionVid, UniverseIndex, inherent::IntoKind}; + +use crate::next_solver::{Const, Region, SolverDefId, Ty}; + +#[derive(Clone, Debug)] +pub enum RegionVariableValue<'db> { + Known { value: Region<'db> }, + Unknown { universe: UniverseIndex }, +} + +#[derive(PartialEq, Copy, Clone, Debug)] +pub struct RegionVidKey<'db> { + pub vid: RegionVid, + pub phantom: PhantomData>, +} + +impl<'db> From for RegionVidKey<'db> { + fn from(vid: RegionVid) -> Self { + RegionVidKey { vid, phantom: PhantomData } + } +} + +impl<'db> UnifyKey for RegionVidKey<'db> { + type Value = RegionVariableValue<'db>; + #[inline] + fn index(&self) -> u32 { + self.vid.as_u32() + } + #[inline] + fn from_index(i: u32) -> Self { + RegionVidKey::from(RegionVid::from_u32(i)) + } + fn tag() -> &'static str { + "RegionVidKey" + } +} + +pub struct RegionUnificationError; +impl<'db> UnifyValue for RegionVariableValue<'db> { + type Error = RegionUnificationError; + + fn unify_values(value1: &Self, value2: &Self) -> Result { + match (value1, value2) { + (RegionVariableValue::Known { .. }, RegionVariableValue::Known { .. }) => { + Err(RegionUnificationError) + } + + (RegionVariableValue::Known { value }, RegionVariableValue::Unknown { universe }) + | (RegionVariableValue::Unknown { universe }, RegionVariableValue::Known { value }) => { + let universe_of_value = match (*value).kind() { + RegionKind::ReStatic + | RegionKind::ReErased + | RegionKind::ReLateParam(..) + | RegionKind::ReEarlyParam(..) + | RegionKind::ReError(_) => UniverseIndex::ROOT, + RegionKind::RePlaceholder(placeholder) => placeholder.universe, + RegionKind::ReVar(..) | RegionKind::ReBound(..) => { + panic!("not a universal region") + } + }; + + if universe.can_name(universe_of_value) { + Ok(RegionVariableValue::Known { value: *value }) + } else { + Err(RegionUnificationError) + } + } + + ( + RegionVariableValue::Unknown { universe: a }, + RegionVariableValue::Unknown { universe: b }, + ) => { + // If we unify two unconstrained regions then whatever + // value they wind up taking (which must be the same value) must + // be nameable by both universes. Therefore, the resulting + // universe is the minimum of the two universes, because that is + // the one which contains the fewest names in scope. + Ok(RegionVariableValue::Unknown { universe: (*a).min(*b) }) + } + } + } +} + +// Generic consts. + +#[derive(Copy, Clone, Debug)] +pub struct ConstVariableOrigin { + /// `DefId` of the const parameter this was instantiated for, if any. + /// + /// This should only be used for diagnostics. + pub param_def_id: Option, +} + +#[derive(Clone, Debug)] +pub enum ConstVariableValue<'db> { + Known { value: Const<'db> }, + Unknown { origin: ConstVariableOrigin, universe: UniverseIndex }, +} + +impl<'db> ConstVariableValue<'db> { + /// If this value is known, returns the const it is known to be. + /// Otherwise, `None`. + pub fn known(&self) -> Option> { + match self { + ConstVariableValue::Unknown { .. } => None, + ConstVariableValue::Known { value } => Some(*value), + } + } +} + +#[derive(PartialEq, Copy, Clone, Debug)] +pub struct ConstVidKey<'db> { + pub vid: ConstVid, + pub phantom: PhantomData>, +} + +impl<'db> From for ConstVidKey<'db> { + fn from(vid: ConstVid) -> Self { + ConstVidKey { vid, phantom: PhantomData } + } +} + +impl<'db> UnifyKey for ConstVidKey<'db> { + type Value = ConstVariableValue<'db>; + #[inline] + fn index(&self) -> u32 { + self.vid.as_u32() + } + #[inline] + fn from_index(i: u32) -> Self { + ConstVidKey::from(ConstVid::from_u32(i)) + } + fn tag() -> &'static str { + "ConstVidKey" + } +} + +impl<'db> UnifyValue for ConstVariableValue<'db> { + type Error = NoError; + + fn unify_values(value1: &Self, value2: &Self) -> Result { + match (value1, value2) { + (ConstVariableValue::Known { .. }, ConstVariableValue::Known { .. }) => { + panic!("equating two const variables, both of which have known values") + } + + // If one side is known, prefer that one. + (ConstVariableValue::Known { .. }, ConstVariableValue::Unknown { .. }) => { + Ok(value1.clone()) + } + (ConstVariableValue::Unknown { .. }, ConstVariableValue::Known { .. }) => { + Ok(value2.clone()) + } + + // If both sides are *unknown*, it hardly matters, does it? + ( + ConstVariableValue::Unknown { origin, universe: universe1 }, + ConstVariableValue::Unknown { origin: _, universe: universe2 }, + ) => { + // If we unify two unbound variables, ?T and ?U, then whatever + // value they wind up taking (which must be the same value) must + // be nameable by both universes. Therefore, the resulting + // universe is the minimum of the two universes, because that is + // the one which contains the fewest names in scope. + let universe = cmp::min(*universe1, *universe2); + Ok(ConstVariableValue::Unknown { origin: *origin, universe }) + } + } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs new file mode 100644 index 000000000000..9c1bd52065fd --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -0,0 +1,2173 @@ +//! Things related to the Interner in the next-trait-solver. +#![allow(unused)] + +use base_db::Crate; +use chalk_ir::{ProgramClauseImplication, SeparatorTraitRef, Variances}; +use hir_def::lang_item::LangItem; +use hir_def::signatures::{FieldData, FnFlags, ImplFlags, StructFlags, TraitFlags}; +use hir_def::{AdtId, BlockId, TypeAliasId, VariantId}; +use hir_def::{AttrDefId, Lookup}; +use hir_def::{CallableDefId, EnumVariantId, ItemContainerId, StructId, UnionId}; +use intern::sym::non_exhaustive; +use intern::{Interned, impl_internable, sym}; +use la_arena::Idx; +use rustc_abi::{Align, ReprFlags, ReprOptions}; +use rustc_hash::FxHashSet; +use rustc_index::bit_set::DenseBitSet; +use rustc_type_ir::elaborate::elaborate; +use rustc_type_ir::error::TypeError; +use rustc_type_ir::inherent::{ + AdtDef as _, GenericArgs as _, GenericsOf, IntoKind, SliceLike as _, Span as _, +}; +use rustc_type_ir::lang_items::TraitSolverLangItem; +use rustc_type_ir::solve::SizedTraitKind; +use rustc_type_ir::{ + AliasTerm, AliasTermKind, AliasTy, EarlyBinder, FlagComputation, Flags, ImplPolarity, InferTy, + ProjectionPredicate, TraitPredicate, TraitRef, Upcast, +}; +use salsa::plumbing::AsId; +use smallvec::{SmallVec, smallvec}; +use std::fmt; +use std::ops::ControlFlow; +use syntax::ast::SelfParamKind; +use triomphe::Arc; + +use rustc_ast_ir::visit::VisitorResult; +use rustc_index::IndexVec; +use rustc_type_ir::TypeVisitableExt; +use rustc_type_ir::{ + BoundVar, CollectAndApply, DebruijnIndex, GenericArgKind, RegionKind, TermKind, UniverseIndex, + Variance, WithCachedTypeInfo, elaborate, + inherent::{self, Const as _, Region as _, Ty as _}, + ir_print, relate, +}; + +use crate::lower_nextsolver::{self, TyLoweringContext}; +use crate::method_resolution::{ALL_FLOAT_FPS, ALL_INT_FPS, TyFingerprint}; +use crate::next_solver::util::{ContainsTypeErrors, explicit_item_bounds, for_trait_impls}; +use crate::next_solver::{ + CanonicalVarKind, FxIndexMap, InternedWrapperNoDebug, RegionAssumptions, SolverDefIds, +}; +use crate::{ConstScalar, FnAbi, Interner, db::HirDatabase}; + +use super::generics::generics; +use super::util::sizedness_constraint_for_ty; +use super::{ + Binder, BoundExistentialPredicate, BoundExistentialPredicates, BoundTy, BoundTyKind, Clause, + Clauses, Const, ConstKind, ErrorGuaranteed, ExprConst, ExternalConstraints, + ExternalConstraintsData, GenericArg, GenericArgs, InternedClausesWrapper, ParamConst, ParamEnv, + ParamTy, PlaceholderConst, PlaceholderTy, PredefinedOpaques, PredefinedOpaquesData, Predicate, + PredicateKind, Term, Ty, TyKind, Tys, ValueConst, + abi::Safety, + fold::{BoundVarReplacer, BoundVarReplacerDelegate, FnMutDelegate}, + generics::Generics, + mapping::ChalkToNextSolver, + region::{ + BoundRegion, BoundRegionKind, EarlyParamRegion, LateParamRegion, PlaceholderRegion, Region, + }, +}; +use super::{ClauseKind, SolverDefId, Valtree}; + +#[macro_export] +#[doc(hidden)] +macro_rules! _interned_vec_nolifetime_salsa { + ($name:ident, $ty:ty) => { + interned_vec_nolifetime_salsa!($name, $ty, nofold); + + impl<'db> rustc_type_ir::TypeFoldable> for $name { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + use rustc_type_ir::inherent::SliceLike as _; + let inner: smallvec::SmallVec<[_; 2]> = + self.iter().map(|v| v.try_fold_with(folder)).collect::>()?; + Ok($name::new_(folder.cx().db(), inner)) + } + fn fold_with>>( + self, + folder: &mut F, + ) -> Self { + use rustc_type_ir::inherent::SliceLike as _; + let inner: smallvec::SmallVec<[_; 2]> = + self.iter().map(|v| v.fold_with(folder)).collect(); + $name::new_(folder.cx().db(), inner) + } + } + + impl<'db> rustc_type_ir::TypeVisitable> for $name { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + use rustc_ast_ir::visit::VisitorResult; + use rustc_type_ir::inherent::SliceLike as _; + rustc_ast_ir::walk_visitable_list!(visitor, self.as_slice().iter()); + V::Result::output() + } + } + }; + ($name:ident, $ty:ty, nofold) => { + #[salsa::interned(no_lifetime, constructor = new_, debug)] + pub struct $name { + #[returns(ref)] + inner_: smallvec::SmallVec<[$ty; 2]>, + } + + impl $name { + pub fn new_from_iter<'db>( + interner: DbInterner<'db>, + data: impl IntoIterator, + ) -> Self { + $name::new_(interner.db(), data.into_iter().collect::>()) + } + + pub fn inner(&self) -> &smallvec::SmallVec<[$ty; 2]> { + // SAFETY: ¯\_(ツ)_/¯ + salsa::with_attached_database(|db| { + let inner = self.inner_(db); + unsafe { std::mem::transmute(inner) } + }) + .unwrap() + } + } + + impl rustc_type_ir::inherent::SliceLike for $name { + type Item = $ty; + + type IntoIter = as IntoIterator>::IntoIter; + + fn iter(self) -> Self::IntoIter { + self.inner().clone().into_iter() + } + + fn as_slice(&self) -> &[Self::Item] { + self.inner().as_slice() + } + } + + impl IntoIterator for $name { + type Item = $ty; + type IntoIter = ::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + rustc_type_ir::inherent::SliceLike::iter(self) + } + } + + impl Default for $name { + fn default() -> Self { + $name::new_from_iter(DbInterner::conjure(), []) + } + } + }; +} + +pub use crate::_interned_vec_nolifetime_salsa as interned_vec_nolifetime_salsa; + +#[macro_export] +#[doc(hidden)] +macro_rules! _interned_vec_db { + ($name:ident, $ty:ident) => { + interned_vec_db!($name, $ty, nofold); + + impl<'db> rustc_type_ir::TypeFoldable> for $name<'db> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + use rustc_type_ir::inherent::SliceLike as _; + let inner: smallvec::SmallVec<[_; 2]> = + self.iter().map(|v| v.try_fold_with(folder)).collect::>()?; + Ok($name::new_(folder.cx().db(), inner)) + } + fn fold_with>>( + self, + folder: &mut F, + ) -> Self { + use rustc_type_ir::inherent::SliceLike as _; + let inner: smallvec::SmallVec<[_; 2]> = + self.iter().map(|v| v.fold_with(folder)).collect(); + $name::new_(folder.cx().db(), inner) + } + } + + impl<'db> rustc_type_ir::TypeVisitable> for $name<'db> { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + use rustc_ast_ir::visit::VisitorResult; + use rustc_type_ir::inherent::SliceLike as _; + rustc_ast_ir::walk_visitable_list!(visitor, self.as_slice().iter()); + V::Result::output() + } + } + }; + ($name:ident, $ty:ident, nofold) => { + #[salsa::interned(constructor = new_)] + pub struct $name<'db> { + #[returns(ref)] + inner_: smallvec::SmallVec<[$ty<'db>; 2]>, + } + + impl<'db> std::fmt::Debug for $name<'db> { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.as_slice().fmt(fmt) + } + } + + impl<'db> $name<'db> { + pub fn new_from_iter( + interner: DbInterner<'db>, + data: impl IntoIterator>, + ) -> Self { + $name::new_(interner.db(), data.into_iter().collect::>()) + } + + pub fn inner(&self) -> &smallvec::SmallVec<[$ty<'db>; 2]> { + // SAFETY: ¯\_(ツ)_/¯ + salsa::with_attached_database(|db| { + let inner = self.inner_(db); + unsafe { std::mem::transmute(inner) } + }) + .unwrap() + } + } + + impl<'db> rustc_type_ir::inherent::SliceLike for $name<'db> { + type Item = $ty<'db>; + + type IntoIter = ; 2]> as IntoIterator>::IntoIter; + + fn iter(self) -> Self::IntoIter { + self.inner().clone().into_iter() + } + + fn as_slice(&self) -> &[Self::Item] { + self.inner().as_slice() + } + } + + impl<'db> IntoIterator for $name<'db> { + type Item = $ty<'db>; + type IntoIter = ::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + rustc_type_ir::inherent::SliceLike::iter(self) + } + } + + impl<'db> Default for $name<'db> { + fn default() -> Self { + $name::new_from_iter(DbInterner::conjure(), []) + } + } + }; +} + +pub use crate::_interned_vec_db as interned_vec_db; + +#[derive(Debug, Copy, Clone)] +pub struct DbInterner<'db> { + pub(crate) db: &'db dyn HirDatabase, + pub(crate) krate: Option, + pub(crate) block: Option, +} + +// FIXME: very wrong, see https://github.com/rust-lang/rust/pull/144808 +unsafe impl Send for DbInterner<'_> {} +unsafe impl Sync for DbInterner<'_> {} + +impl<'db> DbInterner<'db> { + // FIXME(next-solver): remove this method + pub fn conjure() -> DbInterner<'db> { + salsa::with_attached_database(|db| DbInterner { + db: unsafe { + std::mem::transmute::<&dyn HirDatabase, &'db dyn HirDatabase>(db.as_view()) + }, + krate: None, + block: None, + }) + .unwrap() + } + + pub fn new_with( + db: &'db dyn HirDatabase, + krate: Option, + block: Option, + ) -> DbInterner<'db> { + DbInterner { db, krate, block } + } + + pub fn db(&self) -> &'db dyn HirDatabase { + self.db + } +} + +// This is intentionally left as `()` +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +pub struct Span(()); + +impl<'db> inherent::Span> for Span { + fn dummy() -> Self { + Span(()) + } +} + +interned_vec_nolifetime_salsa!(BoundVarKinds, BoundVarKind, nofold); + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum BoundVarKind { + Ty(BoundTyKind), + Region(BoundRegionKind), + Const, +} + +impl BoundVarKind { + pub fn expect_region(self) -> BoundRegionKind { + match self { + BoundVarKind::Region(lt) => lt, + _ => panic!("expected a region, but found another kind"), + } + } + + pub fn expect_ty(self) -> BoundTyKind { + match self { + BoundVarKind::Ty(ty) => ty, + _ => panic!("expected a type, but found another kind"), + } + } + + pub fn expect_const(self) { + match self { + BoundVarKind::Const => (), + _ => panic!("expected a const, but found another kind"), + } + } +} + +interned_vec_db!(CanonicalVars, CanonicalVarKind, nofold); + +pub struct DepNodeIndex; + +#[derive(Debug)] +pub struct Tracked(T); + +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct Placeholder { + pub universe: UniverseIndex, + pub bound: T, +} + +impl std::fmt::Debug for Placeholder { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result { + if self.universe == UniverseIndex::ROOT { + write!(f, "!{:?}", self.bound) + } else { + write!(f, "!{}_{:?}", self.universe.index(), self.bound) + } + } +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +pub struct AllocId; + +interned_vec_nolifetime_salsa!(VariancesOf, Variance, nofold); + +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct VariantIdx(usize); + +// FIXME: could/should store actual data? +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub enum VariantDef { + Struct(StructId), + Union(UnionId), + Enum(EnumVariantId), +} + +impl VariantDef { + pub fn id(&self) -> VariantId { + match self { + VariantDef::Struct(struct_id) => VariantId::StructId(*struct_id), + VariantDef::Union(union_id) => VariantId::UnionId(*union_id), + VariantDef::Enum(enum_variant_id) => VariantId::EnumVariantId(*enum_variant_id), + } + } + + pub fn fields(&self, db: &dyn HirDatabase) -> Vec<(Idx, FieldData)> { + let id: VariantId = match self { + VariantDef::Struct(it) => (*it).into(), + VariantDef::Union(it) => (*it).into(), + VariantDef::Enum(it) => (*it).into(), + }; + id.fields(db).fields().iter().map(|(id, data)| (id, data.clone())).collect() + } +} + +/* +/// Definition of a variant -- a struct's fields or an enum variant. +#[derive(Debug, HashStable, TyEncodable, TyDecodable)] +pub struct VariantDef { + /// `DefId` that identifies the variant itself. + /// If this variant belongs to a struct or union, then this is a copy of its `DefId`. + pub def_id: DefId, + /// `DefId` that identifies the variant's constructor. + /// If this variant is a struct variant, then this is `None`. + pub ctor: Option<(CtorKind, DefId)>, + /// Variant or struct name, maybe empty for anonymous adt (struct or union). + pub name: Symbol, + /// Discriminant of this variant. + pub discr: VariantDiscr, + /// Fields of this variant. + pub fields: IndexVec, + /// The error guarantees from parser, if any. + tainted: Option, + /// Flags of the variant (e.g. is field list non-exhaustive)? + flags: VariantFlags, +} +*/ + +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct AdtFlags { + is_enum: bool, + is_union: bool, + is_struct: bool, + is_phantom_data: bool, + is_fundamental: bool, + is_box: bool, + is_manually_drop: bool, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct AdtDefInner { + pub id: AdtId, + variants: Vec<(VariantIdx, VariantDef)>, + flags: AdtFlags, + repr: ReprOptions, +} + +// We're gonna cheat a little bit and implement `Hash` on only the `DefId` and +// accept there might be collisions for def ids from different crates (or across +// different tests, oh my). +impl std::hash::Hash for AdtDefInner { + #[inline] + fn hash(&self, s: &mut H) { + self.id.hash(s) + } +} + +#[salsa::interned(no_lifetime, constructor = new_)] +pub struct AdtDef { + #[returns(ref)] + data_: AdtDefInner, +} + +impl AdtDef { + pub fn new<'db>(def_id: AdtId, interner: DbInterner<'db>) -> Self { + let db = interner.db(); + let (flags, variants, repr) = match def_id { + AdtId::StructId(struct_id) => { + let data = db.struct_signature(struct_id); + + let flags = AdtFlags { + is_enum: false, + is_union: false, + is_struct: true, + is_phantom_data: data.flags.contains(StructFlags::IS_PHANTOM_DATA), + is_fundamental: data.flags.contains(StructFlags::FUNDAMENTAL), + is_box: data.flags.contains(StructFlags::IS_BOX), + is_manually_drop: data.flags.contains(StructFlags::IS_MANUALLY_DROP), + }; + + let variants = vec![(VariantIdx(0), VariantDef::Struct(struct_id))]; + + let mut repr = ReprOptions::default(); + repr.align = data.repr.and_then(|r| r.align); + repr.pack = data.repr.and_then(|r| r.pack); + repr.int = data.repr.and_then(|r| r.int); + + let mut repr_flags = ReprFlags::empty(); + if flags.is_box { + repr_flags.insert(ReprFlags::IS_LINEAR); + } + if data.repr.is_some_and(|r| r.c()) { + repr_flags.insert(ReprFlags::IS_C); + } + if data.repr.is_some_and(|r| r.simd()) { + repr_flags.insert(ReprFlags::IS_SIMD); + } + repr.flags = repr_flags; + + (flags, variants, repr) + } + AdtId::UnionId(union_id) => { + let data = db.union_signature(union_id); + + let flags = AdtFlags { + is_enum: false, + is_union: true, + is_struct: false, + is_phantom_data: false, + is_fundamental: false, + is_box: false, + is_manually_drop: false, + }; + + let variants = vec![(VariantIdx(0), VariantDef::Union(union_id))]; + + let mut repr = ReprOptions::default(); + repr.align = data.repr.and_then(|r| r.align); + repr.pack = data.repr.and_then(|r| r.pack); + repr.int = data.repr.and_then(|r| r.int); + + let mut repr_flags = ReprFlags::empty(); + if flags.is_box { + repr_flags.insert(ReprFlags::IS_LINEAR); + } + if data.repr.is_some_and(|r| r.c()) { + repr_flags.insert(ReprFlags::IS_C); + } + if data.repr.is_some_and(|r| r.simd()) { + repr_flags.insert(ReprFlags::IS_SIMD); + } + repr.flags = repr_flags; + + (flags, variants, repr) + } + AdtId::EnumId(enum_id) => { + let flags = AdtFlags { + is_enum: true, + is_union: false, + is_struct: false, + is_phantom_data: false, + is_fundamental: false, + is_box: false, + is_manually_drop: false, + }; + + let variants = enum_id + .enum_variants(db) + .variants + .iter() + .enumerate() + .map(|(idx, v)| (VariantIdx(idx), v)) + .map(|(idx, v)| (idx, VariantDef::Enum(v.0))) + .collect(); + + let data = db.enum_signature(enum_id); + + let mut repr = ReprOptions::default(); + repr.align = data.repr.and_then(|r| r.align); + repr.pack = data.repr.and_then(|r| r.pack); + repr.int = data.repr.and_then(|r| r.int); + + let mut repr_flags = ReprFlags::empty(); + if flags.is_box { + repr_flags.insert(ReprFlags::IS_LINEAR); + } + if data.repr.is_some_and(|r| r.c()) { + repr_flags.insert(ReprFlags::IS_C); + } + if data.repr.is_some_and(|r| r.simd()) { + repr_flags.insert(ReprFlags::IS_SIMD); + } + repr.flags = repr_flags; + + (flags, variants, repr) + } + }; + + AdtDef::new_(db, AdtDefInner { id: def_id, variants, flags, repr }) + } + + pub fn inner(&self) -> &AdtDefInner { + salsa::with_attached_database(|db| { + let inner = self.data_(db); + // SAFETY: ¯\_(ツ)_/¯ + unsafe { std::mem::transmute(inner) } + }) + .unwrap() + } + + pub fn is_enum(&self) -> bool { + self.inner().flags.is_enum + } + + #[inline] + pub fn repr(self) -> ReprOptions { + self.inner().repr + } + + /// Asserts this is a struct or union and returns its unique variant. + pub fn non_enum_variant(self) -> VariantDef { + assert!(self.inner().flags.is_struct || self.inner().flags.is_union); + self.inner().variants[0].1.clone() + } +} + +impl<'db> inherent::AdtDef> for AdtDef { + fn def_id(self) -> as rustc_type_ir::Interner>::DefId { + SolverDefId::AdtId(self.inner().id) + } + + fn is_struct(self) -> bool { + self.inner().flags.is_struct + } + + fn is_phantom_data(self) -> bool { + self.inner().flags.is_phantom_data + } + + fn is_fundamental(self) -> bool { + self.inner().flags.is_fundamental + } + + fn struct_tail_ty( + self, + interner: DbInterner<'db>, + ) -> Option< + rustc_type_ir::EarlyBinder< + DbInterner<'db>, + as rustc_type_ir::Interner>::Ty, + >, + > { + let db = interner.db(); + let hir_def::AdtId::StructId(struct_id) = self.inner().id else { + return None; + }; + let id: VariantId = struct_id.into(); + let field_types = interner.db().field_types_ns(id); + + field_types.iter().last().map(|f| *f.1) + } + + fn all_field_tys( + self, + interner: DbInterner<'db>, + ) -> rustc_type_ir::EarlyBinder< + DbInterner<'db>, + impl IntoIterator as rustc_type_ir::Interner>::Ty>, + > { + let db = interner.db(); + // FIXME: this is disabled just to match the behavior with chalk right now + let field_tys = |id: VariantId| { + let variant_data = id.fields(db); + let fields = if variant_data.fields().is_empty() { + vec![] + } else { + let field_types = db.field_types_ns(id); + variant_data + .fields() + .iter() + .map(|(idx, _)| { + let ty = field_types[idx]; + ty.skip_binder() + }) + .collect() + }; + }; + let field_tys = |id: VariantId| vec![]; + let tys: Vec<_> = match self.inner().id { + hir_def::AdtId::StructId(id) => field_tys(id.into()), + hir_def::AdtId::UnionId(id) => field_tys(id.into()), + hir_def::AdtId::EnumId(id) => id + .enum_variants(db) + .variants + .iter() + .flat_map(|&(variant_id, _, _)| field_tys(variant_id.into())) + .collect(), + }; + + rustc_type_ir::EarlyBinder::bind(tys) + } + + fn sizedness_constraint( + self, + interner: DbInterner<'db>, + sizedness: SizedTraitKind, + ) -> Option< + rustc_type_ir::EarlyBinder< + DbInterner<'db>, + as rustc_type_ir::Interner>::Ty, + >, + > { + if self.is_struct() { + let tail_ty = self.all_field_tys(interner).skip_binder().into_iter().last()?; + + let constraint_ty = sizedness_constraint_for_ty(interner, sizedness, tail_ty)?; + + Some(EarlyBinder::bind(constraint_ty)) + } else { + None + } + } + + fn destructor( + self, + interner: DbInterner<'db>, + ) -> Option { + // FIXME(next-solver) + None + } + + fn is_manually_drop(self) -> bool { + self.inner().flags.is_manually_drop + } +} + +impl fmt::Debug for AdtDef { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + salsa::with_attached_database(|db| match self.inner().id { + AdtId::StructId(struct_id) => { + let data = db.as_view::().struct_signature(struct_id); + f.write_str(data.name.as_str()) + } + AdtId::UnionId(union_id) => { + let data = db.as_view::().union_signature(union_id); + f.write_str(data.name.as_str()) + } + AdtId::EnumId(enum_id) => { + let data = db.as_view::().enum_signature(enum_id); + f.write_str(data.name.as_str()) + } + }) + .unwrap_or_else(|| f.write_str(&format!("AdtDef({:?})", self.inner().id))) + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct Features; + +impl<'db> inherent::Features> for Features { + fn generic_const_exprs(self) -> bool { + false + } + + fn coroutine_clone(self) -> bool { + false + } + + fn associated_const_equality(self) -> bool { + false + } + + fn feature_bound_holds_in_crate(self, symbol: ()) -> bool { + false + } +} + +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct UnsizingParams(pub(crate) DenseBitSet); + +impl std::ops::Deref for UnsizingParams { + type Target = DenseBitSet; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +pub type PatternKind<'db> = rustc_type_ir::PatternKind>; + +#[salsa::interned(constructor = new_, debug)] +pub struct Pattern<'db> { + #[returns(ref)] + kind_: InternedWrapperNoDebug>, +} + +impl<'db> std::fmt::Debug for InternedWrapperNoDebug> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } +} + +impl<'db> Pattern<'db> { + pub fn new(interner: DbInterner<'db>, kind: PatternKind<'db>) -> Self { + Pattern::new_(interner.db(), InternedWrapperNoDebug(kind)) + } + + pub fn inner(&self) -> &PatternKind<'db> { + salsa::with_attached_database(|db| { + let inner = &self.kind_(db).0; + // SAFETY: The caller already has access to a `Ty<'db>`, so borrowchecking will + // make sure that our returned value is valid for the lifetime `'db`. + unsafe { std::mem::transmute(inner) } + }) + .unwrap() + } +} + +impl<'db> Flags for Pattern<'db> { + fn flags(&self) -> rustc_type_ir::TypeFlags { + match self.inner() { + PatternKind::Range { start, end } => { + FlagComputation::for_const_kind(&start.kind()).flags + | FlagComputation::for_const_kind(&end.kind()).flags + } + PatternKind::Or(pats) => { + let mut flags = pats.as_slice()[0].flags(); + for pat in pats.as_slice()[1..].iter() { + flags |= pat.flags(); + } + flags + } + } + } + + fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex { + match self.inner() { + PatternKind::Range { start, end } => { + start.outer_exclusive_binder().max(end.outer_exclusive_binder()) + } + PatternKind::Or(pats) => { + let mut idx = pats.as_slice()[0].outer_exclusive_binder(); + for pat in pats.as_slice()[1..].iter() { + idx = idx.max(pat.outer_exclusive_binder()); + } + idx + } + } + } +} + +impl<'db> rustc_type_ir::inherent::IntoKind for Pattern<'db> { + type Kind = rustc_type_ir::PatternKind>; + fn kind(self) -> Self::Kind { + *self.inner() + } +} + +impl<'db> rustc_type_ir::relate::Relate> for Pattern<'db> { + fn relate>>( + relation: &mut R, + a: Self, + b: Self, + ) -> rustc_type_ir::relate::RelateResult, Self> { + let tcx = relation.cx(); + match (a.kind(), b.kind()) { + ( + PatternKind::Range { start: start_a, end: end_a }, + PatternKind::Range { start: start_b, end: end_b }, + ) => { + let start = relation.relate(start_a, start_b)?; + let end = relation.relate(end_a, end_b)?; + Ok(Pattern::new(tcx, PatternKind::Range { start, end })) + } + (PatternKind::Or(a), PatternKind::Or(b)) => { + if a.len() != b.len() { + return Err(TypeError::Mismatch); + } + let pats = CollectAndApply::collect_and_apply( + std::iter::zip(a.iter(), b.iter()).map(|(a, b)| relation.relate(a, b)), + |g| PatList::new_from_iter(tcx, g.iter().cloned()), + )?; + Ok(Pattern::new(tcx, PatternKind::Or(pats))) + } + (PatternKind::Range { .. } | PatternKind::Or(_), _) => Err(TypeError::Mismatch), + } + } +} + +interned_vec_db!(PatList, Pattern); + +impl<'db> rustc_type_ir::Interner for DbInterner<'db> { + type DefId = SolverDefId; + type LocalDefId = SolverDefId; + type LocalDefIds = SolverDefIds; + type Span = Span; + + type GenericArgs = GenericArgs<'db>; + type GenericArgsSlice = GenericArgs<'db>; + type GenericArg = GenericArg<'db>; + + type Term = Term<'db>; + + type BoundVarKinds = BoundVarKinds; + type BoundVarKind = BoundVarKind; + + type PredefinedOpaques = PredefinedOpaques<'db>; + + fn mk_predefined_opaques_in_body( + self, + data: rustc_type_ir::solve::PredefinedOpaquesData, + ) -> Self::PredefinedOpaques { + PredefinedOpaques::new(self, data) + } + + type CanonicalVarKinds = CanonicalVars<'db>; + + fn mk_canonical_var_kinds( + self, + kinds: &[rustc_type_ir::CanonicalVarKind], + ) -> Self::CanonicalVarKinds { + CanonicalVars::new_from_iter(self, kinds.iter().cloned()) + } + + type ExternalConstraints = ExternalConstraints<'db>; + + fn mk_external_constraints( + self, + data: rustc_type_ir::solve::ExternalConstraintsData, + ) -> Self::ExternalConstraints { + ExternalConstraints::new(self, data) + } + + type DepNodeIndex = DepNodeIndex; + + type Tracked = Tracked; + + type Ty = Ty<'db>; + type Tys = Tys<'db>; + type FnInputTys = Tys<'db>; + type ParamTy = ParamTy; + type BoundTy = BoundTy; + type PlaceholderTy = PlaceholderTy; + type Symbol = (); + + type ErrorGuaranteed = ErrorGuaranteed; + type BoundExistentialPredicates = BoundExistentialPredicates<'db>; + type AllocId = AllocId; + type Pat = Pattern<'db>; + type PatList = PatList<'db>; + type Safety = Safety; + type Abi = FnAbi; + + type Const = Const<'db>; + type PlaceholderConst = PlaceholderConst; + type ParamConst = ParamConst; + type BoundConst = rustc_type_ir::BoundVar; + type ValueConst = ValueConst<'db>; + type ValTree = Valtree<'db>; + type ExprConst = ExprConst; + + type Region = Region<'db>; + type EarlyParamRegion = EarlyParamRegion; + type LateParamRegion = LateParamRegion; + type BoundRegion = BoundRegion; + type PlaceholderRegion = PlaceholderRegion; + + type RegionAssumptions = RegionAssumptions<'db>; + + type ParamEnv = ParamEnv<'db>; + type Predicate = Predicate<'db>; + type Clause = Clause<'db>; + type Clauses = Clauses<'db>; + + type GenericsOf = Generics; + + type VariancesOf = VariancesOf; + + type AdtDef = AdtDef; + + type Features = Features; + + fn mk_args(self, args: &[Self::GenericArg]) -> Self::GenericArgs { + GenericArgs::new_from_iter(self, args.iter().cloned()) + } + + fn mk_args_from_iter(self, args: I) -> T::Output + where + I: Iterator, + T: rustc_type_ir::CollectAndApply, + { + CollectAndApply::collect_and_apply(args, |g| { + GenericArgs::new_from_iter(self, g.iter().cloned()) + }) + } + + type UnsizingParams = UnsizingParams; + + fn mk_tracked( + self, + data: T, + dep_node: Self::DepNodeIndex, + ) -> Self::Tracked { + Tracked(data) + } + + fn get_tracked(self, tracked: &Self::Tracked) -> T { + tracked.0.clone() + } + + fn with_cached_task(self, task: impl FnOnce() -> T) -> (T, Self::DepNodeIndex) { + (task(), DepNodeIndex) + } + + fn with_global_cache( + self, + f: impl FnOnce(&mut rustc_type_ir::search_graph::GlobalCache) -> R, + ) -> R { + salsa::with_attached_database(|db| { + tls_cache::with_cache( + unsafe { + std::mem::transmute::<&dyn HirDatabase, &'db dyn HirDatabase>( + db.as_view::(), + ) + }, + f, + ) + }) + .unwrap() + } + + fn canonical_param_env_cache_get_or_insert( + self, + param_env: Self::ParamEnv, + f: impl FnOnce() -> rustc_type_ir::CanonicalParamEnvCacheEntry, + from_entry: impl FnOnce(&rustc_type_ir::CanonicalParamEnvCacheEntry) -> R, + ) -> R { + from_entry(&f()) + } + + fn evaluation_is_concurrent(&self) -> bool { + false + } + + fn expand_abstract_consts>(self, t: T) -> T { + t + } + + fn generics_of(self, def_id: Self::DefId) -> Self::GenericsOf { + generics(self.db(), def_id) + } + + fn variances_of(self, def_id: Self::DefId) -> Self::VariancesOf { + match def_id { + SolverDefId::FunctionId(def_id) => VariancesOf::new_from_iter( + self, + self.db() + .variances_of(hir_def::GenericDefId::FunctionId(def_id)) + .as_deref() + .unwrap_or_default() + .iter() + .map(|v| v.to_nextsolver(self)), + ), + SolverDefId::AdtId(def_id) => VariancesOf::new_from_iter( + self, + self.db() + .variances_of(hir_def::GenericDefId::AdtId(def_id)) + .as_deref() + .unwrap_or_default() + .iter() + .map(|v| v.to_nextsolver(self)), + ), + SolverDefId::InternedOpaqueTyId(_def_id) => { + // FIXME(next-solver): track variances + VariancesOf::new_from_iter( + self, + (0..self.generics_of(def_id).count()).map(|_| Variance::Invariant), + ) + } + _ => VariancesOf::new_from_iter(self, []), + } + } + + fn type_of(self, def_id: Self::DefId) -> rustc_type_ir::EarlyBinder { + let def_id = match def_id { + SolverDefId::TypeAliasId(id) => { + use hir_def::Lookup; + match id.lookup(self.db()).container { + ItemContainerId::ImplId(it) => it, + _ => panic!("assoc ty value should be in impl"), + }; + crate::TyDefId::TypeAliasId(id) + } + SolverDefId::AdtId(id) => crate::TyDefId::AdtId(id), + _ => panic!("Unexpected def_id `{def_id:?}` provided for `type_of`"), + }; + self.db().ty_ns(def_id) + } + + fn adt_def(self, adt_def_id: Self::DefId) -> Self::AdtDef { + let def_id = match adt_def_id { + SolverDefId::AdtId(adt_id) => adt_id, + _ => panic!("Invalid DefId passed to adt_def"), + }; + AdtDef::new(def_id, self) + } + + fn alias_ty_kind(self, alias: rustc_type_ir::AliasTy) -> rustc_type_ir::AliasTyKind { + // FIXME: not currently creating any others + rustc_type_ir::AliasTyKind::Projection + } + + fn alias_term_kind( + self, + alias: rustc_type_ir::AliasTerm, + ) -> rustc_type_ir::AliasTermKind { + match alias.def_id { + SolverDefId::InternedOpaqueTyId(_) => AliasTermKind::OpaqueTy, + SolverDefId::TypeAliasId(_) => AliasTermKind::ProjectionTy, + SolverDefId::ConstId(_) => AliasTermKind::UnevaluatedConst, + _ => unreachable!("Unexpected alias: {:?}", alias.def_id), + } + } + + fn trait_ref_and_own_args_for_alias( + self, + def_id: Self::DefId, + args: Self::GenericArgs, + ) -> (rustc_type_ir::TraitRef, Self::GenericArgsSlice) { + let trait_def_id = self.parent(def_id); + let trait_generics = self.generics_of(trait_def_id); + let trait_args = GenericArgs::new_from_iter( + self, + args.as_slice()[0..trait_generics.own_params.len()].iter().cloned(), + ); + let alias_args = + GenericArgs::new_from_iter(self, args.iter().skip(trait_generics.own_params.len())); + (TraitRef::new_from_args(self, trait_def_id, trait_args), alias_args) + } + + fn check_args_compatible(self, def_id: Self::DefId, args: Self::GenericArgs) -> bool { + // FIXME + true + } + + fn debug_assert_args_compatible(self, def_id: Self::DefId, args: Self::GenericArgs) {} + + fn debug_assert_existential_args_compatible( + self, + def_id: Self::DefId, + args: Self::GenericArgs, + ) { + } + + fn mk_type_list_from_iter(self, args: I) -> T::Output + where + I: Iterator, + T: rustc_type_ir::CollectAndApply, + { + CollectAndApply::collect_and_apply(args, |g| Tys::new_from_iter(self, g.iter().cloned())) + } + + fn parent(self, def_id: Self::DefId) -> Self::DefId { + use hir_def::Lookup; + + let container = match def_id { + SolverDefId::FunctionId(it) => it.lookup(self.db()).container, + SolverDefId::TypeAliasId(it) => it.lookup(self.db()).container, + SolverDefId::ForeignId(it) => it.lookup(self.db()).container, + SolverDefId::ConstId(it) => it.lookup(self.db()).container, + SolverDefId::InternedClosureId(it) => { + return self + .db() + .lookup_intern_closure(it) + .0 + .as_generic_def_id(self.db()) + .unwrap() + .into(); + } + SolverDefId::InternedCoroutineId(it) => { + return self + .db() + .lookup_intern_coroutine(it) + .0 + .as_generic_def_id(self.db()) + .unwrap() + .into(); + } + SolverDefId::StaticId(_) + | SolverDefId::AdtId(_) + | SolverDefId::TraitId(_) + | SolverDefId::ImplId(_) + | SolverDefId::TraitAliasId(_) + | SolverDefId::Ctor(..) + | SolverDefId::InternedOpaqueTyId(..) => panic!(), + }; + + match container { + ItemContainerId::ImplId(it) => it.into(), + ItemContainerId::TraitId(it) => it.into(), + ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => panic!(), + } + } + + fn recursion_limit(self) -> usize { + 50 + } + + fn features(self) -> Self::Features { + Features + } + + fn fn_sig( + self, + def_id: Self::DefId, + ) -> rustc_type_ir::EarlyBinder>> + { + let id = match def_id { + SolverDefId::FunctionId(id) => CallableDefId::FunctionId(id), + SolverDefId::Ctor(ctor) => match ctor { + super::Ctor::Struct(struct_id) => CallableDefId::StructId(struct_id), + super::Ctor::Enum(enum_variant_id) => CallableDefId::EnumVariantId(enum_variant_id), + }, + def => unreachable!("{:?}", def), + }; + self.db().callable_item_signature_ns(id) + } + + fn coroutine_movability(self, def_id: Self::DefId) -> rustc_ast_ir::Movability { + unimplemented!() + } + + fn coroutine_for_closure(self, def_id: Self::DefId) -> Self::DefId { + unimplemented!() + } + + fn generics_require_sized_self(self, def_id: Self::DefId) -> bool { + let sized_trait = + LangItem::Sized.resolve_trait(self.db(), self.krate.expect("Must have self.krate")); + let Some(sized_id) = sized_trait else { + return false; /* No Sized trait, can't require it! */ + }; + let sized_def_id = sized_id.into(); + + // Search for a predicate like `Self : Sized` amongst the trait bounds. + let predicates = self.predicates_of(def_id); + elaborate(self, predicates.iter_identity()).any(|pred| match pred.kind().skip_binder() { + ClauseKind::Trait(ref trait_pred) => { + trait_pred.def_id() == sized_def_id + && matches!( + trait_pred.self_ty().kind(), + TyKind::Param(ParamTy { index: 0, .. }) + ) + } + ClauseKind::RegionOutlives(_) + | ClauseKind::TypeOutlives(_) + | ClauseKind::Projection(_) + | ClauseKind::ConstArgHasType(_, _) + | ClauseKind::WellFormed(_) + | ClauseKind::ConstEvaluatable(_) + | ClauseKind::HostEffect(..) + | ClauseKind::UnstableFeature(_) => false, + }) + } + + #[tracing::instrument(skip(self), ret)] + fn item_bounds( + self, + def_id: Self::DefId, + ) -> rustc_type_ir::EarlyBinder> { + explicit_item_bounds(self, def_id).map_bound(|bounds| { + Clauses::new_from_iter(self, elaborate(self, bounds).collect::>()) + }) + } + + #[tracing::instrument(skip(self), ret)] + fn item_self_bounds( + self, + def_id: Self::DefId, + ) -> rustc_type_ir::EarlyBinder> { + explicit_item_bounds(self, def_id).map_bound(|bounds| { + Clauses::new_from_iter( + self, + elaborate(self, bounds).filter_only_self().collect::>(), + ) + }) + } + + fn item_non_self_bounds( + self, + def_id: Self::DefId, + ) -> rustc_type_ir::EarlyBinder> { + let all_bounds: FxHashSet<_> = self.item_bounds(def_id).skip_binder().into_iter().collect(); + let own_bounds: FxHashSet<_> = + self.item_self_bounds(def_id).skip_binder().into_iter().collect(); + if all_bounds.len() == own_bounds.len() { + EarlyBinder::bind(Clauses::new_from_iter(self, [])) + } else { + EarlyBinder::bind(Clauses::new_from_iter( + self, + all_bounds.difference(&own_bounds).cloned(), + )) + } + } + + #[tracing::instrument(level = "debug", skip(self), ret)] + fn predicates_of( + self, + def_id: Self::DefId, + ) -> rustc_type_ir::EarlyBinder> { + let predicates = self.db().generic_predicates_ns(def_id.try_into().unwrap()); + let predicates: Vec<_> = predicates.iter().cloned().collect(); + EarlyBinder::bind(predicates.into_iter()) + } + + #[tracing::instrument(level = "debug", skip(self), ret)] + fn own_predicates_of( + self, + def_id: Self::DefId, + ) -> rustc_type_ir::EarlyBinder> { + let predicates = self.db().generic_predicates_without_parent_ns(def_id.try_into().unwrap()); + let predicates: Vec<_> = predicates.iter().cloned().collect(); + EarlyBinder::bind(predicates.into_iter()) + } + + #[tracing::instrument(skip(self), ret)] + fn explicit_super_predicates_of( + self, + def_id: Self::DefId, + ) -> rustc_type_ir::EarlyBinder> + { + let predicates: Vec<(Clause<'db>, Span)> = self + .db() + .generic_predicates_ns(def_id.try_into().unwrap()) + .iter() + .cloned() + .map(|p| (p, Span::dummy())) + .collect(); + rustc_type_ir::EarlyBinder::bind(predicates) + } + + #[tracing::instrument(skip(self), ret)] + fn explicit_implied_predicates_of( + self, + def_id: Self::DefId, + ) -> rustc_type_ir::EarlyBinder> + { + let predicates: Vec<(Clause<'db>, Span)> = self + .db() + .generic_predicates_ns(def_id.try_into().unwrap()) + .iter() + .cloned() + .map(|p| (p, Span::dummy())) + .collect(); + rustc_type_ir::EarlyBinder::bind(predicates) + } + + fn impl_super_outlives( + self, + impl_def_id: Self::DefId, + ) -> rustc_type_ir::EarlyBinder> { + let impl_id = match impl_def_id { + SolverDefId::ImplId(id) => id, + _ => unreachable!(), + }; + let trait_ref = self.db().impl_trait_ns(impl_id).expect("expected an impl of trait"); + trait_ref.map_bound(|trait_ref| { + let clause: Clause<'_> = trait_ref.upcast(self); + Clauses::new_from_iter( + self, + rustc_type_ir::elaborate::elaborate(self, [clause]).filter(|clause| { + matches!( + clause.kind().skip_binder(), + ClauseKind::TypeOutlives(_) | ClauseKind::RegionOutlives(_) + ) + }), + ) + }) + } + + fn const_conditions( + self, + def_id: Self::DefId, + ) -> rustc_type_ir::EarlyBinder< + Self, + impl IntoIterator>>, + > { + rustc_type_ir::EarlyBinder::bind([unimplemented!()]) + } + + fn has_target_features(self, def_id: Self::DefId) -> bool { + false + } + + fn require_lang_item( + self, + lang_item: rustc_type_ir::lang_items::TraitSolverLangItem, + ) -> Self::DefId { + let lang_item = match lang_item { + rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFn => LangItem::AsyncFn, + rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFnKindHelper => unimplemented!(), + rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFnKindUpvars => unimplemented!(), + rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFnMut => LangItem::AsyncFnMut, + rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFnOnce => LangItem::AsyncFnOnce, + rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFnOnceOutput => { + LangItem::AsyncFnOnceOutput + } + rustc_type_ir::lang_items::TraitSolverLangItem::AsyncIterator => unimplemented!(), + rustc_type_ir::lang_items::TraitSolverLangItem::CallOnceFuture => { + LangItem::CallOnceFuture + } + rustc_type_ir::lang_items::TraitSolverLangItem::CallRefFuture => { + LangItem::CallRefFuture + } + rustc_type_ir::lang_items::TraitSolverLangItem::Clone => LangItem::Clone, + rustc_type_ir::lang_items::TraitSolverLangItem::Copy => LangItem::Copy, + rustc_type_ir::lang_items::TraitSolverLangItem::Coroutine => LangItem::Coroutine, + rustc_type_ir::lang_items::TraitSolverLangItem::CoroutineReturn => { + LangItem::CoroutineReturn + } + rustc_type_ir::lang_items::TraitSolverLangItem::CoroutineYield => { + LangItem::CoroutineYield + } + rustc_type_ir::lang_items::TraitSolverLangItem::Destruct => LangItem::Destruct, + rustc_type_ir::lang_items::TraitSolverLangItem::DiscriminantKind => { + LangItem::DiscriminantKind + } + rustc_type_ir::lang_items::TraitSolverLangItem::Drop => LangItem::Drop, + rustc_type_ir::lang_items::TraitSolverLangItem::DynMetadata => LangItem::DynMetadata, + rustc_type_ir::lang_items::TraitSolverLangItem::Fn => LangItem::Fn, + rustc_type_ir::lang_items::TraitSolverLangItem::FnMut => LangItem::FnMut, + rustc_type_ir::lang_items::TraitSolverLangItem::FnOnce => LangItem::FnOnce, + rustc_type_ir::lang_items::TraitSolverLangItem::FnPtrTrait => LangItem::FnPtrTrait, + rustc_type_ir::lang_items::TraitSolverLangItem::FusedIterator => unimplemented!(), + rustc_type_ir::lang_items::TraitSolverLangItem::Future => LangItem::Future, + rustc_type_ir::lang_items::TraitSolverLangItem::FutureOutput => LangItem::FutureOutput, + rustc_type_ir::lang_items::TraitSolverLangItem::Iterator => LangItem::Iterator, + rustc_type_ir::lang_items::TraitSolverLangItem::Metadata => LangItem::Metadata, + rustc_type_ir::lang_items::TraitSolverLangItem::Option => LangItem::Option, + rustc_type_ir::lang_items::TraitSolverLangItem::PointeeTrait => LangItem::PointeeTrait, + rustc_type_ir::lang_items::TraitSolverLangItem::Poll => LangItem::Poll, + rustc_type_ir::lang_items::TraitSolverLangItem::Sized => LangItem::Sized, + rustc_type_ir::lang_items::TraitSolverLangItem::MetaSized => LangItem::MetaSized, + rustc_type_ir::lang_items::TraitSolverLangItem::PointeeSized => LangItem::PointeeSized, + rustc_type_ir::lang_items::TraitSolverLangItem::TransmuteTrait => { + LangItem::TransmuteTrait + } + rustc_type_ir::lang_items::TraitSolverLangItem::Tuple => LangItem::Tuple, + rustc_type_ir::lang_items::TraitSolverLangItem::Unpin => LangItem::Unpin, + rustc_type_ir::lang_items::TraitSolverLangItem::Unsize => LangItem::Unsize, + rustc_type_ir::lang_items::TraitSolverLangItem::BikeshedGuaranteedNoDrop => { + unimplemented!() + } + }; + let target = hir_def::lang_item::lang_item( + self.db(), + self.krate.expect("Must have self.krate"), + lang_item, + ) + .unwrap_or_else(|| panic!("Lang item {lang_item:?} required but not found.")); + match target { + hir_def::lang_item::LangItemTarget::EnumId(enum_id) => enum_id.into(), + hir_def::lang_item::LangItemTarget::Function(function_id) => function_id.into(), + hir_def::lang_item::LangItemTarget::ImplDef(impl_id) => impl_id.into(), + hir_def::lang_item::LangItemTarget::Static(static_id) => static_id.into(), + hir_def::lang_item::LangItemTarget::Struct(struct_id) => struct_id.into(), + hir_def::lang_item::LangItemTarget::Union(union_id) => union_id.into(), + hir_def::lang_item::LangItemTarget::TypeAlias(type_alias_id) => type_alias_id.into(), + hir_def::lang_item::LangItemTarget::Trait(trait_id) => trait_id.into(), + hir_def::lang_item::LangItemTarget::EnumVariant(enum_variant_id) => unimplemented!(), + } + } + + #[allow(clippy::match_like_matches_macro)] + fn is_lang_item( + self, + def_id: Self::DefId, + lang_item: rustc_type_ir::lang_items::TraitSolverLangItem, + ) -> bool { + use rustc_type_ir::lang_items::TraitSolverLangItem::*; + + // FIXME: derive PartialEq on TraitSolverLangItem + self.as_lang_item(def_id).map_or(false, |l| match (l, lang_item) { + (AsyncFn, AsyncFn) => true, + (AsyncFnKindHelper, AsyncFnKindHelper) => true, + (AsyncFnKindUpvars, AsyncFnKindUpvars) => true, + (AsyncFnMut, AsyncFnMut) => true, + (AsyncFnOnce, AsyncFnOnce) => true, + (AsyncFnOnceOutput, AsyncFnOnceOutput) => true, + (AsyncIterator, AsyncIterator) => true, + (CallOnceFuture, CallOnceFuture) => true, + (CallRefFuture, CallRefFuture) => true, + (Clone, Clone) => true, + (Copy, Copy) => true, + (Coroutine, Coroutine) => true, + (CoroutineReturn, CoroutineReturn) => true, + (CoroutineYield, CoroutineYield) => true, + (Destruct, Destruct) => true, + (DiscriminantKind, DiscriminantKind) => true, + (Drop, Drop) => true, + (DynMetadata, DynMetadata) => true, + (Fn, Fn) => true, + (FnMut, FnMut) => true, + (FnOnce, FnOnce) => true, + (FnPtrTrait, FnPtrTrait) => true, + (FusedIterator, FusedIterator) => true, + (Future, Future) => true, + (FutureOutput, FutureOutput) => true, + (Iterator, Iterator) => true, + (Metadata, Metadata) => true, + (Option, Option) => true, + (PointeeTrait, PointeeTrait) => true, + (Poll, Poll) => true, + (Sized, Sized) => true, + (TransmuteTrait, TransmuteTrait) => true, + (Tuple, Tuple) => true, + (Unpin, Unpin) => true, + (Unsize, Unsize) => true, + _ => false, + }) + } + + fn as_lang_item( + self, + def_id: Self::DefId, + ) -> Option { + use rustc_type_ir::lang_items::TraitSolverLangItem; + + let def_id: AttrDefId = match def_id { + SolverDefId::TraitId(id) => id.into(), + SolverDefId::TypeAliasId(id) => id.into(), + _ => panic!("Unexpected SolverDefId in as_lang_item"), + }; + let lang_item = self.db().lang_attr(def_id)?; + Some(match lang_item { + LangItem::Sized => TraitSolverLangItem::Sized, + LangItem::MetaSized => TraitSolverLangItem::MetaSized, + LangItem::PointeeSized => TraitSolverLangItem::PointeeSized, + LangItem::Unsize => TraitSolverLangItem::Unsize, + LangItem::StructuralPeq => return None, + LangItem::StructuralTeq => return None, + LangItem::Copy => TraitSolverLangItem::Copy, + LangItem::Clone => TraitSolverLangItem::Clone, + LangItem::Sync => return None, + LangItem::DiscriminantKind => TraitSolverLangItem::DiscriminantKind, + LangItem::Discriminant => return None, + LangItem::PointeeTrait => TraitSolverLangItem::PointeeTrait, + LangItem::Metadata => TraitSolverLangItem::Metadata, + LangItem::DynMetadata => TraitSolverLangItem::DynMetadata, + LangItem::Freeze => return None, + LangItem::FnPtrTrait => TraitSolverLangItem::FnPtrTrait, + LangItem::FnPtrAddr => return None, + LangItem::Drop => TraitSolverLangItem::Drop, + LangItem::Destruct => TraitSolverLangItem::Destruct, + LangItem::CoerceUnsized => return None, + LangItem::DispatchFromDyn => return None, + LangItem::TransmuteOpts => return None, + LangItem::TransmuteTrait => TraitSolverLangItem::TransmuteTrait, + LangItem::Add => return None, + LangItem::Sub => return None, + LangItem::Mul => return None, + LangItem::Div => return None, + LangItem::Rem => return None, + LangItem::Neg => return None, + LangItem::Not => return None, + LangItem::BitXor => return None, + LangItem::BitAnd => return None, + LangItem::BitOr => return None, + LangItem::Shl => return None, + LangItem::Shr => return None, + LangItem::AddAssign => return None, + LangItem::SubAssign => return None, + LangItem::MulAssign => return None, + LangItem::DivAssign => return None, + LangItem::RemAssign => return None, + LangItem::BitXorAssign => return None, + LangItem::BitAndAssign => return None, + LangItem::BitOrAssign => return None, + LangItem::ShlAssign => return None, + LangItem::ShrAssign => return None, + LangItem::Index => return None, + LangItem::IndexMut => return None, + LangItem::UnsafeCell => return None, + LangItem::VaList => return None, + LangItem::Deref => return None, + LangItem::DerefMut => return None, + LangItem::DerefTarget => return None, + LangItem::Receiver => return None, + LangItem::Fn => TraitSolverLangItem::Fn, + LangItem::FnMut => TraitSolverLangItem::FnMut, + LangItem::FnOnce => TraitSolverLangItem::FnOnce, + LangItem::FnOnceOutput => return None, + LangItem::Future => TraitSolverLangItem::Future, + LangItem::CoroutineState => return None, + LangItem::Coroutine => TraitSolverLangItem::Coroutine, + LangItem::CoroutineReturn => TraitSolverLangItem::CoroutineReturn, + LangItem::CoroutineYield => TraitSolverLangItem::CoroutineYield, + LangItem::Unpin => TraitSolverLangItem::Unpin, + LangItem::Pin => return None, + LangItem::PartialEq => return None, + LangItem::PartialOrd => return None, + LangItem::CVoid => return None, + LangItem::Panic => return None, + LangItem::PanicNounwind => return None, + LangItem::PanicFmt => return None, + LangItem::PanicDisplay => return None, + LangItem::ConstPanicFmt => return None, + LangItem::PanicBoundsCheck => return None, + LangItem::PanicMisalignedPointerDereference => return None, + LangItem::PanicInfo => return None, + LangItem::PanicLocation => return None, + LangItem::PanicImpl => return None, + LangItem::PanicCannotUnwind => return None, + LangItem::BeginPanic => return None, + LangItem::FormatAlignment => return None, + LangItem::FormatArgument => return None, + LangItem::FormatArguments => return None, + LangItem::FormatCount => return None, + LangItem::FormatPlaceholder => return None, + LangItem::FormatUnsafeArg => return None, + LangItem::ExchangeMalloc => return None, + LangItem::BoxFree => return None, + LangItem::DropInPlace => return None, + LangItem::AllocLayout => return None, + LangItem::Start => return None, + LangItem::EhPersonality => return None, + LangItem::EhCatchTypeinfo => return None, + LangItem::OwnedBox => return None, + LangItem::PhantomData => return None, + LangItem::ManuallyDrop => return None, + LangItem::MaybeUninit => return None, + LangItem::AlignOffset => return None, + LangItem::Termination => return None, + LangItem::Try => return None, + LangItem::Tuple => TraitSolverLangItem::Tuple, + LangItem::SliceLen => return None, + LangItem::TryTraitFromResidual => return None, + LangItem::TryTraitFromOutput => return None, + LangItem::TryTraitBranch => return None, + LangItem::TryTraitFromYeet => return None, + LangItem::PointerLike => return None, + LangItem::ConstParamTy => return None, + LangItem::Poll => TraitSolverLangItem::Poll, + LangItem::PollReady => return None, + LangItem::PollPending => return None, + LangItem::ResumeTy => return None, + LangItem::GetContext => return None, + LangItem::Context => return None, + LangItem::FuturePoll => return None, + LangItem::FutureOutput => TraitSolverLangItem::FutureOutput, + LangItem::Option => TraitSolverLangItem::Option, + LangItem::OptionSome => return None, + LangItem::OptionNone => return None, + LangItem::ResultOk => return None, + LangItem::ResultErr => return None, + LangItem::ControlFlowContinue => return None, + LangItem::ControlFlowBreak => return None, + LangItem::IntoFutureIntoFuture => return None, + LangItem::IntoIterIntoIter => return None, + LangItem::IteratorNext => return None, + LangItem::Iterator => TraitSolverLangItem::Iterator, + LangItem::PinNewUnchecked => return None, + LangItem::RangeFrom => return None, + LangItem::RangeFull => return None, + LangItem::RangeInclusiveStruct => return None, + LangItem::RangeInclusiveNew => return None, + LangItem::Range => return None, + LangItem::RangeToInclusive => return None, + LangItem::RangeTo => return None, + LangItem::String => return None, + LangItem::CStr => return None, + LangItem::AsyncFn => TraitSolverLangItem::AsyncFn, + LangItem::AsyncFnMut => TraitSolverLangItem::AsyncFnMut, + LangItem::AsyncFnOnce => TraitSolverLangItem::AsyncFnOnce, + LangItem::AsyncFnOnceOutput => TraitSolverLangItem::AsyncFnOnceOutput, + LangItem::CallRefFuture => TraitSolverLangItem::CallRefFuture, + LangItem::CallOnceFuture => TraitSolverLangItem::CallOnceFuture, + LangItem::Ordering => return None, + LangItem::PanicNullPointerDereference => return None, + LangItem::ReceiverTarget => return None, + LangItem::UnsafePinned => return None, + LangItem::AsyncFnOnceOutput => TraitSolverLangItem::AsyncFnOnceOutput, + }) + } + + fn associated_type_def_ids(self, def_id: Self::DefId) -> impl IntoIterator { + let trait_ = match def_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + trait_.trait_items(self.db()).associated_types().map(|id| id.into()) + } + + fn for_each_relevant_impl( + self, + trait_def_id: Self::DefId, + self_ty: Self::Ty, + mut f: impl FnMut(Self::DefId), + ) { + let trait_ = match trait_def_id { + SolverDefId::TraitId(id) => id, + _ => panic!("for_each_relevant_impl called for non-trait"), + }; + + let self_ty_fp = TyFingerprint::for_trait_impl_ns(&self_ty); + let fps: &[TyFingerprint] = match self_ty.kind() { + TyKind::Infer(InferTy::IntVar(..)) => &ALL_INT_FPS, + TyKind::Infer(InferTy::FloatVar(..)) => &ALL_FLOAT_FPS, + _ => self_ty_fp.as_slice(), + }; + + if fps.is_empty() { + for_trait_impls( + self.db(), + self.krate.expect("Must have self.krate"), + self.block, + trait_, + self_ty_fp, + |impls| { + for i in impls.for_trait(trait_) { + use rustc_type_ir::TypeVisitable; + let contains_errors = self.db().impl_trait_ns(i).map_or(false, |b| { + b.skip_binder().visit_with(&mut ContainsTypeErrors).is_break() + }); + if contains_errors { + continue; + } + + f(SolverDefId::ImplId(i)); + } + ControlFlow::Continue(()) + }, + ); + } else { + for_trait_impls( + self.db(), + self.krate.expect("Must have self.krate"), + self.block, + trait_, + self_ty_fp, + |impls| { + for fp in fps { + for i in impls.for_trait_and_self_ty(trait_, *fp) { + use rustc_type_ir::TypeVisitable; + let contains_errors = self.db().impl_trait_ns(i).map_or(false, |b| { + b.skip_binder().visit_with(&mut ContainsTypeErrors).is_break() + }); + if contains_errors { + continue; + } + + f(SolverDefId::ImplId(i)); + } + } + ControlFlow::Continue(()) + }, + ); + } + } + + fn has_item_definition(self, def_id: Self::DefId) -> bool { + // FIXME: should check if has value + true + } + + fn impl_is_default(self, impl_def_id: Self::DefId) -> bool { + // FIXME + false + } + + #[tracing::instrument(skip(self), ret)] + fn impl_trait_ref( + self, + impl_def_id: Self::DefId, + ) -> rustc_type_ir::EarlyBinder> { + let impl_id = match impl_def_id { + SolverDefId::ImplId(id) => id, + _ => panic!("Unexpected SolverDefId in impl_trait_ref"), + }; + + let db = self.db(); + + db.impl_trait_ns(impl_id) + // ImplIds for impls where the trait ref can't be resolved should never reach trait solving + .expect("invalid impl passed to trait solver") + } + + fn impl_polarity(self, impl_def_id: Self::DefId) -> rustc_type_ir::ImplPolarity { + let impl_id = match impl_def_id { + SolverDefId::ImplId(id) => id, + _ => unreachable!(), + }; + let impl_data = self.db().impl_signature(impl_id); + if impl_data.flags.contains(ImplFlags::NEGATIVE) { + ImplPolarity::Negative + } else { + ImplPolarity::Positive + } + } + + fn trait_is_auto(self, trait_def_id: Self::DefId) -> bool { + let trait_ = match trait_def_id { + SolverDefId::TraitId(id) => id, + _ => panic!("Unexpected SolverDefId in trait_is_auto"), + }; + let trait_data = self.db().trait_signature(trait_); + trait_data.flags.contains(TraitFlags::AUTO) + } + + fn trait_is_alias(self, trait_def_id: Self::DefId) -> bool { + matches!(trait_def_id, SolverDefId::TraitAliasId(_)) + } + + fn trait_is_dyn_compatible(self, trait_def_id: Self::DefId) -> bool { + let trait_ = match trait_def_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + crate::dyn_compatibility::dyn_compatibility(self.db(), trait_).is_none() + } + + fn trait_is_fundamental(self, def_id: Self::DefId) -> bool { + let trait_ = match def_id { + SolverDefId::TraitId(id) => id, + _ => panic!("Unexpected SolverDefId in trait_is_fundamental"), + }; + let trait_data = self.db().trait_signature(trait_); + trait_data.flags.contains(TraitFlags::FUNDAMENTAL) + } + + fn trait_may_be_implemented_via_object(self, trait_def_id: Self::DefId) -> bool { + // FIXME(next-solver) + true + } + + fn is_impl_trait_in_trait(self, def_id: Self::DefId) -> bool { + // FIXME(next-solver) + false + } + + fn delay_bug(self, msg: impl ToString) -> Self::ErrorGuaranteed { + panic!("Bug encountered in next-trait-solver.") + } + + fn is_general_coroutine(self, coroutine_def_id: Self::DefId) -> bool { + // FIXME(next-solver) + true + } + + fn coroutine_is_async(self, coroutine_def_id: Self::DefId) -> bool { + // FIXME(next-solver) + true + } + + fn coroutine_is_gen(self, coroutine_def_id: Self::DefId) -> bool { + // FIXME(next-solver) + false + } + + fn coroutine_is_async_gen(self, coroutine_def_id: Self::DefId) -> bool { + // FIXME(next-solver) + false + } + + fn unsizing_params_for_adt(self, adt_def_id: Self::DefId) -> Self::UnsizingParams { + let id = match adt_def_id { + SolverDefId::AdtId(id) => id, + _ => unreachable!(), + }; + let def = AdtDef::new(id, self); + let num_params = self.generics_of(adt_def_id).count(); + + let maybe_unsizing_param_idx = |arg: GenericArg<'db>| match arg.kind() { + GenericArgKind::Type(ty) => match ty.kind() { + rustc_type_ir::TyKind::Param(p) => Some(p.index), + _ => None, + }, + GenericArgKind::Lifetime(_) => None, + GenericArgKind::Const(ct) => match ct.kind() { + rustc_type_ir::ConstKind::Param(p) => Some(p.index), + _ => None, + }, + }; + + // The last field of the structure has to exist and contain type/const parameters. + let variant = def.non_enum_variant(); + let fields = variant.fields(self.db()); + let Some((tail_field, prefix_fields)) = fields.split_last() else { + return UnsizingParams(DenseBitSet::new_empty(num_params)); + }; + + let field_types = self.db().field_types_ns(variant.id()); + let mut unsizing_params = DenseBitSet::new_empty(num_params); + let ty = field_types[tail_field.0]; + for arg in ty.instantiate_identity().walk() { + if let Some(i) = maybe_unsizing_param_idx(arg) { + unsizing_params.insert(i); + } + } + + // Ensure none of the other fields mention the parameters used + // in unsizing. + for field in prefix_fields { + for arg in field_types[field.0].instantiate_identity().walk() { + if let Some(i) = maybe_unsizing_param_idx(arg) { + unsizing_params.remove(i); + } + } + } + + UnsizingParams(unsizing_params) + } + + fn anonymize_bound_vars>( + self, + value: rustc_type_ir::Binder, + ) -> rustc_type_ir::Binder { + struct Anonymize<'a, 'db> { + interner: DbInterner<'db>, + map: &'a mut FxIndexMap, + } + impl<'db> BoundVarReplacerDelegate<'db> for Anonymize<'_, 'db> { + fn replace_region(&mut self, br: BoundRegion) -> Region<'db> { + let entry = self.map.entry(br.var); + let index = entry.index(); + let var = BoundVar::from_usize(index); + let kind = (*entry.or_insert_with(|| BoundVarKind::Region(BoundRegionKind::Anon))) + .expect_region(); + let br = BoundRegion { var, kind }; + Region::new_bound(self.interner, DebruijnIndex::ZERO, br) + } + fn replace_ty(&mut self, bt: BoundTy) -> Ty<'db> { + let entry = self.map.entry(bt.var); + let index = entry.index(); + let var = BoundVar::from_usize(index); + let kind = + (*entry.or_insert_with(|| BoundVarKind::Ty(BoundTyKind::Anon))).expect_ty(); + Ty::new_bound(self.interner, DebruijnIndex::ZERO, BoundTy { var, kind }) + } + fn replace_const(&mut self, bv: BoundVar) -> Const<'db> { + let entry = self.map.entry(bv); + let index = entry.index(); + let var = BoundVar::from_usize(index); + let () = (*entry.or_insert_with(|| BoundVarKind::Const)).expect_const(); + Const::new_bound(self.interner, DebruijnIndex::ZERO, var) + } + } + + let mut map = Default::default(); + let delegate = Anonymize { interner: self, map: &mut map }; + let inner = self.replace_escaping_bound_vars_uncached(value.skip_binder(), delegate); + let bound_vars = CollectAndApply::collect_and_apply(map.into_values(), |xs| { + BoundVarKinds::new_from_iter(self, xs.iter().cloned()) + }); + Binder::bind_with_vars(inner, bound_vars) + } + + fn opaque_types_defined_by(self, defining_anchor: Self::LocalDefId) -> Self::LocalDefIds { + // FIXME(next-solver) + SolverDefIds::new_from_iter(self, []) + } + + fn alias_has_const_conditions(self, def_id: Self::DefId) -> bool { + // FIXME(next-solver) + false + } + + fn explicit_implied_const_bounds( + self, + def_id: Self::DefId, + ) -> rustc_type_ir::EarlyBinder< + Self, + impl IntoIterator>>, + > { + // FIXME(next-solver) + rustc_type_ir::EarlyBinder::bind([]) + } + + fn fn_is_const(self, def_id: Self::DefId) -> bool { + let id = match def_id { + SolverDefId::FunctionId(id) => id, + _ => unreachable!(), + }; + self.db().function_signature(id).flags.contains(FnFlags::CONST) + } + + fn impl_is_const(self, def_id: Self::DefId) -> bool { + false + } + + fn opt_alias_variances( + self, + kind: impl Into, + def_id: Self::DefId, + ) -> Option { + None + } + + fn type_of_opaque_hir_typeck( + self, + def_id: Self::LocalDefId, + ) -> rustc_type_ir::EarlyBinder { + // FIXME(next-solver) + unimplemented!() + } + + fn coroutine_hidden_types( + self, + def_id: Self::DefId, + ) -> rustc_type_ir::EarlyBinder< + Self, + rustc_type_ir::Binder>, + > { + // FIXME(next-solver) + unimplemented!() + } + + fn is_default_trait(self, def_id: Self::DefId) -> bool { + self.as_lang_item(def_id).map_or(false, |l| matches!(l, TraitSolverLangItem::Sized)) + } + + fn trait_is_coinductive(self, trait_def_id: Self::DefId) -> bool { + let id = match trait_def_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + self.db().trait_signature(id).flags.contains(TraitFlags::COINDUCTIVE) + } + + fn trait_is_unsafe(self, trait_def_id: Self::DefId) -> bool { + let id = match trait_def_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + self.db().trait_signature(id).flags.contains(TraitFlags::UNSAFE) + } + + fn impl_self_is_guaranteed_unsized(self, def_id: Self::DefId) -> bool { + false + } + + fn impl_specializes(self, impl_def_id: Self::DefId, victim_def_id: Self::DefId) -> bool { + false + } + + fn next_trait_solver_globally(self) -> bool { + true + } + + fn opaque_types_and_coroutines_defined_by( + self, + defining_anchor: Self::LocalDefId, + ) -> Self::LocalDefIds { + // FIXME(next-solver) + unimplemented!() + } +} + +impl<'db> DbInterner<'db> { + pub fn shift_bound_var_indices(self, bound_vars: usize, value: T) -> T + where + T: rustc_type_ir::TypeFoldable, + { + let shift_bv = |bv: BoundVar| BoundVar::from_usize(bv.as_usize() + bound_vars); + self.replace_escaping_bound_vars_uncached( + value, + FnMutDelegate { + regions: &mut |r: BoundRegion| { + Region::new_bound( + self, + DebruijnIndex::ZERO, + BoundRegion { var: shift_bv(r.var), kind: r.kind }, + ) + }, + types: &mut |t: BoundTy| { + Ty::new_bound( + self, + DebruijnIndex::ZERO, + BoundTy { var: shift_bv(t.var), kind: t.kind }, + ) + }, + consts: &mut |c| Const::new_bound(self, DebruijnIndex::ZERO, shift_bv(c)), + }, + ) + } + + pub fn replace_escaping_bound_vars_uncached>>( + self, + value: T, + delegate: impl BoundVarReplacerDelegate<'db>, + ) -> T { + if !value.has_escaping_bound_vars() { + value + } else { + let mut replacer = BoundVarReplacer::new(self, delegate); + value.fold_with(&mut replacer) + } + } + + pub fn replace_bound_vars_uncached>>( + self, + value: Binder<'db, T>, + delegate: impl BoundVarReplacerDelegate<'db>, + ) -> T { + self.replace_escaping_bound_vars_uncached(value.skip_binder(), delegate) + } +} + +macro_rules! TrivialTypeTraversalImpls { + ($($ty:ty,)+) => { + $( + impl<'db> rustc_type_ir::TypeFoldable> for $ty { + fn try_fold_with>>( + self, + _: &mut F, + ) -> ::std::result::Result { + Ok(self) + } + + #[inline] + fn fold_with>>( + self, + _: &mut F, + ) -> Self { + self + } + } + + impl<'db> rustc_type_ir::TypeVisitable> for $ty { + #[inline] + fn visit_with>>( + &self, + _: &mut F) + -> F::Result + { + ::output() + } + } + )+ + }; +} + +TrivialTypeTraversalImpls! { + SolverDefId, + Pattern<'db>, + Safety, + FnAbi, + Span, + ParamConst, + ParamTy, + BoundRegion, + BoundVar, + Placeholder, + Placeholder, + Placeholder, +} + +pub(crate) use tls_cache::with_new_cache; +mod tls_cache { + use crate::db::HirDatabase; + + use super::DbInterner; + use rustc_type_ir::search_graph::GlobalCache; + use std::cell::RefCell; + + scoped_tls::scoped_thread_local!(static GLOBAL_CACHE: RefCell>>); + + pub(crate) fn with_new_cache(f: impl FnOnce() -> T) -> T { + GLOBAL_CACHE.set(&RefCell::new(GlobalCache::default()), f) + } + + pub(super) fn with_cache<'db, T>( + _db: &'db dyn HirDatabase, + f: impl FnOnce(&mut GlobalCache>) -> T, + ) -> T { + // SAFETY: No idea + let call = move |slot: &RefCell<_>| { + f(unsafe { + std::mem::transmute::< + &mut GlobalCache>, + &mut GlobalCache>, + >(&mut *slot.borrow_mut()) + }) + }; + if GLOBAL_CACHE.is_set() { + GLOBAL_CACHE.with(call) + } else { + GLOBAL_CACHE.set(&RefCell::new(GlobalCache::default()), || GLOBAL_CACHE.with(call)) + } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs new file mode 100644 index 000000000000..4d32b2713235 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs @@ -0,0 +1,267 @@ +//! Things related to IR printing in the next-trait-solver. + +use std::any::type_name_of_val; + +use rustc_type_ir::inherent::SliceLike; +use rustc_type_ir::{self as ty, ir_print::IrPrint}; + +use crate::db::HirDatabase; + +use super::SolverDefId; +use super::interner::DbInterner; + +impl<'db> IrPrint> for DbInterner<'db> { + fn print(t: &ty::AliasTy, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug(t: &ty::AliasTy, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + salsa::with_attached_database(|db| match t.def_id { + SolverDefId::TypeAliasId(id) => fmt.write_str(&format!( + "AliasTy({:?}[{:?}])", + db.as_view::().type_alias_signature(id).name.as_str(), + t.args + )), + SolverDefId::InternedOpaqueTyId(id) => { + fmt.write_str(&format!("AliasTy({:?}[{:?}])", id, t.args)) + } + _ => panic!("Expected TypeAlias or OpaqueTy."), + }) + .unwrap_or_else(|| fmt.write_str(&format!("AliasTy({:?}[{:?}])", t.def_id, t.args))) + } +} + +impl<'db> IrPrint> for DbInterner<'db> { + fn print(t: &ty::AliasTerm, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug(t: &ty::AliasTerm, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + salsa::with_attached_database(|db| match t.def_id { + SolverDefId::TypeAliasId(id) => fmt.write_str(&format!( + "AliasTerm({:?}[{:?}])", + db.as_view::().type_alias_signature(id).name.as_str(), + t.args + )), + SolverDefId::InternedOpaqueTyId(id) => { + fmt.write_str(&format!("AliasTerm({:?}[{:?}])", id, t.args)) + } + _ => panic!("Expected TypeAlias or OpaqueTy."), + }) + .unwrap_or_else(|| fmt.write_str(&format!("AliasTerm({:?}[{:?}])", t.def_id, t.args))) + } +} +impl<'db> IrPrint> for DbInterner<'db> { + fn print(t: &ty::TraitRef, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug(t: &ty::TraitRef, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + salsa::with_attached_database(|db| { + let trait_ = match t.def_id { + SolverDefId::TraitId(id) => id, + _ => panic!("Expected trait."), + }; + let self_ty = &t.args.as_slice()[0]; + let trait_args = &t.args.as_slice()[1..]; + if trait_args.is_empty() { + fmt.write_str(&format!( + "{:?}: {}", + self_ty, + db.as_view::().trait_signature(trait_).name.as_str() + )) + } else { + fmt.write_str(&format!( + "{:?}: {}<{:?}>", + self_ty, + db.as_view::().trait_signature(trait_).name.as_str(), + trait_args + )) + } + }) + .unwrap_or_else(|| fmt.write_str(&format!("TraitRef({:?}[{:?}])", t.def_id, t.args))) + } +} +impl<'db> IrPrint> for DbInterner<'db> { + fn print(t: &ty::TraitPredicate, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug( + t: &ty::TraitPredicate, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + fmt.write_str(&format!("TODO: {:?}", type_name_of_val(t))) + } +} +impl<'db> IrPrint> for DbInterner<'db> { + fn print( + t: &rustc_type_ir::HostEffectPredicate, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug( + t: &rustc_type_ir::HostEffectPredicate, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + fmt.write_str(&format!("TODO: {:?}", type_name_of_val(t))) + } +} +impl<'db> IrPrint> for DbInterner<'db> { + fn print( + t: &ty::ExistentialTraitRef, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug( + t: &ty::ExistentialTraitRef, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + salsa::with_attached_database(|db| { + let trait_ = match t.def_id { + SolverDefId::TraitId(id) => id, + _ => panic!("Expected trait."), + }; + fmt.write_str(&format!( + "ExistentialTraitRef({:?}[{:?}])", + db.as_view::().trait_signature(trait_).name.as_str(), + t.args + )) + }) + .unwrap_or_else(|| { + fmt.write_str(&format!("ExistentialTraitRef({:?}[{:?}])", t.def_id, t.args)) + }) + } +} +impl<'db> IrPrint> for DbInterner<'db> { + fn print( + t: &ty::ExistentialProjection, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug( + t: &ty::ExistentialProjection, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + salsa::with_attached_database(|db| { + let id = match t.def_id { + SolverDefId::TypeAliasId(id) => id, + _ => panic!("Expected trait."), + }; + fmt.write_str(&format!( + "ExistentialProjection(({:?}[{:?}]) -> {:?})", + db.as_view::().type_alias_signature(id).name.as_str(), + t.args, + t.term + )) + }) + .unwrap_or_else(|| { + fmt.write_str(&format!( + "ExistentialProjection(({:?}[{:?}]) -> {:?})", + t.def_id, t.args, t.term + )) + }) + } +} +impl<'db> IrPrint> for DbInterner<'db> { + fn print( + t: &ty::ProjectionPredicate, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug( + t: &ty::ProjectionPredicate, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + salsa::with_attached_database(|db| { + let id = match t.projection_term.def_id { + SolverDefId::TypeAliasId(id) => id, + _ => panic!("Expected trait."), + }; + fmt.write_str(&format!( + "ProjectionPredicate(({:?}[{:?}]) -> {:?})", + db.as_view::().type_alias_signature(id).name.as_str(), + t.projection_term.args, + t.term + )) + }) + .unwrap_or_else(|| { + fmt.write_str(&format!( + "ProjectionPredicate(({:?}[{:?}]) -> {:?})", + t.projection_term.def_id, t.projection_term.args, t.term + )) + }) + } +} +impl<'db> IrPrint> for DbInterner<'db> { + fn print(t: &ty::NormalizesTo, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug( + t: &ty::NormalizesTo, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + fmt.write_str(&format!("TODO: {:?}", type_name_of_val(t))) + } +} +impl<'db> IrPrint> for DbInterner<'db> { + fn print( + t: &ty::SubtypePredicate, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug( + t: &ty::SubtypePredicate, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + fmt.write_str(&format!("TODO: {:?}", type_name_of_val(t))) + } +} +impl<'db> IrPrint> for DbInterner<'db> { + fn print(t: &ty::CoercePredicate, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug( + t: &ty::CoercePredicate, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + fmt.write_str(&format!("TODO: {:?}", type_name_of_val(t))) + } +} +impl<'db> IrPrint> for DbInterner<'db> { + fn print(t: &ty::FnSig, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug(t: &ty::FnSig, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fmt.write_str(&format!("TODO: {:?}", type_name_of_val(t))) + } +} + +impl<'db> IrPrint>> for DbInterner<'db> { + fn print( + t: &rustc_type_ir::PatternKind>, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + Self::print_debug(t, fmt) + } + + fn print_debug( + t: &rustc_type_ir::PatternKind>, + fmt: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + fmt.write_str(&format!("TODO: {:?}", type_name_of_val(t))) + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs new file mode 100644 index 000000000000..3a3206bef38b --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -0,0 +1,1368 @@ +//! Things useful for mapping to/from Chalk and next-trait-solver types. + +use base_db::Crate; +use chalk_ir::{ + CanonicalVarKind, CanonicalVarKinds, ForeignDefId, InferenceVar, Substitution, TyVariableKind, + WellFormed, fold::Shift, interner::HasInterner, +}; +use hir_def::{ + CallableDefId, ConstParamId, FunctionId, GeneralConstId, LifetimeParamId, TypeAliasId, + TypeOrConstParamId, TypeParamId, signatures::TraitFlags, +}; +use intern::sym; +use rustc_type_ir::{ + AliasTerm, BoundVar, DebruijnIndex, ExistentialProjection, ExistentialTraitRef, Interner as _, + OutlivesPredicate, ProjectionPredicate, TypeFoldable, TypeSuperFoldable, TypeVisitable, + TypeVisitableExt, UniverseIndex, elaborate, + inherent::{BoundVarLike, Clause as _, IntoKind, PlaceholderLike, SliceLike, Ty as _}, + shift_vars, + solve::Goal, +}; +use salsa::plumbing::AsId; + +use crate::{ + ConcreteConst, ConstScalar, ImplTraitId, Interner, + db::{ + HirDatabase, InternedClosureId, InternedCoroutineId, InternedOpaqueTyId, + InternedTypeOrConstParamId, + }, + from_assoc_type_id, from_chalk_trait_id, + mapping::ToChalk, + next_solver::{ + Binder, ClauseKind, ConstBytes, TraitPredicate, UnevaluatedConst, + interner::{AdtDef, BoundVarKind, BoundVarKinds, DbInterner}, + }, + to_assoc_type_id, to_chalk_trait_id, to_foreign_def_id, +}; + +use super::{ + BoundExistentialPredicate, BoundExistentialPredicates, BoundRegion, BoundRegionKind, BoundTy, + BoundTyKind, Canonical, CanonicalVars, Clause, Clauses, Const, Ctor, EarlyParamRegion, + ErrorGuaranteed, ExistentialPredicate, GenericArg, GenericArgs, ParamConst, ParamEnv, ParamTy, + Placeholder, PlaceholderConst, PlaceholderRegion, PlaceholderTy, Predicate, PredicateKind, + Region, SolverDefId, SubtypePredicate, Term, TraitRef, Ty, Tys, ValueConst, VariancesOf, +}; + +pub fn to_placeholder_idx( + db: &dyn HirDatabase, + id: TypeOrConstParamId, + map: impl Fn(BoundVar) -> T, +) -> Placeholder { + let interned_id = InternedTypeOrConstParamId::new(db, id); + Placeholder { + universe: UniverseIndex::ZERO, + bound: map(BoundVar::from_usize(interned_id.as_id().index() as usize)), + } +} + +pub fn convert_binder_to_early_binder<'db, T: rustc_type_ir::TypeFoldable>>( + interner: DbInterner<'db>, + binder: rustc_type_ir::Binder, T>, +) -> rustc_type_ir::EarlyBinder, T> { + let mut folder = BinderToEarlyBinder { interner, debruijn: rustc_type_ir::DebruijnIndex::ZERO }; + rustc_type_ir::EarlyBinder::bind(binder.skip_binder().fold_with(&mut folder)) +} + +struct BinderToEarlyBinder<'db> { + interner: DbInterner<'db>, + debruijn: rustc_type_ir::DebruijnIndex, +} + +impl<'db> rustc_type_ir::TypeFolder> for BinderToEarlyBinder<'db> { + fn cx(&self) -> DbInterner<'db> { + self.interner + } + + fn fold_binder( + &mut self, + t: rustc_type_ir::Binder, T>, + ) -> rustc_type_ir::Binder, T> + where + T: TypeFoldable>, + { + self.debruijn.shift_in(1); + let result = t.super_fold_with(self); + self.debruijn.shift_out(1); + result + } + + fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> { + match t.kind() { + rustc_type_ir::TyKind::Bound(debruijn, bound_ty) if self.debruijn == debruijn => { + let var: rustc_type_ir::BoundVar = bound_ty.var(); + Ty::new(self.cx(), rustc_type_ir::TyKind::Param(ParamTy { index: var.as_u32() })) + } + _ => t.super_fold_with(self), + } + } + + fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { + match r.kind() { + rustc_type_ir::ReBound(debruijn, bound_region) if self.debruijn == debruijn => { + let var: rustc_type_ir::BoundVar = bound_region.var(); + Region::new( + self.cx(), + rustc_type_ir::RegionKind::ReEarlyParam(EarlyParamRegion { + index: var.as_u32(), + }), + ) + } + _ => r, + } + } + + fn fold_const(&mut self, c: Const<'db>) -> Const<'db> { + match c.kind() { + rustc_type_ir::ConstKind::Bound(debruijn, var) if self.debruijn == debruijn => { + Const::new( + self.cx(), + rustc_type_ir::ConstKind::Param(ParamConst { index: var.as_u32() }), + ) + } + _ => c.super_fold_with(self), + } + } +} + +pub trait ChalkToNextSolver<'db, Out> { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> Out; +} + +impl<'db> ChalkToNextSolver<'db, Ty<'db>> for chalk_ir::Ty { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> Ty<'db> { + Ty::new( + interner, + match self.kind(Interner) { + chalk_ir::TyKind::Adt(adt_id, substitution) => { + let def = AdtDef::new(adt_id.0, interner); + let args = substitution.to_nextsolver(interner); + rustc_type_ir::TyKind::Adt(def, args) + } + chalk_ir::TyKind::AssociatedType(assoc_type_id, substitution) => { + let def_id = SolverDefId::TypeAliasId(from_assoc_type_id(*assoc_type_id)); + let args: GenericArgs<'db> = substitution.to_nextsolver(interner); + let alias_ty = rustc_type_ir::AliasTy::new(interner, def_id, args.iter()); + rustc_type_ir::TyKind::Alias(rustc_type_ir::AliasTyKind::Projection, alias_ty) + } + chalk_ir::TyKind::Scalar(scalar) => match scalar { + chalk_ir::Scalar::Bool => rustc_type_ir::TyKind::Bool, + chalk_ir::Scalar::Char => rustc_type_ir::TyKind::Char, + chalk_ir::Scalar::Int(chalk_ir::IntTy::Isize) => { + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::Isize) + } + chalk_ir::Scalar::Int(chalk_ir::IntTy::I8) => { + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I8) + } + chalk_ir::Scalar::Int(chalk_ir::IntTy::I16) => { + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I16) + } + chalk_ir::Scalar::Int(chalk_ir::IntTy::I32) => { + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I32) + } + chalk_ir::Scalar::Int(chalk_ir::IntTy::I64) => { + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I64) + } + chalk_ir::Scalar::Int(chalk_ir::IntTy::I128) => { + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I128) + } + chalk_ir::Scalar::Uint(chalk_ir::UintTy::Usize) => { + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::Usize) + } + chalk_ir::Scalar::Uint(chalk_ir::UintTy::U8) => { + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U8) + } + chalk_ir::Scalar::Uint(chalk_ir::UintTy::U16) => { + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U16) + } + chalk_ir::Scalar::Uint(chalk_ir::UintTy::U32) => { + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U32) + } + chalk_ir::Scalar::Uint(chalk_ir::UintTy::U64) => { + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U64) + } + chalk_ir::Scalar::Uint(chalk_ir::UintTy::U128) => { + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U128) + } + chalk_ir::Scalar::Float(chalk_ir::FloatTy::F16) => { + rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F16) + } + chalk_ir::Scalar::Float(chalk_ir::FloatTy::F32) => { + rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F32) + } + chalk_ir::Scalar::Float(chalk_ir::FloatTy::F64) => { + rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F64) + } + chalk_ir::Scalar::Float(chalk_ir::FloatTy::F128) => { + rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F128) + } + }, + chalk_ir::TyKind::Tuple(_, substitution) => { + let args = substitution.to_nextsolver(interner); + rustc_type_ir::TyKind::Tuple(args) + } + chalk_ir::TyKind::Array(ty, len) => rustc_type_ir::TyKind::Array( + ty.to_nextsolver(interner), + len.to_nextsolver(interner), + ), + chalk_ir::TyKind::Slice(ty) => { + rustc_type_ir::TyKind::Slice(ty.to_nextsolver(interner)) + } + chalk_ir::TyKind::Raw(mutability, ty) => rustc_type_ir::RawPtr( + ty.to_nextsolver(interner), + mutability.to_nextsolver(interner), + ), + chalk_ir::TyKind::Ref(mutability, lifetime, ty) => rustc_type_ir::TyKind::Ref( + lifetime.to_nextsolver(interner), + ty.to_nextsolver(interner), + mutability.to_nextsolver(interner), + ), + chalk_ir::TyKind::OpaqueType(def_id, substitution) => { + let id: InternedOpaqueTyId = (*def_id).into(); + let args: GenericArgs<'db> = substitution.to_nextsolver(interner); + let alias_ty = rustc_type_ir::AliasTy::new(interner, id.into(), args); + rustc_type_ir::TyKind::Alias(rustc_type_ir::AliasTyKind::Opaque, alias_ty) + } + chalk_ir::TyKind::FnDef(fn_def_id, substitution) => { + let def_id = CallableDefId::from_chalk(interner.db(), *fn_def_id); + let id: SolverDefId = match def_id { + CallableDefId::FunctionId(id) => id.into(), + CallableDefId::StructId(id) => SolverDefId::Ctor(Ctor::Struct(id)), + CallableDefId::EnumVariantId(id) => SolverDefId::Ctor(Ctor::Enum(id)), + }; + rustc_type_ir::TyKind::FnDef(id, substitution.to_nextsolver(interner)) + } + chalk_ir::TyKind::Str => rustc_type_ir::TyKind::Str, + chalk_ir::TyKind::Never => rustc_type_ir::TyKind::Never, + chalk_ir::TyKind::Closure(closure_id, substitution) => { + let id: InternedClosureId = (*closure_id).into(); + rustc_type_ir::TyKind::Closure(id.into(), substitution.to_nextsolver(interner)) + } + chalk_ir::TyKind::Coroutine(coroutine_id, substitution) => { + let id: InternedCoroutineId = (*coroutine_id).into(); + rustc_type_ir::TyKind::Coroutine( + id.into(), + substitution.to_nextsolver(interner), + ) + } + chalk_ir::TyKind::CoroutineWitness(coroutine_id, substitution) => { + let id: InternedCoroutineId = (*coroutine_id).into(); + rustc_type_ir::TyKind::CoroutineWitness( + id.into(), + substitution.to_nextsolver(interner), + ) + } + chalk_ir::TyKind::Foreign(foreign_def_id) => rustc_type_ir::TyKind::Foreign( + SolverDefId::ForeignId(crate::from_foreign_def_id(*foreign_def_id)), + ), + chalk_ir::TyKind::Error => rustc_type_ir::TyKind::Error(ErrorGuaranteed), + chalk_ir::TyKind::Placeholder(placeholder_index) => { + rustc_type_ir::TyKind::Placeholder(PlaceholderTy::new_anon( + placeholder_index.ui.to_nextsolver(interner), + rustc_type_ir::BoundVar::from_usize(placeholder_index.idx), + )) + } + chalk_ir::TyKind::Dyn(dyn_ty) => { + // exists { for<...> ^1.0: ... } + let bounds = BoundExistentialPredicates::new_from_iter( + interner, + dyn_ty.bounds.skip_binders().iter(Interner).filter_map(|pred| { + // for<...> ^1.0: ... + let (val, binders) = pred.clone().into_value_and_skipped_binders(); + let bound_vars = binders.to_nextsolver(interner); + let clause = match val { + chalk_ir::WhereClause::Implemented(trait_ref) => { + let trait_id = from_chalk_trait_id(trait_ref.trait_id); + if interner + .db() + .trait_signature(trait_id) + .flags + .contains(TraitFlags::AUTO) + { + ExistentialPredicate::AutoTrait(SolverDefId::TraitId( + trait_id, + )) + } else { + let def_id = SolverDefId::TraitId(trait_id); + let args = GenericArgs::new_from_iter( + interner, + trait_ref + .substitution + .iter(Interner) + .skip(1) + .map(|a| a.clone().shifted_out(Interner).unwrap()) + .map(|a| a.to_nextsolver(interner)), + ); + let trait_ref = ExistentialTraitRef::new_from_args( + interner, def_id, args, + ); + ExistentialPredicate::Trait(trait_ref) + } + } + chalk_ir::WhereClause::AliasEq(alias_eq) => { + let (def_id, args) = match &alias_eq.alias { + chalk_ir::AliasTy::Projection(projection) => { + let id = + from_assoc_type_id(projection.associated_ty_id); + let def_id = SolverDefId::TypeAliasId(id); + let generics = interner.generics_of(def_id); + let parent_len = generics.parent_count; + let substs = projection.substitution.iter(Interner).skip(1); + + let args = GenericArgs::new_from_iter( + interner, + substs + .map(|a| { + a.clone().shifted_out(Interner).unwrap() + }) + .map(|a| a.to_nextsolver(interner)), + ); + (def_id, args) + } + chalk_ir::AliasTy::Opaque(opaque_ty) => { + panic!("Invalid ExistentialPredicate (opaques can't be named)."); + } + }; + let term = alias_eq + .ty + .clone() + .shifted_out(Interner) + .unwrap() + .to_nextsolver(interner) + .into(); + let projection = ExistentialProjection::new_from_args( + interner, def_id, args, term, + ); + ExistentialPredicate::Projection(projection) + } + chalk_ir::WhereClause::LifetimeOutlives(lifetime_outlives) => { + return None; + } + chalk_ir::WhereClause::TypeOutlives(type_outlives) => return None, + }; + + Some(Binder::bind_with_vars(clause, bound_vars)) + }), + ); + let region = dyn_ty.lifetime.to_nextsolver(interner); + let kind = rustc_type_ir::DynKind::Dyn; + rustc_type_ir::TyKind::Dynamic(bounds, region, kind) + } + chalk_ir::TyKind::Alias(alias_ty) => match alias_ty { + chalk_ir::AliasTy::Projection(projection_ty) => { + let def_id = SolverDefId::TypeAliasId(from_assoc_type_id( + projection_ty.associated_ty_id, + )); + let alias_ty = rustc_type_ir::AliasTy::new_from_args( + interner, + def_id, + projection_ty.substitution.to_nextsolver(interner), + ); + rustc_type_ir::TyKind::Alias( + rustc_type_ir::AliasTyKind::Projection, + alias_ty, + ) + } + chalk_ir::AliasTy::Opaque(opaque_ty) => { + let id: InternedOpaqueTyId = opaque_ty.opaque_ty_id.into(); + let def_id = SolverDefId::InternedOpaqueTyId(id); + let alias_ty = rustc_type_ir::AliasTy::new_from_args( + interner, + def_id, + opaque_ty.substitution.to_nextsolver(interner), + ); + rustc_type_ir::TyKind::Alias(rustc_type_ir::AliasTyKind::Opaque, alias_ty) + } + }, + chalk_ir::TyKind::Function(fn_pointer) => { + let sig_tys = fn_pointer.clone().into_binders(Interner).to_nextsolver(interner); + let header = rustc_type_ir::FnHeader { + abi: fn_pointer.sig.abi, + c_variadic: fn_pointer.sig.variadic, + safety: match fn_pointer.sig.safety { + chalk_ir::Safety::Safe => super::abi::Safety::Safe, + chalk_ir::Safety::Unsafe => super::abi::Safety::Unsafe, + }, + }; + + rustc_type_ir::TyKind::FnPtr(sig_tys, header) + } + chalk_ir::TyKind::BoundVar(bound_var) => rustc_type_ir::TyKind::Bound( + bound_var.debruijn.to_nextsolver(interner), + BoundTy { + var: rustc_type_ir::BoundVar::from_usize(bound_var.index), + kind: BoundTyKind::Anon, + }, + ), + chalk_ir::TyKind::InferenceVar(inference_var, ty_variable_kind) => { + rustc_type_ir::TyKind::Infer( + (*inference_var, *ty_variable_kind).to_nextsolver(interner), + ) + } + }, + ) + } +} + +impl<'db> ChalkToNextSolver<'db, Region<'db>> for chalk_ir::Lifetime { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> Region<'db> { + Region::new( + interner, + match self.data(Interner) { + chalk_ir::LifetimeData::BoundVar(bound_var) => rustc_type_ir::RegionKind::ReBound( + bound_var.debruijn.to_nextsolver(interner), + BoundRegion { + var: rustc_type_ir::BoundVar::from_u32(bound_var.index as u32), + kind: BoundRegionKind::Anon, + }, + ), + chalk_ir::LifetimeData::InferenceVar(inference_var) => { + rustc_type_ir::RegionKind::ReVar(rustc_type_ir::RegionVid::from_u32( + inference_var.index(), + )) + } + chalk_ir::LifetimeData::Placeholder(placeholder_index) => { + rustc_type_ir::RegionKind::RePlaceholder(PlaceholderRegion::new_anon( + rustc_type_ir::UniverseIndex::from_u32(placeholder_index.ui.counter as u32), + rustc_type_ir::BoundVar::from_u32(placeholder_index.idx as u32), + )) + } + chalk_ir::LifetimeData::Static => rustc_type_ir::RegionKind::ReStatic, + chalk_ir::LifetimeData::Erased => rustc_type_ir::RegionKind::ReErased, + chalk_ir::LifetimeData::Phantom(_, _) => { + unreachable!() + } + chalk_ir::LifetimeData::Error => { + rustc_type_ir::RegionKind::ReError(ErrorGuaranteed) + } + }, + ) + } +} + +impl<'db> ChalkToNextSolver<'db, Const<'db>> for chalk_ir::Const { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> Const<'db> { + let data = self.data(Interner); + Const::new( + interner, + match &data.value { + chalk_ir::ConstValue::BoundVar(bound_var) => rustc_type_ir::ConstKind::Bound( + bound_var.debruijn.to_nextsolver(interner), + rustc_type_ir::BoundVar::from_usize(bound_var.index), + ), + chalk_ir::ConstValue::InferenceVar(inference_var) => { + rustc_type_ir::ConstKind::Infer(rustc_type_ir::InferConst::Var( + rustc_type_ir::ConstVid::from_u32(inference_var.index()), + )) + } + chalk_ir::ConstValue::Placeholder(placeholder_index) => { + rustc_type_ir::ConstKind::Placeholder(PlaceholderConst::new( + placeholder_index.ui.to_nextsolver(interner), + rustc_type_ir::BoundVar::from_usize(placeholder_index.idx), + )) + } + chalk_ir::ConstValue::Concrete(concrete_const) => match &concrete_const.interned { + ConstScalar::Bytes(bytes, memory) => { + rustc_type_ir::ConstKind::Value(ValueConst::new( + data.ty.to_nextsolver(interner), + ConstBytes(bytes.clone(), memory.clone()), + )) + } + ConstScalar::UnevaluatedConst(c, subst) => { + let def = match *c { + GeneralConstId::ConstId(id) => SolverDefId::ConstId(id), + GeneralConstId::StaticId(id) => SolverDefId::StaticId(id), + }; + let args = subst.to_nextsolver(interner); + rustc_type_ir::ConstKind::Unevaluated(UnevaluatedConst::new(def, args)) + } + ConstScalar::Unknown => rustc_type_ir::ConstKind::Error(ErrorGuaranteed), + }, + }, + ) + } +} + +impl<'db> ChalkToNextSolver<'db, rustc_type_ir::FnSigTys>> + for chalk_ir::FnSubst +{ + fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_type_ir::FnSigTys> { + rustc_type_ir::FnSigTys { + inputs_and_output: Tys::new_from_iter( + interner, + self.0.iter(Interner).map(|g| g.assert_ty_ref(Interner).to_nextsolver(interner)), + ), + } + } +} + +impl< + 'db, + U: TypeVisitable>, + T: Clone + ChalkToNextSolver<'db, U> + HasInterner, +> ChalkToNextSolver<'db, rustc_type_ir::Binder, U>> for chalk_ir::Binders +{ + fn to_nextsolver( + &self, + interner: DbInterner<'db>, + ) -> rustc_type_ir::Binder, U> { + let (val, binders) = self.clone().into_value_and_skipped_binders(); + rustc_type_ir::Binder::bind_with_vars( + val.to_nextsolver(interner), + binders.to_nextsolver(interner), + ) + } +} + +impl<'db> ChalkToNextSolver<'db, BoundVarKinds> for chalk_ir::VariableKinds { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> BoundVarKinds { + BoundVarKinds::new_from_iter( + interner, + self.iter(Interner).map(|v| v.to_nextsolver(interner)), + ) + } +} + +impl<'db> ChalkToNextSolver<'db, BoundVarKind> for chalk_ir::VariableKind { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> BoundVarKind { + match self { + chalk_ir::VariableKind::Ty(_ty_variable_kind) => BoundVarKind::Ty(BoundTyKind::Anon), + chalk_ir::VariableKind::Lifetime => BoundVarKind::Region(BoundRegionKind::Anon), + chalk_ir::VariableKind::Const(_ty) => BoundVarKind::Const, + } + } +} + +impl<'db> ChalkToNextSolver<'db, GenericArg<'db>> for chalk_ir::GenericArg { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> GenericArg<'db> { + match self.data(Interner) { + chalk_ir::GenericArgData::Ty(ty) => ty.to_nextsolver(interner).into(), + chalk_ir::GenericArgData::Lifetime(lifetime) => lifetime.to_nextsolver(interner).into(), + chalk_ir::GenericArgData::Const(const_) => const_.to_nextsolver(interner).into(), + } + } +} +impl<'db> ChalkToNextSolver<'db, GenericArgs<'db>> for chalk_ir::Substitution { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> GenericArgs<'db> { + GenericArgs::new_from_iter( + interner, + self.iter(Interner).map(|arg| -> GenericArg<'db> { arg.to_nextsolver(interner) }), + ) + } +} + +impl<'db> ChalkToNextSolver<'db, Tys<'db>> for chalk_ir::Substitution { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> Tys<'db> { + Tys::new_from_iter( + interner, + self.iter(Interner).map(|arg| -> Ty<'db> { + match arg.data(Interner) { + chalk_ir::GenericArgData::Ty(ty) => ty.to_nextsolver(interner), + chalk_ir::GenericArgData::Lifetime(_) => unreachable!(), + chalk_ir::GenericArgData::Const(_) => unreachable!(), + } + }), + ) + } +} + +impl<'db> ChalkToNextSolver<'db, rustc_type_ir::DebruijnIndex> for chalk_ir::DebruijnIndex { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_type_ir::DebruijnIndex { + rustc_type_ir::DebruijnIndex::from_u32(self.depth()) + } +} + +impl<'db> ChalkToNextSolver<'db, rustc_type_ir::UniverseIndex> for chalk_ir::UniverseIndex { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_type_ir::UniverseIndex { + rustc_type_ir::UniverseIndex::from_u32(self.counter as u32) + } +} + +impl<'db> ChalkToNextSolver<'db, rustc_type_ir::InferTy> + for (chalk_ir::InferenceVar, chalk_ir::TyVariableKind) +{ + fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_type_ir::InferTy { + match self.1 { + chalk_ir::TyVariableKind::General => { + rustc_type_ir::InferTy::TyVar(rustc_type_ir::TyVid::from_u32(self.0.index())) + } + chalk_ir::TyVariableKind::Integer => { + rustc_type_ir::InferTy::IntVar(rustc_type_ir::IntVid::from_u32(self.0.index())) + } + chalk_ir::TyVariableKind::Float => { + rustc_type_ir::InferTy::FloatVar(rustc_type_ir::FloatVid::from_u32(self.0.index())) + } + } + } +} + +impl<'db> ChalkToNextSolver<'db, rustc_ast_ir::Mutability> for chalk_ir::Mutability { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_ast_ir::Mutability { + match self { + chalk_ir::Mutability::Mut => rustc_ast_ir::Mutability::Mut, + chalk_ir::Mutability::Not => rustc_ast_ir::Mutability::Not, + } + } +} + +impl<'db> ChalkToNextSolver<'db, rustc_type_ir::Variance> for crate::Variance { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> rustc_type_ir::Variance { + match self { + crate::Variance::Covariant => rustc_type_ir::Variance::Covariant, + crate::Variance::Invariant => rustc_type_ir::Variance::Invariant, + crate::Variance::Contravariant => rustc_type_ir::Variance::Contravariant, + crate::Variance::Bivariant => rustc_type_ir::Variance::Bivariant, + } + } +} + +impl<'db> ChalkToNextSolver<'db, Canonical<'db, Goal, Predicate<'db>>>> + for chalk_ir::Canonical>> +{ + fn to_nextsolver( + &self, + interner: DbInterner<'db>, + ) -> Canonical<'db, Goal, Predicate<'db>>> { + let param_env = self.value.environment.to_nextsolver(interner); + let variables = CanonicalVars::new_from_iter( + interner, + self.binders.iter(Interner).map(|k| match &k.kind { + chalk_ir::VariableKind::Ty(ty_variable_kind) => match ty_variable_kind { + TyVariableKind::General => rustc_type_ir::CanonicalVarKind::Ty( + rustc_type_ir::CanonicalTyVarKind::General(UniverseIndex::ROOT), + ), + TyVariableKind::Integer => { + rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::Int) + } + TyVariableKind::Float => rustc_type_ir::CanonicalVarKind::Ty( + rustc_type_ir::CanonicalTyVarKind::Float, + ), + }, + chalk_ir::VariableKind::Lifetime => { + rustc_type_ir::CanonicalVarKind::Region(UniverseIndex::ROOT) + } + chalk_ir::VariableKind::Const(ty) => { + rustc_type_ir::CanonicalVarKind::Const(UniverseIndex::ROOT) + } + }), + ); + Canonical { + max_universe: UniverseIndex::ROOT, + value: Goal::new(interner, param_env, self.value.goal.to_nextsolver(interner)), + variables, + } + } +} + +impl<'db> ChalkToNextSolver<'db, Predicate<'db>> for chalk_ir::Goal { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> Predicate<'db> { + match self.data(Interner) { + chalk_ir::GoalData::Quantified(quantifier_kind, binders) => { + if !binders.binders.is_empty(Interner) { + panic!("Should not be constructed."); + } + let (val, _) = binders.clone().into_value_and_skipped_binders(); + val.shifted_out(Interner).unwrap().to_nextsolver(interner) + } + chalk_ir::GoalData::Implies(program_clauses, goal) => { + panic!("Should not be constructed.") + } + chalk_ir::GoalData::All(goals) => panic!("Should not be constructed."), + chalk_ir::GoalData::Not(goal) => panic!("Should not be constructed."), + chalk_ir::GoalData::EqGoal(eq_goal) => panic!("Should not be constructed."), + chalk_ir::GoalData::SubtypeGoal(subtype_goal) => { + let subtype_predicate = SubtypePredicate { + a: subtype_goal.a.to_nextsolver(interner), + b: subtype_goal.b.to_nextsolver(interner), + a_is_expected: true, + }; + let pred_kind = PredicateKind::Subtype(subtype_predicate); + let pred_kind = Binder::bind_with_vars( + shift_vars(interner, pred_kind, 1), + BoundVarKinds::new_from_iter(interner, []), + ); + Predicate::new(interner, pred_kind) + } + chalk_ir::GoalData::DomainGoal(domain_goal) => { + let pred_kind = domain_goal.to_nextsolver(interner); + let pred_kind = Binder::bind_with_vars( + shift_vars(interner, pred_kind, 1), + BoundVarKinds::new_from_iter(interner, []), + ); + Predicate::new(interner, pred_kind) + } + chalk_ir::GoalData::CannotProve => panic!("Should not be constructed."), + } + } +} + +impl<'db> ChalkToNextSolver<'db, ParamEnv<'db>> for chalk_ir::Environment { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> ParamEnv<'db> { + let clauses = Clauses::new_from_iter( + interner, + self.clauses.iter(Interner).map(|c| c.to_nextsolver(interner)), + ); + let clauses = + Clauses::new_from_iter(interner, elaborate::elaborate(interner, clauses.iter())); + ParamEnv { clauses } + } +} + +impl<'db> ChalkToNextSolver<'db, Clause<'db>> for chalk_ir::ProgramClause { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> Clause<'db> { + Clause(Predicate::new(interner, self.data(Interner).0.to_nextsolver(interner))) + } +} + +impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> + for chalk_ir::ProgramClauseImplication +{ + fn to_nextsolver(&self, interner: DbInterner<'db>) -> PredicateKind<'db> { + assert!(self.conditions.is_empty(Interner)); + assert!(self.constraints.is_empty(Interner)); + self.consequence.to_nextsolver(interner) + } +} + +impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> for chalk_ir::DomainGoal { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> PredicateKind<'db> { + match self { + chalk_ir::DomainGoal::Holds(where_clause) => match where_clause { + chalk_ir::WhereClause::Implemented(trait_ref) => { + let predicate = TraitPredicate { + trait_ref: trait_ref.to_nextsolver(interner), + polarity: rustc_type_ir::PredicatePolarity::Positive, + }; + PredicateKind::Clause(ClauseKind::Trait(predicate)) + } + chalk_ir::WhereClause::AliasEq(alias_eq) => match &alias_eq.alias { + chalk_ir::AliasTy::Projection(p) => { + let def_id = + SolverDefId::TypeAliasId(from_assoc_type_id(p.associated_ty_id)); + let args = p.substitution.to_nextsolver(interner); + let term: Ty<'db> = alias_eq.ty.to_nextsolver(interner); + let term: Term<'db> = term.into(); + let predicate = ProjectionPredicate { + projection_term: AliasTerm::new_from_args(interner, def_id, args), + term, + }; + PredicateKind::Clause(ClauseKind::Projection(predicate)) + } + chalk_ir::AliasTy::Opaque(opaque) => { + let id: InternedOpaqueTyId = opaque.opaque_ty_id.into(); + let def_id = SolverDefId::InternedOpaqueTyId(id); + let args = opaque.substitution.to_nextsolver(interner); + let term: Ty<'db> = alias_eq.ty.to_nextsolver(interner); + let term: Term<'db> = term.into(); + let opaque_ty = Ty::new( + interner, + rustc_type_ir::TyKind::Alias( + rustc_type_ir::AliasTyKind::Opaque, + rustc_type_ir::AliasTy::new_from_args(interner, def_id, args), + ), + ) + .into(); + PredicateKind::AliasRelate( + opaque_ty, + term, + rustc_type_ir::AliasRelationDirection::Equate, + ) + } + }, + chalk_ir::WhereClause::LifetimeOutlives(lifetime_outlives) => { + let predicate = OutlivesPredicate( + lifetime_outlives.a.to_nextsolver(interner), + lifetime_outlives.b.to_nextsolver(interner), + ); + PredicateKind::Clause(ClauseKind::RegionOutlives(predicate)) + } + chalk_ir::WhereClause::TypeOutlives(type_outlives) => { + let predicate = OutlivesPredicate( + type_outlives.ty.to_nextsolver(interner), + type_outlives.lifetime.to_nextsolver(interner), + ); + PredicateKind::Clause(ClauseKind::TypeOutlives(predicate)) + } + }, + chalk_ir::DomainGoal::Normalize(normalize) => { + let proj_ty = match &normalize.alias { + chalk_ir::AliasTy::Projection(proj) => proj, + _ => unimplemented!(), + }; + let args: GenericArgs<'db> = proj_ty.substitution.to_nextsolver(interner); + let alias = rustc_type_ir::AliasTerm::new( + interner, + from_assoc_type_id(proj_ty.associated_ty_id).into(), + args, + ); + let term = normalize.ty.to_nextsolver(interner).into(); + let normalizes_to = rustc_type_ir::NormalizesTo { alias, term }; + PredicateKind::NormalizesTo(normalizes_to) + } + chalk_ir::DomainGoal::WellFormed(well_formed) => { + let term = match well_formed { + WellFormed::Trait(_) => panic!("Should not be constructed."), + WellFormed::Ty(ty) => Term::Ty(ty.to_nextsolver(interner)), + }; + PredicateKind::Clause(rustc_type_ir::ClauseKind::WellFormed(term)) + } + chalk_ir::DomainGoal::FromEnv(from_env) => match from_env { + chalk_ir::FromEnv::Trait(trait_ref) => { + let predicate = TraitPredicate { + trait_ref: trait_ref.to_nextsolver(interner), + polarity: rustc_type_ir::PredicatePolarity::Positive, + }; + PredicateKind::Clause(ClauseKind::Trait(predicate)) + } + chalk_ir::FromEnv::Ty(ty) => PredicateKind::Clause(ClauseKind::WellFormed( + Term::Ty(ty.to_nextsolver(interner)), + )), + }, + chalk_ir::DomainGoal::IsLocal(ty) => panic!("Should not be constructed."), + chalk_ir::DomainGoal::IsUpstream(ty) => panic!("Should not be constructed."), + chalk_ir::DomainGoal::IsFullyVisible(ty) => panic!("Should not be constructed."), + chalk_ir::DomainGoal::LocalImplAllowed(trait_ref) => { + panic!("Should not be constructed.") + } + chalk_ir::DomainGoal::Compatible => panic!("Should not be constructed."), + chalk_ir::DomainGoal::DownstreamType(ty) => panic!("Should not be constructed."), + chalk_ir::DomainGoal::Reveal => panic!("Should not be constructed."), + chalk_ir::DomainGoal::ObjectSafe(trait_id) => panic!("Should not be constructed."), + } + } +} + +impl<'db> ChalkToNextSolver<'db, TraitRef<'db>> for chalk_ir::TraitRef { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> TraitRef<'db> { + let args = self.substitution.to_nextsolver(interner); + TraitRef::new_from_args( + interner, + SolverDefId::TraitId(from_chalk_trait_id(self.trait_id)), + args, + ) + } +} + +impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> for chalk_ir::WhereClause { + fn to_nextsolver(&self, interner: DbInterner<'db>) -> PredicateKind<'db> { + match self { + chalk_ir::WhereClause::Implemented(trait_ref) => { + let predicate = TraitPredicate { + trait_ref: trait_ref.to_nextsolver(interner), + polarity: rustc_type_ir::PredicatePolarity::Positive, + }; + PredicateKind::Clause(ClauseKind::Trait(predicate)) + } + chalk_ir::WhereClause::AliasEq(alias_eq) => { + let projection = match &alias_eq.alias { + chalk_ir::AliasTy::Projection(p) => p, + _ => unimplemented!(), + }; + let def_id = + SolverDefId::TypeAliasId(from_assoc_type_id(projection.associated_ty_id)); + let args = projection.substitution.to_nextsolver(interner); + let term: Ty<'db> = alias_eq.ty.to_nextsolver(interner); + let term: Term<'db> = term.into(); + let predicate = ProjectionPredicate { + projection_term: AliasTerm::new_from_args(interner, def_id, args), + term, + }; + PredicateKind::Clause(ClauseKind::Projection(predicate)) + } + chalk_ir::WhereClause::TypeOutlives(type_outlives) => { + let ty = type_outlives.ty.to_nextsolver(interner); + let r = type_outlives.lifetime.to_nextsolver(interner); + PredicateKind::Clause(ClauseKind::TypeOutlives(OutlivesPredicate(ty, r))) + } + chalk_ir::WhereClause::LifetimeOutlives(lifetime_outlives) => { + let a = lifetime_outlives.a.to_nextsolver(interner); + let b = lifetime_outlives.b.to_nextsolver(interner); + PredicateKind::Clause(ClauseKind::RegionOutlives(OutlivesPredicate(a, b))) + } + } + } +} + +pub fn convert_canonical_args_for_result<'db>( + interner: DbInterner<'db>, + args: Canonical<'db, Vec>>, +) -> chalk_ir::Canonical> { + let Canonical { value, variables, max_universe } = args; + let binders = CanonicalVarKinds::from_iter( + Interner, + variables.iter().map(|v| match v { + rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::General(_)) => { + CanonicalVarKind::new( + chalk_ir::VariableKind::Ty(TyVariableKind::General), + chalk_ir::UniverseIndex::ROOT, + ) + } + rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::Int) => { + CanonicalVarKind::new( + chalk_ir::VariableKind::Ty(TyVariableKind::Integer), + chalk_ir::UniverseIndex::ROOT, + ) + } + rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::Float) => { + CanonicalVarKind::new( + chalk_ir::VariableKind::Ty(TyVariableKind::Float), + chalk_ir::UniverseIndex::ROOT, + ) + } + rustc_type_ir::CanonicalVarKind::Region(universe_index) => CanonicalVarKind::new( + chalk_ir::VariableKind::Lifetime, + chalk_ir::UniverseIndex::ROOT, + ), + rustc_type_ir::CanonicalVarKind::Const(universe_index) => CanonicalVarKind::new( + chalk_ir::VariableKind::Const(crate::TyKind::Error.intern(Interner)), + chalk_ir::UniverseIndex::ROOT, + ), + rustc_type_ir::CanonicalVarKind::PlaceholderTy(_) => unimplemented!(), + rustc_type_ir::CanonicalVarKind::PlaceholderRegion(_) => unimplemented!(), + rustc_type_ir::CanonicalVarKind::PlaceholderConst(_) => unimplemented!(), + }), + ); + chalk_ir::Canonical { + binders, + value: chalk_ir::ConstrainedSubst { + constraints: chalk_ir::Constraints::empty(Interner), + subst: convert_args_for_result(interner, &value), + }, + } +} + +pub fn convert_args_for_result<'db>( + interner: DbInterner<'db>, + args: &[GenericArg<'db>], +) -> crate::Substitution { + let mut substs = Vec::with_capacity(args.len()); + for arg in args { + match (*arg).kind() { + rustc_type_ir::GenericArgKind::Type(ty) => { + let ty = convert_ty_for_result(interner, ty); + substs.push(chalk_ir::GenericArgData::Ty(ty).intern(Interner)); + } + rustc_type_ir::GenericArgKind::Lifetime(region) => { + let lifetime = convert_region_for_result(region); + substs.push(chalk_ir::GenericArgData::Lifetime(lifetime).intern(Interner)); + } + rustc_type_ir::GenericArgKind::Const(const_) => { + substs.push( + chalk_ir::GenericArgData::Const(convert_const_for_result(interner, const_)) + .intern(Interner), + ); + } + } + } + Substitution::from_iter(Interner, substs) +} + +pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) -> crate::Ty { + use crate::{Scalar, TyKind}; + use chalk_ir::{FloatTy, IntTy, UintTy}; + match ty.kind() { + rustc_type_ir::TyKind::Bool => TyKind::Scalar(Scalar::Bool), + rustc_type_ir::TyKind::Char => TyKind::Scalar(Scalar::Char), + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I8) => { + TyKind::Scalar(Scalar::Int(IntTy::I8)) + } + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I16) => { + TyKind::Scalar(Scalar::Int(IntTy::I16)) + } + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I32) => { + TyKind::Scalar(Scalar::Int(IntTy::I32)) + } + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I64) => { + TyKind::Scalar(Scalar::Int(IntTy::I64)) + } + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::I128) => { + TyKind::Scalar(Scalar::Int(IntTy::I128)) + } + rustc_type_ir::TyKind::Int(rustc_type_ir::IntTy::Isize) => { + TyKind::Scalar(Scalar::Int(IntTy::Isize)) + } + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U8) => { + TyKind::Scalar(Scalar::Uint(UintTy::U8)) + } + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U16) => { + TyKind::Scalar(Scalar::Uint(UintTy::U16)) + } + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U32) => { + TyKind::Scalar(Scalar::Uint(UintTy::U32)) + } + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U64) => { + TyKind::Scalar(Scalar::Uint(UintTy::U64)) + } + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::U128) => { + TyKind::Scalar(Scalar::Uint(UintTy::U128)) + } + rustc_type_ir::TyKind::Uint(rustc_type_ir::UintTy::Usize) => { + TyKind::Scalar(Scalar::Uint(UintTy::Usize)) + } + rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F16) => { + TyKind::Scalar(Scalar::Float(FloatTy::F16)) + } + rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F32) => { + TyKind::Scalar(Scalar::Float(FloatTy::F32)) + } + rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F64) => { + TyKind::Scalar(Scalar::Float(FloatTy::F64)) + } + rustc_type_ir::TyKind::Float(rustc_type_ir::FloatTy::F128) => { + TyKind::Scalar(Scalar::Float(FloatTy::F128)) + } + rustc_type_ir::TyKind::Str => TyKind::Str, + rustc_type_ir::TyKind::Error(_) => TyKind::Error, + rustc_type_ir::TyKind::Never => TyKind::Never, + + rustc_type_ir::TyKind::Adt(def, args) => { + let adt_id = def.inner().id; + let subst = convert_args_for_result(interner, args.as_slice()); + TyKind::Adt(chalk_ir::AdtId(adt_id), subst) + } + + rustc_type_ir::TyKind::Infer(infer_ty) => { + let (var, kind) = match infer_ty { + rustc_type_ir::InferTy::TyVar(var) => { + (InferenceVar::from(var.as_u32()), TyVariableKind::General) + } + rustc_type_ir::InferTy::IntVar(var) => { + (InferenceVar::from(var.as_u32()), TyVariableKind::Integer) + } + rustc_type_ir::InferTy::FloatVar(var) => { + (InferenceVar::from(var.as_u32()), TyVariableKind::Float) + } + rustc_type_ir::InferTy::FreshFloatTy(..) + | rustc_type_ir::InferTy::FreshIntTy(..) + | rustc_type_ir::InferTy::FreshTy(..) => { + panic!("Freshening shouldn't happen.") + } + }; + TyKind::InferenceVar(var, kind) + } + + rustc_type_ir::TyKind::Ref(r, ty, mutability) => { + let mutability = match mutability { + rustc_ast_ir::Mutability::Mut => chalk_ir::Mutability::Mut, + rustc_ast_ir::Mutability::Not => chalk_ir::Mutability::Not, + }; + let r = convert_region_for_result(r); + let ty = convert_ty_for_result(interner, ty); + TyKind::Ref(mutability, r, ty) + } + + rustc_type_ir::TyKind::Tuple(tys) => { + let size = tys.len(); + let subst = Substitution::from_iter( + Interner, + tys.iter().map(|ty| { + chalk_ir::GenericArgData::Ty(convert_ty_for_result(interner, ty)) + .intern(Interner) + }), + ); + TyKind::Tuple(size, subst) + } + + rustc_type_ir::TyKind::Array(ty, const_) => { + let ty = convert_ty_for_result(interner, ty); + let const_ = convert_const_for_result(interner, const_); + TyKind::Array(ty, const_) + } + + rustc_type_ir::TyKind::Alias(alias_ty_kind, alias_ty) => match alias_ty_kind { + rustc_type_ir::AliasTyKind::Projection => { + let assoc_ty_id = match alias_ty.def_id { + SolverDefId::TypeAliasId(id) => id, + _ => unreachable!(), + }; + let associated_ty_id = to_assoc_type_id(assoc_ty_id); + let substitution = convert_args_for_result(interner, alias_ty.args.as_slice()); + TyKind::AssociatedType(associated_ty_id, substitution) + } + rustc_type_ir::AliasTyKind::Opaque => { + let opaque_ty_id = match alias_ty.def_id { + SolverDefId::InternedOpaqueTyId(id) => id, + _ => unreachable!(), + }; + let substitution = convert_args_for_result(interner, alias_ty.args.as_slice()); + TyKind::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy { + opaque_ty_id: opaque_ty_id.into(), + substitution, + })) + } + rustc_type_ir::AliasTyKind::Inherent => unimplemented!(), + rustc_type_ir::AliasTyKind::Free => unimplemented!(), + }, + + rustc_type_ir::TyKind::Placeholder(placeholder) => { + let ui = chalk_ir::UniverseIndex { counter: placeholder.universe.as_usize() }; + let placeholder_index = + chalk_ir::PlaceholderIndex { idx: placeholder.bound.var.as_usize(), ui }; + TyKind::Placeholder(placeholder_index) + } + + rustc_type_ir::TyKind::Bound(debruijn_index, ty) => TyKind::BoundVar(chalk_ir::BoundVar { + debruijn: chalk_ir::DebruijnIndex::new(debruijn_index.as_u32()), + index: ty.var.as_usize(), + }), + + rustc_type_ir::TyKind::FnPtr(bound_sig, fn_header) => { + let num_binders = bound_sig.bound_vars().len(); + let sig = chalk_ir::FnSig { + abi: fn_header.abi, + safety: match fn_header.safety { + crate::next_solver::abi::Safety::Safe => chalk_ir::Safety::Safe, + crate::next_solver::abi::Safety::Unsafe => chalk_ir::Safety::Unsafe, + }, + variadic: fn_header.c_variadic, + }; + let args = GenericArgs::new_from_iter( + interner, + bound_sig.skip_binder().inputs_and_output.iter().map(|a| a.into()), + ); + let substitution = convert_args_for_result(interner, args.as_slice()); + let substitution = chalk_ir::FnSubst(substitution); + let fnptr = chalk_ir::FnPointer { num_binders, sig, substitution }; + TyKind::Function(fnptr) + } + + rustc_type_ir::TyKind::Dynamic(preds, region, dyn_kind) => { + assert!(matches!(dyn_kind, rustc_type_ir::DynKind::Dyn)); + let self_ty = Ty::new_bound( + interner, + DebruijnIndex::from_u32(1), + BoundTy { kind: BoundTyKind::Anon, var: BoundVar::from_u32(0) }, + ); + let bounds = chalk_ir::QuantifiedWhereClauses::from_iter( + Interner, + preds.iter().map(|p| { + let binders = chalk_ir::VariableKinds::from_iter( + Interner, + p.bound_vars().iter().map(|b| match b { + BoundVarKind::Ty(kind) => { + chalk_ir::VariableKind::Ty(TyVariableKind::General) + } + BoundVarKind::Region(kind) => chalk_ir::VariableKind::Lifetime, + BoundVarKind::Const => { + chalk_ir::VariableKind::Const(crate::TyKind::Error.intern(Interner)) + } + }), + ); + let where_clause = match p.skip_binder() { + rustc_type_ir::ExistentialPredicate::Trait(trait_ref) => { + let trait_ref = TraitRef::new( + interner, + trait_ref.def_id, + [self_ty.into()].into_iter().chain(trait_ref.args.iter()), + ); + let trait_id = match trait_ref.def_id { + SolverDefId::TraitId(id) => to_chalk_trait_id(id), + _ => unreachable!(), + }; + let substitution = + convert_args_for_result(interner, trait_ref.args.as_slice()); + let trait_ref = chalk_ir::TraitRef { trait_id, substitution }; + chalk_ir::WhereClause::Implemented(trait_ref) + } + rustc_type_ir::ExistentialPredicate::AutoTrait(trait_) => { + let trait_id = match trait_ { + SolverDefId::TraitId(id) => to_chalk_trait_id(id), + _ => unreachable!(), + }; + let substitution = chalk_ir::Substitution::empty(Interner); + let trait_ref = chalk_ir::TraitRef { trait_id, substitution }; + chalk_ir::WhereClause::Implemented(trait_ref) + } + rustc_type_ir::ExistentialPredicate::Projection(existential_projection) => { + let projection = ProjectionPredicate { + projection_term: AliasTerm::new( + interner, + existential_projection.def_id, + [self_ty.into()] + .iter() + .chain(existential_projection.args.iter()), + ), + term: existential_projection.term, + }; + let associated_ty_id = match projection.projection_term.def_id { + SolverDefId::TypeAliasId(id) => to_assoc_type_id(id), + _ => unreachable!(), + }; + let substitution = convert_args_for_result( + interner, + projection.projection_term.args.as_slice(), + ); + let alias = chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { + associated_ty_id, + substitution, + }); + let ty = match projection.term { + Term::Ty(ty) => ty, + _ => unreachable!(), + }; + let ty = convert_ty_for_result(interner, ty); + let alias_eq = chalk_ir::AliasEq { alias, ty }; + chalk_ir::WhereClause::AliasEq(alias_eq) + } + }; + chalk_ir::Binders::new(binders, where_clause) + }), + ); + let binders = chalk_ir::VariableKinds::from1( + Interner, + chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General), + ); + let bounds = chalk_ir::Binders::new(binders, bounds); + let dyn_ty = chalk_ir::DynTy { bounds, lifetime: convert_region_for_result(region) }; + TyKind::Dyn(dyn_ty) + } + + rustc_type_ir::TyKind::Slice(ty) => { + let ty = convert_ty_for_result(interner, ty); + TyKind::Slice(ty) + } + + rustc_type_ir::TyKind::Foreign(foreign) => { + let def_id = match foreign { + SolverDefId::ForeignId(id) => id, + _ => unreachable!(), + }; + TyKind::Foreign(to_foreign_def_id(def_id)) + } + rustc_type_ir::TyKind::Pat(_, _) => unimplemented!(), + rustc_type_ir::TyKind::RawPtr(ty, mutability) => { + let mutability = match mutability { + rustc_ast_ir::Mutability::Mut => chalk_ir::Mutability::Mut, + rustc_ast_ir::Mutability::Not => chalk_ir::Mutability::Not, + }; + let ty = convert_ty_for_result(interner, ty); + TyKind::Raw(mutability, ty) + } + rustc_type_ir::TyKind::FnDef(def_id, args) => { + let id = match def_id { + SolverDefId::FunctionId(id) => CallableDefId::FunctionId(id), + SolverDefId::Ctor(Ctor::Struct(id)) => CallableDefId::StructId(id), + SolverDefId::Ctor(Ctor::Enum(id)) => CallableDefId::EnumVariantId(id), + _ => unreachable!(), + }; + let subst = convert_args_for_result(interner, args.as_slice()); + TyKind::FnDef(id.to_chalk(interner.db()), subst) + } + + rustc_type_ir::TyKind::Closure(def_id, args) => { + let id = match def_id { + SolverDefId::InternedClosureId(id) => id, + _ => unreachable!(), + }; + let subst = convert_args_for_result(interner, args.as_slice()); + TyKind::Closure(id.into(), subst) + } + rustc_type_ir::TyKind::CoroutineClosure(_, _) => unimplemented!(), + rustc_type_ir::TyKind::Coroutine(def_id, args) => { + let id = match def_id { + SolverDefId::InternedCoroutineId(id) => id, + _ => unreachable!(), + }; + let subst = convert_args_for_result(interner, args.as_slice()); + TyKind::Coroutine(id.into(), subst) + } + rustc_type_ir::TyKind::CoroutineWitness(def_id, args) => { + let id = match def_id { + SolverDefId::InternedCoroutineId(id) => id, + _ => unreachable!(), + }; + let subst = convert_args_for_result(interner, args.as_slice()); + TyKind::CoroutineWitness(id.into(), subst) + } + + rustc_type_ir::TyKind::Param(_) => unimplemented!(), + rustc_type_ir::TyKind::UnsafeBinder(_) => unimplemented!(), + } + .intern(Interner) +} + +fn convert_const_for_result<'db>(interner: DbInterner<'db>, const_: Const<'db>) -> crate::Const { + let value: chalk_ir::ConstValue = match const_.kind() { + rustc_type_ir::ConstKind::Param(_) => unimplemented!(), + rustc_type_ir::ConstKind::Infer(rustc_type_ir::InferConst::Var(var)) => { + chalk_ir::ConstValue::InferenceVar(chalk_ir::InferenceVar::from(var.as_u32())) + } + rustc_type_ir::ConstKind::Infer(rustc_type_ir::InferConst::Fresh(fresh)) => { + panic!("Vars should not be freshened.") + } + rustc_type_ir::ConstKind::Bound(debruijn_index, var) => { + chalk_ir::ConstValue::BoundVar(chalk_ir::BoundVar::new( + chalk_ir::DebruijnIndex::new(debruijn_index.as_u32()), + var.index(), + )) + } + rustc_type_ir::ConstKind::Placeholder(placeholder_const) => { + chalk_ir::ConstValue::Placeholder(chalk_ir::PlaceholderIndex { + ui: chalk_ir::UniverseIndex { counter: placeholder_const.universe.as_usize() }, + idx: placeholder_const.bound.as_usize(), + }) + } + rustc_type_ir::ConstKind::Unevaluated(unevaluated_const) => { + let id = match unevaluated_const.def { + SolverDefId::ConstId(id) => GeneralConstId::ConstId(id), + SolverDefId::StaticId(id) => GeneralConstId::StaticId(id), + _ => unreachable!(), + }; + let subst = convert_args_for_result(interner, unevaluated_const.args.as_slice()); + chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { + interned: ConstScalar::UnevaluatedConst(id, subst), + }) + } + rustc_type_ir::ConstKind::Value(value_const) => { + let bytes = value_const.value.inner(); + let value = chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { + interned: ConstScalar::Bytes(bytes.0.clone(), bytes.1.clone()), + }); + return chalk_ir::ConstData { + ty: convert_ty_for_result(interner, value_const.ty), + value, + } + .intern(Interner); + } + rustc_type_ir::ConstKind::Error(_) => { + chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { + interned: ConstScalar::Unknown, + }) + } + rustc_type_ir::ConstKind::Expr(_) => unimplemented!(), + }; + chalk_ir::ConstData { ty: crate::TyKind::Error.intern(Interner), value }.intern(Interner) +} + +fn convert_region_for_result<'db>(region: Region<'db>) -> crate::Lifetime { + match region.kind() { + rustc_type_ir::RegionKind::ReEarlyParam(early) => unimplemented!(), + rustc_type_ir::RegionKind::ReBound(db, bound) => chalk_ir::Lifetime::new( + Interner, + chalk_ir::LifetimeData::BoundVar(chalk_ir::BoundVar::new( + chalk_ir::DebruijnIndex::new(db.as_u32()), + bound.var.as_usize(), + )), + ), + rustc_type_ir::RegionKind::ReLateParam(_) => unimplemented!(), + rustc_type_ir::RegionKind::ReStatic => { + chalk_ir::Lifetime::new(Interner, chalk_ir::LifetimeData::Static) + } + rustc_type_ir::RegionKind::ReVar(vid) => chalk_ir::Lifetime::new( + Interner, + chalk_ir::LifetimeData::InferenceVar(chalk_ir::InferenceVar::from(vid.as_u32())), + ), + rustc_type_ir::RegionKind::RePlaceholder(placeholder) => chalk_ir::Lifetime::new( + Interner, + chalk_ir::LifetimeData::Placeholder(chalk_ir::PlaceholderIndex { + idx: placeholder.bound.var.as_usize(), + ui: chalk_ir::UniverseIndex { counter: placeholder.universe.as_usize() }, + }), + ), + rustc_type_ir::RegionKind::ReErased => { + chalk_ir::Lifetime::new(Interner, chalk_ir::LifetimeData::Erased) + } + rustc_type_ir::RegionKind::ReError(_) => { + chalk_ir::Lifetime::new(Interner, chalk_ir::LifetimeData::Error) + } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/opaques.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/opaques.rs new file mode 100644 index 000000000000..43589ab2ef13 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/opaques.rs @@ -0,0 +1,167 @@ +//! Things related to opaques in the next-trait-solver. + +use intern::Interned; +use rustc_ast_ir::try_visit; + +use crate::next_solver::SolverDefId; + +use super::{CanonicalVarKind, DbInterner, interned_vec_nolifetime_salsa}; + +pub type OpaqueTypeKey<'db> = rustc_type_ir::OpaqueTypeKey>; +pub type PredefinedOpaquesData<'db> = rustc_type_ir::solve::PredefinedOpaquesData>; +pub type ExternalConstraintsData<'db> = + rustc_type_ir::solve::ExternalConstraintsData>; + +#[salsa::interned(constructor = new_, debug)] +pub struct PredefinedOpaques<'db> { + #[returns(ref)] + kind_: rustc_type_ir::solve::PredefinedOpaquesData>, +} + +impl<'db> PredefinedOpaques<'db> { + pub fn new(interner: DbInterner<'db>, data: PredefinedOpaquesData<'db>) -> Self { + PredefinedOpaques::new_(interner.db(), data) + } + + pub fn inner(&self) -> &PredefinedOpaquesData<'db> { + salsa::with_attached_database(|db| { + let inner = self.kind_(db); + // SAFETY: ¯\_(ツ)_/¯ + unsafe { std::mem::transmute(inner) } + }) + .unwrap() + } +} + +impl<'db> rustc_type_ir::TypeVisitable> for PredefinedOpaques<'db> { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + self.opaque_types.visit_with(visitor) + } +} + +impl<'db> rustc_type_ir::TypeFoldable> for PredefinedOpaques<'db> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + Ok(PredefinedOpaques::new( + folder.cx(), + PredefinedOpaquesData { + opaque_types: self + .opaque_types + .iter() + .cloned() + .map(|opaque| opaque.try_fold_with(folder)) + .collect::>()?, + }, + )) + } + fn fold_with>>(self, folder: &mut F) -> Self { + PredefinedOpaques::new( + folder.cx(), + PredefinedOpaquesData { + opaque_types: self + .opaque_types + .iter() + .cloned() + .map(|opaque| opaque.fold_with(folder)) + .collect(), + }, + ) + } +} + +impl<'db> std::ops::Deref for PredefinedOpaques<'db> { + type Target = PredefinedOpaquesData<'db>; + + fn deref(&self) -> &Self::Target { + self.inner() + } +} + +interned_vec_nolifetime_salsa!(SolverDefIds, SolverDefId); + +#[salsa::interned(constructor = new_, debug)] +pub struct ExternalConstraints<'db> { + #[returns(ref)] + kind_: rustc_type_ir::solve::ExternalConstraintsData>, +} + +impl<'db> ExternalConstraints<'db> { + pub fn new(interner: DbInterner<'db>, data: ExternalConstraintsData<'db>) -> Self { + ExternalConstraints::new_(interner.db(), data) + } + + pub fn inner(&self) -> &ExternalConstraintsData<'db> { + salsa::with_attached_database(|db| { + let inner = self.kind_(db); + // SAFETY: ¯\_(ツ)_/¯ + unsafe { std::mem::transmute(inner) } + }) + .unwrap() + } +} + +impl<'db> std::ops::Deref for ExternalConstraints<'db> { + type Target = ExternalConstraintsData<'db>; + + fn deref(&self) -> &Self::Target { + self.inner() + } +} + +impl<'db> rustc_type_ir::TypeVisitable> for ExternalConstraints<'db> { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + try_visit!(self.region_constraints.visit_with(visitor)); + try_visit!(self.opaque_types.visit_with(visitor)); + self.normalization_nested_goals.visit_with(visitor) + } +} + +impl<'db> rustc_type_ir::TypeFoldable> for ExternalConstraints<'db> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + Ok(ExternalConstraints::new( + folder.cx(), + ExternalConstraintsData { + region_constraints: self.region_constraints.clone().try_fold_with(folder)?, + opaque_types: self + .opaque_types + .iter() + .cloned() + .map(|opaque| opaque.try_fold_with(folder)) + .collect::>()?, + normalization_nested_goals: self + .normalization_nested_goals + .clone() + .try_fold_with(folder)?, + }, + )) + } + fn fold_with>>(self, folder: &mut F) -> Self { + ExternalConstraints::new( + folder.cx(), + ExternalConstraintsData { + region_constraints: self.region_constraints.clone().fold_with(folder), + opaque_types: self + .opaque_types + .iter() + .cloned() + .map(|opaque| opaque.fold_with(folder)) + .collect(), + normalization_nested_goals: self + .normalization_nested_goals + .clone() + .fold_with(folder), + }, + ) + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs new file mode 100644 index 000000000000..50261bb5e381 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs @@ -0,0 +1,894 @@ +//! Things related to predicates. + +use std::cmp::Ordering; + +use intern::Interned; +use rustc_ast_ir::try_visit; +use rustc_type_ir::{ + self as ty, CollectAndApply, DebruijnIndex, EarlyBinder, FlagComputation, Flags, + PredicatePolarity, TypeFlags, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, + TypeVisitable, Upcast, UpcastFrom, VisitorResult, WithCachedTypeInfo, + elaborate::Elaboratable, + error::{ExpectedFound, TypeError}, + inherent::{IntoKind, SliceLike}, + relate::Relate, +}; +use smallvec::{SmallVec, smallvec}; + +use super::{Binder, BoundVarKinds, DbInterner, Region, Ty, interned_vec_db}; + +pub type BoundExistentialPredicate<'db> = Binder<'db, ExistentialPredicate<'db>>; + +pub type TraitRef<'db> = ty::TraitRef>; +pub type AliasTerm<'db> = ty::AliasTerm>; +pub type ProjectionPredicate<'db> = ty::ProjectionPredicate>; +pub type ExistentialPredicate<'db> = ty::ExistentialPredicate>; +pub type ExistentialTraitRef<'db> = ty::ExistentialTraitRef>; +pub type ExistentialProjection<'db> = ty::ExistentialProjection>; +pub type TraitPredicate<'db> = ty::TraitPredicate>; +pub type ClauseKind<'db> = ty::ClauseKind>; +pub type PredicateKind<'db> = ty::PredicateKind>; +pub type NormalizesTo<'db> = ty::NormalizesTo>; +pub type CoercePredicate<'db> = ty::CoercePredicate>; +pub type SubtypePredicate<'db> = ty::SubtypePredicate>; +pub type OutlivesPredicate<'db, T> = ty::OutlivesPredicate, T>; +pub type RegionOutlivesPredicate<'db> = OutlivesPredicate<'db, Region<'db>>; +pub type TypeOutlivesPredicate<'db> = OutlivesPredicate<'db, Ty<'db>>; +pub type PolyTraitPredicate<'db> = Binder<'db, TraitPredicate<'db>>; +pub type PolyRegionOutlivesPredicate<'db> = Binder<'db, RegionOutlivesPredicate<'db>>; +pub type PolyTypeOutlivesPredicate<'db> = Binder<'db, TypeOutlivesPredicate<'db>>; +pub type PolySubtypePredicate<'db> = Binder<'db, SubtypePredicate<'db>>; +pub type PolyCoercePredicate<'db> = Binder<'db, CoercePredicate<'db>>; +pub type PolyProjectionPredicate<'db> = Binder<'db, ProjectionPredicate<'db>>; +pub type PolyTraitRef<'db> = Binder<'db, TraitRef<'db>>; +pub type PolyExistentialTraitRef<'db> = Binder<'db, ExistentialTraitRef<'db>>; +pub type PolyExistentialProjection<'db> = Binder<'db, ExistentialProjection<'db>>; + +/// Compares via an ordering that will not change if modules are reordered or other changes are +/// made to the tree. In particular, this ordering is preserved across incremental compilations. +fn stable_cmp_existential_predicate<'db>( + a: &ExistentialPredicate<'db>, + b: &ExistentialPredicate<'db>, +) -> Ordering { + // FIXME: this is actual unstable - see impl in predicate.rs in `rustc_middle` + match (a, b) { + (ExistentialPredicate::Trait(_), ExistentialPredicate::Trait(_)) => Ordering::Equal, + (ExistentialPredicate::Projection(a), ExistentialPredicate::Projection(b)) => { + // Should sort by def path hash + Ordering::Equal + } + (ExistentialPredicate::AutoTrait(a), ExistentialPredicate::AutoTrait(b)) => { + // Should sort by def path hash + Ordering::Equal + } + (ExistentialPredicate::Trait(_), _) => Ordering::Less, + (ExistentialPredicate::Projection(_), ExistentialPredicate::Trait(_)) => Ordering::Greater, + (ExistentialPredicate::Projection(_), _) => Ordering::Less, + (ExistentialPredicate::AutoTrait(_), _) => Ordering::Greater, + } +} +interned_vec_db!(BoundExistentialPredicates, BoundExistentialPredicate); + +impl<'db> rustc_type_ir::inherent::BoundExistentialPredicates> + for BoundExistentialPredicates<'db> +{ + fn principal_def_id(self) -> Option< as rustc_type_ir::Interner>::DefId> { + self.principal().map(|trait_ref| trait_ref.skip_binder().def_id) + } + + fn principal( + self, + ) -> Option< + rustc_type_ir::Binder, rustc_type_ir::ExistentialTraitRef>>, + > { + self.inner()[0] + .map_bound(|this| match this { + ExistentialPredicate::Trait(tr) => Some(tr), + _ => None, + }) + .transpose() + } + + fn auto_traits( + self, + ) -> impl IntoIterator as rustc_type_ir::Interner>::DefId> { + self.iter().filter_map(|predicate| match predicate.skip_binder() { + ExistentialPredicate::AutoTrait(did) => Some(did), + _ => None, + }) + } + + fn projection_bounds( + self, + ) -> impl IntoIterator< + Item = rustc_type_ir::Binder< + DbInterner<'db>, + rustc_type_ir::ExistentialProjection>, + >, + > { + self.iter().filter_map(|predicate| { + predicate + .map_bound(|pred| match pred { + ExistentialPredicate::Projection(projection) => Some(projection), + _ => None, + }) + .transpose() + }) + } +} + +impl<'db> rustc_type_ir::relate::Relate> for BoundExistentialPredicates<'db> { + fn relate>>( + relation: &mut R, + a: Self, + b: Self, + ) -> rustc_type_ir::relate::RelateResult, Self> { + let interner = relation.cx(); + + // We need to perform this deduplication as we sometimes generate duplicate projections in `a`. + let mut a_v: Vec<_> = a.into_iter().collect(); + let mut b_v: Vec<_> = b.into_iter().collect(); + // `skip_binder` here is okay because `stable_cmp` doesn't look at binders + a_v.sort_by(|a, b| { + stable_cmp_existential_predicate(a.as_ref().skip_binder(), b.as_ref().skip_binder()) + }); + a_v.dedup(); + b_v.sort_by(|a, b| { + stable_cmp_existential_predicate(a.as_ref().skip_binder(), b.as_ref().skip_binder()) + }); + b_v.dedup(); + if a_v.len() != b_v.len() { + return Err(TypeError::ExistentialMismatch(ExpectedFound::new(a, b))); + } + + let v = std::iter::zip(a_v, b_v).map( + |(ep_a, ep_b): ( + Binder<'_, ty::ExistentialPredicate<_>>, + Binder<'_, ty::ExistentialPredicate<_>>, + )| { + match (ep_a.skip_binder(), ep_b.skip_binder()) { + (ty::ExistentialPredicate::Trait(a), ty::ExistentialPredicate::Trait(b)) => { + Ok(ep_a.rebind(ty::ExistentialPredicate::Trait( + relation.relate(ep_a.rebind(a), ep_b.rebind(b))?.skip_binder(), + ))) + } + ( + ty::ExistentialPredicate::Projection(a), + ty::ExistentialPredicate::Projection(b), + ) => Ok(ep_a.rebind(ty::ExistentialPredicate::Projection( + relation.relate(ep_a.rebind(a), ep_b.rebind(b))?.skip_binder(), + ))), + ( + ty::ExistentialPredicate::AutoTrait(a), + ty::ExistentialPredicate::AutoTrait(b), + ) if a == b => Ok(ep_a.rebind(ty::ExistentialPredicate::AutoTrait(a))), + _ => Err(TypeError::ExistentialMismatch(ExpectedFound::new(a, b))), + } + }, + ); + + CollectAndApply::collect_and_apply(v, |g| { + BoundExistentialPredicates::new_from_iter(interner, g.iter().cloned()) + }) + } +} + +#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Clone)] +pub struct InternedWrapperNoDebug(pub(crate) T); + +#[salsa::interned(constructor = new_)] +pub struct Predicate<'db> { + #[returns(ref)] + kind_: InternedWrapperNoDebug>>>, +} + +impl<'db> std::fmt::Debug for Predicate<'db> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.inner().internee.fmt(f) + } +} + +impl<'db> std::fmt::Debug + for InternedWrapperNoDebug>>> +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Binder<")?; + match self.0.internee.skip_binder() { + rustc_type_ir::PredicateKind::Clause(clause_kind) => { + write!(f, "{clause_kind:?}") + } + rustc_type_ir::PredicateKind::DynCompatible(trait_def_id) => { + write!(f, "the trait `{trait_def_id:?}` is dyn-compatible") + } + rustc_type_ir::PredicateKind::Subtype(subtype_predicate) => { + write!(f, "{subtype_predicate:?}") + } + rustc_type_ir::PredicateKind::Coerce(coerce_predicate) => { + write!(f, "{coerce_predicate:?}") + } + rustc_type_ir::PredicateKind::ConstEquate(c1, c2) => { + write!(f, "the constant `{c1:?}` equals `{c2:?}`") + } + rustc_type_ir::PredicateKind::Ambiguous => write!(f, "ambiguous"), + rustc_type_ir::PredicateKind::NormalizesTo(data) => write!(f, "{data:?}"), + rustc_type_ir::PredicateKind::AliasRelate(t1, t2, dir) => { + write!(f, "{t1:?} {dir:?} {t2:?}") + } + }?; + write!(f, ", [{:?}]>", self.0.internee.bound_vars())?; + Ok(()) + } +} + +impl<'db> Predicate<'db> { + pub fn new(interner: DbInterner<'db>, kind: Binder<'db, PredicateKind<'db>>) -> Self { + let flags = FlagComputation::for_predicate(kind); + let cached = WithCachedTypeInfo { + internee: kind, + flags: flags.flags, + outer_exclusive_binder: flags.outer_exclusive_binder, + }; + Predicate::new_(interner.db(), InternedWrapperNoDebug(cached)) + } + + pub fn inner(&self) -> &WithCachedTypeInfo>> { + salsa::with_attached_database(|db| { + let inner = &self.kind_(db).0; + // SAFETY: The caller already has access to a `Predicate<'db>`, so borrowchecking will + // make sure that our returned value is valid for the lifetime `'db`. + unsafe { std::mem::transmute(inner) } + }) + .unwrap() + } + + /// Flips the polarity of a Predicate. + /// + /// Given `T: Trait` predicate it returns `T: !Trait` and given `T: !Trait` returns `T: Trait`. + pub fn flip_polarity(self) -> Option> { + let kind = self + .kind() + .map_bound(|kind| match kind { + PredicateKind::Clause(ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity, + })) => Some(PredicateKind::Clause(ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity: polarity.flip(), + }))), + + _ => None, + }) + .transpose()?; + + Some(Predicate::new(DbInterner::conjure(), kind)) + } + + pub fn as_trait_clause(self) -> Option> { + let predicate = self.kind(); + match predicate.skip_binder() { + PredicateKind::Clause(ClauseKind::Trait(t)) => Some(predicate.rebind(t)), + PredicateKind::Clause(ClauseKind::Projection(..)) + | PredicateKind::Clause(ClauseKind::HostEffect(..)) + | PredicateKind::Clause(ClauseKind::ConstArgHasType(..)) + | PredicateKind::Clause(ClauseKind::UnstableFeature(_)) + | PredicateKind::NormalizesTo(..) + | PredicateKind::AliasRelate(..) + | PredicateKind::Subtype(..) + | PredicateKind::Coerce(..) + | PredicateKind::Clause(ClauseKind::RegionOutlives(..)) + | PredicateKind::Clause(ClauseKind::WellFormed(..)) + | PredicateKind::DynCompatible(..) + | PredicateKind::Clause(ClauseKind::TypeOutlives(..)) + | PredicateKind::Clause(ClauseKind::ConstEvaluatable(..)) + | PredicateKind::ConstEquate(..) + | PredicateKind::Ambiguous => None, + } + } +} + +// FIXME: should make a "header" in interned_vec + +#[derive(Debug, Clone)] +pub struct InternedClausesWrapper<'db>(SmallVec<[Clause<'db>; 2]>, TypeFlags, DebruijnIndex); + +impl<'db> PartialEq for InternedClausesWrapper<'db> { + fn eq(&self, other: &Self) -> bool { + self.0.eq(&other.0) + } +} + +impl<'db> Eq for InternedClausesWrapper<'db> {} + +impl<'db> std::hash::Hash for InternedClausesWrapper<'db> { + fn hash(&self, state: &mut H) { + self.0.hash(state) + } +} + +type InternedClauses<'db> = Interned>; + +#[salsa::interned(constructor = new_)] +pub struct Clauses<'db> { + #[returns(ref)] + inner_: InternedClausesWrapper<'db>, +} + +impl<'db> Clauses<'db> { + pub fn new_from_iter( + interner: DbInterner<'db>, + data: impl IntoIterator>, + ) -> Self { + let clauses: SmallVec<_> = data.into_iter().collect(); + let flags = FlagComputation::>::for_clauses(&clauses); + let wrapper = InternedClausesWrapper(clauses, flags.flags, flags.outer_exclusive_binder); + Clauses::new_(interner.db(), wrapper) + } + + pub fn inner(&self) -> &InternedClausesWrapper<'db> { + salsa::with_attached_database(|db| { + let inner = self.inner_(db); + // SAFETY: The caller already has access to a `Clauses<'db>`, so borrowchecking will + // make sure that our returned value is valid for the lifetime `'db`. + unsafe { std::mem::transmute(inner) } + }) + .unwrap() + } +} + +impl<'db> std::fmt::Debug for Clauses<'db> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.inner().0.fmt(f) + } +} + +impl<'db> rustc_type_ir::inherent::Clauses> for Clauses<'db> {} + +impl<'db> rustc_type_ir::inherent::SliceLike for Clauses<'db> { + type Item = Clause<'db>; + + type IntoIter = ; 2]> as IntoIterator>::IntoIter; + + fn iter(self) -> Self::IntoIter { + self.inner().0.clone().into_iter() + } + + fn as_slice(&self) -> &[Self::Item] { + self.inner().0.as_slice() + } +} + +impl<'db> IntoIterator for Clauses<'db> { + type Item = Clause<'db>; + type IntoIter = ::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + rustc_type_ir::inherent::SliceLike::iter(self) + } +} + +impl<'db> Default for Clauses<'db> { + fn default() -> Self { + Clauses::new_from_iter(DbInterner::conjure(), []) + } +} + +impl<'db> rustc_type_ir::TypeSuperFoldable> for Clauses<'db> { + fn try_super_fold_with>>( + self, + folder: &mut F, + ) -> Result { + let mut clauses: SmallVec<[_; 2]> = SmallVec::with_capacity(self.inner().0.len()); + for c in self { + clauses.push(c.try_fold_with(folder)?); + } + Ok(Clauses::new_from_iter(folder.cx(), clauses)) + } + + fn super_fold_with>>( + self, + folder: &mut F, + ) -> Self { + let mut clauses: SmallVec<[_; 2]> = SmallVec::with_capacity(self.inner().0.len()); + for c in self { + clauses.push(c.fold_with(folder)); + } + Clauses::new_from_iter(folder.cx(), clauses) + } +} + +impl<'db> rustc_type_ir::TypeFoldable> for Clauses<'db> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + use rustc_type_ir::inherent::SliceLike as _; + let inner: smallvec::SmallVec<[_; 2]> = + self.iter().map(|v| v.try_fold_with(folder)).collect::>()?; + Ok(Clauses::new_from_iter(folder.cx(), inner)) + } + fn fold_with>>(self, folder: &mut F) -> Self { + use rustc_type_ir::inherent::SliceLike as _; + let inner: smallvec::SmallVec<[_; 2]> = self.iter().map(|v| v.fold_with(folder)).collect(); + Clauses::new_from_iter(folder.cx(), inner) + } +} + +impl<'db> rustc_type_ir::TypeVisitable> for Clauses<'db> { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + use rustc_ast_ir::visit::VisitorResult; + use rustc_type_ir::inherent::SliceLike as _; + rustc_ast_ir::walk_visitable_list!(visitor, self.as_slice().iter()); + V::Result::output() + } +} + +impl<'db> rustc_type_ir::Flags for Clauses<'db> { + fn flags(&self) -> rustc_type_ir::TypeFlags { + self.inner().1 + } + + fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex { + self.inner().2 + } +} + +impl<'db> rustc_type_ir::TypeSuperVisitable> for Clauses<'db> { + fn super_visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + self.as_slice().visit_with(visitor) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] // TODO implement Debug by hand +pub struct Clause<'db>(pub(crate) Predicate<'db>); + +// We could cram the reveal into the clauses like rustc does, probably +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub struct ParamEnv<'db> { + pub(crate) clauses: Clauses<'db>, +} + +impl<'db> ParamEnv<'db> { + pub fn empty() -> Self { + ParamEnv { clauses: Clauses::new_from_iter(DbInterner::conjure(), []) } + } +} + +impl<'db> TypeVisitable> for ParamEnv<'db> { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + try_visit!(self.clauses.visit_with(visitor)); + V::Result::output() + } +} + +impl<'db> TypeFoldable> for ParamEnv<'db> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + Ok(ParamEnv { clauses: self.clauses.try_fold_with(folder)? }) + } + fn fold_with>>(self, folder: &mut F) -> Self { + ParamEnv { clauses: self.clauses.fold_with(folder) } + } +} + +impl<'db> rustc_type_ir::inherent::ParamEnv> for ParamEnv<'db> { + fn caller_bounds(self) -> impl rustc_type_ir::inherent::SliceLike> { + self.clauses + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct ParamEnvAnd<'db, T> { + pub param_env: ParamEnv<'db>, + pub value: T, +} + +impl<'db, T> ParamEnvAnd<'db, T> { + pub fn into_parts(self) -> (ParamEnv<'db>, T) { + (self.param_env, self.value) + } +} + +impl<'db> TypeVisitable> for Predicate<'db> { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + visitor.visit_predicate(*self) + } +} + +impl<'db> TypeSuperVisitable> for Predicate<'db> { + fn super_visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + (*self).kind().visit_with(visitor) + } +} + +impl<'db> TypeFoldable> for Predicate<'db> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + folder.try_fold_predicate(self) + } + fn fold_with>>(self, folder: &mut F) -> Self { + folder.fold_predicate(self) + } +} + +impl<'db> TypeSuperFoldable> for Predicate<'db> { + fn try_super_fold_with>>( + self, + folder: &mut F, + ) -> Result { + let new = self.kind().try_fold_with(folder)?; + Ok(Predicate::new(folder.cx(), new)) + } + fn super_fold_with>>( + self, + folder: &mut F, + ) -> Self { + let new = self.kind().fold_with(folder); + Predicate::new(folder.cx(), new) + } +} + +impl<'db> Elaboratable> for Predicate<'db> { + fn predicate(&self) -> as rustc_type_ir::Interner>::Predicate { + *self + } + + fn child(&self, clause: as rustc_type_ir::Interner>::Clause) -> Self { + clause.as_predicate() + } + + fn child_with_derived_cause( + &self, + clause: as rustc_type_ir::Interner>::Clause, + _span: as rustc_type_ir::Interner>::Span, + _parent_trait_pred: rustc_type_ir::Binder< + DbInterner<'db>, + rustc_type_ir::TraitPredicate>, + >, + _index: usize, + ) -> Self { + clause.as_predicate() + } +} + +impl<'db> Flags for Predicate<'db> { + fn flags(&self) -> rustc_type_ir::TypeFlags { + self.inner().flags + } + + fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex { + self.inner().outer_exclusive_binder + } +} + +impl<'db> IntoKind for Predicate<'db> { + type Kind = Binder<'db, PredicateKind<'db>>; + + fn kind(self) -> Self::Kind { + self.inner().internee + } +} + +impl<'db> UpcastFrom, ty::PredicateKind>> for Predicate<'db> { + fn upcast_from(from: ty::PredicateKind>, interner: DbInterner<'db>) -> Self { + Binder::dummy(from).upcast(interner) + } +} +impl<'db> + UpcastFrom, ty::Binder, ty::PredicateKind>>> + for Predicate<'db> +{ + fn upcast_from( + from: ty::Binder, ty::PredicateKind>>, + interner: DbInterner<'db>, + ) -> Self { + Predicate::new(interner, from) + } +} +impl<'db> UpcastFrom, ty::ClauseKind>> for Predicate<'db> { + fn upcast_from(from: ty::ClauseKind>, interner: DbInterner<'db>) -> Self { + Binder::dummy(PredicateKind::Clause(from)).upcast(interner) + } +} +impl<'db> UpcastFrom, ty::Binder, ty::ClauseKind>>> + for Predicate<'db> +{ + fn upcast_from( + from: ty::Binder, ty::ClauseKind>>, + interner: DbInterner<'db>, + ) -> Self { + from.map_bound(PredicateKind::Clause).upcast(interner) + } +} +impl<'db> UpcastFrom, Clause<'db>> for Predicate<'db> { + fn upcast_from(from: Clause<'db>, _interner: DbInterner<'db>) -> Self { + from.0 + } +} +impl<'db> UpcastFrom, ty::NormalizesTo>> for Predicate<'db> { + fn upcast_from(from: ty::NormalizesTo>, interner: DbInterner<'db>) -> Self { + PredicateKind::NormalizesTo(from).upcast(interner) + } +} +impl<'db> UpcastFrom, ty::TraitRef>> for Predicate<'db> { + fn upcast_from(from: ty::TraitRef>, interner: DbInterner<'db>) -> Self { + Binder::dummy(from).upcast(interner) + } +} +impl<'db> UpcastFrom, ty::Binder, ty::TraitRef>>> + for Predicate<'db> +{ + fn upcast_from( + from: ty::Binder, ty::TraitRef>>, + interner: DbInterner<'db>, + ) -> Self { + from.map_bound(|trait_ref| TraitPredicate { + trait_ref, + polarity: PredicatePolarity::Positive, + }) + .upcast(interner) + } +} +impl<'db> UpcastFrom, Binder<'db, ty::TraitPredicate>>> + for Predicate<'db> +{ + fn upcast_from( + from: Binder<'db, ty::TraitPredicate>>, + interner: DbInterner<'db>, + ) -> Self { + from.map_bound(|it| PredicateKind::Clause(ClauseKind::Trait(it))).upcast(interner) + } +} +impl<'db> UpcastFrom, Binder<'db, ProjectionPredicate<'db>>> for Predicate<'db> { + fn upcast_from(from: Binder<'db, ProjectionPredicate<'db>>, interner: DbInterner<'db>) -> Self { + from.map_bound(|it| PredicateKind::Clause(ClauseKind::Projection(it))).upcast(interner) + } +} +impl<'db> UpcastFrom, ProjectionPredicate<'db>> for Predicate<'db> { + fn upcast_from(from: ProjectionPredicate<'db>, interner: DbInterner<'db>) -> Self { + PredicateKind::Clause(ClauseKind::Projection(from)).upcast(interner) + } +} +impl<'db> UpcastFrom, ty::TraitPredicate>> for Predicate<'db> { + fn upcast_from(from: ty::TraitPredicate>, interner: DbInterner<'db>) -> Self { + PredicateKind::Clause(ClauseKind::Trait(from)).upcast(interner) + } +} +impl<'db> UpcastFrom, ty::OutlivesPredicate, Ty<'db>>> + for Predicate<'db> +{ + fn upcast_from( + from: ty::OutlivesPredicate, Ty<'db>>, + interner: DbInterner<'db>, + ) -> Self { + PredicateKind::Clause(ClauseKind::TypeOutlives(from)).upcast(interner) + } +} +impl<'db> UpcastFrom, ty::OutlivesPredicate, Region<'db>>> + for Predicate<'db> +{ + fn upcast_from( + from: ty::OutlivesPredicate, Region<'db>>, + interner: DbInterner<'db>, + ) -> Self { + PredicateKind::Clause(ClauseKind::RegionOutlives(from)).upcast(interner) + } +} + +impl<'db> rustc_type_ir::inherent::Predicate> for Predicate<'db> { + fn as_clause(self) -> Option< as rustc_type_ir::Interner>::Clause> { + match self.kind().skip_binder() { + PredicateKind::Clause(..) => Some(self.expect_clause()), + _ => None, + } + } + + /// Whether this projection can be soundly normalized. + /// + /// Wf predicates must not be normalized, as normalization + /// can remove required bounds which would cause us to + /// unsoundly accept some programs. See #91068. + fn allow_normalization(self) -> bool { + // TODO: this should probably live in rustc_type_ir + match self.inner().as_ref().skip_binder() { + PredicateKind::Clause(ClauseKind::WellFormed(_)) + | PredicateKind::AliasRelate(..) + | PredicateKind::NormalizesTo(..) => false, + PredicateKind::Clause(ClauseKind::Trait(_)) + | PredicateKind::Clause(ClauseKind::RegionOutlives(_)) + | PredicateKind::Clause(ClauseKind::TypeOutlives(_)) + | PredicateKind::Clause(ClauseKind::Projection(_)) + | PredicateKind::Clause(ClauseKind::ConstArgHasType(..)) + | PredicateKind::Clause(ClauseKind::HostEffect(..)) + | PredicateKind::Clause(ClauseKind::UnstableFeature(_)) + | PredicateKind::DynCompatible(_) + | PredicateKind::Subtype(_) + | PredicateKind::Coerce(_) + | PredicateKind::Clause(ClauseKind::ConstEvaluatable(_)) + | PredicateKind::ConstEquate(_, _) + | PredicateKind::Ambiguous => true, + } + } +} + +impl<'db> Predicate<'db> { + /// Assert that the predicate is a clause. + pub fn expect_clause(self) -> Clause<'db> { + match self.kind().skip_binder() { + PredicateKind::Clause(..) => Clause(self), + _ => panic!("{self:?} is not a clause"), + } + } +} + +impl<'db> TypeVisitable> for Clause<'db> { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + visitor.visit_predicate((*self).as_predicate()) + } +} + +impl<'db> TypeFoldable> for Clause<'db> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + Ok(folder.try_fold_predicate(self.as_predicate())?.expect_clause()) + } + fn fold_with>>(self, folder: &mut F) -> Self { + folder.fold_predicate(self.as_predicate()).expect_clause() + } +} + +impl<'db> IntoKind for Clause<'db> { + type Kind = Binder<'db, ClauseKind<'db>>; + + fn kind(self) -> Self::Kind { + self.0.kind().map_bound(|pk| match pk { + PredicateKind::Clause(kind) => kind, + _ => unreachable!(), + }) + } +} + +impl<'db> Clause<'db> { + pub fn as_predicate(self) -> Predicate<'db> { + self.0 + } +} + +impl<'db> Elaboratable> for Clause<'db> { + fn predicate(&self) -> as rustc_type_ir::Interner>::Predicate { + self.0 + } + + fn child(&self, clause: as rustc_type_ir::Interner>::Clause) -> Self { + clause + } + + fn child_with_derived_cause( + &self, + clause: as rustc_type_ir::Interner>::Clause, + _span: as rustc_type_ir::Interner>::Span, + _parent_trait_pred: rustc_type_ir::Binder< + DbInterner<'db>, + rustc_type_ir::TraitPredicate>, + >, + _index: usize, + ) -> Self { + clause + } +} + +impl<'db> UpcastFrom, ty::Binder, ty::ClauseKind>>> + for Clause<'db> +{ + fn upcast_from( + from: ty::Binder, ty::ClauseKind>>, + interner: DbInterner<'db>, + ) -> Self { + Clause(from.map_bound(PredicateKind::Clause).upcast(interner)) + } +} +impl<'db> UpcastFrom, ty::TraitRef>> for Clause<'db> { + fn upcast_from(from: ty::TraitRef>, interner: DbInterner<'db>) -> Self { + Clause(from.upcast(interner)) + } +} +impl<'db> UpcastFrom, ty::Binder, ty::TraitRef>>> + for Clause<'db> +{ + fn upcast_from( + from: ty::Binder, ty::TraitRef>>, + interner: DbInterner<'db>, + ) -> Self { + Clause(from.upcast(interner)) + } +} +impl<'db> UpcastFrom, ty::TraitPredicate>> for Clause<'db> { + fn upcast_from(from: ty::TraitPredicate>, interner: DbInterner<'db>) -> Self { + Clause(from.upcast(interner)) + } +} +impl<'db> + UpcastFrom, ty::Binder, ty::TraitPredicate>>> + for Clause<'db> +{ + fn upcast_from( + from: ty::Binder, ty::TraitPredicate>>, + interner: DbInterner<'db>, + ) -> Self { + Clause(from.upcast(interner)) + } +} +impl<'db> UpcastFrom, ty::ProjectionPredicate>> for Clause<'db> { + fn upcast_from( + from: ty::ProjectionPredicate>, + interner: DbInterner<'db>, + ) -> Self { + Clause(from.upcast(interner)) + } +} +impl<'db> + UpcastFrom< + DbInterner<'db>, + ty::Binder, ty::ProjectionPredicate>>, + > for Clause<'db> +{ + fn upcast_from( + from: ty::Binder, ty::ProjectionPredicate>>, + interner: DbInterner<'db>, + ) -> Self { + Clause(from.upcast(interner)) + } +} + +impl<'db> rustc_type_ir::inherent::Clause> for Clause<'db> { + fn as_predicate(self) -> as rustc_type_ir::Interner>::Predicate { + self.0 + } + + fn instantiate_supertrait( + self, + cx: DbInterner<'db>, + trait_ref: rustc_type_ir::Binder, rustc_type_ir::TraitRef>>, + ) -> Self { + tracing::debug!(?self, ?trait_ref); + // See the rustc impl for a long comment + let bound_pred = self.kind(); + let pred_bound_vars = bound_pred.bound_vars(); + let trait_bound_vars = trait_ref.bound_vars(); + // 1) Self: Bar1<'a, '^0.0> -> Self: Bar1<'a, '^0.1> + let shifted_pred = + cx.shift_bound_var_indices(trait_bound_vars.len(), bound_pred.skip_binder()); + // 2) Self: Bar1<'a, '^0.1> -> T: Bar1<'^0.0, '^0.1> + let new = EarlyBinder::bind(shifted_pred).instantiate(cx, trait_ref.skip_binder().args); + // 3) ['x] + ['b] -> ['x, 'b] + let bound_vars = + BoundVarKinds::new_from_iter(cx, trait_bound_vars.iter().chain(pred_bound_vars.iter())); + + let predicate: Predicate<'db> = + ty::Binder::bind_with_vars(PredicateKind::Clause(new), bound_vars).upcast(cx); + predicate.expect_clause() + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project.rs new file mode 100644 index 000000000000..e57808728ae9 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project.rs @@ -0,0 +1,3 @@ +//! Projection code for next-solver. + +pub(crate) mod solve_normalize; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project/solve_normalize.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project/solve_normalize.rs new file mode 100644 index 000000000000..a8fb2ae14f1c --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project/solve_normalize.rs @@ -0,0 +1,264 @@ +//! Normalization within a next-solver infer context. + +use std::fmt::Debug; + +use rustc_next_trait_solver::placeholder::BoundVarReplacer; +use rustc_type_ir::{ + AliasRelationDirection, FallibleTypeFolder, Flags, Interner, TermKind, TypeFoldable, + TypeFolder, TypeSuperFoldable, TypeVisitableExt, UniverseIndex, + inherent::{IntoKind, Span as _, Term as _}, +}; + +use crate::next_solver::{ + Binder, Const, ConstKind, DbInterner, Goal, ParamEnv, Predicate, PredicateKind, Span, Term, Ty, + TyKind, + fulfill::{FulfillmentCtxt, NextSolverError}, + infer::{ + InferCtxt, + at::At, + traits::{Obligation, ObligationCause}, + }, + util::PlaceholderReplacer, +}; + +/// Deeply normalize all aliases in `value`. This does not handle inference and expects +/// its input to be already fully resolved. +pub fn deeply_normalize<'db, T>(at: At<'_, 'db>, value: T) -> Result>> +where + T: TypeFoldable>, +{ + assert!(!value.has_escaping_bound_vars()); + deeply_normalize_with_skipped_universes(at, value, vec![]) +} + +/// Deeply normalize all aliases in `value`. This does not handle inference and expects +/// its input to be already fully resolved. +/// +/// Additionally takes a list of universes which represents the binders which have been +/// entered before passing `value` to the function. This is currently needed for +/// `normalize_erasing_regions`, which skips binders as it walks through a type. +pub fn deeply_normalize_with_skipped_universes<'db, T>( + at: At<'_, 'db>, + value: T, + universes: Vec>, +) -> Result>> +where + T: TypeFoldable>, +{ + let (value, coroutine_goals) = + deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals( + at, value, universes, + )?; + assert_eq!(coroutine_goals, vec![]); + + Ok(value) +} + +/// Deeply normalize all aliases in `value`. This does not handle inference and expects +/// its input to be already fully resolved. +/// +/// Additionally takes a list of universes which represents the binders which have been +/// entered before passing `value` to the function. This is currently needed for +/// `normalize_erasing_regions`, which skips binders as it walks through a type. +/// +/// This returns a set of stalled obligations involving coroutines if the typing mode of +/// the underlying infcx has any stalled coroutine def ids. +pub fn deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals<'db, T>( + at: At<'_, 'db>, + value: T, + universes: Vec>, +) -> Result<(T, Vec>>), Vec>> +where + T: TypeFoldable>, +{ + let fulfill_cx = FulfillmentCtxt::new(at.infcx); + let mut folder = NormalizationFolder { at, fulfill_cx, depth: 0, universes }; + let value = value.try_fold_with(&mut folder)?; + let errors = folder.fulfill_cx.select_all_or_error(at.infcx); + if errors.is_empty() { Ok((value, vec![])) } else { Err(errors) } +} + +struct NormalizationFolder<'me, 'db> { + at: At<'me, 'db>, + fulfill_cx: FulfillmentCtxt<'db>, + depth: usize, + universes: Vec>, +} + +impl<'db> NormalizationFolder<'_, 'db> { + fn normalize_alias_term( + &mut self, + alias_term: Term<'db>, + ) -> Result, Vec>> { + let infcx = self.at.infcx; + let tcx = infcx.interner; + let recursion_limit = tcx.recursion_limit(); + if self.depth > recursion_limit { + return Err(vec![]); + } + + self.depth += 1; + + let infer_term = infcx.next_term_var_of_kind(alias_term); + let obligation = Obligation::new( + tcx, + self.at.cause.clone(), + self.at.param_env, + PredicateKind::AliasRelate(alias_term, infer_term, AliasRelationDirection::Equate), + ); + + self.fulfill_cx.register_predicate_obligation(infcx, obligation); + self.select_all_and_stall_coroutine_predicates()?; + + // Alias is guaranteed to be fully structurally resolved, + // so we can super fold here. + let term = infcx.resolve_vars_if_possible(infer_term); + // super-folding the `term` will directly fold the `Ty` or `Const` so + // we have to match on the term and super-fold them manually. + let result = match term.kind() { + TermKind::Ty(ty) => ty.try_super_fold_with(self)?.into(), + TermKind::Const(ct) => ct.try_super_fold_with(self)?.into(), + }; + self.depth -= 1; + Ok(result) + } + + fn select_all_and_stall_coroutine_predicates( + &mut self, + ) -> Result<(), Vec>> { + let errors = self.fulfill_cx.select_where_possible(self.at.infcx); + if !errors.is_empty() { + return Err(errors); + } + + let errors = self.fulfill_cx.collect_remaining_errors(self.at.infcx); + if !errors.is_empty() { + return Err(errors); + } + + Ok(()) + } +} + +impl<'db> FallibleTypeFolder> for NormalizationFolder<'_, 'db> { + type Error = Vec>; + + fn cx(&self) -> DbInterner<'db> { + self.at.infcx.interner + } + + fn try_fold_binder>>( + &mut self, + t: Binder<'db, T>, + ) -> Result, Self::Error> { + self.universes.push(None); + let t = t.try_super_fold_with(self)?; + self.universes.pop(); + Ok(t) + } + + #[tracing::instrument(level = "trace", skip(self), ret)] + fn try_fold_ty(&mut self, ty: Ty<'db>) -> Result, Self::Error> { + let infcx = self.at.infcx; + debug_assert_eq!(ty, infcx.shallow_resolve(ty)); + if !ty.has_aliases() { + return Ok(ty); + } + + let TyKind::Alias(..) = ty.kind() else { return ty.try_super_fold_with(self) }; + + if ty.has_escaping_bound_vars() { + let (ty, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, ty); + let result = self.normalize_alias_term(ty.into())?.expect_type(); + Ok(PlaceholderReplacer::replace_placeholders( + infcx, + mapped_regions, + mapped_types, + mapped_consts, + &self.universes, + result, + )) + } else { + Ok(self.normalize_alias_term(ty.into())?.expect_type()) + } + } + + #[tracing::instrument(level = "trace", skip(self), ret)] + fn try_fold_const(&mut self, ct: Const<'db>) -> Result, Self::Error> { + let infcx = self.at.infcx; + debug_assert_eq!(ct, infcx.shallow_resolve_const(ct)); + if !ct.has_aliases() { + return Ok(ct); + } + + let ConstKind::Unevaluated(..) = ct.kind() else { return ct.try_super_fold_with(self) }; + + if ct.has_escaping_bound_vars() { + let (ct, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, ct); + let result = self.normalize_alias_term(ct.into())?.expect_const(); + Ok(PlaceholderReplacer::replace_placeholders( + infcx, + mapped_regions, + mapped_types, + mapped_consts, + &self.universes, + result, + )) + } else { + Ok(self.normalize_alias_term(ct.into())?.expect_const()) + } + } +} + +// Deeply normalize a value and return it +pub(crate) fn deeply_normalize_for_diagnostics<'db, T: TypeFoldable>>( + infcx: &InferCtxt<'db>, + param_env: ParamEnv<'db>, + t: T, +) -> T { + t.fold_with(&mut DeeplyNormalizeForDiagnosticsFolder { + at: infcx.at(&ObligationCause::dummy(), param_env), + }) +} + +struct DeeplyNormalizeForDiagnosticsFolder<'a, 'db> { + at: At<'a, 'db>, +} + +impl<'db> TypeFolder> for DeeplyNormalizeForDiagnosticsFolder<'_, 'db> { + fn cx(&self) -> DbInterner<'db> { + self.at.infcx.interner + } + + fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> { + let infcx = self.at.infcx; + let result: Result<_, Vec>> = infcx.commit_if_ok(|_| { + deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals( + self.at, + ty, + vec![None; ty.outer_exclusive_binder().as_usize()], + ) + }); + match result { + Ok((ty, _)) => ty, + Err(_) => ty.super_fold_with(self), + } + } + + fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> { + let infcx = self.at.infcx; + let result: Result<_, Vec>> = infcx.commit_if_ok(|_| { + deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals( + self.at, + ct, + vec![None; ct.outer_exclusive_binder().as_usize()], + ) + }); + match result { + Ok((ct, _)) => ct, + Err(_) => ct.super_fold_with(self), + } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs new file mode 100644 index 000000000000..c59cdac5f99f --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs @@ -0,0 +1,332 @@ +//! Things related to regions. + +use intern::{Interned, Symbol}; +use rustc_type_ir::{ + BoundVar, Flags, INNERMOST, RegionVid, TypeFlags, TypeFoldable, TypeVisitable, VisitorResult, + inherent::{IntoKind, PlaceholderLike, SliceLike}, + relate::Relate, +}; + +use crate::next_solver::{GenericArg, OutlivesPredicate}; + +use super::{ + ErrorGuaranteed, SolverDefId, interned_vec_db, + interner::{BoundVarKind, DbInterner, Placeholder}, +}; + +type RegionKind<'db> = rustc_type_ir::RegionKind>; + +#[salsa::interned(constructor = new_, debug)] +pub struct Region<'db> { + #[returns(ref)] + kind_: RegionKind<'db>, +} + +impl<'db> Region<'db> { + pub fn new(interner: DbInterner<'db>, kind: RegionKind<'db>) -> Self { + Region::new_(interner.db(), kind) + } + + pub fn inner(&self) -> &RegionKind<'db> { + salsa::with_attached_database(|db| { + let inner = self.kind_(db); + // SAFETY: The caller already has access to a `Region<'db>`, so borrowchecking will + // make sure that our returned value is valid for the lifetime `'db`. + unsafe { std::mem::transmute::<&RegionKind<'_>, &RegionKind<'db>>(inner) } + }) + .unwrap() + } + + pub fn new_early_param( + interner: DbInterner<'db>, + early_bound_region: EarlyParamRegion, + ) -> Self { + Region::new(interner, RegionKind::ReEarlyParam(early_bound_region)) + } + + pub fn new_placeholder(interner: DbInterner<'db>, placeholder: PlaceholderRegion) -> Self { + Region::new(interner, RegionKind::RePlaceholder(placeholder)) + } + + pub fn new_var(interner: DbInterner<'db>, v: RegionVid) -> Region<'db> { + Region::new(interner, RegionKind::ReVar(v)) + } + + pub fn is_placeholder(&self) -> bool { + matches!(self.inner(), RegionKind::RePlaceholder(..)) + } + + pub fn is_static(&self) -> bool { + matches!(self.inner(), RegionKind::ReStatic) + } + + pub fn error(interner: DbInterner<'db>) -> Self { + Region::new(interner, RegionKind::ReError(ErrorGuaranteed)) + } + + pub fn type_flags(&self) -> TypeFlags { + let mut flags = TypeFlags::empty(); + + match &self.inner() { + RegionKind::ReVar(..) => { + flags |= TypeFlags::HAS_FREE_REGIONS; + flags |= TypeFlags::HAS_FREE_LOCAL_REGIONS; + flags |= TypeFlags::HAS_RE_INFER; + } + RegionKind::RePlaceholder(..) => { + flags |= TypeFlags::HAS_FREE_REGIONS; + flags |= TypeFlags::HAS_FREE_LOCAL_REGIONS; + flags |= TypeFlags::HAS_RE_PLACEHOLDER; + } + RegionKind::ReEarlyParam(..) => { + flags |= TypeFlags::HAS_FREE_REGIONS; + flags |= TypeFlags::HAS_FREE_LOCAL_REGIONS; + flags |= TypeFlags::HAS_RE_PARAM; + } + RegionKind::ReLateParam(..) => { + flags |= TypeFlags::HAS_FREE_REGIONS; + flags |= TypeFlags::HAS_FREE_LOCAL_REGIONS; + } + RegionKind::ReStatic => { + flags |= TypeFlags::HAS_FREE_REGIONS; + } + RegionKind::ReBound(..) => { + flags |= TypeFlags::HAS_RE_BOUND; + } + RegionKind::ReErased => { + flags |= TypeFlags::HAS_RE_ERASED; + } + RegionKind::ReError(..) => { + flags |= TypeFlags::HAS_FREE_REGIONS; + flags |= TypeFlags::HAS_ERROR; + } + } + + flags + } +} + +pub type PlaceholderRegion = Placeholder; + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct EarlyParamRegion { + pub index: u32, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +/// The parameter representation of late-bound function parameters, "some region +/// at least as big as the scope `fr.scope`". +/// +/// Similar to a placeholder region as we create `LateParam` regions when entering a binder +/// except they are always in the root universe and instead of using a boundvar to distinguish +/// between others we use the `DefId` of the parameter. For this reason the `bound_region` field +/// should basically always be `BoundRegionKind::Named` as otherwise there is no way of telling +/// different parameters apart. +pub struct LateParamRegion { + pub scope: SolverDefId, + pub bound_region: BoundRegionKind, +} + +impl std::fmt::Debug for LateParamRegion { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "ReLateParam({:?}, {:?})", self.scope, self.bound_region) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub enum BoundRegionKind { + /// An anonymous region parameter for a given fn (&T) + Anon, + + /// Named region parameters for functions (a in &'a T) + /// + /// The `DefId` is needed to distinguish free regions in + /// the event of shadowing. + Named(SolverDefId), + + /// Anonymous region for the implicit env pointer parameter + /// to a closure + ClosureEnv, +} + +impl std::fmt::Debug for BoundRegionKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match *self { + BoundRegionKind::Anon => write!(f, "BrAnon"), + BoundRegionKind::Named(did) => { + write!(f, "BrNamed({did:?})") + } + BoundRegionKind::ClosureEnv => write!(f, "BrEnv"), + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct BoundRegion { + pub var: BoundVar, + pub kind: BoundRegionKind, +} + +impl rustc_type_ir::inherent::ParamLike for EarlyParamRegion { + fn index(self) -> u32 { + self.index + } +} + +impl std::fmt::Debug for EarlyParamRegion { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "#{}", self.index) + // write!(f, "{}/#{}", self.name, self.index) + } +} + +impl<'db> rustc_type_ir::inherent::BoundVarLike> for BoundRegion { + fn var(self) -> BoundVar { + self.var + } + + fn assert_eq(self, var: BoundVarKind) { + assert_eq!(self.kind, var.expect_region()) + } +} + +impl core::fmt::Debug for BoundRegion { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.kind { + BoundRegionKind::Anon => write!(f, "{:?}", self.var), + BoundRegionKind::ClosureEnv => write!(f, "{:?}.Env", self.var), + BoundRegionKind::Named(def) => { + write!(f, "{:?}.Named({:?})", self.var, def) + } + } + } +} + +impl BoundRegionKind { + pub fn is_named(&self) -> bool { + matches!(self, BoundRegionKind::Named(_)) + } + + pub fn get_name(&self) -> Option { + None + } + + pub fn get_id(&self) -> Option { + match self { + BoundRegionKind::Named(id) => Some(*id), + _ => None, + } + } +} + +impl<'db> IntoKind for Region<'db> { + type Kind = RegionKind<'db>; + + fn kind(self) -> Self::Kind { + *self.inner() + } +} + +impl<'db> TypeVisitable> for Region<'db> { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + visitor.visit_region(*self) + } +} + +impl<'db> TypeFoldable> for Region<'db> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + folder.try_fold_region(self) + } + fn fold_with>>(self, folder: &mut F) -> Self { + folder.fold_region(self) + } +} + +impl<'db> Relate> for Region<'db> { + fn relate>>( + relation: &mut R, + a: Self, + b: Self, + ) -> rustc_type_ir::relate::RelateResult, Self> { + relation.regions(a, b) + } +} + +impl<'db> Flags for Region<'db> { + fn flags(&self) -> rustc_type_ir::TypeFlags { + self.type_flags() + } + + fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex { + match &self.inner() { + RegionKind::ReBound(debruijn, _) => debruijn.shifted_in(1), + _ => INNERMOST, + } + } +} + +impl<'db> rustc_type_ir::inherent::Region> for Region<'db> { + fn new_bound( + interner: DbInterner<'db>, + debruijn: rustc_type_ir::DebruijnIndex, + var: BoundRegion, + ) -> Self { + Region::new(interner, RegionKind::ReBound(debruijn, var)) + } + + fn new_anon_bound( + interner: DbInterner<'db>, + debruijn: rustc_type_ir::DebruijnIndex, + var: rustc_type_ir::BoundVar, + ) -> Self { + Region::new( + interner, + RegionKind::ReBound(debruijn, BoundRegion { var, kind: BoundRegionKind::Anon }), + ) + } + + fn new_static(interner: DbInterner<'db>) -> Self { + Region::new(interner, RegionKind::ReStatic) + } + + fn new_placeholder( + interner: DbInterner<'db>, + var: as rustc_type_ir::Interner>::PlaceholderRegion, + ) -> Self { + Region::new(interner, RegionKind::RePlaceholder(var)) + } +} + +impl<'db> PlaceholderLike> for PlaceholderRegion { + type Bound = BoundRegion; + + fn universe(self) -> rustc_type_ir::UniverseIndex { + self.universe + } + + fn var(self) -> rustc_type_ir::BoundVar { + self.bound.var + } + + fn with_updated_universe(self, ui: rustc_type_ir::UniverseIndex) -> Self { + Placeholder { universe: ui, bound: self.bound } + } + + fn new(ui: rustc_type_ir::UniverseIndex, bound: Self::Bound) -> Self { + Placeholder { universe: ui, bound } + } + + fn new_anon(ui: rustc_type_ir::UniverseIndex, var: rustc_type_ir::BoundVar) -> Self { + Placeholder { universe: ui, bound: BoundRegion { var, kind: BoundRegionKind::Anon } } + } +} + +type GenericArgOutlivesPredicate<'db> = OutlivesPredicate<'db, GenericArg<'db>>; + +interned_vec_db!(RegionAssumptions, GenericArgOutlivesPredicate); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs new file mode 100644 index 000000000000..45888ac4d235 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs @@ -0,0 +1,289 @@ +//! Defining `SolverContext` for next-trait-solver. + +use hir_def::{AssocItemId, GeneralConstId, TypeAliasId}; +use rustc_next_trait_solver::delegate::SolverDelegate; +use rustc_type_ir::{ + InferCtxtLike, Interner, PredicatePolarity, TypeFlags, TypeVisitableExt, UniverseIndex, + inherent::{IntoKind, SliceLike, Span as _, Term as _, Ty as _}, + lang_items::TraitSolverLangItem, + solve::{Certainty, NoSolution}, +}; + +use crate::{ + TraitRefExt, + db::HirDatabase, + next_solver::{ + ClauseKind, CoercePredicate, PredicateKind, SubtypePredicate, + mapping::{ChalkToNextSolver, convert_args_for_result}, + util::sizedness_fast_path, + }, +}; + +use super::{ + Canonical, CanonicalVarValues, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, + ParamEnv, Predicate, SolverDefId, Span, Ty, UnevaluatedConst, + infer::{DbInternerInferExt, InferCtxt, canonical::instantiate::CanonicalExt}, +}; + +pub type Goal<'db, P> = rustc_type_ir::solve::Goal, P>; + +#[repr(transparent)] +pub(crate) struct SolverContext<'db>(pub(crate) InferCtxt<'db>); + +impl<'a, 'db> From<&'a InferCtxt<'db>> for &'a SolverContext<'db> { + fn from(infcx: &'a InferCtxt<'db>) -> Self { + // SAFETY: `repr(transparent)` + unsafe { std::mem::transmute(infcx) } + } +} + +impl<'db> std::ops::Deref for SolverContext<'db> { + type Target = InferCtxt<'db>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'db> SolverDelegate for SolverContext<'db> { + type Interner = DbInterner<'db>; + type Infcx = InferCtxt<'db>; + + fn cx(&self) -> Self::Interner { + self.0.interner + } + + fn build_with_canonical( + cx: Self::Interner, + canonical: &rustc_type_ir::CanonicalQueryInput, + ) -> (Self, V, rustc_type_ir::CanonicalVarValues) + where + V: rustc_type_ir::TypeFoldable, + { + let (infcx, value, vars) = cx.infer_ctxt().build_with_canonical(canonical); + (SolverContext(infcx), value, vars) + } + + fn fresh_var_for_kind_with_span( + &self, + arg: ::GenericArg, + span: ::Span, + ) -> ::GenericArg { + unimplemented!() + } + + fn leak_check( + &self, + max_input_universe: rustc_type_ir::UniverseIndex, + ) -> Result<(), NoSolution> { + Ok(()) + } + + fn well_formed_goals( + &self, + param_env: ::ParamEnv, + arg: ::Term, + ) -> Option< + Vec< + rustc_type_ir::solve::Goal< + Self::Interner, + ::Predicate, + >, + >, + > { + unimplemented!() + } + + fn make_deduplicated_outlives_constraints( + &self, + ) -> Vec< + rustc_type_ir::OutlivesPredicate< + Self::Interner, + ::GenericArg, + >, + > { + // FIXME: add if we care about regions + vec![] + } + + fn instantiate_canonical( + &self, + canonical: rustc_type_ir::Canonical, + values: rustc_type_ir::CanonicalVarValues, + ) -> V + where + V: rustc_type_ir::TypeFoldable, + { + canonical.instantiate(self.cx(), &values) + } + + fn instantiate_canonical_var_with_infer( + &self, + cv_info: rustc_type_ir::CanonicalVarKind, + _span: ::Span, + universe_map: impl Fn(rustc_type_ir::UniverseIndex) -> rustc_type_ir::UniverseIndex, + ) -> ::GenericArg { + self.0.instantiate_canonical_var(cv_info, universe_map) + } + + fn add_item_bounds_for_hidden_type( + &self, + def_id: ::DefId, + args: ::GenericArgs, + param_env: ::ParamEnv, + hidden_ty: ::Ty, + goals: &mut Vec< + rustc_type_ir::solve::Goal< + Self::Interner, + ::Predicate, + >, + >, + ) { + unimplemented!() + } + + fn fetch_eligible_assoc_item( + &self, + goal_trait_ref: rustc_type_ir::TraitRef, + trait_assoc_def_id: ::DefId, + impl_def_id: ::DefId, + ) -> Result::DefId>, ErrorGuaranteed> { + let impl_id = match impl_def_id { + SolverDefId::ImplId(id) => id, + _ => panic!("Unexpected SolverDefId"), + }; + let trait_assoc_id = match trait_assoc_def_id { + SolverDefId::TypeAliasId(id) => id, + _ => panic!("Unexpected SolverDefId"), + }; + let trait_ref = self + .0 + .interner + .db() + .impl_trait(impl_id) + // ImplIds for impls where the trait ref can't be resolved should never reach solver + .expect("invalid impl passed to next-solver") + .into_value_and_skipped_binders() + .0; + let trait_ = trait_ref.hir_trait_id(); + let trait_data = trait_.trait_items(self.0.interner.db()); + let id = + impl_id.impl_items(self.0.interner.db()).items.iter().find_map(|item| -> Option<_> { + match item { + (_, AssocItemId::TypeAliasId(type_alias)) => { + let name = &self.0.interner.db().type_alias_signature(*type_alias).name; + let found_trait_assoc_id = trait_data.associated_type_by_name(name)?; + (found_trait_assoc_id == trait_assoc_id).then_some(*type_alias) + } + _ => None, + } + }); + Ok(id.map(SolverDefId::TypeAliasId)) + } + + fn is_transmutable( + &self, + dst: ::Ty, + src: ::Ty, + assume: ::Const, + ) -> Result { + unimplemented!() + } + + fn evaluate_const( + &self, + param_env: ::ParamEnv, + uv: rustc_type_ir::UnevaluatedConst, + ) -> Option<::Const> { + let c = match uv.def { + SolverDefId::ConstId(c) => GeneralConstId::ConstId(c), + SolverDefId::StaticId(c) => GeneralConstId::StaticId(c), + _ => unreachable!(), + }; + let subst = convert_args_for_result(self.interner, uv.args.as_slice()); + let ec = self.cx().db.const_eval(c, subst, None).ok()?; + Some(ec.to_nextsolver(self.interner)) + } + + fn compute_goal_fast_path( + &self, + goal: rustc_type_ir::solve::Goal< + Self::Interner, + ::Predicate, + >, + span: ::Span, + ) -> Option { + if let Some(trait_pred) = goal.predicate.as_trait_clause() { + if self.shallow_resolve(trait_pred.self_ty().skip_binder()).is_ty_var() + // We don't do this fast path when opaques are defined since we may + // eventually use opaques to incompletely guide inference via ty var + // self types. + // FIXME: Properly consider opaques here. + && self.inner.borrow_mut().opaque_types().is_empty() + { + return Some(Certainty::AMBIGUOUS); + } + + if trait_pred.polarity() == PredicatePolarity::Positive { + match self.0.cx().as_lang_item(trait_pred.def_id()) { + Some(TraitSolverLangItem::Sized) | Some(TraitSolverLangItem::MetaSized) => { + let predicate = self.resolve_vars_if_possible(goal.predicate); + if sizedness_fast_path(self.cx(), predicate, goal.param_env) { + return Some(Certainty::Yes); + } + } + Some(TraitSolverLangItem::Copy | TraitSolverLangItem::Clone) => { + let self_ty = + self.resolve_vars_if_possible(trait_pred.self_ty().skip_binder()); + // Unlike `Sized` traits, which always prefer the built-in impl, + // `Copy`/`Clone` may be shadowed by a param-env candidate which + // could force a lifetime error or guide inference. While that's + // not generally desirable, it is observable, so for now let's + // ignore this fast path for types that have regions or infer. + if !self_ty + .has_type_flags(TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_INFER) + && self_ty.is_trivially_pure_clone_copy() + { + return Some(Certainty::Yes); + } + } + _ => {} + } + } + } + + let pred = goal.predicate.kind(); + match pred.no_bound_vars()? { + PredicateKind::Clause(ClauseKind::RegionOutlives(outlives)) => Some(Certainty::Yes), + PredicateKind::Clause(ClauseKind::TypeOutlives(outlives)) => Some(Certainty::Yes), + PredicateKind::Subtype(SubtypePredicate { a, b, .. }) + | PredicateKind::Coerce(CoercePredicate { a, b }) => { + if self.shallow_resolve(a).is_ty_var() && self.shallow_resolve(b).is_ty_var() { + // FIXME: We also need to register a subtype relation between these vars + // when those are added, and if they aren't in the same sub root then + // we should mark this goal as `has_changed`. + Some(Certainty::AMBIGUOUS) + } else { + None + } + } + PredicateKind::Clause(ClauseKind::ConstArgHasType(ct, _)) => { + if self.shallow_resolve_const(ct).is_ct_infer() { + Some(Certainty::AMBIGUOUS) + } else { + None + } + } + PredicateKind::Clause(ClauseKind::WellFormed(arg)) => { + if arg.is_trivially_wf(self.interner) { + Some(Certainty::Yes) + } else if arg.is_infer() { + Some(Certainty::AMBIGUOUS) + } else { + None + } + } + _ => None, + } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs new file mode 100644 index 000000000000..0c0fe686b77d --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -0,0 +1,943 @@ +//! Things related to tys in the next-trait-solver. + +use intern::{Interned, Symbol, sym}; +use rustc_abi::{Float, Integer, Size}; +use rustc_ast_ir::{Mutability, try_visit, visit::VisitorResult}; +use rustc_type_ir::{ + BoundVar, ClosureKind, FlagComputation, Flags, FloatTy, FloatVid, InferTy, IntTy, IntVid, + TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, UintTy, + WithCachedTypeInfo, + inherent::{ + AdtDef, BoundVarLike, GenericArgs as _, IntoKind, ParamLike, PlaceholderLike, SliceLike, + }, + relate::Relate, + solve::SizedTraitKind, + walk::TypeWalker, +}; +use salsa::plumbing::{AsId, FromId}; +use smallvec::SmallVec; + +use crate::{ + db::HirDatabase, + interner::InternedWrapperNoDebug, + next_solver::util::{CoroutineArgsExt, IntegerTypeExt}, +}; + +use super::{ + BoundVarKind, DbInterner, GenericArgs, Placeholder, SolverDefId, interned_vec_db, + util::{FloatExt, IntegerExt}, +}; + +pub type TyKind<'db> = rustc_type_ir::TyKind>; +pub type FnHeader<'db> = rustc_type_ir::FnHeader>; + +#[salsa::interned(constructor = new_)] +pub struct Ty<'db> { + #[returns(ref)] + kind_: InternedWrapperNoDebug>>, +} + +const _: () = { + const fn is_copy() {} + is_copy::>(); +}; + +impl<'db> Ty<'db> { + pub fn new(interner: DbInterner<'db>, kind: TyKind<'db>) -> Self { + let flags = FlagComputation::for_kind(&kind); + let cached = WithCachedTypeInfo { + internee: kind, + flags: flags.flags, + outer_exclusive_binder: flags.outer_exclusive_binder, + }; + Ty::new_(interner.db(), InternedWrapperNoDebug(cached)) + } + + pub fn inner(&self) -> &WithCachedTypeInfo> { + salsa::with_attached_database(|db| { + let inner = &self.kind_(db).0; + // SAFETY: The caller already has access to a `Ty<'db>`, so borrowchecking will + // make sure that our returned value is valid for the lifetime `'db`. + unsafe { std::mem::transmute(inner) } + }) + .unwrap() + } + + pub fn new_param(interner: DbInterner<'db>, index: u32, name: Symbol) -> Self { + Ty::new(interner, TyKind::Param(ParamTy { index })) + } + + pub fn new_placeholder(interner: DbInterner<'db>, placeholder: PlaceholderTy) -> Self { + Ty::new(interner, TyKind::Placeholder(placeholder)) + } + + pub fn new_infer(interner: DbInterner<'db>, infer: InferTy) -> Self { + Ty::new(interner, TyKind::Infer(infer)) + } + + pub fn new_int_var(interner: DbInterner<'db>, v: IntVid) -> Self { + Ty::new_infer(interner, InferTy::IntVar(v)) + } + + pub fn new_float_var(interner: DbInterner<'db>, v: FloatVid) -> Self { + Ty::new_infer(interner, InferTy::FloatVar(v)) + } + + pub fn new_int(interner: DbInterner<'db>, i: IntTy) -> Self { + Ty::new(interner, TyKind::Int(i)) + } + + pub fn new_uint(interner: DbInterner<'db>, ui: UintTy) -> Self { + Ty::new(interner, TyKind::Uint(ui)) + } + + pub fn new_float(interner: DbInterner<'db>, f: FloatTy) -> Self { + Ty::new(interner, TyKind::Float(f)) + } + + pub fn new_fresh(interner: DbInterner<'db>, n: u32) -> Self { + Ty::new_infer(interner, InferTy::FreshTy(n)) + } + + pub fn new_fresh_int(interner: DbInterner<'db>, n: u32) -> Self { + Ty::new_infer(interner, InferTy::FreshIntTy(n)) + } + + pub fn new_fresh_float(interner: DbInterner<'db>, n: u32) -> Self { + Ty::new_infer(interner, InferTy::FreshFloatTy(n)) + } + + /// Returns the `Size` for primitive types (bool, uint, int, char, float). + pub fn primitive_size(self, interner: DbInterner<'db>) -> Size { + match self.kind() { + TyKind::Bool => Size::from_bytes(1), + TyKind::Char => Size::from_bytes(4), + TyKind::Int(ity) => Integer::from_int_ty(&interner, ity).size(), + TyKind::Uint(uty) => Integer::from_uint_ty(&interner, uty).size(), + TyKind::Float(fty) => Float::from_float_ty(fty).size(), + _ => panic!("non primitive type"), + } + } + + pub fn int_size_and_signed(self, interner: DbInterner<'db>) -> (Size, bool) { + match self.kind() { + TyKind::Int(ity) => (Integer::from_int_ty(&interner, ity).size(), true), + TyKind::Uint(uty) => (Integer::from_uint_ty(&interner, uty).size(), false), + _ => panic!("non integer discriminant"), + } + } + + pub fn walk(self) -> TypeWalker> { + TypeWalker::new(self.into()) + } + + /// Fast path helper for testing if a type is `Sized` or `MetaSized`. + /// + /// Returning true means the type is known to implement the sizedness trait. Returning `false` + /// means nothing -- could be sized, might not be. + /// + /// Note that we could never rely on the fact that a type such as `[_]` is trivially `!Sized` + /// because we could be in a type environment with a bound such as `[_]: Copy`. A function with + /// such a bound obviously never can be called, but that doesn't mean it shouldn't typecheck. + /// This is why this method doesn't return `Option`. + #[tracing::instrument(skip(tcx), level = "debug")] + pub fn has_trivial_sizedness(self, tcx: DbInterner<'db>, sizedness: SizedTraitKind) -> bool { + match self.kind() { + TyKind::Infer(InferTy::IntVar(_) | InferTy::FloatVar(_)) + | TyKind::Uint(_) + | TyKind::Int(_) + | TyKind::Bool + | TyKind::Float(_) + | TyKind::FnDef(..) + | TyKind::FnPtr(..) + | TyKind::UnsafeBinder(_) + | TyKind::RawPtr(..) + | TyKind::Char + | TyKind::Ref(..) + | TyKind::Coroutine(..) + | TyKind::CoroutineWitness(..) + | TyKind::Array(..) + | TyKind::Pat(..) + | TyKind::Closure(..) + | TyKind::CoroutineClosure(..) + | TyKind::Never + | TyKind::Error(_) => true, + + TyKind::Str | TyKind::Slice(_) | TyKind::Dynamic(_, _, _) => match sizedness { + SizedTraitKind::Sized => false, + SizedTraitKind::MetaSized => true, + }, + + TyKind::Foreign(..) => match sizedness { + SizedTraitKind::Sized | SizedTraitKind::MetaSized => false, + }, + + TyKind::Tuple(tys) => { + tys.last().is_none_or(|ty| ty.has_trivial_sizedness(tcx, sizedness)) + } + + TyKind::Adt(def, args) => def + .sizedness_constraint(tcx, sizedness) + .is_none_or(|ty| ty.instantiate(tcx, args).has_trivial_sizedness(tcx, sizedness)), + + TyKind::Alias(..) | TyKind::Param(_) | TyKind::Placeholder(..) | TyKind::Bound(..) => { + false + } + + TyKind::Infer(InferTy::TyVar(_)) => false, + + TyKind::Infer( + InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_), + ) => { + panic!("`has_trivial_sizedness` applied to unexpected type: {self:?}") + } + } + } + + /// Fast path helper for primitives which are always `Copy` and which + /// have a side-effect-free `Clone` impl. + /// + /// Returning true means the type is known to be pure and `Copy+Clone`. + /// Returning `false` means nothing -- could be `Copy`, might not be. + /// + /// This is mostly useful for optimizations, as these are the types + /// on which we can replace cloning with dereferencing. + pub fn is_trivially_pure_clone_copy(self) -> bool { + match self.kind() { + TyKind::Bool | TyKind::Char | TyKind::Never => true, + + // These aren't even `Clone` + TyKind::Str | TyKind::Slice(..) | TyKind::Foreign(..) | TyKind::Dynamic(..) => false, + + TyKind::Infer(InferTy::FloatVar(_) | InferTy::IntVar(_)) + | TyKind::Int(..) + | TyKind::Uint(..) + | TyKind::Float(..) => true, + + // ZST which can't be named are fine. + TyKind::FnDef(..) => true, + + TyKind::Array(element_ty, _len) => element_ty.is_trivially_pure_clone_copy(), + + // A 100-tuple isn't "trivial", so doing this only for reasonable sizes. + TyKind::Tuple(field_tys) => { + field_tys.len() <= 3 && field_tys.iter().all(Self::is_trivially_pure_clone_copy) + } + + TyKind::Pat(ty, _) => ty.is_trivially_pure_clone_copy(), + + // Sometimes traits aren't implemented for every ABI or arity, + // because we can't be generic over everything yet. + TyKind::FnPtr(..) => false, + + // Definitely absolutely not copy. + TyKind::Ref(_, _, Mutability::Mut) => false, + + // The standard library has a blanket Copy impl for shared references and raw pointers, + // for all unsized types. + TyKind::Ref(_, _, Mutability::Not) | TyKind::RawPtr(..) => true, + + TyKind::Coroutine(..) | TyKind::CoroutineWitness(..) => false, + + // Might be, but not "trivial" so just giving the safe answer. + TyKind::Adt(..) | TyKind::Closure(..) | TyKind::CoroutineClosure(..) => false, + + TyKind::UnsafeBinder(_) => false, + + // Needs normalization or revealing to determine, so no is the safe answer. + TyKind::Alias(..) => false, + + TyKind::Param(..) + | TyKind::Placeholder(..) + | TyKind::Bound(..) + | TyKind::Infer(..) + | TyKind::Error(..) => false, + } + } + + pub fn is_trivially_wf(self, tcx: DbInterner<'db>) -> bool { + match self.kind() { + TyKind::Bool + | TyKind::Char + | TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) + | TyKind::Str + | TyKind::Never + | TyKind::Param(_) + | TyKind::Placeholder(_) + | TyKind::Bound(..) => true, + + TyKind::Slice(ty) => { + ty.is_trivially_wf(tcx) && ty.has_trivial_sizedness(tcx, SizedTraitKind::Sized) + } + TyKind::RawPtr(ty, _) => ty.is_trivially_wf(tcx), + + TyKind::FnPtr(sig_tys, _) => { + sig_tys.skip_binder().inputs_and_output.iter().all(|ty| ty.is_trivially_wf(tcx)) + } + TyKind::Ref(_, ty, _) => ty.is_global() && ty.is_trivially_wf(tcx), + + TyKind::Infer(infer) => match infer { + InferTy::TyVar(_) => false, + InferTy::IntVar(_) | InferTy::FloatVar(_) => true, + InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_) => true, + }, + + TyKind::Adt(_, _) + | TyKind::Tuple(_) + | TyKind::Array(..) + | TyKind::Foreign(_) + | TyKind::Pat(_, _) + | TyKind::FnDef(..) + | TyKind::UnsafeBinder(..) + | TyKind::Dynamic(..) + | TyKind::Closure(..) + | TyKind::CoroutineClosure(..) + | TyKind::Coroutine(..) + | TyKind::CoroutineWitness(..) + | TyKind::Alias(..) + | TyKind::Error(_) => false, + } + } +} + +impl<'db> std::fmt::Debug for Ty<'db> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.inner().internee.fmt(f) + } +} + +impl<'db> std::fmt::Debug for InternedWrapperNoDebug>> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.internee.fmt(f) + } +} + +impl<'db> IntoKind for Ty<'db> { + type Kind = TyKind<'db>; + + fn kind(self) -> Self::Kind { + self.inner().internee + } +} + +impl<'db> TypeVisitable> for Ty<'db> { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + visitor.visit_ty(*self) + } +} + +impl<'db> TypeSuperVisitable> for Ty<'db> { + fn super_visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + match (*self).kind() { + TyKind::RawPtr(ty, _mutbl) => ty.visit_with(visitor), + TyKind::Array(typ, sz) => { + try_visit!(typ.visit_with(visitor)); + sz.visit_with(visitor) + } + TyKind::Slice(typ) => typ.visit_with(visitor), + TyKind::Adt(_, args) => args.visit_with(visitor), + TyKind::Dynamic(ref trait_ty, ref reg, _) => { + try_visit!(trait_ty.visit_with(visitor)); + reg.visit_with(visitor) + } + TyKind::Tuple(ts) => ts.visit_with(visitor), + TyKind::FnDef(_, args) => args.visit_with(visitor), + TyKind::FnPtr(ref sig_tys, _) => sig_tys.visit_with(visitor), + TyKind::UnsafeBinder(f) => f.visit_with(visitor), + TyKind::Ref(r, ty, _) => { + try_visit!(r.visit_with(visitor)); + ty.visit_with(visitor) + } + TyKind::Coroutine(_did, ref args) => args.visit_with(visitor), + TyKind::CoroutineWitness(_did, ref args) => args.visit_with(visitor), + TyKind::Closure(_did, ref args) => args.visit_with(visitor), + TyKind::CoroutineClosure(_did, ref args) => args.visit_with(visitor), + TyKind::Alias(_, ref data) => data.visit_with(visitor), + + TyKind::Pat(ty, pat) => { + try_visit!(ty.visit_with(visitor)); + pat.visit_with(visitor) + } + + TyKind::Error(guar) => guar.visit_with(visitor), + + TyKind::Bool + | TyKind::Char + | TyKind::Str + | TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) + | TyKind::Infer(_) + | TyKind::Bound(..) + | TyKind::Placeholder(..) + | TyKind::Param(..) + | TyKind::Never + | TyKind::Foreign(..) => V::Result::output(), + } + } +} + +impl<'db> TypeFoldable> for Ty<'db> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + folder.try_fold_ty(self) + } + fn fold_with>>(self, folder: &mut F) -> Self { + folder.fold_ty(self) + } +} + +impl<'db> TypeSuperFoldable> for Ty<'db> { + fn try_super_fold_with>>( + self, + folder: &mut F, + ) -> Result { + let kind = match self.kind() { + TyKind::RawPtr(ty, mutbl) => TyKind::RawPtr(ty.try_fold_with(folder)?, mutbl), + TyKind::Array(typ, sz) => { + TyKind::Array(typ.try_fold_with(folder)?, sz.try_fold_with(folder)?) + } + TyKind::Slice(typ) => TyKind::Slice(typ.try_fold_with(folder)?), + TyKind::Adt(tid, args) => TyKind::Adt(tid, args.try_fold_with(folder)?), + TyKind::Dynamic(trait_ty, region, representation) => TyKind::Dynamic( + trait_ty.try_fold_with(folder)?, + region.try_fold_with(folder)?, + representation, + ), + TyKind::Tuple(ts) => TyKind::Tuple(ts.try_fold_with(folder)?), + TyKind::FnDef(def_id, args) => TyKind::FnDef(def_id, args.try_fold_with(folder)?), + TyKind::FnPtr(sig_tys, hdr) => TyKind::FnPtr(sig_tys.try_fold_with(folder)?, hdr), + TyKind::UnsafeBinder(f) => TyKind::UnsafeBinder(f.try_fold_with(folder)?), + TyKind::Ref(r, ty, mutbl) => { + TyKind::Ref(r.try_fold_with(folder)?, ty.try_fold_with(folder)?, mutbl) + } + TyKind::Coroutine(did, args) => TyKind::Coroutine(did, args.try_fold_with(folder)?), + TyKind::CoroutineWitness(did, args) => { + TyKind::CoroutineWitness(did, args.try_fold_with(folder)?) + } + TyKind::Closure(did, args) => TyKind::Closure(did, args.try_fold_with(folder)?), + TyKind::CoroutineClosure(did, args) => { + TyKind::CoroutineClosure(did, args.try_fold_with(folder)?) + } + TyKind::Alias(kind, data) => TyKind::Alias(kind, data.try_fold_with(folder)?), + TyKind::Pat(ty, pat) => { + TyKind::Pat(ty.try_fold_with(folder)?, pat.try_fold_with(folder)?) + } + + TyKind::Bool + | TyKind::Char + | TyKind::Str + | TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) + | TyKind::Error(_) + | TyKind::Infer(_) + | TyKind::Param(..) + | TyKind::Bound(..) + | TyKind::Placeholder(..) + | TyKind::Never + | TyKind::Foreign(..) => return Ok(self), + }; + + Ok(if self.kind() == kind { self } else { Ty::new(folder.cx(), kind) }) + } + fn super_fold_with>>( + self, + folder: &mut F, + ) -> Self { + let kind = match self.kind() { + TyKind::RawPtr(ty, mutbl) => TyKind::RawPtr(ty.fold_with(folder), mutbl), + TyKind::Array(typ, sz) => TyKind::Array(typ.fold_with(folder), sz.fold_with(folder)), + TyKind::Slice(typ) => TyKind::Slice(typ.fold_with(folder)), + TyKind::Adt(tid, args) => TyKind::Adt(tid, args.fold_with(folder)), + TyKind::Dynamic(trait_ty, region, representation) => TyKind::Dynamic( + trait_ty.fold_with(folder), + region.fold_with(folder), + representation, + ), + TyKind::Tuple(ts) => TyKind::Tuple(ts.fold_with(folder)), + TyKind::FnDef(def_id, args) => TyKind::FnDef(def_id, args.fold_with(folder)), + TyKind::FnPtr(sig_tys, hdr) => TyKind::FnPtr(sig_tys.fold_with(folder), hdr), + TyKind::UnsafeBinder(f) => TyKind::UnsafeBinder(f.fold_with(folder)), + TyKind::Ref(r, ty, mutbl) => { + TyKind::Ref(r.fold_with(folder), ty.fold_with(folder), mutbl) + } + TyKind::Coroutine(did, args) => TyKind::Coroutine(did, args.fold_with(folder)), + TyKind::CoroutineWitness(did, args) => { + TyKind::CoroutineWitness(did, args.fold_with(folder)) + } + TyKind::Closure(did, args) => TyKind::Closure(did, args.fold_with(folder)), + TyKind::CoroutineClosure(did, args) => { + TyKind::CoroutineClosure(did, args.fold_with(folder)) + } + TyKind::Alias(kind, data) => TyKind::Alias(kind, data.fold_with(folder)), + TyKind::Pat(ty, pat) => TyKind::Pat(ty.fold_with(folder), pat.fold_with(folder)), + + TyKind::Bool + | TyKind::Char + | TyKind::Str + | TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) + | TyKind::Error(_) + | TyKind::Infer(_) + | TyKind::Param(..) + | TyKind::Bound(..) + | TyKind::Placeholder(..) + | TyKind::Never + | TyKind::Foreign(..) => return self, + }; + + if self.kind() == kind { self } else { Ty::new(folder.cx(), kind) } + } +} + +impl<'db> Relate> for Ty<'db> { + fn relate>>( + relation: &mut R, + a: Self, + b: Self, + ) -> rustc_type_ir::relate::RelateResult, Self> { + relation.tys(a, b) + } +} + +impl<'db> Flags for Ty<'db> { + fn flags(&self) -> rustc_type_ir::TypeFlags { + self.inner().flags + } + + fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex { + self.inner().outer_exclusive_binder + } +} + +impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> { + fn new_unit(interner: DbInterner<'db>) -> Self { + Ty::new(interner, TyKind::Tuple(Default::default())) + } + + fn new_bool(interner: DbInterner<'db>) -> Self { + Ty::new(interner, TyKind::Bool) + } + + fn new_u8(interner: DbInterner<'db>) -> Self { + Ty::new(interner, TyKind::Uint(rustc_type_ir::UintTy::U8)) + } + + fn new_usize(interner: DbInterner<'db>) -> Self { + Ty::new(interner, TyKind::Uint(rustc_type_ir::UintTy::Usize)) + } + + fn new_infer(interner: DbInterner<'db>, var: rustc_type_ir::InferTy) -> Self { + Ty::new(interner, TyKind::Infer(var)) + } + + fn new_var(interner: DbInterner<'db>, var: rustc_type_ir::TyVid) -> Self { + Ty::new(interner, TyKind::Infer(rustc_type_ir::InferTy::TyVar(var))) + } + + fn new_param(interner: DbInterner<'db>, param: ParamTy) -> Self { + Ty::new(interner, TyKind::Param(param)) + } + + fn new_placeholder(interner: DbInterner<'db>, param: PlaceholderTy) -> Self { + Ty::new(interner, TyKind::Placeholder(param)) + } + + fn new_bound( + interner: DbInterner<'db>, + debruijn: rustc_type_ir::DebruijnIndex, + var: BoundTy, + ) -> Self { + Ty::new(interner, TyKind::Bound(debruijn, var)) + } + + fn new_anon_bound( + interner: DbInterner<'db>, + debruijn: rustc_type_ir::DebruijnIndex, + var: BoundVar, + ) -> Self { + Ty::new(interner, TyKind::Bound(debruijn, BoundTy { var, kind: BoundTyKind::Anon })) + } + + fn new_alias( + interner: DbInterner<'db>, + kind: rustc_type_ir::AliasTyKind, + alias_ty: rustc_type_ir::AliasTy>, + ) -> Self { + Ty::new(interner, TyKind::Alias(kind, alias_ty)) + } + + fn new_error(interner: DbInterner<'db>, guar: ErrorGuaranteed) -> Self { + Ty::new(interner, TyKind::Error(guar)) + } + + fn new_adt( + interner: DbInterner<'db>, + adt_def: as rustc_type_ir::Interner>::AdtDef, + args: GenericArgs<'db>, + ) -> Self { + Ty::new(interner, TyKind::Adt(adt_def, args)) + } + + fn new_foreign( + interner: DbInterner<'db>, + def_id: as rustc_type_ir::Interner>::DefId, + ) -> Self { + Ty::new(interner, TyKind::Foreign(def_id)) + } + + fn new_dynamic( + interner: DbInterner<'db>, + preds: as rustc_type_ir::Interner>::BoundExistentialPredicates, + region: as rustc_type_ir::Interner>::Region, + kind: rustc_type_ir::DynKind, + ) -> Self { + Ty::new(interner, TyKind::Dynamic(preds, region, kind)) + } + + fn new_coroutine( + interner: DbInterner<'db>, + def_id: as rustc_type_ir::Interner>::DefId, + args: as rustc_type_ir::Interner>::GenericArgs, + ) -> Self { + Ty::new(interner, TyKind::Coroutine(def_id, args)) + } + + fn new_coroutine_closure( + interner: DbInterner<'db>, + def_id: as rustc_type_ir::Interner>::DefId, + args: as rustc_type_ir::Interner>::GenericArgs, + ) -> Self { + Ty::new(interner, TyKind::CoroutineClosure(def_id, args)) + } + + fn new_closure( + interner: DbInterner<'db>, + def_id: as rustc_type_ir::Interner>::DefId, + args: as rustc_type_ir::Interner>::GenericArgs, + ) -> Self { + Ty::new(interner, TyKind::Closure(def_id, args)) + } + + fn new_coroutine_witness( + interner: DbInterner<'db>, + def_id: as rustc_type_ir::Interner>::DefId, + args: as rustc_type_ir::Interner>::GenericArgs, + ) -> Self { + Ty::new(interner, TyKind::CoroutineWitness(def_id, args)) + } + + fn new_ptr(interner: DbInterner<'db>, ty: Self, mutbl: rustc_ast_ir::Mutability) -> Self { + Ty::new(interner, TyKind::RawPtr(ty, mutbl)) + } + + fn new_ref( + interner: DbInterner<'db>, + region: as rustc_type_ir::Interner>::Region, + ty: Self, + mutbl: rustc_ast_ir::Mutability, + ) -> Self { + Ty::new(interner, TyKind::Ref(region, ty, mutbl)) + } + + fn new_array_with_const_len( + interner: DbInterner<'db>, + ty: Self, + len: as rustc_type_ir::Interner>::Const, + ) -> Self { + Ty::new(interner, TyKind::Array(ty, len)) + } + + fn new_slice(interner: DbInterner<'db>, ty: Self) -> Self { + Ty::new(interner, TyKind::Slice(ty)) + } + + fn new_tup( + interner: DbInterner<'db>, + tys: &[ as rustc_type_ir::Interner>::Ty], + ) -> Self { + Ty::new(interner, TyKind::Tuple(Tys::new_from_iter(interner, tys.iter().cloned()))) + } + + fn new_tup_from_iter(interner: DbInterner<'db>, iter: It) -> T::Output + where + It: Iterator, + T: rustc_type_ir::CollectAndApply, + { + T::collect_and_apply(iter, |ts| Ty::new_tup(interner, ts)) + } + + fn new_fn_def( + interner: DbInterner<'db>, + def_id: as rustc_type_ir::Interner>::DefId, + args: as rustc_type_ir::Interner>::GenericArgs, + ) -> Self { + Ty::new(interner, TyKind::FnDef(def_id, args)) + } + + fn new_fn_ptr( + interner: DbInterner<'db>, + sig: rustc_type_ir::Binder, rustc_type_ir::FnSig>>, + ) -> Self { + let (sig_tys, header) = sig.split(); + Ty::new(interner, TyKind::FnPtr(sig_tys, header)) + } + + fn new_pat( + interner: DbInterner<'db>, + ty: Self, + pat: as rustc_type_ir::Interner>::Pat, + ) -> Self { + Ty::new(interner, TyKind::Pat(ty, pat)) + } + + fn tuple_fields(self) -> as rustc_type_ir::Interner>::Tys { + match self.kind() { + TyKind::Tuple(args) => args, + _ => panic!("tuple_fields called on non-tuple: {self:?}"), + } + } + + fn to_opt_closure_kind(self) -> Option { + match self.kind() { + TyKind::Int(int_ty) => match int_ty { + IntTy::I8 => Some(ClosureKind::Fn), + IntTy::I16 => Some(ClosureKind::FnMut), + IntTy::I32 => Some(ClosureKind::FnOnce), + _ => unreachable!("cannot convert type `{:?}` to a closure kind", self), + }, + + // "Bound" types appear in canonical queries when the + // closure type is not yet known, and `Placeholder` and `Param` + // may be encountered in generic `AsyncFnKindHelper` goals. + TyKind::Bound(..) | TyKind::Placeholder(_) | TyKind::Param(_) | TyKind::Infer(_) => { + None + } + + TyKind::Error(_) => Some(ClosureKind::Fn), + + _ => unreachable!("cannot convert type `{:?}` to a closure kind", self), + } + } + + fn from_closure_kind(interner: DbInterner<'db>, kind: rustc_type_ir::ClosureKind) -> Self { + match kind { + ClosureKind::Fn => Ty::new(interner, TyKind::Int(IntTy::I8)), + ClosureKind::FnMut => Ty::new(interner, TyKind::Int(IntTy::I16)), + ClosureKind::FnOnce => Ty::new(interner, TyKind::Int(IntTy::I32)), + } + } + + fn from_coroutine_closure_kind( + interner: DbInterner<'db>, + kind: rustc_type_ir::ClosureKind, + ) -> Self { + match kind { + ClosureKind::Fn | ClosureKind::FnMut => Ty::new(interner, TyKind::Int(IntTy::I16)), + ClosureKind::FnOnce => Ty::new(interner, TyKind::Int(IntTy::I32)), + } + } + + fn discriminant_ty( + self, + interner: DbInterner<'db>, + ) -> as rustc_type_ir::Interner>::Ty { + match self.kind() { + TyKind::Adt(adt, _) if adt.is_enum() => adt.repr().discr_type().to_ty(interner), + TyKind::Coroutine(_, args) => args.as_coroutine().discr_ty(interner), + + TyKind::Param(_) | TyKind::Alias(..) | TyKind::Infer(InferTy::TyVar(_)) => { + /* + let assoc_items = tcx.associated_item_def_ids( + tcx.require_lang_item(hir::LangItem::DiscriminantKind, None), + ); + TyKind::new_projection_from_args(tcx, assoc_items[0], tcx.mk_args(&[self.into()])) + */ + unimplemented!() + } + + TyKind::Pat(ty, _) => ty.discriminant_ty(interner), + + TyKind::Bool + | TyKind::Char + | TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) + | TyKind::Adt(..) + | TyKind::Foreign(_) + | TyKind::Str + | TyKind::Array(..) + | TyKind::Slice(_) + | TyKind::RawPtr(_, _) + | TyKind::Ref(..) + | TyKind::FnDef(..) + | TyKind::FnPtr(..) + | TyKind::Dynamic(..) + | TyKind::Closure(..) + | TyKind::CoroutineClosure(..) + | TyKind::CoroutineWitness(..) + | TyKind::Never + | TyKind::Tuple(_) + | TyKind::Error(_) + | TyKind::Infer(InferTy::IntVar(_) | InferTy::FloatVar(_)) => { + Ty::new(interner, TyKind::Uint(UintTy::U8)) + } + + TyKind::Bound(..) + | TyKind::Placeholder(_) + | TyKind::Infer( + InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_), + ) => { + panic!( + "`dself.iter().map(|v| v.try_fold_with(folder)).collect::>()?iscriminant_ty` applied to unexpected type: {self:?}" + ) + } + TyKind::UnsafeBinder(..) => unimplemented!(), + } + } + + fn new_unsafe_binder( + interner: DbInterner<'db>, + ty: rustc_type_ir::Binder< + DbInterner<'db>, + as rustc_type_ir::Interner>::Ty, + >, + ) -> Self { + Ty::new(interner, TyKind::UnsafeBinder(ty.into())) + } + + fn has_unsafe_fields(self) -> bool { + false + } +} + +interned_vec_db!(Tys, Ty); + +impl<'db> rustc_type_ir::inherent::Tys> for Tys<'db> { + fn inputs(self) -> as rustc_type_ir::Interner>::FnInputTys { + Tys::new_from_iter( + DbInterner::conjure(), + self.as_slice().split_last().unwrap().1.iter().cloned(), + ) + } + + fn output(self) -> as rustc_type_ir::Interner>::Ty { + *self.as_slice().split_last().unwrap().0 + } +} + +pub type PlaceholderTy = Placeholder; + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct ParamTy { + pub index: u32, +} + +impl ParamTy { + pub fn to_ty<'db>(self, interner: DbInterner<'db>) -> Ty<'db> { + Ty::new_param(interner, self.index, sym::MISSING_NAME.clone()) + } +} + +impl std::fmt::Debug for ParamTy { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "#{}", self.index) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct BoundTy { + pub var: BoundVar, + pub kind: BoundTyKind, +} + +impl std::fmt::Debug for BoundTy { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self.kind { + BoundTyKind::Anon => write!(f, "{:?}", self.var), + BoundTyKind::Param(def_id) => write!(f, "{def_id:?}"), + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub enum BoundTyKind { + Anon, + Param(SolverDefId), +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub struct ErrorGuaranteed; + +impl<'db> TypeVisitable> for ErrorGuaranteed { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> V::Result { + visitor.visit_error(*self) + } +} + +impl<'db> TypeFoldable> for ErrorGuaranteed { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + Ok(self) + } + fn fold_with>>(self, folder: &mut F) -> Self { + self + } +} + +impl ParamLike for ParamTy { + fn index(self) -> u32 { + self.index + } +} + +impl<'db> BoundVarLike> for BoundTy { + fn var(self) -> BoundVar { + self.var + } + + fn assert_eq(self, var: BoundVarKind) { + assert_eq!(self.kind, var.expect_ty()) + } +} + +impl<'db> PlaceholderLike> for PlaceholderTy { + type Bound = BoundTy; + + fn universe(self) -> rustc_type_ir::UniverseIndex { + self.universe + } + + fn var(self) -> BoundVar { + self.bound.var + } + + fn with_updated_universe(self, ui: rustc_type_ir::UniverseIndex) -> Self { + Placeholder { universe: ui, bound: self.bound } + } + + fn new(ui: rustc_type_ir::UniverseIndex, bound: BoundTy) -> Self { + Placeholder { universe: ui, bound } + } + + fn new_anon(ui: rustc_type_ir::UniverseIndex, var: rustc_type_ir::BoundVar) -> Self { + Placeholder { universe: ui, bound: BoundTy { var, kind: BoundTyKind::Anon } } + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs new file mode 100644 index 000000000000..cedc203f7f5a --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs @@ -0,0 +1,1064 @@ +//! Various utilities for the next-trait-solver. + +use std::iter; +use std::ops::{self, ControlFlow}; + +use base_db::Crate; +use hir_def::lang_item::LangItem; +use hir_def::{BlockId, HasModule, ItemContainerId, Lookup}; +use intern::sym; +use la_arena::Idx; +use rustc_abi::{Float, HasDataLayout, Integer, IntegerType, Primitive, ReprOptions}; +use rustc_type_ir::data_structures::IndexMap; +use rustc_type_ir::inherent::{ + AdtDef, Const as _, GenericArg as _, GenericArgs as _, ParamEnv as _, Region as _, SliceLike, + Ty as _, +}; +use rustc_type_ir::lang_items::TraitSolverLangItem; +use rustc_type_ir::solve::SizedTraitKind; +use rustc_type_ir::{ + BoundVar, Canonical, DebruijnIndex, GenericArgKind, INNERMOST, Interner, PredicatePolarity, + TypeFlags, TypeVisitable, TypeVisitableExt, +}; +use rustc_type_ir::{ + ConstKind, CoroutineArgs, FloatTy, IntTy, RegionKind, TypeFolder, TypeSuperFoldable, + TypeSuperVisitable, TypeVisitor, UintTy, UniverseIndex, inherent::IntoKind, +}; +use rustc_type_ir::{InferCtxtLike, TypeFoldable}; + +use crate::lower_nextsolver::{LifetimeElisionKind, TyLoweringContext}; +use crate::next_solver::infer::InferCtxt; +use crate::next_solver::{ + CanonicalVarKind, FxIndexMap, ParamEnv, Placeholder, PlaceholderConst, PlaceholderRegion, + TypingMode, +}; +use crate::{ + db::HirDatabase, + from_foreign_def_id, + method_resolution::{TraitImpls, TyFingerprint}, +}; + +use super::fold::{BoundVarReplacer, FnMutDelegate}; +use super::generics::generics; +use super::{ + AliasTerm, AliasTy, Binder, BoundRegion, BoundTy, BoundTyKind, BoundVarKind, BoundVarKinds, + CanonicalVars, Clause, ClauseKind, Clauses, Const, DbInterner, EarlyBinder, GenericArg, + GenericArgs, Predicate, PredicateKind, ProjectionPredicate, Region, SolverContext, SolverDefId, + Term, TraitPredicate, TraitRef, Ty, TyKind, +}; + +#[derive(Clone, Debug)] +pub struct Discr<'db> { + /// Bit representation of the discriminant (e.g., `-128i8` is `0xFF_u128`). + pub val: u128, + pub ty: Ty<'db>, +} + +impl<'db> Discr<'db> { + /// Adds `1` to the value and wraps around if the maximum for the type is reached. + pub fn wrap_incr(self, interner: DbInterner<'db>) -> Self { + self.checked_add(interner, 1).0 + } + pub fn checked_add(self, interner: DbInterner<'db>, n: u128) -> (Self, bool) { + let (size, signed) = self.ty.int_size_and_signed(interner); + let (val, oflo) = if signed { + let min = size.signed_int_min(); + let max = size.signed_int_max(); + let val = size.sign_extend(self.val); + assert!(n < (i128::MAX as u128)); + let n = n as i128; + let oflo = val > max - n; + let val = if oflo { min + (n - (max - val) - 1) } else { val + n }; + // zero the upper bits + let val = val as u128; + let val = size.truncate(val); + (val, oflo) + } else { + let max = size.unsigned_int_max(); + let val = self.val; + let oflo = val > max - n; + let val = if oflo { n - (max - val) - 1 } else { val + n }; + (val, oflo) + }; + (Self { val, ty: self.ty }, oflo) + } +} + +pub trait IntegerTypeExt { + fn to_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db>; + fn initial_discriminant<'db>(&self, interner: DbInterner<'db>) -> Discr<'db>; + fn disr_incr<'db>( + &self, + interner: DbInterner<'db>, + val: Option>, + ) -> Option>; +} + +impl IntegerTypeExt for IntegerType { + fn to_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db> { + match self { + IntegerType::Pointer(true) => Ty::new(interner, TyKind::Int(IntTy::Isize)), + IntegerType::Pointer(false) => Ty::new(interner, TyKind::Uint(UintTy::Usize)), + IntegerType::Fixed(i, s) => i.to_ty(interner, *s), + } + } + + fn initial_discriminant<'db>(&self, interner: DbInterner<'db>) -> Discr<'db> { + Discr { val: 0, ty: self.to_ty(interner) } + } + + fn disr_incr<'db>( + &self, + interner: DbInterner<'db>, + val: Option>, + ) -> Option> { + if let Some(val) = val { + assert_eq!(self.to_ty(interner), val.ty); + let (new, oflo) = val.checked_add(interner, 1); + if oflo { None } else { Some(new) } + } else { + Some(self.initial_discriminant(interner)) + } + } +} + +pub trait IntegerExt { + fn to_ty<'db>(&self, interner: DbInterner<'db>, signed: bool) -> Ty<'db>; + fn from_int_ty(cx: &C, ity: IntTy) -> Integer; + fn from_uint_ty(cx: &C, ity: UintTy) -> Integer; + fn repr_discr<'db>( + interner: DbInterner<'db>, + ty: Ty<'db>, + repr: &ReprOptions, + min: i128, + max: i128, + ) -> (Integer, bool); +} + +impl IntegerExt for Integer { + #[inline] + fn to_ty<'db>(&self, interner: DbInterner<'db>, signed: bool) -> Ty<'db> { + use Integer::*; + match (*self, signed) { + (I8, false) => Ty::new(interner, TyKind::Uint(UintTy::U8)), + (I16, false) => Ty::new(interner, TyKind::Uint(UintTy::U16)), + (I32, false) => Ty::new(interner, TyKind::Uint(UintTy::U32)), + (I64, false) => Ty::new(interner, TyKind::Uint(UintTy::U64)), + (I128, false) => Ty::new(interner, TyKind::Uint(UintTy::U128)), + (I8, true) => Ty::new(interner, TyKind::Int(IntTy::I8)), + (I16, true) => Ty::new(interner, TyKind::Int(IntTy::I16)), + (I32, true) => Ty::new(interner, TyKind::Int(IntTy::I32)), + (I64, true) => Ty::new(interner, TyKind::Int(IntTy::I64)), + (I128, true) => Ty::new(interner, TyKind::Int(IntTy::I128)), + } + } + + fn from_int_ty(cx: &C, ity: IntTy) -> Integer { + use Integer::*; + match ity { + IntTy::I8 => I8, + IntTy::I16 => I16, + IntTy::I32 => I32, + IntTy::I64 => I64, + IntTy::I128 => I128, + IntTy::Isize => cx.data_layout().ptr_sized_integer(), + } + } + fn from_uint_ty(cx: &C, ity: UintTy) -> Integer { + use Integer::*; + match ity { + UintTy::U8 => I8, + UintTy::U16 => I16, + UintTy::U32 => I32, + UintTy::U64 => I64, + UintTy::U128 => I128, + UintTy::Usize => cx.data_layout().ptr_sized_integer(), + } + } + + /// Finds the appropriate Integer type and signedness for the given + /// signed discriminant range and `#[repr]` attribute. + /// N.B.: `u128` values above `i128::MAX` will be treated as signed, but + /// that shouldn't affect anything, other than maybe debuginfo. + fn repr_discr<'db>( + interner: DbInterner<'db>, + ty: Ty<'db>, + repr: &ReprOptions, + min: i128, + max: i128, + ) -> (Integer, bool) { + // Theoretically, negative values could be larger in unsigned representation + // than the unsigned representation of the signed minimum. However, if there + // are any negative values, the only valid unsigned representation is u128 + // which can fit all i128 values, so the result remains unaffected. + let unsigned_fit = Integer::fit_unsigned(std::cmp::max(min as u128, max as u128)); + let signed_fit = std::cmp::max(Integer::fit_signed(min), Integer::fit_signed(max)); + + if let Some(ity) = repr.int { + let discr = Integer::from_attr(&interner, ity); + let fit = if ity.is_signed() { signed_fit } else { unsigned_fit }; + if discr < fit { + panic!( + "Integer::repr_discr: `#[repr]` hint too small for \ + discriminant range of enum `{ty:?}`" + ) + } + return (discr, ity.is_signed()); + } + + let at_least = if repr.c() { + // This is usually I32, however it can be different on some platforms, + // notably hexagon and arm-none/thumb-none + interner.data_layout().c_enum_min_size + } else { + // repr(Rust) enums try to be as small as possible + Integer::I8 + }; + + // If there are no negative values, we can use the unsigned fit. + if min >= 0 { + (std::cmp::max(unsigned_fit, at_least), false) + } else { + (std::cmp::max(signed_fit, at_least), true) + } + } +} + +pub trait FloatExt { + fn to_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db>; + fn from_float_ty(fty: FloatTy) -> Self; +} + +impl FloatExt for Float { + #[inline] + fn to_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db> { + use Float::*; + match *self { + F16 => Ty::new(interner, TyKind::Float(FloatTy::F16)), + F32 => Ty::new(interner, TyKind::Float(FloatTy::F32)), + F64 => Ty::new(interner, TyKind::Float(FloatTy::F64)), + F128 => Ty::new(interner, TyKind::Float(FloatTy::F128)), + } + } + + fn from_float_ty(fty: FloatTy) -> Self { + use Float::*; + match fty { + FloatTy::F16 => F16, + FloatTy::F32 => F32, + FloatTy::F64 => F64, + FloatTy::F128 => F128, + } + } +} + +pub trait PrimitiveExt { + fn to_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db>; + fn to_int_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db>; +} + +impl PrimitiveExt for Primitive { + #[inline] + fn to_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db> { + match *self { + Primitive::Int(i, signed) => i.to_ty(interner, signed), + Primitive::Float(f) => f.to_ty(interner), + Primitive::Pointer(_) => Ty::new( + interner, + TyKind::RawPtr( + Ty::new(interner, TyKind::Tuple(Default::default())), + rustc_ast_ir::Mutability::Mut, + ), + ), + } + } + + /// Return an *integer* type matching this primitive. + /// Useful in particular when dealing with enum discriminants. + #[inline] + fn to_int_ty<'db>(&self, interner: DbInterner<'db>) -> Ty<'db> { + match *self { + Primitive::Int(i, signed) => i.to_ty(interner, signed), + Primitive::Pointer(_) => { + let signed = false; + interner.data_layout().ptr_sized_integer().to_ty(interner, signed) + } + Primitive::Float(_) => panic!("floats do not have an int type"), + } + } +} + +impl<'db> HasDataLayout for DbInterner<'db> { + fn data_layout(&self) -> &rustc_abi::TargetDataLayout { + unimplemented!() + } +} + +pub trait CoroutineArgsExt<'db> { + fn discr_ty(&self, interner: DbInterner<'db>) -> Ty<'db>; +} + +impl<'db> CoroutineArgsExt<'db> for CoroutineArgs> { + /// The type of the state discriminant used in the coroutine type. + #[inline] + fn discr_ty(&self, interner: DbInterner<'db>) -> Ty<'db> { + Ty::new(interner, TyKind::Uint(UintTy::U32)) + } +} + +/// Finds the max universe present +pub struct MaxUniverse { + max_universe: UniverseIndex, +} + +impl Default for MaxUniverse { + fn default() -> Self { + Self::new() + } +} + +impl MaxUniverse { + pub fn new() -> Self { + MaxUniverse { max_universe: UniverseIndex::ROOT } + } + + pub fn max_universe(self) -> UniverseIndex { + self.max_universe + } +} + +impl<'db> TypeVisitor> for MaxUniverse { + type Result = (); + + fn visit_ty(&mut self, t: Ty<'db>) { + if let TyKind::Placeholder(placeholder) = t.kind() { + self.max_universe = UniverseIndex::from_u32( + self.max_universe.as_u32().max(placeholder.universe.as_u32()), + ); + } + + t.super_visit_with(self) + } + + fn visit_const(&mut self, c: Const<'db>) { + if let ConstKind::Placeholder(placeholder) = c.kind() { + self.max_universe = UniverseIndex::from_u32( + self.max_universe.as_u32().max(placeholder.universe.as_u32()), + ); + } + + c.super_visit_with(self) + } + + fn visit_region(&mut self, r: Region<'db>) { + if let RegionKind::RePlaceholder(placeholder) = r.kind() { + self.max_universe = UniverseIndex::from_u32( + self.max_universe.as_u32().max(placeholder.universe.as_u32()), + ); + } + } +} + +pub struct BottomUpFolder<'db, F, G, H> +where + F: FnMut(Ty<'db>) -> Ty<'db>, + G: FnMut(Region<'db>) -> Region<'db>, + H: FnMut(Const<'db>) -> Const<'db>, +{ + pub interner: DbInterner<'db>, + pub ty_op: F, + pub lt_op: G, + pub ct_op: H, +} + +impl<'db, F, G, H> TypeFolder> for BottomUpFolder<'db, F, G, H> +where + F: FnMut(Ty<'db>) -> Ty<'db>, + G: FnMut(Region<'db>) -> Region<'db>, + H: FnMut(Const<'db>) -> Const<'db>, +{ + fn cx(&self) -> DbInterner<'db> { + self.interner + } + + fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> { + let t = ty.super_fold_with(self); + (self.ty_op)(t) + } + + fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { + // This one is a little different, because `super_fold_with` is not + // implemented on non-recursive `Region`. + (self.lt_op)(r) + } + + fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> { + let ct = ct.super_fold_with(self); + (self.ct_op)(ct) + } +} + +pub(crate) fn for_trait_impls( + db: &dyn HirDatabase, + krate: Crate, + block: Option, + trait_id: hir_def::TraitId, + self_ty_fp: Option, + mut f: impl FnMut(&TraitImpls) -> ControlFlow<()>, +) -> ControlFlow<()> { + // Note: Since we're using `impls_for_trait` and `impl_provided_for`, + // only impls where the trait can be resolved should ever reach Chalk. + // `impl_datum` relies on that and will panic if the trait can't be resolved. + let in_deps = db.trait_impls_in_deps(krate); + let in_self = db.trait_impls_in_crate(krate); + let trait_module = trait_id.module(db); + let type_module = match self_ty_fp { + Some(TyFingerprint::Adt(adt_id)) => Some(adt_id.module(db)), + Some(TyFingerprint::ForeignType(type_id)) => Some(from_foreign_def_id(type_id).module(db)), + Some(TyFingerprint::Dyn(trait_id)) => Some(trait_id.module(db)), + _ => None, + }; + + let mut def_blocks = + [trait_module.containing_block(), type_module.and_then(|it| it.containing_block())]; + + let block_impls = iter::successors(block, |&block_id| { + cov_mark::hit!(block_local_impls); + block_id.loc(db).module.containing_block() + }) + .inspect(|&block_id| { + // make sure we don't search the same block twice + def_blocks.iter_mut().for_each(|block| { + if *block == Some(block_id) { + *block = None; + } + }); + }) + .filter_map(|block_id| db.trait_impls_in_block(block_id)); + f(&in_self)?; + for it in in_deps.iter().map(ops::Deref::deref) { + f(it)?; + } + for it in block_impls { + f(&it)?; + } + for it in def_blocks.into_iter().flatten().filter_map(|it| db.trait_impls_in_block(it)) { + f(&it)?; + } + ControlFlow::Continue(()) +} + +// FIXME(next-trait-solver): uplift +pub fn sizedness_constraint_for_ty<'db>( + interner: DbInterner<'db>, + sizedness: SizedTraitKind, + ty: Ty<'db>, +) -> Option> { + use rustc_type_ir::TyKind::*; + + match ty.kind() { + // these are always sized + Bool | Char | Int(..) | Uint(..) | Float(..) | RawPtr(..) | Ref(..) | FnDef(..) + | FnPtr(..) | Array(..) | Closure(..) | CoroutineClosure(..) | Coroutine(..) + | CoroutineWitness(..) | Never => None, + + // these are never sized + Str | Slice(..) | Dynamic(_, _, rustc_type_ir::DynKind::Dyn) => match sizedness { + // Never `Sized` + SizedTraitKind::Sized => Some(ty), + // Always `MetaSized` + SizedTraitKind::MetaSized => None, + }, + + // Maybe `Sized` or `MetaSized` + Param(..) | Alias(..) | Error(_) => Some(ty), + + // We cannot instantiate the binder, so just return the *original* type back, + // but only if the inner type has a sized constraint. Thus we skip the binder, + // but don't actually use the result from `sized_constraint_for_ty`. + UnsafeBinder(inner_ty) => { + sizedness_constraint_for_ty(interner, sizedness, inner_ty.skip_binder()).map(|_| ty) + } + + // Never `MetaSized` or `Sized` + Foreign(..) => Some(ty), + + // Recursive cases + Pat(ty, _) => sizedness_constraint_for_ty(interner, sizedness, ty), + + Tuple(tys) => tys + .into_iter() + .last() + .and_then(|ty| sizedness_constraint_for_ty(interner, sizedness, ty)), + + Adt(adt, args) => { + let tail_ty = + EarlyBinder::bind(adt.all_field_tys(interner).skip_binder().into_iter().last()?) + .instantiate(interner, args); + sizedness_constraint_for_ty(interner, sizedness, tail_ty) + } + + Placeholder(..) | Bound(..) | Infer(..) => { + panic!("unexpected type `{ty:?}` in sizedness_constraint_for_ty") + } + } +} + +pub fn apply_args_to_binder<'db, T: TypeFoldable>>( + b: Binder<'db, T>, + args: GenericArgs<'db>, + interner: DbInterner<'db>, +) -> T { + let types = &mut |ty: BoundTy| args.as_slice()[ty.var.index()].expect_ty(); + let regions = &mut |region: BoundRegion| args.as_slice()[region.var.index()].expect_region(); + let consts = &mut |const_: BoundVar| args.as_slice()[const_.index()].expect_const(); + let mut instantiate = BoundVarReplacer::new(interner, FnMutDelegate { types, regions, consts }); + b.skip_binder().fold_with(&mut instantiate) +} + +pub(crate) fn mini_canonicalize<'db, T: TypeFoldable>>( + mut context: SolverContext<'db>, + val: T, +) -> Canonical, T> { + let mut canon = MiniCanonicalizer { + context: &mut context, + db: DebruijnIndex::ZERO, + vars: IndexMap::default(), + }; + let canon_val = val.fold_with(&mut canon); + let vars = canon.vars; + Canonical { + value: canon_val, + max_universe: UniverseIndex::from_u32(1), + variables: CanonicalVars::new_from_iter( + context.cx(), + vars.iter().map(|(k, v)| match (*k).kind() { + GenericArgKind::Type(ty) => match ty.kind() { + TyKind::Int(..) | TyKind::Uint(..) => { + rustc_type_ir::CanonicalVarKind::Ty(rustc_type_ir::CanonicalTyVarKind::Int) + } + TyKind::Float(..) => rustc_type_ir::CanonicalVarKind::Ty( + rustc_type_ir::CanonicalTyVarKind::Float, + ), + _ => rustc_type_ir::CanonicalVarKind::Ty( + rustc_type_ir::CanonicalTyVarKind::General(UniverseIndex::ZERO), + ), + }, + GenericArgKind::Lifetime(_) => { + rustc_type_ir::CanonicalVarKind::Region(UniverseIndex::ZERO) + } + GenericArgKind::Const(_) => { + rustc_type_ir::CanonicalVarKind::Const(UniverseIndex::ZERO) + } + }), + ), + } +} + +struct MiniCanonicalizer<'a, 'db> { + context: &'a mut SolverContext<'db>, + db: DebruijnIndex, + vars: IndexMap, usize>, +} + +impl<'db> TypeFolder> for MiniCanonicalizer<'_, 'db> { + fn cx(&self) -> DbInterner<'db> { + self.context.cx() + } + + fn fold_binder>>( + &mut self, + t: rustc_type_ir::Binder, T>, + ) -> rustc_type_ir::Binder, T> { + self.db.shift_in(1); + let res = t.map_bound(|t| t.fold_with(self)); + self.db.shift_out(1); + res + } + + fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> { + match t.kind() { + rustc_type_ir::TyKind::Bound(db, _) => { + if db >= self.db { + panic!("Unexpected bound var"); + } + t + } + rustc_type_ir::TyKind::Infer(infer) => { + let t = match infer { + rustc_type_ir::InferTy::TyVar(vid) => { + self.context.opportunistic_resolve_ty_var(vid) + } + rustc_type_ir::InferTy::IntVar(vid) => { + self.context.opportunistic_resolve_int_var(vid) + } + rustc_type_ir::InferTy::FloatVar(vid) => { + self.context.opportunistic_resolve_float_var(vid) + } + _ => t, + }; + let len = self.vars.len(); + let var = *self.vars.entry(t.into()).or_insert(len); + Ty::new( + self.cx(), + TyKind::Bound( + self.db, + BoundTy { kind: super::BoundTyKind::Anon, var: BoundVar::from_usize(var) }, + ), + ) + } + _ => t.super_fold_with(self), + } + } + + fn fold_region( + &mut self, + r: as rustc_type_ir::Interner>::Region, + ) -> as rustc_type_ir::Interner>::Region { + match r.kind() { + RegionKind::ReBound(db, _) => { + if db >= self.db { + panic!("Unexpected bound var"); + } + r + } + RegionKind::ReVar(vid) => { + let len = self.vars.len(); + let var = *self.vars.entry(r.into()).or_insert(len); + Region::new( + self.cx(), + RegionKind::ReBound( + self.db, + BoundRegion { + kind: super::BoundRegionKind::Anon, + var: BoundVar::from_usize(var), + }, + ), + ) + } + _ => r, + } + } + + fn fold_const( + &mut self, + c: as rustc_type_ir::Interner>::Const, + ) -> as rustc_type_ir::Interner>::Const { + match c.kind() { + ConstKind::Bound(db, _) => { + if db >= self.db { + panic!("Unexpected bound var"); + } + c + } + ConstKind::Infer(infer) => { + let len = self.vars.len(); + let var = *self.vars.entry(c.into()).or_insert(len); + Const::new(self.cx(), ConstKind::Bound(self.db, BoundVar::from_usize(var))) + } + _ => c.super_fold_with(self), + } + } +} + +pub fn explicit_item_bounds<'db>( + interner: DbInterner<'db>, + def_id: SolverDefId, +) -> EarlyBinder<'db, Clauses<'db>> { + let db = interner.db(); + match def_id { + SolverDefId::TypeAliasId(type_alias) => { + let trait_ = match type_alias.lookup(db).container { + ItemContainerId::TraitId(t) => t, + _ => panic!("associated type not in trait"), + }; + + // Lower bounds -- we could/should maybe move this to a separate query in `lower` + let type_alias_data = db.type_alias_signature(type_alias); + let generic_params = generics(db, type_alias.into()); + let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &type_alias_data.store, + type_alias.into(), + LifetimeElisionKind::AnonymousReportError, + ); + + let trait_args = GenericArgs::identity_for_item(interner, trait_.into()); + let item_args = GenericArgs::identity_for_item(interner, def_id); + let interner_ty = Ty::new_projection_from_args(interner, def_id, item_args); + + let mut bounds = Vec::new(); + for bound in &type_alias_data.bounds { + ctx.lower_type_bound(bound, interner_ty, false).for_each(|pred| { + bounds.push(pred); + }); + } + + if !ctx.unsized_types.contains(&interner_ty) { + let sized_trait = LangItem::Sized + .resolve_trait(ctx.db, interner.krate.expect("Must have interner.krate")); + let sized_bound = sized_trait.map(|trait_id| { + let trait_ref = TraitRef::new_from_args( + interner, + trait_id.into(), + GenericArgs::new_from_iter(interner, [interner_ty.into()]), + ); + Clause(Predicate::new( + interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity: rustc_type_ir::PredicatePolarity::Positive, + }), + )), + )) + }); + bounds.extend(sized_bound); + bounds.shrink_to_fit(); + } + + rustc_type_ir::EarlyBinder::bind(Clauses::new_from_iter(interner, bounds)) + } + SolverDefId::InternedOpaqueTyId(id) => { + let full_id = db.lookup_intern_impl_trait_id(id); + match full_id { + crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => { + let datas = db + .return_type_impl_traits_ns(func) + .expect("impl trait id without impl traits"); + let datas = (*datas).as_ref().skip_binder(); + let data = &datas.impl_traits[Idx::from_raw(idx.into_raw())]; + EarlyBinder::bind(Clauses::new_from_iter(interner, data.predicates.clone())) + } + crate::ImplTraitId::TypeAliasImplTrait(alias, idx) => { + let datas = db + .type_alias_impl_traits_ns(alias) + .expect("impl trait id without impl traits"); + let datas = (*datas).as_ref().skip_binder(); + let data = &datas.impl_traits[Idx::from_raw(idx.into_raw())]; + EarlyBinder::bind(Clauses::new_from_iter(interner, data.predicates.clone())) + } + crate::ImplTraitId::AsyncBlockTypeImplTrait(..) => { + if let Some((future_trait, future_output)) = LangItem::Future + .resolve_trait(db, interner.krate.expect("Must have interner.krate")) + .and_then(|trait_| { + let alias = trait_.trait_items(db).associated_type_by_name( + &hir_expand::name::Name::new_symbol_root(sym::Output.clone()), + )?; + Some((trait_, alias)) + }) + { + let args = GenericArgs::identity_for_item(interner, def_id); + let out = args.as_slice()[0]; + let mut predicates = vec![]; + + let item_ty = Ty::new_alias( + interner, + rustc_type_ir::AliasTyKind::Opaque, + AliasTy::new_from_args(interner, def_id, args), + ); + + let kind = PredicateKind::Clause(ClauseKind::Trait(TraitPredicate { + polarity: rustc_type_ir::PredicatePolarity::Positive, + trait_ref: TraitRef::new_from_args( + interner, + future_trait.into(), + GenericArgs::new_from_iter(interner, [item_ty.into()]), + ), + })); + predicates.push(Clause(Predicate::new( + interner, + Binder::bind_with_vars( + kind, + BoundVarKinds::new_from_iter( + interner, + [BoundVarKind::Ty(BoundTyKind::Anon)], + ), + ), + ))); + let sized_trait = LangItem::Sized + .resolve_trait(db, interner.krate.expect("Must have interner.krate")); + if let Some(sized_trait_) = sized_trait { + let kind = PredicateKind::Clause(ClauseKind::Trait(TraitPredicate { + polarity: rustc_type_ir::PredicatePolarity::Positive, + trait_ref: TraitRef::new_from_args( + interner, + sized_trait_.into(), + GenericArgs::new_from_iter(interner, [item_ty.into()]), + ), + })); + predicates.push(Clause(Predicate::new( + interner, + Binder::bind_with_vars( + kind, + BoundVarKinds::new_from_iter( + interner, + [BoundVarKind::Ty(BoundTyKind::Anon)], + ), + ), + ))); + } + let kind = + PredicateKind::Clause(ClauseKind::Projection(ProjectionPredicate { + projection_term: AliasTerm::new_from_args( + interner, + future_output.into(), + GenericArgs::new_from_iter(interner, [item_ty.into()]), + ), + term: match out.kind() { + GenericArgKind::Lifetime(lt) => panic!(), + GenericArgKind::Type(ty) => Term::Ty(ty), + GenericArgKind::Const(const_) => Term::Const(const_), + }, + })); + predicates.push(Clause(Predicate::new( + interner, + Binder::bind_with_vars( + kind, + BoundVarKinds::new_from_iter( + interner, + [BoundVarKind::Ty(BoundTyKind::Anon)], + ), + ), + ))); + EarlyBinder::bind(Clauses::new_from_iter(interner, predicates)) + } else { + // If failed to find Symbol’s value as variable is void: Future::Output, return empty bounds as fallback. + EarlyBinder::bind(Clauses::new_from_iter(interner, [])) + } + } + } + } + _ => panic!("Unexpected GeneridDefId"), + } +} + +pub struct ContainsTypeErrors; + +impl<'db> TypeVisitor> for ContainsTypeErrors { + type Result = ControlFlow<()>; + + fn visit_ty(&mut self, t: Ty<'db>) -> Self::Result { + match t.kind() { + rustc_type_ir::TyKind::Error(_) => ControlFlow::Break(()), + _ => t.super_visit_with(self), + } + } +} + +/// The inverse of [`BoundVarReplacer`]: replaces placeholders with the bound vars from which they came. +pub struct PlaceholderReplacer<'a, 'db> { + infcx: &'a InferCtxt<'db>, + mapped_regions: FxIndexMap, + mapped_types: FxIndexMap, BoundTy>, + mapped_consts: FxIndexMap, + universe_indices: &'a [Option], + current_index: DebruijnIndex, +} + +impl<'a, 'db> PlaceholderReplacer<'a, 'db> { + pub fn replace_placeholders>>( + infcx: &'a InferCtxt<'db>, + mapped_regions: FxIndexMap, + mapped_types: FxIndexMap, BoundTy>, + mapped_consts: FxIndexMap, + universe_indices: &'a [Option], + value: T, + ) -> T { + let mut replacer = PlaceholderReplacer { + infcx, + mapped_regions, + mapped_types, + mapped_consts, + universe_indices, + current_index: INNERMOST, + }; + value.fold_with(&mut replacer) + } +} + +impl<'db> TypeFolder> for PlaceholderReplacer<'_, 'db> { + fn cx(&self) -> DbInterner<'db> { + self.infcx.interner + } + + fn fold_binder>>( + &mut self, + t: Binder<'db, T>, + ) -> Binder<'db, T> { + if !t.has_placeholders() && !t.has_infer() { + return t; + } + self.current_index.shift_in(1); + let t = t.super_fold_with(self); + self.current_index.shift_out(1); + t + } + + fn fold_region(&mut self, r0: Region<'db>) -> Region<'db> { + let r1 = match r0.kind() { + RegionKind::ReVar(vid) => self + .infcx + .inner + .borrow_mut() + .unwrap_region_constraints() + .opportunistic_resolve_var(self.infcx.interner, vid), + _ => r0, + }; + + let r2 = match r1.kind() { + RegionKind::RePlaceholder(p) => { + let replace_var = self.mapped_regions.get(&p); + match replace_var { + Some(replace_var) => { + let index = self + .universe_indices + .iter() + .position(|u| matches!(u, Some(pu) if *pu == p.universe)) + .unwrap_or_else(|| panic!("Unexpected placeholder universe.")); + let db = DebruijnIndex::from_usize( + self.universe_indices.len() - index + self.current_index.as_usize() - 1, + ); + Region::new_bound(self.cx(), db, *replace_var) + } + None => r1, + } + } + _ => r1, + }; + + tracing::debug!(?r0, ?r1, ?r2, "fold_region"); + + r2 + } + + fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> { + let ty = self.infcx.shallow_resolve(ty); + match ty.kind() { + TyKind::Placeholder(p) => { + let replace_var = self.mapped_types.get(&p); + match replace_var { + Some(replace_var) => { + let index = self + .universe_indices + .iter() + .position(|u| matches!(u, Some(pu) if *pu == p.universe)) + .unwrap_or_else(|| panic!("Unexpected placeholder universe.")); + let db = DebruijnIndex::from_usize( + self.universe_indices.len() - index + self.current_index.as_usize() - 1, + ); + Ty::new_bound(self.infcx.interner, db, *replace_var) + } + None => { + if ty.has_infer() { + ty.super_fold_with(self) + } else { + ty + } + } + } + } + + _ if ty.has_placeholders() || ty.has_infer() => ty.super_fold_with(self), + _ => ty, + } + } + + fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> { + let ct = self.infcx.shallow_resolve_const(ct); + if let ConstKind::Placeholder(p) = ct.kind() { + let replace_var = self.mapped_consts.get(&p); + match replace_var { + Some(replace_var) => { + let index = self + .universe_indices + .iter() + .position(|u| matches!(u, Some(pu) if *pu == p.universe)) + .unwrap_or_else(|| panic!("Unexpected placeholder universe.")); + let db = DebruijnIndex::from_usize( + self.universe_indices.len() - index + self.current_index.as_usize() - 1, + ); + Const::new_bound(self.infcx.interner, db, *replace_var) + } + None => { + if ct.has_infer() { + ct.super_fold_with(self) + } else { + ct + } + } + } + } else { + ct.super_fold_with(self) + } + } +} + +pub(crate) fn needs_normalization<'db, T: TypeVisitable>>( + infcx: &InferCtxt<'db>, + value: &T, +) -> bool { + let mut flags = TypeFlags::HAS_ALIAS; + + // Opaques are treated as rigid outside of `TypingMode::PostAnalysis`, + // so we can ignore those. + match infcx.typing_mode() { + // FIXME(#132279): We likely want to reveal opaques during post borrowck analysis + TypingMode::Coherence + | TypingMode::Analysis { .. } + | TypingMode::Borrowck { .. } + | TypingMode::PostBorrowckAnalysis { .. } => flags.remove(TypeFlags::HAS_TY_OPAQUE), + TypingMode::PostAnalysis => {} + } + + value.has_type_flags(flags) +} + +pub fn sizedness_fast_path<'db>( + tcx: DbInterner<'db>, + predicate: Predicate<'db>, + param_env: ParamEnv<'db>, +) -> bool { + // Proving `Sized`/`MetaSized`, very often on "obviously sized" types like + // `&T`, accounts for about 60% percentage of the predicates we have to prove. No need to + // canonicalize and all that for such cases. + if let PredicateKind::Clause(ClauseKind::Trait(trait_pred)) = predicate.kind().skip_binder() + && trait_pred.polarity == PredicatePolarity::Positive + { + let sizedness = match tcx.as_lang_item(trait_pred.def_id()) { + Some(TraitSolverLangItem::Sized) => SizedTraitKind::Sized, + Some(TraitSolverLangItem::MetaSized) => SizedTraitKind::MetaSized, + _ => return false, + }; + + // FIXME(sized_hierarchy): this temporarily reverts the `sized_hierarchy` feature + // while a proper fix for `tests/ui/sized-hierarchy/incomplete-inference-issue-143992.rs` + // is pending a proper fix + if matches!(sizedness, SizedTraitKind::MetaSized) { + return true; + } + + if trait_pred.self_ty().has_trivial_sizedness(tcx, sizedness) { + tracing::debug!("fast path -- trivial sizedness"); + return true; + } + + if matches!(trait_pred.self_ty().kind(), TyKind::Param(_) | TyKind::Placeholder(_)) { + for clause in param_env.caller_bounds().iter() { + if let ClauseKind::Trait(clause_pred) = clause.kind().skip_binder() + && clause_pred.polarity == PredicatePolarity::Positive + && clause_pred.self_ty() == trait_pred.self_ty() + && (clause_pred.def_id() == trait_pred.def_id() + || (sizedness == SizedTraitKind::MetaSized + && tcx.is_lang_item(clause_pred.def_id(), TraitSolverLangItem::Sized))) + { + return true; + } + } + } + } + + false +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs index 9605a0b4124d..fc31973022bd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -12,9 +12,6 @@ mod simple; mod traits; mod type_alias_impl_traits; -use std::env; -use std::sync::LazyLock; - use base_db::{Crate, SourceDatabase}; use expect_test::Expect; use hir_def::{ @@ -35,8 +32,6 @@ use syntax::{ ast::{self, AstNode, HasName}, }; use test_fixture::WithFixture; -use tracing_subscriber::{Registry, layer::SubscriberExt}; -use tracing_tree::HierarchicalLayer; use triomphe::Arc; use crate::{ @@ -44,6 +39,7 @@ use crate::{ db::HirDatabase, display::{DisplayTarget, HirDisplay}, infer::{Adjustment, TypeMismatch}, + setup_tracing, test_db::TestDB, }; @@ -51,23 +47,6 @@ use crate::{ // against snapshots of the expected results using expect. Use // `env UPDATE_EXPECT=1 cargo test -p hir_ty` to update the snapshots. -fn setup_tracing() -> Option { - static ENABLE: LazyLock = LazyLock::new(|| env::var("CHALK_DEBUG").is_ok()); - if !*ENABLE { - return None; - } - - let filter: tracing_subscriber::filter::Targets = - env::var("CHALK_DEBUG").ok().and_then(|it| it.parse().ok()).unwrap_or_default(); - let layer = HierarchicalLayer::default() - .with_indent_lines(true) - .with_ansi(false) - .with_indent_amount(2) - .with_writer(std::io::stderr); - let subscriber = Registry::default().with(filter).with(layer); - Some(tracing::subscriber::set_default(subscriber)) -} - #[track_caller] fn check_types(#[rust_analyzer::rust_fixture] ra_fixture: &str) { check_impl(ra_fixture, false, true, false) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs index dbc68eeba1e6..5893894c33ab 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs @@ -12,9 +12,10 @@ use crate::display::{DisplayTarget, HirDisplay}; use crate::mir::MirSpan; use crate::test_db::TestDB; -use super::visit_module; +use super::{setup_tracing, visit_module}; fn check_closure_captures(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { + let _tracing = setup_tracing(); let (db, file_id) = TestDB::with_single_file(ra_fixture); let module = db.module_for_file(file_id.file_id(&db)); let def_map = module.def_map(&db); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs index 3894b4b6f7ba..3b7d4d2184a5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs @@ -96,7 +96,7 @@ fn foo(x: &[T]) -> &[T] { x } fn test() { let x = if true { foo(&[1]) - // ^^^^ adjustments: Deref(None), Borrow(Ref('?8, Not)), Pointer(Unsize) + // ^^^^ adjustments: Deref(None), Borrow(Ref('?11, Not)), Pointer(Unsize) } else { &[1] }; @@ -148,7 +148,7 @@ fn foo(x: &[T]) -> &[T] { x } fn test(i: i32) { let x = match i { 2 => foo(&[2]), - // ^^^^ adjustments: Deref(None), Borrow(Ref('?8, Not)), Pointer(Unsize) + // ^^^^ adjustments: Deref(None), Borrow(Ref('?11, Not)), Pointer(Unsize) 1 => &[1], _ => &[3], }; @@ -484,6 +484,8 @@ fn test() { ); } +// FIXME(next-solver): We could learn more from the `&S` -> `&dyn Foo` coercion if we followed the rustc model +// where unsized is successful if all unsizing trait goals are certain (and non-unsizing goals are delayed). #[test] fn coerce_unsize_trait_object_simple() { check_types( @@ -503,8 +505,8 @@ fn test() { //^ S let obj: &dyn Bar<_, i8, i16> = &S; //^ S - let obj: &dyn Foo = &S; - //^ S + //let obj: &dyn Foo = &S; + // S<{unknown}, {unknown}> }"#, ); } @@ -543,9 +545,9 @@ struct Bar(Foo); fn test() { let _: &Foo<[usize]> = &Foo { t: [1, 2, 3] }; - //^^^^^^^^^^^^^^^^^^^^^ expected &'? Foo<[usize]>, got &'? Foo<[i32; 3]> + //^^^^^^^^^^^^^^^^^^^^^ type: &'? Foo<[usize; 3]> let _: &Bar<[usize]> = &Bar(Foo { t: [1, 2, 3] }); - //^^^^^^^^^^^^^^^^^^^^^^^^^^ expected &'? Bar<[usize]>, got &'? Bar<[i32; 3]> + //^^^^^^^^^^^^^^^^^^^^^^^^^^ type: &'? Bar<[usize; 3]> } "#, ); @@ -899,7 +901,7 @@ impl core::ops::Index for StructMut { fn index(&self, index: usize) -> &Self::Output { &() } } -impl core::ops::IndexMut for StructMut { +impl core::ops::IndexMut for StructMut { fn index_mut(&mut self, index: usize) -> &mut Self::Output { &mut () } } fn test() { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs index 3159499e8670..df9061d23bf5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs @@ -519,6 +519,7 @@ impl SomeStruct { ); } +// FIXME(next-solver): does this test make sense with fast path? #[test] fn add_struct_invalidates_trait_solve() { let (mut db, file_id) = TestDB::with_single_file( @@ -559,7 +560,7 @@ fn main() { let _inference_result = db.infer(def); } }, - &[("trait_solve_shim", 2)], + &[("trait_solve_shim", 0)], expect_test::expect![[r#" [ "source_root_crates_shim", @@ -606,21 +607,17 @@ fn main() { "callable_item_signature_shim", "adt_variance_shim", "variances_of_shim", - "trait_solve_shim", - "trait_datum_shim", - "generic_predicates_shim", - "adt_datum_shim", "trait_impls_in_deps_shim", "trait_impls_in_crate_shim", "impl_trait_with_diagnostics_shim", "impl_self_ty_with_diagnostics_shim", "type_for_adt_tracked", - "impl_datum_shim", - "generic_predicates_shim", - "program_clauses_for_chalk_env_shim", + "impl_trait_with_diagnostics_ns_shim", + "impl_self_ty_with_diagnostics_ns_shim", + "generic_predicates_ns_shim", + "generic_predicates_ns_shim", "value_ty_shim", "generic_predicates_shim", - "trait_solve_shim", "lang_item", ] "#]], @@ -703,10 +700,13 @@ fn main() { "impl_signature_with_source_map_shim", "impl_signature_shim", "callable_item_signature_shim", - "generic_predicates_shim", "trait_impls_in_crate_shim", "impl_trait_with_diagnostics_shim", "impl_self_ty_with_diagnostics_shim", + "impl_trait_with_diagnostics_ns_shim", + "impl_self_ty_with_diagnostics_ns_shim", + "generic_predicates_ns_shim", + "generic_predicates_ns_shim", "generic_predicates_shim", ] "#]], diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs index ea7a113cae3f..5d088e40cded 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs @@ -197,17 +197,17 @@ fn expr_macro_def_expanded_in_various_places() { 39..442 '{ ...!(); }': () 73..94 'spam!(...am!())': {unknown} 100..119 'for _ ...!() {}': fn into_iter(isize) -> ::IntoIter - 100..119 'for _ ...!() {}': IntoIterator::IntoIter + 100..119 'for _ ...!() {}': {unknown} 100..119 'for _ ...!() {}': ! - 100..119 'for _ ...!() {}': IntoIterator::IntoIter - 100..119 'for _ ...!() {}': &'? mut IntoIterator::IntoIter - 100..119 'for _ ...!() {}': fn next>(&'? mut IntoIterator::IntoIter) -> Option< as Iterator>::Item> - 100..119 'for _ ...!() {}': Option> + 100..119 'for _ ...!() {}': {unknown} + 100..119 'for _ ...!() {}': &'? mut {unknown} + 100..119 'for _ ...!() {}': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> + 100..119 'for _ ...!() {}': Option<{unknown}> 100..119 'for _ ...!() {}': () 100..119 'for _ ...!() {}': () 100..119 'for _ ...!() {}': () 100..119 'for _ ...!() {}': () - 104..105 '_': IntoIterator::Item + 104..105 '_': {unknown} 117..119 '{}': () 124..134 '|| spam!()': impl Fn() -> isize 140..156 'while ...!() {}': ! @@ -291,17 +291,17 @@ fn expr_macro_rules_expanded_in_various_places() { 53..456 '{ ...!(); }': () 87..108 'spam!(...am!())': {unknown} 114..133 'for _ ...!() {}': fn into_iter(isize) -> ::IntoIter - 114..133 'for _ ...!() {}': IntoIterator::IntoIter + 114..133 'for _ ...!() {}': {unknown} 114..133 'for _ ...!() {}': ! - 114..133 'for _ ...!() {}': IntoIterator::IntoIter - 114..133 'for _ ...!() {}': &'? mut IntoIterator::IntoIter - 114..133 'for _ ...!() {}': fn next>(&'? mut IntoIterator::IntoIter) -> Option< as Iterator>::Item> - 114..133 'for _ ...!() {}': Option> + 114..133 'for _ ...!() {}': {unknown} + 114..133 'for _ ...!() {}': &'? mut {unknown} + 114..133 'for _ ...!() {}': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> + 114..133 'for _ ...!() {}': Option<{unknown}> 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () - 118..119 '_': IntoIterator::Item + 118..119 '_': {unknown} 131..133 '{}': () 138..148 '|| spam!()': impl Fn() -> isize 154..170 'while ...!() {}': ! diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs index c58ca6c67a8d..f09b3ef6bfd0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs @@ -1134,6 +1134,7 @@ fn test() { (S {}).method(); } fn dyn_trait_super_trait_not_in_scope() { check_infer( r#" + //- minicore: dispatch_from_dyn mod m { pub trait SuperTrait { fn foo(&self) -> u32 { 0 } @@ -1309,7 +1310,7 @@ fn main() { fn dyn_trait_method_priority() { check_types( r#" -//- minicore: from +//- minicore: from, dispatch_from_dyn trait Trait { fn into(&self) -> usize { 0 } } @@ -1823,6 +1824,33 @@ fn test() { ); } +#[test] +fn deref_fun_3() { + check_types( + r#" +//- minicore: receiver + +struct A(T, U); +struct B(T); +struct C(T); + +impl core::ops::Deref for A, u32> { + type Target = B; + fn deref(&self) -> &B { &self.0 } +} + +fn make() -> T { loop {} } + +fn test() { + let a1 = A(make(), make()); + let _: usize = (*a1).0; + a1; + //^^ A, u32> +} +"#, + ); +} + #[test] fn deref_into_inference_var() { check_types( @@ -2077,7 +2105,7 @@ impl Foo { } fn test() { Box::new(Foo).foo(); - //^^^^^^^^^^^^^ adjustments: Deref(None), Borrow(Ref('?3, Not)) + //^^^^^^^^^^^^^ adjustments: Deref(None), Borrow(Ref('?5, Not)) } "#, ); @@ -2095,7 +2123,7 @@ impl Foo { use core::mem::ManuallyDrop; fn test() { ManuallyDrop::new(Foo).foo(); - //^^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Some(Not)))), Borrow(Ref('?4, Not)) + //^^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Some(Not)))), Borrow(Ref('?6, Not)) } "#, ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index c4c17a93c9cd..25061e1dbdb9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -773,7 +773,7 @@ fn issue_4800() { "#, expect![[r#" 379..383 'self': &'? mut PeerSet - 401..424 '{ ... }': dyn Future + '? + 401..424 '{ ... }': dyn Future + 'static 411..418 'loop {}': ! 416..418 '{}': () 575..579 'self': &'? mut Self @@ -781,6 +781,9 @@ fn issue_4800() { ); } +// FIXME(next-solver): Though `Repeat: IntoIterator` does not hold here, we +// should be able to do better at given type hints (with Chalk, we did `IntoIterator::Item>`) +// From what I can tell, the point of this test is to not panic though. #[test] fn issue_4966() { check_infer( @@ -824,11 +827,11 @@ fn issue_4966() { 311..317 'repeat': Repeat f64>> 320..345 'Repeat...nner }': Repeat f64>> 338..343 'inner': Map f64> - 356..359 'vec': Vec f64>>>> - 362..371 'from_iter': fn from_iter f64>>>, Repeat f64>>>(Repeat f64>>) -> Vec f64>>>> - 362..379 'from_i...epeat)': Vec f64>>>> + 356..359 'vec': Vec<{unknown}> + 362..371 'from_iter': fn from_iter<{unknown}, Repeat f64>>>(Repeat f64>>) -> Vec<{unknown}> + 362..379 'from_i...epeat)': Vec<{unknown}> 372..378 'repeat': Repeat f64>> - 386..389 'vec': Vec f64>>>> + 386..389 'vec': Vec<{unknown}> 386..399 'vec.foo_bar()': {unknown} "#]], ); @@ -1224,6 +1227,8 @@ fn mamba(a: U32!(), p: u32) -> u32 { #[test] fn for_loop_block_expr_iterable() { + // FIXME(next-solver): it would be nice to be able to hint `IntoIterator::IntoIter<()>` instead of just `{unknown}` + // (even though `(): IntoIterator` does not hold) check_infer( r#" //- minicore: iterator @@ -1236,17 +1241,17 @@ fn test() { expect![[r#" 10..68 '{ ... } }': () 16..66 'for _ ... }': fn into_iter<()>(()) -> <() as IntoIterator>::IntoIter - 16..66 'for _ ... }': IntoIterator::IntoIter<()> + 16..66 'for _ ... }': {unknown} 16..66 'for _ ... }': ! - 16..66 'for _ ... }': IntoIterator::IntoIter<()> - 16..66 'for _ ... }': &'? mut IntoIterator::IntoIter<()> - 16..66 'for _ ... }': fn next>(&'? mut IntoIterator::IntoIter<()>) -> Option< as Iterator>::Item> - 16..66 'for _ ... }': Option> + 16..66 'for _ ... }': {unknown} + 16..66 'for _ ... }': &'? mut {unknown} + 16..66 'for _ ... }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item> + 16..66 'for _ ... }': Option<{unknown}> 16..66 'for _ ... }': () 16..66 'for _ ... }': () 16..66 'for _ ... }': () 16..66 'for _ ... }': () - 20..21 '_': IntoIterator::Item<()> + 20..21 '_': {unknown} 25..39 '{ let x = 0; }': () 31..32 'x': i32 35..36 '0': i32 @@ -1283,7 +1288,6 @@ fn test() { #[test] fn bug_11242() { - // FIXME: wrong, should be u32 check_types( r#" fn foo() @@ -1292,7 +1296,7 @@ where B: IntoIterator, { let _x: ::Item; - // ^^ {unknown} + // ^^ u32 } pub trait Iterator { @@ -1495,7 +1499,7 @@ fn regression_11688_2() { fn regression_11688_3() { check_types( r#" - //- minicore: iterator + //- minicore: iterator, dispatch_from_dyn struct Ar(T); fn f( num_zeros: usize, @@ -1514,6 +1518,7 @@ fn regression_11688_3() { fn regression_11688_4() { check_types( r#" + //- minicore: dispatch_from_dyn trait Bar { fn baz(&self) -> [i32; C]; } @@ -2035,11 +2040,11 @@ fn issue_17734() { r#" fn test() { let x = S::foo::<'static, &()>(&S); - // ^ Wrap<'?, ()> + // ^ Wrap<'static, ()> let x = S::foo::<&()>(&S); // ^ Wrap<'?, ()> let x = S.foo::<'static, &()>(); - // ^ Wrap<'?, ()> + // ^ Wrap<'static, ()> let x = S.foo::<&()>(); // ^ Wrap<'?, ()> } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index b154e5987857..a995a45f6e75 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -2923,7 +2923,7 @@ fn test { // ^^ impl Fn() let c4 = f1(); - // ^^ impl FnOnce() + ?Sized + // ^^ impl FnOnce() f2(|| { 0 }); // ^^^^^^^^ impl FnOnce() -> i32 @@ -3922,7 +3922,7 @@ fn foo() { expect![[r#" 110..127 '{ ...z(); }': () 116..122 'T::baz': fn baz() -> <{unknown} as Foo>::Gat<'?> - 116..124 'T::baz()': Foo::Gat<'?, {unknown}> + 116..124 'T::baz()': {unknown} "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index 56e31a1af1b9..2e4346a86973 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -1691,7 +1691,7 @@ fn test>(x: T, y: impl Trait) { get2(y); get(set(S)); get2(set(S)); - get2(S::); + get2(S::); }"#, expect![[r#" 49..50 't': T @@ -1703,7 +1703,7 @@ fn test>(x: T, y: impl Trait) { 166..167 't': T 256..257 'x': T 262..263 'y': impl Trait - 289..397 '{ ...r>); }': () + 289..399 '{ ...e>); }': () 295..298 'get': fn get(T) -> ::Type 295..301 'get(x)': u32 299..300 'x': T @@ -1726,9 +1726,9 @@ fn test>(x: T, y: impl Trait) { 367..370 'set': fn set>(S) -> S 367..373 'set(S)': S 371..372 'S': S - 380..384 'get2': fn get2>(S) -> str - 380..394 'get2(S::)': str - 385..393 'S::': S + 380..384 'get2': fn get2>(S) -> usize + 380..396 'get2(S...size>)': usize + 385..395 'S::': S "#]], ); } @@ -2740,7 +2740,7 @@ impl> Foo { fn dyn_trait_through_chalk() { check_types( r#" -//- minicore: deref +//- minicore: deref, unsize, dispatch_from_dyn struct Box {} impl core::ops::Deref for Box { type Target = T; @@ -3228,7 +3228,7 @@ fn foo() { fn infer_dyn_fn_output() { check_types( r#" -//- minicore: fn +//- minicore: fn, dispatch_from_dyn fn foo() { let f: &dyn Fn() -> i32; f(); @@ -4255,8 +4255,8 @@ fn f<'a>(v: &dyn Trait = &'a i32>) { 127..128 'v': &'? (dyn Trait = &'a i32> + '?) 164..195 '{ ...f(); }': () 170..171 'v': &'? (dyn Trait = &'a i32> + '?) - 170..184 'v.get::()': &'? i32 - 170..192 'v.get:...eref()': &'? i32 + 170..184 'v.get::()': {unknown} + 170..192 'v.get:...eref()': &'? {unknown} "#]], ); } @@ -4785,30 +4785,30 @@ fn allowed2<'a>(baz: impl Baz) {} fn allowed3(baz: impl Baz>) {} "#, expect![[r#" - 139..140 'f': impl Fn({unknown}) + ?Sized + 139..140 'f': impl Fn({unknown}) 161..193 '{ ...oo); }': () 171..174 'foo': S 177..178 'S': S - 184..185 'f': impl Fn({unknown}) + ?Sized + 184..185 'f': impl Fn({unknown}) 184..190 'f(foo)': () 186..189 'foo': S - 251..252 'f': impl Fn(&'? {unknown}) + ?Sized + 251..252 'f': impl Fn(&'? {unknown}) 274..307 '{ ...oo); }': () 284..287 'foo': S 290..291 'S': S - 297..298 'f': impl Fn(&'? {unknown}) + ?Sized + 297..298 'f': impl Fn(&'? {unknown}) 297..304 'f(&foo)': () 299..303 '&foo': &'? S 300..303 'foo': S - 325..328 'bar': impl Bar<{unknown}> + ?Sized + 325..328 'bar': impl Bar<{unknown}> 350..352 '{}': () - 405..408 'bar': impl Bar<&'? {unknown}> + ?Sized + 405..408 'bar': impl Bar<&'? {unknown}> 431..433 '{}': () - 447..450 'baz': impl Baz + ?Sized + 447..450 'baz': impl Baz 480..482 '{}': () - 500..503 'baz': impl Baz + ?Sized + 500..503 'baz': impl Baz 544..546 '{}': () - 560..563 'baz': impl Baz> + ?Sized + 560..563 'baz': impl Baz> 598..600 '{}': () "#]], ) @@ -4930,3 +4930,67 @@ fn main() { "#]], ); } + +#[test] +fn new_solver_crash_1() { + check_infer( + r#" +pub trait Deserializer<'de> { + type Error; +} + +fn deserialize_abs_pathbuf<'de, D>(de: D) -> D::Error +where + D: Deserializer<'de>, +{ +} +"#, + expect![[r#" + 84..86 'de': D + 135..138 '{ }': Deserializer::Error<'de, D> + "#]], + ); +} + +#[test] +fn new_solver_crash_2() { + check_infer( + r#" +//- minicore: deref, send, sync +use core::ops::Deref; + +trait Error {} + +struct AnyhowError; + +impl Deref for AnyhowError { + type Target = dyn Error + Send + Sync; + + fn deref(&self) -> &Self::Target { loop {} } +} + +impl AnyhowError { + fn downcast(self) {} +} + + +fn main() { + let e = AnyhowError; + e.downcast::<()>(); +} +"#, + expect![[r#" + 147..151 'self': &'? AnyhowError + 170..181 '{ loop {} }': &'? (dyn Error + Send + Sync + 'static) + 172..179 'loop {}': ! + 177..179 '{}': () + 223..227 'self': AnyhowError + 229..231 '{}': () + 246..298 '{ ...>(); }': () + 256..257 'e': AnyhowError + 260..271 'AnyhowError': AnyhowError + 277..278 'e': AnyhowError + 277..295 'e.down...<()>()': () + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs index f53409af2b30..fe4cf7a3da52 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs @@ -10,6 +10,7 @@ use crate::{ }; use hir_def::{AdtId, ItemContainerId, Lookup, TypeAliasId}; +#[allow(unused)] pub(crate) use unsafe_tls::{set_current_program, with_current_program}; pub(crate) struct DebugContext<'a>(&'a dyn HirDatabase); @@ -136,6 +137,7 @@ mod unsafe_tls { if PROGRAM.is_set() { PROGRAM.with(|prog| op(Some(prog))) } else { op(None) } } + #[allow(dead_code)] pub(crate) fn set_current_program(p: &dyn HirDatabase, op: OP) -> R where OP: FnOnce() -> R, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs index 08b9d242e71d..51cf5be1372c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -1,43 +1,38 @@ //! Trait solving using Chalk. use core::fmt; -use std::env::var; use chalk_ir::{DebruijnIndex, GoalData, fold::TypeFoldable}; -use chalk_recursive::Cache; -use chalk_solve::{Solver, logging_db::LoggingRustIrDatabase, rust_ir}; +use chalk_solve::rust_ir; use base_db::Crate; use hir_def::{BlockId, TraitId, lang_item::LangItem}; use hir_expand::name::Name; use intern::sym; +use rustc_next_trait_solver::solve::{HasChanged, SolverDelegateEvalExt}; +use rustc_type_ir::{ + InferCtxtLike, TypingMode, + inherent::{SliceLike, Span as _}, + solve::Certainty, +}; use span::Edition; -use stdx::{never, panic_context}; +use stdx::never; use triomphe::Arc; use crate::{ - AliasEq, AliasTy, Canonical, DomainGoal, Goal, Guidance, InEnvironment, Interner, ProjectionTy, - ProjectionTyExt, Solution, TraitRefExt, Ty, TyKind, TypeFlags, WhereClause, db::HirDatabase, - infer::unify::InferenceTable, utils::UnevaluatedConstEvaluatorFolder, + AliasEq, AliasTy, Canonical, DomainGoal, Goal, InEnvironment, Interner, ProjectionTy, + ProjectionTyExt, TraitRefExt, Ty, TyKind, TypeFlags, WhereClause, + db::HirDatabase, + infer::unify::InferenceTable, + next_solver::{ + DbInterner, GenericArg, SolverContext, Span, + infer::{DbInternerInferExt, InferCtxt}, + mapping::{ChalkToNextSolver, convert_canonical_args_for_result}, + util::mini_canonicalize, + }, + utils::UnevaluatedConstEvaluatorFolder, }; -/// This controls how much 'time' we give the Chalk solver before giving up. -const CHALK_SOLVER_FUEL: i32 = 1000; - -#[derive(Debug, Copy, Clone)] -pub(crate) struct ChalkContext<'a> { - pub(crate) db: &'a dyn HirDatabase, - pub(crate) krate: Crate, - pub(crate) block: Option, -} - -fn create_chalk_solver() -> chalk_recursive::RecursiveSolver { - let overflow_depth = - var("CHALK_OVERFLOW_DEPTH").ok().and_then(|s| s.parse().ok()).unwrap_or(500); - let max_size = var("CHALK_SOLVER_MAX_SIZE").ok().and_then(|s| s.parse().ok()).unwrap_or(150); - chalk_recursive::RecursiveSolver::new(overflow_depth, max_size, Some(Cache::new())) -} - /// A set of clauses that we assume to be true. E.g. if we are inside this function: /// ```rust /// fn foo(t: T) {} @@ -103,13 +98,43 @@ pub(crate) fn normalize_projection_query( table.resolve_completely(ty) } +fn identity_subst( + binders: chalk_ir::CanonicalVarKinds, +) -> chalk_ir::Canonical> { + let identity_subst = chalk_ir::Substitution::from_iter( + Interner, + binders.iter(Interner).enumerate().map(|(index, c)| { + let index_db = chalk_ir::BoundVar::new(DebruijnIndex::INNERMOST, index); + match &c.kind { + chalk_ir::VariableKind::Ty(_) => { + chalk_ir::GenericArgData::Ty(TyKind::BoundVar(index_db).intern(Interner)) + .intern(Interner) + } + chalk_ir::VariableKind::Lifetime => chalk_ir::GenericArgData::Lifetime( + chalk_ir::LifetimeData::BoundVar(index_db).intern(Interner), + ) + .intern(Interner), + chalk_ir::VariableKind::Const(ty) => chalk_ir::GenericArgData::Const( + chalk_ir::ConstData { + ty: ty.clone(), + value: chalk_ir::ConstValue::BoundVar(index_db), + } + .intern(Interner), + ) + .intern(Interner), + } + }), + ); + chalk_ir::Canonical { binders, value: identity_subst } +} + /// Solve a trait goal using Chalk. pub(crate) fn trait_solve_query( db: &dyn HirDatabase, krate: Crate, block: Option, goal: Canonical>, -) -> Option { +) -> NextTraitSolveResult { let _p = tracing::info_span!("trait_solve_query", detail = ?match &goal.value.goal.data(Interner) { GoalData::DomainGoal(DomainGoal::Holds(WhereClause::Implemented(it))) => db .trait_signature(it.hir_trait_id()) @@ -128,7 +153,110 @@ pub(crate) fn trait_solve_query( && let TyKind::BoundVar(_) = projection_ty.self_type_parameter(db).kind(Interner) { // Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible - return Some(Solution::Ambig(Guidance::Unknown)); + return NextTraitSolveResult::Uncertain(identity_subst(goal.binders.clone())); + } + + // Chalk see `UnevaluatedConst` as a unique concrete value, but we see it as an alias for another const. So + // we should get rid of it when talking to chalk. + let goal = goal + .try_fold_with(&mut UnevaluatedConstEvaluatorFolder { db }, DebruijnIndex::INNERMOST) + .unwrap(); + + // We currently don't deal with universes (I think / hope they're not yet + // relevant for our use cases?) + next_trait_solve(db, krate, block, goal) +} + +fn solve_nextsolver<'db>( + db: &'db dyn HirDatabase, + krate: Crate, + block: Option, + goal: &chalk_ir::UCanonical>>, +) -> Result< + (HasChanged, Certainty, rustc_type_ir::Canonical, Vec>>), + rustc_type_ir::solve::NoSolution, +> { + // FIXME: should use analysis_in_body, but that needs GenericDefId::Block + let context = SolverContext( + DbInterner::new_with(db, Some(krate), block) + .infer_ctxt() + .build(TypingMode::non_body_analysis()), + ); + + match goal.canonical.value.goal.data(Interner) { + // FIXME: args here should be...what? not empty + GoalData::All(goals) if goals.is_empty(Interner) => { + return Ok((HasChanged::No, Certainty::Yes, mini_canonicalize(context, vec![]))); + } + _ => {} + } + + let goal = goal.canonical.to_nextsolver(context.cx()); + tracing::info!(?goal); + + let (goal, var_values) = context.instantiate_canonical(&goal); + tracing::info!(?var_values); + + let res = context.evaluate_root_goal(goal, Span::dummy(), None); + + let vars = + var_values.var_values.iter().map(|g| context.0.resolve_vars_if_possible(g)).collect(); + let canonical_var_values = mini_canonicalize(context, vars); + + let res = res.map(|r| (r.has_changed, r.certainty, canonical_var_values)); + + tracing::debug!("solve_nextsolver({:?}) => {:?}", goal, res); + + res +} + +#[derive(Clone, Debug, PartialEq)] +pub enum NextTraitSolveResult { + Certain(chalk_ir::Canonical>), + Uncertain(chalk_ir::Canonical>), + NoSolution, +} + +impl NextTraitSolveResult { + pub fn no_solution(&self) -> bool { + matches!(self, NextTraitSolveResult::NoSolution) + } + + pub fn certain(&self) -> bool { + matches!(self, NextTraitSolveResult::Certain(..)) + } + + pub fn uncertain(&self) -> bool { + matches!(self, NextTraitSolveResult::Uncertain(..)) + } +} + +/// Solve a trait goal using Chalk. +pub fn next_trait_solve( + db: &dyn HirDatabase, + krate: Crate, + block: Option, + goal: Canonical>, +) -> NextTraitSolveResult { + let detail = match &goal.value.goal.data(Interner) { + GoalData::DomainGoal(DomainGoal::Holds(WhereClause::Implemented(it))) => { + db.trait_signature(it.hir_trait_id()).name.display(db, Edition::LATEST).to_string() + } + GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(_))) => "alias_eq".to_owned(), + _ => "??".to_owned(), + }; + let _p = tracing::info_span!("next_trait_solve", ?detail).entered(); + tracing::info!("next_trait_solve({:?})", goal.value.goal); + + if let GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(AliasEq { + alias: AliasTy::Projection(projection_ty), + .. + }))) = &goal.value.goal.data(Interner) + && let TyKind::BoundVar(_) = projection_ty.self_type_parameter(db).kind(Interner) + { + // Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible + // FIXME + return NextTraitSolveResult::Uncertain(identity_subst(goal.binders.clone())); } // Chalk see `UnevaluatedConst` as a unique concrete value, but we see it as an alias for another const. So @@ -140,70 +268,44 @@ pub(crate) fn trait_solve_query( // We currently don't deal with universes (I think / hope they're not yet // relevant for our use cases?) let u_canonical = chalk_ir::UCanonical { canonical: goal, universes: 1 }; - solve(db, krate, block, &u_canonical) -} + tracing::info!(?u_canonical); -fn solve( - db: &dyn HirDatabase, - krate: Crate, - block: Option, - goal: &chalk_ir::UCanonical>>, -) -> Option> { - let _p = tracing::info_span!("solve", ?krate, ?block).entered(); - let context = ChalkContext { db, krate, block }; - tracing::debug!("solve goal: {:?}", goal); - let mut solver = create_chalk_solver(); + let next_solver_res = solve_nextsolver(db, krate, block, &u_canonical); - let fuel = std::cell::Cell::new(CHALK_SOLVER_FUEL); - - let should_continue = || { - db.unwind_if_revision_cancelled(); - let remaining = fuel.get(); - fuel.set(remaining - 1); - if remaining == 0 { - tracing::debug!("fuel exhausted"); + match next_solver_res { + Err(_) => NextTraitSolveResult::NoSolution, + Ok((_, Certainty::Yes, args)) => NextTraitSolveResult::Certain( + convert_canonical_args_for_result(DbInterner::new_with(db, Some(krate), block), args), + ), + Ok((_, Certainty::Maybe(_), args)) => { + let subst = convert_canonical_args_for_result( + DbInterner::new_with(db, Some(krate), block), + args, + ); + NextTraitSolveResult::Uncertain(chalk_ir::Canonical { + binders: subst.binders, + value: subst.value.subst, + }) } - remaining > 0 - }; - - let mut solve = || { - let _ctx = if is_chalk_debug() || is_chalk_print() { - Some(panic_context::enter(format!("solving {goal:?}"))) - } else { - None - }; - let solution = if is_chalk_print() { - let logging_db = - LoggingRustIrDatabaseLoggingOnDrop(LoggingRustIrDatabase::new(context)); - solver.solve_limited(&logging_db.0, goal, &should_continue) - } else { - solver.solve_limited(&context, goal, &should_continue) - }; - - tracing::debug!("solve({:?}) => {:?}", goal, solution); - - solution - }; - - // don't set the TLS for Chalk unless Chalk debugging is active, to make - // extra sure we only use it for debugging - if is_chalk_debug() { crate::tls::set_current_program(db, solve) } else { solve() } -} - -struct LoggingRustIrDatabaseLoggingOnDrop<'a>(LoggingRustIrDatabase>); - -impl Drop for LoggingRustIrDatabaseLoggingOnDrop<'_> { - fn drop(&mut self) { - tracing::info!("chalk program:\n{}", self.0); } } -fn is_chalk_debug() -> bool { - std::env::var("CHALK_DEBUG").is_ok() -} +/// Solve a trait goal using Chalk. +pub fn next_trait_solve_in_ctxt<'db, 'a>( + infer_ctxt: &'a InferCtxt<'db>, + goal: crate::next_solver::Goal<'db, crate::next_solver::Predicate<'db>>, +) -> Result<(HasChanged, Certainty), rustc_type_ir::solve::NoSolution> { + tracing::info!(?goal); -fn is_chalk_print() -> bool { - std::env::var("CHALK_PRINT").is_ok() + let context = <&SolverContext<'db>>::from(infer_ctxt); + + let res = context.evaluate_root_goal(goal, Span::dummy(), None); + + let res = res.map(|r| (r.has_changed, r.certainty)); + + tracing::debug!("solve_nextsolver({:?}) => {:?}", goal, res); + + res } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] diff --git a/src/tools/rust-analyzer/crates/hir/Cargo.toml b/src/tools/rust-analyzer/crates/hir/Cargo.toml index c68ff706e481..dfa39384320d 100644 --- a/src/tools/rust-analyzer/crates/hir/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir/Cargo.toml @@ -22,6 +22,8 @@ tracing.workspace = true triomphe.workspace = true indexmap.workspace = true +ra-ap-rustc_type_ir.workspace = true + # local deps base-db.workspace = true cfg.workspace = true @@ -36,6 +38,9 @@ span.workspace = true [dev-dependencies] expect-test.workspace = true +tracing.workspace = true +tracing-subscriber.workspace = true +tracing-tree.workspace = true # local deps test-utils.workspace = true diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index a323f97997c6..475906ae51a1 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -20,6 +20,12 @@ #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] #![recursion_limit = "512"] +#[cfg(feature = "in-rust-tree")] +extern crate rustc_type_ir; + +#[cfg(not(feature = "in-rust-tree"))] +extern crate ra_ap_rustc_type_ir as rustc_type_ir; + mod attrs; mod from_id; mod has_source; @@ -54,7 +60,10 @@ use hir_def::{ }, item_tree::ImportAlias, layout::{self, ReprOptions, TargetDataLayout}, - nameres::{self, assoc::TraitItems, diagnostics::DefDiagnostic}, + nameres::{ + assoc::TraitItems, + diagnostics::{DefDiagnostic, DefDiagnosticKind}, + }, per_ns::PerNs, resolver::{HasResolver, Resolver}, signatures::{ImplFlags, StaticFlags, TraitFlags, VariantFields}, @@ -76,11 +85,11 @@ use hir_ty::{ layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding}, method_resolution, mir::{MutBorrowKind, interpret_mir}, + next_solver::{DbInterner, GenericArgs, SolverDefId, infer::InferCtxt}, primitive::UintTy, traits::FnTrait, }; use itertools::Itertools; -use nameres::diagnostics::DefDiagnosticKind; use rustc_hash::FxHashSet; use smallvec::SmallVec; use span::{AstIdNode, Edition, FileId}; @@ -187,6 +196,10 @@ pub struct CrateDependency { } impl Crate { + pub fn base(self) -> base_db::Crate { + self.id + } + pub fn origin(self, db: &dyn HirDatabase) -> CrateOrigin { self.id.data(db).origin.clone() } @@ -1247,6 +1260,25 @@ pub struct Field { pub(crate) id: LocalFieldId, } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct InstantiatedField<'db> { + pub(crate) inner: Field, + pub(crate) args: GenericArgs<'db>, +} + +impl<'db> InstantiatedField<'db> { + /// Returns the type as in the signature of the struct. + pub fn ty(&self, db: &'db dyn HirDatabase) -> TypeNs<'db> { + let krate = self.inner.krate(db); + let interner = DbInterner::new_with(db, Some(krate.base()), None); + + let var_id = self.inner.parent.into(); + let field = db.field_types_ns(var_id)[self.inner.id]; + let ty = field.instantiate(interner, self.args); + TypeNs::new(db, var_id, ty) + } +} + #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] pub struct TupleField { pub owner: DefWithBodyId, @@ -1444,6 +1476,11 @@ impl Struct { pub fn is_unstable(self, db: &dyn HirDatabase) -> bool { db.attrs(self.id.into()).is_unstable() } + + pub fn instantiate_infer<'db>(self, infer_ctxt: &InferCtxt<'db>) -> InstantiatedStruct<'db> { + let args = infer_ctxt.fresh_args_for_item(self.id.into()); + InstantiatedStruct { inner: self, args } + } } impl HasVisibility for Struct { @@ -1454,6 +1491,35 @@ impl HasVisibility for Struct { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct InstantiatedStruct<'db> { + pub(crate) inner: Struct, + pub(crate) args: GenericArgs<'db>, +} + +impl<'db> InstantiatedStruct<'db> { + pub fn fields(self, db: &dyn HirDatabase) -> Vec> { + self.inner + .id + .fields(db) + .fields() + .iter() + .map(|(id, _)| InstantiatedField { + inner: Field { parent: self.inner.into(), id }, + args: self.args, + }) + .collect() + } + + pub fn ty(self, db: &'db dyn HirDatabase) -> TypeNs<'db> { + let krate = self.inner.krate(db); + let interner = DbInterner::new_with(db, Some(krate.base()), None); + + let ty = db.ty_ns(self.inner.id.into()); + TypeNs::new(db, self.inner.id, ty.instantiate(interner, self.args)) + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Union { pub(crate) id: UnionId, @@ -1598,6 +1664,22 @@ impl HasVisibility for Enum { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct InstantiatedEnum<'db> { + pub(crate) inner: Enum, + pub(crate) args: GenericArgs<'db>, +} + +impl<'db> InstantiatedEnum<'db> { + pub fn ty(self, db: &'db dyn HirDatabase) -> TypeNs<'db> { + let krate = self.inner.krate(db); + let interner = DbInterner::new_with(db, Some(krate.base()), None); + + let ty = db.ty_ns(self.inner.id.into()); + TypeNs::new(db, self.inner.id, ty.instantiate(interner, self.args)) + } +} + impl From<&Variant> for DefWithBodyId { fn from(&v: &Variant) -> Self { DefWithBodyId::VariantId(v.into()) @@ -1673,6 +1755,38 @@ impl Variant { pub fn is_unstable(self, db: &dyn HirDatabase) -> bool { db.attrs(self.id.into()).is_unstable() } + + pub fn instantiate_infer<'db>(self, infer_ctxt: &InferCtxt<'db>) -> InstantiatedVariant<'db> { + let args = + infer_ctxt.fresh_args_for_item(self.parent_enum(infer_ctxt.interner.db()).id.into()); + InstantiatedVariant { inner: self, args } + } +} + +// FIXME: Rename to `EnumVariant` +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct InstantiatedVariant<'db> { + pub(crate) inner: Variant, + pub(crate) args: GenericArgs<'db>, +} + +impl<'db> InstantiatedVariant<'db> { + pub fn parent_enum(self, db: &dyn HirDatabase) -> InstantiatedEnum<'db> { + InstantiatedEnum { inner: self.inner.id.lookup(db).parent.into(), args: self.args } + } + + pub fn fields(self, db: &dyn HirDatabase) -> Vec> { + self.inner + .id + .fields(db) + .fields() + .iter() + .map(|(id, _)| InstantiatedField { + inner: Field { parent: self.inner.into(), id }, + args: self.args, + }) + .collect() + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -5072,7 +5186,7 @@ impl<'db> Type<'db> { binders: CanonicalVarKinds::empty(Interner), }; - db.trait_solve(self.env.krate, self.env.block, goal).is_some() + !db.trait_solve(self.env.krate, self.env.block, goal).no_solution() } pub fn normalize_trait_assoc_type( @@ -5827,6 +5941,55 @@ impl<'db> Type<'db> { } } +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub struct TypeNs<'db> { + env: Arc, + ty: hir_ty::next_solver::Ty<'db>, + _pd: PhantomCovariantLifetime<'db>, +} + +impl<'db> TypeNs<'db> { + fn new( + db: &'db dyn HirDatabase, + lexical_env: impl HasResolver, + ty: hir_ty::next_solver::Ty<'db>, + ) -> Self { + let resolver = lexical_env.resolver(db); + let environment = resolver + .generic_def() + .map_or_else(|| TraitEnvironment::empty(resolver.krate()), |d| db.trait_environment(d)); + TypeNs { env: environment, ty, _pd: PhantomCovariantLifetime::new() } + } + + // FIXME: Find better API that also handles const generics + pub fn impls_trait(&self, infcx: InferCtxt<'db>, trait_: Trait, args: &[TypeNs<'db>]) -> bool { + let args = GenericArgs::new_from_iter( + infcx.interner, + [self.ty].into_iter().chain(args.iter().map(|t| t.ty)).map(|t| t.into()), + ); + let trait_ref = hir_ty::next_solver::TraitRef::new( + infcx.interner, + SolverDefId::TraitId(trait_.id), + args, + ); + + let pred_kind = rustc_type_ir::Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Trait(rustc_type_ir::TraitPredicate { + trait_ref, + polarity: rustc_type_ir::PredicatePolarity::Positive, + }), + )); + let predicate = hir_ty::next_solver::Predicate::new(infcx.interner, pred_kind); + let goal = hir_ty::next_solver::Goal::new( + infcx.interner, + hir_ty::next_solver::ParamEnv::empty(), + predicate, + ); + let res = hir_ty::traits::next_trait_solve_in_ctxt(&infcx, goal); + res.map_or(false, |res| matches!(res.1, rustc_type_ir::solve::Certainty::Yes)) + } +} + #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] pub struct InlineAsmOperand { owner: DefWithBodyId, @@ -6476,3 +6639,6 @@ pub fn resolve_absolute_path<'a, I: Iterator + Clone + 'a>( fn as_name_opt(name: Option) -> Name { name.map_or_else(Name::missing, |name| name.as_name()) } + +pub use hir_ty::next_solver; +pub use hir_ty::setup_tracing; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs index af949a064989..cb83c67c9953 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs @@ -1,3 +1,4 @@ +use hir::next_solver::{DbInterner, TypingMode}; use ide_db::{RootDatabase, famous_defs::FamousDefs}; use syntax::ast::{self, AstNode, HasName}; @@ -80,17 +81,20 @@ fn existing_from_impl( sema: &'_ hir::Semantics<'_, RootDatabase>, variant: &ast::Variant, ) -> Option<()> { + let db = sema.db; let variant = sema.to_def(variant)?; - let enum_ = variant.parent_enum(sema.db); - let krate = enum_.module(sema.db).krate(); - + let krate = variant.module(db).krate(); let from_trait = FamousDefs(sema, krate).core_convert_From()?; + let interner = DbInterner::new_with(db, Some(krate.base()), None); + use hir::next_solver::infer::DbInternerInferExt; + let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); - let enum_type = enum_.ty(sema.db); - - let wrapped_type = variant.fields(sema.db).first()?.ty(sema.db); - - if enum_type.impls_trait(sema.db, from_trait, &[wrapped_type]) { Some(()) } else { None } + let variant = variant.instantiate_infer(&infcx); + let enum_ = variant.parent_enum(sema.db); + let field_ty = variant.fields(sema.db).first()?.ty(sema.db); + let enum_ty = enum_.ty(sema.db); + tracing::debug!(?enum_, ?field_ty, ?enum_ty); + enum_ty.impls_trait(infcx, from_trait, &[field_ty]).then_some(()) } #[cfg(test)] @@ -119,15 +123,19 @@ impl From for A { ); } + // FIXME(next-solver): it would be nice to not be *required* to resolve the + // path in order to properly generate assists #[test] fn test_generate_from_impl_for_enum_complicated_path() { check_assist( generate_from_impl_for_enum, r#" //- minicore: from +mod foo { pub mod bar { pub mod baz { pub struct Boo; } } } enum A { $0One(foo::bar::baz::Boo) } "#, r#" +mod foo { pub mod bar { pub mod baz { pub struct Boo; } } } enum A { One(foo::bar::baz::Boo) } impl From for A { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs index 6076d5cb5ced..deef4a9aac6d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs @@ -1,4 +1,5 @@ use ast::make; +use hir::next_solver::{DbInterner, TypingMode}; use hir::{HasCrate, ModuleDef, Semantics}; use ide_db::{ RootDatabase, famous_defs::FamousDefs, helpers::mod_path_to_ast, @@ -47,6 +48,7 @@ pub(crate) fn generate_single_field_struct_from( let strukt_name = ctx.find_node_at_offset::()?; let adt = ast::Adt::cast(strukt_name.syntax().parent()?)?; let ast::Adt::Struct(strukt) = adt else { + tracing::debug!(?adt); return None; }; @@ -57,10 +59,12 @@ pub(crate) fn generate_single_field_struct_from( let constructors = make_constructors(ctx, module, &types); if constructors.iter().filter(|expr| expr.is_none()).count() != 1 { + tracing::debug!(?constructors); return None; } let main_field_i = constructors.iter().position(Option::is_none)?; if from_impl_exists(&strukt, main_field_i, &ctx.sema).is_some() { + tracing::debug!(?strukt, ?main_field_i); return None; } @@ -200,6 +204,7 @@ fn get_fields(strukt: &ast::Struct) -> Option<(Option>, Vec, assist_label: Option<&str>, ) { + let _tracing = setup_tracing(); let (mut db, file_with_caret_id, range_or_offset) = RootDatabase::with_range_or_offset(before); db.enable_proc_attr_macros(); let text_without_caret = db.file_text(file_with_caret_id.file_id(&db)).text(&db).to_string(); @@ -318,7 +319,9 @@ fn check_with_config( _ => AssistResolveStrategy::All, }; let mut acc = Assists::new(&ctx, resolve); - handler(&mut acc, &ctx); + salsa::attach(&db, || { + handler(&mut acc, &ctx); + }); let mut res = acc.finish(); let assist = match assist_label { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index f75123324f37..129964f8387a 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -1384,14 +1384,15 @@ fn baz() { fn skip_iter() { check_no_kw( r#" - //- minicore: iterator + //- minicore: iterator, clone, builtin_impls fn foo() { [].$0 } "#, expect![[r#" - me clone() (as Clone) fn(&self) -> Self - me into_iter() (as IntoIterator) fn(self) -> ::IntoIter + me clone() (as Clone) fn(&self) -> Self + me fmt(…) (use core::fmt::Debug) fn(&self, &mut Formatter<'_>) -> Result<(), Error> + me into_iter() (as IntoIterator) fn(self) -> ::IntoIter "#]], ); check_no_kw( diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs index a70a1138d2f4..1a4c97e70b4a 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs @@ -10,6 +10,7 @@ mod snippet; #[cfg(test)] mod tests; +use base_db::salsa; use ide_db::{ FilePosition, FxHashSet, RootDatabase, imports::insert_use::{self, ImportScope}, @@ -228,7 +229,7 @@ pub fn completions( { let acc = &mut completions; - match analysis { + salsa::attach(db, || match analysis { CompletionAnalysis::Name(name_ctx) => completions::complete_name(acc, ctx, name_ctx), CompletionAnalysis::NameRef(name_ref_ctx) => { completions::complete_name_ref(acc, ctx, name_ref_ctx) @@ -256,7 +257,7 @@ pub fn completions( ); } CompletionAnalysis::UnexpandedAttrTT { .. } | CompletionAnalysis::String { .. } => (), - } + }) } Some(completions.into()) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs index fdc3d9a13bc9..4b3b271ca20e 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs @@ -26,7 +26,7 @@ mod visibility; use base_db::SourceDatabase; use expect_test::Expect; -use hir::PrefixKind; +use hir::{PrefixKind, setup_tracing}; use ide_db::{ FilePosition, RootDatabase, SnippetCap, imports::insert_use::{ImportGranularity, InsertUseConfig}, @@ -120,6 +120,8 @@ fn completion_list_with_config_raw( include_keywords: bool, trigger_character: Option, ) -> Vec { + let _tracing = setup_tracing(); + // filter out all but one built-in type completion for smaller test outputs let items = get_all_items(config, ra_fixture, trigger_character); items diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs index 27c91bc7c455..bb10086a08c6 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs @@ -1,3 +1,4 @@ +use base_db::salsa; use expect_test::{Expect, expect}; use crate::{ @@ -19,25 +20,29 @@ fn check_with_config( let (ctx, analysis) = crate::context::CompletionContext::new(&db, position, &config).unwrap(); let mut acc = crate::completions::Completions::default(); - if let CompletionAnalysis::Name(NameContext { kind: NameKind::IdentPat(pat_ctx), .. }) = - &analysis - { - crate::completions::flyimport::import_on_the_fly_pat(&mut acc, &ctx, pat_ctx); - } - if let CompletionAnalysis::NameRef(name_ref_ctx) = &analysis { - match &name_ref_ctx.kind { - NameRefKind::Path(path) => { - crate::completions::flyimport::import_on_the_fly_path(&mut acc, &ctx, path); - } - NameRefKind::DotAccess(dot_access) => { - crate::completions::flyimport::import_on_the_fly_dot(&mut acc, &ctx, dot_access); - } - NameRefKind::Pattern(pattern) => { - crate::completions::flyimport::import_on_the_fly_pat(&mut acc, &ctx, pattern); - } - _ => (), + salsa::attach(ctx.db, || { + if let CompletionAnalysis::Name(NameContext { kind: NameKind::IdentPat(pat_ctx), .. }) = + &analysis + { + crate::completions::flyimport::import_on_the_fly_pat(&mut acc, &ctx, pat_ctx); } - } + if let CompletionAnalysis::NameRef(name_ref_ctx) = &analysis { + match &name_ref_ctx.kind { + NameRefKind::Path(path) => { + crate::completions::flyimport::import_on_the_fly_path(&mut acc, &ctx, path); + } + NameRefKind::DotAccess(dot_access) => { + crate::completions::flyimport::import_on_the_fly_dot( + &mut acc, &ctx, dot_access, + ); + } + NameRefKind::Pattern(pattern) => { + crate::completions::flyimport::import_on_the_fly_pat(&mut acc, &ctx, pattern); + } + _ => (), + } + } + }); expect.assert_eq(&super::render_completion_list(Vec::from(acc))); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs index 148203107c4c..84ddff8f617a 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs @@ -677,6 +677,7 @@ fn bar() -> Bar { expect![[r#" fn foo() (as Foo) fn() -> Self ex Bar + ex Bar::foo() ex bar() "#]], ); @@ -706,6 +707,7 @@ fn bar() -> Bar { fn bar() fn() fn foo() (as Foo) fn() -> Self ex Bar + ex Bar::foo() ex bar() "#]], ); @@ -734,6 +736,7 @@ fn bar() -> Bar { expect![[r#" fn foo() (as Foo) fn() -> Self ex Bar + ex Bar::foo() ex bar() "#]], ); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs index 1e80d02926d1..6331090d9c90 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs @@ -157,7 +157,7 @@ fn h(x: &Y) -> Y { fn no_false_positive_dyn_fn() { check_diagnostics( r#" -//- minicore: copy, fn +//- minicore: copy, fn, dispatch_from_dyn fn f(x: &mut &mut dyn Fn()) { x(); } @@ -166,7 +166,7 @@ struct X<'a> { field: &'a mut dyn Fn(), } -fn f(x: &mut X<'_>) { +fn g(x: &mut X<'_>) { (x.field)(); } "#, diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index a1db92641f5e..a4eb3d47d708 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -92,7 +92,7 @@ use hir::{ use ide_db::{ EditionedFileId, FileId, FileRange, FxHashMap, FxHashSet, RootDatabase, Severity, SnippetCap, assists::{Assist, AssistId, AssistResolveStrategy, ExprFillDefaultMode}, - base_db::{ReleaseChannel, RootQueryDb as _}, + base_db::{ReleaseChannel, RootQueryDb as _, salsa}, generated::lints::{CLIPPY_LINT_GROUPS, DEFAULT_LINT_GROUPS, DEFAULT_LINTS, Lint, LintGroup}, imports::insert_use::InsertUseConfig, label::Label, @@ -537,10 +537,12 @@ pub fn full_diagnostics( resolve: &AssistResolveStrategy, file_id: FileId, ) -> Vec { - let mut res = syntax_diagnostics(db, config, file_id); - let sema = semantic_diagnostics(db, config, resolve, file_id); - res.extend(sema); - res + salsa::attach(db, || { + let mut res = syntax_diagnostics(db, config, file_id); + let sema = semantic_diagnostics(db, config, resolve, file_id); + res.extend(sema); + res + }) } /// Returns whether to keep this diagnostic (or remove it). diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs index 181993154e59..c3cc5a08b56b 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs @@ -2,6 +2,7 @@ mod overly_long_real_world_cases; +use hir::setup_tracing; use ide_db::{ LineIndexDatabase, RootDatabase, assists::{AssistResolveStrategy, ExprFillDefaultMode}, @@ -198,6 +199,8 @@ pub(crate) fn check_diagnostics_with_config( config: DiagnosticsConfig, #[rust_analyzer::rust_fixture] ra_fixture: &str, ) { + let _tracing = setup_tracing(); + let (db, files) = RootDatabase::with_many_files(ra_fixture); let mut annotations = files .iter() diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/resolving.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/resolving.rs index a4e2cfbaee27..1d5f5adf2eef 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/resolving.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/resolving.rs @@ -1,7 +1,7 @@ //! This module is responsible for resolving paths within rules. use hir::AsAssocItem; -use ide_db::FxHashMap; +use ide_db::{FxHashMap, base_db::salsa}; use parsing::Placeholder; use syntax::{ SmolStr, SyntaxKind, SyntaxNode, SyntaxToken, @@ -48,16 +48,20 @@ impl<'db> ResolvedRule<'db> { resolution_scope: &ResolutionScope<'db>, index: usize, ) -> Result, SsrError> { - let resolver = - Resolver { resolution_scope, placeholders_by_stand_in: rule.placeholders_by_stand_in }; - let resolved_template = match rule.template { - Some(template) => Some(resolver.resolve_pattern_tree(template)?), - None => None, - }; - Ok(ResolvedRule { - pattern: resolver.resolve_pattern_tree(rule.pattern)?, - template: resolved_template, - index, + salsa::attach(resolution_scope.scope.db, || { + let resolver = Resolver { + resolution_scope, + placeholders_by_stand_in: rule.placeholders_by_stand_in, + }; + let resolved_template = match rule.template { + Some(template) => Some(resolver.resolve_pattern_tree(template)?), + None => None, + }; + Ok(ResolvedRule { + pattern: resolver.resolve_pattern_tree(rule.pattern)?, + template: resolved_template, + index, + }) }) } diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs index c2f08f8ca2e6..b004c0757674 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs @@ -4,6 +4,7 @@ use expect_test::{Expect, expect}; use hir::Semantics; use ide_db::{ FilePosition, FileRange, RootDatabase, + base_db::salsa, defs::Definition, documentation::{DocsRangeMap, Documentation, HasDocs}, }; @@ -63,8 +64,10 @@ fn check_doc_links(#[rust_analyzer::rust_fixture] ra_fixture: &str) { .flat_map(|(text_range, link, ns)| { let attr = range.map(text_range); let is_inner_attr = attr.map(|(_file, attr)| attr.is_inner_attr()).unwrap_or(false); - let def = resolve_doc_path_for_def(sema.db, cursor_def, &link, ns, is_inner_attr) - .unwrap_or_else(|| panic!("Failed to resolve {link}")); + let def = salsa::attach(sema.db, || { + resolve_doc_path_for_def(sema.db, cursor_def, &link, ns, is_inner_attr) + .unwrap_or_else(|| panic!("Failed to resolve {link}")) + }); def.try_to_nav(sema.db).unwrap().into_iter().zip(iter::repeat(link)) }) .map(|(nav_target, link)| { diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index f768d4b68f46..633feec622cd 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -10,7 +10,7 @@ use hir::{ }; use ide_db::{ RootDatabase, SymbolKind, - base_db::{AnchoredPath, SourceDatabase}, + base_db::{AnchoredPath, SourceDatabase, salsa}, defs::{Definition, IdentClass}, famous_defs::FamousDefs, helpers::pick_best_token, @@ -108,7 +108,7 @@ pub(crate) fn goto_definition( } Some( - IdentClass::classify_node(sema, &parent)? + salsa::attach(sema.db, || IdentClass::classify_node(sema, &parent))? .definitions() .into_iter() .flat_map(|(def, _)| { diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs index 02d96a647328..4f172621908c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs @@ -234,6 +234,7 @@ impl crate::T for crate::Foo {} ); } + // FIXME(next-solver): it would be nice to be able to also point to `&Foo` #[test] fn goto_implementation_all_impls() { check( @@ -246,7 +247,6 @@ impl Foo {} impl T for Foo {} //^^^ impl T for &Foo {} - //^^^^ "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index 44c98a43f694..a48fe43e8080 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -12,6 +12,7 @@ use hir::{ }; use ide_db::{ FileRange, FxIndexSet, Ranker, RootDatabase, + base_db::salsa, defs::{Definition, IdentClass, NameRefClass, OperatorClass}, famous_defs::FamousDefs, helpers::pick_best_token, @@ -290,7 +291,7 @@ fn hover_offset( .into_iter() .unique_by(|&((def, _), _, _, _)| def) .map(|((def, subst), macro_arm, hovered_definition, node)| { - hover_for_definition( + salsa::attach(sema.db, || hover_for_definition( sema, file_id, def, @@ -301,7 +302,7 @@ fn hover_offset( config, edition, display_target, - ) + )) }) .collect::>(), ) diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index 51b5900e8155..1c0272cdfacd 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -10,6 +10,7 @@ use hir::{ }; use ide_db::{ RootDatabase, + base_db::salsa, defs::Definition, documentation::{DocsRangeMap, HasDocs}, famous_defs::FamousDefs, @@ -44,7 +45,7 @@ pub(super) fn type_info_of( Either::Left(expr) => sema.type_of_expr(expr)?, Either::Right(pat) => sema.type_of_pat(pat)?, }; - type_info(sema, _config, ty_info, edition, display_target) + salsa::attach(sema.db, || type_info(sema, _config, ty_info, edition, display_target)) } pub(super) fn closure_expr( diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index c5480217a91e..6f1bbad6ba16 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -6,6 +6,8 @@ use crate::{ HoverConfig, HoverDocFormat, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, fixture, }; +use hir::setup_tracing; + const HOVER_BASE_CONFIG: HoverConfig = HoverConfig { links_in_hover: false, memory_layout: Some(MemoryLayoutHoverConfig { @@ -38,6 +40,7 @@ fn check_hover_no_result(#[rust_analyzer::rust_fixture] ra_fixture: &str) { #[track_caller] fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { + let _tracing = setup_tracing(); let (analysis, position) = fixture::position(ra_fixture); let hover = analysis .hover( diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index 8c2a2f6f72f5..f6416fe51c30 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -8,7 +8,9 @@ use hir::{ ClosureStyle, DisplayTarget, EditionedFileId, HasVisibility, HirDisplay, HirDisplayError, HirWrite, InRealFile, ModuleDef, ModuleDefId, Semantics, sym, }; -use ide_db::{FileRange, RootDatabase, famous_defs::FamousDefs, text_edit::TextEditBuilder}; +use ide_db::{ + FileRange, RootDatabase, base_db::salsa, famous_defs::FamousDefs, text_edit::TextEditBuilder, +}; use ide_db::{FxHashSet, text_edit::TextEdit}; use itertools::Itertools; use smallvec::{SmallVec, smallvec}; @@ -734,7 +736,7 @@ fn label_of_ty( config: &InlayHintsConfig, display_target: DisplayTarget, ) -> Result<(), HirDisplayError> { - let iter_item_type = hint_iterator(sema, famous_defs, ty); + let iter_item_type = salsa::attach(sema.db, || hint_iterator(sema, famous_defs, ty)); match iter_item_type { Some((iter_trait, item, ty)) => { const LABEL_START: &str = "impl "; diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs index 4d020bac3aad..39554d777795 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs @@ -411,10 +411,9 @@ fn main() { (()) == {()}; // ^^& // ^^^^& - let closure: dyn Fn = || (); + let closure: &dyn Fn = &|| (); + //^^^^^^&* closure(); - //^^^^^^^(& - //^^^^^^^) Struct[0]; //^^^^^^(& //^^^^^^) @@ -507,9 +506,10 @@ fn main() { (()) == {()}; // ^^.& // ^^^^.& - let closure: dyn Fn = || (); + let closure: &dyn Fn = &|| (); + //^^^^^^( + //^^^^^^).*.&. closure(); - //^^^^^^^.& Struct[0]; //^^^^^^.& &mut Struct[0]; diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs index 922e9598aa01..7a4af4f7549c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs @@ -909,7 +909,7 @@ fn main() { foo(plus_one); let add_mul = bar(|x: u8| { x + 1 }); - // ^^^^^^^ impl FnOnce(u8) -> u8 + ?Sized + // ^^^^^^^ impl FnOnce(u8) -> u8 let closure = if let Some(6) = add_mul(2).checked_sub(1) { // ^^^^^^^ fn(i32) -> i32 diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs index 382573b68011..2cccb9639f3e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs +++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs @@ -733,7 +733,7 @@ fn signature_help_for_tuple_pat_ish<'db>( mod tests { use expect_test::{Expect, expect}; - use ide_db::FilePosition; + use ide_db::{FilePosition, base_db::salsa}; use stdx::format_to; use test_fixture::ChangeFixture; @@ -762,7 +762,7 @@ mod tests { "# ); let (db, position) = position(&fixture); - let sig_help = crate::signature_help::signature_help(&db, position); + let sig_help = salsa::attach(&db, || crate::signature_help::signature_help(&db, position)); let actual = match sig_help { Some(sig_help) => { let mut rendered = String::new(); diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs index 3ca172977cb9..e3dc3ed3c742 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs @@ -16,7 +16,7 @@ use std::ops::ControlFlow; use either::Either; use hir::{DefWithBody, EditionedFileId, InFile, InRealFile, MacroKind, Name, Semantics}; -use ide_db::{FxHashMap, FxHashSet, Ranker, RootDatabase, SymbolKind}; +use ide_db::{FxHashMap, FxHashSet, Ranker, RootDatabase, SymbolKind, base_db::salsa}; use syntax::{ AstNode, AstToken, NodeOrToken, SyntaxKind::*, @@ -434,15 +434,17 @@ fn traverse( |node| unsafe_ops.contains(&InFile::new(descended_element.file_id, node)); let element = match descended_element.value { NodeOrToken::Node(name_like) => { - let hl = highlight::name_like( - sema, - krate, - bindings_shadow_count, - &is_unsafe_node, - config.syntactic_name_ref_highlighting, - name_like, - edition, - ); + let hl = salsa::attach(sema.db, || { + highlight::name_like( + sema, + krate, + bindings_shadow_count, + &is_unsafe_node, + config.syntactic_name_ref_highlighting, + name_like, + edition, + ) + }); if hl.is_some() && !in_macro { // skip highlighting the contained token of our name-like node // as that would potentially overwrite our result diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html index 00925bd81ed8..d99b29cfb8fa 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html @@ -127,9 +127,9 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd let y = &mut x; let z = &y; - let Foo { x: z, y } = Foo { x: z, y }; + let Foo { x: z, y } = Foo { x: z, y }; - y; + y; let mut foo = Foo { x, y: x }; let foo2 = Foo { x, y: x }; @@ -142,7 +142,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd copy.qux(); copy.baz(copy); - let a = |x| x; + let a = |x| x; let bar = Foo::baz; let baz = (-42,); @@ -172,13 +172,13 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd } async fn learn_and_sing() { - let song = learn_song().await; - sing_song(song).await; + let song = learn_song().await; + sing_song(song).await; } async fn async_main() { let f1 = learn_and_sing(); - let f2 = dance(); + let f2 = dance(); futures::join!(f1, f2); } diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index 4780743c4d92..983ed3684a4f 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -178,6 +178,8 @@ define_symbols! { core, coroutine_state, coroutine, + coroutine_return, + coroutine_yield, count, crate_type, CStr, @@ -226,6 +228,8 @@ define_symbols! { async_fn_once_output, async_fn_mut, async_fn, + call_ref_future, + call_once_future, fn_ptr_addr, fn_ptr_trait, format_alignment, @@ -416,6 +420,7 @@ define_symbols! { rustc_allow_incoherent_impl, rustc_builtin_macro, rustc_coherence_is_core, + rustc_coinductive, rustc_const_panic_str, rustc_deallocator, rustc_deprecated_safe_2024, diff --git a/src/tools/rust-analyzer/crates/profile/src/stop_watch.rs b/src/tools/rust-analyzer/crates/profile/src/stop_watch.rs index 9f3e636ef816..00c37c01d25e 100644 --- a/src/tools/rust-analyzer/crates/profile/src/stop_watch.rs +++ b/src/tools/rust-analyzer/crates/profile/src/stop_watch.rs @@ -37,10 +37,10 @@ impl StopWatch { .build() .map_err(|err| eprintln!("Failed to create perf counter: {err}")) .ok(); - if let Some(counter) = &mut counter { - if let Err(err) = counter.enable() { - eprintln!("Failed to start perf counter: {err}") - } + if let Some(counter) = &mut counter + && let Err(err) = counter.enable() + { + eprintln!("Failed to start perf counter: {err}") } counter } else { diff --git a/src/tools/rust-analyzer/crates/query-group-macro/src/queries.rs b/src/tools/rust-analyzer/crates/query-group-macro/src/queries.rs index c151cca07272..22a26c49fa53 100644 --- a/src/tools/rust-analyzer/crates/query-group-macro/src/queries.rs +++ b/src/tools/rust-analyzer/crates/query-group-macro/src/queries.rs @@ -48,7 +48,8 @@ impl ToTokens for TrackedQuery { quote!(#(#options),*) }) .into_iter() - .chain(self.lru.map(|lru| quote!(lru = #lru))); + .chain(self.lru.map(|lru| quote!(lru = #lru))) + .chain(Some(quote!(unsafe(non_update_return_type)))); let annotation = quote!(#[salsa_macros::tracked( #(#options),* )]); let pat_and_tys = &self.pat_and_tys; diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index 7b719b5dec75..b735b323ce43 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -35,7 +35,7 @@ //! error: fmt //! fmt: option, result, transmute, coerce_unsized, copy, clone, derive //! fmt_before_1_89_0: fmt -//! fn: tuple +//! fn: sized, tuple //! from: sized, result //! future: pin //! coroutine: pin @@ -325,6 +325,18 @@ pub mod clone { *self } } + + impl Clone for [T; 0] { + fn clone(&self) -> Self { + [] + } + } + + impl Clone for [T; 1] { + fn clone(&self) -> Self { + [self[0].clone()] + } + } // endregion:builtin_impls // region:derive @@ -1035,6 +1047,7 @@ pub mod ops { #[lang = "coroutine"] pub trait Coroutine { + #[lang = "coroutine_yield"] type Yield; #[lang = "coroutine_return"] type Return; From 6b8a812aafaad6b0edd04d12569fe43ee1e69eed Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Sun, 10 Aug 2025 01:38:17 -0400 Subject: [PATCH 037/682] parser: fix parsing of trait bound polarity and for-binders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rustc AST allows both `for<>` binders and `?` polarity modifiers in trait bounds, but they are parsed in a specific order and validated for correctness: 1. `for<>` binder is parsed first. 2. Polarity modifiers (`?`, `!`) are parsed second. 3. The parser validates that binders and polarity modifiers do not conflict: ```rust if let Some(binder_span) = binder_span { match modifiers.polarity { BoundPolarity::Maybe(polarity_span) => { // Error: "for<...> binder not allowed with ? polarity" } } } ``` This implies: - `for<> ?Sized` → Valid syntax. Invalid semantics. - `?for<> Sized` → Invalid syntax. However, rust-analyzer incorrectly had special-case logic that allowed `?for<>` as valid syntax. This fix removes that incorrect special case, making rust-analyzer reject `?for<> Sized` as a syntax error, matching rustc behavior. This has caused confusion in other crates (such as syn) which rely on these files to implement correct syntax evaluation. --- .../parser/src/grammar/generic_params.rs | 11 ++--- .../parser/test_data/generated/runner.rs | 6 +++ ...invalid_question_for_type_trait_bound.rast | 49 +++++++++++++++++++ .../invalid_question_for_type_trait_bound.rs | 1 + .../ok/question_for_type_trait_bound.rast | 23 +++++---- .../ok/question_for_type_trait_bound.rs | 2 +- 6 files changed, 73 insertions(+), 19 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/invalid_question_for_type_trait_bound.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/invalid_question_for_type_trait_bound.rs diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs index cb1b59f64977..d419817e5cd7 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs @@ -182,12 +182,6 @@ fn type_bound(p: &mut Parser<'_>) -> bool { ); m.complete(p, USE_BOUND_GENERIC_ARGS); } - T![?] if p.nth_at(1, T![for]) => { - // test question_for_type_trait_bound - // fn f() where T: ?for<> Sized {} - p.bump_any(); - types::for_type(p, false) - } _ => { if path_type_bound(p).is_err() { m.abandon(p); @@ -219,8 +213,13 @@ fn path_type_bound(p: &mut Parser<'_>) -> Result<(), ()> { // test async_trait_bound // fn async_foo(_: impl async Fn(&i32)) {} p.eat(T![async]); + // test question_for_type_trait_bound + // fn f() where T: for<> ?Sized {} p.eat(T![?]); + // test_err invalid_question_for_type_trait_bound + // fn f() where T: ?for<> Sized {} + if paths::is_use_path_start(p) { types::path_type_bounds(p, false); // test_err type_bounds_macro_call_recovery diff --git a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs index c642e1a3354f..a3cfe64e6e73 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs @@ -796,6 +796,12 @@ mod err { #[test] fn impl_type() { run_and_expect_errors("test_data/parser/inline/err/impl_type.rs"); } #[test] + fn invalid_question_for_type_trait_bound() { + run_and_expect_errors( + "test_data/parser/inline/err/invalid_question_for_type_trait_bound.rs", + ); + } + #[test] fn let_else_right_curly_brace() { run_and_expect_errors("test_data/parser/inline/err/let_else_right_curly_brace.rs"); } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/invalid_question_for_type_trait_bound.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/invalid_question_for_type_trait_bound.rast new file mode 100644 index 000000000000..b060ee81d617 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/invalid_question_for_type_trait_bound.rast @@ -0,0 +1,49 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "f" + GENERIC_PARAM_LIST + L_ANGLE "<" + TYPE_PARAM + NAME + IDENT "T" + R_ANGLE ">" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + WHERE_CLAUSE + WHERE_KW "where" + WHITESPACE " " + WHERE_PRED + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "T" + COLON ":" + WHITESPACE " " + TYPE_BOUND_LIST + QUESTION "?" + WHERE_PRED + FOR_BINDER + FOR_KW "for" + GENERIC_PARAM_LIST + L_ANGLE "<" + R_ANGLE ">" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Sized" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + R_CURLY "}" + WHITESPACE "\n" +error 20: expected comma +error 31: expected colon diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/invalid_question_for_type_trait_bound.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/invalid_question_for_type_trait_bound.rs new file mode 100644 index 000000000000..f80dd90d446c --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/invalid_question_for_type_trait_bound.rs @@ -0,0 +1 @@ +fn f() where T: ?for<> Sized {} diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/question_for_type_trait_bound.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/question_for_type_trait_bound.rast index cb296153c8f1..69db1aee2c5a 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/question_for_type_trait_bound.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/question_for_type_trait_bound.rast @@ -27,19 +27,18 @@ SOURCE_FILE WHITESPACE " " TYPE_BOUND_LIST TYPE_BOUND + FOR_BINDER + FOR_KW "for" + GENERIC_PARAM_LIST + L_ANGLE "<" + R_ANGLE ">" + WHITESPACE " " QUESTION "?" - FOR_TYPE - FOR_BINDER - FOR_KW "for" - GENERIC_PARAM_LIST - L_ANGLE "<" - R_ANGLE ">" - WHITESPACE " " - PATH_TYPE - PATH - PATH_SEGMENT - NAME_REF - IDENT "Sized" + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Sized" WHITESPACE " " BLOCK_EXPR STMT_LIST diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/question_for_type_trait_bound.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/question_for_type_trait_bound.rs index f80dd90d446c..96353df3b9b3 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/question_for_type_trait_bound.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/question_for_type_trait_bound.rs @@ -1 +1 @@ -fn f() where T: ?for<> Sized {} +fn f() where T: for<> ?Sized {} From 8c0f4b40803a6d0c6ed6525026217c8d2532d205 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 10 Aug 2025 14:37:35 +0800 Subject: [PATCH 038/682] Fix extract_expressions_from_format_string on write! **Input**: ```rust fn main() { write!(f, "{2+3}$0") } ``` **Old output**: ```rust fn main() { write!("{}"$0, 2+3) } ``` **This PR output**: ```rust fn main() { write!(f, "{}"$0, 2+3) } ``` --- .../extract_expressions_from_format_string.rs | 46 ++++++++++++++++--- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs index cdc0e967101a..e3c7ea1b0939 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs @@ -7,8 +7,8 @@ use itertools::Itertools; use syntax::{ AstNode, AstToken, NodeOrToken, SyntaxKind::WHITESPACE, - T, - ast::{self, make, syntax_factory::SyntaxFactory}, + SyntaxToken, T, + ast::{self, TokenTree, make, syntax_factory::SyntaxFactory}, }; // Assist: extract_expressions_from_format_string @@ -58,10 +58,11 @@ pub(crate) fn extract_expressions_from_format_string( tt.syntax().text_range(), |edit| { // Extract existing arguments in macro - let tokens = tt.token_trees_and_tokens().collect_vec(); + let mut raw_tokens = tt.token_trees_and_tokens().skip(1).collect_vec(); + let format_string_index = format_str_index(&raw_tokens, &fmt_string); + let tokens = raw_tokens.split_off(format_string_index); let existing_args = if let [ - _opening_bracket, NodeOrToken::Token(_format_string), _args_start_comma, tokens @ .., @@ -90,9 +91,11 @@ pub(crate) fn extract_expressions_from_format_string( // Start building the new args let mut existing_args = existing_args.into_iter(); - let mut new_tt_bits = vec![NodeOrToken::Token(make::tokens::literal(&new_fmt))]; + let mut new_tt_bits = raw_tokens; let mut placeholder_indexes = vec![]; + new_tt_bits.push(NodeOrToken::Token(make::tokens::literal(&new_fmt))); + for arg in extracted_args { if matches!(arg, Arg::Expr(_) | Arg::Placeholder) { // insert ", " before each arg @@ -150,7 +153,9 @@ pub(crate) fn extract_expressions_from_format_string( } // Add the final tabstop after the format literal - if let Some(NodeOrToken::Token(literal)) = new_tt.token_trees_and_tokens().nth(1) { + if let Some(NodeOrToken::Token(literal)) = + new_tt.token_trees_and_tokens().nth(1 + format_string_index) + { let annotation = edit.make_tabstop_after(cap); editor.add_annotation(literal, annotation); } @@ -163,6 +168,17 @@ pub(crate) fn extract_expressions_from_format_string( Some(()) } +fn format_str_index( + raw_tokens: &[NodeOrToken], + fmt_string: &ast::String, +) -> usize { + let fmt_string = fmt_string.syntax(); + raw_tokens + .iter() + .position(|tt| tt.as_token().is_some_and(|tt| tt == fmt_string)) + .unwrap_or_default() +} + #[cfg(test)] mod tests { use super::*; @@ -186,6 +202,24 @@ fn main() { ); } + #[test] + fn multiple_middle_arg_on_write() { + check_assist( + extract_expressions_from_format_string, + r#" +//- minicore: write +fn main() { + write!(writer(), "{} {x + 1:b} {}$0", y + 2, 2); +} +"#, + r#" +fn main() { + write!(writer(), "{} {:b} {}"$0, y + 2, x + 1, 2); +} +"#, + ); + } + #[test] fn single_arg() { check_assist( From b9f2bb7dd11f23c7922fdc024a270bf8a4675616 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Sun, 10 Aug 2025 16:43:36 +0900 Subject: [PATCH 039/682] internal: Make flycheck generational --- .../crates/rust-analyzer/src/diagnostics.rs | 47 ++++++-- .../crates/rust-analyzer/src/flycheck.rs | 112 ++++++++++++++---- .../crates/rust-analyzer/src/main_loop.rs | 24 +++- .../crates/rust-analyzer/src/reload.rs | 3 + 4 files changed, 148 insertions(+), 38 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs index 438a2a0ba1ea..cd7c632d105e 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs @@ -26,6 +26,19 @@ pub struct DiagnosticsMapConfig { pub(crate) type DiagnosticsGeneration = usize; +#[derive(Debug, Clone)] +pub(crate) struct WorkspaceFlycheckDiagnostic { + pub(crate) generation: DiagnosticsGeneration, + pub(crate) per_package: + FxHashMap>, FxHashMap>>, +} + +impl WorkspaceFlycheckDiagnostic { + fn new(generation: DiagnosticsGeneration) -> Self { + WorkspaceFlycheckDiagnostic { generation, per_package: Default::default() } + } +} + #[derive(Debug, Default, Clone)] pub(crate) struct DiagnosticCollection { // FIXME: should be FxHashMap> @@ -33,9 +46,7 @@ pub(crate) struct DiagnosticCollection { FxHashMap)>, pub(crate) native_semantic: FxHashMap)>, - // FIXME: should be Vec - pub(crate) check: - Vec>, FxHashMap>>>, + pub(crate) check: Vec, pub(crate) check_fixes: CheckFixes, changes: FxHashSet, /// Counter for supplying a new generation number for diagnostics. @@ -57,7 +68,7 @@ impl DiagnosticCollection { let Some(check) = self.check.get_mut(flycheck_id) else { return; }; - self.changes.extend(check.drain().flat_map(|(_, v)| v.into_keys())); + self.changes.extend(check.per_package.drain().flat_map(|(_, v)| v.into_keys())); if let Some(fixes) = Arc::make_mut(&mut self.check_fixes).get_mut(flycheck_id) { fixes.clear(); } @@ -66,7 +77,9 @@ impl DiagnosticCollection { pub(crate) fn clear_check_all(&mut self) { Arc::make_mut(&mut self.check_fixes).clear(); self.changes.extend( - self.check.iter_mut().flat_map(|it| it.drain().flat_map(|(_, v)| v.into_keys())), + self.check + .iter_mut() + .flat_map(|it| it.per_package.drain().flat_map(|(_, v)| v.into_keys())), ) } @@ -79,7 +92,7 @@ impl DiagnosticCollection { return; }; let package_id = Some(package_id); - if let Some(checks) = check.remove(&package_id) { + if let Some(checks) = check.per_package.remove(&package_id) { self.changes.extend(checks.into_keys()); } if let Some(fixes) = Arc::make_mut(&mut self.check_fixes).get_mut(flycheck_id) { @@ -87,6 +100,16 @@ impl DiagnosticCollection { } } + pub(crate) fn clear_check_older_than( + &mut self, + flycheck_id: usize, + generation: DiagnosticsGeneration, + ) { + if self.check[flycheck_id].generation < generation { + self.clear_check(flycheck_id); + } + } + pub(crate) fn clear_native_for(&mut self, file_id: FileId) { self.native_syntax.remove(&file_id); self.native_semantic.remove(&file_id); @@ -96,15 +119,23 @@ impl DiagnosticCollection { pub(crate) fn add_check_diagnostic( &mut self, flycheck_id: usize, + generation: DiagnosticsGeneration, package_id: &Option>, file_id: FileId, diagnostic: lsp_types::Diagnostic, fix: Option>, ) { if self.check.len() <= flycheck_id { - self.check.resize_with(flycheck_id + 1, Default::default); + self.check + .resize_with(flycheck_id + 1, || WorkspaceFlycheckDiagnostic::new(generation)); + } + + // Getting message from old generation. Might happen in restarting checks. + if self.check[flycheck_id].generation > generation { + return; } let diagnostics = self.check[flycheck_id] + .per_package .entry(package_id.clone()) .or_default() .entry(file_id) @@ -177,7 +208,7 @@ impl DiagnosticCollection { let check = self .check .iter() - .flat_map(|it| it.values()) + .flat_map(|it| it.per_package.values()) .filter_map(move |it| it.get(&file_id)) .flatten(); native_syntax.chain(native_semantic).chain(check) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index e4e0bcdc1cd0..233bedec6900 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -1,7 +1,12 @@ //! Flycheck provides the functionality needed to run `cargo check` to provide //! LSP diagnostics based on the output of the command. -use std::{fmt, io, process::Command, time::Duration}; +use std::{ + fmt, io, + process::Command, + sync::atomic::{AtomicUsize, Ordering}, + time::Duration, +}; use cargo_metadata::PackageId; use crossbeam_channel::{Receiver, Sender, select_biased, unbounded}; @@ -18,7 +23,10 @@ pub(crate) use cargo_metadata::diagnostic::{ use toolchain::Tool; use triomphe::Arc; -use crate::command::{CargoParser, CommandHandle}; +use crate::{ + command::{CargoParser, CommandHandle}, + diagnostics::DiagnosticsGeneration, +}; #[derive(Clone, Debug, Default, PartialEq, Eq)] pub(crate) enum InvocationStrategy { @@ -138,36 +146,54 @@ pub(crate) struct FlycheckHandle { sender: Sender, _thread: stdx::thread::JoinHandle, id: usize, + generation: AtomicUsize, } impl FlycheckHandle { pub(crate) fn spawn( id: usize, + generation: DiagnosticsGeneration, sender: Sender, config: FlycheckConfig, sysroot_root: Option, workspace_root: AbsPathBuf, manifest_path: Option, ) -> FlycheckHandle { - let actor = - FlycheckActor::new(id, sender, config, sysroot_root, workspace_root, manifest_path); + let actor = FlycheckActor::new( + id, + generation, + sender, + config, + sysroot_root, + workspace_root, + manifest_path, + ); let (sender, receiver) = unbounded::(); let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker, format!("Flycheck{id}")) .spawn(move || actor.run(receiver)) .expect("failed to spawn thread"); - FlycheckHandle { id, sender, _thread: thread } + FlycheckHandle { id, generation: generation.into(), sender, _thread: thread } } /// Schedule a re-start of the cargo check worker to do a workspace wide check. pub(crate) fn restart_workspace(&self, saved_file: Option) { - self.sender.send(StateChange::Restart { package: None, saved_file, target: None }).unwrap(); + let generation = self.generation.fetch_add(1, Ordering::Relaxed) + 1; + self.sender + .send(StateChange::Restart { generation, package: None, saved_file, target: None }) + .unwrap(); } /// Schedule a re-start of the cargo check worker to do a package wide check. pub(crate) fn restart_for_package(&self, package: String, target: Option) { + let generation = self.generation.fetch_add(1, Ordering::Relaxed) + 1; self.sender - .send(StateChange::Restart { package: Some(package), saved_file: None, target }) + .send(StateChange::Restart { + generation, + package: Some(package), + saved_file: None, + target, + }) .unwrap(); } @@ -179,23 +205,31 @@ impl FlycheckHandle { pub(crate) fn id(&self) -> usize { self.id } + + pub(crate) fn generation(&self) -> DiagnosticsGeneration { + self.generation.load(Ordering::Relaxed) + } +} + +#[derive(Debug)] +pub(crate) enum ClearDiagnosticsKind { + All, + OlderThan(DiagnosticsGeneration), + Package(Arc), } pub(crate) enum FlycheckMessage { /// Request adding a diagnostic with fixes included to a file AddDiagnostic { id: usize, + generation: DiagnosticsGeneration, workspace_root: Arc, diagnostic: Diagnostic, package_id: Option>, }, /// Request clearing all outdated diagnostics. - ClearDiagnostics { - id: usize, - /// The package whose diagnostics to clear, or if unspecified, all diagnostics. - package_id: Option>, - }, + ClearDiagnostics { id: usize, kind: ClearDiagnosticsKind }, /// Request check progress notification to client Progress { @@ -208,18 +242,23 @@ pub(crate) enum FlycheckMessage { impl fmt::Debug for FlycheckMessage { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - FlycheckMessage::AddDiagnostic { id, workspace_root, diagnostic, package_id } => f + FlycheckMessage::AddDiagnostic { + id, + generation, + workspace_root, + diagnostic, + package_id, + } => f .debug_struct("AddDiagnostic") .field("id", id) + .field("generation", generation) .field("workspace_root", workspace_root) .field("package_id", package_id) .field("diagnostic_code", &diagnostic.code.as_ref().map(|it| &it.code)) .finish(), - FlycheckMessage::ClearDiagnostics { id, package_id } => f - .debug_struct("ClearDiagnostics") - .field("id", id) - .field("package_id", package_id) - .finish(), + FlycheckMessage::ClearDiagnostics { id, kind } => { + f.debug_struct("ClearDiagnostics").field("id", id).field("kind", kind).finish() + } FlycheckMessage::Progress { id, progress } => { f.debug_struct("Progress").field("id", id).field("progress", progress).finish() } @@ -237,7 +276,12 @@ pub(crate) enum Progress { } enum StateChange { - Restart { package: Option, saved_file: Option, target: Option }, + Restart { + generation: DiagnosticsGeneration, + package: Option, + saved_file: Option, + target: Option, + }, Cancel, } @@ -246,6 +290,7 @@ struct FlycheckActor { /// The workspace id of this flycheck instance. id: usize, + generation: DiagnosticsGeneration, sender: Sender, config: FlycheckConfig, manifest_path: Option, @@ -283,6 +328,7 @@ pub(crate) const SAVED_FILE_PLACEHOLDER: &str = "$saved_file"; impl FlycheckActor { fn new( id: usize, + generation: DiagnosticsGeneration, sender: Sender, config: FlycheckConfig, sysroot_root: Option, @@ -292,6 +338,7 @@ impl FlycheckActor { tracing::info!(%id, ?workspace_root, "Spawning flycheck"); FlycheckActor { id, + generation, sender, config, sysroot_root, @@ -327,7 +374,12 @@ impl FlycheckActor { tracing::debug!(flycheck_id = self.id, "flycheck cancelled"); self.cancel_check_process(); } - Event::RequestStateChange(StateChange::Restart { package, saved_file, target }) => { + Event::RequestStateChange(StateChange::Restart { + generation, + package, + saved_file, + target, + }) => { // Cancel the previously spawned process self.cancel_check_process(); while let Ok(restart) = inbox.recv_timeout(Duration::from_millis(50)) { @@ -337,6 +389,8 @@ impl FlycheckActor { } } + self.generation = generation; + let Some(command) = self.check_command(package.as_deref(), saved_file.as_deref(), target) else { @@ -383,7 +437,16 @@ impl FlycheckActor { // Clear everything for good measure self.send(FlycheckMessage::ClearDiagnostics { id: self.id, - package_id: None, + kind: ClearDiagnosticsKind::All, + }); + } else if res.is_ok() { + // We clear diagnostics for packages on + // `[CargoCheckMessage::CompilerArtifact]` but there seem to be setups where + // cargo may not report an artifact to our runner at all. To handle such + // cases, clear stale diagnostics when flycheck completes successfully. + self.send(FlycheckMessage::ClearDiagnostics { + id: self.id, + kind: ClearDiagnosticsKind::OlderThan(self.generation), }); } self.clear_diagnostics_state(); @@ -412,7 +475,7 @@ impl FlycheckActor { ); self.send(FlycheckMessage::ClearDiagnostics { id: self.id, - package_id: Some(package_id), + kind: ClearDiagnosticsKind::Package(package_id), }); } } @@ -435,7 +498,7 @@ impl FlycheckActor { ); self.send(FlycheckMessage::ClearDiagnostics { id: self.id, - package_id: Some(package_id.clone()), + kind: ClearDiagnosticsKind::Package(package_id.clone()), }); } } else if self.diagnostics_received @@ -444,11 +507,12 @@ impl FlycheckActor { self.diagnostics_received = DiagnosticsReceived::YesAndClearedForAll; self.send(FlycheckMessage::ClearDiagnostics { id: self.id, - package_id: None, + kind: ClearDiagnosticsKind::All, }); } self.send(FlycheckMessage::AddDiagnostic { id: self.id, + generation: self.generation, package_id, workspace_root: self.root.clone(), diagnostic, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index f5932d8cff02..c6762f318326 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -20,7 +20,7 @@ use crate::{ config::Config, diagnostics::{DiagnosticsGeneration, NativeDiagnosticsFetchKind, fetch_native_diagnostics}, discover::{DiscoverArgument, DiscoverCommand, DiscoverProjectMessage}, - flycheck::{self, FlycheckMessage}, + flycheck::{self, ClearDiagnosticsKind, FlycheckMessage}, global_state::{ FetchBuildDataResponse, FetchWorkspaceRequest, FetchWorkspaceResponse, GlobalState, file_id_to_url, url_to_file_id, @@ -1008,7 +1008,13 @@ impl GlobalState { fn handle_flycheck_msg(&mut self, message: FlycheckMessage) { match message { - FlycheckMessage::AddDiagnostic { id, workspace_root, diagnostic, package_id } => { + FlycheckMessage::AddDiagnostic { + id, + generation, + workspace_root, + diagnostic, + package_id, + } => { let snap = self.snapshot(); let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp( &self.config.diagnostics_map(None), @@ -1020,6 +1026,7 @@ impl GlobalState { match url_to_file_id(&self.vfs.read().0, &diag.url) { Ok(Some(file_id)) => self.diagnostics.add_check_diagnostic( id, + generation, &package_id, file_id, diag.diagnostic, @@ -1035,12 +1042,17 @@ impl GlobalState { }; } } - FlycheckMessage::ClearDiagnostics { id, package_id: None } => { + FlycheckMessage::ClearDiagnostics { id, kind: ClearDiagnosticsKind::All } => { self.diagnostics.clear_check(id) } - FlycheckMessage::ClearDiagnostics { id, package_id: Some(package_id) } => { - self.diagnostics.clear_check_for_package(id, package_id) - } + FlycheckMessage::ClearDiagnostics { + id, + kind: ClearDiagnosticsKind::OlderThan(generation), + } => self.diagnostics.clear_check_older_than(id, generation), + FlycheckMessage::ClearDiagnostics { + id, + kind: ClearDiagnosticsKind::Package(package_id), + } => self.diagnostics.clear_check_for_package(id, package_id), FlycheckMessage::Progress { id, progress } => { let (state, message) = match progress { flycheck::Progress::DidStart => (Progress::Begin, None), diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index aa38aa72d44e..f8f29eee126e 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -855,11 +855,13 @@ impl GlobalState { invocation_strategy.clone() } }; + let next_gen = self.flycheck.iter().map(|f| f.generation() + 1).max().unwrap_or_default(); self.flycheck = match invocation_strategy { crate::flycheck::InvocationStrategy::Once => { vec![FlycheckHandle::spawn( 0, + next_gen, sender, config, None, @@ -898,6 +900,7 @@ impl GlobalState { .map(|(id, (root, manifest_path), sysroot_root)| { FlycheckHandle::spawn( id, + next_gen, sender.clone(), config.clone(), sysroot_root, From f2c0c3dd44471e46ebb3823cc751c9c42aabc1aa Mon Sep 17 00:00:00 2001 From: Daniel Paoliello Date: Sun, 10 Aug 2025 11:56:35 -0700 Subject: [PATCH 040/682] Add testing for Arm64EC Windows --- library/stdarch/.github/workflows/main.yml | 10 +- .../core_arch/src/aarch64/neon/generated.rs | 248 ++++++++++++++ .../src/arm_shared/neon/generated.rs | 308 ++++++++++++++++++ .../core_arch/src/arm_shared/neon/mod.rs | 8 +- .../spec/neon/aarch64.spec.yml | 144 ++++++++ .../spec/neon/arm_shared.spec.yml | 111 +++++++ .../crates/stdarch-test/src/disassembly.rs | 6 +- 7 files changed, 823 insertions(+), 12 deletions(-) diff --git a/library/stdarch/.github/workflows/main.yml b/library/stdarch/.github/workflows/main.yml index 048ce9864604..b0d476f0e2ea 100644 --- a/library/stdarch/.github/workflows/main.yml +++ b/library/stdarch/.github/workflows/main.yml @@ -117,6 +117,8 @@ jobs: os: windows-2025 - tuple: aarch64-pc-windows-msvc os: windows-11-arm + - tuple: arm64ec-pc-windows-msvc + os: windows-11-arm - tuple: x86_64-pc-windows-gnu os: windows-2025 # - tuple: i686-pc-windows-gnu @@ -207,14 +209,6 @@ jobs: rustup update nightly --no-self-update rustup default nightly shell: bash - if: matrix.target.os != 'windows-11-arm' - - name: Install Rust for `windows-11-arm` runners - # The arm runners don't have Rust pre-installed (https://github.com/actions/partner-runner-images/issues/77) - run: | - curl https://sh.rustup.rs | sh -s -- -y --default-toolchain nightly - echo "$HOME/.cargo/bin" >> $GITHUB_PATH - shell: bash - if: matrix.target.os == 'windows-11-arm' - run: rustup target add ${{ matrix.target.tuple }} shell: bash diff --git a/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs b/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs index bc4c438038dc..554a809db8db 100644 --- a/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs +++ b/library/stdarch/crates/core_arch/src/aarch64/neon/generated.rs @@ -188,6 +188,7 @@ pub fn vabds_f32(a: f32, b: f32) -> f32 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fabd))] pub fn vabdh_f16(a: f16, b: f16) -> f16 { unsafe { simd_extract!(vabd_f16(vdup_n_f16(a), vdup_n_f16(b)), 0) } @@ -947,6 +948,7 @@ pub fn vbcaxq_u64(a: uint64x2_t, b: uint64x2_t, c: uint64x2_t) -> uint64x2_t { #[target_feature(enable = "neon,fp16")] #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fcma"))] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fcadd))] pub fn vcadd_rot270_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { @@ -964,6 +966,7 @@ pub fn vcadd_rot270_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { #[target_feature(enable = "neon,fp16")] #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fcma"))] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fcadd))] pub fn vcaddq_rot270_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { @@ -1029,6 +1032,7 @@ pub fn vcaddq_rot270_f64(a: float64x2_t, b: float64x2_t) -> float64x2_t { #[target_feature(enable = "neon,fp16")] #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fcma"))] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fcadd))] pub fn vcadd_rot90_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { @@ -1046,6 +1050,7 @@ pub fn vcadd_rot90_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { #[target_feature(enable = "neon,fp16")] #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fcma"))] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fcadd))] pub fn vcaddq_rot90_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { @@ -1175,6 +1180,7 @@ pub fn vcages_f32(a: f32, b: f32) -> u32 { #[cfg_attr(test, assert_instr(facge))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcageh_f16(a: f16, b: f16) -> u16 { unsafe extern "unadjusted" { #[cfg_attr( @@ -1255,6 +1261,7 @@ pub fn vcagts_f32(a: f32, b: f32) -> u32 { #[cfg_attr(test, assert_instr(facgt))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcagth_f16(a: f16, b: f16) -> u16 { unsafe extern "unadjusted" { #[cfg_attr( @@ -1307,6 +1314,7 @@ pub fn vcales_f32(a: f32, b: f32) -> u32 { #[cfg_attr(test, assert_instr(facge))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcaleh_f16(a: f16, b: f16) -> u16 { vcageh_f16(b, a) } @@ -1352,6 +1360,7 @@ pub fn vcalts_f32(a: f32, b: f32) -> u32 { #[cfg_attr(test, assert_instr(facgt))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcalth_f16(a: f16, b: f16) -> u16 { vcagth_f16(b, a) } @@ -1469,6 +1478,7 @@ pub fn vceqd_u64(a: u64, b: u64) -> u64 { #[cfg_attr(test, assert_instr(fcmp))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vceqh_f16(a: f16, b: f16) -> u16 { unsafe { simd_extract!(vceq_f16(vdup_n_f16(a), vdup_n_f16(b)), 0) } } @@ -1478,6 +1488,7 @@ pub fn vceqh_f16(a: f16, b: f16) -> u16 { #[cfg_attr(test, assert_instr(fcmeq))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vceqz_f16(a: float16x4_t) -> uint16x4_t { let b: f16x4 = f16x4::new(0.0, 0.0, 0.0, 0.0); unsafe { simd_eq(a, transmute(b)) } @@ -1488,6 +1499,7 @@ pub fn vceqz_f16(a: float16x4_t) -> uint16x4_t { #[cfg_attr(test, assert_instr(fcmeq))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vceqzq_f16(a: float16x8_t) -> uint16x8_t { let b: f16x8 = f16x8::new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); unsafe { simd_eq(a, transmute(b)) } @@ -1756,6 +1768,7 @@ pub fn vceqzd_u64(a: u64) -> u64 { #[cfg_attr(test, assert_instr(fcmp))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vceqzh_f16(a: f16) -> u16 { unsafe { simd_extract!(vceqz_f16(vdup_n_f16(a)), 0) } } @@ -1873,6 +1886,7 @@ pub fn vcged_u64(a: u64, b: u64) -> u64 { #[cfg_attr(test, assert_instr(fcmp))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcgeh_f16(a: f16, b: f16) -> u16 { unsafe { simd_extract!(vcge_f16(vdup_n_f16(a), vdup_n_f16(b)), 0) } } @@ -2029,6 +2043,7 @@ pub fn vcgezd_s64(a: i64) -> u64 { #[cfg_attr(test, assert_instr(fcmp))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcgezh_f16(a: f16) -> u16 { unsafe { simd_extract!(vcgez_f16(vdup_n_f16(a)), 0) } } @@ -2128,6 +2143,7 @@ pub fn vcgtd_u64(a: u64, b: u64) -> u64 { #[cfg_attr(test, assert_instr(fcmp))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcgth_f16(a: f16, b: f16) -> u16 { unsafe { simd_extract!(vcgt_f16(vdup_n_f16(a), vdup_n_f16(b)), 0) } } @@ -2284,6 +2300,7 @@ pub fn vcgtzd_s64(a: i64) -> u64 { #[cfg_attr(test, assert_instr(fcmp))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcgtzh_f16(a: f16) -> u16 { unsafe { simd_extract!(vcgtz_f16(vdup_n_f16(a)), 0) } } @@ -2383,6 +2400,7 @@ pub fn vcled_s64(a: i64, b: i64) -> u64 { #[cfg_attr(test, assert_instr(fcmp))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcleh_f16(a: f16, b: f16) -> u16 { unsafe { simd_extract!(vcle_f16(vdup_n_f16(a), vdup_n_f16(b)), 0) } } @@ -2539,6 +2557,7 @@ pub fn vclezd_s64(a: i64) -> u64 { #[cfg_attr(test, assert_instr(fcmp))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vclezh_f16(a: f16) -> u16 { unsafe { simd_extract!(vclez_f16(vdup_n_f16(a)), 0) } } @@ -2620,6 +2639,7 @@ pub fn vcltd_s64(a: i64, b: i64) -> u64 { #[cfg_attr(test, assert_instr(fcmp))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vclth_f16(a: f16, b: f16) -> u16 { unsafe { simd_extract!(vclt_f16(vdup_n_f16(a), vdup_n_f16(b)), 0) } } @@ -2794,6 +2814,7 @@ pub fn vcltzd_s64(a: i64) -> u64 { #[cfg_attr(test, assert_instr(fcmp))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcltzh_f16(a: f16) -> u16 { unsafe { simd_extract!(vcltz_f16(vdup_n_f16(a)), 0) } } @@ -2803,6 +2824,7 @@ pub fn vcltzh_f16(a: f16) -> u16 { #[target_feature(enable = "neon,fcma")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fcmla))] pub fn vcmla_f16(a: float16x4_t, b: float16x4_t, c: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { @@ -2820,6 +2842,7 @@ pub fn vcmla_f16(a: float16x4_t, b: float16x4_t, c: float16x4_t) -> float16x4_t #[target_feature(enable = "neon,fcma")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fcmla))] pub fn vcmlaq_f16(a: float16x8_t, b: float16x8_t, c: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { @@ -2887,6 +2910,7 @@ pub fn vcmlaq_f64(a: float64x2_t, b: float64x2_t, c: float64x2_t) -> float64x2_t #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcmla_lane_f16( a: float16x4_t, b: float16x4_t, @@ -2915,6 +2939,7 @@ pub fn vcmla_lane_f16( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcmlaq_lane_f16( a: float16x8_t, b: float16x8_t, @@ -2992,6 +3017,7 @@ pub fn vcmlaq_lane_f32( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcmla_laneq_f16( a: float16x4_t, b: float16x4_t, @@ -3020,6 +3046,7 @@ pub fn vcmla_laneq_f16( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcmlaq_laneq_f16( a: float16x8_t, b: float16x8_t, @@ -3095,6 +3122,7 @@ pub fn vcmlaq_laneq_f32( #[target_feature(enable = "neon,fcma")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fcmla))] pub fn vcmla_rot180_f16(a: float16x4_t, b: float16x4_t, c: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { @@ -3112,6 +3140,7 @@ pub fn vcmla_rot180_f16(a: float16x4_t, b: float16x4_t, c: float16x4_t) -> float #[target_feature(enable = "neon,fcma")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fcmla))] pub fn vcmlaq_rot180_f16(a: float16x8_t, b: float16x8_t, c: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { @@ -3179,6 +3208,7 @@ pub fn vcmlaq_rot180_f64(a: float64x2_t, b: float64x2_t, c: float64x2_t) -> floa #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcmla_rot180_lane_f16( a: float16x4_t, b: float16x4_t, @@ -3207,6 +3237,7 @@ pub fn vcmla_rot180_lane_f16( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcmlaq_rot180_lane_f16( a: float16x8_t, b: float16x8_t, @@ -3284,6 +3315,7 @@ pub fn vcmlaq_rot180_lane_f32( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcmla_rot180_laneq_f16( a: float16x4_t, b: float16x4_t, @@ -3312,6 +3344,7 @@ pub fn vcmla_rot180_laneq_f16( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcmlaq_rot180_laneq_f16( a: float16x8_t, b: float16x8_t, @@ -3387,6 +3420,7 @@ pub fn vcmlaq_rot180_laneq_f32( #[target_feature(enable = "neon,fcma")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fcmla))] pub fn vcmla_rot270_f16(a: float16x4_t, b: float16x4_t, c: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { @@ -3404,6 +3438,7 @@ pub fn vcmla_rot270_f16(a: float16x4_t, b: float16x4_t, c: float16x4_t) -> float #[target_feature(enable = "neon,fcma")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fcmla))] pub fn vcmlaq_rot270_f16(a: float16x8_t, b: float16x8_t, c: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { @@ -3471,6 +3506,7 @@ pub fn vcmlaq_rot270_f64(a: float64x2_t, b: float64x2_t, c: float64x2_t) -> floa #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcmla_rot270_lane_f16( a: float16x4_t, b: float16x4_t, @@ -3499,6 +3535,7 @@ pub fn vcmla_rot270_lane_f16( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcmlaq_rot270_lane_f16( a: float16x8_t, b: float16x8_t, @@ -3576,6 +3613,7 @@ pub fn vcmlaq_rot270_lane_f32( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcmla_rot270_laneq_f16( a: float16x4_t, b: float16x4_t, @@ -3604,6 +3642,7 @@ pub fn vcmla_rot270_laneq_f16( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcmlaq_rot270_laneq_f16( a: float16x8_t, b: float16x8_t, @@ -3679,6 +3718,7 @@ pub fn vcmlaq_rot270_laneq_f32( #[target_feature(enable = "neon,fcma")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fcmla))] pub fn vcmla_rot90_f16(a: float16x4_t, b: float16x4_t, c: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { @@ -3696,6 +3736,7 @@ pub fn vcmla_rot90_f16(a: float16x4_t, b: float16x4_t, c: float16x4_t) -> float1 #[target_feature(enable = "neon,fcma")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fcmla))] pub fn vcmlaq_rot90_f16(a: float16x8_t, b: float16x8_t, c: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { @@ -3763,6 +3804,7 @@ pub fn vcmlaq_rot90_f64(a: float64x2_t, b: float64x2_t, c: float64x2_t) -> float #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcmla_rot90_lane_f16( a: float16x4_t, b: float16x4_t, @@ -3791,6 +3833,7 @@ pub fn vcmla_rot90_lane_f16( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcmlaq_rot90_lane_f16( a: float16x8_t, b: float16x8_t, @@ -3868,6 +3911,7 @@ pub fn vcmlaq_rot90_lane_f32( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcmla_rot90_laneq_f16( a: float16x4_t, b: float16x4_t, @@ -3896,6 +3940,7 @@ pub fn vcmla_rot90_laneq_f16( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcmlaq_rot90_laneq_f16( a: float16x8_t, b: float16x8_t, @@ -7161,6 +7206,7 @@ pub fn vcvtq_f64_u64(a: uint64x2_t) -> float64x2_t { #[cfg_attr(test, assert_instr(fcvtn2))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvt_high_f16_f32(a: float16x4_t, b: float32x4_t) -> float16x8_t { vcombine_f16(a, vcvt_f16_f32(b)) } @@ -7170,6 +7216,7 @@ pub fn vcvt_high_f16_f32(a: float16x4_t, b: float32x4_t) -> float16x8_t { #[cfg_attr(test, assert_instr(fcvtl2))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvt_high_f32_f16(a: float16x8_t) -> float32x4_t { vcvt_f32_f16(vget_high_f16(a)) } @@ -7408,6 +7455,7 @@ pub fn vcvtq_u64_f64(a: float64x2_t) -> uint64x2_t { #[cfg_attr(test, assert_instr(fcvtas))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvta_s16_f16(a: float16x4_t) -> int16x4_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -7424,6 +7472,7 @@ pub fn vcvta_s16_f16(a: float16x4_t) -> int16x4_t { #[cfg_attr(test, assert_instr(fcvtas))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtaq_s16_f16(a: float16x8_t) -> int16x8_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -7504,6 +7553,7 @@ pub fn vcvtaq_s64_f64(a: float64x2_t) -> int64x2_t { #[cfg_attr(test, assert_instr(fcvtau))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvta_u16_f16(a: float16x4_t) -> uint16x4_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -7520,6 +7570,7 @@ pub fn vcvta_u16_f16(a: float16x4_t) -> uint16x4_t { #[cfg_attr(test, assert_instr(fcvtau))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtaq_u16_f16(a: float16x8_t) -> uint16x8_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -7600,6 +7651,7 @@ pub fn vcvtaq_u64_f64(a: float64x2_t) -> uint64x2_t { #[cfg_attr(test, assert_instr(fcvtas))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtah_s16_f16(a: f16) -> i16 { vcvtah_s32_f16(a) as i16 } @@ -7609,6 +7661,7 @@ pub fn vcvtah_s16_f16(a: f16) -> i16 { #[cfg_attr(test, assert_instr(fcvtas))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtah_s32_f16(a: f16) -> i32 { unsafe extern "unadjusted" { #[cfg_attr( @@ -7625,6 +7678,7 @@ pub fn vcvtah_s32_f16(a: f16) -> i32 { #[cfg_attr(test, assert_instr(fcvtas))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtah_s64_f16(a: f16) -> i64 { unsafe extern "unadjusted" { #[cfg_attr( @@ -7641,6 +7695,7 @@ pub fn vcvtah_s64_f16(a: f16) -> i64 { #[cfg_attr(test, assert_instr(fcvtau))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtah_u16_f16(a: f16) -> u16 { vcvtah_u32_f16(a) as u16 } @@ -7650,6 +7705,7 @@ pub fn vcvtah_u16_f16(a: f16) -> u16 { #[cfg_attr(test, assert_instr(fcvtau))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtah_u32_f16(a: f16) -> u32 { unsafe extern "unadjusted" { #[cfg_attr( @@ -7666,6 +7722,7 @@ pub fn vcvtah_u32_f16(a: f16) -> u32 { #[cfg_attr(test, assert_instr(fcvtau))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtah_u64_f16(a: f16) -> u64 { unsafe extern "unadjusted" { #[cfg_attr( @@ -7764,6 +7821,7 @@ pub fn vcvts_f32_s32(a: i32) -> f32 { #[cfg_attr(test, assert_instr(scvtf))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_f16_s16(a: i16) -> f16 { a as f16 } @@ -7773,6 +7831,7 @@ pub fn vcvth_f16_s16(a: i16) -> f16 { #[cfg_attr(test, assert_instr(scvtf))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_f16_s32(a: i32) -> f16 { a as f16 } @@ -7782,6 +7841,7 @@ pub fn vcvth_f16_s32(a: i32) -> f16 { #[cfg_attr(test, assert_instr(scvtf))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_f16_s64(a: i64) -> f16 { a as f16 } @@ -7791,6 +7851,7 @@ pub fn vcvth_f16_s64(a: i64) -> f16 { #[cfg_attr(test, assert_instr(ucvtf))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_f16_u16(a: u16) -> f16 { a as f16 } @@ -7800,6 +7861,7 @@ pub fn vcvth_f16_u16(a: u16) -> f16 { #[cfg_attr(test, assert_instr(ucvtf))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_f16_u32(a: u32) -> f16 { a as f16 } @@ -7809,6 +7871,7 @@ pub fn vcvth_f16_u32(a: u32) -> f16 { #[cfg_attr(test, assert_instr(ucvtf))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_f16_u64(a: u64) -> f16 { a as f16 } @@ -7819,6 +7882,7 @@ pub fn vcvth_f16_u64(a: u64) -> f16 { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_n_f16_s16(a: i16) -> f16 { static_assert!(N >= 1 && N <= 16); vcvth_n_f16_s32::(a as i32) @@ -7830,6 +7894,7 @@ pub fn vcvth_n_f16_s16(a: i16) -> f16 { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_n_f16_s32(a: i32) -> f16 { static_assert!(N >= 1 && N <= 16); unsafe extern "unadjusted" { @@ -7848,6 +7913,7 @@ pub fn vcvth_n_f16_s32(a: i32) -> f16 { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_n_f16_s64(a: i64) -> f16 { static_assert!(N >= 1 && N <= 16); unsafe extern "unadjusted" { @@ -7866,6 +7932,7 @@ pub fn vcvth_n_f16_s64(a: i64) -> f16 { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_n_f16_u16(a: u16) -> f16 { static_assert!(N >= 1 && N <= 16); vcvth_n_f16_u32::(a as u32) @@ -7877,6 +7944,7 @@ pub fn vcvth_n_f16_u16(a: u16) -> f16 { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_n_f16_u32(a: u32) -> f16 { static_assert!(N >= 1 && N <= 16); unsafe extern "unadjusted" { @@ -7895,6 +7963,7 @@ pub fn vcvth_n_f16_u32(a: u32) -> f16 { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_n_f16_u64(a: u64) -> f16 { static_assert!(N >= 1 && N <= 16); unsafe extern "unadjusted" { @@ -7913,6 +7982,7 @@ pub fn vcvth_n_f16_u64(a: u64) -> f16 { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_n_s16_f16(a: f16) -> i16 { static_assert!(N >= 1 && N <= 16); vcvth_n_s32_f16::(a) as i16 @@ -7924,6 +7994,7 @@ pub fn vcvth_n_s16_f16(a: f16) -> i16 { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_n_s32_f16(a: f16) -> i32 { static_assert!(N >= 1 && N <= 16); unsafe extern "unadjusted" { @@ -7942,6 +8013,7 @@ pub fn vcvth_n_s32_f16(a: f16) -> i32 { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_n_s64_f16(a: f16) -> i64 { static_assert!(N >= 1 && N <= 16); unsafe extern "unadjusted" { @@ -7960,6 +8032,7 @@ pub fn vcvth_n_s64_f16(a: f16) -> i64 { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_n_u16_f16(a: f16) -> u16 { static_assert!(N >= 1 && N <= 16); vcvth_n_u32_f16::(a) as u16 @@ -7971,6 +8044,7 @@ pub fn vcvth_n_u16_f16(a: f16) -> u16 { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_n_u32_f16(a: f16) -> u32 { static_assert!(N >= 1 && N <= 16); unsafe extern "unadjusted" { @@ -7989,6 +8063,7 @@ pub fn vcvth_n_u32_f16(a: f16) -> u32 { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_n_u64_f16(a: f16) -> u64 { static_assert!(N >= 1 && N <= 16); unsafe extern "unadjusted" { @@ -8006,6 +8081,7 @@ pub fn vcvth_n_u64_f16(a: f16) -> u64 { #[cfg_attr(test, assert_instr(fcvtzs))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_s16_f16(a: f16) -> i16 { a as i16 } @@ -8015,6 +8091,7 @@ pub fn vcvth_s16_f16(a: f16) -> i16 { #[cfg_attr(test, assert_instr(fcvtzs))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_s32_f16(a: f16) -> i32 { a as i32 } @@ -8024,6 +8101,7 @@ pub fn vcvth_s32_f16(a: f16) -> i32 { #[cfg_attr(test, assert_instr(fcvtzs))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_s64_f16(a: f16) -> i64 { a as i64 } @@ -8033,6 +8111,7 @@ pub fn vcvth_s64_f16(a: f16) -> i64 { #[cfg_attr(test, assert_instr(fcvtzu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_u16_f16(a: f16) -> u16 { a as u16 } @@ -8042,6 +8121,7 @@ pub fn vcvth_u16_f16(a: f16) -> u16 { #[cfg_attr(test, assert_instr(fcvtzu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_u32_f16(a: f16) -> u32 { a as u32 } @@ -8051,6 +8131,7 @@ pub fn vcvth_u32_f16(a: f16) -> u32 { #[cfg_attr(test, assert_instr(fcvtzu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvth_u64_f16(a: f16) -> u64 { a as u64 } @@ -8060,6 +8141,7 @@ pub fn vcvth_u64_f16(a: f16) -> u64 { #[cfg_attr(test, assert_instr(fcvtms))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtm_s16_f16(a: float16x4_t) -> int16x4_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -8076,6 +8158,7 @@ pub fn vcvtm_s16_f16(a: float16x4_t) -> int16x4_t { #[cfg_attr(test, assert_instr(fcvtms))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtmq_s16_f16(a: float16x8_t) -> int16x8_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -8156,6 +8239,7 @@ pub fn vcvtmq_s64_f64(a: float64x2_t) -> int64x2_t { #[cfg_attr(test, assert_instr(fcvtmu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtm_u16_f16(a: float16x4_t) -> uint16x4_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -8172,6 +8256,7 @@ pub fn vcvtm_u16_f16(a: float16x4_t) -> uint16x4_t { #[cfg_attr(test, assert_instr(fcvtmu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtmq_u16_f16(a: float16x8_t) -> uint16x8_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -8252,6 +8337,7 @@ pub fn vcvtmq_u64_f64(a: float64x2_t) -> uint64x2_t { #[cfg_attr(test, assert_instr(fcvtms))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtmh_s16_f16(a: f16) -> i16 { vcvtmh_s32_f16(a) as i16 } @@ -8261,6 +8347,7 @@ pub fn vcvtmh_s16_f16(a: f16) -> i16 { #[cfg_attr(test, assert_instr(fcvtms))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtmh_s32_f16(a: f16) -> i32 { unsafe extern "unadjusted" { #[cfg_attr( @@ -8277,6 +8364,7 @@ pub fn vcvtmh_s32_f16(a: f16) -> i32 { #[cfg_attr(test, assert_instr(fcvtms))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtmh_s64_f16(a: f16) -> i64 { unsafe extern "unadjusted" { #[cfg_attr( @@ -8293,6 +8381,7 @@ pub fn vcvtmh_s64_f16(a: f16) -> i64 { #[cfg_attr(test, assert_instr(fcvtmu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtmh_u16_f16(a: f16) -> u16 { vcvtmh_u32_f16(a) as u16 } @@ -8302,6 +8391,7 @@ pub fn vcvtmh_u16_f16(a: f16) -> u16 { #[cfg_attr(test, assert_instr(fcvtmu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtmh_u32_f16(a: f16) -> u32 { unsafe extern "unadjusted" { #[cfg_attr( @@ -8318,6 +8408,7 @@ pub fn vcvtmh_u32_f16(a: f16) -> u32 { #[cfg_attr(test, assert_instr(fcvtmu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtmh_u64_f16(a: f16) -> u64 { unsafe extern "unadjusted" { #[cfg_attr( @@ -8398,6 +8489,7 @@ pub fn vcvtmd_u64_f64(a: f64) -> u64 { #[cfg_attr(test, assert_instr(fcvtns))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtn_s16_f16(a: float16x4_t) -> int16x4_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -8414,6 +8506,7 @@ pub fn vcvtn_s16_f16(a: float16x4_t) -> int16x4_t { #[cfg_attr(test, assert_instr(fcvtns))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtnq_s16_f16(a: float16x8_t) -> int16x8_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -8494,6 +8587,7 @@ pub fn vcvtnq_s64_f64(a: float64x2_t) -> int64x2_t { #[cfg_attr(test, assert_instr(fcvtnu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtn_u16_f16(a: float16x4_t) -> uint16x4_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -8510,6 +8604,7 @@ pub fn vcvtn_u16_f16(a: float16x4_t) -> uint16x4_t { #[cfg_attr(test, assert_instr(fcvtnu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtnq_u16_f16(a: float16x8_t) -> uint16x8_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -8590,6 +8685,7 @@ pub fn vcvtnq_u64_f64(a: float64x2_t) -> uint64x2_t { #[cfg_attr(test, assert_instr(fcvtns))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtnh_s16_f16(a: f16) -> i16 { vcvtnh_s32_f16(a) as i16 } @@ -8599,6 +8695,7 @@ pub fn vcvtnh_s16_f16(a: f16) -> i16 { #[cfg_attr(test, assert_instr(fcvtns))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtnh_s32_f16(a: f16) -> i32 { unsafe extern "unadjusted" { #[cfg_attr( @@ -8615,6 +8712,7 @@ pub fn vcvtnh_s32_f16(a: f16) -> i32 { #[cfg_attr(test, assert_instr(fcvtns))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtnh_s64_f16(a: f16) -> i64 { unsafe extern "unadjusted" { #[cfg_attr( @@ -8631,6 +8729,7 @@ pub fn vcvtnh_s64_f16(a: f16) -> i64 { #[cfg_attr(test, assert_instr(fcvtnu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtnh_u16_f16(a: f16) -> u16 { vcvtnh_u32_f16(a) as u16 } @@ -8640,6 +8739,7 @@ pub fn vcvtnh_u16_f16(a: f16) -> u16 { #[cfg_attr(test, assert_instr(fcvtnu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtnh_u32_f16(a: f16) -> u32 { unsafe extern "unadjusted" { #[cfg_attr( @@ -8656,6 +8756,7 @@ pub fn vcvtnh_u32_f16(a: f16) -> u32 { #[cfg_attr(test, assert_instr(fcvtnu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtnh_u64_f16(a: f16) -> u64 { unsafe extern "unadjusted" { #[cfg_attr( @@ -8736,6 +8837,7 @@ pub fn vcvtnd_u64_f64(a: f64) -> u64 { #[cfg_attr(test, assert_instr(fcvtps))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtp_s16_f16(a: float16x4_t) -> int16x4_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -8752,6 +8854,7 @@ pub fn vcvtp_s16_f16(a: float16x4_t) -> int16x4_t { #[cfg_attr(test, assert_instr(fcvtps))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtpq_s16_f16(a: float16x8_t) -> int16x8_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -8832,6 +8935,7 @@ pub fn vcvtpq_s64_f64(a: float64x2_t) -> int64x2_t { #[cfg_attr(test, assert_instr(fcvtpu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtp_u16_f16(a: float16x4_t) -> uint16x4_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -8848,6 +8952,7 @@ pub fn vcvtp_u16_f16(a: float16x4_t) -> uint16x4_t { #[cfg_attr(test, assert_instr(fcvtpu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtpq_u16_f16(a: float16x8_t) -> uint16x8_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -8928,6 +9033,7 @@ pub fn vcvtpq_u64_f64(a: float64x2_t) -> uint64x2_t { #[cfg_attr(test, assert_instr(fcvtps))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtph_s16_f16(a: f16) -> i16 { vcvtph_s32_f16(a) as i16 } @@ -8937,6 +9043,7 @@ pub fn vcvtph_s16_f16(a: f16) -> i16 { #[cfg_attr(test, assert_instr(fcvtps))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtph_s32_f16(a: f16) -> i32 { unsafe extern "unadjusted" { #[cfg_attr( @@ -8953,6 +9060,7 @@ pub fn vcvtph_s32_f16(a: f16) -> i32 { #[cfg_attr(test, assert_instr(fcvtps))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtph_s64_f16(a: f16) -> i64 { unsafe extern "unadjusted" { #[cfg_attr( @@ -8969,6 +9077,7 @@ pub fn vcvtph_s64_f16(a: f16) -> i64 { #[cfg_attr(test, assert_instr(fcvtpu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtph_u16_f16(a: f16) -> u16 { vcvtph_u32_f16(a) as u16 } @@ -8978,6 +9087,7 @@ pub fn vcvtph_u16_f16(a: f16) -> u16 { #[cfg_attr(test, assert_instr(fcvtpu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtph_u32_f16(a: f16) -> u32 { unsafe extern "unadjusted" { #[cfg_attr( @@ -8994,6 +9104,7 @@ pub fn vcvtph_u32_f16(a: f16) -> u32 { #[cfg_attr(test, assert_instr(fcvtpu))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtph_u64_f16(a: f16) -> u64 { unsafe extern "unadjusted" { #[cfg_attr( @@ -9305,6 +9416,7 @@ pub fn vcvtxd_f32_f64(a: f64) -> f32 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fdiv))] pub fn vdiv_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe { simd_div(a, b) } @@ -9314,6 +9426,7 @@ pub fn vdiv_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fdiv))] pub fn vdivq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe { simd_div(a, b) } @@ -9359,6 +9472,7 @@ pub fn vdivq_f64(a: float64x2_t, b: float64x2_t) -> float64x2_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(nop))] pub fn vdivh_f16(a: f16, b: f16) -> f16 { a / b @@ -9608,6 +9722,7 @@ pub fn vdupd_lane_u64(a: uint64x1_t) -> u64 { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vduph_lane_f16(a: float16x4_t) -> f16 { static_assert_uimm_bits!(N, 2); unsafe { simd_extract!(a, N as u32) } @@ -9619,6 +9734,7 @@ pub fn vduph_lane_f16(a: float16x4_t) -> f16 { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vduph_laneq_f16(a: float16x8_t) -> f16 { static_assert_uimm_bits!(N, 4); unsafe { simd_extract!(a, N as u32) } @@ -9977,6 +10093,7 @@ pub fn vfma_f64(a: float64x1_t, b: float64x1_t, c: float64x1_t) -> float64x1_t { #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfma_lane_f16( a: float16x4_t, b: float16x4_t, @@ -9992,6 +10109,7 @@ pub fn vfma_lane_f16( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfma_laneq_f16( a: float16x4_t, b: float16x4_t, @@ -10007,6 +10125,7 @@ pub fn vfma_laneq_f16( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmaq_lane_f16( a: float16x8_t, b: float16x8_t, @@ -10022,6 +10141,7 @@ pub fn vfmaq_lane_f16( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmaq_laneq_f16( a: float16x8_t, b: float16x8_t, @@ -10140,6 +10260,7 @@ pub fn vfma_laneq_f64( #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmla))] pub fn vfma_n_f16(a: float16x4_t, b: float16x4_t, c: f16) -> float16x4_t { vfma_f16(a, b, vdup_n_f16(c)) @@ -10149,6 +10270,7 @@ pub fn vfma_n_f16(a: float16x4_t, b: float16x4_t, c: f16) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmla))] pub fn vfmaq_n_f16(a: float16x8_t, b: float16x8_t, c: f16) -> float16x8_t { vfmaq_f16(a, b, vdupq_n_f16(c)) @@ -10182,6 +10304,7 @@ pub fn vfmad_lane_f64(a: f64, b: f64, c: float64x1_t) -> f64 { #[cfg_attr(test, assert_instr(fmadd))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmah_f16(a: f16, b: f16, c: f16) -> f16 { unsafe { fmaf16(b, c, a) } } @@ -10192,6 +10315,7 @@ pub fn vfmah_f16(a: f16, b: f16, c: f16) -> f16 { #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmah_lane_f16(a: f16, b: f16, v: float16x4_t) -> f16 { static_assert_uimm_bits!(LANE, 2); unsafe { @@ -10206,6 +10330,7 @@ pub fn vfmah_lane_f16(a: f16, b: f16, v: float16x4_t) -> f16 { #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmah_laneq_f16(a: f16, b: f16, v: float16x8_t) -> f16 { static_assert_uimm_bits!(LANE, 3); unsafe { @@ -10294,6 +10419,7 @@ pub fn vfmad_laneq_f64(a: f64, b: f64, c: float64x2_t) -> f64 { #[target_feature(enable = "neon,fp16")] #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmlal2))] pub fn vfmlal_high_f16(r: float32x2_t, a: float16x4_t, b: float16x4_t) -> float32x2_t { unsafe extern "unadjusted" { @@ -10311,6 +10437,7 @@ pub fn vfmlal_high_f16(r: float32x2_t, a: float16x4_t, b: float16x4_t) -> float3 #[target_feature(enable = "neon,fp16")] #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmlal2))] pub fn vfmlalq_high_f16(r: float32x4_t, a: float16x8_t, b: float16x8_t) -> float32x4_t { unsafe extern "unadjusted" { @@ -10330,6 +10457,7 @@ pub fn vfmlalq_high_f16(r: float32x4_t, a: float16x8_t, b: float16x8_t) -> float #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[rustc_legacy_const_generics(3)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmlal_lane_high_f16( r: float32x2_t, a: float16x4_t, @@ -10346,6 +10474,7 @@ pub fn vfmlal_lane_high_f16( #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[rustc_legacy_const_generics(3)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmlal_laneq_high_f16( r: float32x2_t, a: float16x4_t, @@ -10362,6 +10491,7 @@ pub fn vfmlal_laneq_high_f16( #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[rustc_legacy_const_generics(3)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmlalq_lane_high_f16( r: float32x4_t, a: float16x8_t, @@ -10378,6 +10508,7 @@ pub fn vfmlalq_lane_high_f16( #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[rustc_legacy_const_generics(3)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmlalq_laneq_high_f16( r: float32x4_t, a: float16x8_t, @@ -10394,6 +10525,7 @@ pub fn vfmlalq_laneq_high_f16( #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[rustc_legacy_const_generics(3)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmlal_lane_low_f16( r: float32x2_t, a: float16x4_t, @@ -10410,6 +10542,7 @@ pub fn vfmlal_lane_low_f16( #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[rustc_legacy_const_generics(3)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmlal_laneq_low_f16( r: float32x2_t, a: float16x4_t, @@ -10426,6 +10559,7 @@ pub fn vfmlal_laneq_low_f16( #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[rustc_legacy_const_generics(3)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmlalq_lane_low_f16( r: float32x4_t, a: float16x8_t, @@ -10442,6 +10576,7 @@ pub fn vfmlalq_lane_low_f16( #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[rustc_legacy_const_generics(3)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmlalq_laneq_low_f16( r: float32x4_t, a: float16x8_t, @@ -10456,6 +10591,7 @@ pub fn vfmlalq_laneq_low_f16( #[target_feature(enable = "neon,fp16")] #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmlal))] pub fn vfmlal_low_f16(r: float32x2_t, a: float16x4_t, b: float16x4_t) -> float32x2_t { unsafe extern "unadjusted" { @@ -10473,6 +10609,7 @@ pub fn vfmlal_low_f16(r: float32x2_t, a: float16x4_t, b: float16x4_t) -> float32 #[target_feature(enable = "neon,fp16")] #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmlal))] pub fn vfmlalq_low_f16(r: float32x4_t, a: float16x8_t, b: float16x8_t) -> float32x4_t { unsafe extern "unadjusted" { @@ -10490,6 +10627,7 @@ pub fn vfmlalq_low_f16(r: float32x4_t, a: float16x8_t, b: float16x8_t) -> float3 #[target_feature(enable = "neon,fp16")] #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmlsl2))] pub fn vfmlsl_high_f16(r: float32x2_t, a: float16x4_t, b: float16x4_t) -> float32x2_t { unsafe extern "unadjusted" { @@ -10507,6 +10645,7 @@ pub fn vfmlsl_high_f16(r: float32x2_t, a: float16x4_t, b: float16x4_t) -> float3 #[target_feature(enable = "neon,fp16")] #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmlsl2))] pub fn vfmlslq_high_f16(r: float32x4_t, a: float16x8_t, b: float16x8_t) -> float32x4_t { unsafe extern "unadjusted" { @@ -10526,6 +10665,7 @@ pub fn vfmlslq_high_f16(r: float32x4_t, a: float16x8_t, b: float16x8_t) -> float #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[rustc_legacy_const_generics(3)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmlsl_lane_high_f16( r: float32x2_t, a: float16x4_t, @@ -10542,6 +10682,7 @@ pub fn vfmlsl_lane_high_f16( #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[rustc_legacy_const_generics(3)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmlsl_laneq_high_f16( r: float32x2_t, a: float16x4_t, @@ -10558,6 +10699,7 @@ pub fn vfmlsl_laneq_high_f16( #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[rustc_legacy_const_generics(3)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmlslq_lane_high_f16( r: float32x4_t, a: float16x8_t, @@ -10574,6 +10716,7 @@ pub fn vfmlslq_lane_high_f16( #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[rustc_legacy_const_generics(3)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmlslq_laneq_high_f16( r: float32x4_t, a: float16x8_t, @@ -10590,6 +10733,7 @@ pub fn vfmlslq_laneq_high_f16( #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[rustc_legacy_const_generics(3)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmlsl_lane_low_f16( r: float32x2_t, a: float16x4_t, @@ -10606,6 +10750,7 @@ pub fn vfmlsl_lane_low_f16( #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[rustc_legacy_const_generics(3)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmlsl_laneq_low_f16( r: float32x2_t, a: float16x4_t, @@ -10622,6 +10767,7 @@ pub fn vfmlsl_laneq_low_f16( #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[rustc_legacy_const_generics(3)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmlslq_lane_low_f16( r: float32x4_t, a: float16x8_t, @@ -10638,6 +10784,7 @@ pub fn vfmlslq_lane_low_f16( #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[rustc_legacy_const_generics(3)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmlslq_laneq_low_f16( r: float32x4_t, a: float16x8_t, @@ -10652,6 +10799,7 @@ pub fn vfmlslq_laneq_low_f16( #[target_feature(enable = "neon,fp16")] #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmlsl))] pub fn vfmlsl_low_f16(r: float32x2_t, a: float16x4_t, b: float16x4_t) -> float32x2_t { unsafe extern "unadjusted" { @@ -10669,6 +10817,7 @@ pub fn vfmlsl_low_f16(r: float32x2_t, a: float16x4_t, b: float16x4_t) -> float32 #[target_feature(enable = "neon,fp16")] #[cfg_attr(not(target_arch = "arm"), target_feature(enable = "fhm"))] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmlsl))] pub fn vfmlslq_low_f16(r: float32x4_t, a: float16x8_t, b: float16x8_t) -> float32x4_t { unsafe extern "unadjusted" { @@ -10699,6 +10848,7 @@ pub fn vfms_f64(a: float64x1_t, b: float64x1_t, c: float64x1_t) -> float64x1_t { #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfms_lane_f16( a: float16x4_t, b: float16x4_t, @@ -10714,6 +10864,7 @@ pub fn vfms_lane_f16( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfms_laneq_f16( a: float16x4_t, b: float16x4_t, @@ -10729,6 +10880,7 @@ pub fn vfms_laneq_f16( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmsq_lane_f16( a: float16x8_t, b: float16x8_t, @@ -10744,6 +10896,7 @@ pub fn vfmsq_lane_f16( #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmsq_laneq_f16( a: float16x8_t, b: float16x8_t, @@ -10862,6 +11015,7 @@ pub fn vfms_laneq_f64( #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmls))] pub fn vfms_n_f16(a: float16x4_t, b: float16x4_t, c: f16) -> float16x4_t { vfms_f16(a, b, vdup_n_f16(c)) @@ -10871,6 +11025,7 @@ pub fn vfms_n_f16(a: float16x4_t, b: float16x4_t, c: f16) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmls))] pub fn vfmsq_n_f16(a: float16x8_t, b: float16x8_t, c: f16) -> float16x8_t { vfmsq_f16(a, b, vdupq_n_f16(c)) @@ -10890,6 +11045,7 @@ pub fn vfms_n_f64(a: float64x1_t, b: float64x1_t, c: f64) -> float64x1_t { #[cfg_attr(test, assert_instr(fmsub))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmsh_f16(a: f16, b: f16, c: f16) -> f16 { vfmah_f16(a, -b, c) } @@ -10900,6 +11056,7 @@ pub fn vfmsh_f16(a: f16, b: f16, c: f16) -> f16 { #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmsh_lane_f16(a: f16, b: f16, v: float16x4_t) -> f16 { static_assert_uimm_bits!(LANE, 2); unsafe { @@ -10914,6 +11071,7 @@ pub fn vfmsh_lane_f16(a: f16, b: f16, v: float16x4_t) -> f16 { #[rustc_legacy_const_generics(3)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmsh_laneq_f16(a: f16, b: f16, v: float16x8_t) -> f16 { static_assert_uimm_bits!(LANE, 3); unsafe { @@ -11005,6 +11163,7 @@ pub fn vfmsd_laneq_f64(a: f64, b: f64, c: float64x2_t) -> f64 { #[target_feature(enable = "neon,fp16")] #[cfg_attr(test, assert_instr(ldr))] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld1_f16(ptr: *const f16) -> float16x4_t { crate::ptr::read_unaligned(ptr.cast()) } @@ -11016,6 +11175,7 @@ pub unsafe fn vld1_f16(ptr: *const f16) -> float16x4_t { #[target_feature(enable = "neon,fp16")] #[cfg_attr(test, assert_instr(ldr))] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld1q_f16(ptr: *const f16) -> float16x8_t { crate::ptr::read_unaligned(ptr.cast()) } @@ -13107,6 +13267,7 @@ pub fn vmaxq_f64(a: float64x2_t, b: float64x2_t) -> float64x2_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmax))] pub fn vmaxh_f16(a: f16, b: f16) -> f16 { unsafe extern "unadjusted" { @@ -13141,6 +13302,7 @@ pub fn vmaxnmq_f64(a: float64x2_t, b: float64x2_t) -> float64x2_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmaxnm))] pub fn vmaxnmh_f16(a: f16, b: f16) -> f16 { f16::max(a, b) @@ -13150,6 +13312,7 @@ pub fn vmaxnmh_f16(a: f16, b: f16) -> f16 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmaxnmv))] pub fn vmaxnmv_f16(a: float16x4_t) -> f16 { unsafe { simd_reduce_max(a) } @@ -13159,6 +13322,7 @@ pub fn vmaxnmv_f16(a: float16x4_t) -> f16 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmaxnmv))] pub fn vmaxnmvq_f16(a: float16x8_t) -> f16 { unsafe { simd_reduce_max(a) } @@ -13195,6 +13359,7 @@ pub fn vmaxnmvq_f32(a: float32x4_t) -> f32 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmaxv))] pub fn vmaxv_f16(a: float16x4_t) -> f16 { unsafe extern "unadjusted" { @@ -13211,6 +13376,7 @@ pub fn vmaxv_f16(a: float16x4_t) -> f16 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmaxv))] pub fn vmaxvq_f16(a: float16x8_t) -> f16 { unsafe extern "unadjusted" { @@ -13415,6 +13581,7 @@ pub fn vminq_f64(a: float64x2_t, b: float64x2_t) -> float64x2_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmin))] pub fn vminh_f16(a: f16, b: f16) -> f16 { unsafe extern "unadjusted" { @@ -13449,6 +13616,7 @@ pub fn vminnmq_f64(a: float64x2_t, b: float64x2_t) -> float64x2_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fminnm))] pub fn vminnmh_f16(a: f16, b: f16) -> f16 { f16::min(a, b) @@ -13458,6 +13626,7 @@ pub fn vminnmh_f16(a: f16, b: f16) -> f16 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fminnmv))] pub fn vminnmv_f16(a: float16x4_t) -> f16 { unsafe { simd_reduce_min(a) } @@ -13467,6 +13636,7 @@ pub fn vminnmv_f16(a: float16x4_t) -> f16 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fminnmv))] pub fn vminnmvq_f16(a: float16x8_t) -> f16 { unsafe { simd_reduce_min(a) } @@ -13503,6 +13673,7 @@ pub fn vminnmvq_f32(a: float32x4_t) -> f32 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fminv))] pub fn vminv_f16(a: float16x4_t) -> f16 { unsafe extern "unadjusted" { @@ -13519,6 +13690,7 @@ pub fn vminv_f16(a: float16x4_t) -> f16 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fminv))] pub fn vminvq_f16(a: float16x8_t) -> f16 { unsafe extern "unadjusted" { @@ -14554,6 +14726,7 @@ pub fn vmul_lane_f64(a: float64x1_t, b: float64x1_t) -> float64 #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmul_laneq_f16(a: float16x4_t, b: float16x8_t) -> float16x4_t { static_assert_uimm_bits!(LANE, 3); unsafe { @@ -14570,6 +14743,7 @@ pub fn vmul_laneq_f16(a: float16x4_t, b: float16x8_t) -> float1 #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmulq_laneq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { static_assert_uimm_bits!(LANE, 3); unsafe { @@ -14640,6 +14814,7 @@ pub fn vmuld_lane_f64(a: f64, b: float64x1_t) -> f64 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(nop))] pub fn vmulh_f16(a: f16, b: f16) -> f16 { a * b @@ -14651,6 +14826,7 @@ pub fn vmulh_f16(a: f16, b: f16) -> f16 { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmulh_lane_f16(a: f16, b: float16x4_t) -> f16 { static_assert_uimm_bits!(LANE, 2); unsafe { @@ -14665,6 +14841,7 @@ pub fn vmulh_lane_f16(a: f16, b: float16x4_t) -> f16 { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmulh_laneq_f16(a: f16, b: float16x8_t) -> f16 { static_assert_uimm_bits!(LANE, 3); unsafe { @@ -15073,6 +15250,7 @@ pub fn vmuld_laneq_f64(a: f64, b: float64x2_t) -> f64 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmulx))] pub fn vmulx_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { @@ -15089,6 +15267,7 @@ pub fn vmulx_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmulx))] pub fn vmulxq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { @@ -15171,6 +15350,7 @@ pub fn vmulxq_f64(a: float64x2_t, b: float64x2_t) -> float64x2_t { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmulx_lane_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { static_assert_uimm_bits!(LANE, 2); unsafe { @@ -15187,6 +15367,7 @@ pub fn vmulx_lane_f16(a: float16x4_t, b: float16x4_t) -> float1 #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmulx_laneq_f16(a: float16x4_t, b: float16x8_t) -> float16x4_t { static_assert_uimm_bits!(LANE, 3); unsafe { @@ -15203,6 +15384,7 @@ pub fn vmulx_laneq_f16(a: float16x4_t, b: float16x8_t) -> float #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmulxq_lane_f16(a: float16x8_t, b: float16x4_t) -> float16x8_t { static_assert_uimm_bits!(LANE, 2); unsafe { @@ -15232,6 +15414,7 @@ pub fn vmulxq_lane_f16(a: float16x8_t, b: float16x4_t) -> float #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmulxq_laneq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { static_assert_uimm_bits!(LANE, 3); unsafe { @@ -15347,6 +15530,7 @@ pub fn vmulx_laneq_f64(a: float64x1_t, b: float64x2_t) -> float #[cfg_attr(test, assert_instr(fmulx))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmulx_n_f16(a: float16x4_t, b: f16) -> float16x4_t { vmulx_f16(a, vdup_n_f16(b)) } @@ -15356,6 +15540,7 @@ pub fn vmulx_n_f16(a: float16x4_t, b: f16) -> float16x4_t { #[cfg_attr(test, assert_instr(fmulx))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmulxq_n_f16(a: float16x8_t, b: f16) -> float16x8_t { vmulxq_f16(a, vdupq_n_f16(b)) } @@ -15440,6 +15625,7 @@ pub fn vmulxs_laneq_f32(a: f32, b: float32x4_t) -> f32 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmulx))] pub fn vmulxh_f16(a: f16, b: f16) -> f16 { unsafe extern "unadjusted" { @@ -15458,6 +15644,7 @@ pub fn vmulxh_f16(a: f16, b: f16) -> f16 { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmulxh_lane_f16(a: f16, b: float16x4_t) -> f16 { static_assert_uimm_bits!(LANE, 2); unsafe { vmulxh_f16(a, simd_extract!(b, LANE as u32)) } @@ -15469,6 +15656,7 @@ pub fn vmulxh_lane_f16(a: f16, b: float16x4_t) -> f16 { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmulxh_laneq_f16(a: f16, b: float16x8_t) -> f16 { static_assert_uimm_bits!(LANE, 3); unsafe { vmulxh_f16(a, simd_extract!(b, LANE as u32)) } @@ -15534,6 +15722,7 @@ pub fn vnegd_s64(a: i64) -> i64 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fneg))] pub fn vnegh_f16(a: f16) -> f16 { -a @@ -15587,6 +15776,7 @@ pub fn vpaddd_u64(a: uint64x2_t) -> u64 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(faddp))] pub fn vpaddq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { @@ -15805,6 +15995,7 @@ pub fn vpaddq_u64(a: uint64x2_t, b: uint64x2_t) -> uint64x2_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmaxp))] pub fn vpmax_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { @@ -15821,6 +16012,7 @@ pub fn vpmax_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmaxp))] pub fn vpmaxq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { @@ -15837,6 +16029,7 @@ pub fn vpmaxq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmaxnmp))] pub fn vpmaxnm_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { @@ -15853,6 +16046,7 @@ pub fn vpmaxnm_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fmaxnmp))] pub fn vpmaxnmq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { @@ -16109,6 +16303,7 @@ pub fn vpmaxs_f32(a: float32x2_t) -> f32 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fminp))] pub fn vpmin_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { @@ -16125,6 +16320,7 @@ pub fn vpmin_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fminp))] pub fn vpminq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { @@ -16141,6 +16337,7 @@ pub fn vpminq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fminnmp))] pub fn vpminnm_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { @@ -16157,6 +16354,7 @@ pub fn vpminnm_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fminnmp))] pub fn vpminnmq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { @@ -21135,6 +21333,7 @@ pub fn vrecpes_f32(a: f32) -> f32 { #[cfg_attr(test, assert_instr(frecpe))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vrecpeh_f16(a: f16) -> f16 { unsafe extern "unadjusted" { #[cfg_attr( @@ -21215,6 +21414,7 @@ pub fn vrecpss_f32(a: f32, b: f32) -> f32 { #[cfg_attr(test, assert_instr(frecps))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vrecpsh_f16(a: f16, b: f16) -> f16 { unsafe extern "unadjusted" { #[cfg_attr( @@ -21263,6 +21463,7 @@ pub fn vrecpxs_f32(a: f32) -> f32 { #[cfg_attr(test, assert_instr(frecpx))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vrecpxh_f16(a: f16) -> f16 { unsafe extern "unadjusted" { #[cfg_attr( @@ -21279,6 +21480,7 @@ pub fn vrecpxh_f16(a: f16) -> f16 { #[cfg(target_endian = "little")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(nop))] pub fn vreinterpret_f64_f16(a: float16x4_t) -> float64x1_t { unsafe { transmute(a) } @@ -21289,6 +21491,7 @@ pub fn vreinterpret_f64_f16(a: float16x4_t) -> float64x1_t { #[cfg(target_endian = "big")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(nop))] pub fn vreinterpret_f64_f16(a: float16x4_t) -> float64x1_t { let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; @@ -21300,6 +21503,7 @@ pub fn vreinterpret_f64_f16(a: float16x4_t) -> float64x1_t { #[cfg(target_endian = "little")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(nop))] pub fn vreinterpretq_f64_f16(a: float16x8_t) -> float64x2_t { unsafe { transmute(a) } @@ -21310,6 +21514,7 @@ pub fn vreinterpretq_f64_f16(a: float16x8_t) -> float64x2_t { #[cfg(target_endian = "big")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(nop))] pub fn vreinterpretq_f64_f16(a: float16x8_t) -> float64x2_t { let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; @@ -21324,6 +21529,7 @@ pub fn vreinterpretq_f64_f16(a: float16x8_t) -> float64x2_t { #[cfg(target_endian = "little")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(nop))] pub fn vreinterpret_f16_f64(a: float64x1_t) -> float16x4_t { unsafe { transmute(a) } @@ -21334,6 +21540,7 @@ pub fn vreinterpret_f16_f64(a: float64x1_t) -> float16x4_t { #[cfg(target_endian = "big")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(nop))] pub fn vreinterpret_f16_f64(a: float64x1_t) -> float16x4_t { unsafe { @@ -21347,6 +21554,7 @@ pub fn vreinterpret_f16_f64(a: float64x1_t) -> float16x4_t { #[cfg(target_endian = "little")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(nop))] pub fn vreinterpretq_f16_f64(a: float64x2_t) -> float16x8_t { unsafe { transmute(a) } @@ -21357,6 +21565,7 @@ pub fn vreinterpretq_f16_f64(a: float64x2_t) -> float16x8_t { #[cfg(target_endian = "big")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(nop))] pub fn vreinterpretq_f16_f64(a: float64x2_t) -> float16x8_t { let a: float64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; @@ -22935,6 +23144,7 @@ pub fn vrnd64z_f64(a: float64x1_t) -> float64x1_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frintz))] pub fn vrnd_f16(a: float16x4_t) -> float16x4_t { unsafe { simd_trunc(a) } @@ -22944,6 +23154,7 @@ pub fn vrnd_f16(a: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frintz))] pub fn vrndq_f16(a: float16x8_t) -> float16x8_t { unsafe { simd_trunc(a) } @@ -22989,6 +23200,7 @@ pub fn vrndq_f64(a: float64x2_t) -> float64x2_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frinta))] pub fn vrnda_f16(a: float16x4_t) -> float16x4_t { unsafe { simd_round(a) } @@ -22998,6 +23210,7 @@ pub fn vrnda_f16(a: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frinta))] pub fn vrndaq_f16(a: float16x8_t) -> float16x8_t { unsafe { simd_round(a) } @@ -23043,6 +23256,7 @@ pub fn vrndaq_f64(a: float64x2_t) -> float64x2_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frinta))] pub fn vrndah_f16(a: f16) -> f16 { unsafe { roundf16(a) } @@ -23052,6 +23266,7 @@ pub fn vrndah_f16(a: f16) -> f16 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frintz))] pub fn vrndh_f16(a: f16) -> f16 { unsafe { truncf16(a) } @@ -23061,6 +23276,7 @@ pub fn vrndh_f16(a: f16) -> f16 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frinti))] pub fn vrndi_f16(a: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { @@ -23077,6 +23293,7 @@ pub fn vrndi_f16(a: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frinti))] pub fn vrndiq_f16(a: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { @@ -23157,6 +23374,7 @@ pub fn vrndiq_f64(a: float64x2_t) -> float64x2_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frinti))] pub fn vrndih_f16(a: f16) -> f16 { unsafe extern "unadjusted" { @@ -23173,6 +23391,7 @@ pub fn vrndih_f16(a: f16) -> f16 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frintm))] pub fn vrndm_f16(a: float16x4_t) -> float16x4_t { unsafe { simd_floor(a) } @@ -23182,6 +23401,7 @@ pub fn vrndm_f16(a: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frintm))] pub fn vrndmq_f16(a: float16x8_t) -> float16x8_t { unsafe { simd_floor(a) } @@ -23227,6 +23447,7 @@ pub fn vrndmq_f64(a: float64x2_t) -> float64x2_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frintm))] pub fn vrndmh_f16(a: f16) -> f16 { unsafe { floorf16(a) } @@ -23268,6 +23489,7 @@ pub fn vrndnq_f64(a: float64x2_t) -> float64x2_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frintn))] pub fn vrndnh_f16(a: f16) -> f16 { unsafe extern "unadjusted" { @@ -23300,6 +23522,7 @@ pub fn vrndns_f32(a: f32) -> f32 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frintp))] pub fn vrndp_f16(a: float16x4_t) -> float16x4_t { unsafe { simd_ceil(a) } @@ -23309,6 +23532,7 @@ pub fn vrndp_f16(a: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frintp))] pub fn vrndpq_f16(a: float16x8_t) -> float16x8_t { unsafe { simd_ceil(a) } @@ -23354,6 +23578,7 @@ pub fn vrndpq_f64(a: float64x2_t) -> float64x2_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frintp))] pub fn vrndph_f16(a: f16) -> f16 { unsafe { ceilf16(a) } @@ -23363,6 +23588,7 @@ pub fn vrndph_f16(a: f16) -> f16 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frintx))] pub fn vrndx_f16(a: float16x4_t) -> float16x4_t { unsafe { simd_round_ties_even(a) } @@ -23372,6 +23598,7 @@ pub fn vrndx_f16(a: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frintx))] pub fn vrndxq_f16(a: float16x8_t) -> float16x8_t { unsafe { simd_round_ties_even(a) } @@ -23417,6 +23644,7 @@ pub fn vrndxq_f64(a: float64x2_t) -> float64x2_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(frintx))] pub fn vrndxh_f16(a: f16) -> f16 { round_ties_even_f16(a) @@ -23623,6 +23851,7 @@ pub fn vrsqrtes_f32(a: f32) -> f32 { #[cfg_attr(test, assert_instr(frsqrte))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vrsqrteh_f16(a: f16) -> f16 { unsafe extern "unadjusted" { #[cfg_attr( @@ -23703,6 +23932,7 @@ pub fn vrsqrtss_f32(a: f32, b: f32) -> f32 { #[target_feature(enable = "neon,fp16")] #[cfg_attr(test, assert_instr(frsqrts))] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vrsqrtsh_f16(a: f16, b: f16) -> f16 { unsafe extern "unadjusted" { #[cfg_attr( @@ -24791,6 +25021,7 @@ pub fn vsqadds_u32(a: u32, b: i32) -> u32 { #[cfg_attr(test, assert_instr(fsqrt))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vsqrt_f16(a: float16x4_t) -> float16x4_t { unsafe { simd_fsqrt(a) } } @@ -24800,6 +25031,7 @@ pub fn vsqrt_f16(a: float16x4_t) -> float16x4_t { #[cfg_attr(test, assert_instr(fsqrt))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vsqrtq_f16(a: float16x8_t) -> float16x8_t { unsafe { simd_fsqrt(a) } } @@ -24844,6 +25076,7 @@ pub fn vsqrtq_f64(a: float64x2_t) -> float64x2_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(fsqrt))] pub fn vsqrth_f16(a: f16) -> f16 { unsafe { sqrtf16(a) } @@ -25177,6 +25410,7 @@ pub fn vsrid_n_u64(a: u64, b: u64) -> u64 { #[cfg_attr(test, assert_instr(str))] #[allow(clippy::cast_ptr_alignment)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst1_f16(ptr: *mut f16, a: float16x4_t) { crate::ptr::write_unaligned(ptr.cast(), a) } @@ -25189,6 +25423,7 @@ pub unsafe fn vst1_f16(ptr: *mut f16, a: float16x4_t) { #[cfg_attr(test, assert_instr(str))] #[allow(clippy::cast_ptr_alignment)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst1q_f16(ptr: *mut f16, a: float16x8_t) { crate::ptr::write_unaligned(ptr.cast(), a) } @@ -26488,6 +26723,7 @@ pub fn vsubd_u64(a: u64, b: u64) -> u64 { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(nop))] pub fn vsubh_f16(a: f16, b: f16) -> f16 { a - b @@ -27283,6 +27519,7 @@ pub fn vtbx4_p8(a: poly8x8_t, b: poly8x8x4_t, c: uint8x8_t) -> poly8x8_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, not(target_env = "msvc")), assert_instr(trn1))] pub fn vtrn1_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe { simd_shuffle!(a, b, [0, 4, 2, 6]) } @@ -27292,6 +27529,7 @@ pub fn vtrn1_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, not(target_env = "msvc")), assert_instr(trn1))] pub fn vtrn1q_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe { simd_shuffle!(a, b, [0, 8, 2, 10, 4, 12, 6, 14]) } @@ -27517,6 +27755,7 @@ pub fn vtrn1q_p16(a: poly16x8_t, b: poly16x8_t) -> poly16x8_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, not(target_env = "msvc")), assert_instr(trn2))] pub fn vtrn2_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe { simd_shuffle!(a, b, [1, 5, 3, 7]) } @@ -27526,6 +27765,7 @@ pub fn vtrn2_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, not(target_env = "msvc")), assert_instr(trn2))] pub fn vtrn2q_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe { simd_shuffle!(a, b, [1, 9, 3, 11, 5, 13, 7, 15]) } @@ -28056,6 +28296,7 @@ pub fn vusdotq_laneq_s32(a: int32x4_t, b: uint8x16_t, c: int8x1 #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, not(target_env = "msvc")), assert_instr(uzp1))] pub fn vuzp1_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe { simd_shuffle!(a, b, [0, 2, 4, 6]) } @@ -28065,6 +28306,7 @@ pub fn vuzp1_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, not(target_env = "msvc")), assert_instr(uzp1))] pub fn vuzp1q_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe { simd_shuffle!(a, b, [0, 2, 4, 6, 8, 10, 12, 14]) } @@ -28290,6 +28532,7 @@ pub fn vuzp1q_p16(a: poly16x8_t, b: poly16x8_t) -> poly16x8_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, not(target_env = "msvc")), assert_instr(uzp2))] pub fn vuzp2_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe { simd_shuffle!(a, b, [1, 3, 5, 7]) } @@ -28299,6 +28542,7 @@ pub fn vuzp2_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, not(target_env = "msvc")), assert_instr(uzp2))] pub fn vuzp2q_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe { simd_shuffle!(a, b, [1, 3, 5, 7, 9, 11, 13, 15]) } @@ -28542,6 +28786,7 @@ pub fn vxarq_u64(a: uint64x2_t, b: uint64x2_t) -> uint64x2_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, not(target_env = "msvc")), assert_instr(zip1))] pub fn vzip1_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe { simd_shuffle!(a, b, [0, 4, 1, 5]) } @@ -28551,6 +28796,7 @@ pub fn vzip1_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, not(target_env = "msvc")), assert_instr(zip1))] pub fn vzip1q_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe { simd_shuffle!(a, b, [0, 8, 1, 9, 2, 10, 3, 11]) } @@ -28776,6 +29022,7 @@ pub fn vzip1q_p64(a: poly64x2_t, b: poly64x2_t) -> poly64x2_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, not(target_env = "msvc")), assert_instr(zip2))] pub fn vzip2_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe { simd_shuffle!(a, b, [2, 6, 3, 7]) } @@ -28785,6 +29032,7 @@ pub fn vzip2_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { #[inline] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, not(target_env = "msvc")), assert_instr(zip2))] pub fn vzip2q_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe { simd_shuffle!(a, b, [4, 12, 5, 13, 6, 14, 7, 15]) } diff --git a/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs b/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs index 32531c7da135..fd150bcaf2b7 100644 --- a/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs +++ b/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs @@ -820,6 +820,7 @@ pub fn vabaq_u8(a: uint8x16_t, b: uint8x16_t, c: uint8x16_t) -> uint8x16_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vabd_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vabds.v4f16")] @@ -842,6 +843,7 @@ pub fn vabd_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vabdq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vabds.v8f16")] @@ -1405,6 +1407,7 @@ pub fn vabdl_u32(a: uint32x2_t, b: uint32x2_t) -> uint64x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vabs_f16(a: float16x4_t) -> float16x4_t { unsafe { simd_fabs(a) } } @@ -1419,6 +1422,7 @@ pub fn vabs_f16(a: float16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vabsq_f16(a: float16x8_t) -> float16x8_t { unsafe { simd_fabs(a) } } @@ -1625,6 +1629,7 @@ pub fn vabsq_s32(a: int32x4_t) -> int32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vabsh_f16(a: f16) -> f16 { unsafe { simd_extract!(vabs_f16(vdup_n_f16(a)), 0) } } @@ -1639,6 +1644,7 @@ pub fn vabsh_f16(a: f16) -> f16 { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vadd_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe { simd_add(a, b) } } @@ -1653,6 +1659,7 @@ pub fn vadd_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vaddq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe { simd_add(a, b) } } @@ -2129,6 +2136,7 @@ pub fn vaddq_p64(a: poly64x2_t, b: poly64x2_t) -> poly64x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vaddh_f16(a: f16, b: f16) -> f16 { a + b } @@ -3828,6 +3836,7 @@ pub fn vbicq_u8(a: uint8x16_t, b: uint8x16_t) -> uint8x16_t { assert_instr(bsl) )] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vbsl_f16(a: uint16x4_t, b: float16x4_t, c: float16x4_t) -> float16x4_t { let not = int16x4_t::splat(-1); unsafe { @@ -3848,6 +3857,7 @@ pub fn vbsl_f16(a: uint16x4_t, b: float16x4_t, c: float16x4_t) -> float16x4_t { assert_instr(bsl) )] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vbslq_f16(a: uint16x8_t, b: float16x8_t, c: float16x8_t) -> float16x8_t { let not = int16x8_t::splat(-1); unsafe { @@ -4462,6 +4472,7 @@ pub fn vbslq_u8(a: uint8x16_t, b: uint8x16_t, c: uint8x16_t) -> uint8x16_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcage_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vacge.v4i16.v4f16")] @@ -4484,6 +4495,7 @@ pub fn vcage_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcageq_f16(a: float16x8_t, b: float16x8_t) -> uint16x8_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vacge.v8i16.v8f16")] @@ -4564,6 +4576,7 @@ pub fn vcageq_f32(a: float32x4_t, b: float32x4_t) -> uint32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcagt_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vacgt.v4i16.v4f16")] @@ -4586,6 +4599,7 @@ pub fn vcagt_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcagtq_f16(a: float16x8_t, b: float16x8_t) -> uint16x8_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vacgt.v8i16.v8f16")] @@ -4666,6 +4680,7 @@ pub fn vcagtq_f32(a: float32x4_t, b: float32x4_t) -> uint32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcale_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { vcage_f16(b, a) } @@ -4680,6 +4695,7 @@ pub fn vcale_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcaleq_f16(a: float16x8_t, b: float16x8_t) -> uint16x8_t { vcageq_f16(b, a) } @@ -4736,6 +4752,7 @@ pub fn vcaleq_f32(a: float32x4_t, b: float32x4_t) -> uint32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcalt_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { vcagt_f16(b, a) } @@ -4750,6 +4767,7 @@ pub fn vcalt_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcaltq_f16(a: float16x8_t, b: float16x8_t) -> uint16x8_t { vcagtq_f16(b, a) } @@ -4806,6 +4824,7 @@ pub fn vcaltq_f32(a: float32x4_t, b: float32x4_t) -> uint32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vceq_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { unsafe { simd_eq(a, b) } } @@ -4820,6 +4839,7 @@ pub fn vceq_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vceqq_f16(a: float16x8_t, b: float16x8_t) -> uint16x8_t { unsafe { simd_eq(a, b) } } @@ -5170,6 +5190,7 @@ pub fn vceqq_p8(a: poly8x16_t, b: poly8x16_t) -> uint8x16_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcge_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { unsafe { simd_ge(a, b) } } @@ -5184,6 +5205,7 @@ pub fn vcge_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcgeq_f16(a: float16x8_t, b: float16x8_t) -> uint16x8_t { unsafe { simd_ge(a, b) } } @@ -5492,6 +5514,7 @@ pub fn vcgeq_u32(a: uint32x4_t, b: uint32x4_t) -> uint32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcgez_f16(a: float16x4_t) -> uint16x4_t { let b: f16x4 = f16x4::new(0.0, 0.0, 0.0, 0.0); unsafe { simd_ge(a, transmute(b)) } @@ -5507,6 +5530,7 @@ pub fn vcgez_f16(a: float16x4_t) -> uint16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcgezq_f16(a: float16x8_t) -> uint16x8_t { let b: f16x8 = f16x8::new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); unsafe { simd_ge(a, transmute(b)) } @@ -5522,6 +5546,7 @@ pub fn vcgezq_f16(a: float16x8_t) -> uint16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcgt_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { unsafe { simd_gt(a, b) } } @@ -5536,6 +5561,7 @@ pub fn vcgt_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcgtq_f16(a: float16x8_t, b: float16x8_t) -> uint16x8_t { unsafe { simd_gt(a, b) } } @@ -5844,6 +5870,7 @@ pub fn vcgtq_u32(a: uint32x4_t, b: uint32x4_t) -> uint32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcgtz_f16(a: float16x4_t) -> uint16x4_t { let b: f16x4 = f16x4::new(0.0, 0.0, 0.0, 0.0); unsafe { simd_gt(a, transmute(b)) } @@ -5859,6 +5886,7 @@ pub fn vcgtz_f16(a: float16x4_t) -> uint16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcgtzq_f16(a: float16x8_t) -> uint16x8_t { let b: f16x8 = f16x8::new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); unsafe { simd_gt(a, transmute(b)) } @@ -5874,6 +5902,7 @@ pub fn vcgtzq_f16(a: float16x8_t) -> uint16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcle_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { unsafe { simd_le(a, b) } } @@ -5888,6 +5917,7 @@ pub fn vcle_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcleq_f16(a: float16x8_t, b: float16x8_t) -> uint16x8_t { unsafe { simd_le(a, b) } } @@ -6196,6 +6226,7 @@ pub fn vcleq_u32(a: uint32x4_t, b: uint32x4_t) -> uint32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vclez_f16(a: float16x4_t) -> uint16x4_t { let b: f16x4 = f16x4::new(0.0, 0.0, 0.0, 0.0); unsafe { simd_le(a, transmute(b)) } @@ -6211,6 +6242,7 @@ pub fn vclez_f16(a: float16x4_t) -> uint16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vclezq_f16(a: float16x8_t) -> uint16x8_t { let b: f16x8 = f16x8::new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); unsafe { simd_le(a, transmute(b)) } @@ -6526,6 +6558,7 @@ pub fn vclsq_u32(a: uint32x4_t) -> int32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vclt_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { unsafe { simd_lt(a, b) } } @@ -6540,6 +6573,7 @@ pub fn vclt_f16(a: float16x4_t, b: float16x4_t) -> uint16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcltq_f16(a: float16x8_t, b: float16x8_t) -> uint16x8_t { unsafe { simd_lt(a, b) } } @@ -6848,6 +6882,7 @@ pub fn vcltq_u32(a: uint32x4_t, b: uint32x4_t) -> uint32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcltz_f16(a: float16x4_t) -> uint16x4_t { let b: f16x4 = f16x4::new(0.0, 0.0, 0.0, 0.0); unsafe { simd_lt(a, transmute(b)) } @@ -6863,6 +6898,7 @@ pub fn vcltz_f16(a: float16x4_t) -> uint16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcltzq_f16(a: float16x8_t) -> uint16x8_t { let b: f16x8 = f16x8::new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); unsafe { simd_lt(a, transmute(b)) } @@ -7536,6 +7572,7 @@ pub fn vcntq_p8(a: poly8x16_t) -> poly8x16_t { #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(nop))] pub fn vcombine_f16(a: float16x4_t, b: float16x4_t) -> float16x8_t { unsafe { simd_shuffle!(a, b, [0, 1, 2, 3, 4, 5, 6, 7]) } @@ -7756,6 +7793,7 @@ pub fn vcombine_p64(a: poly64x1_t, b: poly64x1_t) -> poly64x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcreate_f16(a: u64) -> float16x4_t { unsafe { transmute(a) } } @@ -7771,6 +7809,7 @@ pub fn vcreate_f16(a: u64) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcreate_f16(a: u64) -> float16x4_t { unsafe { let ret_val: float16x4_t = transmute(a); @@ -8274,6 +8313,7 @@ pub fn vcreate_p64(a: u64) -> poly64x1_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvt_f16_f32(a: float32x4_t) -> float16x4_t { unsafe { simd_cast(a) } } @@ -8288,6 +8328,7 @@ pub fn vcvt_f16_f32(a: float32x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvt_f16_s16(a: int16x4_t) -> float16x4_t { unsafe { simd_cast(a) } } @@ -8302,6 +8343,7 @@ pub fn vcvt_f16_s16(a: int16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtq_f16_s16(a: int16x8_t) -> float16x8_t { unsafe { simd_cast(a) } } @@ -8316,6 +8358,7 @@ pub fn vcvtq_f16_s16(a: int16x8_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvt_f16_u16(a: uint16x4_t) -> float16x4_t { unsafe { simd_cast(a) } } @@ -8330,6 +8373,7 @@ pub fn vcvt_f16_u16(a: uint16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtq_f16_u16(a: uint16x8_t) -> float16x8_t { unsafe { simd_cast(a) } } @@ -8344,6 +8388,7 @@ pub fn vcvtq_f16_u16(a: uint16x8_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvt_f32_f16(a: float16x4_t) -> float32x4_t { unsafe { simd_cast(a) } } @@ -8443,6 +8488,7 @@ pub fn vcvtq_f32_u32(a: uint32x4_t) -> float32x4_t { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvt_n_f16_s16(a: int16x4_t) -> float16x4_t { static_assert!(N >= 1 && N <= 16); unsafe extern "unadjusted" { @@ -8470,6 +8516,7 @@ pub fn vcvt_n_f16_s16(a: int16x4_t) -> float16x4_t { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtq_n_f16_s16(a: int16x8_t) -> float16x8_t { static_assert!(N >= 1 && N <= 16); unsafe extern "unadjusted" { @@ -8497,6 +8544,7 @@ pub fn vcvtq_n_f16_s16(a: int16x8_t) -> float16x8_t { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvt_n_f16_u16(a: uint16x4_t) -> float16x4_t { static_assert!(N >= 1 && N <= 16); unsafe extern "unadjusted" { @@ -8524,6 +8572,7 @@ pub fn vcvt_n_f16_u16(a: uint16x4_t) -> float16x4_t { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtq_n_f16_u16(a: uint16x8_t) -> float16x8_t { static_assert!(N >= 1 && N <= 16); unsafe extern "unadjusted" { @@ -8703,6 +8752,7 @@ pub fn vcvtq_n_f32_u32(a: uint32x4_t) -> float32x4_t { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvt_n_s16_f16(a: float16x4_t) -> int16x4_t { static_assert!(N >= 1 && N <= 16); unsafe extern "unadjusted" { @@ -8730,6 +8780,7 @@ pub fn vcvt_n_s16_f16(a: float16x4_t) -> int16x4_t { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtq_n_s16_f16(a: float16x8_t) -> int16x8_t { static_assert!(N >= 1 && N <= 16); unsafe extern "unadjusted" { @@ -8833,6 +8884,7 @@ pub fn vcvtq_n_s32_f32(a: float32x4_t) -> int32x4_t { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvt_n_u16_f16(a: float16x4_t) -> uint16x4_t { static_assert!(N >= 1 && N <= 16); unsafe extern "unadjusted" { @@ -8860,6 +8912,7 @@ pub fn vcvt_n_u16_f16(a: float16x4_t) -> uint16x4_t { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtq_n_u16_f16(a: float16x8_t) -> uint16x8_t { static_assert!(N >= 1 && N <= 16); unsafe extern "unadjusted" { @@ -8962,6 +9015,7 @@ pub fn vcvtq_n_u32_f32(a: float32x4_t) -> uint32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvt_s16_f16(a: float16x4_t) -> int16x4_t { unsafe { simd_cast(a) } } @@ -8976,6 +9030,7 @@ pub fn vcvt_s16_f16(a: float16x4_t) -> int16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtq_s16_f16(a: float16x8_t) -> int16x8_t { unsafe { simd_cast(a) } } @@ -9048,6 +9103,7 @@ pub fn vcvtq_s32_f32(a: float32x4_t) -> int32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvt_u16_f16(a: float16x4_t) -> uint16x4_t { unsafe { simd_cast(a) } } @@ -9062,6 +9118,7 @@ pub fn vcvt_u16_f16(a: float16x4_t) -> uint16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vcvtq_u16_f16(a: float16x8_t) -> uint16x8_t { unsafe { simd_cast(a) } } @@ -9361,6 +9418,7 @@ pub fn vdotq_u32(a: uint32x4_t, b: uint8x16_t, c: uint8x16_t) -> uint32x4_t { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vdup_lane_f16(a: float16x4_t) -> float16x4_t { static_assert_uimm_bits!(N, 2); unsafe { simd_shuffle!(a, a, [N as u32, N as u32, N as u32, N as u32]) } @@ -9377,6 +9435,7 @@ pub fn vdup_lane_f16(a: float16x4_t) -> float16x4_t { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vdupq_lane_f16(a: float16x4_t) -> float16x8_t { static_assert_uimm_bits!(N, 2); unsafe { @@ -9922,6 +9981,7 @@ pub fn vdup_lane_u64(a: uint64x1_t) -> uint64x1_t { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vdup_laneq_f16(a: float16x8_t) -> float16x4_t { static_assert_uimm_bits!(N, 3); unsafe { simd_shuffle!(a, a, [N as u32, N as u32, N as u32, N as u32]) } @@ -9938,6 +9998,7 @@ pub fn vdup_laneq_f16(a: float16x8_t) -> float16x4_t { #[rustc_legacy_const_generics(1)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vdupq_laneq_f16(a: float16x8_t) -> float16x8_t { static_assert_uimm_bits!(N, 3); unsafe { @@ -10482,6 +10543,7 @@ pub fn vdup_laneq_u64(a: uint64x2_t) -> uint64x1_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vdup_n_f16(a: f16) -> float16x4_t { float16x4_t::splat(a) } @@ -10496,6 +10558,7 @@ pub fn vdup_n_f16(a: f16) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vdupq_n_f16(a: f16) -> float16x8_t { float16x8_t::splat(a) } @@ -11443,6 +11506,7 @@ pub fn veorq_u64(a: uint64x2_t, b: uint64x2_t) -> uint64x2_t { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vext_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { static_assert_uimm_bits!(N, 2); unsafe { @@ -11814,6 +11878,7 @@ pub fn vextq_p16(a: poly16x8_t, b: poly16x8_t) -> poly16x8_t { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vextq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { static_assert_uimm_bits!(N, 3); unsafe { @@ -12394,6 +12459,7 @@ pub fn vextq_p8(a: poly8x16_t, b: poly8x16_t) -> poly8x16_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfma_f16(a: float16x4_t, b: float16x4_t, c: float16x4_t) -> float16x4_t { unsafe { simd_fma(b, c, a) } } @@ -12408,6 +12474,7 @@ pub fn vfma_f16(a: float16x4_t, b: float16x4_t, c: float16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmaq_f16(a: float16x8_t, b: float16x8_t, c: float16x8_t) -> float16x8_t { unsafe { simd_fma(b, c, a) } } @@ -12507,6 +12574,7 @@ pub fn vfmaq_n_f32(a: float32x4_t, b: float32x4_t, c: f32) -> float32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfms_f16(a: float16x4_t, b: float16x4_t, c: float16x4_t) -> float16x4_t { unsafe { let b: float16x4_t = simd_neg(b); @@ -12525,6 +12593,7 @@ pub fn vfms_f16(a: float16x4_t, b: float16x4_t, c: float16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vfmsq_f16(a: float16x8_t, b: float16x8_t, c: float16x8_t) -> float16x8_t { unsafe { let b: float16x8_t = simd_neg(b); @@ -12627,6 +12696,7 @@ pub fn vfmsq_n_f32(a: float32x4_t, b: float32x4_t, c: f32) -> float32x4_t { #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(nop))] pub fn vget_high_f16(a: float16x8_t) -> float16x4_t { unsafe { simd_shuffle!(a, a, [4, 5, 6, 7]) } @@ -12637,6 +12707,7 @@ pub fn vget_high_f16(a: float16x8_t) -> float16x4_t { #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(nop))] pub fn vget_low_f16(a: float16x8_t) -> float16x4_t { unsafe { simd_shuffle!(a, a, [0, 1, 2, 3]) } @@ -12884,6 +12955,7 @@ pub fn vget_high_u64(a: uint64x2_t) -> uint64x1_t { )] #[rustc_legacy_const_generics(1)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vget_lane_f16(a: float16x4_t) -> f16 { static_assert_uimm_bits!(LANE, 2); unsafe { simd_extract!(a, LANE as u32) } @@ -12900,6 +12972,7 @@ pub fn vget_lane_f16(a: float16x4_t) -> f16 { )] #[rustc_legacy_const_generics(1)] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vgetq_lane_f16(a: float16x8_t) -> f16 { static_assert_uimm_bits!(LANE, 3); unsafe { simd_extract!(a, LANE as u32) } @@ -14256,6 +14329,7 @@ pub fn vhsubq_u32(a: uint32x4_t, b: uint32x4_t) -> uint32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld1_dup_f16(ptr: *const f16) -> float16x4_t { let x: float16x4_t = vld1_lane_f16::<0>(ptr, transmute(f16x4::splat(0.0))); simd_shuffle!(x, x, [0, 0, 0, 0]) @@ -14273,6 +14347,7 @@ pub unsafe fn vld1_dup_f16(ptr: *const f16) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld1q_dup_f16(ptr: *const f16) -> float16x8_t { let x: float16x8_t = vld1q_lane_f16::<0>(ptr, transmute(f16x8::splat(0.0))); simd_shuffle!(x, x, [0, 0, 0, 0, 0, 0, 0, 0]) @@ -14843,6 +14918,7 @@ pub unsafe fn vld1_dup_u64(ptr: *const u64) -> uint64x1_t { #[target_feature(enable = "neon,v7")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr("vld1.16"))] pub unsafe fn vld1_f16(ptr: *const f16) -> float16x4_t { transmute(vld1_v4f16( @@ -14860,6 +14936,7 @@ pub unsafe fn vld1_f16(ptr: *const f16) -> float16x4_t { #[target_feature(enable = "neon,v7")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr("vld1.16"))] pub unsafe fn vld1_f16(ptr: *const f16) -> float16x4_t { let ret_val: float16x4_t = transmute(vld1_v4f16( @@ -14878,6 +14955,7 @@ pub unsafe fn vld1_f16(ptr: *const f16) -> float16x4_t { #[target_feature(enable = "neon,v7")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr("vld1.16"))] pub unsafe fn vld1q_f16(ptr: *const f16) -> float16x8_t { transmute(vld1q_v8f16( @@ -14895,6 +14973,7 @@ pub unsafe fn vld1q_f16(ptr: *const f16) -> float16x8_t { #[target_feature(enable = "neon,v7")] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr("vld1.16"))] pub unsafe fn vld1q_f16(ptr: *const f16) -> float16x8_t { let ret_val: float16x8_t = transmute(vld1q_v8f16( @@ -14916,6 +14995,7 @@ pub unsafe fn vld1q_f16(ptr: *const f16) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld1_f16_x2(a: *const f16) -> float16x4x2_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -14940,6 +15020,7 @@ pub unsafe fn vld1_f16_x2(a: *const f16) -> float16x4x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld1_f16_x3(a: *const f16) -> float16x4x3_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -14964,6 +15045,7 @@ pub unsafe fn vld1_f16_x3(a: *const f16) -> float16x4x3_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld1_f16_x4(a: *const f16) -> float16x4x4_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -14988,6 +15070,7 @@ pub unsafe fn vld1_f16_x4(a: *const f16) -> float16x4x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld1q_f16_x2(a: *const f16) -> float16x8x2_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -15012,6 +15095,7 @@ pub unsafe fn vld1q_f16_x2(a: *const f16) -> float16x8x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld1q_f16_x3(a: *const f16) -> float16x8x3_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -15036,6 +15120,7 @@ pub unsafe fn vld1q_f16_x3(a: *const f16) -> float16x8x3_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld1q_f16_x4(a: *const f16) -> float16x8x4_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -15732,6 +15817,7 @@ pub unsafe fn vld1q_f32_x4(a: *const f32) -> float32x4x4_t { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld1_lane_f16(ptr: *const f16, src: float16x4_t) -> float16x4_t { static_assert_uimm_bits!(LANE, 2); simd_insert!(src, LANE as u32, *ptr) @@ -15750,6 +15836,7 @@ pub unsafe fn vld1_lane_f16(ptr: *const f16, src: float16x4_t) #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld1q_lane_f16(ptr: *const f16, src: float16x8_t) -> float16x8_t { static_assert_uimm_bits!(LANE, 3); simd_insert!(src, LANE as u32, *ptr) @@ -19490,6 +19577,7 @@ unsafe fn vld1q_v8i16(a: *const i8, b: i32) -> int16x8_t { #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] unsafe fn vld1_v4f16(a: *const i8, b: i32) -> float16x4_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vld1.v4f16")] @@ -19507,6 +19595,7 @@ unsafe fn vld1_v4f16(a: *const i8, b: i32) -> float16x4_t { #[cfg_attr(all(test, target_arch = "arm"), assert_instr(nop))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] unsafe fn vld1q_v8f16(a: *const i8, b: i32) -> float16x8_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vld1.v8f16")] @@ -19548,6 +19637,7 @@ pub unsafe fn vld1q_dup_p64(ptr: *const p64) -> poly64x2_t { #[target_feature(enable = "neon,fp16")] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(vld2))] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld2_dup_f16(a: *const f16) -> float16x4x2_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vld2dup.v4f16.p0")] @@ -19565,6 +19655,7 @@ pub unsafe fn vld2_dup_f16(a: *const f16) -> float16x4x2_t { #[target_feature(enable = "neon,fp16")] #[cfg_attr(all(test, target_arch = "arm"), assert_instr(vld2))] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld2q_dup_f16(a: *const f16) -> float16x8x2_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vld2dup.v8f16.p0")] @@ -19584,6 +19675,7 @@ pub unsafe fn vld2q_dup_f16(a: *const f16) -> float16x8x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld2_dup_f16(a: *const f16) -> float16x4x2_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -19606,6 +19698,7 @@ pub unsafe fn vld2_dup_f16(a: *const f16) -> float16x4x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld2q_dup_f16(a: *const f16) -> float16x8x2_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -20521,6 +20614,7 @@ pub unsafe fn vld2q_dup_p16(a: *const p16) -> poly16x8x2_t { #[cfg_attr(all(test, target_arch = "arm"), assert_instr(vld2))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld2_f16(a: *const f16) -> float16x4x2_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vld2.v4f16.p0")] @@ -20538,6 +20632,7 @@ pub unsafe fn vld2_f16(a: *const f16) -> float16x4x2_t { #[cfg_attr(all(test, target_arch = "arm"), assert_instr(vld2))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld2q_f16(a: *const f16) -> float16x8x2_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vld2.v8f16.p0")] @@ -20557,6 +20652,7 @@ pub unsafe fn vld2q_f16(a: *const f16) -> float16x8x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld2_f16(a: *const f16) -> float16x4x2_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -20579,6 +20675,7 @@ pub unsafe fn vld2_f16(a: *const f16) -> float16x4x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld2q_f16(a: *const f16) -> float16x8x2_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -20880,6 +20977,7 @@ pub unsafe fn vld2q_s32(a: *const i32) -> int32x4x2_t { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld2_lane_f16(a: *const f16, b: float16x4x2_t) -> float16x4x2_t { static_assert_uimm_bits!(LANE, 2); unsafe extern "unadjusted" { @@ -20905,6 +21003,7 @@ pub unsafe fn vld2_lane_f16(a: *const f16, b: float16x4x2_t) -> #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld2q_lane_f16(a: *const f16, b: float16x8x2_t) -> float16x8x2_t { static_assert_uimm_bits!(LANE, 3); unsafe extern "unadjusted" { @@ -20932,6 +21031,7 @@ pub unsafe fn vld2q_lane_f16(a: *const f16, b: float16x8x2_t) - #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld2_lane_f16(a: *const f16, b: float16x4x2_t) -> float16x4x2_t { static_assert_uimm_bits!(LANE, 2); unsafe extern "unadjusted" { @@ -20957,6 +21057,7 @@ pub unsafe fn vld2_lane_f16(a: *const f16, b: float16x4x2_t) -> #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld2q_lane_f16(a: *const f16, b: float16x8x2_t) -> float16x8x2_t { static_assert_uimm_bits!(LANE, 3); unsafe extern "unadjusted" { @@ -22109,6 +22210,7 @@ pub unsafe fn vld2q_p16(a: *const p16) -> poly16x8x2_t { #[cfg_attr(all(test, target_arch = "arm"), assert_instr(vld3))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld3_dup_f16(a: *const f16) -> float16x4x3_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vld3dup.v4f16.p0")] @@ -22126,6 +22228,7 @@ pub unsafe fn vld3_dup_f16(a: *const f16) -> float16x4x3_t { #[cfg_attr(all(test, target_arch = "arm"), assert_instr(vld3))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld3q_dup_f16(a: *const f16) -> float16x8x3_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vld3dup.v8f16.p0")] @@ -22145,6 +22248,7 @@ pub unsafe fn vld3q_dup_f16(a: *const f16) -> float16x8x3_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld3_dup_f16(a: *const f16) -> float16x4x3_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -22167,6 +22271,7 @@ pub unsafe fn vld3_dup_f16(a: *const f16) -> float16x4x3_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld3q_dup_f16(a: *const f16) -> float16x8x3_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -23104,6 +23209,7 @@ pub unsafe fn vld3q_dup_p16(a: *const p16) -> poly16x8x3_t { #[cfg_attr(all(test, target_arch = "arm"), assert_instr(vld3))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld3_f16(a: *const f16) -> float16x4x3_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vld3.v4f16.p0")] @@ -23121,6 +23227,7 @@ pub unsafe fn vld3_f16(a: *const f16) -> float16x4x3_t { #[cfg_attr(all(test, target_arch = "arm"), assert_instr(vld3))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld3q_f16(a: *const f16) -> float16x8x3_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vld3.v8f16.p0")] @@ -23140,6 +23247,7 @@ pub unsafe fn vld3q_f16(a: *const f16) -> float16x8x3_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld3_f16(a: *const f16) -> float16x4x3_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -23162,6 +23270,7 @@ pub unsafe fn vld3_f16(a: *const f16) -> float16x4x3_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld3q_f16(a: *const f16) -> float16x8x3_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -23463,6 +23572,7 @@ pub unsafe fn vld3q_s32(a: *const i32) -> int32x4x3_t { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld3_lane_f16(a: *const f16, b: float16x4x3_t) -> float16x4x3_t { static_assert_uimm_bits!(LANE, 2); unsafe extern "unadjusted" { @@ -23489,6 +23599,7 @@ pub unsafe fn vld3_lane_f16(a: *const f16, b: float16x4x3_t) -> #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld3q_lane_f16(a: *const f16, b: float16x8x3_t) -> float16x8x3_t { static_assert_uimm_bits!(LANE, 3); unsafe extern "unadjusted" { @@ -23517,6 +23628,7 @@ pub unsafe fn vld3q_lane_f16(a: *const f16, b: float16x8x3_t) - #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld3_lane_f16(a: *const f16, b: float16x4x3_t) -> float16x4x3_t { static_assert_uimm_bits!(LANE, 2); unsafe extern "unadjusted" { @@ -23547,6 +23659,7 @@ pub unsafe fn vld3_lane_f16(a: *const f16, b: float16x4x3_t) -> #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld3q_lane_f16(a: *const f16, b: float16x8x3_t) -> float16x8x3_t { static_assert_uimm_bits!(LANE, 3); unsafe extern "unadjusted" { @@ -24775,6 +24888,7 @@ pub unsafe fn vld3q_lane_f32(a: *const f32, b: float32x4x3_t) - #[cfg_attr(all(test, target_arch = "arm"), assert_instr(vld4))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld4_dup_f16(a: *const f16) -> float16x4x4_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vld4dup.v4f16.p0")] @@ -24792,6 +24906,7 @@ pub unsafe fn vld4_dup_f16(a: *const f16) -> float16x4x4_t { #[cfg_attr(all(test, target_arch = "arm"), assert_instr(vld4))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld4q_dup_f16(a: *const f16) -> float16x8x4_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vld4dup.v8f16.p0")] @@ -24811,6 +24926,7 @@ pub unsafe fn vld4q_dup_f16(a: *const f16) -> float16x8x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld4_dup_f16(a: *const f16) -> float16x4x4_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -24833,6 +24949,7 @@ pub unsafe fn vld4_dup_f16(a: *const f16) -> float16x4x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld4q_dup_f16(a: *const f16) -> float16x8x4_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -25792,6 +25909,7 @@ pub unsafe fn vld4q_dup_p16(a: *const p16) -> poly16x8x4_t { #[cfg_attr(all(test, target_arch = "arm"), assert_instr(vld4))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld4_f16(a: *const f16) -> float16x4x4_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vld4.v4f16.p0")] @@ -25809,6 +25927,7 @@ pub unsafe fn vld4_f16(a: *const f16) -> float16x4x4_t { #[cfg_attr(all(test, target_arch = "arm"), assert_instr(vld4))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld4q_f16(a: *const f16) -> float16x8x4_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vld4.v8f16.p0")] @@ -25828,6 +25947,7 @@ pub unsafe fn vld4q_f16(a: *const f16) -> float16x8x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld4_f16(a: *const f16) -> float16x4x4_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -25850,6 +25970,7 @@ pub unsafe fn vld4_f16(a: *const f16) -> float16x4x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld4q_f16(a: *const f16) -> float16x8x4_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -26151,6 +26272,7 @@ pub unsafe fn vld4q_s32(a: *const i32) -> int32x4x4_t { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld4_lane_f16(a: *const f16, b: float16x4x4_t) -> float16x4x4_t { static_assert_uimm_bits!(LANE, 2); unsafe extern "unadjusted" { @@ -26178,6 +26300,7 @@ pub unsafe fn vld4_lane_f16(a: *const f16, b: float16x4x4_t) -> #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld4q_lane_f16(a: *const f16, b: float16x8x4_t) -> float16x8x4_t { static_assert_uimm_bits!(LANE, 3); unsafe extern "unadjusted" { @@ -26207,6 +26330,7 @@ pub unsafe fn vld4q_lane_f16(a: *const f16, b: float16x8x4_t) - #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld4_lane_f16(a: *const f16, b: float16x4x4_t) -> float16x4x4_t { static_assert_uimm_bits!(LANE, 2); unsafe extern "unadjusted" { @@ -26238,6 +26362,7 @@ pub unsafe fn vld4_lane_f16(a: *const f16, b: float16x4x4_t) -> #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vld4q_lane_f16(a: *const f16, b: float16x8x4_t) -> float16x8x4_t { static_assert_uimm_bits!(LANE, 3); unsafe extern "unadjusted" { @@ -27527,6 +27652,7 @@ pub unsafe fn vldrq_p128(a: *const p128) -> p128 { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmax_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vmaxs.v4f16")] @@ -27549,6 +27675,7 @@ pub fn vmax_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmaxq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vmaxs.v8f16")] @@ -27917,6 +28044,7 @@ pub fn vmaxq_u32(a: uint32x4_t, b: uint32x4_t) -> uint32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmaxnm_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe { simd_fmax(a, b) } } @@ -27931,6 +28059,7 @@ pub fn vmaxnm_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmaxnmq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe { simd_fmax(a, b) } } @@ -27987,6 +28116,7 @@ pub fn vmaxnmq_f32(a: float32x4_t, b: float32x4_t) -> float32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmin_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vmins.v4f16")] @@ -28009,6 +28139,7 @@ pub fn vmin_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vminq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vmins.v8f16")] @@ -28377,6 +28508,7 @@ pub fn vminq_u32(a: uint32x4_t, b: uint32x4_t) -> uint32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vminnm_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe { simd_fmin(a, b) } } @@ -28391,6 +28523,7 @@ pub fn vminnm_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vminnmq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe { simd_fmin(a, b) } } @@ -31573,6 +31706,7 @@ pub fn vmmlaq_u32(a: uint32x4_t, b: uint8x16_t, c: uint8x16_t) -> uint32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmov_n_f16(a: f16) -> float16x4_t { vdup_n_f16(a) } @@ -31587,6 +31721,7 @@ pub fn vmov_n_f16(a: f16) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmovq_n_f16(a: f16) -> float16x8_t { vdupq_n_f16(a) } @@ -32315,6 +32450,7 @@ pub fn vmovn_u64(a: uint64x2_t) -> uint32x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmul_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe { simd_mul(a, b) } } @@ -32329,6 +32465,7 @@ pub fn vmul_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmulq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe { simd_mul(a, b) } } @@ -32386,6 +32523,7 @@ pub fn vmulq_f32(a: float32x4_t, b: float32x4_t) -> float32x4_t { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmul_lane_f16(a: float16x4_t, v: float16x4_t) -> float16x4_t { static_assert_uimm_bits!(LANE, 2); unsafe { @@ -32407,6 +32545,7 @@ pub fn vmul_lane_f16(a: float16x4_t, v: float16x4_t) -> float16 #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmulq_lane_f16(a: float16x8_t, v: float16x4_t) -> float16x8_t { static_assert_uimm_bits!(LANE, 2); unsafe { @@ -33022,6 +33161,7 @@ pub fn vmulq_laneq_u32(a: uint32x4_t, b: uint32x4_t) -> uint32x )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmul_n_f16(a: float16x4_t, b: f16) -> float16x4_t { unsafe { simd_mul(a, vdup_n_f16(b)) } } @@ -33036,6 +33176,7 @@ pub fn vmul_n_f16(a: float16x4_t, b: f16) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vmulq_n_f16(a: float16x8_t, b: f16) -> float16x8_t { unsafe { simd_mul(a, vdupq_n_f16(b)) } } @@ -34369,6 +34510,7 @@ pub fn vmvnq_u8(a: uint8x16_t) -> uint8x16_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vneg_f16(a: float16x4_t) -> float16x4_t { unsafe { simd_neg(a) } } @@ -34383,6 +34525,7 @@ pub fn vneg_f16(a: float16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vnegq_f16(a: float16x8_t) -> float16x8_t { unsafe { simd_neg(a) } } @@ -35613,6 +35756,7 @@ pub fn vpadalq_u32(a: uint64x2_t, b: uint32x4_t) -> uint64x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vpadd_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vpadd.v4f16")] @@ -41943,6 +42087,7 @@ pub fn vraddhn_u64(a: uint64x2_t, b: uint64x2_t) -> uint32x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vrecpe_f16(a: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vrecpe.v4f16")] @@ -41965,6 +42110,7 @@ pub fn vrecpe_f16(a: float16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vrecpeq_f16(a: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vrecpe.v8f16")] @@ -42103,6 +42249,7 @@ pub fn vrecpeq_u32(a: uint32x4_t) -> uint32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vrecps_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vrecps.v4f16")] @@ -42125,6 +42272,7 @@ pub fn vrecps_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vrecpsq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vrecps.v8f16")] @@ -42206,6 +42354,7 @@ pub fn vrecpsq_f32(a: float32x4_t, b: float32x4_t) -> float32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f32_f16(a: float16x4_t) -> float32x2_t { unsafe { transmute(a) } } @@ -42221,6 +42370,7 @@ pub fn vreinterpret_f32_f16(a: float16x4_t) -> float32x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f32_f16(a: float16x4_t) -> float32x2_t { let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { @@ -42240,6 +42390,7 @@ pub fn vreinterpret_f32_f16(a: float16x4_t) -> float32x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_s8_f16(a: float16x4_t) -> int8x8_t { unsafe { transmute(a) } } @@ -42255,6 +42406,7 @@ pub fn vreinterpret_s8_f16(a: float16x4_t) -> int8x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_s8_f16(a: float16x4_t) -> int8x8_t { let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { @@ -42274,6 +42426,7 @@ pub fn vreinterpret_s8_f16(a: float16x4_t) -> int8x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_s16_f16(a: float16x4_t) -> int16x4_t { unsafe { transmute(a) } } @@ -42289,6 +42442,7 @@ pub fn vreinterpret_s16_f16(a: float16x4_t) -> int16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_s16_f16(a: float16x4_t) -> int16x4_t { let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { @@ -42308,6 +42462,7 @@ pub fn vreinterpret_s16_f16(a: float16x4_t) -> int16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_s32_f16(a: float16x4_t) -> int32x2_t { unsafe { transmute(a) } } @@ -42323,6 +42478,7 @@ pub fn vreinterpret_s32_f16(a: float16x4_t) -> int32x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_s32_f16(a: float16x4_t) -> int32x2_t { let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { @@ -42342,6 +42498,7 @@ pub fn vreinterpret_s32_f16(a: float16x4_t) -> int32x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_s64_f16(a: float16x4_t) -> int64x1_t { unsafe { transmute(a) } } @@ -42357,6 +42514,7 @@ pub fn vreinterpret_s64_f16(a: float16x4_t) -> int64x1_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_s64_f16(a: float16x4_t) -> int64x1_t { let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { transmute(a) } @@ -42373,6 +42531,7 @@ pub fn vreinterpret_s64_f16(a: float16x4_t) -> int64x1_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_u8_f16(a: float16x4_t) -> uint8x8_t { unsafe { transmute(a) } } @@ -42388,6 +42547,7 @@ pub fn vreinterpret_u8_f16(a: float16x4_t) -> uint8x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_u8_f16(a: float16x4_t) -> uint8x8_t { let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { @@ -42407,6 +42567,7 @@ pub fn vreinterpret_u8_f16(a: float16x4_t) -> uint8x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_u16_f16(a: float16x4_t) -> uint16x4_t { unsafe { transmute(a) } } @@ -42422,6 +42583,7 @@ pub fn vreinterpret_u16_f16(a: float16x4_t) -> uint16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_u16_f16(a: float16x4_t) -> uint16x4_t { let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { @@ -42441,6 +42603,7 @@ pub fn vreinterpret_u16_f16(a: float16x4_t) -> uint16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_u32_f16(a: float16x4_t) -> uint32x2_t { unsafe { transmute(a) } } @@ -42456,6 +42619,7 @@ pub fn vreinterpret_u32_f16(a: float16x4_t) -> uint32x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_u32_f16(a: float16x4_t) -> uint32x2_t { let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { @@ -42475,6 +42639,7 @@ pub fn vreinterpret_u32_f16(a: float16x4_t) -> uint32x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_u64_f16(a: float16x4_t) -> uint64x1_t { unsafe { transmute(a) } } @@ -42490,6 +42655,7 @@ pub fn vreinterpret_u64_f16(a: float16x4_t) -> uint64x1_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_u64_f16(a: float16x4_t) -> uint64x1_t { let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { transmute(a) } @@ -42506,6 +42672,7 @@ pub fn vreinterpret_u64_f16(a: float16x4_t) -> uint64x1_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_p8_f16(a: float16x4_t) -> poly8x8_t { unsafe { transmute(a) } } @@ -42521,6 +42688,7 @@ pub fn vreinterpret_p8_f16(a: float16x4_t) -> poly8x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_p8_f16(a: float16x4_t) -> poly8x8_t { let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { @@ -42540,6 +42708,7 @@ pub fn vreinterpret_p8_f16(a: float16x4_t) -> poly8x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_p16_f16(a: float16x4_t) -> poly16x4_t { unsafe { transmute(a) } } @@ -42555,6 +42724,7 @@ pub fn vreinterpret_p16_f16(a: float16x4_t) -> poly16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_p16_f16(a: float16x4_t) -> poly16x4_t { let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { @@ -42574,6 +42744,7 @@ pub fn vreinterpret_p16_f16(a: float16x4_t) -> poly16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f32_f16(a: float16x8_t) -> float32x4_t { unsafe { transmute(a) } } @@ -42589,6 +42760,7 @@ pub fn vreinterpretq_f32_f16(a: float16x8_t) -> float32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f32_f16(a: float16x8_t) -> float32x4_t { let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -42608,6 +42780,7 @@ pub fn vreinterpretq_f32_f16(a: float16x8_t) -> float32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_s8_f16(a: float16x8_t) -> int8x16_t { unsafe { transmute(a) } } @@ -42623,6 +42796,7 @@ pub fn vreinterpretq_s8_f16(a: float16x8_t) -> int8x16_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_s8_f16(a: float16x8_t) -> int8x16_t { let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -42646,6 +42820,7 @@ pub fn vreinterpretq_s8_f16(a: float16x8_t) -> int8x16_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_s16_f16(a: float16x8_t) -> int16x8_t { unsafe { transmute(a) } } @@ -42661,6 +42836,7 @@ pub fn vreinterpretq_s16_f16(a: float16x8_t) -> int16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_s16_f16(a: float16x8_t) -> int16x8_t { let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -42680,6 +42856,7 @@ pub fn vreinterpretq_s16_f16(a: float16x8_t) -> int16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_s32_f16(a: float16x8_t) -> int32x4_t { unsafe { transmute(a) } } @@ -42695,6 +42872,7 @@ pub fn vreinterpretq_s32_f16(a: float16x8_t) -> int32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_s32_f16(a: float16x8_t) -> int32x4_t { let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -42714,6 +42892,7 @@ pub fn vreinterpretq_s32_f16(a: float16x8_t) -> int32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_s64_f16(a: float16x8_t) -> int64x2_t { unsafe { transmute(a) } } @@ -42729,6 +42908,7 @@ pub fn vreinterpretq_s64_f16(a: float16x8_t) -> int64x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_s64_f16(a: float16x8_t) -> int64x2_t { let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -42748,6 +42928,7 @@ pub fn vreinterpretq_s64_f16(a: float16x8_t) -> int64x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_u8_f16(a: float16x8_t) -> uint8x16_t { unsafe { transmute(a) } } @@ -42763,6 +42944,7 @@ pub fn vreinterpretq_u8_f16(a: float16x8_t) -> uint8x16_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_u8_f16(a: float16x8_t) -> uint8x16_t { let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -42786,6 +42968,7 @@ pub fn vreinterpretq_u8_f16(a: float16x8_t) -> uint8x16_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_u16_f16(a: float16x8_t) -> uint16x8_t { unsafe { transmute(a) } } @@ -42801,6 +42984,7 @@ pub fn vreinterpretq_u16_f16(a: float16x8_t) -> uint16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_u16_f16(a: float16x8_t) -> uint16x8_t { let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -42820,6 +43004,7 @@ pub fn vreinterpretq_u16_f16(a: float16x8_t) -> uint16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_u32_f16(a: float16x8_t) -> uint32x4_t { unsafe { transmute(a) } } @@ -42835,6 +43020,7 @@ pub fn vreinterpretq_u32_f16(a: float16x8_t) -> uint32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_u32_f16(a: float16x8_t) -> uint32x4_t { let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -42854,6 +43040,7 @@ pub fn vreinterpretq_u32_f16(a: float16x8_t) -> uint32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_u64_f16(a: float16x8_t) -> uint64x2_t { unsafe { transmute(a) } } @@ -42869,6 +43056,7 @@ pub fn vreinterpretq_u64_f16(a: float16x8_t) -> uint64x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_u64_f16(a: float16x8_t) -> uint64x2_t { let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -42888,6 +43076,7 @@ pub fn vreinterpretq_u64_f16(a: float16x8_t) -> uint64x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_p8_f16(a: float16x8_t) -> poly8x16_t { unsafe { transmute(a) } } @@ -42903,6 +43092,7 @@ pub fn vreinterpretq_p8_f16(a: float16x8_t) -> poly8x16_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_p8_f16(a: float16x8_t) -> poly8x16_t { let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -42926,6 +43116,7 @@ pub fn vreinterpretq_p8_f16(a: float16x8_t) -> poly8x16_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_p16_f16(a: float16x8_t) -> poly16x8_t { unsafe { transmute(a) } } @@ -42941,6 +43132,7 @@ pub fn vreinterpretq_p16_f16(a: float16x8_t) -> poly16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_p16_f16(a: float16x8_t) -> poly16x8_t { let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -42960,6 +43152,7 @@ pub fn vreinterpretq_p16_f16(a: float16x8_t) -> poly16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_f32(a: float32x2_t) -> float16x4_t { unsafe { transmute(a) } } @@ -42975,6 +43168,7 @@ pub fn vreinterpret_f16_f32(a: float32x2_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_f32(a: float32x2_t) -> float16x4_t { let a: float32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; unsafe { @@ -42994,6 +43188,7 @@ pub fn vreinterpret_f16_f32(a: float32x2_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_f32(a: float32x4_t) -> float16x8_t { unsafe { transmute(a) } } @@ -43009,6 +43204,7 @@ pub fn vreinterpretq_f16_f32(a: float32x4_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_f32(a: float32x4_t) -> float16x8_t { let a: float32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { @@ -43028,6 +43224,7 @@ pub fn vreinterpretq_f16_f32(a: float32x4_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_s8(a: int8x8_t) -> float16x4_t { unsafe { transmute(a) } } @@ -43043,6 +43240,7 @@ pub fn vreinterpret_f16_s8(a: int8x8_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_s8(a: int8x8_t) -> float16x4_t { let a: int8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -43062,6 +43260,7 @@ pub fn vreinterpret_f16_s8(a: int8x8_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_s8(a: int8x16_t) -> float16x8_t { unsafe { transmute(a) } } @@ -43077,6 +43276,7 @@ pub fn vreinterpretq_f16_s8(a: int8x16_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_s8(a: int8x16_t) -> float16x8_t { let a: int8x16_t = unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; @@ -43097,6 +43297,7 @@ pub fn vreinterpretq_f16_s8(a: int8x16_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_s16(a: int16x4_t) -> float16x4_t { unsafe { transmute(a) } } @@ -43112,6 +43313,7 @@ pub fn vreinterpret_f16_s16(a: int16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_s16(a: int16x4_t) -> float16x4_t { let a: int16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { @@ -43131,6 +43333,7 @@ pub fn vreinterpret_f16_s16(a: int16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_s16(a: int16x8_t) -> float16x8_t { unsafe { transmute(a) } } @@ -43146,6 +43349,7 @@ pub fn vreinterpretq_f16_s16(a: int16x8_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_s16(a: int16x8_t) -> float16x8_t { let a: int16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -43165,6 +43369,7 @@ pub fn vreinterpretq_f16_s16(a: int16x8_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_s32(a: int32x2_t) -> float16x4_t { unsafe { transmute(a) } } @@ -43180,6 +43385,7 @@ pub fn vreinterpret_f16_s32(a: int32x2_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_s32(a: int32x2_t) -> float16x4_t { let a: int32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; unsafe { @@ -43199,6 +43405,7 @@ pub fn vreinterpret_f16_s32(a: int32x2_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_s32(a: int32x4_t) -> float16x8_t { unsafe { transmute(a) } } @@ -43214,6 +43421,7 @@ pub fn vreinterpretq_f16_s32(a: int32x4_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_s32(a: int32x4_t) -> float16x8_t { let a: int32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { @@ -43233,6 +43441,7 @@ pub fn vreinterpretq_f16_s32(a: int32x4_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_s64(a: int64x1_t) -> float16x4_t { unsafe { transmute(a) } } @@ -43248,6 +43457,7 @@ pub fn vreinterpret_f16_s64(a: int64x1_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_s64(a: int64x1_t) -> float16x4_t { unsafe { let ret_val: float16x4_t = transmute(a); @@ -43266,6 +43476,7 @@ pub fn vreinterpret_f16_s64(a: int64x1_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_s64(a: int64x2_t) -> float16x8_t { unsafe { transmute(a) } } @@ -43281,6 +43492,7 @@ pub fn vreinterpretq_f16_s64(a: int64x2_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_s64(a: int64x2_t) -> float16x8_t { let a: int64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; unsafe { @@ -43300,6 +43512,7 @@ pub fn vreinterpretq_f16_s64(a: int64x2_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_u8(a: uint8x8_t) -> float16x4_t { unsafe { transmute(a) } } @@ -43315,6 +43528,7 @@ pub fn vreinterpret_f16_u8(a: uint8x8_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_u8(a: uint8x8_t) -> float16x4_t { let a: uint8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -43334,6 +43548,7 @@ pub fn vreinterpret_f16_u8(a: uint8x8_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_u8(a: uint8x16_t) -> float16x8_t { unsafe { transmute(a) } } @@ -43349,6 +43564,7 @@ pub fn vreinterpretq_f16_u8(a: uint8x16_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_u8(a: uint8x16_t) -> float16x8_t { let a: uint8x16_t = unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; @@ -43369,6 +43585,7 @@ pub fn vreinterpretq_f16_u8(a: uint8x16_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_u16(a: uint16x4_t) -> float16x4_t { unsafe { transmute(a) } } @@ -43384,6 +43601,7 @@ pub fn vreinterpret_f16_u16(a: uint16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_u16(a: uint16x4_t) -> float16x4_t { let a: uint16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { @@ -43403,6 +43621,7 @@ pub fn vreinterpret_f16_u16(a: uint16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_u16(a: uint16x8_t) -> float16x8_t { unsafe { transmute(a) } } @@ -43418,6 +43637,7 @@ pub fn vreinterpretq_f16_u16(a: uint16x8_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_u16(a: uint16x8_t) -> float16x8_t { let a: uint16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -43437,6 +43657,7 @@ pub fn vreinterpretq_f16_u16(a: uint16x8_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_u32(a: uint32x2_t) -> float16x4_t { unsafe { transmute(a) } } @@ -43452,6 +43673,7 @@ pub fn vreinterpret_f16_u32(a: uint32x2_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_u32(a: uint32x2_t) -> float16x4_t { let a: uint32x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; unsafe { @@ -43471,6 +43693,7 @@ pub fn vreinterpret_f16_u32(a: uint32x2_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_u32(a: uint32x4_t) -> float16x8_t { unsafe { transmute(a) } } @@ -43486,6 +43709,7 @@ pub fn vreinterpretq_f16_u32(a: uint32x4_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_u32(a: uint32x4_t) -> float16x8_t { let a: uint32x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { @@ -43505,6 +43729,7 @@ pub fn vreinterpretq_f16_u32(a: uint32x4_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_u64(a: uint64x1_t) -> float16x4_t { unsafe { transmute(a) } } @@ -43520,6 +43745,7 @@ pub fn vreinterpret_f16_u64(a: uint64x1_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_u64(a: uint64x1_t) -> float16x4_t { unsafe { let ret_val: float16x4_t = transmute(a); @@ -43538,6 +43764,7 @@ pub fn vreinterpret_f16_u64(a: uint64x1_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_u64(a: uint64x2_t) -> float16x8_t { unsafe { transmute(a) } } @@ -43553,6 +43780,7 @@ pub fn vreinterpretq_f16_u64(a: uint64x2_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_u64(a: uint64x2_t) -> float16x8_t { let a: uint64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; unsafe { @@ -43572,6 +43800,7 @@ pub fn vreinterpretq_f16_u64(a: uint64x2_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_p8(a: poly8x8_t) -> float16x4_t { unsafe { transmute(a) } } @@ -43587,6 +43816,7 @@ pub fn vreinterpret_f16_p8(a: poly8x8_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_p8(a: poly8x8_t) -> float16x4_t { let a: poly8x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -43606,6 +43836,7 @@ pub fn vreinterpret_f16_p8(a: poly8x8_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_p8(a: poly8x16_t) -> float16x8_t { unsafe { transmute(a) } } @@ -43621,6 +43852,7 @@ pub fn vreinterpretq_f16_p8(a: poly8x16_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_p8(a: poly8x16_t) -> float16x8_t { let a: poly8x16_t = unsafe { simd_shuffle!(a, a, [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]) }; @@ -43641,6 +43873,7 @@ pub fn vreinterpretq_f16_p8(a: poly8x16_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_p16(a: poly16x4_t) -> float16x4_t { unsafe { transmute(a) } } @@ -43656,6 +43889,7 @@ pub fn vreinterpret_f16_p16(a: poly16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_p16(a: poly16x4_t) -> float16x4_t { let a: poly16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { @@ -43675,6 +43909,7 @@ pub fn vreinterpret_f16_p16(a: poly16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_p16(a: poly16x8_t) -> float16x8_t { unsafe { transmute(a) } } @@ -43690,6 +43925,7 @@ pub fn vreinterpretq_f16_p16(a: poly16x8_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_p16(a: poly16x8_t) -> float16x8_t { let a: poly16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -43709,6 +43945,7 @@ pub fn vreinterpretq_f16_p16(a: poly16x8_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_p128(a: p128) -> float16x8_t { unsafe { transmute(a) } } @@ -43724,6 +43961,7 @@ pub fn vreinterpretq_f16_p128(a: p128) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_p128(a: p128) -> float16x8_t { unsafe { let ret_val: float16x8_t = transmute(a); @@ -43742,6 +43980,7 @@ pub fn vreinterpretq_f16_p128(a: p128) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_p64_f16(a: float16x4_t) -> poly64x1_t { unsafe { transmute(a) } } @@ -43757,6 +43996,7 @@ pub fn vreinterpret_p64_f16(a: float16x4_t) -> poly64x1_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_p64_f16(a: float16x4_t) -> poly64x1_t { let a: float16x4_t = unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) }; unsafe { transmute(a) } @@ -43773,6 +44013,7 @@ pub fn vreinterpret_p64_f16(a: float16x4_t) -> poly64x1_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_p128_f16(a: float16x8_t) -> p128 { unsafe { transmute(a) } } @@ -43788,6 +44029,7 @@ pub fn vreinterpretq_p128_f16(a: float16x8_t) -> p128 { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_p128_f16(a: float16x8_t) -> p128 { let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { transmute(a) } @@ -43804,6 +44046,7 @@ pub fn vreinterpretq_p128_f16(a: float16x8_t) -> p128 { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_p64_f16(a: float16x8_t) -> poly64x2_t { unsafe { transmute(a) } } @@ -43819,6 +44062,7 @@ pub fn vreinterpretq_p64_f16(a: float16x8_t) -> poly64x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_p64_f16(a: float16x8_t) -> poly64x2_t { let a: float16x8_t = unsafe { simd_shuffle!(a, a, [7, 6, 5, 4, 3, 2, 1, 0]) }; unsafe { @@ -43838,6 +44082,7 @@ pub fn vreinterpretq_p64_f16(a: float16x8_t) -> poly64x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_p64(a: poly64x1_t) -> float16x4_t { unsafe { transmute(a) } } @@ -43853,6 +44098,7 @@ pub fn vreinterpret_f16_p64(a: poly64x1_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpret_f16_p64(a: poly64x1_t) -> float16x4_t { unsafe { let ret_val: float16x4_t = transmute(a); @@ -43871,6 +44117,7 @@ pub fn vreinterpret_f16_p64(a: poly64x1_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_p64(a: poly64x2_t) -> float16x8_t { unsafe { transmute(a) } } @@ -43886,6 +44133,7 @@ pub fn vreinterpretq_f16_p64(a: poly64x2_t) -> float16x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vreinterpretq_f16_p64(a: poly64x2_t) -> float16x8_t { let a: poly64x2_t = unsafe { simd_shuffle!(a, a, [1, 0]) }; unsafe { @@ -57882,6 +58130,7 @@ pub fn vrev64q_u8(a: uint8x16_t) -> uint8x16_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vrev64_f16(a: float16x4_t) -> float16x4_t { unsafe { simd_shuffle!(a, a, [3, 2, 1, 0]) } } @@ -57896,6 +58145,7 @@ pub fn vrev64_f16(a: float16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vrev64q_f16(a: float16x8_t) -> float16x8_t { unsafe { simd_shuffle!(a, a, [3, 2, 1, 0, 7, 6, 5, 4]) } } @@ -58258,6 +58508,7 @@ pub fn vrhaddq_u32(a: uint32x4_t, b: uint32x4_t) -> uint32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vrndn_f16(a: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -58280,6 +58531,7 @@ pub fn vrndn_f16(a: float16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vrndnq_f16(a: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { #[cfg_attr( @@ -59366,6 +59618,7 @@ pub fn vrshrn_n_u64(a: uint64x2_t) -> uint32x2_t { assert_instr(frsqrte) )] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vrsqrte_f16(a: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vrsqrte.v4f16")] @@ -59388,6 +59641,7 @@ pub fn vrsqrte_f16(a: float16x4_t) -> float16x4_t { assert_instr(frsqrte) )] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vrsqrteq_f16(a: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vrsqrte.v8f16")] @@ -59526,6 +59780,7 @@ pub fn vrsqrteq_u32(a: uint32x4_t) -> uint32x4_t { assert_instr(frsqrts) )] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vrsqrts_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vrsqrts.v4f16")] @@ -59548,6 +59803,7 @@ pub fn vrsqrts_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { assert_instr(frsqrts) )] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vrsqrtsq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vrsqrts.v8f16")] @@ -60231,6 +60487,7 @@ pub fn vrsubhn_u64(a: uint64x2_t, b: uint64x2_t) -> uint32x2_t { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vset_lane_f16(a: f16, b: float16x4_t) -> float16x4_t { static_assert_uimm_bits!(LANE, 2); unsafe { simd_insert!(b, LANE as u32, a) } @@ -60247,6 +60504,7 @@ pub fn vset_lane_f16(a: f16, b: float16x4_t) -> float16x4_t { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vsetq_lane_f16(a: f16, b: float16x8_t) -> float16x8_t { static_assert_uimm_bits!(LANE, 3); unsafe { simd_insert!(b, LANE as u32, a) } @@ -63699,6 +63957,7 @@ pub fn vsriq_n_p16(a: poly16x8_t, b: poly16x8_t) -> poly16x8_t { #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr("vst1.16"))] pub unsafe fn vst1_f16(ptr: *mut f16, a: float16x4_t) { vst1_v4f16( @@ -63716,6 +63975,7 @@ pub unsafe fn vst1_f16(ptr: *mut f16, a: float16x4_t) { #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr("vst1.16"))] pub unsafe fn vst1q_f16(ptr: *mut f16, a: float16x8_t) { vst1q_v8f16( @@ -63734,6 +63994,7 @@ pub unsafe fn vst1q_f16(ptr: *mut f16, a: float16x8_t) { #[cfg_attr(test, assert_instr(vst1))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst1_f16_x2(a: *mut f16, b: float16x4x2_t) { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vst1x2.p0.v4f16")] @@ -63751,6 +64012,7 @@ pub unsafe fn vst1_f16_x2(a: *mut f16, b: float16x4x2_t) { #[cfg_attr(test, assert_instr(vst1))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst1q_f16_x2(a: *mut f16, b: float16x8x2_t) { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vst1x2.p0.v8f16")] @@ -63767,6 +64029,7 @@ pub unsafe fn vst1q_f16_x2(a: *mut f16, b: float16x8x2_t) { #[cfg_attr(test, assert_instr(st1))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst1_f16_x2(a: *mut f16, b: float16x4x2_t) { unsafe extern "unadjusted" { #[cfg_attr( @@ -63786,6 +64049,7 @@ pub unsafe fn vst1_f16_x2(a: *mut f16, b: float16x4x2_t) { #[cfg_attr(test, assert_instr(st1))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst1q_f16_x2(a: *mut f16, b: float16x8x2_t) { unsafe extern "unadjusted" { #[cfg_attr( @@ -63806,6 +64070,7 @@ pub unsafe fn vst1q_f16_x2(a: *mut f16, b: float16x8x2_t) { #[cfg_attr(test, assert_instr(vst1))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst1_f16_x3(a: *mut f16, b: float16x4x3_t) { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vst1x3.p0.v4f16")] @@ -63823,6 +64088,7 @@ pub unsafe fn vst1_f16_x3(a: *mut f16, b: float16x4x3_t) { #[cfg_attr(test, assert_instr(vst1))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst1q_f16_x3(a: *mut f16, b: float16x8x3_t) { unsafe extern "unadjusted" { #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vst1x3.p0.v8f16")] @@ -63839,6 +64105,7 @@ pub unsafe fn vst1q_f16_x3(a: *mut f16, b: float16x8x3_t) { #[cfg_attr(test, assert_instr(st1))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst1_f16_x3(a: *mut f16, b: float16x4x3_t) { unsafe extern "unadjusted" { #[cfg_attr( @@ -63858,6 +64125,7 @@ pub unsafe fn vst1_f16_x3(a: *mut f16, b: float16x4x3_t) { #[cfg_attr(test, assert_instr(st1))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst1q_f16_x3(a: *mut f16, b: float16x8x3_t) { unsafe extern "unadjusted" { #[cfg_attr( @@ -63877,6 +64145,7 @@ pub unsafe fn vst1q_f16_x3(a: *mut f16, b: float16x8x3_t) { #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(vst1))] pub unsafe fn vst1_f16_x4(a: *mut f16, b: float16x4x4_t) { unsafe extern "unadjusted" { @@ -63900,6 +64169,7 @@ pub unsafe fn vst1_f16_x4(a: *mut f16, b: float16x4x4_t) { #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(vst1))] pub unsafe fn vst1q_f16_x4(a: *mut f16, b: float16x8x4_t) { unsafe extern "unadjusted" { @@ -63923,6 +64193,7 @@ pub unsafe fn vst1q_f16_x4(a: *mut f16, b: float16x8x4_t) { #[cfg_attr(test, assert_instr(st1))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst1_f16_x4(a: *mut f16, b: float16x4x4_t) { unsafe extern "unadjusted" { #[cfg_attr( @@ -63948,6 +64219,7 @@ pub unsafe fn vst1_f16_x4(a: *mut f16, b: float16x4x4_t) { #[cfg_attr(test, assert_instr(st1))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst1q_f16_x4(a: *mut f16, b: float16x8x4_t) { unsafe extern "unadjusted" { #[cfg_attr( @@ -64556,6 +64828,7 @@ pub unsafe fn vst1q_f32_x4(a: *mut f32, b: float32x4x4_t) { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst1_lane_f16(a: *mut f16, b: float16x4_t) { static_assert_uimm_bits!(LANE, 2); *a = simd_extract!(b, LANE as u32); @@ -64574,6 +64847,7 @@ pub unsafe fn vst1_lane_f16(a: *mut f16, b: float16x4_t) { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst1q_lane_f16(a: *mut f16, b: float16x8_t) { static_assert_uimm_bits!(LANE, 3); *a = simd_extract!(b, LANE as u32); @@ -67138,6 +67412,7 @@ unsafe fn vst1q_v8i16(addr: *const i8, val: int16x8_t, align: i32) { #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr("vst1.16"))] unsafe fn vst1_v4f16(addr: *const i8, val: float16x4_t, align: i32) { unsafe extern "unadjusted" { @@ -67155,6 +67430,7 @@ unsafe fn vst1_v4f16(addr: *const i8, val: float16x4_t, align: i32) { #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(all(test, target_arch = "arm"), assert_instr("vst1.16"))] unsafe fn vst1q_v8f16(addr: *const i8, val: float16x8_t, align: i32) { unsafe extern "unadjusted" { @@ -67196,6 +67472,7 @@ pub unsafe fn vst1q_lane_p64(a: *mut p64, b: poly64x2_t) { #[cfg(not(target_arch = "arm"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(st2))] pub unsafe fn vst2_f16(a: *mut f16, b: float16x4x2_t) { unsafe extern "unadjusted" { @@ -67215,6 +67492,7 @@ pub unsafe fn vst2_f16(a: *mut f16, b: float16x4x2_t) { #[cfg(not(target_arch = "arm"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(st2))] pub unsafe fn vst2q_f16(a: *mut f16, b: float16x8x2_t) { unsafe extern "unadjusted" { @@ -67235,6 +67513,7 @@ pub unsafe fn vst2q_f16(a: *mut f16, b: float16x8x2_t) { #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(vst2))] pub unsafe fn vst2_f16(a: *mut f16, b: float16x4x2_t) { unsafe extern "unadjusted" { @@ -67252,6 +67531,7 @@ pub unsafe fn vst2_f16(a: *mut f16, b: float16x4x2_t) { #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(vst2))] pub unsafe fn vst2q_f16(a: *mut f16, b: float16x8x2_t) { unsafe extern "unadjusted" { @@ -67550,6 +67830,7 @@ pub unsafe fn vst2q_s32(a: *mut i32, b: int32x4x2_t) { #[cfg_attr(test, assert_instr(st2, LANE = 0))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst2_lane_f16(a: *mut f16, b: float16x4x2_t) { static_assert_uimm_bits!(LANE, 2); unsafe extern "unadjusted" { @@ -67571,6 +67852,7 @@ pub unsafe fn vst2_lane_f16(a: *mut f16, b: float16x4x2_t) { #[cfg_attr(test, assert_instr(st2, LANE = 0))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst2q_lane_f16(a: *mut f16, b: float16x8x2_t) { static_assert_uimm_bits!(LANE, 3); unsafe extern "unadjusted" { @@ -67593,6 +67875,7 @@ pub unsafe fn vst2q_lane_f16(a: *mut f16, b: float16x8x2_t) { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst2_lane_f16(a: *mut f16, b: float16x4x2_t) { static_assert_uimm_bits!(LANE, 2); unsafe extern "unadjusted" { @@ -67612,6 +67895,7 @@ pub unsafe fn vst2_lane_f16(a: *mut f16, b: float16x4x2_t) { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst2q_lane_f16(a: *mut f16, b: float16x8x2_t) { static_assert_uimm_bits!(LANE, 1); unsafe extern "unadjusted" { @@ -68413,6 +68697,7 @@ pub unsafe fn vst2q_p16(a: *mut p16, b: poly16x8x2_t) { #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(vst3))] pub unsafe fn vst3_f16(a: *mut f16, b: float16x4x3_t) { unsafe extern "unadjusted" { @@ -68430,6 +68715,7 @@ pub unsafe fn vst3_f16(a: *mut f16, b: float16x4x3_t) { #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(vst3))] pub unsafe fn vst3q_f16(a: *mut f16, b: float16x8x3_t) { unsafe extern "unadjusted" { @@ -68446,6 +68732,7 @@ pub unsafe fn vst3q_f16(a: *mut f16, b: float16x8x3_t) { #[cfg(not(target_arch = "arm"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(st3))] pub unsafe fn vst3_f16(a: *mut f16, b: float16x4x3_t) { unsafe extern "unadjusted" { @@ -68465,6 +68752,7 @@ pub unsafe fn vst3_f16(a: *mut f16, b: float16x4x3_t) { #[cfg(not(target_arch = "arm"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(st3))] pub unsafe fn vst3q_f16(a: *mut f16, b: float16x8x3_t) { unsafe extern "unadjusted" { @@ -68767,6 +69055,7 @@ pub unsafe fn vst3q_s32(a: *mut i32, b: int32x4x3_t) { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst3_lane_f16(a: *mut f16, b: float16x4x3_t) { static_assert_uimm_bits!(LANE, 2); unsafe extern "unadjusted" { @@ -68793,6 +69082,7 @@ pub unsafe fn vst3_lane_f16(a: *mut f16, b: float16x4x3_t) { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst3q_lane_f16(a: *mut f16, b: float16x8x3_t) { static_assert_uimm_bits!(LANE, 3); unsafe extern "unadjusted" { @@ -68818,6 +69108,7 @@ pub unsafe fn vst3q_lane_f16(a: *mut f16, b: float16x8x3_t) { #[cfg_attr(test, assert_instr(st3, LANE = 0))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst3_lane_f16(a: *mut f16, b: float16x4x3_t) { static_assert_uimm_bits!(LANE, 2); unsafe extern "unadjusted" { @@ -68839,6 +69130,7 @@ pub unsafe fn vst3_lane_f16(a: *mut f16, b: float16x4x3_t) { #[cfg_attr(test, assert_instr(st3, LANE = 0))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst3q_lane_f16(a: *mut f16, b: float16x8x3_t) { static_assert_uimm_bits!(LANE, 3); unsafe extern "unadjusted" { @@ -69685,6 +69977,7 @@ pub unsafe fn vst3q_p16(a: *mut p16, b: poly16x8x3_t) { #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(vst4))] pub unsafe fn vst4_f16(a: *mut f16, b: float16x4x4_t) { unsafe extern "unadjusted" { @@ -69709,6 +70002,7 @@ pub unsafe fn vst4_f16(a: *mut f16, b: float16x4x4_t) { #[cfg_attr(target_arch = "arm", target_feature(enable = "v7"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(vst4))] pub unsafe fn vst4q_f16(a: *mut f16, b: float16x8x4_t) { unsafe extern "unadjusted" { @@ -69732,6 +70026,7 @@ pub unsafe fn vst4q_f16(a: *mut f16, b: float16x8x4_t) { #[cfg(not(target_arch = "arm"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(st4))] pub unsafe fn vst4_f16(a: *mut f16, b: float16x4x4_t) { unsafe extern "unadjusted" { @@ -69751,6 +70046,7 @@ pub unsafe fn vst4_f16(a: *mut f16, b: float16x4x4_t) { #[cfg(not(target_arch = "arm"))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] #[cfg_attr(test, assert_instr(st4))] pub unsafe fn vst4q_f16(a: *mut f16, b: float16x8x4_t) { unsafe extern "unadjusted" { @@ -70102,6 +70398,7 @@ pub unsafe fn vst4q_s32(a: *mut i32, b: int32x4x4_t) { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst4_lane_f16(a: *mut f16, b: float16x4x4_t) { static_assert_uimm_bits!(LANE, 2); unsafe extern "unadjusted" { @@ -70129,6 +70426,7 @@ pub unsafe fn vst4_lane_f16(a: *mut f16, b: float16x4x4_t) { #[rustc_legacy_const_generics(2)] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst4q_lane_f16(a: *mut f16, b: float16x8x4_t) { static_assert_uimm_bits!(LANE, 3); unsafe extern "unadjusted" { @@ -70155,6 +70453,7 @@ pub unsafe fn vst4q_lane_f16(a: *mut f16, b: float16x8x4_t) { #[cfg_attr(test, assert_instr(st4, LANE = 0))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst4_lane_f16(a: *mut f16, b: float16x4x4_t) { static_assert_uimm_bits!(LANE, 2); unsafe extern "unadjusted" { @@ -70183,6 +70482,7 @@ pub unsafe fn vst4_lane_f16(a: *mut f16, b: float16x4x4_t) { #[cfg_attr(test, assert_instr(st4, LANE = 0))] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub unsafe fn vst4q_lane_f16(a: *mut f16, b: float16x8x4_t) { static_assert_uimm_bits!(LANE, 3); unsafe extern "unadjusted" { @@ -71124,6 +71424,7 @@ pub unsafe fn vstrq_p128(a: *mut p128, b: p128) { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vsub_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { unsafe { simd_sub(a, b) } } @@ -71138,6 +71439,7 @@ pub fn vsub_f16(a: float16x4_t, b: float16x4_t) -> float16x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vsubq_f16(a: float16x8_t, b: float16x8_t) -> float16x8_t { unsafe { simd_sub(a, b) } } @@ -73002,6 +73304,7 @@ pub fn vtbx4_p8(a: poly8x8_t, b: poly8x8x4_t, c: uint8x8_t) -> poly8x8_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vtrn_f16(a: float16x4_t, b: float16x4_t) -> float16x4x2_t { unsafe { let a1: float16x4_t = simd_shuffle!(a, b, [0, 4, 2, 6]); @@ -73024,6 +73327,7 @@ pub fn vtrn_f16(a: float16x4_t, b: float16x4_t) -> float16x4x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vtrnq_f16(a: float16x8_t, b: float16x8_t) -> float16x8x2_t { unsafe { let a1: float16x8_t = simd_shuffle!(a, b, [0, 8, 2, 10, 4, 12, 6, 14]); @@ -74134,6 +74438,7 @@ pub fn vusmmlaq_s32(a: int32x4_t, b: uint8x16_t, c: int8x16_t) -> int32x4_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vuzp_f16(a: float16x4_t, b: float16x4_t) -> float16x4x2_t { unsafe { let a0: float16x4_t = simd_shuffle!(a, b, [0, 2, 4, 6]); @@ -74156,6 +74461,7 @@ pub fn vuzp_f16(a: float16x4_t, b: float16x4_t) -> float16x4x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vuzpq_f16(a: float16x8_t, b: float16x8_t) -> float16x8x2_t { unsafe { let a0: float16x8_t = simd_shuffle!(a, b, [0, 2, 4, 6, 8, 10, 12, 14]); @@ -74724,6 +75030,7 @@ pub fn vuzpq_p16(a: poly16x8_t, b: poly16x8_t) -> poly16x8x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vzip_f16(a: float16x4_t, b: float16x4_t) -> float16x4x2_t { unsafe { let a0: float16x4_t = simd_shuffle!(a, b, [0, 4, 1, 5]); @@ -74746,6 +75053,7 @@ pub fn vzip_f16(a: float16x4_t, b: float16x4_t) -> float16x4x2_t { )] #[target_feature(enable = "neon,fp16")] #[unstable(feature = "stdarch_neon_f16", issue = "136306")] +#[cfg(not(target_arch = "arm64ec"))] pub fn vzipq_f16(a: float16x8_t, b: float16x8_t) -> float16x8x2_t { unsafe { let a0: float16x8_t = simd_shuffle!(a, b, [0, 8, 1, 9, 2, 10, 3, 11]); diff --git a/library/stdarch/crates/core_arch/src/arm_shared/neon/mod.rs b/library/stdarch/crates/core_arch/src/arm_shared/neon/mod.rs index 60c9daef68c4..fbd1967c544a 100644 --- a/library/stdarch/crates/core_arch/src/arm_shared/neon/mod.rs +++ b/library/stdarch/crates/core_arch/src/arm_shared/neon/mod.rs @@ -5503,8 +5503,12 @@ mod tests { test_vcombine!(test_vcombine_s16 => vcombine_s16([3_i16, -4, 5, -6], [13_i16, -14, 15, -16])); test_vcombine!(test_vcombine_u16 => vcombine_u16([3_u16, 4, 5, 6], [13_u16, 14, 15, 16])); test_vcombine!(test_vcombine_p16 => vcombine_p16([3_u16, 4, 5, 6], [13_u16, 14, 15, 16])); - test_vcombine!(test_vcombine_f16 => vcombine_f16([3_f16, 4., 5., 6.], - [13_f16, 14., 15., 16.])); + #[cfg(not(target_arch = "arm64ec"))] + mod fp16 { + use super::*; + test_vcombine!(test_vcombine_f16 => vcombine_f16([3_f16, 4., 5., 6.], + [13_f16, 14., 15., 16.])); + } test_vcombine!(test_vcombine_s32 => vcombine_s32([3_i32, -4], [13_i32, -14])); test_vcombine!(test_vcombine_u32 => vcombine_u32([3_u32, 4], [13_u32, 14])); diff --git a/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml b/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml index a31613e6b1ae..a1a837bc6106 100644 --- a/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml +++ b/library/stdarch/crates/stdarch-gen-arm/spec/neon/aarch64.spec.yml @@ -17,6 +17,10 @@ neon-stable: &neon-stable target-not-arm: &target-not-arm FnCall: [cfg, [{ FnCall: [not, ['target_arch = "arm"']]}]] +# #[cfg(not(target_arch = "arm64ec"))] +target-not-arm64ec: &target-not-arm64ec + FnCall: [cfg, [{ FnCall: [not, ['target_arch = "arm64ec"']]}]] + # #[cfg_attr(all(test, not(target_env = "msvc"))] msvc-disabled: &msvc-disabled FnCall: [all, [test, {FnCall: [not, ['target_env = "msvc"']]}]] @@ -169,6 +173,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fabd] safety: safe types: @@ -368,6 +373,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcmp]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["h_f16", "f16", "u16"] @@ -559,6 +565,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcmp]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["h_f16", "f16", "u16"] @@ -642,6 +649,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcmp]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["h_f16", "f16", "u16"] @@ -777,6 +785,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcmp]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["h_f16", "f16", "u16"] @@ -859,6 +868,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcmp]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["h_f16", "f16", "u16"] @@ -931,6 +941,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [facgt]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["h_f16", "f16", "u16", i32] @@ -988,6 +999,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [facge]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["h_f16", "f16", "u16", i32] @@ -1036,6 +1048,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [facgt]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["h_f16", "f16", "u16"] @@ -1078,6 +1091,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [facge]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["h_f16", "f16", "u16"] @@ -1175,6 +1189,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['1']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -1202,6 +1217,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['1']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -1219,6 +1235,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['1']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -1246,6 +1263,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['1']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -1264,6 +1282,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['1']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -1386,6 +1405,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [scvtf]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["s16", "f16", "h_f16_s16", i16] @@ -1402,6 +1422,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtzs]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "s16", "h", i16, 'a as i16'] @@ -1418,6 +1439,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtzu]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "u16", "h", 'a as u16'] @@ -1435,6 +1457,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [ucvtf]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["u16", "f16", "h_f16_u16"] @@ -1486,6 +1509,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtn2]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x8_t, float16x4_t, float32x4_t] @@ -1503,6 +1527,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtl2]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float32x4_t, float16x8_t] @@ -1650,6 +1675,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['1']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -1675,6 +1701,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['1']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -1693,6 +1720,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['1']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -1783,6 +1811,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtas]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, int16x4_t] @@ -1821,6 +1850,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtau]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "u32", 'h_u32_f16'] @@ -1842,6 +1872,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtas]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "i32", 'h_s32_f16'] @@ -1863,6 +1894,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtas]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "i16", 'h_s16_f16', 's32'] @@ -1877,6 +1909,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtau]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "u16", 'h_u16_f16', 'u32'] @@ -1948,6 +1981,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtns]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, int16x4_t] @@ -1968,6 +2002,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtnu]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t] @@ -1987,6 +2022,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtns]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "i32", 'h'] @@ -2007,6 +2043,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtns]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "i16", 'h', 'i32'] @@ -2022,6 +2059,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtnu]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "u32", 'h'] @@ -2042,6 +2080,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtnu]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "u16", 'h', 'u32'] @@ -2077,6 +2116,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtms]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, int16x4_t] @@ -2097,6 +2137,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtmu]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t] @@ -2291,6 +2332,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtps]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, int16x4_t] @@ -2311,6 +2353,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtpu]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t] @@ -2331,6 +2374,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtps]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "i32", 'h'] @@ -2351,6 +2395,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtps]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "i16", 'h', 'i32'] @@ -2365,6 +2410,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtpu]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "u32", 'h'] @@ -2385,6 +2431,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtpu]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "u16", 'h', 'u32'] @@ -2531,6 +2578,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['1']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -2549,6 +2597,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['1']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -2770,6 +2819,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fneg] safety: safe types: @@ -2986,6 +3036,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [frintx] safety: safe types: @@ -3002,6 +3053,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [frintx] safety: safe types: @@ -3033,6 +3085,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [frinta] safety: safe types: @@ -3049,6 +3102,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [frinta] safety: safe types: @@ -3096,6 +3150,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [frintn] safety: safe types: @@ -3130,6 +3185,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [frintm] safety: safe types: @@ -3146,6 +3202,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [frintm] safety: safe types: @@ -3178,6 +3235,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [frintp] safety: safe types: @@ -3193,6 +3251,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [frintp] safety: safe types: @@ -3222,6 +3281,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [frintz] safety: safe types: @@ -3238,6 +3298,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [frintz] safety: safe types: @@ -3273,6 +3334,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [frinti] safety: safe types: @@ -3293,6 +3355,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec # TODO: double check me assert_instr: [frinti] safety: safe @@ -5205,6 +5268,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fmulx] safety: safe types: @@ -5243,6 +5307,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fmulx] safety: safe types: @@ -5385,6 +5450,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['2']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ["const LANE: i32"] safety: safe types: @@ -5438,6 +5504,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['2']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ["const LANE: i32"] safety: safe types: @@ -5462,6 +5529,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fmulx]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, "f16"] @@ -5546,6 +5614,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fmla] safety: safe types: @@ -5582,6 +5651,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fdiv] safety: safe types: @@ -5597,6 +5667,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [nop] safety: safe types: @@ -5637,6 +5708,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [nop] safety: safe types: @@ -5928,6 +6000,7 @@ intrinsics: - *neon-fp16 - *enable-fcma - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fcadd] safety: safe types: @@ -5948,6 +6021,7 @@ intrinsics: - *neon-fp16 - *enable-fcma - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fcadd] safety: safe types: @@ -5988,6 +6062,7 @@ intrinsics: - FnCall: [target_feature, ['enable = "neon,fcma"']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fcmla] safety: safe types: @@ -6028,6 +6103,7 @@ intrinsics: - FnCall: [target_feature, ['enable = "neon,fcma"']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fcmla] safety: safe types: @@ -6069,6 +6145,7 @@ intrinsics: - FnCall: [target_feature, ['enable = "neon,fcma"']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fcmla] safety: safe types: @@ -6113,6 +6190,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['3']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ["const LANE: i32"] safety: safe types: @@ -6158,6 +6236,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['3']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ["const LANE: i32"] safety: safe types: @@ -6203,6 +6282,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['3']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ["const LANE: i32"] safety: safe types: @@ -6245,6 +6325,7 @@ intrinsics: - FnCall: [target_feature, ['enable = "neon,fcma"']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fcmla] safety: safe types: @@ -6290,6 +6371,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['3']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ["const LANE: i32"] safety: safe types: @@ -6337,6 +6419,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['3']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ["const LANE: i32"] safety: safe types: @@ -6384,6 +6467,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['3']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ["const LANE: i32"] safety: safe types: @@ -6430,6 +6514,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['3']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ["const LANE: i32"] safety: safe types: @@ -6473,6 +6558,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['3']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ["const LANE: i32"] safety: safe types: @@ -6568,6 +6654,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fmax] safety: safe types: @@ -6601,6 +6688,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fmaxnm] safety: safe types: @@ -6616,6 +6704,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fminnm] safety: safe types: @@ -6657,6 +6746,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fmaxnmv] safety: safe types: @@ -6673,6 +6763,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fminnmv] safety: safe types: @@ -6689,6 +6780,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fmaxv] safety: safe types: @@ -6708,6 +6800,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fminv] safety: safe types: @@ -6762,6 +6855,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fmin] safety: safe types: @@ -6875,6 +6969,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [faddp] safety: safe types: @@ -6894,6 +6989,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fmaxp] safety: safe types: @@ -6914,6 +7010,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fmaxnmp] safety: safe types: @@ -6934,6 +7031,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fminp] safety: safe types: @@ -6954,6 +7052,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fminnmp] safety: safe types: @@ -8379,6 +8478,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fsqrt]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - float16x4_t @@ -8393,6 +8493,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fsqrt] safety: safe types: @@ -8445,6 +8546,7 @@ intrinsics: - *neon-fp16 - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [frsqrts]]}]] - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [h_f16, "f16"] @@ -8501,6 +8603,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [frecpe]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [h_f16, "f16"] @@ -8557,6 +8660,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [frecps]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [h_f16, "f16"] @@ -8595,6 +8699,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [frecpx]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [h_f16, "f16"] @@ -8687,6 +8792,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [nop] safety: safe types: @@ -9586,6 +9692,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec - FnCall: [cfg_attr, [{FnCall: [all, [test, {FnCall: [not, ['target_env = "msvc"']]}]]}, {FnCall: [assert_instr, [trn1]]}]] safety: safe types: @@ -9647,6 +9754,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec - FnCall: [cfg_attr, [{FnCall: [all, [test, {FnCall: [not, ['target_env = "msvc"']]}]]}, {FnCall: [assert_instr, [trn2]]}]] safety: safe types: @@ -9715,6 +9823,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec - FnCall: [cfg_attr, [{FnCall: [all, [test, {FnCall: [not, ['target_env = "msvc"']]}]]}, {FnCall: [assert_instr, [zip2]]}]] safety: safe types: @@ -9765,6 +9874,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec - FnCall: [cfg_attr, [{FnCall: [all, [test, {FnCall: [not, ['target_env = "msvc"']]}]]}, {FnCall: [assert_instr, [zip1]]}]] safety: safe types: @@ -9826,6 +9936,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec - FnCall: [cfg_attr, [{FnCall: [all, [test, {FnCall: [not, ['target_env = "msvc"']]}]]}, {FnCall: [assert_instr, [uzp1]]}]] safety: safe types: @@ -9891,6 +10002,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec - FnCall: [cfg_attr, [{FnCall: [all, [test, {FnCall: [not, ['target_env = "msvc"']]}]]}, {FnCall: [assert_instr, [uzp2]]}]] safety: safe types: @@ -10180,6 +10292,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['3']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: safe types: @@ -10206,6 +10319,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['3']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: safe types: @@ -10230,6 +10344,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fmsub]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "h_f16"] @@ -10342,6 +10457,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fmadd]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "h_f16"] @@ -10358,6 +10474,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['3']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: safe types: @@ -10377,6 +10494,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['3']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: safe types: @@ -10541,6 +10659,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcmeq]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t, 'f16x4', 'f16x4::new(0.0, 0.0, 0.0, 0.0)'] @@ -10576,6 +10695,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcmp]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "u16", "h_f16"] @@ -10700,6 +10820,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcmp]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["h_f16", "f16", "u16"] @@ -10842,6 +10963,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcmp]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["h_f16", "f16", "u16"] @@ -10972,6 +11094,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcmp]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["h_f16", "f16", "u16"] @@ -11126,6 +11249,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['2']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: safe types: @@ -11146,6 +11270,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [nop] safety: safe types: @@ -11183,6 +11308,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['2']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: safe types: @@ -11328,6 +11454,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcmp]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "u16", 'h_f16'] @@ -11399,6 +11526,7 @@ intrinsics: attr: - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fmls] safety: safe types: @@ -11651,6 +11779,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [frsqrte]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["h_f16", "f16"] @@ -11734,6 +11863,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtau]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t] @@ -11772,6 +11902,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtms]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "i32", 'h'] @@ -11792,6 +11923,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtms]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "i16", 'h', 'i32'] @@ -11807,6 +11939,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtmu]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "u32", 'h'] @@ -11827,6 +11960,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [fcvtmu]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["f16", "u16", 'h', 'u32'] @@ -12857,6 +12991,7 @@ intrinsics: - FnCall: [target_feature, ['enable = "{type[2]}"']] - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [ldr]]}]] - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -12924,6 +13059,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [str]]}]] - FnCall: [allow, ['clippy::cast_ptr_alignment']] - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -13637,6 +13773,7 @@ intrinsics: - *neon-fp16 - *enable-fhm - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fmlal2] safety: safe types: @@ -13660,6 +13797,7 @@ intrinsics: - *enable-fhm - FnCall: [rustc_legacy_const_generics, ['3']] - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: safe types: @@ -13684,6 +13822,7 @@ intrinsics: - *neon-fp16 - *enable-fhm - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fmlal] safety: safe types: @@ -13707,6 +13846,7 @@ intrinsics: - *enable-fhm - FnCall: [rustc_legacy_const_generics, ['3']] - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: safe types: @@ -13731,6 +13871,7 @@ intrinsics: - *neon-fp16 - *enable-fhm - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fmlsl2] safety: safe types: @@ -13753,6 +13894,7 @@ intrinsics: - *enable-fhm - FnCall: [rustc_legacy_const_generics, ['3']] - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: safe types: @@ -13777,6 +13919,7 @@ intrinsics: - *neon-fp16 - *enable-fhm - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [fmlsl] safety: safe types: @@ -13799,6 +13942,7 @@ intrinsics: - *enable-fhm - FnCall: [rustc_legacy_const_generics, ['3']] - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: safe types: diff --git a/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml b/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml index c96c6e2a0c0b..f16a257399bc 100644 --- a/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml +++ b/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml @@ -37,6 +37,10 @@ target-is-arm: &target-is-arm target-not-arm: &target-not-arm FnCall: [cfg, [{ FnCall: [not, ['target_arch = "arm"']]}]] +# #[cfg(not(target_arch = "arm64ec"))] +target-not-arm64ec: &target-not-arm64ec + FnCall: [cfg, [{ FnCall: [not, ['target_arch = "arm64ec"']]}]] + not-arm: ¬-arm FnCall: [not, ['target_arch = "arm"']] @@ -278,6 +282,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fabd]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - float16x4_t @@ -396,6 +401,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fcmeq]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t] @@ -457,6 +463,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fabs]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - float16x4_t @@ -474,6 +481,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fabs]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ['h_f16', 'f16'] @@ -555,6 +563,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fcmgt]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t] @@ -573,6 +582,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fcmgt]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t, f16x4, 'f16x4::new(0.0, 0.0, 0.0, 0.0)'] @@ -651,6 +661,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fcmge]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t] @@ -668,6 +679,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fcmle]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t, f16x4, 'f16x4::new(0.0, 0.0, 0.0, 0.0)'] @@ -849,6 +861,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [facgt]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t] @@ -895,6 +908,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [facge]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t] @@ -935,6 +949,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [facgt]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t] @@ -970,6 +985,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [facge]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t] @@ -1004,6 +1020,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [scvtf]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [int16x4_t, float16x4_t] @@ -1038,6 +1055,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [ucvtf]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [uint16x4_t, float16x4_t] @@ -1109,6 +1127,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['1']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -1140,6 +1159,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['1']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -1171,6 +1191,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['1']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -1229,6 +1250,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['1']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -1482,6 +1504,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['1']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -1501,6 +1524,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [dup]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, f16, 'float16x4', '_n_'] @@ -1519,6 +1543,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['1']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -1740,6 +1765,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['2']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -1758,6 +1784,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['2']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const N: i32'] safety: safe types: @@ -2207,6 +2234,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fneg]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, 'f16'] @@ -2478,6 +2506,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [frintn]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - float16x4_t @@ -2743,6 +2772,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [ld1]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -2773,6 +2803,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ["2"]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: unsafe: [neon] @@ -2793,6 +2824,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [ld1r]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -3385,6 +3417,7 @@ intrinsics: - FnCall: [cfg_attr, [*test-is-arm, {FnCall: [assert_instr, [vld2]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -3413,6 +3446,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [ld2]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -3440,6 +3474,7 @@ intrinsics: - *neon-fp16 - FnCall: [cfg_attr, [*test-is-arm, {FnCall: [assert_instr, [vld2]]}]] - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -3469,6 +3504,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [ld2r]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -3498,6 +3534,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ["2"]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: - "const LANE: i32" safety: @@ -3540,6 +3577,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ["2"]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: - "const LANE: i32" safety: @@ -3580,6 +3618,7 @@ intrinsics: - FnCall: [cfg_attr, [*test-is-arm, {FnCall: [assert_instr, [vld3]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -3608,6 +3647,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [ld3]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -3635,6 +3675,7 @@ intrinsics: - FnCall: [cfg_attr, [*test-is-arm, {FnCall: [assert_instr, [vld3]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -3664,6 +3705,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [ld3r]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -3693,6 +3735,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ["2"]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: - "const LANE: i32" safety: @@ -3737,6 +3780,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ["2"]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: - "const LANE: i32" safety: @@ -4718,6 +4762,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ["2"]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec types: - ['*mut f16', float16x4_t, '2'] - ['*mut f16', float16x8_t, '3'] @@ -4955,6 +5000,7 @@ intrinsics: - *neon-v7 - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [vst1] types: - [f16, float16x4x4_t, float16x4_t] @@ -5098,6 +5144,7 @@ intrinsics: - *target-not-arm - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [st2] safety: unsafe: [neon] @@ -5188,6 +5235,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [st2, 'LANE = 0']]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: unsafe: [neon] @@ -5279,6 +5327,7 @@ intrinsics: - *neon-v7 - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [vst2] safety: unsafe: [neon] @@ -5345,6 +5394,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['2']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: unsafe: [neon] @@ -5555,6 +5605,7 @@ intrinsics: - *neon-v7 - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [vst3] safety: unsafe: [neon] @@ -5623,6 +5674,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['2']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: unsafe: [neon] @@ -5683,6 +5735,7 @@ intrinsics: - *target-not-arm - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [st3] safety: unsafe: [neon] @@ -5747,6 +5800,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [st3, 'LANE = 0']]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: unsafe: [neon] @@ -5960,6 +6014,7 @@ intrinsics: - *neon-v7 - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [vst4] safety: unsafe: [neon] @@ -6029,6 +6084,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['2']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: unsafe: [neon] @@ -6091,6 +6147,7 @@ intrinsics: - *target-not-arm - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [st4] safety: unsafe: [neon] @@ -6157,6 +6214,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [st4, 'LANE = 0']]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: unsafe: [neon] @@ -6317,6 +6375,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fmul]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [f16, float16x4_t] @@ -6366,6 +6425,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['2']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ["const LANE: i32"] safety: safe types: @@ -6569,6 +6629,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fmla]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - float16x4_t @@ -6678,6 +6739,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fsub]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ['f16', float16x4_t] @@ -6696,6 +6758,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fadd]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - float16x4_t @@ -6716,6 +6779,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fadd]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ['h_f16', 'f16'] @@ -7194,6 +7258,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fmax]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - float16x4_t @@ -7236,6 +7301,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fmaxnm]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - float16x4_t @@ -7254,6 +7320,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fminnm]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - float16x4_t @@ -7340,6 +7407,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fmin]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - float16x4_t @@ -7404,6 +7472,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [faddp]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - float16x4_t @@ -8247,6 +8316,7 @@ intrinsics: - FnCall: [cfg_attr, [*test-is-arm, {FnCall: [assert_instr, [vrsqrts]]}]] - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [frsqrts]]}]] - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - float16x4_t @@ -8295,6 +8365,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [frecpe]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - float16x4_t @@ -8343,6 +8414,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [frecps]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - float16x4_t @@ -8675,6 +8747,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [nop]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: # non-q @@ -8737,6 +8810,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [nop]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [poly64x1_t, float16x4_t] @@ -8759,6 +8833,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [rev64]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, "[3, 2, 1, 0]"] @@ -9074,6 +9149,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [nop]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ["u64", float16x4_t] @@ -9147,6 +9223,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ['2']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: safe types: @@ -9578,6 +9655,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [trn2]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, float16x4x2_t, '[0, 4, 2, 6]', '[1, 5, 3, 7]'] @@ -9733,6 +9811,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [zip2]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, float16x4x2_t, '[0, 4, 1, 5]', '[2, 6, 3, 7]'] @@ -9803,6 +9882,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [uzp2]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, float16x4x2_t, '[0, 2, 4, 6]', '[1, 3, 5, 7]'] @@ -10050,6 +10130,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [vst1]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -10077,6 +10158,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [st1]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -10103,6 +10185,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [vst1]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -10179,6 +10262,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [st1]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -10233,6 +10317,7 @@ intrinsics: - FnCall: [cfg_attr, [test, {FnCall: [assert_instr, [st1]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -10376,6 +10461,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fcmge]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t] @@ -10394,6 +10480,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fcmge]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t, f16x4, 'f16x4::new(0.0, 0.0, 0.0, 0.0)'] @@ -10633,6 +10720,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fcvtzu]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t] @@ -10652,6 +10740,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fcvtn]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float32x4_t, float16x4_t] @@ -10668,6 +10757,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fcvtl]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, float32x4_t] @@ -11029,6 +11119,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fmul]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, "f16"] @@ -11141,6 +11232,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fcmgt]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t] @@ -11159,6 +11251,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fcmlt]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, uint16x4_t, f16x4, 'f16x4::new(0.0, 0.0, 0.0, 0.0)'] @@ -11271,6 +11364,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fmls]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - float16x4_t @@ -11386,6 +11480,7 @@ intrinsics: - FnCall: [cfg_attr, [*test-is-arm, {FnCall: [assert_instr, [vrsqrte]]}]] - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [frsqrte]]}]] - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - float16x4_t @@ -11499,6 +11594,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [fcvtzs]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, int16x4_t] @@ -11748,6 +11844,7 @@ intrinsics: - FnCall: [cfg_attr, [*test-is-arm, {FnCall: [assert_instr, [nop]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -11834,6 +11931,7 @@ intrinsics: - FnCall: [target_feature, ['enable = "{type[3]}"']] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec - FnCall: [cfg_attr, [*test-is-arm, {FnCall: [assert_instr, ['{type[2]}']]}]] types: - ['*const f16', float16x4_t, '"vld1.16"', 'neon,v7', 'crate::mem::align_of::() as i32', '_v4f16'] @@ -12117,6 +12215,7 @@ intrinsics: - FnCall: [cfg_attr, [*test-is-arm, {FnCall: [assert_instr, [vld4]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -12145,6 +12244,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [ld4]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -12172,6 +12272,7 @@ intrinsics: - FnCall: [cfg_attr, [*test-is-arm, {FnCall: [assert_instr, [vld4]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -12201,6 +12302,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [ld4r]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: unsafe: [neon] types: @@ -12230,6 +12332,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ["2"]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: - "const LANE: i32" safety: @@ -12276,6 +12379,7 @@ intrinsics: - FnCall: [rustc_legacy_const_generics, ["2"]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec static_defs: - "const LANE: i32" safety: @@ -13674,6 +13778,7 @@ intrinsics: - *neon-v7 - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec - FnCall: [cfg_attr, [*test-is-arm, {FnCall: [assert_instr, ['"vst1.{type[4]}"']]}]] types: - ['_v4f16', '* const i8', float16x4_t, i32, '16'] @@ -13738,6 +13843,7 @@ intrinsics: - *neon-v7 - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec - FnCall: [cfg_attr, [*test-is-arm, {FnCall: [assert_instr, ['"vst1.{type[2]}"']]}]] types: - ['*mut f16', float16x4_t, '16', 'transmute(a)', 'crate::mem::align_of::() as i32', '_v4f16'] @@ -13918,6 +14024,7 @@ intrinsics: - *neon-v7 - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [nop] safety: safe types: @@ -13933,6 +14040,7 @@ intrinsics: - *neon-v7 - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec assert_instr: [nop] safety: safe types: @@ -13952,6 +14060,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [nop, 'LANE = 0']]}]] - FnCall: [rustc_legacy_const_generics, ["1"]] - *neon-unstable-f16 + - *target-not-arm64ec static_defs: ['const LANE: i32'] safety: safe types: @@ -13971,6 +14080,7 @@ intrinsics: - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, [dup]]}]] - *neon-fp16 - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - [float16x4_t, f16] @@ -14585,6 +14695,7 @@ intrinsics: - FnCall: [cfg_attr, [*test-is-arm, { FnCall: [assert_instr, ['vbsl']]}]] - FnCall: [cfg_attr, [*neon-target-aarch64-arm64ec, {FnCall: [assert_instr, ['bsl']]}]] - *neon-unstable-f16 + - *target-not-arm64ec safety: safe types: - ['vbslq_f16', 'uint16x8_t', 'float16x8_t', 'int16x8_t::splat(-1)'] diff --git a/library/stdarch/crates/stdarch-test/src/disassembly.rs b/library/stdarch/crates/stdarch-test/src/disassembly.rs index f5167ea8d8ef..4c136cff02ae 100644 --- a/library/stdarch/crates/stdarch-test/src/disassembly.rs +++ b/library/stdarch/crates/stdarch-test/src/disassembly.rs @@ -27,9 +27,9 @@ fn normalize(mut symbol: &str) -> String { symbol = symbol[last_colon + 1..].to_string(); } - // Normalize to no leading underscore to handle platforms that may + // Normalize to no leading mangling chars to handle platforms that may // inject extra ones in symbol names. - while symbol.starts_with('_') || symbol.starts_with('.') { + while symbol.starts_with('_') || symbol.starts_with('.') || symbol.starts_with('#') { symbol.remove(0); } // Windows/x86 has a suffix such as @@4. @@ -49,6 +49,8 @@ pub(crate) fn disassemble_myself() -> HashSet { "i686-pc-windows-msvc" } else if cfg!(target_arch = "aarch64") { "aarch64-pc-windows-msvc" + } else if cfg!(target_arch = "arm64ec") { + "arm64ec-pc-windows-msvc" } else { panic!("disassembly unimplemented") }; From 802fa9255b59c8787d679f5da1f5534f9c0580b0 Mon Sep 17 00:00:00 2001 From: Ifeanyi Orizu Date: Sun, 10 Aug 2025 10:02:03 -0500 Subject: [PATCH 041/682] Add config option to exclude locals from doc search --- .../crates/ide/src/file_structure.rs | 307 +++++++++++++++++- src/tools/rust-analyzer/crates/ide/src/lib.rs | 12 +- .../crates/rust-analyzer/src/cli/symbols.rs | 9 +- .../crates/rust-analyzer/src/config.rs | 16 + .../rust-analyzer/src/handlers/request.rs | 17 +- .../docs/book/src/configuration_generated.md | 7 + .../rust-analyzer/editors/code/package.json | 10 + 7 files changed, 350 insertions(+), 28 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs index 6820f99facf2..e0cd32baa97e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs +++ b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs @@ -23,6 +23,11 @@ pub enum StructureNodeKind { Region, } +#[derive(Debug, Clone)] +pub struct FileStructureConfig { + pub exclude_locals: bool, +} + // Feature: File Structure // // Provides a tree of the symbols defined in the file. Can be used to @@ -36,21 +41,24 @@ pub enum StructureNodeKind { // | VS Code | Ctrl+Shift+O | // // ![File Structure](https://user-images.githubusercontent.com/48062697/113020654-b42fc800-917a-11eb-8388-e7dc4d92b02e.gif) -pub(crate) fn file_structure(file: &SourceFile) -> Vec { +pub(crate) fn file_structure( + file: &SourceFile, + config: &FileStructureConfig, +) -> Vec { let mut res = Vec::new(); let mut stack = Vec::new(); for event in file.syntax().preorder_with_tokens() { match event { WalkEvent::Enter(NodeOrToken::Node(node)) => { - if let Some(mut symbol) = structure_node(&node) { + if let Some(mut symbol) = structure_node(&node, config) { symbol.parent = stack.last().copied(); stack.push(res.len()); res.push(symbol); } } WalkEvent::Leave(NodeOrToken::Node(node)) => { - if structure_node(&node).is_some() { + if structure_node(&node, config).is_some() { stack.pop().unwrap(); } } @@ -71,7 +79,7 @@ pub(crate) fn file_structure(file: &SourceFile) -> Vec { res } -fn structure_node(node: &SyntaxNode) -> Option { +fn structure_node(node: &SyntaxNode, config: &FileStructureConfig) -> Option { fn decl(node: N, kind: StructureNodeKind) -> Option { decl_with_detail(&node, None, kind) } @@ -187,6 +195,10 @@ fn structure_node(node: &SyntaxNode) -> Option { Some(node) }, ast::LetStmt(it) => { + if config.exclude_locals { + return None; + } + let pat = it.pat()?; let mut label = String::new(); @@ -254,9 +266,19 @@ mod tests { use super::*; + const DEFAULT_CONFIG: FileStructureConfig = FileStructureConfig { exclude_locals: true }; + fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) { + check_with_config(ra_fixture, &DEFAULT_CONFIG, expect); + } + + fn check_with_config( + #[rust_analyzer::rust_fixture] ra_fixture: &str, + config: &FileStructureConfig, + expect: Expect, + ) { let file = SourceFile::parse(ra_fixture, span::Edition::CURRENT).ok().unwrap(); - let structure = file_structure(&file); + let structure = file_structure(&file, config); expect.assert_debug_eq(&structure) } @@ -701,13 +723,264 @@ fn let_statements() { ), deprecated: false, }, + ] + "#]], + ); + } + + #[test] + fn test_file_structure_include_locals() { + check_with_config( + r#" +struct Foo { + x: i32 +} + +mod m { + fn bar1() {} + fn bar2(t: T) -> T {} + fn bar3(a: A, + b: B) -> Vec< + u32 + > {} +} + +enum E { X, Y(i32) } +type T = (); +static S: i32 = 42; +const C: i32 = 42; +trait Tr {} +trait Alias = Tr; + +macro_rules! mc { + () => {} +} + +fn let_statements() { + let x = 42; + let mut y = x; + let Foo { + .. + } = Foo { x }; + _ = (); + let _ = g(); +} +"#, + &FileStructureConfig { exclude_locals: false }, + expect![[r#" + [ + StructureNode { + parent: None, + label: "Foo", + navigation_range: 8..11, + node_range: 1..26, + kind: SymbolKind( + Struct, + ), + detail: None, + deprecated: false, + }, StructureNode { parent: Some( - 27, + 0, ), label: "x", - navigation_range: 684..685, - node_range: 680..691, + navigation_range: 18..19, + node_range: 18..24, + kind: SymbolKind( + Field, + ), + detail: Some( + "i32", + ), + deprecated: false, + }, + StructureNode { + parent: None, + label: "m", + navigation_range: 32..33, + node_range: 28..158, + kind: SymbolKind( + Module, + ), + detail: None, + deprecated: false, + }, + StructureNode { + parent: Some( + 2, + ), + label: "bar1", + navigation_range: 43..47, + node_range: 40..52, + kind: SymbolKind( + Function, + ), + detail: Some( + "fn()", + ), + deprecated: false, + }, + StructureNode { + parent: Some( + 2, + ), + label: "bar2", + navigation_range: 60..64, + node_range: 57..81, + kind: SymbolKind( + Function, + ), + detail: Some( + "fn(t: T) -> T", + ), + deprecated: false, + }, + StructureNode { + parent: Some( + 2, + ), + label: "bar3", + navigation_range: 89..93, + node_range: 86..156, + kind: SymbolKind( + Function, + ), + detail: Some( + "fn(a: A, b: B) -> Vec< u32 >", + ), + deprecated: false, + }, + StructureNode { + parent: None, + label: "E", + navigation_range: 165..166, + node_range: 160..180, + kind: SymbolKind( + Enum, + ), + detail: None, + deprecated: false, + }, + StructureNode { + parent: Some( + 6, + ), + label: "X", + navigation_range: 169..170, + node_range: 169..170, + kind: SymbolKind( + Variant, + ), + detail: None, + deprecated: false, + }, + StructureNode { + parent: Some( + 6, + ), + label: "Y", + navigation_range: 172..173, + node_range: 172..178, + kind: SymbolKind( + Variant, + ), + detail: None, + deprecated: false, + }, + StructureNode { + parent: None, + label: "T", + navigation_range: 186..187, + node_range: 181..193, + kind: SymbolKind( + TypeAlias, + ), + detail: Some( + "()", + ), + deprecated: false, + }, + StructureNode { + parent: None, + label: "S", + navigation_range: 201..202, + node_range: 194..213, + kind: SymbolKind( + Static, + ), + detail: Some( + "i32", + ), + deprecated: false, + }, + StructureNode { + parent: None, + label: "C", + navigation_range: 220..221, + node_range: 214..232, + kind: SymbolKind( + Const, + ), + detail: Some( + "i32", + ), + deprecated: false, + }, + StructureNode { + parent: None, + label: "Tr", + navigation_range: 239..241, + node_range: 233..244, + kind: SymbolKind( + Trait, + ), + detail: None, + deprecated: false, + }, + StructureNode { + parent: None, + label: "Alias", + navigation_range: 251..256, + node_range: 245..262, + kind: SymbolKind( + TraitAlias, + ), + detail: None, + deprecated: false, + }, + StructureNode { + parent: None, + label: "mc", + navigation_range: 277..279, + node_range: 264..296, + kind: SymbolKind( + Macro, + ), + detail: None, + deprecated: false, + }, + StructureNode { + parent: None, + label: "let_statements", + navigation_range: 301..315, + node_range: 298..429, + kind: SymbolKind( + Function, + ), + detail: Some( + "fn()", + ), + deprecated: false, + }, + StructureNode { + parent: Some( + 15, + ), + label: "x", + navigation_range: 328..329, + node_range: 324..335, kind: SymbolKind( Local, ), @@ -716,11 +989,11 @@ fn let_statements() { }, StructureNode { parent: Some( - 27, + 15, ), label: "mut y", - navigation_range: 700..705, - node_range: 696..710, + navigation_range: 344..349, + node_range: 340..354, kind: SymbolKind( Local, ), @@ -729,11 +1002,11 @@ fn let_statements() { }, StructureNode { parent: Some( - 27, + 15, ), label: "Foo { .. }", - navigation_range: 719..741, - node_range: 715..754, + navigation_range: 363..385, + node_range: 359..398, kind: SymbolKind( Local, ), @@ -742,11 +1015,11 @@ fn let_statements() { }, StructureNode { parent: Some( - 27, + 15, ), label: "_", - navigation_range: 804..805, - node_range: 800..812, + navigation_range: 419..420, + node_range: 415..427, kind: SymbolKind( Local, ), diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 98877482ed86..5349ebb7c82a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -81,7 +81,7 @@ pub use crate::{ annotations::{Annotation, AnnotationConfig, AnnotationKind, AnnotationLocation}, call_hierarchy::{CallHierarchyConfig, CallItem}, expand_macro::ExpandedMacro, - file_structure::{StructureNode, StructureNodeKind}, + file_structure::{FileStructureConfig, StructureNode, StructureNodeKind}, folding_ranges::{Fold, FoldKind}, highlight_related::{HighlightRelatedConfig, HighlightedRange}, hover::{ @@ -430,12 +430,16 @@ impl Analysis { /// Returns a tree representation of symbols in the file. Useful to draw a /// file outline. - pub fn file_structure(&self, file_id: FileId) -> Cancellable> { + pub fn file_structure( + &self, + config: &FileStructureConfig, + file_id: FileId, + ) -> Cancellable> { // FIXME: Edition self.with_db(|db| { let editioned_file_id_wrapper = EditionedFileId::current_edition(&self.db, file_id); - - file_structure::file_structure(&db.parse(editioned_file_id_wrapper).tree()) + let source_file = db.parse(editioned_file_id_wrapper).tree(); + file_structure::file_structure(&source_file, config) }) } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/symbols.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/symbols.rs index 9fad6723afcd..d7af56d3e15b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/symbols.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/symbols.rs @@ -1,5 +1,5 @@ //! Read Rust code on stdin, print syntax tree on stdout. -use ide::Analysis; +use ide::{Analysis, FileStructureConfig}; use crate::cli::{flags, read_stdin}; @@ -7,7 +7,12 @@ impl flags::Symbols { pub fn run(self) -> anyhow::Result<()> { let text = read_stdin()?; let (analysis, file_id) = Analysis::from_single_file(text); - let structure = analysis.file_structure(file_id).unwrap(); + let structure = analysis + // The default setting in config.rs (document_symbol_search_excludeLocals) is to exclude + // locals because it is unlikely that users want document search to return the names of + // local variables, but here we include them deliberately. + .file_structure(&FileStructureConfig { exclude_locals: false }, file_id) + .unwrap(); for s in structure { println!("{s:?}"); } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 1a00295b9ac1..77e5a8e079f8 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -858,6 +858,9 @@ config_data! { /// check will be performed. check_workspace: bool = true, + /// Exclude all locals from document symbol search. + document_symbol_search_excludeLocals: bool = true, + /// These proc-macros will be ignored when trying to expand them. /// /// This config takes a map of crate names with the exported proc-macro names to ignore as values. @@ -1481,6 +1484,13 @@ pub enum FilesWatcher { Server, } +/// Configuration for document symbol search requests. +#[derive(Debug, Clone)] +pub struct DocumentSymbolConfig { + /// Should locals be excluded. + pub search_exclude_locals: bool, +} + #[derive(Debug, Clone)] pub struct NotificationsConfig { pub cargo_toml_not_found: bool, @@ -2438,6 +2448,12 @@ impl Config { } } + pub fn document_symbol(&self, source_root: Option) -> DocumentSymbolConfig { + DocumentSymbolConfig { + search_exclude_locals: *self.document_symbol_search_excludeLocals(source_root), + } + } + pub fn workspace_symbol(&self, source_root: Option) -> WorkspaceSymbolConfig { WorkspaceSymbolConfig { search_exclude_imports: *self.workspace_symbol_search_excludeImports(source_root), diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index 25c0aac405e7..74ed5e344af6 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -8,8 +8,9 @@ use anyhow::Context; use base64::{Engine, prelude::BASE64_STANDARD}; use ide::{ AnnotationConfig, AssistKind, AssistResolveStrategy, Cancellable, CompletionFieldsToResolve, - FilePosition, FileRange, HoverAction, HoverGotoTypeData, InlayFieldsToResolve, Query, - RangeInfo, ReferenceCategory, Runnable, RunnableKind, SingleResolve, SourceChange, TextEdit, + FilePosition, FileRange, FileStructureConfig, HoverAction, HoverGotoTypeData, + InlayFieldsToResolve, Query, RangeInfo, ReferenceCategory, Runnable, RunnableKind, + SingleResolve, SourceChange, TextEdit, }; use ide_db::{FxHashMap, SymbolKind}; use itertools::Itertools; @@ -568,7 +569,14 @@ pub(crate) fn handle_document_symbol( let mut parents: Vec<(lsp_types::DocumentSymbol, Option)> = Vec::new(); - for symbol in snap.analysis.file_structure(file_id)? { + let config = snap.config.document_symbol(None); + + let structure_nodes = snap.analysis.file_structure( + &FileStructureConfig { exclude_locals: config.search_exclude_locals }, + file_id, + )?; + + for symbol in structure_nodes { let mut tags = Vec::new(); if symbol.deprecated { tags.push(SymbolTag::DEPRECATED) @@ -588,8 +596,7 @@ pub(crate) fn handle_document_symbol( parents.push((doc_symbol, symbol.parent)); } - // Builds hierarchy from a flat list, in reverse order (so that indices - // makes sense) + // Builds hierarchy from a flat list, in reverse order (so that indices make sense) let document_symbols = { let mut acc = Vec::new(); while let Some((mut node, parent_idx)) = parents.pop() { diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md index 99a30d8f6213..6ee956fe0dbf 100644 --- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -610,6 +610,13 @@ The warnings will be indicated by a blue squiggly underline in code and a blue i the `Problems Panel`. +## rust-analyzer.document.symbol.search.excludeLocals {#document.symbol.search.excludeLocals} + +Default: `true` + +Exclude all locals from document symbol search. + + ## rust-analyzer.files.exclude {#files.exclude} Default: `[]` diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 470db244f14b..328eb509b4ee 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -1585,6 +1585,16 @@ } } }, + { + "title": "Document", + "properties": { + "rust-analyzer.document.symbol.search.excludeLocals": { + "markdownDescription": "Exclude all locals from document symbol search.", + "default": true, + "type": "boolean" + } + } + }, { "title": "Files", "properties": { From 00d000ce74f8e3bed8abc4cff382f62dcca0d8d5 Mon Sep 17 00:00:00 2001 From: Ifeanyi Orizu Date: Sun, 10 Aug 2025 10:02:34 -0500 Subject: [PATCH 042/682] Fix minor things --- .../crates/ide/src/file_structure.rs | 1 - .../crates/rust-analyzer/src/config.rs | 2 +- .../rust-analyzer/src/handlers/request.rs | 38 +++++++++---------- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs index e0cd32baa97e..ac15af0334fc 100644 --- a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs +++ b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs @@ -213,7 +213,6 @@ fn structure_node(node: &SyntaxNode, config: &FileStructureConfig) -> Option { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 77e5a8e079f8..d4cd56dc55bf 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -3083,7 +3083,7 @@ macro_rules! _config_data { }) => { /// Default config values for this grouping. #[allow(non_snake_case)] - #[derive(Debug, Clone )] + #[derive(Debug, Clone)] struct $name { $($field: $ty,)* } impl_for_config_data!{ diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index 74ed5e344af6..6cb28aecf748 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -567,7 +567,7 @@ pub(crate) fn handle_document_symbol( let file_id = try_default!(from_proto::file_id(&snap, ¶ms.text_document.uri)?); let line_index = snap.file_line_index(file_id)?; - let mut parents: Vec<(lsp_types::DocumentSymbol, Option)> = Vec::new(); + let mut symbols: Vec<(lsp_types::DocumentSymbol, Option)> = Vec::new(); let config = snap.config.document_symbol(None); @@ -576,38 +576,38 @@ pub(crate) fn handle_document_symbol( file_id, )?; - for symbol in structure_nodes { + for node in structure_nodes { let mut tags = Vec::new(); - if symbol.deprecated { + if node.deprecated { tags.push(SymbolTag::DEPRECATED) }; #[allow(deprecated)] - let doc_symbol = lsp_types::DocumentSymbol { - name: symbol.label, - detail: symbol.detail, - kind: to_proto::structure_node_kind(symbol.kind), + let symbol = lsp_types::DocumentSymbol { + name: node.label, + detail: node.detail, + kind: to_proto::structure_node_kind(node.kind), tags: Some(tags), - deprecated: Some(symbol.deprecated), - range: to_proto::range(&line_index, symbol.node_range), - selection_range: to_proto::range(&line_index, symbol.navigation_range), + deprecated: Some(node.deprecated), + range: to_proto::range(&line_index, node.node_range), + selection_range: to_proto::range(&line_index, node.navigation_range), children: None, }; - parents.push((doc_symbol, symbol.parent)); + symbols.push((symbol, node.parent)); } - // Builds hierarchy from a flat list, in reverse order (so that indices make sense) + // Builds hierarchy from a flat list, in reverse order (so that the indices make sense) let document_symbols = { let mut acc = Vec::new(); - while let Some((mut node, parent_idx)) = parents.pop() { - if let Some(children) = &mut node.children { + while let Some((mut symbol, parent_idx)) = symbols.pop() { + if let Some(children) = &mut symbol.children { children.reverse(); } let parent = match parent_idx { None => &mut acc, - Some(i) => parents[i].0.children.get_or_insert_with(Vec::new), + Some(i) => symbols[i].0.children.get_or_insert_with(Vec::new), }; - parent.push(node); + parent.push(symbol); } acc.reverse(); acc @@ -617,7 +617,7 @@ pub(crate) fn handle_document_symbol( document_symbols.into() } else { let url = to_proto::url(&snap, file_id); - let mut symbol_information = Vec::::new(); + let mut symbol_information = Vec::new(); for symbol in document_symbols { flatten_document_symbol(&symbol, None, &url, &mut symbol_information); } @@ -654,7 +654,7 @@ pub(crate) fn handle_workspace_symbol( let _p = tracing::info_span!("handle_workspace_symbol").entered(); let config = snap.config.workspace_symbol(None); - let (all_symbols, libs) = decide_search_scope_and_kind(¶ms, &config); + let (all_symbols, libs) = decide_search_kind_and_scope(¶ms, &config); let query = { let query: String = params.query.chars().filter(|&c| c != '#' && c != '*').collect(); @@ -677,7 +677,7 @@ pub(crate) fn handle_workspace_symbol( return Ok(Some(lsp_types::WorkspaceSymbolResponse::Nested(res))); - fn decide_search_scope_and_kind( + fn decide_search_kind_and_scope( params: &WorkspaceSymbolParams, config: &WorkspaceSymbolConfig, ) -> (bool, bool) { From af10cb727fd55ce8638653f18d2edd4b8005add7 Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Mon, 11 Aug 2025 04:25:52 +0000 Subject: [PATCH 043/682] Prepare for merging from rust-lang/rust This updates the rust-version file to 21a19c297d4f5a03501d92ca251bd7a17073c08a. --- src/tools/rust-analyzer/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version index 2178caf63968..02b217f7d80d 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -733dab558992d902d6d17576de1da768094e2cf3 +21a19c297d4f5a03501d92ca251bd7a17073c08a From 7daaa4ee01f4c4d026293a6e3a90e4c8b4f5aa12 Mon Sep 17 00:00:00 2001 From: "Shoyu Vanilla (Flint)" Date: Mon, 11 Aug 2025 18:18:55 +0900 Subject: [PATCH 044/682] hotfix: Update flycheck diagnostics generation --- src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs index cd7c632d105e..2711bdba693b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs @@ -134,6 +134,7 @@ impl DiagnosticCollection { if self.check[flycheck_id].generation > generation { return; } + self.check[flycheck_id].generation = generation; let diagnostics = self.check[flycheck_id] .per_package .entry(package_id.clone()) From 380d89d82f514d7e998f7b3b0e6bdb2065b711bc Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Mon, 11 Aug 2025 01:57:27 +0900 Subject: [PATCH 045/682] Make import sorting order follow 2024 edition style --- .../ide-assists/src/handlers/merge_imports.rs | 2 +- .../src/handlers/normalize_import.rs | 22 +- .../ide-completion/src/tests/flyimport.rs | 2 +- .../ide-db/src/imports/insert_use/tests.rs | 26 +-- .../ide-db/src/imports/merge_imports.rs | 217 ++++++++++++++++-- 5 files changed, 219 insertions(+), 50 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs index 6bf7f5849148..9ba73d23dd24 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_imports.rs @@ -605,7 +605,7 @@ use foo::$0{ ", r" use foo::{ - bar::baz, FooBar + FooBar, bar::baz, }; ", ) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/normalize_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/normalize_import.rs index bba28b5fc8af..36da1d178824 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/normalize_import.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/normalize_import.rs @@ -109,8 +109,8 @@ mod tests { #[test] fn test_order() { check_assist_variations!( - "foo::{*, Qux, bar::{Quux, Bar}, baz, FOO_BAZ, self, Baz}", - "foo::{self, bar::{Bar, Quux}, baz, Baz, Qux, FOO_BAZ, *}" + "foo::{*, Qux, bar::{Quux, Bar}, baz, FOO_BAZ, self, Baz, v10, v9, r#aaa}", + "foo::{self, Baz, FOO_BAZ, Qux, r#aaa, bar::{Bar, Quux}, baz, v9, v10, *}" ); } @@ -145,17 +145,17 @@ fn main() { #[test] fn test_redundant_braces() { - check_assist_variations!("foo::{bar::{baz, Qux}}", "foo::bar::{baz, Qux}"); + check_assist_variations!("foo::{bar::{baz, Qux}}", "foo::bar::{Qux, baz}"); check_assist_variations!("foo::{bar::{self}}", "foo::bar::{self}"); check_assist_variations!("foo::{bar::{*}}", "foo::bar::*"); check_assist_variations!("foo::{bar::{Qux as Quux}}", "foo::bar::Qux as Quux"); check_assist_variations!( "foo::bar::{{FOO_BAZ, Qux, self}, {*, baz}}", - "foo::bar::{self, baz, Qux, FOO_BAZ, *}" + "foo::bar::{self, FOO_BAZ, Qux, baz, *}" ); check_assist_variations!( "foo::bar::{{{FOO_BAZ}, {{Qux}, {self}}}, {{*}, {baz}}}", - "foo::bar::{self, baz, Qux, FOO_BAZ, *}" + "foo::bar::{self, FOO_BAZ, Qux, baz, *}" ); } @@ -163,11 +163,11 @@ fn main() { fn test_merge() { check_assist_variations!( "foo::{*, bar, {FOO_BAZ, qux}, bar::{*, baz}, {Quux}}", - "foo::{bar::{self, baz, *}, qux, Quux, FOO_BAZ, *}" + "foo::{FOO_BAZ, Quux, bar::{self, baz, *}, qux, *}" ); check_assist_variations!( "foo::{*, bar, {FOO_BAZ, qux}, bar::{*, baz}, {Quux, bar::{baz::Foo}}}", - "foo::{bar::{self, baz::{self, Foo}, *}, qux, Quux, FOO_BAZ, *}" + "foo::{FOO_BAZ, Quux, bar::{self, baz::{self, Foo}, *}, qux, *}" ); } @@ -229,15 +229,15 @@ use { check_assist_not_applicable_variations!("foo::bar"); check_assist_not_applicable_variations!("foo::bar::*"); check_assist_not_applicable_variations!("foo::bar::Qux as Quux"); - check_assist_not_applicable_variations!("foo::bar::{self, baz, Qux, FOO_BAZ, *}"); + check_assist_not_applicable_variations!("foo::bar::{self, FOO_BAZ, Qux, baz, *}"); check_assist_not_applicable_variations!( - "foo::{self, bar::{Bar, Quux}, baz, Baz, Qux, FOO_BAZ, *}" + "foo::{self, Baz, FOO_BAZ, Qux, bar::{Bar, Quux}, baz, *}" ); check_assist_not_applicable_variations!( - "foo::{bar::{self, baz, *}, qux, Quux, FOO_BAZ, *}" + "foo::{FOO_BAZ, Quux, bar::{self, baz, *}, qux, *}" ); check_assist_not_applicable_variations!( - "foo::{bar::{self, baz::{self, Foo}, *}, qux, Quux, FOO_BAZ, *}" + "foo::{bar::{self, FOO_BAZ, Quux, baz::{self, Foo}, *}, qux, *}" ); } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs index 27c91bc7c455..04bed418eec7 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs @@ -114,7 +114,7 @@ fn main() { } "#, r#" -use dep::{some_module::{SecondStruct, ThirdStruct}, FirstStruct}; +use dep::{FirstStruct, some_module::{SecondStruct, ThirdStruct}}; fn main() { ThirdStruct diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs index 4a00854f01a5..3350e1c3d207 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use/tests.rs @@ -782,18 +782,18 @@ fn merge_groups_long_last_list() { fn merge_groups_long_full_nested() { check_crate( "std::foo::bar::Baz", - r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};", - r"use std::foo::bar::{quux::{Fez, Fizz}, Baz, Qux};", + r"use std::foo::bar::{quux::{Fez, Fizz}, Qux};", + r"use std::foo::bar::{Baz, Qux, quux::{Fez, Fizz}};", ); check_crate( "std::foo::bar::r#Baz", - r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};", - r"use std::foo::bar::{quux::{Fez, Fizz}, r#Baz, Qux};", + r"use std::foo::bar::{quux::{Fez, Fizz}, Qux};", + r"use std::foo::bar::{r#Baz, Qux, quux::{Fez, Fizz}};", ); check_one( "std::foo::bar::Baz", - r"use {std::foo::bar::{Qux, quux::{Fez, Fizz}}};", - r"use {std::foo::bar::{quux::{Fez, Fizz}, Baz, Qux}};", + r"use {std::foo::bar::{quux::{Fez, Fizz}}, Qux};", + r"use {Qux, std::foo::bar::{Baz, quux::{Fez, Fizz}}};", ); } @@ -811,13 +811,13 @@ use std::foo::bar::{Qux, quux::{Fez, Fizz}};", fn merge_groups_full_nested_deep() { check_crate( "std::foo::bar::quux::Baz", - r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};", - r"use std::foo::bar::{quux::{Baz, Fez, Fizz}, Qux};", + r"use std::foo::bar::{quux::{Fez, Fizz}, Qux};", + r"use std::foo::bar::{Qux, quux::{Baz, Fez, Fizz}};", ); check_one( "std::foo::bar::quux::Baz", - r"use {std::foo::bar::{Qux, quux::{Fez, Fizz}}};", - r"use {std::foo::bar::{quux::{Baz, Fez, Fizz}, Qux}};", + r"use {std::foo::bar::{quux::{Fez, Fizz}}, Qux};", + r"use {Qux, std::foo::bar::quux::{Baz, Fez, Fizz}};", ); } @@ -988,8 +988,8 @@ use syntax::SyntaxKind::{self, *};", fn merge_glob_nested() { check_crate( "foo::bar::quux::Fez", - r"use foo::bar::{Baz, quux::*};", - r"use foo::bar::{quux::{Fez, *}, Baz};", + r"use foo::bar::{quux::*, Baz};", + r"use foo::bar::{Baz, quux::{Fez, *}};", ) } @@ -998,7 +998,7 @@ fn merge_nested_considers_first_segments() { check_crate( "hir_ty::display::write_bounds_like_dyn_trait", r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter}, method_resolution};", - r"use hir_ty::{autoderef, display::{write_bounds_like_dyn_trait, HirDisplayError, HirFormatter}, method_resolution};", + r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter, write_bounds_like_dyn_trait}, method_resolution};", ); } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs index 61962e593476..4e779a7d858e 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs @@ -3,7 +3,6 @@ use std::cmp::Ordering; use itertools::{EitherOrBoth, Itertools}; use parser::T; -use stdx::is_upper_snake_case; use syntax::{ Direction, SyntaxElement, algo, ast::{ @@ -543,12 +542,13 @@ fn use_tree_cmp_bin_search(lhs: &ast::UseTree, rhs: &ast::UseTree) -> Ordering { } } -/// Orders use trees following `rustfmt`'s algorithm for ordering imports, which is `self`, `super` -/// and `crate` first, then identifier imports with lowercase ones first and upper snake case -/// (e.g. UPPER_SNAKE_CASE) ones last, then glob imports, and at last list imports. +/// Orders use trees following `rustfmt`'s version sorting algorithm for ordering imports. /// -/// Example: `foo::{self, baz, foo, Baz, Qux, FOO_BAZ, *, {Bar}}` -/// Ref: . +/// Example: `foo::{self, Baz, FOO_BAZ, Qux, baz, foo, *, {Bar}}` +/// +/// Ref: +/// - +/// - pub(super) fn use_tree_cmp(a: &ast::UseTree, b: &ast::UseTree) -> Ordering { let a_is_simple_path = a.is_simple_path() && a.rename().is_none(); let b_is_simple_path = b.is_simple_path() && b.rename().is_none(); @@ -613,26 +613,9 @@ fn path_segment_cmp(a: &ast::PathSegment, b: &ast::PathSegment) -> Ordering { (Some(_), None) => Ordering::Greater, (None, Some(_)) => Ordering::Less, (Some(a_name), Some(b_name)) => { - // snake_case < UpperCamelCase < UPPER_SNAKE_CASE let a_text = a_name.as_str().trim_start_matches("r#"); let b_text = b_name.as_str().trim_start_matches("r#"); - if a_text.starts_with(char::is_lowercase) - && b_text.starts_with(char::is_uppercase) - { - return Ordering::Less; - } - if a_text.starts_with(char::is_uppercase) - && b_text.starts_with(char::is_lowercase) - { - return Ordering::Greater; - } - if !is_upper_snake_case(a_text) && is_upper_snake_case(b_text) { - return Ordering::Less; - } - if is_upper_snake_case(a_text) && !is_upper_snake_case(b_text) { - return Ordering::Greater; - } - a_text.cmp(b_text) + version_sort::version_sort(a_text, b_text) } } } @@ -740,3 +723,189 @@ fn remove_subtree_if_only_self(use_tree: &ast::UseTree) { _ => (), } } + +// Taken from rustfmt +// https://github.com/rust-lang/rustfmt/blob/0332da01486508710f2a542111e40513bfb215aa/src/sort.rs +mod version_sort { + // Original rustfmt code contains some clippy lints. + // Suppress them to minimize changes from upstream. + #![allow(clippy::all)] + + use std::cmp::Ordering; + + use itertools::{EitherOrBoth, Itertools}; + + struct VersionChunkIter<'a> { + ident: &'a str, + start: usize, + } + + impl<'a> VersionChunkIter<'a> { + pub(crate) fn new(ident: &'a str) -> Self { + Self { ident, start: 0 } + } + + fn parse_numeric_chunk( + &mut self, + mut chars: std::str::CharIndices<'a>, + ) -> Option> { + let mut end = self.start; + let mut is_end_of_chunk = false; + + while let Some((idx, c)) = chars.next() { + end = self.start + idx; + + if c.is_ascii_digit() { + continue; + } + + is_end_of_chunk = true; + break; + } + + let source = if is_end_of_chunk { + let value = &self.ident[self.start..end]; + self.start = end; + value + } else { + let value = &self.ident[self.start..]; + self.start = self.ident.len(); + value + }; + + let zeros = source.chars().take_while(|c| *c == '0').count(); + let value = source.parse::().ok()?; + + Some(VersionChunk::Number { value, zeros, source }) + } + + fn parse_str_chunk( + &mut self, + mut chars: std::str::CharIndices<'a>, + ) -> Option> { + let mut end = self.start; + let mut is_end_of_chunk = false; + + while let Some((idx, c)) = chars.next() { + end = self.start + idx; + + if c == '_' { + is_end_of_chunk = true; + break; + } + + if !c.is_ascii_digit() { + continue; + } + + is_end_of_chunk = true; + break; + } + + let source = if is_end_of_chunk { + let value = &self.ident[self.start..end]; + self.start = end; + value + } else { + let value = &self.ident[self.start..]; + self.start = self.ident.len(); + value + }; + + Some(VersionChunk::Str(source)) + } + } + + impl<'a> Iterator for VersionChunkIter<'a> { + type Item = VersionChunk<'a>; + + fn next(&mut self) -> Option { + let mut chars = self.ident[self.start..].char_indices(); + let (_, next) = chars.next()?; + + if next == '_' { + self.start = self.start + next.len_utf8(); + return Some(VersionChunk::Underscore); + } + + if next.is_ascii_digit() { + return self.parse_numeric_chunk(chars); + } + + self.parse_str_chunk(chars) + } + } + + /// Represents a chunk in the version-sort algorithm + #[derive(Debug, PartialEq, Eq)] + enum VersionChunk<'a> { + /// A single `_` in an identifier. Underscores are sorted before all other characters. + Underscore, + /// A &str chunk in the version sort. + Str(&'a str), + /// A numeric chunk in the version sort. Keeps track of the numeric value and leading zeros. + Number { value: usize, zeros: usize, source: &'a str }, + } + + /// Determine which side of the version-sort comparison had more leading zeros. + #[derive(Debug, PartialEq, Eq)] + enum MoreLeadingZeros { + Left, + Right, + Equal, + } + + pub(super) fn version_sort(a: &str, b: &str) -> Ordering { + let iter_a = VersionChunkIter::new(a); + let iter_b = VersionChunkIter::new(b); + let mut more_leading_zeros = MoreLeadingZeros::Equal; + + for either_or_both in iter_a.zip_longest(iter_b) { + match either_or_both { + EitherOrBoth::Left(_) => return std::cmp::Ordering::Greater, + EitherOrBoth::Right(_) => return std::cmp::Ordering::Less, + EitherOrBoth::Both(a, b) => match (a, b) { + (VersionChunk::Underscore, VersionChunk::Underscore) => { + continue; + } + (VersionChunk::Underscore, _) => return std::cmp::Ordering::Less, + (_, VersionChunk::Underscore) => return std::cmp::Ordering::Greater, + (VersionChunk::Str(ca), VersionChunk::Str(cb)) + | (VersionChunk::Str(ca), VersionChunk::Number { source: cb, .. }) + | (VersionChunk::Number { source: ca, .. }, VersionChunk::Str(cb)) => { + match ca.cmp(&cb) { + std::cmp::Ordering::Equal => { + continue; + } + order @ _ => return order, + } + } + ( + VersionChunk::Number { value: va, zeros: lza, .. }, + VersionChunk::Number { value: vb, zeros: lzb, .. }, + ) => match va.cmp(&vb) { + std::cmp::Ordering::Equal => { + if lza == lzb { + continue; + } + + if more_leading_zeros == MoreLeadingZeros::Equal && lza > lzb { + more_leading_zeros = MoreLeadingZeros::Left; + } else if more_leading_zeros == MoreLeadingZeros::Equal && lza < lzb { + more_leading_zeros = MoreLeadingZeros::Right; + } + continue; + } + order @ _ => return order, + }, + }, + } + } + + match more_leading_zeros { + MoreLeadingZeros::Equal => std::cmp::Ordering::Equal, + MoreLeadingZeros::Left => std::cmp::Ordering::Less, + MoreLeadingZeros::Right => std::cmp::Ordering::Greater, + } + } +} From 6c3c620e6b65a130a6996cca14cb9e19cc8ea65c Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Tue, 12 Aug 2025 00:24:44 +0900 Subject: [PATCH 046/682] fix: Panic while trying to clear old diagnostics while there's nothing --- src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs index 2711bdba693b..30f530100f9d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs @@ -105,7 +105,7 @@ impl DiagnosticCollection { flycheck_id: usize, generation: DiagnosticsGeneration, ) { - if self.check[flycheck_id].generation < generation { + if self.check.get(flycheck_id).is_some_and(|it| it.generation < generation) { self.clear_check(flycheck_id); } } From 875c158686fc47e083c83d1ccee4359ba4cc64f9 Mon Sep 17 00:00:00 2001 From: sgasho Date: Mon, 11 Aug 2025 22:19:50 +0900 Subject: [PATCH 047/682] fix: Implement default member to resolve IdentPat --- .../src/handlers/add_missing_impl_members.rs | 49 +++++++++++++++ .../crates/ide-db/src/path_transform.rs | 61 ++++++++++++++++--- 2 files changed, 102 insertions(+), 8 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs index 11201afb8a7f..7e03eb30304b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs @@ -2418,6 +2418,55 @@ pub struct MyStruct; impl other_file_2::Trait for MyStruct { $0type Iter; +}"#, + ); + } + + #[test] + fn test_qualify_ident_pat_in_default_members() { + check_assist( + add_missing_default_members, + r#" +//- /lib.rs crate:b new_source_root:library +pub enum State { + Active, + Inactive, +} + +use State::*; + +pub trait Checker { + fn check(&self) -> State; + + fn is_active(&self) -> bool { + match self.check() { + Active => true, + Inactive => false, + } + } +} +//- /main.rs crate:a deps:b +struct MyChecker; + +impl b::Checker for MyChecker { + fn check(&self) -> b::State { + todo!(); + }$0 +}"#, + r#" +struct MyChecker; + +impl b::Checker for MyChecker { + fn check(&self) -> b::State { + todo!(); + } + + $0fn is_active(&self) -> bool { + match self.check() { + b::State::Active => true, + b::State::Inactive => false, + } + } }"#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs index 5d88afec5095..a76a0551dd8d 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs @@ -11,7 +11,7 @@ use rustc_hash::FxHashMap; use span::Edition; use syntax::{ NodeOrToken, SyntaxNode, - ast::{self, AstNode, HasGenericArgs, make}, + ast::{self, AstNode, HasGenericArgs, HasName, make}, syntax_editor::{self, SyntaxEditor}, }; @@ -315,32 +315,49 @@ impl Ctx<'_> { } fn transform_path(&self, path: &SyntaxNode) -> SyntaxNode { - fn find_child_paths(root_path: &SyntaxNode) -> Vec { - let mut result = Vec::new(); + fn find_child_paths_and_ident_pats( + root_path: &SyntaxNode, + ) -> Vec> { + let mut result: Vec> = Vec::new(); for child in root_path.children() { if let Some(child_path) = ast::Path::cast(child.clone()) { - result.push(child_path); + result.push(either::Left(child_path)); + } else if let Some(child_ident_pat) = ast::IdentPat::cast(child.clone()) { + result.push(either::Right(child_ident_pat)); } else { - result.extend(find_child_paths(&child)); + result.extend(find_child_paths_and_ident_pats(&child)); } } result } + let root_path = path.clone_subtree(); - let result = find_child_paths(&root_path); + + let result = find_child_paths_and_ident_pats(&root_path); let mut editor = SyntaxEditor::new(root_path.clone()); for sub_path in result { let new = self.transform_path(sub_path.syntax()); editor.replace(sub_path.syntax(), new); } + let update_sub_item = editor.finish().new_root().clone().clone_subtree(); - let item = find_child_paths(&update_sub_item); + let item = find_child_paths_and_ident_pats(&update_sub_item); let mut editor = SyntaxEditor::new(update_sub_item); for sub_path in item { - self.transform_path_(&mut editor, &sub_path); + self.transform_path_or_ident_pat(&mut editor, &sub_path); } editor.finish().new_root().clone() } + fn transform_path_or_ident_pat( + &self, + editor: &mut SyntaxEditor, + item: &Either, + ) -> Option<()> { + match item { + Either::Left(path) => self.transform_path_(editor, path), + Either::Right(ident_pat) => self.transform_ident_pat(editor, ident_pat), + } + } fn transform_path_(&self, editor: &mut SyntaxEditor, path: &ast::Path) -> Option<()> { if path.qualifier().is_some() { @@ -515,6 +532,34 @@ impl Ctx<'_> { } Some(()) } + + fn transform_ident_pat( + &self, + editor: &mut SyntaxEditor, + ident_pat: &ast::IdentPat, + ) -> Option<()> { + let name = ident_pat.name()?; + + let temp_path = make::path_from_text(&name.text()); + + let resolution = self.source_scope.speculative_resolve(&temp_path)?; + + match resolution { + hir::PathResolution::Def(def) if def.as_assoc_item(self.source_scope.db).is_none() => { + let cfg = ImportPathConfig { + prefer_no_std: false, + prefer_prelude: true, + prefer_absolute: false, + allow_unstable: true, + }; + let found_path = self.target_module.find_path(self.source_scope.db, def, cfg)?; + let res = mod_path_to_ast(&found_path, self.target_edition).clone_for_update(); + editor.replace(ident_pat.syntax(), res.syntax()); + Some(()) + } + _ => None, + } + } } // FIXME: It would probably be nicer if we could get this via HIR (i.e. get the From 49ebe9f849202561ac02ece6e8683a6602bf1f38 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 13 Aug 2025 05:23:58 +0300 Subject: [PATCH 048/682] Only import the item in "Unqualify method call" if needed --- .../rust-analyzer/crates/hir/src/semantics.rs | 5 + .../src/handlers/unqualify_method_call.rs | 118 +++++++++++++++++- 2 files changed, 122 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 05f06f97fdc2..6be44532d033 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -2247,6 +2247,11 @@ impl<'db> SemanticsScope<'db> { } } + /// Checks if a trait is in scope, either because of an import or because we're in an impl of it. + pub fn can_use_trait_methods(&self, t: Trait) -> bool { + self.resolver.traits_in_scope(self.db).contains(&t.id) + } + /// Resolve a path as-if it was written at the given scope. This is /// necessary a heuristic, as it doesn't take hygiene into account. pub fn speculative_resolve(&self, ast_path: &ast::Path) -> Option { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unqualify_method_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unqualify_method_call.rs index 1f89a3d5f17c..a58b1da621c7 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unqualify_method_call.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unqualify_method_call.rs @@ -1,3 +1,4 @@ +use hir::AsAssocItem; use syntax::{ TextRange, ast::{self, AstNode, HasArgList, prec::ExprPrecedence}, @@ -43,6 +44,7 @@ pub(crate) fn unqualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) let qualifier = path.qualifier()?; let method_name = path.segment()?.name_ref()?; + let scope = ctx.sema.scope(path.syntax())?; let res = ctx.sema.resolve_path(&path)?; let hir::PathResolution::Def(hir::ModuleDef::Function(fun)) = res else { return None }; if !fun.has_self_param(ctx.sema.db) { @@ -78,7 +80,14 @@ pub(crate) fn unqualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) edit.insert(close, ")"); } edit.replace(replace_comma, format!(".{method_name}(")); - add_import(qualifier, ctx, edit); + + if let Some(fun) = fun.as_assoc_item(ctx.db()) + && let Some(trait_) = fun.container_or_implemented_trait(ctx.db()) + && !scope.can_use_trait_methods(trait_) + { + // Only add an import for trait methods that are not already imported. + add_import(qualifier, ctx, edit); + } }, ) } @@ -235,4 +244,111 @@ impl S { fn assoc(S: S, S: S) {} } fn f() { S::assoc$0(S, S); }"#, ); } + + #[test] + fn inherent_method() { + check_assist( + unqualify_method_call, + r#" +mod foo { + pub struct Bar; + impl Bar { + pub fn bar(self) {} + } +} + +fn baz() { + foo::Bar::b$0ar(foo::Bar); +} + "#, + r#" +mod foo { + pub struct Bar; + impl Bar { + pub fn bar(self) {} + } +} + +fn baz() { + foo::Bar.bar(); +} + "#, + ); + } + + #[test] + fn trait_method_in_impl() { + check_assist( + unqualify_method_call, + r#" +mod foo { + pub trait Bar { + pub fn bar(self) {} + } +} + +struct Baz; +impl foo::Bar for Baz { + fn bar(self) { + foo::Bar::b$0ar(Baz); + } +} + "#, + r#" +mod foo { + pub trait Bar { + pub fn bar(self) {} + } +} + +struct Baz; +impl foo::Bar for Baz { + fn bar(self) { + Baz.bar(); + } +} + "#, + ); + } + + #[test] + fn trait_method_already_imported() { + check_assist( + unqualify_method_call, + r#" +mod foo { + pub struct Foo; + pub trait Bar { + pub fn bar(self) {} + } + impl Bar for Foo { + pub fn bar(self) {} + } +} + +use foo::Bar; + +fn baz() { + foo::Bar::b$0ar(foo::Foo); +} + "#, + r#" +mod foo { + pub struct Foo; + pub trait Bar { + pub fn bar(self) {} + } + impl Bar for Foo { + pub fn bar(self) {} + } +} + +use foo::Bar; + +fn baz() { + foo::Foo.bar(); +} + "#, + ); + } } From 8d247472e5f9612b4bf9668de937ad9c373b891c Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Mon, 4 Aug 2025 16:11:51 +0800 Subject: [PATCH 049/682] Merge Trait and TraitAlias handling --- .../rust-analyzer/crates/hir-def/src/attr.rs | 2 - .../rust-analyzer/crates/hir-def/src/db.rs | 20 +--- .../crates/hir-def/src/dyn_map.rs | 5 +- .../crates/hir-def/src/expr_store/lower.rs | 24 +---- .../crates/hir-def/src/expr_store/pretty.rs | 1 - .../src/expr_store/tests/signatures.rs | 2 - .../crates/hir-def/src/hir/generics.rs | 11 --- .../crates/hir-def/src/item_scope.rs | 1 - .../crates/hir-def/src/item_tree.rs | 8 -- .../crates/hir-def/src/item_tree/lower.rs | 16 +-- .../crates/hir-def/src/item_tree/pretty.rs | 8 +- .../rust-analyzer/crates/hir-def/src/lib.rs | 14 --- .../crates/hir-def/src/nameres/assoc.rs | 10 +- .../crates/hir-def/src/nameres/collector.rs | 16 +-- .../crates/hir-def/src/resolver.rs | 13 +-- .../crates/hir-def/src/signatures.rs | 31 +----- .../rust-analyzer/crates/hir-def/src/src.rs | 9 +- .../crates/hir-ty/src/generics.rs | 5 +- .../rust-analyzer/crates/hir-ty/src/infer.rs | 1 - .../crates/hir-ty/src/lower/path.rs | 7 +- .../rust-analyzer/crates/hir-ty/src/tests.rs | 1 + .../crates/hir-ty/src/variance.rs | 5 - .../rust-analyzer/crates/hir/src/attrs.rs | 8 +- .../rust-analyzer/crates/hir/src/display.rs | 21 +--- .../rust-analyzer/crates/hir/src/from_id.rs | 5 - .../crates/hir/src/has_source.rs | 11 +-- src/tools/rust-analyzer/crates/hir/src/lib.rs | 51 +--------- .../rust-analyzer/crates/hir/src/semantics.rs | 12 +-- .../hir/src/semantics/child_by_source.rs | 3 - .../crates/hir/src/semantics/source_to_def.rs | 16 +-- .../crates/hir/src/source_analyzer.rs | 4 +- .../rust-analyzer/crates/hir/src/symbols.rs | 3 - .../src/handlers/extract_module.rs | 1 - .../src/handlers/fix_visibility.rs | 4 - .../ide-assists/src/handlers/move_bounds.rs | 1 - .../ide-completion/src/completions/type.rs | 4 +- .../crates/ide-completion/src/context.rs | 1 - .../ide-completion/src/context/analysis.rs | 3 - .../crates/ide-completion/src/item.rs | 1 - .../crates/ide-completion/src/render.rs | 8 +- .../crates/ide-db/src/active_parameter.rs | 1 - .../rust-analyzer/crates/ide-db/src/defs.rs | 15 +-- .../crates/ide-db/src/documentation.rs | 3 +- .../ide-db/src/imports/import_assets.rs | 2 - .../rust-analyzer/crates/ide-db/src/lib.rs | 2 - .../rust-analyzer/crates/ide-db/src/rename.rs | 1 - .../rust-analyzer/crates/ide-db/src/search.rs | 1 - .../crates/ide-db/src/symbol_index.rs | 1 - .../rust-analyzer/crates/ide/src/doc_links.rs | 5 +- .../crates/ide/src/file_structure.rs | 3 +- .../crates/ide/src/folding_ranges.rs | 7 -- .../crates/ide/src/hover/render.rs | 1 - .../rust-analyzer/crates/ide/src/moniker.rs | 1 - .../rust-analyzer/crates/ide/src/move_item.rs | 1 - .../crates/ide/src/navigation_target.rs | 11 --- .../crates/ide/src/references.rs | 2 +- .../rust-analyzer/crates/ide/src/runnables.rs | 1 - .../crates/ide/src/signature_help.rs | 4 - .../ide/src/syntax_highlighting/highlight.rs | 2 - .../ide/src/syntax_highlighting/inject.rs | 1 - .../ide/src/syntax_highlighting/tags.rs | 1 - .../test_data/highlight_strings.html | 4 +- .../crates/parser/src/grammar/items/traits.rs | 2 +- .../parser/src/syntax_kind/generated.rs | 2 - .../parser/inline/ok/trait_alias.rast | 2 +- .../inline/ok/trait_alias_where_clause.rast | 4 +- .../crates/rust-analyzer/src/lsp/to_proto.rs | 5 +- .../rust-analyzer/crates/span/src/ast_id.rs | 3 +- .../rust-analyzer/crates/syntax/rust.ungram | 8 +- .../rust-analyzer/crates/syntax/src/ast.rs | 3 +- .../crates/syntax/src/ast/edit_in_place.rs | 36 +------ .../crates/syntax/src/ast/generated/nodes.rs | 97 +------------------ .../crates/syntax/src/ast/node_ext.rs | 45 --------- .../xtask/src/codegen/grammar.rs | 1 - 74 files changed, 68 insertions(+), 577 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs index 53250510f875..b4fcfa11aea7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs @@ -554,7 +554,6 @@ impl AttrsWithOwner { AdtId::UnionId(it) => attrs_from_ast_id_loc(db, it), }, AttrDefId::TraitId(it) => attrs_from_ast_id_loc(db, it), - AttrDefId::TraitAliasId(it) => attrs_from_ast_id_loc(db, it), AttrDefId::MacroId(it) => match it { MacroId::Macro2Id(it) => attrs_from_ast_id_loc(db, it), MacroId::MacroRulesId(it) => attrs_from_ast_id_loc(db, it), @@ -659,7 +658,6 @@ impl AttrsWithOwner { AttrDefId::StaticId(id) => any_has_attrs(db, id), AttrDefId::ConstId(id) => any_has_attrs(db, id), AttrDefId::TraitId(id) => any_has_attrs(db, id), - AttrDefId::TraitAliasId(id) => any_has_attrs(db, id), AttrDefId::TypeAliasId(id) => any_has_attrs(db, id), AttrDefId::MacroId(id) => match id { MacroId::Macro2Id(id) => any_has_attrs(db, id), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/db.rs b/src/tools/rust-analyzer/crates/hir-def/src/db.rs index c67bb2422ac6..4e1d598623ab 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs @@ -15,8 +15,8 @@ use crate::{ EnumVariantId, EnumVariantLoc, ExternBlockId, ExternBlockLoc, ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc, LocalFieldId, Macro2Id, Macro2Loc, MacroExpander, MacroId, MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, ProcMacroId, - ProcMacroLoc, StaticId, StaticLoc, StructId, StructLoc, TraitAliasId, TraitAliasLoc, TraitId, - TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, UseId, UseLoc, VariantId, + ProcMacroLoc, StaticId, StaticLoc, StructId, StructLoc, TraitId, TraitLoc, TypeAliasId, + TypeAliasLoc, UnionId, UnionLoc, UseId, UseLoc, VariantId, attr::{Attrs, AttrsWithOwner}, expr_store::{ Body, BodySourceMap, ExpressionStore, ExpressionStoreSourceMap, scope::ExprScopes, @@ -28,7 +28,7 @@ use crate::{ nameres::crate_def_map, signatures::{ ConstSignature, EnumSignature, FunctionSignature, ImplSignature, StaticSignature, - StructSignature, TraitAliasSignature, TraitSignature, TypeAliasSignature, UnionSignature, + StructSignature, TraitSignature, TypeAliasSignature, UnionSignature, }, tt, visibility::{self, Visibility}, @@ -69,9 +69,6 @@ pub trait InternDatabase: RootQueryDb { #[salsa::interned] fn intern_trait(&self, loc: TraitLoc) -> TraitId; - #[salsa::interned] - fn intern_trait_alias(&self, loc: TraitAliasLoc) -> TraitAliasId; - #[salsa::interned] fn intern_type_alias(&self, loc: TypeAliasLoc) -> TypeAliasId; @@ -152,11 +149,6 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + SourceDatabase { self.function_signature_with_source_map(e).0 } - #[salsa::tracked] - fn trait_alias_signature(&self, e: TraitAliasId) -> Arc { - self.trait_alias_signature_with_source_map(e).0 - } - #[salsa::tracked] fn type_alias_signature(&self, e: TypeAliasId) -> Arc { self.type_alias_signature_with_source_map(e).0 @@ -210,12 +202,6 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + SourceDatabase { e: FunctionId, ) -> (Arc, Arc); - #[salsa::invoke(TraitAliasSignature::query)] - fn trait_alias_signature_with_source_map( - &self, - e: TraitAliasId, - ) -> (Arc, Arc); - #[salsa::invoke(TypeAliasSignature::query)] fn type_alias_signature_with_source_map( &self, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs index 20018b61e5cc..7d3a94b03833 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs @@ -33,8 +33,8 @@ pub mod keys { use crate::{ BlockId, ConstId, EnumId, EnumVariantId, ExternBlockId, ExternCrateId, FieldId, FunctionId, - ImplId, LifetimeParamId, Macro2Id, MacroRulesId, ProcMacroId, StaticId, StructId, - TraitAliasId, TraitId, TypeAliasId, TypeOrConstParamId, UnionId, UseId, + ImplId, LifetimeParamId, Macro2Id, MacroRulesId, ProcMacroId, StaticId, StructId, TraitId, + TypeAliasId, TypeOrConstParamId, UnionId, UseId, dyn_map::{DynMap, Policy}, }; @@ -48,7 +48,6 @@ pub mod keys { pub const IMPL: Key = Key::new(); pub const EXTERN_BLOCK: Key = Key::new(); pub const TRAIT: Key = Key::new(); - pub const TRAIT_ALIAS: Key = Key::new(); pub const STRUCT: Key = Key::new(); pub const UNION: Key = Key::new(); pub const ENUM: Key = Key::new(); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 3b9281ffb9c1..3794cb18e936 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -33,7 +33,7 @@ use tt::TextRange; use crate::{ AdtId, BlockId, BlockLoc, DefWithBodyId, FunctionId, GenericDefId, ImplId, MacroId, - ModuleDefId, ModuleId, TraitAliasId, TraitId, TypeAliasId, UnresolvedMacro, + ModuleDefId, ModuleId, TraitId, TypeAliasId, UnresolvedMacro, builtin_type::BuiltinUint, db::DefDatabase, expr_store::{ @@ -252,28 +252,6 @@ pub(crate) fn lower_trait( (store, source_map, params) } -pub(crate) fn lower_trait_alias( - db: &dyn DefDatabase, - module: ModuleId, - trait_syntax: InFile, - trait_id: TraitAliasId, -) -> (ExpressionStore, ExpressionStoreSourceMap, Arc) { - let mut expr_collector = ExprCollector::new(db, module, trait_syntax.file_id); - let mut collector = generics::GenericParamsCollector::with_self_param( - &mut expr_collector, - trait_id.into(), - trait_syntax.value.type_bound_list(), - ); - collector.lower( - &mut expr_collector, - trait_syntax.value.generic_param_list(), - trait_syntax.value.where_clause(), - ); - let params = collector.finish(); - let (store, source_map) = expr_collector.store.finish(); - (store, source_map, params) -} - pub(crate) fn lower_type_alias( db: &dyn DefDatabase, module: ModuleId, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs index b81dcc1fe96d..5b9da3c5e668 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs @@ -183,7 +183,6 @@ pub fn print_signature(db: &dyn DefDatabase, owner: GenericDefId, edition: Editi } GenericDefId::ImplId(id) => format!("unimplemented {id:?}"), GenericDefId::StaticId(id) => format!("unimplemented {id:?}"), - GenericDefId::TraitAliasId(id) => format!("unimplemented {id:?}"), GenericDefId::TraitId(id) => format!("unimplemented {id:?}"), GenericDefId::TypeAliasId(id) => format!("unimplemented {id:?}"), } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/signatures.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/signatures.rs index efb558a77581..b68674c7a74f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/signatures.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/signatures.rs @@ -24,7 +24,6 @@ fn lower_and_print(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expe ModuleDefId::ConstId(id) => id.into(), ModuleDefId::StaticId(id) => id.into(), ModuleDefId::TraitId(id) => id.into(), - ModuleDefId::TraitAliasId(id) => id.into(), ModuleDefId::TypeAliasId(id) => id.into(), ModuleDefId::EnumVariantId(_) => continue, ModuleDefId::BuiltinType(_) => continue, @@ -51,7 +50,6 @@ fn lower_and_print(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expe GenericDefId::ImplId(_id) => (), GenericDefId::StaticId(_id) => (), - GenericDefId::TraitAliasId(_id) => (), GenericDefId::TraitId(_id) => (), GenericDefId::TypeAliasId(_id) => (), } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs index 94e683cb0f8f..60cd66bf6b08 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs @@ -203,9 +203,6 @@ impl GenericParams { } GenericDefId::ImplId(impl_id) => db.impl_signature(impl_id).generic_params.clone(), GenericDefId::StaticId(_) => EMPTY.clone(), - GenericDefId::TraitAliasId(trait_alias_id) => { - db.trait_alias_signature(trait_alias_id).generic_params.clone() - } GenericDefId::TraitId(trait_id) => db.trait_signature(trait_id).generic_params.clone(), GenericDefId::TypeAliasId(type_alias_id) => { db.type_alias_signature(type_alias_id).generic_params.clone() @@ -246,10 +243,6 @@ impl GenericParams { let sig = db.static_signature(id); (EMPTY.clone(), sig.store.clone()) } - GenericDefId::TraitAliasId(id) => { - let sig = db.trait_alias_signature(id); - (sig.generic_params.clone(), sig.store.clone()) - } GenericDefId::TraitId(id) => { let sig = db.trait_signature(id); (sig.generic_params.clone(), sig.store.clone()) @@ -294,10 +287,6 @@ impl GenericParams { let (sig, sm) = db.static_signature_with_source_map(id); (EMPTY.clone(), sig.store.clone(), sm) } - GenericDefId::TraitAliasId(id) => { - let (sig, sm) = db.trait_alias_signature_with_source_map(id); - (sig.generic_params.clone(), sig.store.clone(), sm) - } GenericDefId::TraitId(id) => { let (sig, sm) = db.trait_signature_with_source_map(id); (sig.generic_params.clone(), sig.store.clone(), sm) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs index 8f526d1a2369..77ed664f4443 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs @@ -872,7 +872,6 @@ impl PerNs { PerNs::values(def, v, import.and_then(ImportOrExternCrate::import_or_glob)) } ModuleDefId::TraitId(_) => PerNs::types(def, v, import), - ModuleDefId::TraitAliasId(_) => PerNs::types(def, v, import), ModuleDefId::TypeAliasId(_) => PerNs::types(def, v, import), ModuleDefId::BuiltinType(_) => PerNs::types(def, v, import), ModuleDefId::MacroId(mac) => PerNs::macros(mac, v, import), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs index c63333985749..f35df8d3a7e1 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs @@ -276,7 +276,6 @@ enum SmallModItem { Static(Static), Struct(Struct), Trait(Trait), - TraitAlias(TraitAlias), TypeAlias(TypeAlias), Union(Union), } @@ -404,7 +403,6 @@ ModItemId -> Static in small_data -> ast::Static, Struct in small_data -> ast::Struct, Trait in small_data -> ast::Trait, - TraitAlias in small_data -> ast::TraitAlias, TypeAlias in small_data -> ast::TypeAlias, Union in small_data -> ast::Union, Use in big_data -> ast::Use, @@ -583,12 +581,6 @@ pub struct Trait { pub(crate) visibility: RawVisibilityId, } -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct TraitAlias { - pub name: Name, - pub(crate) visibility: RawVisibilityId, -} - #[derive(Debug, Clone, Eq, PartialEq)] pub struct Impl {} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs index 032b287cd6a8..454e06399583 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs @@ -23,7 +23,7 @@ use crate::{ BigModItem, Const, Enum, ExternBlock, ExternCrate, FieldsShape, Function, Impl, ImportAlias, Interned, ItemTree, ItemTreeAstId, Macro2, MacroCall, MacroRules, Mod, ModItemId, ModKind, ModPath, RawAttrs, RawVisibility, RawVisibilityId, SmallModItem, - Static, Struct, StructKind, Trait, TraitAlias, TypeAlias, Union, Use, UseTree, UseTreeKind, + Static, Struct, StructKind, Trait, TypeAlias, Union, Use, UseTree, UseTreeKind, VisibilityExplicitness, }, }; @@ -134,7 +134,6 @@ impl<'a> Ctx<'a> { ast::Item::Const(ast) => self.lower_const(ast).into(), ast::Item::Module(ast) => self.lower_module(ast)?.into(), ast::Item::Trait(ast) => self.lower_trait(ast)?.into(), - ast::Item::TraitAlias(ast) => self.lower_trait_alias(ast)?.into(), ast::Item::Impl(ast) => self.lower_impl(ast).into(), ast::Item::Use(ast) => self.lower_use(ast)?.into(), ast::Item::ExternCrate(ast) => self.lower_extern_crate(ast)?.into(), @@ -267,19 +266,6 @@ impl<'a> Ctx<'a> { Some(ast_id) } - fn lower_trait_alias( - &mut self, - trait_alias_def: &ast::TraitAlias, - ) -> Option> { - let name = trait_alias_def.name()?.as_name(); - let visibility = self.lower_visibility(trait_alias_def); - let ast_id = self.source_ast_id_map.ast_id(trait_alias_def); - - let alias = TraitAlias { name, visibility }; - self.tree.small_data.insert(ast_id.upcast(), SmallModItem::TraitAlias(alias)); - Some(ast_id) - } - fn lower_impl(&mut self, impl_def: &ast::Impl) -> ItemTreeAstId { let ast_id = self.source_ast_id_map.ast_id(impl_def); // Note that trait impls don't get implicit `Self` unlike traits, because here they are a diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs index 696174cb072b..94a6cce3ce33 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs @@ -8,7 +8,7 @@ use crate::{ item_tree::{ Const, DefDatabase, Enum, ExternBlock, ExternCrate, FieldsShape, Function, Impl, ItemTree, Macro2, MacroCall, MacroRules, Mod, ModItemId, ModKind, RawAttrs, RawVisibilityId, Static, - Struct, Trait, TraitAlias, TypeAlias, Union, Use, UseTree, UseTreeKind, + Struct, Trait, TypeAlias, Union, Use, UseTree, UseTreeKind, }, visibility::RawVisibility, }; @@ -250,12 +250,6 @@ impl Printer<'_> { self.print_visibility(*visibility); w!(self, "trait {} {{ ... }}", name.display(self.db, self.edition)); } - ModItemId::TraitAlias(ast_id) => { - let TraitAlias { name, visibility } = &self.tree[ast_id]; - self.print_ast_id(ast_id.erase()); - self.print_visibility(*visibility); - wln!(self, "trait {} = ..;", name.display(self.db, self.edition)); - } ModItemId::Impl(ast_id) => { let Impl {} = &self.tree[ast_id]; self.print_ast_id(ast_id.erase()); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index bdf8b453e2d6..1f5b1e023702 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -318,9 +318,6 @@ impl TraitId { } } -pub type TraitAliasLoc = ItemLoc; -impl_intern!(TraitAliasId, TraitAliasLoc, intern_trait_alias, lookup_intern_trait_alias); - type TypeAliasLoc = AssocItemLoc; impl_intern!(TypeAliasId, TypeAliasLoc, intern_type_alias, lookup_intern_type_alias); @@ -742,7 +739,6 @@ pub enum ModuleDefId { ConstId(ConstId), StaticId(StaticId), TraitId(TraitId), - TraitAliasId(TraitAliasId), TypeAliasId(TypeAliasId), BuiltinType(BuiltinType), MacroId(MacroId), @@ -756,7 +752,6 @@ impl_from!( ConstId, StaticId, TraitId, - TraitAliasId, TypeAliasId, BuiltinType for ModuleDefId @@ -862,7 +857,6 @@ pub enum GenericDefId { // More importantly, this completes the set of items that contain type references // which is to be used by the signature expression store in the future. StaticId(StaticId), - TraitAliasId(TraitAliasId), TraitId(TraitId), TypeAliasId(TypeAliasId), } @@ -872,7 +866,6 @@ impl_from!( FunctionId, ImplId, StaticId, - TraitAliasId, TraitId, TypeAliasId for GenericDefId @@ -902,7 +895,6 @@ impl GenericDefId { GenericDefId::AdtId(AdtId::UnionId(it)) => file_id_and_params_of_item_loc(db, it), GenericDefId::AdtId(AdtId::EnumId(it)) => file_id_and_params_of_item_loc(db, it), GenericDefId::TraitId(it) => file_id_and_params_of_item_loc(db, it), - GenericDefId::TraitAliasId(it) => file_id_and_params_of_item_loc(db, it), GenericDefId::ImplId(it) => file_id_and_params_of_item_loc(db, it), GenericDefId::ConstId(it) => (it.lookup(db).id.file_id, None), GenericDefId::StaticId(it) => (it.lookup(db).id.file_id, None), @@ -978,7 +970,6 @@ pub enum AttrDefId { StaticId(StaticId), ConstId(ConstId), TraitId(TraitId), - TraitAliasId(TraitAliasId), TypeAliasId(TypeAliasId), MacroId(MacroId), ImplId(ImplId), @@ -997,7 +988,6 @@ impl_from!( ConstId, FunctionId, TraitId, - TraitAliasId, TypeAliasId, MacroId(Macro2Id, MacroRulesId, ProcMacroId), ImplId, @@ -1020,7 +1010,6 @@ impl TryFrom for AttrDefId { ModuleDefId::StaticId(it) => Ok(it.into()), ModuleDefId::TraitId(it) => Ok(it.into()), ModuleDefId::TypeAliasId(it) => Ok(it.into()), - ModuleDefId::TraitAliasId(id) => Ok(id.into()), ModuleDefId::MacroId(id) => Ok(id.into()), ModuleDefId::BuiltinType(_) => Err(()), } @@ -1266,7 +1255,6 @@ impl HasModule for GenericDefId { GenericDefId::FunctionId(it) => it.module(db), GenericDefId::AdtId(it) => it.module(db), GenericDefId::TraitId(it) => it.module(db), - GenericDefId::TraitAliasId(it) => it.module(db), GenericDefId::TypeAliasId(it) => it.module(db), GenericDefId::ImplId(it) => it.module(db), GenericDefId::ConstId(it) => it.module(db), @@ -1286,7 +1274,6 @@ impl HasModule for AttrDefId { AttrDefId::StaticId(it) => it.module(db), AttrDefId::ConstId(it) => it.module(db), AttrDefId::TraitId(it) => it.module(db), - AttrDefId::TraitAliasId(it) => it.module(db), AttrDefId::TypeAliasId(it) => it.module(db), AttrDefId::ImplId(it) => it.module(db), AttrDefId::ExternBlockId(it) => it.module(db), @@ -1316,7 +1303,6 @@ impl ModuleDefId { ModuleDefId::ConstId(id) => id.module(db), ModuleDefId::StaticId(id) => id.module(db), ModuleDefId::TraitId(id) => id.module(db), - ModuleDefId::TraitAliasId(id) => id.module(db), ModuleDefId::TypeAliasId(id) => id.module(db), ModuleDefId::MacroId(id) => id.module(db), ModuleDefId::BuiltinType(_) => return None, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs index 07210df88736..8d2a386de8ec 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/assoc.rs @@ -51,10 +51,18 @@ impl TraitItems { tr: TraitId, ) -> (TraitItems, DefDiagnostics) { let ItemLoc { container: module_id, id: ast_id } = tr.lookup(db); + let ast_id_map = db.ast_id_map(ast_id.file_id); + let source = ast_id.with_value(ast_id_map.get(ast_id.value)).to_node(db); + if source.eq_token().is_some() { + // FIXME(trait-alias) probably needs special handling here + return ( + TraitItems { macro_calls: ThinVec::new(), items: Box::default() }, + DefDiagnostics::new(vec![]), + ); + } let collector = AssocItemCollector::new(db, module_id, ItemContainerId::TraitId(tr), ast_id.file_id); - let source = ast_id.with_value(collector.ast_id_map.get(ast_id.value)).to_node(db); let (items, macro_calls, diagnostics) = collector.collect(source.assoc_item_list()); (TraitItems { macro_calls, items }, DefDiagnostics::new(diagnostics)) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index 267c4451b9d7..07e61718222b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -30,7 +30,7 @@ use crate::{ ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, ImplLoc, Intern, ItemContainerId, LocalModuleId, Lookup, Macro2Id, Macro2Loc, MacroExpander, MacroId, MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, ModuleDefId, ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc, - StructLoc, TraitAliasLoc, TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro, UseId, UseLoc, + StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro, UseId, UseLoc, attr::Attrs, db::DefDatabase, item_scope::{GlobId, ImportId, ImportOrExternCrate, PerNsGlobImports}, @@ -1954,20 +1954,6 @@ impl ModCollector<'_, '_> { false, ); } - ModItemId::TraitAlias(id) => { - let it = &self.item_tree[id]; - - let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]); - update_def( - self.def_collector, - TraitAliasLoc { container: module, id: InFile::new(self.file_id(), id) } - .intern(db) - .into(), - &it.name, - vis, - false, - ); - } ModItemId::TypeAlias(id) => { let it = &self.item_tree[id]; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs index a10990e6a8f9..698292c2fbea 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -20,7 +20,7 @@ use crate::{ EnumVariantId, ExternBlockId, ExternCrateId, FunctionId, FxIndexMap, GenericDefId, GenericParamId, HasModule, ImplId, ItemContainerId, LifetimeParamId, LocalModuleId, Lookup, Macro2Id, MacroId, MacroRulesId, ModuleDefId, ModuleId, ProcMacroId, StaticId, StructId, - TraitAliasId, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UseId, VariantId, + TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UseId, VariantId, builtin_type::BuiltinType, db::DefDatabase, expr_store::{ @@ -105,7 +105,6 @@ pub enum TypeNs { TypeAliasId(TypeAliasId), BuiltinType(BuiltinType), TraitId(TraitId), - TraitAliasId(TraitAliasId), ModuleId(ModuleId), } @@ -1150,7 +1149,6 @@ impl<'db> ModuleItemMap<'db> { let ty = match def.def { ModuleDefId::AdtId(it) => TypeNs::AdtId(it), ModuleDefId::TraitId(it) => TypeNs::TraitId(it), - ModuleDefId::TraitAliasId(it) => TypeNs::TraitAliasId(it), ModuleDefId::TypeAliasId(it) => TypeNs::TypeAliasId(it), ModuleDefId::BuiltinType(it) => TypeNs::BuiltinType(it), @@ -1195,7 +1193,6 @@ fn to_value_ns(per_ns: PerNs) -> Option<(ValueNs, Option)> { ModuleDefId::AdtId(AdtId::EnumId(_) | AdtId::UnionId(_)) | ModuleDefId::TraitId(_) - | ModuleDefId::TraitAliasId(_) | ModuleDefId::TypeAliasId(_) | ModuleDefId::BuiltinType(_) | ModuleDefId::MacroId(_) @@ -1214,7 +1211,6 @@ fn to_type_ns(per_ns: PerNs) -> Option<(TypeNs, Option)> { ModuleDefId::BuiltinType(it) => TypeNs::BuiltinType(it), ModuleDefId::TraitId(it) => TypeNs::TraitId(it), - ModuleDefId::TraitAliasId(it) => TypeNs::TraitAliasId(it), ModuleDefId::ModuleId(it) => TypeNs::ModuleId(it), @@ -1320,12 +1316,6 @@ impl HasResolver for TraitId { } } -impl HasResolver for TraitAliasId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { - lookup_resolver(db, self).push_generic_params_scope(db, self.into()) - } -} - impl + Copy> HasResolver for T { fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> { let def = self.into(); @@ -1410,7 +1400,6 @@ impl HasResolver for GenericDefId { GenericDefId::FunctionId(inner) => inner.resolver(db), GenericDefId::AdtId(adt) => adt.resolver(db), GenericDefId::TraitId(inner) => inner.resolver(db), - GenericDefId::TraitAliasId(inner) => inner.resolver(db), GenericDefId::TypeAliasId(inner) => inner.resolver(db), GenericDefId::ImplId(inner) => inner.resolver(db), GenericDefId::ConstId(inner) => inner.resolver(db), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs index 598b2c0314a9..906c1c0e9ff1 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs @@ -20,15 +20,13 @@ use triomphe::Arc; use crate::{ ConstId, EnumId, EnumVariantId, EnumVariantLoc, ExternBlockId, FunctionId, HasModule, ImplId, - ItemContainerId, ModuleId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, UnionId, - VariantId, + ItemContainerId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, UnionId, VariantId, attr::Attrs, db::DefDatabase, expr_store::{ ExpressionStore, ExpressionStoreSourceMap, lower::{ - ExprCollector, lower_function, lower_generic_params, lower_trait, lower_trait_alias, - lower_type_alias, + ExprCollector, lower_function, lower_generic_params, lower_trait, lower_type_alias, }, }, hir::{ExprId, PatId, generics::GenericParams}, @@ -469,31 +467,6 @@ impl TraitSignature { } } -#[derive(Debug, PartialEq, Eq)] -pub struct TraitAliasSignature { - pub name: Name, - pub generic_params: Arc, - pub store: Arc, -} - -impl TraitAliasSignature { - pub fn query( - db: &dyn DefDatabase, - id: TraitAliasId, - ) -> (Arc, Arc) { - let loc = id.lookup(db); - - let source = loc.source(db); - let name = as_name_opt(source.value.name()); - let (store, source_map, generic_params) = lower_trait_alias(db, loc.container, source, id); - - ( - Arc::new(TraitAliasSignature { generic_params, store: Arc::new(store), name }), - Arc::new(source_map), - ) - } -} - bitflags! { #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] pub struct FnFlags: u16 { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/src.rs b/src/tools/rust-analyzer/crates/hir-def/src/src.rs index aa373a27b0d5..367b543cf908 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/src.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/src.rs @@ -71,7 +71,7 @@ impl HasChildSource> for UseId { } impl HasChildSource for GenericDefId { - type Value = Either; + type Value = Either; fn child_source( &self, db: &dyn DefDatabase, @@ -89,12 +89,7 @@ impl HasChildSource for GenericDefId { GenericDefId::TraitId(id) => { let trait_ref = id.lookup(db).source(db).value; let idx = idx_iter.next().unwrap(); - params.insert(idx, Either::Right(ast::TraitOrAlias::Trait(trait_ref))); - } - GenericDefId::TraitAliasId(id) => { - let alias = id.lookup(db).source(db).value; - let idx = idx_iter.next().unwrap(); - params.insert(idx, Either::Right(ast::TraitOrAlias::TraitAlias(alias))); + params.insert(idx, Either::Right(trait_ref)); } _ => {} } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs index e6470e5272d0..412b3ac42502 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs @@ -273,7 +273,7 @@ impl Generics { pub(crate) fn trait_self_param_idx(db: &dyn DefDatabase, def: GenericDefId) -> Option { match def { - GenericDefId::TraitId(_) | GenericDefId::TraitAliasId(_) => { + GenericDefId::TraitId(_) => { let params = db.generic_params(def); params.trait_self_param().map(|idx| idx.into_raw().into_u32() as usize) } @@ -295,8 +295,7 @@ pub(crate) fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Opt GenericDefId::StaticId(_) | GenericDefId::AdtId(_) | GenericDefId::TraitId(_) - | GenericDefId::ImplId(_) - | GenericDefId::TraitAliasId(_) => return None, + | GenericDefId::ImplId(_) => return None, }; match container { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 7a11ec660d04..d778cc0e30ed 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -1790,7 +1790,6 @@ impl<'db> InferenceContext<'db> { TypeNs::AdtId(AdtId::EnumId(_)) | TypeNs::BuiltinType(_) | TypeNs::TraitId(_) - | TypeNs::TraitAliasId(_) | TypeNs::ModuleId(_) => { // FIXME diagnostic (self.err_ty(), None) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs index 9519c38eeddf..fb85909d7f41 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs @@ -220,10 +220,6 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { }; return (ty, None); } - TypeNs::TraitAliasId(_) => { - // FIXME(trait_alias): Implement trait alias. - return (TyKind::Error.intern(Interner), None); - } TypeNs::GenericParam(param_id) => match self.ctx.type_param_mode { ParamLoweringMode::Placeholder => { TyKind::Placeholder(to_placeholder_idx(self.ctx.db, param_id.into())) @@ -311,8 +307,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { TypeNs::AdtId(_) | TypeNs::EnumVariantId(_) | TypeNs::TypeAliasId(_) - | TypeNs::TraitId(_) - | TypeNs::TraitAliasId(_) => {} + | TypeNs::TraitId(_) => {} } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs index fc31973022bd..c5f78e2f3cec 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -9,6 +9,7 @@ mod never_type; mod patterns; mod regression; mod simple; +mod trait_aliases; mod traits; mod type_alias_impl_traits; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs index 08a215fecf62..d8a058533f76 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs @@ -990,7 +990,6 @@ struct FixedPoint(&'static FixedPoint<(), T, U>, V); ModuleDefId::AdtId(it) => it.into(), ModuleDefId::ConstId(it) => it.into(), ModuleDefId::TraitId(it) => it.into(), - ModuleDefId::TraitAliasId(it) => it.into(), ModuleDefId::TypeAliasId(it) => it.into(), _ => return, }) @@ -1021,10 +1020,6 @@ struct FixedPoint(&'static FixedPoint<(), T, U>, V); let loc = it.lookup(&db); loc.source(&db).value.name().unwrap() } - GenericDefId::TraitAliasId(it) => { - let loc = it.lookup(&db); - loc.source(&db).value.name().unwrap() - } GenericDefId::TypeAliasId(it) => { let loc = it.lookup(&db); loc.source(&db).value.name().unwrap() diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs index c8645b628239..928753ef0ede 100644 --- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs @@ -19,7 +19,7 @@ use hir_ty::{db::HirDatabase, method_resolution}; use crate::{ Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, DocLinkDef, Enum, ExternCrateDecl, Field, Function, GenericParam, HasCrate, Impl, LifetimeParam, Macro, Module, ModuleDef, Static, - Struct, Trait, TraitAlias, Type, TypeAlias, TypeParam, Union, Variant, VariantDef, + Struct, Trait, Type, TypeAlias, TypeParam, Union, Variant, VariantDef, }; pub trait HasAttrs { @@ -48,7 +48,6 @@ impl_has_attrs![ (Static, StaticId), (Const, ConstId), (Trait, TraitId), - (TraitAlias, TraitAliasId), (TypeAlias, TypeAliasId), (Macro, MacroId), (Function, FunctionId), @@ -137,7 +136,6 @@ fn resolve_doc_path_on_( AttrDefId::StaticId(it) => it.resolver(db), AttrDefId::ConstId(it) => it.resolver(db), AttrDefId::TraitId(it) => it.resolver(db), - AttrDefId::TraitAliasId(it) => it.resolver(db), AttrDefId::TypeAliasId(it) => it.resolver(db), AttrDefId::ImplId(it) => it.resolver(db), AttrDefId::ExternBlockId(it) => it.resolver(db), @@ -216,10 +214,6 @@ fn resolve_assoc_or_field( DocLinkDef::ModuleDef(def) }); } - TypeNs::TraitAliasId(_) => { - // XXX: Do these get resolved? - return None; - } TypeNs::ModuleId(_) => { return None; } diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index 2960ebedf380..b9c5131ffbb5 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -23,8 +23,8 @@ use itertools::Itertools; use crate::{ Adt, AsAssocItem, AssocItem, AssocItemContainer, Const, ConstParam, Crate, Enum, ExternCrateDecl, Field, Function, GenericParam, HasCrate, HasVisibility, Impl, LifetimeParam, - Macro, Module, SelfParam, Static, Struct, StructKind, Trait, TraitAlias, TraitRef, TupleField, - TyBuilder, Type, TypeAlias, TypeOrConstParam, TypeParam, Union, Variant, + Macro, Module, SelfParam, Static, Struct, StructKind, Trait, TraitRef, TupleField, TyBuilder, + Type, TypeAlias, TypeOrConstParam, TypeParam, Union, Variant, }; impl HirDisplay for Function { @@ -751,6 +751,7 @@ impl HirDisplay for TraitRef<'_> { impl HirDisplay for Trait { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { + // FIXME(trait-alias) needs special handling to print the equal sign write_trait_header(self, f)?; let def_id = GenericDefId::TraitId(self.id); let has_where_clause = write_where_clause(def_id, f)?; @@ -802,22 +803,6 @@ fn write_trait_header(trait_: &Trait, f: &mut HirFormatter<'_>) -> Result<(), Hi Ok(()) } -impl HirDisplay for TraitAlias { - fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { - write_visibility(self.module(f.db).id, self.visibility(f.db), f)?; - let data = f.db.trait_alias_signature(self.id); - write!(f, "trait {}", data.name.display(f.db, f.edition()))?; - let def_id = GenericDefId::TraitAliasId(self.id); - write_generic_params(def_id, f)?; - f.write_str(" = ")?; - // FIXME: Currently we lower every bounds in a trait alias as a trait bound on `Self` i.e. - // `trait Foo = Bar` is stored and displayed as `trait Foo = where Self: Bar`, which might - // be less readable. - write_where_clause(def_id, f)?; - Ok(()) - } -} - impl HirDisplay for TypeAlias { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { write_visibility(self.module(f.db).id, self.visibility(f.db), f)?; diff --git a/src/tools/rust-analyzer/crates/hir/src/from_id.rs b/src/tools/rust-analyzer/crates/hir/src/from_id.rs index c6446693df3e..bc025c5ef5cf 100644 --- a/src/tools/rust-analyzer/crates/hir/src/from_id.rs +++ b/src/tools/rust-analyzer/crates/hir/src/from_id.rs @@ -37,7 +37,6 @@ from_id![ (hir_def::EnumId, crate::Enum), (hir_def::TypeAliasId, crate::TypeAlias), (hir_def::TraitId, crate::Trait), - (hir_def::TraitAliasId, crate::TraitAlias), (hir_def::StaticId, crate::Static), (hir_def::ConstId, crate::Const), (hir_def::FunctionId, crate::Function), @@ -113,7 +112,6 @@ impl From for ModuleDef { ModuleDefId::ConstId(it) => ModuleDef::Const(it.into()), ModuleDefId::StaticId(it) => ModuleDef::Static(it.into()), ModuleDefId::TraitId(it) => ModuleDef::Trait(it.into()), - ModuleDefId::TraitAliasId(it) => ModuleDef::TraitAlias(it.into()), ModuleDefId::TypeAliasId(it) => ModuleDef::TypeAlias(it.into()), ModuleDefId::BuiltinType(it) => ModuleDef::BuiltinType(it.into()), ModuleDefId::MacroId(it) => ModuleDef::Macro(it.into()), @@ -131,7 +129,6 @@ impl From for ModuleDefId { ModuleDef::Const(it) => ModuleDefId::ConstId(it.into()), ModuleDef::Static(it) => ModuleDefId::StaticId(it.into()), ModuleDef::Trait(it) => ModuleDefId::TraitId(it.into()), - ModuleDef::TraitAlias(it) => ModuleDefId::TraitAliasId(it.into()), ModuleDef::TypeAlias(it) => ModuleDefId::TypeAliasId(it.into()), ModuleDef::BuiltinType(it) => ModuleDefId::BuiltinType(it.into()), ModuleDef::Macro(it) => ModuleDefId::MacroId(it.into()), @@ -177,7 +174,6 @@ impl From for GenericDefId { GenericDef::Function(it) => GenericDefId::FunctionId(it.id), GenericDef::Adt(it) => GenericDefId::AdtId(it.into()), GenericDef::Trait(it) => GenericDefId::TraitId(it.id), - GenericDef::TraitAlias(it) => GenericDefId::TraitAliasId(it.id), GenericDef::TypeAlias(it) => GenericDefId::TypeAliasId(it.id), GenericDef::Impl(it) => GenericDefId::ImplId(it.id), GenericDef::Const(it) => GenericDefId::ConstId(it.id), @@ -192,7 +188,6 @@ impl From for GenericDef { GenericDefId::FunctionId(it) => GenericDef::Function(it.into()), GenericDefId::AdtId(it) => GenericDef::Adt(it.into()), GenericDefId::TraitId(it) => GenericDef::Trait(it.into()), - GenericDefId::TraitAliasId(it) => GenericDef::TraitAlias(it.into()), GenericDefId::TypeAliasId(it) => GenericDef::TypeAlias(it.into()), GenericDefId::ImplId(it) => GenericDef::Impl(it.into()), GenericDefId::ConstId(it) => GenericDef::Const(it.into()), diff --git a/src/tools/rust-analyzer/crates/hir/src/has_source.rs b/src/tools/rust-analyzer/crates/hir/src/has_source.rs index 4767d4792e71..ae82275e3873 100644 --- a/src/tools/rust-analyzer/crates/hir/src/has_source.rs +++ b/src/tools/rust-analyzer/crates/hir/src/has_source.rs @@ -14,8 +14,7 @@ use tt::TextRange; use crate::{ Adt, Callee, Const, Enum, ExternCrateDecl, Field, FieldSource, Function, Impl, InlineAsmOperand, Label, LifetimeParam, LocalSource, Macro, Module, Param, SelfParam, Static, - Struct, Trait, TraitAlias, TypeAlias, TypeOrConstParam, Union, Variant, VariantDef, - db::HirDatabase, + Struct, Trait, TypeAlias, TypeOrConstParam, Union, Variant, VariantDef, db::HirDatabase, }; pub trait HasSource { @@ -168,12 +167,6 @@ impl HasSource for Trait { Some(self.id.lookup(db).source(db)) } } -impl HasSource for TraitAlias { - type Ast = ast::TraitAlias; - fn source(self, db: &dyn HirDatabase) -> Option> { - Some(self.id.lookup(db).source(db)) - } -} impl HasSource for TypeAlias { type Ast = ast::TypeAlias; fn source(self, db: &dyn HirDatabase) -> Option> { @@ -202,7 +195,7 @@ impl HasSource for Impl { } impl HasSource for TypeOrConstParam { - type Ast = Either; + type Ast = Either; fn source(self, db: &dyn HirDatabase) -> Option> { let child_source = self.id.parent.child_source(db); child_source.map(|it| it.get(self.id.local_id).cloned()).transpose() diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 475906ae51a1..4eb50c1c31c5 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -52,7 +52,7 @@ use hir_def::{ CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId, ExternCrateId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, ItemContainerId, LifetimeParamId, LocalFieldId, Lookup, MacroExpander, MacroId, ModuleId, StaticId, StructId, SyntheticSyntax, - TraitAliasId, TupleId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, + TupleId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, expr_store::{ExpressionStoreDiagnostics, ExpressionStoreSourceMap}, hir::{ BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, LabelId, Pat, @@ -333,7 +333,6 @@ pub enum ModuleDef { Const(Const), Static(Static), Trait(Trait), - TraitAlias(TraitAlias), TypeAlias(TypeAlias), BuiltinType(BuiltinType), Macro(Macro), @@ -346,7 +345,6 @@ impl_from!( Const, Static, Trait, - TraitAlias, TypeAlias, BuiltinType, Macro @@ -373,7 +371,6 @@ impl ModuleDef { ModuleDef::Const(it) => Some(it.module(db)), ModuleDef::Static(it) => Some(it.module(db)), ModuleDef::Trait(it) => Some(it.module(db)), - ModuleDef::TraitAlias(it) => Some(it.module(db)), ModuleDef::TypeAlias(it) => Some(it.module(db)), ModuleDef::Macro(it) => Some(it.module(db)), ModuleDef::BuiltinType(_) => None, @@ -402,7 +399,6 @@ impl ModuleDef { ModuleDef::Const(it) => it.name(db)?, ModuleDef::Adt(it) => it.name(db), ModuleDef::Trait(it) => it.name(db), - ModuleDef::TraitAlias(it) => it.name(db), ModuleDef::Function(it) => it.name(db), ModuleDef::Variant(it) => it.name(db), ModuleDef::TypeAlias(it) => it.name(db), @@ -425,7 +421,6 @@ impl ModuleDef { Adt::Union(it) => it.id.into(), }, ModuleDef::Trait(it) => it.id.into(), - ModuleDef::TraitAlias(it) => it.id.into(), ModuleDef::Function(it) => it.id.into(), ModuleDef::TypeAlias(it) => it.id.into(), ModuleDef::Module(it) => it.id.into(), @@ -465,7 +460,6 @@ impl ModuleDef { ModuleDef::Module(_) | ModuleDef::Adt(_) | ModuleDef::Trait(_) - | ModuleDef::TraitAlias(_) | ModuleDef::TypeAlias(_) | ModuleDef::Macro(_) | ModuleDef::BuiltinType(_) => None, @@ -478,7 +472,6 @@ impl ModuleDef { ModuleDef::Function(it) => Some(it.into()), ModuleDef::Adt(it) => Some(it.into()), ModuleDef::Trait(it) => Some(it.into()), - ModuleDef::TraitAlias(it) => Some(it.into()), ModuleDef::TypeAlias(it) => Some(it.into()), ModuleDef::Module(_) | ModuleDef::Variant(_) @@ -498,7 +491,6 @@ impl ModuleDef { ModuleDef::Const(it) => it.attrs(db), ModuleDef::Static(it) => it.attrs(db), ModuleDef::Trait(it) => it.attrs(db), - ModuleDef::TraitAlias(it) => it.attrs(db), ModuleDef::TypeAlias(it) => it.attrs(db), ModuleDef::Macro(it) => it.attrs(db), ModuleDef::BuiltinType(_) => return None, @@ -524,7 +516,6 @@ impl HasVisibility for ModuleDef { ModuleDef::Const(it) => it.visibility(db), ModuleDef::Static(it) => it.visibility(db), ModuleDef::Trait(it) => it.visibility(db), - ModuleDef::TraitAlias(it) => it.visibility(db), ModuleDef::TypeAlias(it) => it.visibility(db), ModuleDef::Variant(it) => it.visibility(db), ModuleDef::Macro(it) => it.visibility(db), @@ -3045,29 +3036,6 @@ impl HasVisibility for Trait { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct TraitAlias { - pub(crate) id: TraitAliasId, -} - -impl TraitAlias { - pub fn module(self, db: &dyn HirDatabase) -> Module { - Module { id: self.id.lookup(db).container } - } - - pub fn name(self, db: &dyn HirDatabase) -> Name { - db.trait_alias_signature(self.id).name.clone() - } -} - -impl HasVisibility for TraitAlias { - fn visibility(&self, db: &dyn HirDatabase) -> Visibility { - let loc = self.id.lookup(db); - let source = loc.source(db); - visibility_from_ast(db, self.id, source.map(|src| src.visibility())) - } -} - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct TypeAlias { pub(crate) id: TypeAliasId, @@ -3690,7 +3658,6 @@ pub enum GenericDef { Function(Function), Adt(Adt), Trait(Trait), - TraitAlias(TraitAlias), TypeAlias(TypeAlias), Impl(Impl), // consts can have type parameters from their parents (i.e. associated consts of traits) @@ -3701,7 +3668,6 @@ impl_from!( Function, Adt(Struct, Enum, Union), Trait, - TraitAlias, TypeAlias, Impl, Const, @@ -3751,7 +3717,6 @@ impl GenericDef { GenericDef::Function(it) => it.id.into(), GenericDef::Adt(it) => it.into(), GenericDef::Trait(it) => it.id.into(), - GenericDef::TraitAlias(it) => it.id.into(), GenericDef::TypeAlias(it) => it.id.into(), GenericDef::Impl(it) => it.id.into(), GenericDef::Const(it) => it.id.into(), @@ -3776,7 +3741,6 @@ impl GenericDef { GenericDefId::FunctionId(it) => db.function_signature_with_source_map(it).1, GenericDefId::ImplId(it) => db.impl_signature_with_source_map(it).1, GenericDefId::StaticId(_) => return, - GenericDefId::TraitAliasId(it) => db.trait_alias_signature_with_source_map(it).1, GenericDefId::TraitId(it) => db.trait_signature_with_source_map(it).1, GenericDefId::TypeAliasId(it) => db.type_alias_signature_with_source_map(it).1, }; @@ -3813,7 +3777,6 @@ impl GenericDef { GenericDef::Adt(Adt::Enum(_)) => "enum", GenericDef::Adt(Adt::Union(_)) => "union", GenericDef::Trait(_) => "trait", - GenericDef::TraitAlias(_) => "trait alias", GenericDef::TypeAlias(_) => "type alias", GenericDef::Impl(_) => "impl", GenericDef::Const(_) => "constant", @@ -6401,12 +6364,6 @@ impl HasCrate for Trait { } } -impl HasCrate for TraitAlias { - fn krate(&self, db: &dyn HirDatabase) -> Crate { - self.module(db).krate() - } -} - impl HasCrate for Static { fn krate(&self, db: &dyn HirDatabase) -> Crate { self.module(db).krate() @@ -6500,12 +6457,6 @@ impl HasContainer for Trait { } } -impl HasContainer for TraitAlias { - fn container(&self, db: &dyn HirDatabase) -> ItemContainer { - ItemContainer::Module(Module { id: self.id.lookup(db).container }) - } -} - impl HasContainer for ExternBlock { fn container(&self, db: &dyn HirDatabase) -> ItemContainer { ItemContainer::Module(Module { id: self.id.lookup(db).container }) diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 05f06f97fdc2..b43165fd8ad7 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -46,8 +46,8 @@ use crate::{ Adjust, Adjustment, Adt, AutoBorrow, BindingMode, BuiltinAttr, Callable, Const, ConstParam, Crate, DefWithBody, DeriveHelper, Enum, Field, Function, GenericSubstitution, HasSource, Impl, InFile, InlineAsmOperand, ItemInNs, Label, LifetimeParam, Local, Macro, Module, ModuleDef, - Name, OverloadedDeref, ScopeDef, Static, Struct, ToolModule, Trait, TraitAlias, TupleField, - Type, TypeAlias, TypeParam, Union, Variant, VariantDef, + Name, OverloadedDeref, ScopeDef, Static, Struct, ToolModule, Trait, TupleField, Type, + TypeAlias, TypeParam, Union, Variant, VariantDef, db::HirDatabase, semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, source_analyzer::{SourceAnalyzer, name_hygiene, resolve_hir_path}, @@ -85,8 +85,7 @@ impl PathResolution { | ModuleDef::Function(_) | ModuleDef::Module(_) | ModuleDef::Static(_) - | ModuleDef::Trait(_) - | ModuleDef::TraitAlias(_), + | ModuleDef::Trait(_), ) => None, PathResolution::Def(ModuleDef::TypeAlias(alias)) => { Some(TypeNs::TypeAliasId((*alias).into())) @@ -343,10 +342,6 @@ impl Semantics<'_, DB> { self.imp.to_def(s) } - pub fn to_trait_alias_def(&self, t: &ast::TraitAlias) -> Option { - self.imp.to_def(t) - } - pub fn to_trait_def(&self, t: &ast::Trait) -> Option { self.imp.to_def(t) } @@ -2138,7 +2133,6 @@ to_def_impls![ (crate::Enum, ast::Enum, enum_to_def), (crate::Union, ast::Union, union_to_def), (crate::Trait, ast::Trait, trait_to_def), - (crate::TraitAlias, ast::TraitAlias, trait_alias_to_def), (crate::Impl, ast::Impl, impl_to_def), (crate::TypeAlias, ast::TypeAlias, type_alias_to_def), (crate::Const, ast::Const, const_to_def), diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs index e7db93d375d3..5019a5987e51 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs @@ -154,9 +154,6 @@ impl ChildBySource for ItemScope { } ModuleDefId::StaticId(id) => insert_item_loc(db, map, file_id, id, keys::STATIC), ModuleDefId::TraitId(id) => insert_item_loc(db, map, file_id, id, keys::TRAIT), - ModuleDefId::TraitAliasId(id) => { - insert_item_loc(db, map, file_id, id, keys::TRAIT_ALIAS) - } ModuleDefId::AdtId(adt) => match adt { AdtId::StructId(id) => insert_item_loc(db, map, file_id, id, keys::STRUCT), AdtId::UnionId(id) => insert_item_loc(db, map, file_id, id, keys::UNION), diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs index 71ee0f693896..44df4d8fc8c8 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs @@ -89,8 +89,8 @@ use either::Either; use hir_def::{ AdtId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId, ExternCrateId, FieldId, FunctionId, GenericDefId, GenericParamId, ImplId, LifetimeParamId, - Lookup, MacroId, ModuleId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, TypeParamId, - UnionId, UseId, VariantId, + Lookup, MacroId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId, + UseId, VariantId, dyn_map::{ DynMap, keys::{self, Key}, @@ -252,12 +252,6 @@ impl SourceToDefCtx<'_, '_> { pub(super) fn trait_to_def(&mut self, src: InFile<&ast::Trait>) -> Option { self.to_def(src, keys::TRAIT) } - pub(super) fn trait_alias_to_def( - &mut self, - src: InFile<&ast::TraitAlias>, - ) -> Option { - self.to_def(src, keys::TRAIT_ALIAS) - } pub(super) fn impl_to_def(&mut self, src: InFile<&ast::Impl>) -> Option { self.to_def(src, keys::IMPL) } @@ -555,9 +549,6 @@ impl SourceToDefCtx<'_, '_> { } ast::Item::Enum(it) => this.enum_to_def(InFile::new(file_id, it)).map(Into::into), ast::Item::Trait(it) => this.trait_to_def(InFile::new(file_id, it)).map(Into::into), - ast::Item::TraitAlias(it) => { - this.trait_alias_to_def(InFile::new(file_id, it)).map(Into::into) - } ast::Item::TypeAlias(it) => { this.type_alias_to_def(InFile::new(file_id, it)).map(Into::into) } @@ -636,9 +627,6 @@ impl SourceToDefCtx<'_, '_> { ast::Item::TypeAlias(it) => ChildContainer::GenericDefId( self.type_alias_to_def(container.with_value(it))?.into(), ), - ast::Item::TraitAlias(it) => ChildContainer::GenericDefId( - self.trait_alias_to_def(container.with_value(it))?.into(), - ), ast::Item::Struct(it) => { let def = self.struct_to_def(container.with_value(it))?; let is_in_body = it.field_list().is_some_and(|it| { diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index d25fb1d8cdb7..126392af4619 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -10,7 +10,7 @@ use std::iter::{self, once}; use crate::{ Adt, AssocItem, BindingMode, BuiltinAttr, BuiltinType, Callable, Const, DeriveHelper, Field, Function, GenericSubstitution, Local, Macro, ModuleDef, Static, Struct, ToolModule, Trait, - TraitAlias, TupleField, Type, TypeAlias, Variant, + TupleField, Type, TypeAlias, Variant, db::HirDatabase, semantics::{PathResolution, PathResolutionPerNs}, }; @@ -1587,7 +1587,6 @@ fn resolve_hir_path_( TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()), TypeNs::BuiltinType(it) => PathResolution::Def(BuiltinType::from(it).into()), TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()), - TypeNs::TraitAliasId(it) => PathResolution::Def(TraitAlias::from(it).into()), TypeNs::ModuleId(it) => PathResolution::Def(ModuleDef::Module(it.into())), }; match unresolved { @@ -1737,7 +1736,6 @@ fn resolve_hir_path_qualifier( TypeNs::TypeAliasId(it) => PathResolution::Def(TypeAlias::from(it).into()), TypeNs::BuiltinType(it) => PathResolution::Def(BuiltinType::from(it).into()), TypeNs::TraitId(it) => PathResolution::Def(Trait::from(it).into()), - TypeNs::TraitAliasId(it) => PathResolution::Def(TraitAlias::from(it).into()), TypeNs::ModuleId(it) => PathResolution::Def(ModuleDef::Module(it.into())), }; match unresolved { diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs index dca10193e29b..d8c624e5c689 100644 --- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs +++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs @@ -148,9 +148,6 @@ impl<'a> SymbolCollector<'a> { let trait_do_not_complete = this.push_decl(id, name, false, None); this.collect_from_trait(id, trait_do_not_complete); } - ModuleDefId::TraitAliasId(id) => { - this.push_decl(id, name, false, None); - } ModuleDefId::TypeAliasId(id) => { this.push_decl(id, name, false, None); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs index e783b8693456..dad19bfb8a2c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs @@ -613,7 +613,6 @@ impl Module { | Definition::Const(_) | Definition::Static(_) | Definition::Trait(_) - | Definition::TraitAlias(_) | Definition::TypeAlias(_) ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs index 3badc17d01af..55de537debf3 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs @@ -136,10 +136,6 @@ fn target_data_for_def( target_name = Some(t.name(db)); offset_target_and_file_id(db, t)? } - hir::ModuleDef::TraitAlias(t) => { - target_name = Some(t.name(db)); - offset_target_and_file_id(db, t)? - } hir::ModuleDef::TypeAlias(t) => { target_name = Some(t.name(db)); offset_target_and_file_id(db, t)? diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs index a9df6f6fc366..e5425abab097 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_bounds.rs @@ -55,7 +55,6 @@ pub(crate) fn move_bounds_to_where_clause( match parent { ast::Fn(it) => it.get_or_create_where_clause(), ast::Trait(it) => it.get_or_create_where_clause(), - ast::TraitAlias(it) => it.get_or_create_where_clause(), ast::Impl(it) => it.get_or_create_where_clause(), ast::Enum(it) => it.get_or_create_where_clause(), ast::Struct(it) => it.get_or_create_where_clause(), diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs index 7c38c7d8ce44..fc27cbd65a1e 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs @@ -37,9 +37,7 @@ pub(crate) fn complete_type_path( true } // Type things are fine - ScopeDef::ModuleDef( - BuiltinType(_) | Adt(_) | Module(_) | Trait(_) | TraitAlias(_) | TypeAlias(_), - ) + ScopeDef::ModuleDef(BuiltinType(_) | Adt(_) | Module(_) | Trait(_) | TypeAlias(_)) | ScopeDef::AdtSelfType(_) | ScopeDef::Unknown | ScopeDef::GenericParam(TypeParam(_)) => location.complete_types(), diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index cfd7f80d40b3..28c00cde126c 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -525,7 +525,6 @@ impl CompletionContext<'_> { hir::ModuleDef::Const(it) => self.is_visible(it), hir::ModuleDef::Static(it) => self.is_visible(it), hir::ModuleDef::Trait(it) => self.is_visible(it), - hir::ModuleDef::TraitAlias(it) => self.is_visible(it), hir::ModuleDef::TypeAlias(it) => self.is_visible(it), hir::ModuleDef::Macro(it) => self.is_visible(it), hir::ModuleDef::BuiltinType(_) => Visible::Yes, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 9d246017131a..23db63c1a95f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -1037,9 +1037,6 @@ fn classify_name_ref<'db>( sema.source(trait_)?.value.generic_param_list() } } - hir::ModuleDef::TraitAlias(trait_) => { - sema.source(trait_)?.value.generic_param_list() - } hir::ModuleDef::TypeAlias(ty_) => { sema.source(ty_)?.value.generic_param_list() } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index f27cd0781665..5fb9dc93c93d 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -399,7 +399,6 @@ impl CompletionItemKind { SymbolKind::Struct => "st", SymbolKind::ToolModule => "tm", SymbolKind::Trait => "tt", - SymbolKind::TraitAlias => "tr", SymbolKind::TypeAlias => "ta", SymbolKind::TypeParam => "tp", SymbolKind::Union => "un", diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index 3d7a4067c2cd..7d23f9d14c66 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -486,10 +486,7 @@ fn render_resolution_path( | ScopeDef::Label(_) | ScopeDef::Unknown | ScopeDef::ModuleDef( - ModuleDef::Trait(_) - | ModuleDef::TraitAlias(_) - | ModuleDef::Module(_) - | ModuleDef::TypeAlias(_), + ModuleDef::Trait(_) | ModuleDef::Module(_) | ModuleDef::TypeAlias(_), ) => (), }; @@ -542,9 +539,6 @@ fn res_to_kind(resolution: ScopeDef) -> CompletionItemKind { ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::SymbolKind(SymbolKind::Const), ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::SymbolKind(SymbolKind::Static), ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::SymbolKind(SymbolKind::Trait), - ScopeDef::ModuleDef(TraitAlias(..)) => { - CompletionItemKind::SymbolKind(SymbolKind::TraitAlias) - } ScopeDef::ModuleDef(TypeAlias(..)) => CompletionItemKind::SymbolKind(SymbolKind::TypeAlias), ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType, ScopeDef::GenericParam(param) => CompletionItemKind::SymbolKind(match param { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs b/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs index 9edfc113f764..4fb7d142ed5f 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/active_parameter.rs @@ -125,7 +125,6 @@ pub fn generic_def_for_node( hir::PathResolution::Def(hir::ModuleDef::Adt(it)) => it.into(), hir::PathResolution::Def(hir::ModuleDef::Function(it)) => it.into(), hir::PathResolution::Def(hir::ModuleDef::Trait(it)) => it.into(), - hir::PathResolution::Def(hir::ModuleDef::TraitAlias(it)) => it.into(), hir::PathResolution::Def(hir::ModuleDef::TypeAlias(it)) => it.into(), hir::PathResolution::Def(hir::ModuleDef::Variant(it)) => { variant = Some(it); diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs index 2a4fcf6a2e5f..61a21ccf2f77 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs @@ -16,7 +16,7 @@ use hir::{ ExternCrateDecl, Field, Function, GenericDef, GenericParam, GenericSubstitution, HasContainer, HasVisibility, HirDisplay, Impl, InlineAsmOperand, ItemContainer, Label, Local, Macro, Module, ModuleDef, Name, PathResolution, Semantics, Static, StaticLifetime, Struct, ToolModule, Trait, - TraitAlias, TupleField, TypeAlias, Variant, VariantDef, Visibility, + TupleField, TypeAlias, Variant, VariantDef, Visibility, }; use span::Edition; use stdx::{format_to, impl_from}; @@ -40,7 +40,6 @@ pub enum Definition { Const(Const), Static(Static), Trait(Trait), - TraitAlias(TraitAlias), TypeAlias(TypeAlias), SelfType(Impl), GenericParam(GenericParam), @@ -83,7 +82,6 @@ impl Definition { Definition::Const(it) => it.module(db), Definition::Static(it) => it.module(db), Definition::Trait(it) => it.module(db), - Definition::TraitAlias(it) => it.module(db), Definition::TypeAlias(it) => it.module(db), Definition::Variant(it) => it.module(db), Definition::SelfType(it) => it.module(db), @@ -122,7 +120,6 @@ impl Definition { Definition::Const(it) => container_to_definition(it.container(db)), Definition::Static(it) => container_to_definition(it.container(db)), Definition::Trait(it) => container_to_definition(it.container(db)), - Definition::TraitAlias(it) => container_to_definition(it.container(db)), Definition::TypeAlias(it) => container_to_definition(it.container(db)), Definition::Variant(it) => Some(Adt::Enum(it.parent_enum(db)).into()), Definition::SelfType(it) => Some(it.module(db).into()), @@ -151,7 +148,6 @@ impl Definition { Definition::Const(it) => it.visibility(db), Definition::Static(it) => it.visibility(db), Definition::Trait(it) => it.visibility(db), - Definition::TraitAlias(it) => it.visibility(db), Definition::TypeAlias(it) => it.visibility(db), Definition::Variant(it) => it.visibility(db), Definition::ExternCrateDecl(it) => it.visibility(db), @@ -185,7 +181,6 @@ impl Definition { Definition::Const(it) => it.name(db)?, Definition::Static(it) => it.name(db), Definition::Trait(it) => it.name(db), - Definition::TraitAlias(it) => it.name(db), Definition::TypeAlias(it) => it.name(db), Definition::BuiltinType(it) => it.name(), Definition::TupleField(it) => it.name(), @@ -230,7 +225,6 @@ impl Definition { Definition::Const(it) => it.docs_with_rangemap(db), Definition::Static(it) => it.docs_with_rangemap(db), Definition::Trait(it) => it.docs_with_rangemap(db), - Definition::TraitAlias(it) => it.docs_with_rangemap(db), Definition::TypeAlias(it) => { it.docs_with_rangemap(db).or_else(|| { // docs are missing, try to fall back to the docs of the aliased item. @@ -321,7 +315,6 @@ impl Definition { Definition::Const(it) => it.display(db, display_target).to_string(), Definition::Static(it) => it.display(db, display_target).to_string(), Definition::Trait(it) => it.display(db, display_target).to_string(), - Definition::TraitAlias(it) => it.display(db, display_target).to_string(), Definition::TypeAlias(it) => it.display(db, display_target).to_string(), Definition::BuiltinType(it) => { it.name().display(db, display_target.edition).to_string() @@ -589,7 +582,6 @@ impl<'db> NameClass<'db> { ast::Item::Module(it) => Definition::Module(sema.to_def(&it)?), ast::Item::Static(it) => Definition::Static(sema.to_def(&it)?), ast::Item::Trait(it) => Definition::Trait(sema.to_def(&it)?), - ast::Item::TraitAlias(it) => Definition::TraitAlias(sema.to_def(&it)?), ast::Item::TypeAlias(it) => Definition::TypeAlias(sema.to_def(&it)?), ast::Item::Enum(it) => Definition::Adt(hir::Adt::Enum(sema.to_def(&it)?)), ast::Item::Struct(it) => Definition::Adt(hir::Adt::Struct(sema.to_def(&it)?)), @@ -895,7 +887,7 @@ impl<'db> NameRefClass<'db> { } impl_from!( - Field, Module, Function, Adt, Variant, Const, Static, Trait, TraitAlias, TypeAlias, BuiltinType, Local, + Field, Module, Function, Adt, Variant, Const, Static, Trait, TypeAlias, BuiltinType, Local, GenericParam, Label, Macro, ExternCrateDecl for Definition ); @@ -975,7 +967,6 @@ impl From for Definition { ModuleDef::Const(it) => Definition::Const(it), ModuleDef::Static(it) => Definition::Static(it), ModuleDef::Trait(it) => Definition::Trait(it), - ModuleDef::TraitAlias(it) => Definition::TraitAlias(it), ModuleDef::TypeAlias(it) => Definition::TypeAlias(it), ModuleDef::Macro(it) => Definition::Macro(it), ModuleDef::BuiltinType(it) => Definition::BuiltinType(it), @@ -1017,7 +1008,6 @@ impl From for Definition { GenericDef::Function(it) => it.into(), GenericDef::Adt(it) => it.into(), GenericDef::Trait(it) => it.into(), - GenericDef::TraitAlias(it) => it.into(), GenericDef::TypeAlias(it) => it.into(), GenericDef::Impl(it) => it.into(), GenericDef::Const(it) => it.into(), @@ -1033,7 +1023,6 @@ impl TryFrom for GenericDef { Definition::Function(it) => Ok(it.into()), Definition::Adt(it) => Ok(it.into()), Definition::Trait(it) => Ok(it.into()), - Definition::TraitAlias(it) => Ok(it.into()), Definition::TypeAlias(it) => Ok(it.into()), Definition::SelfType(it) => Ok(it.into()), Definition::Const(it) => Ok(it.into()), diff --git a/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs b/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs index 30c355f8b3f9..cab19aadfd01 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs @@ -195,8 +195,7 @@ macro_rules! impl_has_docs { } impl_has_docs![ - Variant, Field, Static, Const, Trait, TraitAlias, TypeAlias, Macro, Function, Adt, Module, - Impl, Crate, + Variant, Field, Static, Const, Trait, TypeAlias, Macro, Function, Adt, Module, Impl, Crate, ]; macro_rules! impl_has_docs_enum { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs index 9f35988924b9..1012f993344f 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs @@ -475,8 +475,6 @@ fn validate_resolvable( } // FIXME ModuleDef::Trait(_) => return None, - // FIXME - ModuleDef::TraitAlias(_) => return None, ModuleDef::TypeAlias(alias) => alias.ty(db), ModuleDef::BuiltinType(builtin) => builtin.ty(db), ModuleDef::Adt(adt) => adt.ty(db), diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index 49f7f63a04a4..1f074de4cd7d 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -273,7 +273,6 @@ pub enum SymbolKind { Struct, ToolModule, Trait, - TraitAlias, TypeAlias, TypeParam, Union, @@ -306,7 +305,6 @@ impl From for SymbolKind { hir::ModuleDef::Adt(hir::Adt::Enum(..)) => SymbolKind::Enum, hir::ModuleDef::Adt(hir::Adt::Union(..)) => SymbolKind::Union, hir::ModuleDef::Trait(..) => SymbolKind::Trait, - hir::ModuleDef::TraitAlias(..) => SymbolKind::TraitAlias, hir::ModuleDef::TypeAlias(..) => SymbolKind::TypeAlias, hir::ModuleDef::BuiltinType(..) => SymbolKind::TypeAlias, } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs index 424b27a398b2..a8800c142a22 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs @@ -163,7 +163,6 @@ impl Definition { Definition::Const(it) => name_range(it, sema).and_then(syn_ctx_is_root), Definition::Static(it) => name_range(it, sema).and_then(syn_ctx_is_root), Definition::Trait(it) => name_range(it, sema).and_then(syn_ctx_is_root), - Definition::TraitAlias(it) => name_range(it, sema).and_then(syn_ctx_is_root), Definition::TypeAlias(it) => name_range(it, sema).and_then(syn_ctx_is_root), Definition::Local(it) => { name_range(it.primary_source(sema.db), sema).and_then(syn_ctx_is_root) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs index abd4dc8300b3..f1d076e874d5 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -352,7 +352,6 @@ impl Definition { hir::GenericDef::Function(it) => it.source(db).map(|src| src.syntax().cloned()), hir::GenericDef::Adt(it) => it.source(db).map(|src| src.syntax().cloned()), hir::GenericDef::Trait(it) => it.source(db).map(|src| src.syntax().cloned()), - hir::GenericDef::TraitAlias(it) => it.source(db).map(|src| src.syntax().cloned()), hir::GenericDef::TypeAlias(it) => it.source(db).map(|src| src.syntax().cloned()), hir::GenericDef::Impl(it) => it.source(db).map(|src| src.syntax().cloned()), hir::GenericDef::Const(it) => it.source(db).map(|src| src.syntax().cloned()), diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs index 9c4e6f5cbf82..78ade30c7e3e 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs @@ -357,7 +357,6 @@ impl Query { hir::ModuleDef::Adt(..) | hir::ModuleDef::TypeAlias(..) | hir::ModuleDef::BuiltinType(..) - | hir::ModuleDef::TraitAlias(..) | hir::ModuleDef::Trait(..) ); if non_type_for_type_only_query || !self.matches_assoc_mode(symbol.is_assoc) { diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs index d47cc6507938..c197d559aa89 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -225,7 +225,6 @@ pub(crate) fn resolve_doc_path_for_def( Definition::Const(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), Definition::Static(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), Definition::Trait(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), - Definition::TraitAlias(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), Definition::TypeAlias(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), Definition::Macro(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), Definition::Field(it) => it.resolve_doc_path(db, link, ns, is_inner_doc), @@ -671,11 +670,9 @@ fn filename_and_frag_for_def( None => String::from("index.html"), }, Definition::Trait(t) => { + // FIXME(trait-alias): url should be traitalias. for aliases format!("trait.{}.html", t.name(db).as_str()) } - Definition::TraitAlias(t) => { - format!("traitalias.{}.html", t.name(db).as_str()) - } Definition::TypeAlias(t) => { format!("type.{}.html", t.name(db).as_str()) } diff --git a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs index ac15af0334fc..d5c6eaca9744 100644 --- a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs +++ b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs @@ -162,7 +162,6 @@ fn structure_node(node: &SyntaxNode, config: &FileStructureConfig) -> Option decl(it, StructureNodeKind::SymbolKind(SymbolKind::Enum)), ast::Variant(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Variant)), ast::Trait(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Trait)), - ast::TraitAlias(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::TraitAlias)), ast::Module(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Module)), ast::Macro(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Macro)), ast::TypeAlias(it) => decl_with_type_ref(&it, it.ty(), StructureNodeKind::SymbolKind(SymbolKind::TypeAlias)), @@ -553,7 +552,7 @@ fn let_statements() { navigation_range: 251..256, node_range: 245..262, kind: SymbolKind( - TraitAlias, + Trait, ), detail: None, deprecated: false, diff --git a/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs b/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs index ac64413effeb..3969490e8dcf 100644 --- a/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs +++ b/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs @@ -29,7 +29,6 @@ pub enum FoldKind { Consts, Statics, TypeAliases, - TraitAliases, ExternCrates, // endregion: item runs } @@ -147,11 +146,6 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec { res.push(Fold { range, kind: FoldKind::TypeAliases }) } }, - ast::TraitAlias(alias) => { - if let Some(range) = contiguous_range_for_item_group(alias, &mut visited_nodes) { - res.push(Fold { range, kind: FoldKind::TraitAliases }) - } - }, ast::ExternCrate(extern_crate) => { if let Some(range) = contiguous_range_for_item_group(extern_crate, &mut visited_nodes) { res.push(Fold { range, kind: FoldKind::ExternCrates }) @@ -351,7 +345,6 @@ mod tests { FoldKind::ReturnType => "returntype", FoldKind::MatchArm => "matcharm", FoldKind::Function => "function", - FoldKind::TraitAliases => "traitaliases", FoldKind::ExternCrates => "externcrates", }; assert_eq!(kind, &attr.unwrap()); diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index 1c0272cdfacd..e4c7d42ffe3b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -401,7 +401,6 @@ fn definition_owner_name(db: &RootDatabase, def: Definition, edition: Edition) - Definition::GenericParam(generic_param) => match generic_param.parent() { hir::GenericDef::Adt(it) => Some(it.name(db)), hir::GenericDef::Trait(it) => Some(it.name(db)), - hir::GenericDef::TraitAlias(it) => Some(it.name(db)), hir::GenericDef::TypeAlias(it) => Some(it.name(db)), hir::GenericDef::Impl(i) => i.self_ty(db).as_adt().map(|adt| adt.name(db)), diff --git a/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs index 795c1f2ca3c0..f1aa03c8f267 100644 --- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs +++ b/src/tools/rust-analyzer/crates/ide/src/moniker.rs @@ -209,7 +209,6 @@ pub(crate) fn def_to_kind(db: &RootDatabase, def: Definition) -> SymbolInformati Definition::Const(..) => Constant, Definition::Static(..) => StaticVariable, Definition::Trait(..) => Trait, - Definition::TraitAlias(..) => Trait, Definition::TypeAlias(it) => { if it.as_assoc_item(db).is_some() { AssociatedType diff --git a/src/tools/rust-analyzer/crates/ide/src/move_item.rs b/src/tools/rust-analyzer/crates/ide/src/move_item.rs index f3bb3df1cd8d..b5d47c83a554 100644 --- a/src/tools/rust-analyzer/crates/ide/src/move_item.rs +++ b/src/tools/rust-analyzer/crates/ide/src/move_item.rs @@ -72,7 +72,6 @@ fn find_ancestors(item: SyntaxElement, direction: Direction, range: TextRange) - SyntaxKind::MACRO_CALL, SyntaxKind::TYPE_ALIAS, SyntaxKind::TRAIT, - SyntaxKind::TRAIT_ALIAS, SyntaxKind::IMPL, SyntaxKind::MACRO_DEF, SyntaxKind::STRUCT, diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index 7dc18141bdbc..61b4de0e0e39 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -226,9 +226,6 @@ impl TryToNav for FileSymbol { hir::ModuleDef::Trait(it) => { Some(it.display(db, display_target).to_string()) } - hir::ModuleDef::TraitAlias(it) => { - Some(it.display(db, display_target).to_string()) - } hir::ModuleDef::TypeAlias(it) => { Some(it.display(db, display_target).to_string()) } @@ -261,7 +258,6 @@ impl TryToNav for Definition { Definition::Const(it) => it.try_to_nav(db), Definition::Static(it) => it.try_to_nav(db), Definition::Trait(it) => it.try_to_nav(db), - Definition::TraitAlias(it) => it.try_to_nav(db), Definition::TypeAlias(it) => it.try_to_nav(db), Definition::ExternCrateDecl(it) => it.try_to_nav(db), Definition::InlineAsmOperand(it) => it.try_to_nav(db), @@ -287,7 +283,6 @@ impl TryToNav for hir::ModuleDef { hir::ModuleDef::Const(it) => it.try_to_nav(db), hir::ModuleDef::Static(it) => it.try_to_nav(db), hir::ModuleDef::Trait(it) => it.try_to_nav(db), - hir::ModuleDef::TraitAlias(it) => it.try_to_nav(db), hir::ModuleDef::TypeAlias(it) => it.try_to_nav(db), hir::ModuleDef::Macro(it) => it.try_to_nav(db), hir::ModuleDef::BuiltinType(_) => None, @@ -366,12 +361,6 @@ impl ToNavFromAst for hir::Trait { container_name(db, self, self.krate(db).edition(db)) } } -impl ToNavFromAst for hir::TraitAlias { - const KIND: SymbolKind = SymbolKind::TraitAlias; - fn container_name(self, db: &RootDatabase) -> Option { - container_name(db, self, self.krate(db).edition(db)) - } -} impl TryToNav for D where diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index 86b88a17c75f..dc509b385838 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -1783,7 +1783,7 @@ trait Bar$0 = Foo where Self: ; fn foo(_: impl Bar, _: &dyn Bar) {} "#, expect![[r#" - Bar TraitAlias FileId(0) 13..42 19..22 + Bar Trait FileId(0) 13..42 19..22 FileId(0) 53..56 FileId(0) 66..69 diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs index 83e5c5ab1dfe..77c84292cf3e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs +++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs @@ -493,7 +493,6 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option { Definition::Const(it) => it.attrs(db), Definition::Static(it) => it.attrs(db), Definition::Trait(it) => it.attrs(db), - Definition::TraitAlias(it) => it.attrs(db), Definition::TypeAlias(it) => it.attrs(db), Definition::Macro(it) => it.attrs(db), Definition::SelfType(it) => it.attrs(db), diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs index 2cccb9639f3e..86d02b4098bd 100644 --- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs +++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs @@ -339,10 +339,6 @@ fn signature_help_for_generics( res.doc = it.docs(db); format_to!(res.signature, "trait {}", it.name(db).display(db, edition)); } - hir::GenericDef::TraitAlias(it) => { - res.doc = it.docs(db); - format_to!(res.signature, "trait {}", it.name(db).display(db, edition)); - } hir::GenericDef::TypeAlias(it) => { res.doc = it.docs(db); format_to!(res.signature, "type {}", it.name(db).display(db, edition)); diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs index 8bde8fd97006..d73575fb9549 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs @@ -576,7 +576,6 @@ pub(super) fn highlight_def( h } Definition::Trait(_) => Highlight::new(HlTag::Symbol(SymbolKind::Trait)), - Definition::TraitAlias(_) => Highlight::new(HlTag::Symbol(SymbolKind::TraitAlias)), Definition::TypeAlias(type_) => { let mut h = Highlight::new(HlTag::Symbol(SymbolKind::TypeAlias)); @@ -780,7 +779,6 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight { MACRO_RULES => SymbolKind::Macro, CONST_PARAM => SymbolKind::ConstParam, SELF_PARAM => SymbolKind::SelfParam, - TRAIT_ALIAS => SymbolKind::TraitAlias, ASM_OPERAND_NAMED => SymbolKind::Local, _ => return default.into(), }; diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs index 7f5c2c1ec849..fb33307249a7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs @@ -311,7 +311,6 @@ fn module_def_to_hl_tag(def: Definition) -> HlTag { Definition::Const(_) => SymbolKind::Const, Definition::Static(_) => SymbolKind::Static, Definition::Trait(_) => SymbolKind::Trait, - Definition::TraitAlias(_) => SymbolKind::TraitAlias, Definition::TypeAlias(_) => SymbolKind::TypeAlias, Definition::BuiltinLifetime(_) => SymbolKind::LifetimeParam, Definition::BuiltinType(_) => return HlTag::BuiltinType, diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs index 3b5d1af0ac72..4b8762640c74 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs @@ -160,7 +160,6 @@ impl HlTag { SymbolKind::Struct => "struct", SymbolKind::ToolModule => "tool_module", SymbolKind::Trait => "trait", - SymbolKind::TraitAlias => "trait_alias", SymbolKind::TypeAlias => "type_alias", SymbolKind::TypeParam => "type_param", SymbolKind::Union => "union", diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index f7d798208037..47ee2ad1c0d7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html @@ -78,8 +78,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd } use foo::bar as baz; -trait Bar = Baz; -trait Foo = Bar; +trait Bar = Baz; +trait Foo = Bar; fn main() { let a = '\n'; diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items/traits.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items/traits.rs index 47f86ce8c6cc..c1b1a3fc8a94 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/items/traits.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items/traits.rs @@ -20,7 +20,7 @@ pub(super) fn trait_(p: &mut Parser<'_>, m: Marker) { // trait Z = where Self: T; generic_params::opt_where_clause(p); p.expect(T![;]); - m.complete(p, TRAIT_ALIAS); + m.complete(p, TRAIT); return; } diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs index 3a8041d2df9e..93e02a92abda 100644 --- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs +++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs @@ -284,7 +284,6 @@ pub enum SyntaxKind { STRUCT, TOKEN_TREE, TRAIT, - TRAIT_ALIAS, TRY_EXPR, TUPLE_EXPR, TUPLE_FIELD, @@ -457,7 +456,6 @@ impl SyntaxKind { | STRUCT | TOKEN_TREE | TRAIT - | TRAIT_ALIAS | TRY_EXPR | TUPLE_EXPR | TUPLE_FIELD diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_alias.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_alias.rast index c45f87089800..2ef66484ae48 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_alias.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_alias.rast @@ -1,5 +1,5 @@ SOURCE_FILE - TRAIT_ALIAS + TRAIT TRAIT_KW "trait" WHITESPACE " " NAME diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_alias_where_clause.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_alias_where_clause.rast index 8f678247731d..4443d9d14263 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_alias_where_clause.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/trait_alias_where_clause.rast @@ -1,5 +1,5 @@ SOURCE_FILE - TRAIT_ALIAS + TRAIT TRAIT_KW "trait" WHITESPACE " " NAME @@ -50,7 +50,7 @@ SOURCE_FILE IDENT "Copy" SEMICOLON ";" WHITESPACE "\n" - TRAIT_ALIAS + TRAIT TRAIT_KW "trait" WHITESPACE " " NAME diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs index 292be1d5315d..d51ddb86d197 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs @@ -61,7 +61,7 @@ pub(crate) fn symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind { SymbolKind::Struct => lsp_types::SymbolKind::STRUCT, SymbolKind::Enum => lsp_types::SymbolKind::ENUM, SymbolKind::Variant => lsp_types::SymbolKind::ENUM_MEMBER, - SymbolKind::Trait | SymbolKind::TraitAlias => lsp_types::SymbolKind::INTERFACE, + SymbolKind::Trait => lsp_types::SymbolKind::INTERFACE, SymbolKind::Macro | SymbolKind::ProcMacro | SymbolKind::BuiltinAttr @@ -156,7 +156,6 @@ pub(crate) fn completion_item_kind( SymbolKind::Static => lsp_types::CompletionItemKind::VALUE, SymbolKind::Struct => lsp_types::CompletionItemKind::STRUCT, SymbolKind::Trait => lsp_types::CompletionItemKind::INTERFACE, - SymbolKind::TraitAlias => lsp_types::CompletionItemKind::INTERFACE, SymbolKind::TypeAlias => lsp_types::CompletionItemKind::STRUCT, SymbolKind::TypeParam => lsp_types::CompletionItemKind::TYPE_PARAMETER, SymbolKind::Union => lsp_types::CompletionItemKind::STRUCT, @@ -817,7 +816,6 @@ fn semantic_token_type_and_modifiers( SymbolKind::Union => types::UNION, SymbolKind::TypeAlias => types::TYPE_ALIAS, SymbolKind::Trait => types::INTERFACE, - SymbolKind::TraitAlias => types::INTERFACE, SymbolKind::Macro => types::MACRO, SymbolKind::ProcMacro => types::PROC_MACRO, SymbolKind::BuiltinAttr => types::BUILTIN_ATTRIBUTE, @@ -909,7 +907,6 @@ pub(crate) fn folding_range( | FoldKind::WhereClause | FoldKind::ReturnType | FoldKind::Array - | FoldKind::TraitAliases | FoldKind::ExternCrates | FoldKind::MatchArm | FoldKind::Function => None, diff --git a/src/tools/rust-analyzer/crates/span/src/ast_id.rs b/src/tools/rust-analyzer/crates/span/src/ast_id.rs index a9288ecd6fa1..e803747998b5 100644 --- a/src/tools/rust-analyzer/crates/span/src/ast_id.rs +++ b/src/tools/rust-analyzer/crates/span/src/ast_id.rs @@ -485,8 +485,7 @@ register_has_name_ast_id! { MacroRules = name, Module = name, Static = name, - Trait = name, - TraitAlias = name + Trait = name } macro_rules! register_assoc_item_ast_id { diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram index 6d8a360d715b..d73d60c51f0c 100644 --- a/src/tools/rust-analyzer/crates/syntax/rust.ungram +++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram @@ -154,7 +154,6 @@ Item = | Static | Struct | Trait -| TraitAlias | TypeAlias | Union | Use @@ -306,11 +305,8 @@ Trait = Attr* Visibility? 'unsafe'? 'auto'? 'trait' Name GenericParamList? - (':' TypeBoundList?)? WhereClause? AssocItemList - -TraitAlias = - Attr* Visibility? - 'trait' Name GenericParamList? '=' TypeBoundList? WhereClause? ';' + (((':' TypeBoundList?)? WhereClause? AssocItemList) | + ('=' TypeBoundList? WhereClause? ';')) AssocItemList = '{' Attr* AssocItem* '}' diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast.rs b/src/tools/rust-analyzer/crates/syntax/src/ast.rs index a9aeeedb6542..19c1c5ebea33 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast.rs @@ -26,8 +26,7 @@ pub use self::{ generated::{nodes::*, tokens::*}, node_ext::{ AttrKind, FieldKind, Macro, NameLike, NameOrNameRef, PathSegmentKind, SelfParamKind, - SlicePatComponents, StructKind, TraitOrAlias, TypeBoundKind, TypeOrConstParam, - VisibilityKind, + SlicePatComponents, StructKind, TypeBoundKind, TypeOrConstParam, VisibilityKind, }, operators::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp}, token_ext::{CommentKind, CommentPlacement, CommentShape, IsString, QuoteOffsets, Radix}, diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs index 160f000387b3..1cd8146f6863 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs @@ -99,38 +99,10 @@ impl GenericParamsOwnerEdit for ast::Trait { fn get_or_create_where_clause(&self) -> ast::WhereClause { if self.where_clause().is_none() { - let position = match self.assoc_item_list() { - Some(items) => Position::before(items.syntax()), - None => Position::last_child_of(self.syntax()), - }; - create_where_clause(position); - } - self.where_clause().unwrap() - } -} - -impl GenericParamsOwnerEdit for ast::TraitAlias { - fn get_or_create_generic_param_list(&self) -> ast::GenericParamList { - match self.generic_param_list() { - Some(it) => it, - None => { - let position = if let Some(name) = self.name() { - Position::after(name.syntax) - } else if let Some(trait_token) = self.trait_token() { - Position::after(trait_token) - } else { - Position::last_child_of(self.syntax()) - }; - create_generic_param_list(position) - } - } - } - - fn get_or_create_where_clause(&self) -> ast::WhereClause { - if self.where_clause().is_none() { - let position = match self.semicolon_token() { - Some(tok) => Position::before(tok), - None => Position::last_child_of(self.syntax()), + let position = match (self.assoc_item_list(), self.semicolon_token()) { + (Some(items), _) => Position::before(items.syntax()), + (_, Some(tok)) => Position::before(tok), + (None, None) => Position::last_child_of(self.syntax()), }; create_where_clause(position); } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs index ceb2866ebcdf..d60196d492fc 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs @@ -1614,30 +1614,16 @@ impl Trait { #[inline] pub fn assoc_item_list(&self) -> Option { support::child(&self.syntax) } #[inline] + pub fn semicolon_token(&self) -> Option { support::token(&self.syntax, T![;]) } + #[inline] + pub fn eq_token(&self) -> Option { support::token(&self.syntax, T![=]) } + #[inline] pub fn auto_token(&self) -> Option { support::token(&self.syntax, T![auto]) } #[inline] pub fn trait_token(&self) -> Option { support::token(&self.syntax, T![trait]) } #[inline] pub fn unsafe_token(&self) -> Option { support::token(&self.syntax, T![unsafe]) } } -pub struct TraitAlias { - pub(crate) syntax: SyntaxNode, -} -impl ast::HasAttrs for TraitAlias {} -impl ast::HasDocComments for TraitAlias {} -impl ast::HasGenericParams for TraitAlias {} -impl ast::HasName for TraitAlias {} -impl ast::HasVisibility for TraitAlias {} -impl TraitAlias { - #[inline] - pub fn type_bound_list(&self) -> Option { support::child(&self.syntax) } - #[inline] - pub fn semicolon_token(&self) -> Option { support::token(&self.syntax, T![;]) } - #[inline] - pub fn eq_token(&self) -> Option { support::token(&self.syntax, T![=]) } - #[inline] - pub fn trait_token(&self) -> Option { support::token(&self.syntax, T![trait]) } -} pub struct TryExpr { pub(crate) syntax: SyntaxNode, } @@ -2107,7 +2093,6 @@ pub enum Item { Static(Static), Struct(Struct), Trait(Trait), - TraitAlias(TraitAlias), TypeAlias(TypeAlias), Union(Union), Use(Use), @@ -6801,42 +6786,6 @@ impl fmt::Debug for Trait { f.debug_struct("Trait").field("syntax", &self.syntax).finish() } } -impl AstNode for TraitAlias { - #[inline] - fn kind() -> SyntaxKind - where - Self: Sized, - { - TRAIT_ALIAS - } - #[inline] - fn can_cast(kind: SyntaxKind) -> bool { kind == TRAIT_ALIAS } - #[inline] - fn cast(syntax: SyntaxNode) -> Option { - if Self::can_cast(syntax.kind()) { - Some(Self { syntax }) - } else { - None - } - } - #[inline] - fn syntax(&self) -> &SyntaxNode { &self.syntax } -} -impl hash::Hash for TraitAlias { - fn hash(&self, state: &mut H) { self.syntax.hash(state); } -} -impl Eq for TraitAlias {} -impl PartialEq for TraitAlias { - fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax } -} -impl Clone for TraitAlias { - fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } } -} -impl fmt::Debug for TraitAlias { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("TraitAlias").field("syntax", &self.syntax).finish() - } -} impl AstNode for TryExpr { #[inline] fn kind() -> SyntaxKind @@ -8471,10 +8420,6 @@ impl From for Item { #[inline] fn from(node: Trait) -> Item { Item::Trait(node) } } -impl From for Item { - #[inline] - fn from(node: TraitAlias) -> Item { Item::TraitAlias(node) } -} impl From for Item { #[inline] fn from(node: TypeAlias) -> Item { Item::TypeAlias(node) } @@ -8506,7 +8451,6 @@ impl AstNode for Item { | STATIC | STRUCT | TRAIT - | TRAIT_ALIAS | TYPE_ALIAS | UNION | USE @@ -8529,7 +8473,6 @@ impl AstNode for Item { STATIC => Item::Static(Static { syntax }), STRUCT => Item::Struct(Struct { syntax }), TRAIT => Item::Trait(Trait { syntax }), - TRAIT_ALIAS => Item::TraitAlias(TraitAlias { syntax }), TYPE_ALIAS => Item::TypeAlias(TypeAlias { syntax }), UNION => Item::Union(Union { syntax }), USE => Item::Use(Use { syntax }), @@ -8554,7 +8497,6 @@ impl AstNode for Item { Item::Static(it) => &it.syntax, Item::Struct(it) => &it.syntax, Item::Trait(it) => &it.syntax, - Item::TraitAlias(it) => &it.syntax, Item::TypeAlias(it) => &it.syntax, Item::Union(it) => &it.syntax, Item::Use(it) => &it.syntax, @@ -8984,7 +8926,6 @@ impl AstNode for AnyHasAttrs { | STMT_LIST | STRUCT | TRAIT - | TRAIT_ALIAS | TRY_EXPR | TUPLE_EXPR | TUPLE_FIELD @@ -9257,10 +9198,6 @@ impl From for AnyHasAttrs { #[inline] fn from(node: Trait) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } } } -impl From for AnyHasAttrs { - #[inline] - fn from(node: TraitAlias) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } } -} impl From for AnyHasAttrs { #[inline] fn from(node: TryExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } } @@ -9330,7 +9267,6 @@ impl AstNode for AnyHasDocComments { | STATIC | STRUCT | TRAIT - | TRAIT_ALIAS | TUPLE_FIELD | TYPE_ALIAS | UNION @@ -9420,10 +9356,6 @@ impl From for AnyHasDocComments { #[inline] fn from(node: Trait) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } } } -impl From for AnyHasDocComments { - #[inline] - fn from(node: TraitAlias) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } } -} impl From for AnyHasDocComments { #[inline] fn from(node: TupleField) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } } @@ -9488,7 +9420,7 @@ impl ast::HasGenericParams for AnyHasGenericParams {} impl AstNode for AnyHasGenericParams { #[inline] fn can_cast(kind: SyntaxKind) -> bool { - matches!(kind, CONST | ENUM | FN | IMPL | STRUCT | TRAIT | TRAIT_ALIAS | TYPE_ALIAS | UNION) + matches!(kind, CONST | ENUM | FN | IMPL | STRUCT | TRAIT | TYPE_ALIAS | UNION) } #[inline] fn cast(syntax: SyntaxNode) -> Option { @@ -9536,10 +9468,6 @@ impl From for AnyHasGenericParams { #[inline] fn from(node: Trait) -> AnyHasGenericParams { AnyHasGenericParams { syntax: node.syntax } } } -impl From for AnyHasGenericParams { - #[inline] - fn from(node: TraitAlias) -> AnyHasGenericParams { AnyHasGenericParams { syntax: node.syntax } } -} impl From for AnyHasGenericParams { #[inline] fn from(node: TypeAlias) -> AnyHasGenericParams { AnyHasGenericParams { syntax: node.syntax } } @@ -9646,7 +9574,6 @@ impl AstNode for AnyHasName { | STATIC | STRUCT | TRAIT - | TRAIT_ALIAS | TYPE_ALIAS | TYPE_PARAM | UNION @@ -9739,10 +9666,6 @@ impl From for AnyHasName { #[inline] fn from(node: Trait) -> AnyHasName { AnyHasName { syntax: node.syntax } } } -impl From for AnyHasName { - #[inline] - fn from(node: TraitAlias) -> AnyHasName { AnyHasName { syntax: node.syntax } } -} impl From for AnyHasName { #[inline] fn from(node: TypeAlias) -> AnyHasName { AnyHasName { syntax: node.syntax } } @@ -9832,7 +9755,6 @@ impl AstNode for AnyHasVisibility { | STATIC | STRUCT | TRAIT - | TRAIT_ALIAS | TUPLE_FIELD | TYPE_ALIAS | UNION @@ -9910,10 +9832,6 @@ impl From for AnyHasVisibility { #[inline] fn from(node: Trait) -> AnyHasVisibility { AnyHasVisibility { syntax: node.syntax } } } -impl From for AnyHasVisibility { - #[inline] - fn from(node: TraitAlias) -> AnyHasVisibility { AnyHasVisibility { syntax: node.syntax } } -} impl From for AnyHasVisibility { #[inline] fn from(node: TupleField) -> AnyHasVisibility { AnyHasVisibility { syntax: node.syntax } } @@ -10639,11 +10557,6 @@ impl std::fmt::Display for Trait { std::fmt::Display::fmt(self.syntax(), f) } } -impl std::fmt::Display for TraitAlias { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - std::fmt::Display::fmt(self.syntax(), f) - } -} impl std::fmt::Display for TryExpr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs index 62a7d4df2cf6..42b0f5cf2da1 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs @@ -880,51 +880,6 @@ impl AstNode for TypeOrConstParam { impl HasAttrs for TypeOrConstParam {} -#[derive(Debug, Clone)] -pub enum TraitOrAlias { - Trait(ast::Trait), - TraitAlias(ast::TraitAlias), -} - -impl TraitOrAlias { - pub fn name(&self) -> Option { - match self { - TraitOrAlias::Trait(x) => x.name(), - TraitOrAlias::TraitAlias(x) => x.name(), - } - } -} - -impl AstNode for TraitOrAlias { - fn can_cast(kind: SyntaxKind) -> bool - where - Self: Sized, - { - matches!(kind, SyntaxKind::TRAIT | SyntaxKind::TRAIT_ALIAS) - } - - fn cast(syntax: SyntaxNode) -> Option - where - Self: Sized, - { - let res = match syntax.kind() { - SyntaxKind::TRAIT => TraitOrAlias::Trait(ast::Trait { syntax }), - SyntaxKind::TRAIT_ALIAS => TraitOrAlias::TraitAlias(ast::TraitAlias { syntax }), - _ => return None, - }; - Some(res) - } - - fn syntax(&self) -> &SyntaxNode { - match self { - TraitOrAlias::Trait(it) => it.syntax(), - TraitOrAlias::TraitAlias(it) => it.syntax(), - } - } -} - -impl HasAttrs for TraitOrAlias {} - pub enum VisibilityKind { In(ast::Path), PubCrate, diff --git a/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs b/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs index 824b38fc872d..9bd87a7ef5fe 100644 --- a/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs +++ b/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs @@ -1081,7 +1081,6 @@ fn extract_struct_traits(ast: &mut AstSrc) { "Enum", "Variant", "Trait", - "TraitAlias", "Module", "Static", "Const", From 0d2f8aff1be821907a824e5bad705c9385570fdc Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Mon, 4 Aug 2025 16:11:59 +0800 Subject: [PATCH 050/682] add test for trait alias projections --- .../crates/hir-ty/src/tests/trait_aliases.rs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/tests/trait_aliases.rs diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/trait_aliases.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/trait_aliases.rs new file mode 100644 index 000000000000..302ce550b853 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/trait_aliases.rs @@ -0,0 +1,21 @@ +use crate::tests::check_types; + +#[test] +fn projection() { + check_types( + r#" +#![feature(trait_alias)] + +pub trait A { + type Output; +} + +pub trait B = A; + +pub fn a(x: T::Output) { + x; +// ^ u32 +} +"#, + ); +} From b416845683e0892075352dd55889c625aef0e55c Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 13 Aug 2025 09:18:50 +0200 Subject: [PATCH 051/682] Print fields of interned IDs in hir-ty instead of just the ID --- .../rust-analyzer/crates/hir-ty/src/db.rs | 46 ++++++++++--------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 18b8db21161f..6e24aea76d44 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -3,7 +3,7 @@ use std::sync; -use base_db::{Crate, impl_intern_key}; +use base_db::Crate; use hir_def::{ AdtId, BlockId, CallableDefId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, StaticId, TraitId, @@ -459,40 +459,44 @@ fn hir_database_is_dyn_compatible() { fn _assert_dyn_compatible(_: &dyn HirDatabase) {} } -#[salsa_macros::interned(no_lifetime, revisions = usize::MAX)] +#[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] #[derive(PartialOrd, Ord)] pub struct InternedTypeOrConstParamId { pub loc: TypeOrConstParamId, } -impl ::std::fmt::Debug for InternedTypeOrConstParamId { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - f.debug_tuple(stringify!(InternedTypeOrConstParamId)) - .field(&format_args!("{:04x}", self.0.index())) - .finish() - } -} -#[salsa_macros::interned(no_lifetime, revisions = usize::MAX)] +#[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] #[derive(PartialOrd, Ord)] pub struct InternedLifetimeParamId { pub loc: LifetimeParamId, } -impl ::std::fmt::Debug for InternedLifetimeParamId { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - f.debug_tuple(stringify!(InternedLifetimeParamId)) - .field(&format_args!("{:04x}", self.0.index())) - .finish() - } + +#[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] +#[derive(PartialOrd, Ord)] +pub struct InternedConstParamId { + pub loc: ConstParamId, } -impl_intern_key!(InternedConstParamId, ConstParamId); - -impl_intern_key!(InternedOpaqueTyId, ImplTraitId); +#[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] +#[derive(PartialOrd, Ord)] +pub struct InternedOpaqueTyId { + pub loc: ImplTraitId, +} #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct InternedClosure(pub DefWithBodyId, pub ExprId); -impl_intern_key!(InternedClosureId, InternedClosure); + +#[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] +#[derive(PartialOrd, Ord)] +pub struct InternedClosureId { + pub loc: InternedClosure, +} #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct InternedCoroutine(pub DefWithBodyId, pub ExprId); -impl_intern_key!(InternedCoroutineId, InternedCoroutine); + +#[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] +#[derive(PartialOrd, Ord)] +pub struct InternedCoroutineId { + pub loc: InternedCoroutine, +} From 2a509717b4ddc1a8d73575583c8487956804121a Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 13 Aug 2025 09:18:50 +0200 Subject: [PATCH 052/682] Fix metrics checking out repos into toplevel folder instead of `target` --- src/tools/rust-analyzer/xtask/src/metrics.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/tools/rust-analyzer/xtask/src/metrics.rs b/src/tools/rust-analyzer/xtask/src/metrics.rs index 6ff6a1b15310..c9eea8710606 100644 --- a/src/tools/rust-analyzer/xtask/src/metrics.rs +++ b/src/tools/rust-analyzer/xtask/src/metrics.rs @@ -16,13 +16,16 @@ type Unit = String; impl flags::Metrics { pub(crate) fn run(self, sh: &Shell) -> anyhow::Result<()> { let mut metrics = Metrics::new(sh)?; - if !Path::new("./target/rustc-perf").exists() { - sh.create_dir("./target/rustc-perf")?; - cmd!(sh, "git clone https://github.com/rust-lang/rustc-perf.git ./target/rustc-perf") - .run()?; + if !Path::new("./target/metrics/rustc-perf").exists() { + sh.create_dir("./target/metrics/rustc-perf")?; + cmd!( + sh, + "git clone https://github.com/rust-lang/rustc-perf.git ./target/metrics/rustc-perf" + ) + .run()?; } { - let _d = sh.push_dir("./target/rustc-perf"); + let _d = sh.push_dir("./target/metrics/rustc-perf"); let revision = &metrics.perf_revision; cmd!(sh, "git reset --hard {revision}").run()?; } @@ -88,11 +91,12 @@ impl Metrics { cmd!( sh, - "git clone --depth=1 --branch 1.76.0 https://github.com/rust-lang/rust.git --single-branch" + "git clone --depth=1 --branch 1.76.0 https://github.com/rust-lang/rust.git --single-branch ./target/metrics/rust" ) .run()?; - let output = cmd!(sh, "./target/release/rust-analyzer rustc-tests ./rust").read()?; + let output = + cmd!(sh, "./target/release/rust-analyzer rustc-tests ./target/metrics/rust").read()?; for (metric, value, unit) in parse_metrics(&output) { self.report(metric, value, unit.into()); } @@ -106,7 +110,7 @@ impl Metrics { self.measure_analysis_stats_path( sh, bench, - &format!("./target/rustc-perf/collector/compile-benchmarks/{bench}"), + &format!("./target/metrics/rustc-perf/collector/compile-benchmarks/{bench}"), ) } fn measure_analysis_stats_path( From b62116bda772584e90591bbc6ebf3880a22b0080 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Wed, 13 Aug 2025 15:33:08 +0800 Subject: [PATCH 053/682] fix errors after rebase --- src/tools/rust-analyzer/crates/hir-def/src/signatures.rs | 4 ++++ .../crates/hir-ty/src/lower_nextsolver/path.rs | 7 +------ .../rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs | 6 +----- .../crates/hir-ty/src/next_solver/interner.rs | 8 ++++++-- src/tools/rust-analyzer/crates/ide/src/file_structure.rs | 2 +- 5 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs index 906c1c0e9ff1..bf72fafeae72 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs @@ -402,6 +402,7 @@ bitflags::bitflags! { const SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH = 1 << 6; const RUSTC_PAREN_SUGAR = 1 << 7; const COINDUCTIVE = 1 << 8; + const ALIAS = 1 << 9; } } @@ -426,6 +427,9 @@ impl TraitSignature { if source.value.unsafe_token().is_some() { flags.insert(TraitFlags::UNSAFE); } + if source.value.eq_token().is_some() { + flags.insert(TraitFlags::ALIAS); + } if attrs.by_key(sym::fundamental).exists() { flags |= TraitFlags::FUNDAMENTAL; } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs index b95bb1130fa2..df67b2c59b51 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs @@ -260,10 +260,6 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { }; return (ty, None); } - TypeNs::TraitAliasId(_) => { - // FIXME(trait_alias): Implement trait alias. - return (Ty::new_error(self.ctx.interner, ErrorGuaranteed), None); - } TypeNs::GenericParam(param_id) => { let generics = self.ctx.generics(); let idx = generics.type_or_const_param_idx(param_id.into()); @@ -343,8 +339,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { TypeNs::AdtId(_) | TypeNs::EnumVariantId(_) | TypeNs::TypeAliasId(_) - | TypeNs::TraitId(_) - | TypeNs::TraitAliasId(_) => {} + | TypeNs::TraitId(_) => {} } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs index cfbc10e740e4..64eab2d6b818 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs @@ -2,7 +2,7 @@ use hir_def::{ AdtId, ConstId, EnumId, EnumVariantId, FunctionId, GenericDefId, ImplId, StaticId, StructId, - TraitAliasId, TraitId, TypeAliasId, UnionId, + TraitId, TypeAliasId, UnionId, }; use rustc_type_ir::inherent; use stdx::impl_from; @@ -24,7 +24,6 @@ pub enum SolverDefId { FunctionId(FunctionId), ImplId(ImplId), StaticId(StaticId), - TraitAliasId(TraitAliasId), TraitId(TraitId), TypeAliasId(TypeAliasId), ForeignId(TypeAliasId), @@ -40,7 +39,6 @@ impl_from!( FunctionId, ImplId, StaticId, - TraitAliasId, TraitId, TypeAliasId, InternedClosureId, @@ -57,7 +55,6 @@ impl From for SolverDefId { GenericDefId::FunctionId(function_id) => SolverDefId::FunctionId(function_id), GenericDefId::ImplId(impl_id) => SolverDefId::ImplId(impl_id), GenericDefId::StaticId(static_id) => SolverDefId::StaticId(static_id), - GenericDefId::TraitAliasId(trait_alias_id) => SolverDefId::TraitAliasId(trait_alias_id), GenericDefId::TraitId(trait_id) => SolverDefId::TraitId(trait_id), GenericDefId::TypeAliasId(type_alias_id) => SolverDefId::TypeAliasId(type_alias_id), } @@ -74,7 +71,6 @@ impl TryFrom for GenericDefId { SolverDefId::FunctionId(function_id) => GenericDefId::FunctionId(function_id), SolverDefId::ImplId(impl_id) => GenericDefId::ImplId(impl_id), SolverDefId::StaticId(static_id) => GenericDefId::StaticId(static_id), - SolverDefId::TraitAliasId(trait_alias_id) => GenericDefId::TraitAliasId(trait_alias_id), SolverDefId::TraitId(trait_id) => GenericDefId::TraitId(trait_id), SolverDefId::TypeAliasId(type_alias_id) => GenericDefId::TypeAliasId(type_alias_id), SolverDefId::ForeignId(_) => return Err(value), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 9c1bd52065fd..17a4e7290c2c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -1172,7 +1172,6 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { | SolverDefId::AdtId(_) | SolverDefId::TraitId(_) | SolverDefId::ImplId(_) - | SolverDefId::TraitAliasId(_) | SolverDefId::Ctor(..) | SolverDefId::InternedOpaqueTyId(..) => panic!(), }; @@ -1790,7 +1789,12 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { } fn trait_is_alias(self, trait_def_id: Self::DefId) -> bool { - matches!(trait_def_id, SolverDefId::TraitAliasId(_)) + let trait_ = match trait_def_id { + SolverDefId::TraitId(id) => id, + _ => panic!("Unexpected SolverDefId in trait_is_alias"), + }; + let trait_data = self.db().trait_signature(trait_); + trait_data.flags.contains(TraitFlags::ALIAS) } fn trait_is_dyn_compatible(self, trait_def_id: Self::DefId) -> bool { diff --git a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs index d5c6eaca9744..21254fc4d6a2 100644 --- a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs +++ b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs @@ -943,7 +943,7 @@ fn let_statements() { navigation_range: 251..256, node_range: 245..262, kind: SymbolKind( - TraitAlias, + Trait, ), detail: None, deprecated: false, From 4bbbe6ee2b0c3c630d605c47ba0d4215d2522240 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 13 Aug 2025 09:42:32 +0200 Subject: [PATCH 054/682] fix: Attach db for inlay hint compute --- .../crates/hir-ty/src/next_solver/interner.rs | 2 +- .../crates/ide/src/inlay_hints.rs | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 9c1bd52065fd..54fec4aefbd4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -289,7 +289,7 @@ impl<'db> DbInterner<'db> { krate: None, block: None, }) - .unwrap() + .expect("db is expected to be attached") } pub fn new_with( diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index f6416fe51c30..424890fe370c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -107,14 +107,16 @@ pub(crate) fn inlay_hints( } }; let mut preorder = file.preorder(); - while let Some(event) = preorder.next() { - if matches!((&event, range_limit), (WalkEvent::Enter(node), Some(range)) if range.intersect(node.text_range()).is_none()) - { - preorder.skip_subtree(); - continue; + salsa::attach(sema.db, || { + while let Some(event) = preorder.next() { + if matches!((&event, range_limit), (WalkEvent::Enter(node), Some(range)) if range.intersect(node.text_range()).is_none()) + { + preorder.skip_subtree(); + continue; + } + hints(event); } - hints(event); - } + }); if let Some(range_limit) = range_limit { acc.retain(|hint| range_limit.contains_range(hint.range)); } @@ -736,7 +738,7 @@ fn label_of_ty( config: &InlayHintsConfig, display_target: DisplayTarget, ) -> Result<(), HirDisplayError> { - let iter_item_type = salsa::attach(sema.db, || hint_iterator(sema, famous_defs, ty)); + let iter_item_type = hint_iterator(sema, famous_defs, ty); match iter_item_type { Some((iter_trait, item, ty)) => { const LABEL_START: &str = "impl "; From 39ac6e1eed6312dfb41cd45410804bf629a8b7d1 Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 13 Aug 2025 10:01:17 +0200 Subject: [PATCH 055/682] update a few fixmes, and one trivial improvement --- .../crates/hir-ty/src/next_solver/interner.rs | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 9c1bd52065fd..402c73bb8822 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -22,8 +22,8 @@ use rustc_type_ir::inherent::{ use rustc_type_ir::lang_items::TraitSolverLangItem; use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::{ - AliasTerm, AliasTermKind, AliasTy, EarlyBinder, FlagComputation, Flags, ImplPolarity, InferTy, - ProjectionPredicate, TraitPredicate, TraitRef, Upcast, + AliasTerm, AliasTermKind, AliasTy, AliasTyKind, EarlyBinder, FlagComputation, Flags, + ImplPolarity, InferTy, ProjectionPredicate, TraitPredicate, TraitRef, Upcast, }; use salsa::plumbing::AsId; use smallvec::{SmallVec, smallvec}; @@ -1024,8 +1024,8 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { false } - fn expand_abstract_consts>(self, t: T) -> T { - t + fn expand_abstract_consts>(self, _: T) -> T { + unreachable!("only used by the old trait solver in rustc"); } fn generics_of(self, def_id: Self::DefId) -> Self::GenericsOf { @@ -1054,6 +1054,9 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { ), SolverDefId::InternedOpaqueTyId(_def_id) => { // FIXME(next-solver): track variances + // + // We compute them based on the only `Ty` level info in rustc, + // move `variances_of_opaque` into `rustc_next_trait_solver` for reuse. VariancesOf::new_from_iter( self, (0..self.generics_of(def_id).count()).map(|_| Variance::Invariant), @@ -1074,6 +1077,9 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { crate::TyDefId::TypeAliasId(id) } SolverDefId::AdtId(id) => crate::TyDefId::AdtId(id), + // FIXME(next-solver): need to support opaque types. This uses the types of + // `query mir_borrowck` in rustc. If we're ignoring regions, we could simply + // use the type inferred by general type inference here. _ => panic!("Unexpected def_id `{def_id:?}` provided for `type_of`"), }; self.db().ty_ns(def_id) @@ -1087,9 +1093,12 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { AdtDef::new(def_id, self) } - fn alias_ty_kind(self, alias: rustc_type_ir::AliasTy) -> rustc_type_ir::AliasTyKind { - // FIXME: not currently creating any others - rustc_type_ir::AliasTyKind::Projection + fn alias_ty_kind(self, alias: rustc_type_ir::AliasTy) -> AliasTyKind { + match alias.def_id { + SolverDefId::InternedOpaqueTyId(_) => AliasTyKind::Opaque, + SolverDefId::TypeAliasId(_) => AliasTyKind::Projection, + _ => unimplemented!("Unexpected alias: {:?}", alias.def_id), + } } fn alias_term_kind( @@ -1100,7 +1109,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { SolverDefId::InternedOpaqueTyId(_) => AliasTermKind::OpaqueTy, SolverDefId::TypeAliasId(_) => AliasTermKind::ProjectionTy, SolverDefId::ConstId(_) => AliasTermKind::UnevaluatedConst, - _ => unreachable!("Unexpected alias: {:?}", alias.def_id), + _ => todo!("Unexpected alias: {:?}", alias.def_id), } } @@ -1741,7 +1750,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { } fn has_item_definition(self, def_id: Self::DefId) -> bool { - // FIXME: should check if has value + // FIXME(next-solver): should check if the associated item has a value. true } @@ -1811,7 +1820,8 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { } fn trait_may_be_implemented_via_object(self, trait_def_id: Self::DefId) -> bool { - // FIXME(next-solver) + // FIXME(next-solver): should check the `TraitFlags` for + // the `#[rustc_do_not_implement_via_object]` flag true } @@ -1982,7 +1992,8 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { self, def_id: Self::LocalDefId, ) -> rustc_type_ir::EarlyBinder { - // FIXME(next-solver) + // FIXME(next-solver): This should look at the type computed for the + // opaque by HIR typeck. unimplemented!() } From 00941b3142bf7b50a39b1a2dfa167d3642b5f3db Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 13 Aug 2025 10:07:12 +0200 Subject: [PATCH 056/682] implement `type_of_opaque` --- .../rust-analyzer/crates/hir-ty/src/layout.rs | 23 +--- .../crates/hir-ty/src/next_solver/interner.rs | 100 +++++++++--------- .../rust-analyzer/crates/hir-ty/src/tests.rs | 2 +- ...e_alias_impl_traits.rs => opaque_types.rs} | 16 +++ 4 files changed, 69 insertions(+), 72 deletions(-) rename src/tools/rust-analyzer/crates/hir-ty/src/tests/{type_alias_impl_traits.rs => opaque_types.rs} (92%) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index 819bd583deae..e7bb529ad8f2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -335,27 +335,6 @@ pub fn layout_of_ty_ns_query<'db>( ptr.valid_range_mut().start = 1; Layout::scalar(dl, ptr) } - TyKind::Alias(_, ty) => match ty.def_id { - SolverDefId::TypeAliasId(_) => { - return Err(LayoutError::HasPlaceholder); - } - SolverDefId::InternedOpaqueTyId(opaque) => { - let impl_trait_id = db.lookup_intern_impl_trait_id(opaque); - match impl_trait_id { - crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => { - let infer = db.infer(func.into()); - return db.layout_of_ty(infer.type_of_rpit[idx].clone(), trait_env); - } - crate::ImplTraitId::TypeAliasImplTrait(..) => { - return Err(LayoutError::NotImplemented); - } - crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { - return Err(LayoutError::NotImplemented); - } - } - } - _ => unreachable!(), - }, TyKind::Closure(c, args) => { let id = match c { SolverDefId::InternedClosureId(id) => id, @@ -389,7 +368,7 @@ pub fn layout_of_ty_ns_query<'db>( } TyKind::Error(_) => return Err(LayoutError::HasErrorType), - TyKind::Placeholder(_) | TyKind::Bound(..) | TyKind::Infer(..) | TyKind::Param(..) => { + TyKind::Placeholder(_) | TyKind::Bound(..) | TyKind::Infer(..) | TyKind::Param(..) | TyKind::Alias(..) => { return Err(LayoutError::HasPlaceholder); } }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 402c73bb8822..294d071f58ae 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -626,12 +626,8 @@ impl<'db> inherent::AdtDef> for AdtDef { fn struct_tail_ty( self, interner: DbInterner<'db>, - ) -> Option< - rustc_type_ir::EarlyBinder< - DbInterner<'db>, - as rustc_type_ir::Interner>::Ty, - >, - > { + ) -> Option, as rustc_type_ir::Interner>::Ty>> + { let db = interner.db(); let hir_def::AdtId::StructId(struct_id) = self.inner().id else { return None; @@ -645,7 +641,7 @@ impl<'db> inherent::AdtDef> for AdtDef { fn all_field_tys( self, interner: DbInterner<'db>, - ) -> rustc_type_ir::EarlyBinder< + ) -> EarlyBinder< DbInterner<'db>, impl IntoIterator as rustc_type_ir::Interner>::Ty>, > { @@ -679,19 +675,15 @@ impl<'db> inherent::AdtDef> for AdtDef { .collect(), }; - rustc_type_ir::EarlyBinder::bind(tys) + EarlyBinder::bind(tys) } fn sizedness_constraint( self, interner: DbInterner<'db>, sizedness: SizedTraitKind, - ) -> Option< - rustc_type_ir::EarlyBinder< - DbInterner<'db>, - as rustc_type_ir::Interner>::Ty, - >, - > { + ) -> Option, as rustc_type_ir::Interner>::Ty>> + { if self.is_struct() { let tail_ty = self.all_field_tys(interner).skip_binder().into_iter().last()?; @@ -1066,7 +1058,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { } } - fn type_of(self, def_id: Self::DefId) -> rustc_type_ir::EarlyBinder { + fn type_of(self, def_id: Self::DefId) -> EarlyBinder { let def_id = match def_id { SolverDefId::TypeAliasId(id) => { use hir_def::Lookup; @@ -1077,9 +1069,13 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { crate::TyDefId::TypeAliasId(id) } SolverDefId::AdtId(id) => crate::TyDefId::AdtId(id), - // FIXME(next-solver): need to support opaque types. This uses the types of - // `query mir_borrowck` in rustc. If we're ignoring regions, we could simply - // use the type inferred by general type inference here. + // FIXME(next-solver): This uses the types of `query mir_borrowck` in rustc. + // + // We currently always use the type from HIR typeck which ignores regions. This + // should be fine. + SolverDefId::InternedOpaqueTyId(_) => { + return self.type_of_opaque_hir_typeck(def_id); + } _ => panic!("Unexpected def_id `{def_id:?}` provided for `type_of`"), }; self.db().ty_ns(def_id) @@ -1109,7 +1105,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { SolverDefId::InternedOpaqueTyId(_) => AliasTermKind::OpaqueTy, SolverDefId::TypeAliasId(_) => AliasTermKind::ProjectionTy, SolverDefId::ConstId(_) => AliasTermKind::UnevaluatedConst, - _ => todo!("Unexpected alias: {:?}", alias.def_id), + _ => unimplemented!("Unexpected alias: {:?}", alias.def_id), } } @@ -1204,8 +1200,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { fn fn_sig( self, def_id: Self::DefId, - ) -> rustc_type_ir::EarlyBinder>> - { + ) -> EarlyBinder>> { let id = match def_id { SolverDefId::FunctionId(id) => CallableDefId::FunctionId(id), SolverDefId::Ctor(ctor) => match ctor { @@ -1258,7 +1253,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { fn item_bounds( self, def_id: Self::DefId, - ) -> rustc_type_ir::EarlyBinder> { + ) -> EarlyBinder> { explicit_item_bounds(self, def_id).map_bound(|bounds| { Clauses::new_from_iter(self, elaborate(self, bounds).collect::>()) }) @@ -1268,7 +1263,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { fn item_self_bounds( self, def_id: Self::DefId, - ) -> rustc_type_ir::EarlyBinder> { + ) -> EarlyBinder> { explicit_item_bounds(self, def_id).map_bound(|bounds| { Clauses::new_from_iter( self, @@ -1280,7 +1275,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { fn item_non_self_bounds( self, def_id: Self::DefId, - ) -> rustc_type_ir::EarlyBinder> { + ) -> EarlyBinder> { let all_bounds: FxHashSet<_> = self.item_bounds(def_id).skip_binder().into_iter().collect(); let own_bounds: FxHashSet<_> = self.item_self_bounds(def_id).skip_binder().into_iter().collect(); @@ -1298,7 +1293,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { fn predicates_of( self, def_id: Self::DefId, - ) -> rustc_type_ir::EarlyBinder> { + ) -> EarlyBinder> { let predicates = self.db().generic_predicates_ns(def_id.try_into().unwrap()); let predicates: Vec<_> = predicates.iter().cloned().collect(); EarlyBinder::bind(predicates.into_iter()) @@ -1308,7 +1303,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { fn own_predicates_of( self, def_id: Self::DefId, - ) -> rustc_type_ir::EarlyBinder> { + ) -> EarlyBinder> { let predicates = self.db().generic_predicates_without_parent_ns(def_id.try_into().unwrap()); let predicates: Vec<_> = predicates.iter().cloned().collect(); EarlyBinder::bind(predicates.into_iter()) @@ -1318,8 +1313,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { fn explicit_super_predicates_of( self, def_id: Self::DefId, - ) -> rustc_type_ir::EarlyBinder> - { + ) -> EarlyBinder> { let predicates: Vec<(Clause<'db>, Span)> = self .db() .generic_predicates_ns(def_id.try_into().unwrap()) @@ -1327,15 +1321,14 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { .cloned() .map(|p| (p, Span::dummy())) .collect(); - rustc_type_ir::EarlyBinder::bind(predicates) + EarlyBinder::bind(predicates) } #[tracing::instrument(skip(self), ret)] fn explicit_implied_predicates_of( self, def_id: Self::DefId, - ) -> rustc_type_ir::EarlyBinder> - { + ) -> EarlyBinder> { let predicates: Vec<(Clause<'db>, Span)> = self .db() .generic_predicates_ns(def_id.try_into().unwrap()) @@ -1343,13 +1336,13 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { .cloned() .map(|p| (p, Span::dummy())) .collect(); - rustc_type_ir::EarlyBinder::bind(predicates) + EarlyBinder::bind(predicates) } fn impl_super_outlives( self, impl_def_id: Self::DefId, - ) -> rustc_type_ir::EarlyBinder> { + ) -> EarlyBinder> { let impl_id = match impl_def_id { SolverDefId::ImplId(id) => id, _ => unreachable!(), @@ -1372,11 +1365,11 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { fn const_conditions( self, def_id: Self::DefId, - ) -> rustc_type_ir::EarlyBinder< + ) -> EarlyBinder< Self, impl IntoIterator>>, > { - rustc_type_ir::EarlyBinder::bind([unimplemented!()]) + EarlyBinder::bind([unimplemented!()]) } fn has_target_features(self, def_id: Self::DefId) -> bool { @@ -1763,7 +1756,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { fn impl_trait_ref( self, impl_def_id: Self::DefId, - ) -> rustc_type_ir::EarlyBinder> { + ) -> EarlyBinder> { let impl_id = match impl_def_id { SolverDefId::ImplId(id) => id, _ => panic!("Unexpected SolverDefId in impl_trait_ref"), @@ -1960,12 +1953,12 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { fn explicit_implied_const_bounds( self, def_id: Self::DefId, - ) -> rustc_type_ir::EarlyBinder< + ) -> EarlyBinder< Self, impl IntoIterator>>, > { // FIXME(next-solver) - rustc_type_ir::EarlyBinder::bind([]) + EarlyBinder::bind([]) } fn fn_is_const(self, def_id: Self::DefId) -> bool { @@ -1988,22 +1981,31 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { None } - fn type_of_opaque_hir_typeck( - self, - def_id: Self::LocalDefId, - ) -> rustc_type_ir::EarlyBinder { - // FIXME(next-solver): This should look at the type computed for the - // opaque by HIR typeck. - unimplemented!() + fn type_of_opaque_hir_typeck(self, def_id: Self::LocalDefId) -> EarlyBinder { + match def_id { + SolverDefId::InternedOpaqueTyId(opaque) => { + let impl_trait_id = self.db().lookup_intern_impl_trait_id(opaque); + match impl_trait_id { + crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => { + let infer = self.db().infer(func.into()); + EarlyBinder::bind(infer.type_of_rpit[idx].to_nextsolver(self)) + } + crate::ImplTraitId::TypeAliasImplTrait(..) + | crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { + // FIXME(next-solver) + EarlyBinder::bind(Ty::new_error(self, ErrorGuaranteed)) + } + } + } + _ => panic!("Unexpected SolverDefId in type_of_opaque_hir_typeck"), + } } fn coroutine_hidden_types( self, def_id: Self::DefId, - ) -> rustc_type_ir::EarlyBinder< - Self, - rustc_type_ir::Binder>, - > { + ) -> EarlyBinder>> + { // FIXME(next-solver) unimplemented!() } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs index fc31973022bd..d89cf4bcd45c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -6,11 +6,11 @@ mod incremental; mod macros; mod method_resolution; mod never_type; +mod opaque_types; mod patterns; mod regression; mod simple; mod traits; -mod type_alias_impl_traits; use base_db::{Crate, SourceDatabase}; use expect_test::Expect; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/type_alias_impl_traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs similarity index 92% rename from src/tools/rust-analyzer/crates/hir-ty/src/tests/type_alias_impl_traits.rs rename to src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs index e2b7bf379cc3..36578545a9f4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/type_alias_impl_traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs @@ -159,3 +159,19 @@ static ALIAS: i32 = { "#]], ) } + +#[test] +fn leak_auto_traits() { + check_no_mismatches( + r#" +//- minicore: send +fn foo() -> impl Sized {} + +fn is_send(_: T) {} + +fn main() { + is_send(foo()); +} + "#, + ); +} From 5849fef132daf525016379b6bfc6bd25459371dd Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 13 Aug 2025 10:30:34 +0200 Subject: [PATCH 057/682] layout_of uses `PostAnalysis` --- src/tools/rust-analyzer/crates/hir-ty/src/layout.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index e7bb529ad8f2..2d0471d7e554 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -182,7 +182,7 @@ pub fn layout_of_ty_ns_query<'db>( }; let dl = &*target; let cx = LayoutCx::new(dl); - let infer_ctxt = interner.infer_ctxt().build(TypingMode::non_body_analysis()); + let infer_ctxt = interner.infer_ctxt().build(TypingMode::PostAnalysis); let cause = ObligationCause::dummy(); let ty = deeply_normalize(infer_ctxt.at(&cause, ParamEnv::empty()), ty).unwrap_or(ty); let result = match ty.kind() { @@ -368,7 +368,11 @@ pub fn layout_of_ty_ns_query<'db>( } TyKind::Error(_) => return Err(LayoutError::HasErrorType), - TyKind::Placeholder(_) | TyKind::Bound(..) | TyKind::Infer(..) | TyKind::Param(..) | TyKind::Alias(..) => { + TyKind::Placeholder(_) + | TyKind::Bound(..) + | TyKind::Infer(..) + | TyKind::Param(..) + | TyKind::Alias(..) => { return Err(LayoutError::HasPlaceholder); } }; From 67b1e329080519085716c8e444a43eede0d196a6 Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 13 Aug 2025 11:00:00 +0200 Subject: [PATCH 058/682] manually normalize alias --- .../crates/hir-ty/src/next_solver/interner.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 294d071f58ae..c530c28f8861 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -626,8 +626,7 @@ impl<'db> inherent::AdtDef> for AdtDef { fn struct_tail_ty( self, interner: DbInterner<'db>, - ) -> Option, as rustc_type_ir::Interner>::Ty>> - { + ) -> Option, Ty<'db>>> { let db = interner.db(); let hir_def::AdtId::StructId(struct_id) = self.inner().id else { return None; @@ -641,10 +640,7 @@ impl<'db> inherent::AdtDef> for AdtDef { fn all_field_tys( self, interner: DbInterner<'db>, - ) -> EarlyBinder< - DbInterner<'db>, - impl IntoIterator as rustc_type_ir::Interner>::Ty>, - > { + ) -> EarlyBinder, impl IntoIterator>> { let db = interner.db(); // FIXME: this is disabled just to match the behavior with chalk right now let field_tys = |id: VariantId| { @@ -682,8 +678,7 @@ impl<'db> inherent::AdtDef> for AdtDef { self, interner: DbInterner<'db>, sizedness: SizedTraitKind, - ) -> Option, as rustc_type_ir::Interner>::Ty>> - { + ) -> Option, Ty<'db>>> { if self.is_struct() { let tail_ty = self.all_field_tys(interner).skip_binder().into_iter().last()?; From 1d57d7800db05934a466457c443975d71c4d1e29 Mon Sep 17 00:00:00 2001 From: donni-h <56559005+donni-h@users.noreply.github.com> Date: Wed, 13 Aug 2025 13:03:52 +0200 Subject: [PATCH 059/682] Fix dead link to Cargo.toml in documentation ../../Cargo.toml resolves to https://rust-analyzer.github.io/Cargo.toml, which is an invalid link --- src/tools/rust-analyzer/docs/book/src/contributing/style.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/docs/book/src/contributing/style.md b/src/tools/rust-analyzer/docs/book/src/contributing/style.md index 746f3eb13211..fe09fb6c2fd5 100644 --- a/src/tools/rust-analyzer/docs/book/src/contributing/style.md +++ b/src/tools/rust-analyzer/docs/book/src/contributing/style.md @@ -101,7 +101,7 @@ Including a description and GIF suitable for the changelog means less work for t ## Clippy -We use Clippy to improve the code, but if some lints annoy you, allow them in the [Cargo.toml](../../Cargo.toml) [workspace.lints.clippy] section. +We use Clippy to improve the code, but if some lints annoy you, allow them in the [Cargo.toml](https://github.com/rust-lang/rust-analyzer/blob/master/Cargo.toml) [workspace.lints.clippy] section. # Code From 8ef6a8ff6defd019dff7865ab602bc7acba1afc2 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Wed, 13 Aug 2025 15:24:53 +0100 Subject: [PATCH 060/682] [internal] Update to the latest @vscode/vsce for extension build This isn't a logic change, but it fixes an npm warning during the build. vsce itself hasn't had any major changes between 3.2.2 and 3.6. * https://github.com/microsoft/vscode-vsce/releases/tag/v3.3.0 * https://github.com/microsoft/vscode-vsce/releases/tag/v3.4.0 * https://github.com/microsoft/vscode-vsce/releases/tag/v3.5.0 * https://github.com/microsoft/vscode-vsce/releases/tag/v3.6.0 --- .../editors/code/package-lock.json | 1356 ++++++++++++++--- .../rust-analyzer/editors/code/package.json | 2 +- 2 files changed, 1163 insertions(+), 195 deletions(-) diff --git a/src/tools/rust-analyzer/editors/code/package-lock.json b/src/tools/rust-analyzer/editors/code/package-lock.json index 1dc11ee4e4b8..ad8708e00c51 100644 --- a/src/tools/rust-analyzer/editors/code/package-lock.json +++ b/src/tools/rust-analyzer/editors/code/package-lock.json @@ -26,7 +26,7 @@ "@typescript-eslint/eslint-plugin": "^8.25.0", "@typescript-eslint/parser": "^8.25.0", "@vscode/test-electron": "^2.4.1", - "@vscode/vsce": "^3.2.2", + "@vscode/vsce": "^3.6.0", "esbuild": "^0.25.0", "eslint": "^9.21.0", "eslint-config-prettier": "^10.0.2", @@ -41,6 +41,23 @@ "vscode": "^1.93.0" } }, + "node_modules/@azu/format-text": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@azu/format-text/-/format-text-1.0.2.tgz", + "integrity": "sha512-Swi4N7Edy1Eqq82GxgEECXSSLyn6GOb5htRFPzBDdUkECGXtlf12ynO5oJSpWKPwCaUssOu7NfhDcCWpIC6Ywg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@azu/style-format": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@azu/style-format/-/style-format-1.0.1.tgz", + "integrity": "sha512-AHcTojlNBdD/3/KxIKlg8sxIWHfOtQszLvOpagLTO+bjC3u7SAszu1lf//u7JJC50aUSH+BVWDD/KvaA6Gfn5g==", + "dev": true, + "license": "WTFPL", + "dependencies": { + "@azu/format-text": "^1.0.1" + } + }, "node_modules/@azure/abort-controller": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", @@ -212,6 +229,31 @@ "node": ">=16" } }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.0", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", @@ -947,6 +989,217 @@ "node": ">= 8" } }, + "node_modules/@secretlint/config-creator": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/config-creator/-/config-creator-10.2.2.tgz", + "integrity": "sha512-BynOBe7Hn3LJjb3CqCHZjeNB09s/vgf0baBaHVw67w7gHF0d25c3ZsZ5+vv8TgwSchRdUCRrbbcq5i2B1fJ2QQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/types": "^10.2.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/config-loader": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/config-loader/-/config-loader-10.2.2.tgz", + "integrity": "sha512-ndjjQNgLg4DIcMJp4iaRD6xb9ijWQZVbd9694Ol2IszBIbGPPkwZHzJYKICbTBmh6AH/pLr0CiCaWdGJU7RbpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/profiler": "^10.2.2", + "@secretlint/resolver": "^10.2.2", + "@secretlint/types": "^10.2.2", + "ajv": "^8.17.1", + "debug": "^4.4.1", + "rc-config-loader": "^4.1.3" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/config-loader/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@secretlint/config-loader/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/@secretlint/core": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/core/-/core-10.2.2.tgz", + "integrity": "sha512-6rdwBwLP9+TO3rRjMVW1tX+lQeo5gBbxl1I5F8nh8bgGtKwdlCMhMKsBWzWg1ostxx/tIG7OjZI0/BxsP8bUgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/profiler": "^10.2.2", + "@secretlint/types": "^10.2.2", + "debug": "^4.4.1", + "structured-source": "^4.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/formatter": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/formatter/-/formatter-10.2.2.tgz", + "integrity": "sha512-10f/eKV+8YdGKNQmoDUD1QnYL7TzhI2kzyx95vsJKbEa8akzLAR5ZrWIZ3LbcMmBLzxlSQMMccRmi05yDQ5YDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/resolver": "^10.2.2", + "@secretlint/types": "^10.2.2", + "@textlint/linter-formatter": "^15.2.0", + "@textlint/module-interop": "^15.2.0", + "@textlint/types": "^15.2.0", + "chalk": "^5.4.1", + "debug": "^4.4.1", + "pluralize": "^8.0.0", + "strip-ansi": "^7.1.0", + "table": "^6.9.0", + "terminal-link": "^4.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/formatter/node_modules/chalk": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.5.0.tgz", + "integrity": "sha512-1tm8DTaJhPBG3bIkVeZt1iZM9GfSX2lzOeDVZH9R9ffRHpmHvxZ/QhgQH/aDTkswQVt+YHdXAdS/In/30OjCbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@secretlint/node": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/node/-/node-10.2.2.tgz", + "integrity": "sha512-eZGJQgcg/3WRBwX1bRnss7RmHHK/YlP/l7zOQsrjexYt6l+JJa5YhUmHbuGXS94yW0++3YkEJp0kQGYhiw1DMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/config-loader": "^10.2.2", + "@secretlint/core": "^10.2.2", + "@secretlint/formatter": "^10.2.2", + "@secretlint/profiler": "^10.2.2", + "@secretlint/source-creator": "^10.2.2", + "@secretlint/types": "^10.2.2", + "debug": "^4.4.1", + "p-map": "^7.0.3" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/profiler": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/profiler/-/profiler-10.2.2.tgz", + "integrity": "sha512-qm9rWfkh/o8OvzMIfY8a5bCmgIniSpltbVlUVl983zDG1bUuQNd1/5lUEeWx5o/WJ99bXxS7yNI4/KIXfHexig==", + "dev": true, + "license": "MIT" + }, + "node_modules/@secretlint/resolver": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/resolver/-/resolver-10.2.2.tgz", + "integrity": "sha512-3md0cp12e+Ae5V+crPQYGd6aaO7ahw95s28OlULGyclyyUtf861UoRGS2prnUrKh7MZb23kdDOyGCYb9br5e4w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@secretlint/secretlint-formatter-sarif": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-formatter-sarif/-/secretlint-formatter-sarif-10.2.2.tgz", + "integrity": "sha512-ojiF9TGRKJJw308DnYBucHxkpNovDNu1XvPh7IfUp0A12gzTtxuWDqdpuVezL7/IP8Ua7mp5/VkDMN9OLp1doQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "node-sarif-builder": "^3.2.0" + } + }, + "node_modules/@secretlint/secretlint-rule-no-dotenv": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-rule-no-dotenv/-/secretlint-rule-no-dotenv-10.2.2.tgz", + "integrity": "sha512-KJRbIShA9DVc5Va3yArtJ6QDzGjg3PRa1uYp9As4RsyKtKSSZjI64jVca57FZ8gbuk4em0/0Jq+uy6485wxIdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/types": "^10.2.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/secretlint-rule-preset-recommend": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-rule-preset-recommend/-/secretlint-rule-preset-recommend-10.2.2.tgz", + "integrity": "sha512-K3jPqjva8bQndDKJqctnGfwuAxU2n9XNCPtbXVI5JvC7FnQiNg/yWlQPbMUlBXtBoBGFYp08A94m6fvtc9v+zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/source-creator": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/source-creator/-/source-creator-10.2.2.tgz", + "integrity": "sha512-h6I87xJfwfUTgQ7irWq7UTdq/Bm1RuQ/fYhA3dtTIAop5BwSFmZyrchph4WcoEvbN460BWKmk4RYSvPElIIvxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/types": "^10.2.2", + "istextorbinary": "^9.5.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/types": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/types/-/types-10.2.2.tgz", + "integrity": "sha512-Nqc90v4lWCXyakD6xNyNACBJNJ0tNCwj2WNk/7ivyacYHxiITVgmLUFXTBOeCdy79iz6HtN9Y31uw/jbLrdOAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@stylistic/eslint-plugin": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-4.1.0.tgz", @@ -984,6 +1237,136 @@ "eslint": ">=9.0.0" } }, + "node_modules/@textlint/ast-node-types": { + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-15.2.1.tgz", + "integrity": "sha512-20fEcLPsXg81yWpApv4FQxrZmlFF/Ta7/kz1HGIL+pJo5cSTmkc+eCki3GpOPZIoZk0tbJU8hrlwUb91F+3SNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/linter-formatter": { + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@textlint/linter-formatter/-/linter-formatter-15.2.1.tgz", + "integrity": "sha512-oollG/BHa07+mMt372amxHohteASC+Zxgollc1sZgiyxo4S6EuureV3a4QIQB0NecA+Ak3d0cl0WI/8nou38jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azu/format-text": "^1.0.2", + "@azu/style-format": "^1.0.1", + "@textlint/module-interop": "15.2.1", + "@textlint/resolver": "15.2.1", + "@textlint/types": "15.2.1", + "chalk": "^4.1.2", + "debug": "^4.4.1", + "js-yaml": "^3.14.1", + "lodash": "^4.17.21", + "pluralize": "^2.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "table": "^6.9.0", + "text-table": "^0.2.0" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/linter-formatter/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/pluralize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-2.0.0.tgz", + "integrity": "sha512-TqNZzQCD4S42De9IfnnBvILN7HAW7riLqsCyp8lgjXeysyPlX5HhqKAcJHHHb9XskE4/a+7VGC9zzx8Ls0jOAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/linter-formatter/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@textlint/module-interop": { + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@textlint/module-interop/-/module-interop-15.2.1.tgz", + "integrity": "sha512-b/C/ZNrm05n1ypymDknIcpkBle30V2ZgE3JVqQlA9PnQV46Ky510qrZk6s9yfKgA3m1YRnAw04m8xdVtqjq1qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/resolver": { + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@textlint/resolver/-/resolver-15.2.1.tgz", + "integrity": "sha512-FY3aK4tElEcOJVUsaMj4Zro4jCtKEEwUMIkDL0tcn6ljNcgOF7Em+KskRRk/xowFWayqDtdz5T3u7w/6fjjuJQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/types": { + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@textlint/types/-/types-15.2.1.tgz", + "integrity": "sha512-zyqNhSatK1cwxDUgosEEN43hFh3WCty9Zm2Vm3ogU566IYegifwqN54ey/CiRy/DiO4vMcFHykuQnh2Zwp6LLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@textlint/ast-node-types": "15.2.1" + } + }, "node_modules/@tsconfig/strictest": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@tsconfig/strictest/-/strictest-2.0.5.tgz", @@ -1015,6 +1398,20 @@ "undici-types": "~6.20.0" } }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/sarif": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@types/sarif/-/sarif-2.1.7.tgz", + "integrity": "sha512-kRz0VEkJqWLf1LLVN4pT1cg1Z9wAuvI6L97V3m2f5B76Tg8d413ddvLBPTEHAZJlnn4XSvu0FkZtViCQGVyrXQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/vscode": { "version": "1.93.0", "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.93.0.tgz", @@ -1220,16 +1617,20 @@ } }, "node_modules/@vscode/vsce": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-3.2.2.tgz", - "integrity": "sha512-4TqdUq/yKlQTHcQMk/DamR632bq/+IJDomSbexOMee/UAYWqYm0XHWA6scGslsCpzY+sCWEhhl0nqdOB0XW1kw==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-3.6.0.tgz", + "integrity": "sha512-u2ZoMfymRNJb14aHNawnXJtXHLXDVKc1oKZaH4VELKT/9iWKRVgtQOdwxCgtwSxJoqYvuK4hGlBWQJ05wxADhg==", "dev": true, "license": "MIT", "dependencies": { "@azure/identity": "^4.1.0", + "@secretlint/node": "^10.1.1", + "@secretlint/secretlint-formatter-sarif": "^10.1.1", + "@secretlint/secretlint-rule-no-dotenv": "^10.1.1", + "@secretlint/secretlint-rule-preset-recommend": "^10.1.1", "@vscode/vsce-sign": "^2.0.0", "azure-devops-node-api": "^12.5.0", - "chalk": "^2.4.2", + "chalk": "^4.1.2", "cheerio": "^1.0.0-rc.9", "cockatiel": "^3.1.2", "commander": "^12.1.0", @@ -1243,6 +1644,7 @@ "minimatch": "^3.0.3", "parse-semver": "^1.1.1", "read": "^1.0.7", + "secretlint": "^10.1.1", "semver": "^7.5.2", "tmp": "^0.2.3", "typed-rest-client": "^1.8.4", @@ -1486,6 +1888,22 @@ "integrity": "sha512-PMqBCBvrOVDRqLGooQb+z+t1Q0PiPyurUQeZRR5uHBOVZcW8B04KMmnT12USnhpNX2wCPagWzLVppQMUG3u0Dw==", "license": "MIT" }, + "node_modules/ansi-escapes": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", + "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ansi-regex": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", @@ -1500,16 +1918,18 @@ } }, "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "license": "MIT", "dependencies": { - "color-convert": "^1.9.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/argparse": { @@ -1519,6 +1939,16 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1564,6 +1994,22 @@ ], "license": "MIT" }, + "node_modules/binaryextensions": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-6.11.0.tgz", + "integrity": "sha512-sXnYK/Ij80TO3lcqZVV2YgfKN5QjUWIRk/XSm2J/4bd/lPko3lvk0O4ZppH6m+6hB2/GTu+ptNwVFe1xh+QLQw==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "editions": "^6.21.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, "node_modules/bl": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", @@ -1598,6 +2044,13 @@ "dev": true, "license": "ISC" }, + "node_modules/boundary": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/boundary/-/boundary-2.0.0.tgz", + "integrity": "sha512-rJKn5ooC9u8q13IMCrW0RSp31pxBCHE3y9V/tp3TdWSLf8Em3p6Di4NBpfzbJge9YjjFEsD0RtFEjtvHL5VyEA==", + "dev": true, + "license": "BSD-2-Clause" + }, "node_modules/brace-expansion": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", @@ -1720,18 +2173,20 @@ } }, "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/cheerio": { @@ -1845,39 +2300,6 @@ "node": ">=8" } }, - "node_modules/cliui/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/cliui/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/cliui/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, "node_modules/cliui/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -1938,20 +2360,21 @@ } }, "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "license": "MIT", "dependencies": { - "color-name": "1.1.3" + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true, + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, "node_modules/combined-stream": { @@ -2469,9 +2892,9 @@ } }, "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2685,6 +3108,23 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/editions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/editions/-/editions-6.22.0.tgz", + "integrity": "sha512-UgGlf8IW75je7HZjNDpJdCv4cGJWIi6yumFdZ0R7A8/CIhQiWUjyGLCxdHpd8bmyD1gnkfUNK0oeOXqUS2cpfQ==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "version-range": "^4.15.0" + }, + "engines": { + "ecmascript": ">= es5", + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -2730,6 +3170,19 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -2829,16 +3282,6 @@ "node": ">=6" } }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/eslint": { "version": "9.21.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.21.0.tgz", @@ -2964,22 +3407,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/eslint/node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -2991,43 +3418,6 @@ "concat-map": "0.0.1" } }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/eslint/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/eslint/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/eslint/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -3041,16 +3431,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/eslint/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -3064,19 +3444,6 @@ "node": "*" } }, - "node_modules/eslint/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/espree": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", @@ -3095,6 +3462,20 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/esquery": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", @@ -3213,6 +3594,23 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fastq": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", @@ -3360,6 +3758,21 @@ "license": "MIT", "optional": true }, + "node_modules/fs-extra": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.1.tgz", + "integrity": "sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -3492,6 +3905,37 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/globby": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz", + "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.3", + "path-type": "^6.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -3505,6 +3949,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -3513,13 +3964,13 @@ "license": "MIT" }, "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/has-symbols": { @@ -3702,6 +4153,19 @@ "node": ">=0.8.19" } }, + "node_modules/index-to-position": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.1.0.tgz", + "integrity": "sha512-XPdx9Dq4t9Qk1mTMbWONJqU7boCoumEH7fRET37HX5+khDUl3J2W6PdALxhILYlIYx2amlwYcRPp28p0tSiojg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -3872,6 +4336,24 @@ "dev": true, "license": "ISC" }, + "node_modules/istextorbinary": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-9.5.0.tgz", + "integrity": "sha512-5mbUj3SiZXCuRf9fT3ibzbSSEWiy63gFfksmGfdOzujPjW3k+z8WvIBxcJHBoQNlaZaiyB25deviif2+osLmLw==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "binaryextensions": "^6.11.0", + "editions": "^6.21.0", + "textextensions": "^6.11.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, "node_modules/jackspeak": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.0.tgz", @@ -3897,6 +4379,13 @@ "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -3931,6 +4420,19 @@ "dev": true, "license": "MIT" }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/jsonc-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", @@ -3938,6 +4440,19 @@ "dev": true, "license": "MIT" }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/jsonwebtoken": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", @@ -4103,6 +4618,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -4159,6 +4681,13 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true, + "license": "MIT" + }, "node_modules/log-symbols": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", @@ -4430,6 +4959,55 @@ "license": "MIT", "optional": true }, + "node_modules/node-sarif-builder": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/node-sarif-builder/-/node-sarif-builder-3.2.0.tgz", + "integrity": "sha512-kVIOdynrF2CRodHZeP/97Rh1syTUHBNiw17hUCIVhlhEsWlfJm19MuO56s4MdKbr22xWx6mzMnNAgXzVlIYM9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/sarif": "^2.1.7", + "fs-extra": "^11.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/normalize-package-data": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data/node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -4661,6 +5239,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-map": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz", + "integrity": "sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", @@ -4688,6 +5279,24 @@ "node": ">=6" } }, + "node_modules/parse-json": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", + "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "index-to-position": "^1.1.0", + "type-fest": "^4.39.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/parse-semver": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/parse-semver/-/parse-semver-1.1.1.tgz", @@ -4795,6 +5404,19 @@ "node": "20 || >=22" } }, + "node_modules/path-type": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz", + "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -4802,6 +5424,13 @@ "dev": true, "license": "MIT" }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, "node_modules/picomatch": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", @@ -4815,6 +5444,16 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/prebuild-install": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", @@ -4962,6 +5601,19 @@ "rc": "cli.js" } }, + "node_modules/rc-config-loader": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/rc-config-loader/-/rc-config-loader-4.1.3.tgz", + "integrity": "sha512-kD7FqML7l800i6pS6pvLyIE2ncbk9Du8Q0gp/4hMPhJU6ZxApkoLcGD8ZeqgiAlfwZ6BlETq6qqe+12DUL207w==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "js-yaml": "^4.1.0", + "json5": "^2.2.2", + "require-from-string": "^2.0.2" + } + }, "node_modules/rc/node_modules/strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", @@ -4986,6 +5638,39 @@ "node": ">=0.8" } }, + "node_modules/read-pkg": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz", + "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.3", + "normalize-package-data": "^6.0.0", + "parse-json": "^8.0.0", + "type-fest": "^4.6.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg/node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", @@ -5018,6 +5703,16 @@ "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -5146,6 +5841,28 @@ "dev": true, "license": "ISC" }, + "node_modules/secretlint": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/secretlint/-/secretlint-10.2.2.tgz", + "integrity": "sha512-xVpkeHV/aoWe4vP4TansF622nBEImzCY73y/0042DuJ29iKIaqgoJ8fGxre3rVSHHbxar4FdJobmTnLp9AU0eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/config-creator": "^10.2.2", + "@secretlint/formatter": "^10.2.2", + "@secretlint/node": "^10.2.2", + "@secretlint/profiler": "^10.2.2", + "debug": "^4.4.1", + "globby": "^14.1.0", + "read-pkg": "^9.0.1" + }, + "bin": { + "secretlint": "bin/secretlint.js" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/semver": { "version": "7.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", @@ -5326,6 +6043,80 @@ "simple-concat": "^1.0.0" } }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.22", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz", + "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/stdin-discarder": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz", @@ -5487,17 +6278,130 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/structured-source": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/structured-source/-/structured-source-4.0.0.tgz", + "integrity": "sha512-qGzRFNJDjFieQkl/sVOI2dUjHKRyL9dAJi2gCPGJLbJHBIkyOHxjuocpIEfbLioX+qSJpvbYdT49/YCdMznKxA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boundary": "^2.0.0" + } + }, "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^3.0.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=4" + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz", + "integrity": "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" + } + }, + "node_modules/table": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/table/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/table/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/table/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, "node_modules/tar-fs": { @@ -5587,6 +6491,46 @@ "node": ">= 6" } }, + "node_modules/terminal-link": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-4.0.0.tgz", + "integrity": "sha512-lk+vH+MccxNqgVqSnkMVKx4VLJfnLjDBGzH16JVZjKE2DoxP57s6/vt6JmXV5I3jBcfGrxNrYtC+mPtU7WJztA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "supports-hyperlinks": "^3.2.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/textextensions": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-6.11.0.tgz", + "integrity": "sha512-tXJwSr9355kFJI3lbCkPpUH5cP8/M0GGy2xLO34aZCjMXBaK3SoPnZwr/oWmo1FdCnELcs4npdCIOFtq9W3ruQ==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "editions": "^6.21.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, "node_modules/tmp": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.4.tgz", @@ -5667,6 +6611,19 @@ "node": ">= 0.8.0" } }, + "node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/typed-rest-client": { "version": "1.8.11", "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.11.tgz", @@ -5747,6 +6704,29 @@ "dev": true, "license": "MIT" }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -5781,6 +6761,30 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/version-range": { + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/version-range/-/version-range-4.15.0.tgz", + "integrity": "sha512-Ck0EJbAGxHwprkzFO966t4/5QkRuzh+/I1RxhLgUKKwEn+Cd8NwM60mE3AqBZg5gYODoXW0EFsQvbZjRlvdqbg==", + "dev": true, + "license": "Artistic-2.0", + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, "node_modules/vscode-jsonrpc": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", @@ -5928,42 +6932,6 @@ "node": ">=8" } }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 328eb509b4ee..4975ca858671 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -63,7 +63,7 @@ "@typescript-eslint/eslint-plugin": "^8.25.0", "@typescript-eslint/parser": "^8.25.0", "@vscode/test-electron": "^2.4.1", - "@vscode/vsce": "^3.2.2", + "@vscode/vsce": "^3.6.0", "esbuild": "^0.25.0", "eslint": "^9.21.0", "eslint-config-prettier": "^10.0.2", From 23e8a1e1398f5c17f4c64fe740479c2df31c9fd0 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Wed, 13 Aug 2025 19:49:59 +0000 Subject: [PATCH 061/682] Don't panic if unable to identify host in metrics --- src/tools/rust-analyzer/xtask/src/metrics.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/xtask/src/metrics.rs b/src/tools/rust-analyzer/xtask/src/metrics.rs index c9eea8710606..fd4b600b0347 100644 --- a/src/tools/rust-analyzer/xtask/src/metrics.rs +++ b/src/tools/rust-analyzer/xtask/src/metrics.rs @@ -160,7 +160,7 @@ struct Host { impl Metrics { fn new(sh: &Shell) -> anyhow::Result { - let host = Host::new(sh)?; + let host = Host::new(sh).unwrap_or_else(|_| Host::unknown()); let timestamp = SystemTime::now(); let revision = cmd!(sh, "git rev-parse HEAD").read()?; let perf_revision = "a584462e145a0c04760fd9391daefb4f6bd13a99".into(); @@ -191,9 +191,13 @@ impl Metrics { } impl Host { + fn unknown() -> Host { + Host { os: "unknown".into(), cpu: "unknown".into(), mem: "unknown".into() } + } + fn new(sh: &Shell) -> anyhow::Result { if cfg!(not(target_os = "linux")) { - return Ok(Host { os: "unknown".into(), cpu: "unknown".into(), mem: "unknown".into() }); + return Ok(Host::unknown()); } let os = read_field(sh, "/etc/os-release", "PRETTY_NAME=")?.trim_matches('"').to_owned(); From 8e5818df2eb7d77f3cbed5cbf0c292e8a770d760 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Wed, 13 Aug 2025 19:50:15 +0000 Subject: [PATCH 062/682] Shift vars when mapping Dyn --- .../rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index 3a3206bef38b..4696cf479c19 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -1147,6 +1147,9 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) } }), ); + + let p = shift_vars(interner, p, 1); + let where_clause = match p.skip_binder() { rustc_type_ir::ExistentialPredicate::Trait(trait_ref) => { let trait_ref = TraitRef::new( From 861f9122c899a8ec52c62ecb0a1435226dd5fc1a Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 14 Aug 2025 08:34:31 +0800 Subject: [PATCH 063/682] Fix indent for convert_match_to_let_else Example --- ``` //- minicore: option fn f() { let x$0 = match Some(()) { Some(it) => it, None => {//comment println!("nope"); return }, }; } ``` **Old output**: ```rust fn f() { let Some(x) = Some(()) else {//comment println!("nope"); return }; } ``` **This PR output**: ```rust fn f() { let Some(x) = Some(()) else {//comment println!("nope"); return }; } ``` --- .../src/handlers/convert_match_to_let_else.rs | 64 +++++++++++++++++-- 1 file changed, 58 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs index 9126e869b9a0..1a6d176c9054 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs @@ -1,7 +1,7 @@ use ide_db::defs::{Definition, NameRefClass}; use syntax::{ AstNode, SyntaxNode, - ast::{self, HasName, Name, syntax_factory::SyntaxFactory}, + ast::{self, HasName, Name, edit::AstNodeEdit, syntax_factory::SyntaxFactory}, syntax_editor::SyntaxEditor, }; @@ -45,7 +45,7 @@ pub(crate) fn convert_match_to_let_else(acc: &mut Assists, ctx: &AssistContext<' return None; } - let diverging_arm_expr = match diverging_arm.expr()? { + let diverging_arm_expr = match diverging_arm.expr()?.dedent(1.into()) { ast::Expr::BlockExpr(block) if block.modifier().is_none() && block.label().is_none() => { block.to_string() } @@ -150,7 +150,12 @@ fn rename_variable(pat: &ast::Pat, extracted: &[Name], binding: ast::Pat) -> Syn } } editor.add_mappings(make.finish_with_mappings()); - editor.finish().new_root().clone() + let new_node = editor.finish().new_root().clone(); + if let Some(pat) = ast::Pat::cast(new_node.clone()) { + pat.dedent(1.into()).syntax().clone() + } else { + new_node + } } #[cfg(test)] @@ -209,6 +214,53 @@ fn foo(opt: Option) -> Result { ); } + #[test] + fn indent_level() { + check_assist( + convert_match_to_let_else, + r#" +//- minicore: option +enum Foo { + A(u32), + B(u32), + C(String), +} + +fn foo(opt: Option) -> Result { + let mut state = 2; + let va$0lue = match opt { + Some( + Foo::A(it) + | Foo::B(it) + ) => it, + _ => { + state = 3; + return Err(()) + }, + }; +} + "#, + r#" +enum Foo { + A(u32), + B(u32), + C(String), +} + +fn foo(opt: Option) -> Result { + let mut state = 2; + let Some( + Foo::A(value) + | Foo::B(value) + ) = opt else { + state = 3; + return Err(()) + }; +} + "#, + ); + } + #[test] fn should_not_be_applicable_if_extracting_arm_is_not_an_identity_expr() { cov_mark::check_count!(extracting_arm_is_not_an_identity_expr, 2); @@ -489,9 +541,9 @@ fn f() { r#" fn f() { let Some(x) = Some(()) else {//comment - println!("nope"); - return - }; + println!("nope"); + return + }; } "#, ); From c78177b7f93c9b691a9b8365eaba9c8d11df4cd0 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 14 Aug 2025 10:05:11 +0800 Subject: [PATCH 064/682] Add guard to let-chain for replace_match_with_if_let ```rust fn main() { match$0 Some(0) { Some(n) if n % 2 == 0 && n != 6 => (), _ => code(), } } ``` -> ```rust fn main() { if let Some(n) = Some(0) && n % 2 == 0 && n != 6 { () } else { code() } } --- .../src/handlers/replace_if_let_with_match.rs | 64 +++++++++++++++---- 1 file changed, 52 insertions(+), 12 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs index 15d3db5e749f..dd244375dc91 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs @@ -8,7 +8,7 @@ use ide_db::{ ty_filter::TryEnum, }; use syntax::{ - AstNode, T, TextRange, + AstNode, Edition, T, TextRange, ast::{self, HasName, edit::IndentLevel, edit_in_place::Indent, syntax_factory::SyntaxFactory}, }; @@ -187,7 +187,7 @@ fn make_else_arm( // Assist: replace_match_with_if_let // -// Replaces a binary `match` with a wildcard pattern and no guards with an `if let` expression. +// Replaces a binary `match` with a wildcard pattern with an `if let` expression. // // ``` // enum Action { Move { distance: u32 }, Stop } @@ -225,18 +225,24 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<' let mut arms = match_arm_list.arms(); let (first_arm, second_arm) = (arms.next()?, arms.next()?); - if arms.next().is_some() || first_arm.guard().is_some() || second_arm.guard().is_some() { + if arms.next().is_some() || second_arm.guard().is_some() { + return None; + } + if first_arm.guard().is_some() && ctx.edition() < Edition::Edition2024 { return None; } - let (if_let_pat, then_expr, else_expr) = pick_pattern_and_expr_order( + let (if_let_pat, guard, then_expr, else_expr) = pick_pattern_and_expr_order( &ctx.sema, first_arm.pat()?, second_arm.pat()?, first_arm.expr()?, second_arm.expr()?, + first_arm.guard(), + second_arm.guard(), )?; let scrutinee = match_expr.expr()?; + let guard = guard.and_then(|it| it.condition()); let let_ = match &if_let_pat { ast::Pat::LiteralPat(p) @@ -277,6 +283,11 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<' } _ => make.expr_let(if_let_pat, scrutinee).into(), }; + let condition = if let Some(guard) = guard { + make.expr_bin(condition, ast::BinaryOp::LogicOp(ast::LogicOp::And), guard).into() + } else { + condition + }; let then_expr = then_expr.clone_for_update(); then_expr.reindent_to(IndentLevel::single()); let then_block = make_block_expr(then_expr); @@ -303,18 +314,23 @@ fn pick_pattern_and_expr_order( pat2: ast::Pat, expr: ast::Expr, expr2: ast::Expr, -) -> Option<(ast::Pat, ast::Expr, ast::Expr)> { + guard: Option, + guard2: Option, +) -> Option<(ast::Pat, Option, ast::Expr, ast::Expr)> { + if guard.is_some() && guard2.is_some() { + return None; + } let res = match (pat, pat2) { (ast::Pat::WildcardPat(_), _) => return None, - (pat, ast::Pat::WildcardPat(_)) => (pat, expr, expr2), - (pat, _) if is_empty_expr(&expr2) => (pat, expr, expr2), - (_, pat) if is_empty_expr(&expr) => (pat, expr2, expr), + (pat, ast::Pat::WildcardPat(_)) => (pat, guard, expr, expr2), + (pat, _) if is_empty_expr(&expr2) => (pat, guard, expr, expr2), + (_, pat) if is_empty_expr(&expr) => (pat, guard, expr2, expr), (pat, pat2) => match (binds_name(sema, &pat), binds_name(sema, &pat2)) { (true, true) => return None, - (true, false) => (pat, expr, expr2), - (false, true) => (pat2, expr2, expr), - _ if is_sad_pat(sema, &pat) => (pat2, expr2, expr), - (false, false) => (pat, expr, expr2), + (true, false) => (pat, guard, expr, expr2), + (false, true) => (pat2, guard2, expr2, expr), + _ if is_sad_pat(sema, &pat) => (pat2, guard2, expr2, expr), + (false, false) => (pat, guard, expr, expr2), }, }; Some(res) @@ -1849,6 +1865,30 @@ fn main() { code() } } +"#, + ) + } + + #[test] + fn test_replace_match_with_if_let_chain() { + check_assist( + replace_match_with_if_let, + r#" +fn main() { + match$0 Some(0) { + Some(n) if n % 2 == 0 && n != 6 => (), + _ => code(), + } +} +"#, + r#" +fn main() { + if let Some(n) = Some(0) && n % 2 == 0 && n != 6 { + () + } else { + code() + } +} "#, ) } From 6772f18557e24d9d1f8572839d0ef3cde3b0ac44 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 14 Aug 2025 14:33:33 +0200 Subject: [PATCH 065/682] Track diagnostic generations per package --- .../crates/rust-analyzer/src/diagnostics.rs | 56 +++++++++++-------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs index 30f530100f9d..ee50237c405e 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs @@ -26,17 +26,15 @@ pub struct DiagnosticsMapConfig { pub(crate) type DiagnosticsGeneration = usize; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub(crate) struct WorkspaceFlycheckDiagnostic { - pub(crate) generation: DiagnosticsGeneration, - pub(crate) per_package: - FxHashMap>, FxHashMap>>, + pub(crate) per_package: FxHashMap>, PackageFlycheckDiagnostic>, } -impl WorkspaceFlycheckDiagnostic { - fn new(generation: DiagnosticsGeneration) -> Self { - WorkspaceFlycheckDiagnostic { generation, per_package: Default::default() } - } +#[derive(Debug, Clone)] +pub(crate) struct PackageFlycheckDiagnostic { + generation: DiagnosticsGeneration, + per_file: FxHashMap>, } #[derive(Debug, Default, Clone)] @@ -68,7 +66,7 @@ impl DiagnosticCollection { let Some(check) = self.check.get_mut(flycheck_id) else { return; }; - self.changes.extend(check.per_package.drain().flat_map(|(_, v)| v.into_keys())); + self.changes.extend(check.per_package.drain().flat_map(|(_, v)| v.per_file.into_keys())); if let Some(fixes) = Arc::make_mut(&mut self.check_fixes).get_mut(flycheck_id) { fixes.clear(); } @@ -79,7 +77,7 @@ impl DiagnosticCollection { self.changes.extend( self.check .iter_mut() - .flat_map(|it| it.per_package.drain().flat_map(|(_, v)| v.into_keys())), + .flat_map(|it| it.per_package.drain().flat_map(|(_, v)| v.per_file.into_keys())), ) } @@ -93,7 +91,7 @@ impl DiagnosticCollection { }; let package_id = Some(package_id); if let Some(checks) = check.per_package.remove(&package_id) { - self.changes.extend(checks.into_keys()); + self.changes.extend(checks.per_file.into_keys()); } if let Some(fixes) = Arc::make_mut(&mut self.check_fixes).get_mut(flycheck_id) { fixes.remove(&package_id); @@ -105,8 +103,20 @@ impl DiagnosticCollection { flycheck_id: usize, generation: DiagnosticsGeneration, ) { - if self.check.get(flycheck_id).is_some_and(|it| it.generation < generation) { - self.clear_check(flycheck_id); + if let Some(flycheck) = self.check.get_mut(flycheck_id) { + let mut packages = vec![]; + self.changes.extend( + flycheck + .per_package + .extract_if(|_, v| v.generation < generation) + .inspect(|(package_id, _)| packages.push(package_id.clone())) + .flat_map(|(_, v)| v.per_file.into_keys()), + ); + if let Some(fixes) = Arc::make_mut(&mut self.check_fixes).get_mut(flycheck_id) { + for package in packages { + fixes.remove(&package); + } + } } } @@ -126,21 +136,19 @@ impl DiagnosticCollection { fix: Option>, ) { if self.check.len() <= flycheck_id { - self.check - .resize_with(flycheck_id + 1, || WorkspaceFlycheckDiagnostic::new(generation)); + self.check.resize_with(flycheck_id + 1, WorkspaceFlycheckDiagnostic::default); } + let check = &mut self.check[flycheck_id]; + let package = check.per_package.entry(package_id.clone()).or_insert_with(|| { + PackageFlycheckDiagnostic { generation, per_file: FxHashMap::default() } + }); // Getting message from old generation. Might happen in restarting checks. - if self.check[flycheck_id].generation > generation { + if package.generation > generation { return; } - self.check[flycheck_id].generation = generation; - let diagnostics = self.check[flycheck_id] - .per_package - .entry(package_id.clone()) - .or_default() - .entry(file_id) - .or_default(); + package.generation = generation; + let diagnostics = package.per_file.entry(file_id).or_default(); for existing_diagnostic in diagnostics.iter() { if are_diagnostics_equal(existing_diagnostic, &diagnostic) { return; @@ -210,7 +218,7 @@ impl DiagnosticCollection { .check .iter() .flat_map(|it| it.per_package.values()) - .filter_map(move |it| it.get(&file_id)) + .filter_map(move |it| it.per_file.get(&file_id)) .flatten(); native_syntax.chain(native_semantic).chain(check) } From 58ec13d79389f3886e71d43a0fcf501b84c1a40f Mon Sep 17 00:00:00 2001 From: Ralf Anton Beier Date: Mon, 11 Aug 2025 06:16:34 +0200 Subject: [PATCH 066/682] feat: hint at unterminated strings in unknown prefix errors When encountering 'unknown literal prefix' errors, check for unbalanced quotes in recent code and suggest checking for unterminated string literals. --- .../crates/parser/src/lexed_str.rs | 30 ++++++++++++++++++- .../unterminated_string_unknown_prefix.rast | 15 ++++++++++ .../err/unterminated_string_unknown_prefix.rs | 5 ++++ 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unterminated_string_unknown_prefix.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unterminated_string_unknown_prefix.rs diff --git a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs index 8fff1c3db748..dcf397142cac 100644 --- a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs +++ b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs @@ -149,6 +149,24 @@ impl<'a> Converter<'a> { } } + /// Check for likely unterminated string by analyzing STRING token content + fn has_likely_unterminated_string(&self) -> bool { + let Some(last_idx) = self.res.kind.len().checked_sub(1) else { return false }; + + for i in (0..=last_idx).rev().take(5) { + if self.res.kind[i] == STRING { + let start = self.res.start[i] as usize; + let end = self.res.start.get(i + 1).map(|&s| s as usize).unwrap_or(self.offset); + let content = &self.res.text[start..end]; + + if content.contains('(') && (content.contains("//") || content.contains(";\n")) { + return true; + } + } + } + false + } + fn finalize_with_eof(mut self) -> LexedStr<'a> { self.res.push(EOF, self.offset); self.res @@ -267,7 +285,17 @@ impl<'a> Converter<'a> { rustc_lexer::TokenKind::Unknown => ERROR, rustc_lexer::TokenKind::UnknownPrefix if token_text == "builtin" => IDENT, rustc_lexer::TokenKind::UnknownPrefix => { - errors.push("unknown literal prefix".into()); + let has_unterminated = self.has_likely_unterminated_string(); + + let error_msg = if has_unterminated { + format!( + "unknown literal prefix `{}` (note: check for unterminated string literal)", + token_text + ) + } else { + "unknown literal prefix".to_owned() + }; + errors.push(error_msg); IDENT } rustc_lexer::TokenKind::Eof => EOF, diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unterminated_string_unknown_prefix.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unterminated_string_unknown_prefix.rast new file mode 100644 index 000000000000..f7f24ca3f810 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unterminated_string_unknown_prefix.rast @@ -0,0 +1,15 @@ +FN_KW "fn" +WHITESPACE " " +IDENT "main" +L_PAREN "(" +R_PAREN ")" +WHITESPACE " " +L_CURLY "{" +WHITESPACE "\n " +IDENT "hello" +L_PAREN "(" +STRING "\"world);\n // a bunch of code was here\n env(\"FLAGS" +STRING "\", \"" +MINUS "-" +IDENT "help" error: unknown literal prefix `help` (note: check for unterminated string literal) +STRING "\")\n}" error: Missing trailing `"` symbol to terminate the string literal diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unterminated_string_unknown_prefix.rs b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unterminated_string_unknown_prefix.rs new file mode 100644 index 000000000000..338b9582605b --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unterminated_string_unknown_prefix.rs @@ -0,0 +1,5 @@ +fn main() { + hello("world); + // a bunch of code was here + env("FLAGS", "-help") +} \ No newline at end of file From cc3c5cfb2e46c04778ebba4b0c71e87cb7f7cef7 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Thu, 14 Aug 2025 17:04:54 +0000 Subject: [PATCH 067/682] Add test for webrender-2022 metrics --- .../crates/hir-ty/src/tests/traits.rs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index 2e4346a86973..9d72105624a0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -4994,3 +4994,35 @@ fn main() { "#]], ); } + +#[test] +fn trait_object_binders() { + check_infer( + r#" +//- minicore: iterator, dispatch_from_dyn +fn main() { + struct Box(*const T); + impl Iterator for Box { + type Item = I::Item; + fn next(&mut self) -> Option { + loop {} + } + } + let iter: Box + 'static> = loop {}; + let _ = iter.into_iter(); +}"#, + expect![[r#" + 10..313 '{ ...r(); }': () + 223..227 'iter': Box + 'static> + 273..280 'loop {}': ! + 278..280 '{}': () + 290..291 '_': Box + 'static> + 294..298 'iter': Box + 'static> + 294..310 'iter.i...iter()': Box + 'static> + 152..156 'self': &'? mut Box + 177..208 '{ ... }': Option> + 191..198 'loop {}': ! + 196..198 '{}': () + "#]], + ); +} From 06336efca9cd5851a72ff43db3641485e0e06a79 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Fri, 15 Aug 2025 03:07:43 +0000 Subject: [PATCH 068/682] add comment --- .../rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index 4696cf479c19..20cd8626f299 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -1148,6 +1148,13 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) }), ); + // Rust and chalk have slightly different + // representation for trait objects. + // + // Chalk uses `for for<'a> T0: Trait<'a>` while rustc + // uses `ExistentialPredicate`s, which do not have a self ty. + // We need to shift escaping bound vars by 1 to accommodate + // the newly introduced `for` binder. let p = shift_vars(interner, p, 1); let where_clause = match p.skip_binder() { From b38dd2acb598adb14bd1f08f39090909be13498b Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Fri, 15 Aug 2025 20:15:21 +0300 Subject: [PATCH 069/682] Use a more specific error message when talking about the server logs --- src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index f8f29eee126e..a8a54930c6e5 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -152,7 +152,9 @@ impl GlobalState { if self.fetch_build_data_error().is_err() { status.health |= lsp_ext::Health::Warning; message.push_str("Failed to run build scripts of some packages.\n\n"); - message.push_str("Please refer to the logs for more details on the errors."); + message.push_str( + "Please refer to the language server logs for more details on the errors.", + ); } if let Some(err) = &self.config_errors { status.health |= lsp_ext::Health::Warning; From b9861fb75dda32fb349894fe8c73ab68d0c2f5f7 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Sun, 17 Aug 2025 16:09:52 +0900 Subject: [PATCH 070/682] fix: Make lang items query properly filter out overwritten/excluded sysroots --- .../rust-analyzer/crates/base-db/src/lib.rs | 2 + .../crates/hir-def/src/lang_item.rs | 16 +- .../crates/hir-def/src/nameres.rs | 4 + .../crates/hir-def/src/nameres/collector.rs | 19 ++- .../crates/hir-ty/src/tests/regression.rs | 51 +++++++ .../rust-analyzer/crates/ide-db/src/lib.rs | 6 +- .../crates/test-fixture/src/lib.rs | 138 +++++++++++------- 7 files changed, 167 insertions(+), 69 deletions(-) diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs index b8eadb608fea..dbf949c470c4 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs @@ -30,6 +30,8 @@ use triomphe::Arc; pub use vfs::{AnchoredPath, AnchoredPathBuf, FileId, VfsPath, file_set::FileSet}; pub type FxIndexSet = indexmap::IndexSet; +pub type FxIndexMap = + indexmap::IndexMap>; #[macro_export] macro_rules! impl_intern_key { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs index a0be69cb2f96..600f20677001 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs @@ -12,7 +12,7 @@ use crate::{ StaticId, StructId, TraitId, TypeAliasId, UnionId, db::DefDatabase, expr_store::path::Path, - nameres::{assoc::TraitItems, crate_def_map}, + nameres::{assoc::TraitItems, crate_def_map, crate_local_def_map}, }; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -170,7 +170,19 @@ pub fn lang_item( { return Some(target); } - start_crate.data(db).dependencies.iter().find_map(|dep| lang_item(db, dep.crate_id, item)) + + // Our `CrateGraph` eagerly inserts sysroot dependencies like `core` or `std` into dependencies + // even if the target crate has `#![no_std]`, `#![no_core]` or shadowed sysroot dependencies + // like `dependencies.std.path = ".."`. So we use `extern_prelude()` instead of + // `CrateData.dependencies` here, which has already come through such sysroot complexities + // while nameres. + // + // See https://github.com/rust-lang/rust-analyzer/pull/20475 for details. + crate_local_def_map(db, start_crate).local(db).extern_prelude().find_map(|(_, (krate, _))| { + // Some crates declares themselves as extern crate like `extern crate self as core`. + // Ignore these to prevent cycles. + if krate.krate == start_crate { None } else { lang_item(db, krate.krate, item) } + }) } #[derive(Default, Debug, Clone, PartialEq, Eq)] diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs index 5030585147de..bf6fc15b7d19 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -545,6 +545,10 @@ impl DefMap { self.data.no_std || self.data.no_core } + pub fn is_no_core(&self) -> bool { + self.data.no_core + } + pub fn fn_as_proc_macro(&self, id: FunctionId) -> Option { self.data.fn_proc_macro_mapping.get(&id).copied() } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index 07e61718222b..0f84728dcb4a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -27,10 +27,11 @@ use triomphe::Arc; use crate::{ AdtId, AssocItemId, AstId, AstIdWithPath, ConstLoc, CrateRootModuleId, EnumLoc, ExternBlockLoc, - ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, ImplLoc, Intern, ItemContainerId, - LocalModuleId, Lookup, Macro2Id, Macro2Loc, MacroExpander, MacroId, MacroRulesId, - MacroRulesLoc, MacroRulesLocFlags, ModuleDefId, ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc, - StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro, UseId, UseLoc, + ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, FxIndexMap, ImplLoc, Intern, + ItemContainerId, LocalModuleId, Lookup, Macro2Id, Macro2Loc, MacroExpander, MacroId, + MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, ModuleDefId, ModuleId, ProcMacroId, + ProcMacroLoc, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro, UseId, + UseLoc, attr::Attrs, db::DefDatabase, item_scope::{GlobId, ImportId, ImportOrExternCrate, PerNsGlobImports}, @@ -69,7 +70,7 @@ pub(super) fn collect_defs( // populate external prelude and dependency list let mut deps = - FxHashMap::with_capacity_and_hasher(krate.dependencies.len(), Default::default()); + FxIndexMap::with_capacity_and_hasher(krate.dependencies.len(), Default::default()); for dep in &krate.dependencies { tracing::debug!("crate dep {:?} -> {:?}", dep.name, dep.crate_id); @@ -220,7 +221,7 @@ struct DefCollector<'db> { /// Set only in case of blocks. crate_local_def_map: Option<&'db LocalDefMap>, // The dependencies of the current crate, including optional deps like `test`. - deps: FxHashMap, + deps: FxIndexMap, glob_imports: FxHashMap>, unresolved_imports: Vec, indeterminate_imports: Vec<(ImportDirective, PerNs)>, @@ -332,7 +333,9 @@ impl<'db> DefCollector<'db> { let skip = dep.is_sysroot() && match dep.crate_id.data(self.db).origin { CrateOrigin::Lang(LangCrateOrigin::Core) => crate_data.no_core, - CrateOrigin::Lang(LangCrateOrigin::Std) => crate_data.no_std, + CrateOrigin::Lang(LangCrateOrigin::Std) => { + crate_data.no_core || crate_data.no_std + } _ => false, }; if skip { @@ -2550,7 +2553,7 @@ mod tests { def_map, local_def_map: LocalDefMap::default(), crate_local_def_map: None, - deps: FxHashMap::default(), + deps: FxIndexMap::default(), glob_imports: FxHashMap::default(), unresolved_imports: Vec::new(), indeterminate_imports: Vec::new(), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index 25061e1dbdb9..212fec4a4e4f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -2388,3 +2388,54 @@ pub trait Destruct {} "#, ); } + +#[test] +fn no_duplicated_lang_item_metadata() { + check_types( + r#" +//- minicore: pointee +//- /main.rs crate:main deps:std,core +use std::AtomicPtr; +use std::null_mut; + +fn main() { + let x: AtomicPtr<()> = AtomicPtr::new(null_mut()); + //^ AtomicPtr<()> +} + +//- /lib.rs crate:r#std deps:core +#![no_std] +pub use core::*; + +//- /lib.rs crate:r#core +#![no_core] + +#[lang = "pointee_trait"] +pub trait Pointee { + #[lang = "metadata_type"] + type Metadata; +} + +pub struct AtomicPtr(T); + +impl AtomicPtr { + pub fn new(p: *mut T) -> AtomicPtr { + loop {} + } +} + +#[lang = "pointee_sized"] +pub trait PointeeSized {} +#[lang = "meta_sized"] +pub trait MetaSized: PointeeSized {} +#[lang = "sized"] +pub trait Sized: MetaSized {} + +pub trait Thin = Pointee + PointeeSized; + +pub fn null_mut() -> *mut T { + loop {} +} +"#, + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index 1f074de4cd7d..9d2474d91da6 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -66,13 +66,9 @@ pub use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; pub use ::line_index; /// `base_db` is normally also needed in places where `ide_db` is used, so this re-export is for convenience. -pub use base_db; +pub use base_db::{self, FxIndexMap, FxIndexSet}; pub use span::{self, FileId}; -pub type FxIndexSet = indexmap::IndexSet>; -pub type FxIndexMap = - indexmap::IndexMap>; - pub type FilePosition = FilePositionWrapper; pub type FileRange = FileRangeWrapper; diff --git a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs index 4413d2f222c1..57fca70547bf 100644 --- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs +++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs @@ -3,8 +3,8 @@ use std::{any::TypeId, mem, str::FromStr, sync}; use base_db::{ Crate, CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin, CrateWorkspaceData, - DependencyBuilder, Env, FileChange, FileSet, LangCrateOrigin, SourceDatabase, SourceRoot, - Version, VfsPath, salsa, + DependencyBuilder, Env, FileChange, FileSet, FxIndexMap, LangCrateOrigin, SourceDatabase, + SourceRoot, Version, VfsPath, salsa, }; use cfg::CfgOptions; use hir_expand::{ @@ -20,7 +20,6 @@ use hir_expand::{ }; use intern::{Symbol, sym}; use paths::AbsPathBuf; -use rustc_hash::FxHashMap; use span::{Edition, FileId, Span}; use stdx::itertools::Itertools; use test_utils::{ @@ -147,7 +146,7 @@ impl ChangeFixture { let mut files = Vec::new(); let mut crate_graph = CrateGraphBuilder::default(); - let mut crates = FxHashMap::default(); + let mut crates = FxIndexMap::default(); let mut crate_deps = Vec::new(); let mut default_crate_root: Option = None; let mut default_edition = Edition::CURRENT; @@ -249,37 +248,7 @@ impl ChangeFixture { file_id = FileId::from_raw(file_id.index() + 1); } - if crates.is_empty() { - let crate_root = default_crate_root - .expect("missing default crate root, specify a main.rs or lib.rs"); - crate_graph.add_crate_root( - crate_root, - default_edition, - Some(CrateName::new("ra_test_fixture").unwrap().into()), - None, - default_cfg.clone(), - Some(default_cfg), - default_env, - CrateOrigin::Local { repo: None, name: None }, - false, - proc_macro_cwd.clone(), - crate_ws_data.clone(), - ); - } else { - for (from, to, prelude) in crate_deps { - let from_id = crates[&from]; - let to_id = crates[&to]; - let sysroot = crate_graph[to_id].basic.origin.is_lang(); - crate_graph - .add_dep( - from_id, - DependencyBuilder::with_prelude(to.clone(), to_id, prelude, sysroot), - ) - .unwrap(); - } - } - - if let Some(mini_core) = mini_core { + let mini_core = mini_core.map(|mini_core| { let core_file = file_id; file_id = FileId::from_raw(file_id.index() + 1); @@ -289,8 +258,6 @@ impl ChangeFixture { source_change.change_file(core_file, Some(mini_core.source_code())); - let all_crates = crate_graph.iter().collect::>(); - let core_crate = crate_graph.add_crate_root( core_file, Edition::CURRENT, @@ -308,16 +275,58 @@ impl ChangeFixture { crate_ws_data.clone(), ); - for krate in all_crates { + ( + move || { + DependencyBuilder::with_prelude( + CrateName::new("core").unwrap(), + core_crate, + true, + true, + ) + }, + core_crate, + ) + }); + + if crates.is_empty() { + let crate_root = default_crate_root + .expect("missing default crate root, specify a main.rs or lib.rs"); + let root = crate_graph.add_crate_root( + crate_root, + default_edition, + Some(CrateName::new("ra_test_fixture").unwrap().into()), + None, + default_cfg.clone(), + Some(default_cfg), + default_env, + CrateOrigin::Local { repo: None, name: None }, + false, + proc_macro_cwd.clone(), + crate_ws_data.clone(), + ); + if let Some((mini_core, _)) = mini_core { + crate_graph.add_dep(root, mini_core()).unwrap(); + } + } else { + // Insert minicore first to match with `project-model::workspace` + if let Some((mini_core, core_crate)) = mini_core { + let all_crates = crate_graph.iter().collect::>(); + for krate in all_crates { + if krate == core_crate { + continue; + } + crate_graph.add_dep(krate, mini_core()).unwrap(); + } + } + + for (from, to, prelude) in crate_deps { + let from_id = crates[&from]; + let to_id = crates[&to]; + let sysroot = crate_graph[to_id].basic.origin.is_lang(); crate_graph .add_dep( - krate, - DependencyBuilder::with_prelude( - CrateName::new("core").unwrap(), - core_crate, - true, - true, - ), + from_id, + DependencyBuilder::with_prelude(to.clone(), to_id, prelude, sysroot), ) .unwrap(); } @@ -627,11 +636,23 @@ impl FileMeta { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum ForceNoneLangOrigin { + Yes, + No, +} + fn parse_crate( crate_str: String, current_source_root_kind: SourceRootKind, explicit_non_workspace_member: bool, ) -> (String, CrateOrigin, Option) { + let (crate_str, force_non_lang_origin) = if let Some(s) = crate_str.strip_prefix("r#") { + (s.to_owned(), ForceNoneLangOrigin::Yes) + } else { + (crate_str, ForceNoneLangOrigin::No) + }; + // syntax: // "my_awesome_crate" // "my_awesome_crate@0.0.1,http://example.com" @@ -646,16 +667,25 @@ fn parse_crate( let non_workspace_member = explicit_non_workspace_member || matches!(current_source_root_kind, SourceRootKind::Library); - let origin = match LangCrateOrigin::from(&*name) { - LangCrateOrigin::Other => { - let name = Symbol::intern(&name); - if non_workspace_member { - CrateOrigin::Library { repo, name } - } else { - CrateOrigin::Local { repo, name: Some(name) } - } + let origin = if force_non_lang_origin == ForceNoneLangOrigin::Yes { + let name = Symbol::intern(&name); + if non_workspace_member { + CrateOrigin::Library { repo, name } + } else { + CrateOrigin::Local { repo, name: Some(name) } + } + } else { + match LangCrateOrigin::from(&*name) { + LangCrateOrigin::Other => { + let name = Symbol::intern(&name); + if non_workspace_member { + CrateOrigin::Library { repo, name } + } else { + CrateOrigin::Local { repo, name: Some(name) } + } + } + origin => CrateOrigin::Lang(origin), } - origin => CrateOrigin::Lang(origin), }; (name, origin, version) From 9c7ef48a7bedda1847acb0bfdfe7271e2268c2b7 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Fri, 8 Aug 2025 02:06:01 +0000 Subject: [PATCH 071/682] Convert some of dyn_compatibility to next-solver and remove generic_predicates_without_parent_query --- .../rust-analyzer/crates/hir-ty/src/db.rs | 10 -- .../crates/hir-ty/src/dyn_compatibility.rs | 104 +++++++++++++----- .../rust-analyzer/crates/hir-ty/src/lower.rs | 16 --- src/tools/rust-analyzer/crates/hir/src/lib.rs | 2 +- 4 files changed, 79 insertions(+), 53 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 6e24aea76d44..7a5daac69922 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -179,16 +179,6 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { #[salsa::invoke(crate::lower::generic_predicates_query)] fn generic_predicates(&self, def: GenericDefId) -> GenericPredicates; - #[salsa::invoke(crate::lower::generic_predicates_without_parent_with_diagnostics_query)] - fn generic_predicates_without_parent_with_diagnostics( - &self, - def: GenericDefId, - ) -> (GenericPredicates, Diagnostics); - - #[salsa::invoke(crate::lower::generic_predicates_without_parent_query)] - #[salsa::transparent] - fn generic_predicates_without_parent(&self, def: GenericDefId) -> GenericPredicates; - #[salsa::invoke(crate::lower::trait_environment_for_body_query)] #[salsa::transparent] fn trait_environment_for_body(&self, def: DefWithBodyId) -> Arc; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index 2d21947ec60f..f571b64464d9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -13,6 +13,9 @@ use hir_def::{ TypeAliasId, lang_item::LangItem, signatures::TraitFlags, }; use rustc_hash::FxHashSet; +use rustc_type_ir::{ + AliasTyKind, ClauseKind, PredicatePolarity, TypeSuperVisitable as _, inherent::IntoKind, +}; use smallvec::SmallVec; use crate::{ @@ -21,6 +24,7 @@ use crate::{ db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, generics::{generics, trait_self_param_idx}, + next_solver::{DbInterner, SolverDefId, TraitPredicate}, to_chalk_trait_id, utils::elaborate_clause_supertraits, }; @@ -319,6 +323,61 @@ fn contains_illegal_self_type_reference>( t.visit_with(visitor.as_dyn(), outer_binder).is_break() } +fn contains_illegal_self_type_reference_ns< + 'db, + T: rustc_type_ir::TypeVisitable>, +>( + db: &'db dyn HirDatabase, + trait_: TraitId, + t: &T, + allow_self_projection: AllowSelfProjection, +) -> bool { + struct IllegalSelfTypeVisitor<'db> { + db: &'db dyn HirDatabase, + trait_: TraitId, + super_traits: Option>, + allow_self_projection: AllowSelfProjection, + } + impl<'db> rustc_type_ir::TypeVisitor> for IllegalSelfTypeVisitor<'db> { + type Result = ControlFlow<()>; + + fn visit_ty( + &mut self, + ty: as rustc_type_ir::Interner>::Ty, + ) -> Self::Result { + match ty.kind() { + rustc_type_ir::TyKind::Param(param) if param.index == 0 => ControlFlow::Break(()), + rustc_type_ir::TyKind::Param(_) => ControlFlow::Continue(()), + rustc_type_ir::TyKind::Alias(AliasTyKind::Projection, proj) => match self + .allow_self_projection + { + AllowSelfProjection::Yes => { + let trait_ = proj.trait_def_id(DbInterner::new_with(self.db, None, None)); + let trait_ = match trait_ { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + if self.super_traits.is_none() { + self.super_traits = Some(all_super_traits(self.db, self.trait_)); + } + if self.super_traits.as_ref().is_some_and(|s| s.contains(&trait_)) { + ControlFlow::Continue(()) + } else { + ty.super_visit_with(self) + } + } + AllowSelfProjection::No => ty.super_visit_with(self), + }, + _ => ty.super_visit_with(self), + } + } + } + + let mut visitor = + IllegalSelfTypeVisitor { db, trait_, super_traits: None, allow_self_projection }; + t.visit_with(&mut visitor).is_break() +} + fn dyn_compatibility_violation_for_assoc_item( db: &dyn HirDatabase, trait_: TraitId, @@ -415,40 +474,33 @@ where cb(MethodViolationCode::UndispatchableReceiver)?; } - let predicates = &*db.generic_predicates_without_parent(func.into()); - let trait_self_idx = trait_self_param_idx(db, func.into()); + let predicates = &*db.generic_predicates_without_parent_ns(func.into()); for pred in predicates { - let pred = pred.skip_binders().skip_binders(); + let pred = pred.kind().skip_binder(); - if matches!(pred, WhereClause::TypeOutlives(_)) { + if matches!(pred, ClauseKind::TypeOutlives(_)) { continue; } // Allow `impl AutoTrait` predicates - if let WhereClause::Implemented(TraitRef { trait_id, substitution }) = pred { - let trait_data = db.trait_signature(from_chalk_trait_id(*trait_id)); - if trait_data.flags.contains(TraitFlags::AUTO) - && substitution - .as_slice(Interner) - .first() - .and_then(|arg| arg.ty(Interner)) - .and_then(|ty| ty.bound_var(Interner)) - .is_some_and(|b| { - b.debruijn == DebruijnIndex::ONE && Some(b.index) == trait_self_idx - }) - { - continue; - } + let interner = DbInterner::new_with(db, None, None); + if let ClauseKind::Trait(TraitPredicate { + trait_ref: pred_trait_ref, + polarity: PredicatePolarity::Positive, + }) = pred + && let SolverDefId::TraitId(trait_id) = pred_trait_ref.def_id + && let trait_data = db.trait_signature(trait_id) + && trait_data.flags.contains(TraitFlags::AUTO) + && pred_trait_ref.self_ty() + == crate::next_solver::Ty::new( + interner, + rustc_type_ir::TyKind::Param(crate::next_solver::ParamTy { index: 0 }), + ) + { + continue; } - if contains_illegal_self_type_reference( - db, - func.into(), - trait_, - pred, - DebruijnIndex::ONE, - AllowSelfProjection::Yes, - ) { + if contains_illegal_self_type_reference_ns(db, trait_, &pred, AllowSelfProjection::Yes) { cb(MethodViolationCode::WhereClauseReferencesSelf)?; break; } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 7b6b3c3f8181..065d2ea08407 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -1179,22 +1179,6 @@ pub(crate) fn generic_predicates_query( generic_predicates_filtered_by(db, def, |_, _| true).0 } -pub(crate) fn generic_predicates_without_parent_query( - db: &dyn HirDatabase, - def: GenericDefId, -) -> GenericPredicates { - db.generic_predicates_without_parent_with_diagnostics(def).0 -} - -/// Resolve the where clause(s) of an item with generics, -/// except the ones inherited from the parent -pub(crate) fn generic_predicates_without_parent_with_diagnostics_query( - db: &dyn HirDatabase, - def: GenericDefId, -) -> (GenericPredicates, Diagnostics) { - generic_predicates_filtered_by(db, def, |_, d| d == def) -} - /// Resolve the where clause(s) of an item with generics, /// with a given filter fn generic_predicates_filtered_by( diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 4eb50c1c31c5..11486ec8d669 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -3750,7 +3750,7 @@ impl GenericDef { push_ty_diagnostics( db, acc, - db.generic_predicates_without_parent_with_diagnostics(def).1, + db.generic_predicates_without_parent_with_diagnostics_ns(def).1, &source_map, ); for (param_id, param) in generics.iter_type_or_consts() { From a25a1e34eca61265a78593e5438062033b68f2ca Mon Sep 17 00:00:00 2001 From: jackh726 Date: Fri, 8 Aug 2025 21:10:47 +0000 Subject: [PATCH 072/682] Convert more of dyn_compatibility to next-solver --- .../crates/hir-ty/src/dyn_compatibility.rs | 251 +++++++++--------- 1 file changed, 122 insertions(+), 129 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index f571b64464d9..237cc34d7099 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -4,7 +4,6 @@ use std::ops::ControlFlow; use chalk_ir::{ DebruijnIndex, - cast::Cast, visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, }; use chalk_solve::rust_ir::InlineBound; @@ -12,20 +11,26 @@ use hir_def::{ AssocItemId, ConstId, CrateRootModuleId, FunctionId, GenericDefId, HasModule, TraitId, TypeAliasId, lang_item::LangItem, signatures::TraitFlags, }; +use intern::Symbol; use rustc_hash::FxHashSet; use rustc_type_ir::{ - AliasTyKind, ClauseKind, PredicatePolarity, TypeSuperVisitable as _, inherent::IntoKind, + AliasTyKind, ClauseKind, PredicatePolarity, TypeSuperVisitable as _, TypeVisitable as _, + Upcast, + inherent::{IntoKind, SliceLike}, }; use smallvec::SmallVec; use crate::{ - AliasEq, AliasTy, Binders, BoundVar, CallableSig, DomainGoal, GoalData, ImplTraitId, Interner, - OpaqueTyId, ProjectionTyExt, Substitution, TraitRef, Ty, TyKind, WhereClause, all_super_traits, - db::HirDatabase, + AliasEq, AliasTy, Binders, BoundVar, ImplTraitId, Interner, ProjectionTyExt, Ty, TyKind, + WhereClause, all_super_traits, + db::{HirDatabase, InternedOpaqueTyId}, from_assoc_type_id, from_chalk_trait_id, - generics::{generics, trait_self_param_idx}, - next_solver::{DbInterner, SolverDefId, TraitPredicate}, - to_chalk_trait_id, + generics::trait_self_param_idx, + next_solver::{ + Clauses, DbInterner, GenericArgs, ParamEnv, SolverDefId, TraitPredicate, TypingMode, + infer::DbInternerInferExt, mk_param, + }, + traits::next_trait_solve_in_ctxt, utils::elaborate_clause_supertraits, }; @@ -434,26 +439,17 @@ where cb(MethodViolationCode::AsyncFn)?; } - let sig = db.callable_item_signature(func.into()); - if sig.skip_binders().params().iter().skip(1).any(|ty| { - contains_illegal_self_type_reference( - db, - func.into(), - trait_, - ty, - DebruijnIndex::INNERMOST, - AllowSelfProjection::Yes, - ) + let sig = db.callable_item_signature_ns(func.into()); + if sig.skip_binder().inputs().iter().skip(1).any(|ty| { + contains_illegal_self_type_reference_ns(db, trait_, &ty, AllowSelfProjection::Yes) }) { cb(MethodViolationCode::ReferencesSelfInput)?; } - if contains_illegal_self_type_reference( + if contains_illegal_self_type_reference_ns( db, - func.into(), trait_, - sig.skip_binders().ret(), - DebruijnIndex::INNERMOST, + &sig.skip_binder().output(), AllowSelfProjection::Yes, ) { cb(MethodViolationCode::ReferencesSelfOutput)?; @@ -483,7 +479,7 @@ where } // Allow `impl AutoTrait` predicates - let interner = DbInterner::new_with(db, None, None); + let interner = DbInterner::new_with(db, Some(trait_.krate(db)), None); if let ClauseKind::Trait(TraitPredicate { trait_ref: pred_trait_ref, polarity: PredicatePolarity::Positive, @@ -509,34 +505,30 @@ where ControlFlow::Continue(()) } -fn receiver_is_dispatchable( +fn receiver_is_dispatchable<'db>( db: &dyn HirDatabase, trait_: TraitId, func: FunctionId, - sig: &Binders, + sig: &crate::next_solver::EarlyBinder< + 'db, + crate::next_solver::Binder<'db, rustc_type_ir::FnSig>>, + >, ) -> bool { - let Some(trait_self_idx) = trait_self_param_idx(db, func.into()) else { - return false; - }; + let sig = sig.instantiate_identity(); + + let interner: DbInterner<'_> = DbInterner::new_with(db, Some(trait_.krate(db)), None); + let self_param_ty = crate::next_solver::Ty::new( + interner, + rustc_type_ir::TyKind::Param(crate::next_solver::ParamTy { index: 0 }), + ); // `self: Self` can't be dispatched on, but this is already considered dyn-compatible // See rustc's comment on https://github.com/rust-lang/rust/blob/3f121b9461cce02a703a0e7e450568849dfaa074/compiler/rustc_trait_selection/src/traits/object_safety.rs#L433-L437 - if sig - .skip_binders() - .params() - .first() - .and_then(|receiver| receiver.bound_var(Interner)) - .is_some_and(|b| { - b == BoundVar { debruijn: DebruijnIndex::INNERMOST, index: trait_self_idx } - }) - { + if sig.inputs().iter().next().is_some_and(|p| p.skip_binder() == self_param_ty) { return true; } - let placeholder_subst = generics(db, func.into()).placeholder_subst(db); - - let substituted_sig = sig.clone().substitute(Interner, &placeholder_subst); - let Some(receiver_ty) = substituted_sig.params().first() else { + let Some(&receiver_ty) = sig.inputs().skip_binder().as_slice().first() else { return false; }; @@ -549,118 +541,119 @@ fn receiver_is_dispatchable( return false; }; - // Type `U` - let unsized_self_ty = - TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::U32)).intern(Interner); - // `Receiver[Self => U]` - let Some(unsized_receiver_ty) = receiver_for_self_ty(db, func, unsized_self_ty.clone()) else { + let meta_sized_did = LangItem::MetaSized.resolve_trait(db, krate); + let Some(meta_sized_did) = meta_sized_did else { return false; }; - let self_ty = placeholder_subst.as_slice(Interner)[trait_self_idx].assert_ty_ref(Interner); - let unsized_predicate = WhereClause::Implemented(TraitRef { - trait_id: to_chalk_trait_id(unsize_did), - substitution: Substitution::from_iter(Interner, [self_ty.clone(), unsized_self_ty.clone()]), - }); - let unsized_predicate = - Binders::empty(Interner, unsized_predicate.cast::(Interner)); - let trait_predicate = WhereClause::Implemented(TraitRef { - trait_id: to_chalk_trait_id(trait_), - substitution: Substitution::from_iter( - Interner, - std::iter::once(unsized_self_ty.cast(Interner)) - .chain(placeholder_subst.iter(Interner).skip(1).cloned()), - ), - }); - let trait_predicate = Binders::empty(Interner, trait_predicate.cast::(Interner)); + // Type `U` + let unsized_self_ty = crate::next_solver::Ty::new_param(interner, u32::MAX, Symbol::empty()); + // `Receiver[Self => U]` + let unsized_receiver_ty = receiver_for_self_ty(interner, func, receiver_ty, unsized_self_ty); - let generic_predicates = &*db.generic_predicates(func.into()); + let param_env = { + let generic_predicates = &*db.generic_predicates_ns(func.into()); - let goals = std::iter::once(unsized_predicate).chain(std::iter::once(trait_predicate)).chain( - generic_predicates.iter().map(|pred| { - pred.clone() - .substitute(Interner, &placeholder_subst) - .map(|g| g.cast::(Interner)) - }), + // Self: Unsize + let unsize_predicate = crate::next_solver::TraitRef::new( + interner, + SolverDefId::TraitId(unsize_did), + [self_param_ty, unsized_self_ty], + ); + + // U: Trait + let trait_def_id = SolverDefId::TraitId(trait_); + let args = GenericArgs::for_item(interner, trait_def_id, |name, index, kind, _| { + if index == 0 { unsized_self_ty.into() } else { mk_param(index, name, kind) } + }); + let trait_predicate = + crate::next_solver::TraitRef::new_from_args(interner, trait_def_id, args); + + let meta_sized_predicate = crate::next_solver::TraitRef::new( + interner, + SolverDefId::TraitId(meta_sized_did), + [unsized_self_ty], + ); + + ParamEnv { + clauses: Clauses::new_from_iter( + interner, + generic_predicates.iter().copied().chain([ + unsize_predicate.upcast(interner), + trait_predicate.upcast(interner), + meta_sized_predicate.upcast(interner), + ]), + ), + } + }; + + // Receiver: DispatchFromDyn U]> + let predicate = crate::next_solver::TraitRef::new( + interner, + SolverDefId::TraitId(dispatch_from_dyn_did), + [receiver_ty, unsized_receiver_ty], ); - let clauses = chalk_ir::ProgramClauses::from_iter( - Interner, - goals.into_iter().map(|g| { - chalk_ir::ProgramClause::new( - Interner, - chalk_ir::ProgramClauseData(g.map(|g| chalk_ir::ProgramClauseImplication { - consequence: g, - conditions: chalk_ir::Goals::empty(Interner), - constraints: chalk_ir::Constraints::empty(Interner), - priority: chalk_ir::ClausePriority::High, - })), - ) - }), - ); - let env: chalk_ir::Environment = chalk_ir::Environment { clauses }; + let goal = crate::next_solver::Goal::new(interner, param_env, predicate); - let obligation = WhereClause::Implemented(TraitRef { - trait_id: to_chalk_trait_id(dispatch_from_dyn_did), - substitution: Substitution::from_iter(Interner, [receiver_ty.clone(), unsized_receiver_ty]), - }); - let goal = GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(obligation)).intern(Interner); - - let in_env = chalk_ir::InEnvironment::new(&env, goal); - - let mut table = chalk_solve::infer::InferenceTable::::new(); - let canonicalized = table.canonicalize(Interner, in_env); - - db.trait_solve(krate, None, canonicalized.quantified).certain() + let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); + // the receiver is dispatchable iff the obligation holds + let res = next_trait_solve_in_ctxt(&infcx, goal); + res.map_or(false, |res| matches!(res.1, rustc_type_ir::solve::Certainty::Yes)) } -fn receiver_for_self_ty(db: &dyn HirDatabase, func: FunctionId, ty: Ty) -> Option { - let generics = generics(db, func.into()); - let trait_self_idx = trait_self_param_idx(db, func.into())?; - let subst = generics.placeholder_subst(db); - let subst = Substitution::from_iter( - Interner, - subst.iter(Interner).enumerate().map(|(idx, arg)| { - if idx == trait_self_idx { ty.clone().cast(Interner) } else { arg.clone() } - }), +fn receiver_for_self_ty<'db>( + interner: DbInterner<'db>, + func: FunctionId, + receiver_ty: crate::next_solver::Ty<'db>, + self_ty: crate::next_solver::Ty<'db>, +) -> crate::next_solver::Ty<'db> { + let args = crate::next_solver::GenericArgs::for_item( + interner, + SolverDefId::FunctionId(func), + |name, index, kind, _| { + if index == 0 { self_ty.into() } else { mk_param(index, name, kind) } + }, ); - let sig = db.callable_item_signature(func.into()); - let sig = sig.substitute(Interner, &subst); - sig.params_and_return.first().cloned() + + + crate::next_solver::EarlyBinder::bind(receiver_ty).instantiate(interner, args) } -fn contains_illegal_impl_trait_in_trait( - db: &dyn HirDatabase, - sig: &Binders, +fn contains_illegal_impl_trait_in_trait<'db>( + db: &'db dyn HirDatabase, + sig: &crate::next_solver::EarlyBinder< + 'db, + crate::next_solver::Binder<'db, rustc_type_ir::FnSig>>, + >, ) -> Option { - struct OpaqueTypeCollector(FxHashSet); + struct OpaqueTypeCollector(FxHashSet); - impl TypeVisitor for OpaqueTypeCollector { - type BreakTy = (); + impl<'db> rustc_type_ir::TypeVisitor> for OpaqueTypeCollector { + type Result = ControlFlow<()>; - fn as_dyn(&mut self) -> &mut dyn TypeVisitor { - self - } - - fn interner(&self) -> Interner { - Interner - } - - fn visit_ty(&mut self, ty: &Ty, outer_binder: DebruijnIndex) -> ControlFlow { - if let TyKind::OpaqueType(opaque_ty_id, _) = ty.kind(Interner) { - self.0.insert(*opaque_ty_id); + fn visit_ty( + &mut self, + ty: as rustc_type_ir::Interner>::Ty, + ) -> Self::Result { + if let rustc_type_ir::TyKind::Alias(AliasTyKind::Opaque, op) = ty.kind() { + let id = match op.def_id { + SolverDefId::InternedOpaqueTyId(id) => id, + _ => unreachable!(), + }; + self.0.insert(id); } - ty.super_visit_with(self.as_dyn(), outer_binder) + ty.super_visit_with(self) } } - let ret = sig.skip_binders().ret(); + let ret = sig.skip_binder().output(); let mut visitor = OpaqueTypeCollector(FxHashSet::default()); - _ = ret.visit_with(visitor.as_dyn(), DebruijnIndex::INNERMOST); + _ = ret.visit_with(&mut visitor); // Since we haven't implemented RPITIT in proper way like rustc yet, // just check whether `ret` contains RPIT for now for opaque_ty in visitor.0 { - let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty.into()); + let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty); if matches!(impl_trait_id, ImplTraitId::ReturnTypeImplTrait(..)) { return Some(MethodViolationCode::ReferencesImplTraitInTrait); } From 3f78a1fd5af1bc436062b9b5045b813304b9bb05 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Sat, 9 Aug 2025 23:28:48 +0000 Subject: [PATCH 073/682] impl HirDisplay for next_solver::Ty --- .../crates/hir-ty/src/consteval_nextsolver.rs | 8 +- .../crates/hir-ty/src/display.rs | 547 ++++++++++-------- .../crates/hir-ty/src/dyn_compatibility.rs | 1 - .../rust-analyzer/crates/hir-ty/src/layout.rs | 4 +- .../rust-analyzer/crates/hir-ty/src/lib.rs | 23 + .../crates/hir-ty/src/next_solver/mapping.rs | 7 +- .../next_solver/project/solve_normalize.rs | 13 +- .../crates/hir-ty/src/primitive.rs | 34 ++ .../rust-analyzer/crates/hir-ty/src/tests.rs | 40 +- .../hir-ty/src/tests/closure_captures.rs | 12 +- .../crates/hir-ty/src/tests/opaque_types.rs | 2 +- .../crates/hir-ty/src/tests/regression.rs | 6 +- .../crates/hir-ty/src/tests/traits.rs | 52 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 13 +- .../ide-completion/src/context/tests.rs | 7 +- .../crates/ide/src/hover/render.rs | 2 +- .../crates/ide/src/inlay_hints.rs | 72 +-- .../crates/ide/src/inlay_hints/adjustment.rs | 18 +- src/tools/rust-analyzer/crates/ide/src/lib.rs | 12 +- .../crates/ide/src/navigation_target.rs | 11 +- .../rust-analyzer/crates/ide/src/runnables.rs | 18 +- .../crates/ide/src/signature_help.rs | 5 +- .../crates/ide/src/static_index.rs | 52 +- .../crates/ide/src/view_memory_layout.rs | 9 +- 24 files changed, 580 insertions(+), 388 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs index da4aff54de37..cdf861290ab9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs @@ -132,9 +132,9 @@ pub fn usize_const<'db>(db: &'db dyn HirDatabase, value: Option, krate: Cr ) } -pub fn try_const_usize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option { +pub fn try_const_usize<'db>(db: &'db dyn HirDatabase, c: Const<'db>) -> Option { let interner = DbInterner::new_with(db, None, None); - match (*c).kind() { + match c.kind() { ConstKind::Param(_) => None, ConstKind::Infer(_) => None, ConstKind::Bound(_, _) => None, @@ -147,7 +147,7 @@ pub fn try_const_usize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option< }; let subst = convert_args_for_result(interner, unevaluated_const.args.as_slice()); let ec = db.const_eval(c, subst, None).ok()?.to_nextsolver(interner); - try_const_usize(db, &ec) + try_const_usize(db, ec) } ConstKind::Value(val) => Some(u128::from_le_bytes(pad16(&val.value.inner().0, false))), ConstKind::Error(_) => None, @@ -212,7 +212,7 @@ pub(crate) fn const_eval_discriminant_variant( let c = if is_signed { try_const_isize(db, &c).unwrap() } else { - try_const_usize(db, &c).unwrap() as i128 + try_const_usize(db, c).unwrap() as i128 }; Ok(c) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 8f35a3c21455..9f87c37834d7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -11,8 +11,8 @@ use base_db::Crate; use chalk_ir::{BoundVar, Safety, TyKind}; use either::Either; use hir_def::{ - GenericDefId, HasModule, ImportPathConfig, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, - ModuleId, TraitId, + GenericDefId, HasModule, ImportPathConfig, LocalFieldId, Lookup, ModuleDefId, ModuleId, + TraitId, db::DefDatabase, expr_store::{ExpressionStore, path::Path}, find_path::{self, PrefixKind}, @@ -37,26 +37,34 @@ use rustc_apfloat::{ ieee::{Half as f16, Quad as f128}, }; use rustc_hash::FxHashSet; +use rustc_type_ir::{ + AliasTyKind, + inherent::{AdtDef, IntoKind, SliceLike}, +}; use smallvec::SmallVec; use span::Edition; use stdx::never; use triomphe::Arc; use crate::{ - AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, ConcreteConst, Const, - ConstScalar, ConstValue, DomainGoal, FnAbi, GenericArg, ImplTraitId, Interner, Lifetime, - LifetimeData, LifetimeOutlives, MemoryMap, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt, - QuantifiedWhereClause, Scalar, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, - TyExt, WhereClause, - consteval::try_const_usize, + AliasEq, AliasTy, Binders, CallableDefId, CallableSig, ConcreteConst, Const, ConstScalar, + ConstValue, DomainGoal, FnAbi, GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData, + LifetimeOutlives, MemoryMap, OpaqueTy, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, + TraitEnvironment, TraitRef, TraitRefExt, Ty, TyExt, WhereClause, consteval_nextsolver, db::{HirDatabase, InternedClosure}, - from_assoc_type_id, from_foreign_def_id, from_placeholder_idx, + from_assoc_type_id, from_placeholder_idx, generics::generics, infer::normalize, layout::Layout, lt_from_placeholder_idx, - mapping::from_chalk, mir::pad16, + next_solver::{ + BoundExistentialPredicate, Ctor, DbInterner, GenericArgs, SolverDefId, + mapping::{ + ChalkToNextSolver, convert_args_for_result, convert_const_for_result, + convert_region_for_result, convert_ty_for_result, + }, + }, primitive, to_assoc_type_id, utils::{self, ClosureSubst, detect_variant_from_bytes}, }; @@ -185,6 +193,29 @@ impl HirFormatter<'_> { DisplayLifetime::Never => false, } } + + fn render_region(&self, lifetime: crate::next_solver::Region<'_>) -> bool { + match self.display_lifetimes { + DisplayLifetime::Always => true, + DisplayLifetime::OnlyStatic => { + matches!(lifetime.kind(), rustc_type_ir::RegionKind::ReStatic) + } + DisplayLifetime::OnlyNamed => { + matches!( + lifetime.kind(), + rustc_type_ir::RegionKind::RePlaceholder(_) + | rustc_type_ir::RegionKind::ReEarlyParam(_) + ) + } + DisplayLifetime::OnlyNamedOrStatic => matches!( + lifetime.kind(), + rustc_type_ir::RegionKind::ReStatic + | rustc_type_ir::RegionKind::RePlaceholder(_) + | rustc_type_ir::RegionKind::ReEarlyParam(_) + ), + DisplayLifetime::Never => false, + } + } } pub trait HirDisplay { @@ -476,10 +507,6 @@ impl DisplayKind { matches!(self, Self::SourceCode { .. }) } - fn is_test(self) -> bool { - matches!(self, Self::Test) - } - fn allows_opaque(self) -> bool { match self { Self::SourceCode { allow_opaque, .. } => allow_opaque, @@ -721,59 +748,87 @@ fn render_const_scalar( ty: &Ty, ) -> Result<(), HirDisplayError> { let trait_env = TraitEnvironment::empty(f.krate()); + let interner = DbInterner::new_with(f.db, Some(trait_env.krate), trait_env.block); let ty = normalize(f.db, trait_env.clone(), ty.clone()); - match ty.kind(Interner) { - TyKind::Scalar(s) => match s { - Scalar::Bool => write!(f, "{}", b[0] != 0), - Scalar::Char => { - let it = u128::from_le_bytes(pad16(b, false)) as u32; - let Ok(c) = char::try_from(it) else { - return f.write_str(""); - }; - write!(f, "{c:?}") + let ty = ty.to_nextsolver(interner); + render_const_scalar_inner(f, b, memory_map, ty, trait_env, interner) +} + +fn render_const_scalar_ns( + f: &mut HirFormatter<'_>, + b: &[u8], + memory_map: &MemoryMap, + ty: crate::next_solver::Ty<'_>, +) -> Result<(), HirDisplayError> { + let trait_env = TraitEnvironment::empty(f.krate()); + let interner = DbInterner::new_with(f.db, Some(trait_env.krate), trait_env.block); + let ty = crate::next_solver::project::solve_normalize::normalize( + interner, + trait_env.env.to_nextsolver(interner), + ty, + ); + render_const_scalar_inner(f, b, memory_map, ty, trait_env, interner) +} + +fn render_const_scalar_inner( + f: &mut HirFormatter<'_>, + b: &[u8], + memory_map: &MemoryMap, + ty: crate::next_solver::Ty<'_>, + trait_env: Arc, + interner: DbInterner<'_>, +) -> Result<(), HirDisplayError> { + use rustc_type_ir::TyKind; + match ty.kind() { + TyKind::Bool => write!(f, "{}", b[0] != 0), + TyKind::Char => { + let it = u128::from_le_bytes(pad16(b, false)) as u32; + let Ok(c) = char::try_from(it) else { + return f.write_str(""); + }; + write!(f, "{c:?}") + } + TyKind::Int(_) => { + let it = i128::from_le_bytes(pad16(b, true)); + write!(f, "{it}") + } + TyKind::Uint(_) => { + let it = u128::from_le_bytes(pad16(b, false)); + write!(f, "{it}") + } + TyKind::Float(fl) => match fl { + rustc_type_ir::FloatTy::F16 => { + // FIXME(#17451): Replace with builtins once they are stabilised. + let it = f16::from_bits(u16::from_le_bytes(b.try_into().unwrap()).into()); + let s = it.to_string(); + if s.strip_prefix('-').unwrap_or(&s).chars().all(|c| c.is_ascii_digit()) { + // Match Rust debug formatting + write!(f, "{s}.0") + } else { + write!(f, "{s}") + } } - Scalar::Int(_) => { - let it = i128::from_le_bytes(pad16(b, true)); - write!(f, "{it}") + rustc_type_ir::FloatTy::F32 => { + let it = f32::from_le_bytes(b.try_into().unwrap()); + write!(f, "{it:?}") } - Scalar::Uint(_) => { - let it = u128::from_le_bytes(pad16(b, false)); - write!(f, "{it}") + rustc_type_ir::FloatTy::F64 => { + let it = f64::from_le_bytes(b.try_into().unwrap()); + write!(f, "{it:?}") } - Scalar::Float(fl) => match fl { - chalk_ir::FloatTy::F16 => { - // FIXME(#17451): Replace with builtins once they are stabilised. - let it = f16::from_bits(u16::from_le_bytes(b.try_into().unwrap()).into()); - let s = it.to_string(); - if s.strip_prefix('-').unwrap_or(&s).chars().all(|c| c.is_ascii_digit()) { - // Match Rust debug formatting - write!(f, "{s}.0") - } else { - write!(f, "{s}") - } + rustc_type_ir::FloatTy::F128 => { + // FIXME(#17451): Replace with builtins once they are stabilised. + let it = f128::from_bits(u128::from_le_bytes(b.try_into().unwrap())); + let s = it.to_string(); + if s.strip_prefix('-').unwrap_or(&s).chars().all(|c| c.is_ascii_digit()) { + // Match Rust debug formatting + write!(f, "{s}.0") + } else { + write!(f, "{s}") } - chalk_ir::FloatTy::F32 => { - let it = f32::from_le_bytes(b.try_into().unwrap()); - write!(f, "{it:?}") - } - chalk_ir::FloatTy::F64 => { - let it = f64::from_le_bytes(b.try_into().unwrap()); - write!(f, "{it:?}") - } - chalk_ir::FloatTy::F128 => { - // FIXME(#17451): Replace with builtins once they are stabilised. - let it = f128::from_bits(u128::from_le_bytes(b.try_into().unwrap())); - let s = it.to_string(); - if s.strip_prefix('-').unwrap_or(&s).chars().all(|c| c.is_ascii_digit()) { - // Match Rust debug formatting - write!(f, "{s}.0") - } else { - write!(f, "{s}") - } - } - }, + } }, - TyKind::Ref(_, _, t) => match t.kind(Interner) { + TyKind::Ref(_, t, _) => match t.kind() { TyKind::Str => { let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap()); let size = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap()); @@ -786,7 +841,7 @@ fn render_const_scalar( TyKind::Slice(ty) => { let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap()); let count = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap()); - let Ok(layout) = f.db.layout_of_ty(ty.clone(), trait_env) else { + let Ok(layout) = f.db.layout_of_ty_ns(ty, trait_env) else { return f.write_str(""); }; let size_one = layout.size.bytes_usize(); @@ -810,11 +865,11 @@ fn render_const_scalar( f.write_str(", ")?; } let offset = size_one * i; - render_const_scalar(f, &bytes[offset..offset + size_one], memory_map, ty)?; + render_const_scalar_ns(f, &bytes[offset..offset + size_one], memory_map, ty)?; } f.write_str("]") } - TyKind::Dyn(_) => { + TyKind::Dynamic(_, _, _) => { let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap()); let ty_id = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap()); let Ok(t) = memory_map.vtable_ty(ty_id) else { @@ -828,15 +883,16 @@ fn render_const_scalar( return f.write_str(""); }; f.write_str("&")?; - render_const_scalar(f, bytes, memory_map, t) + render_const_scalar_ns(f, bytes, memory_map, t.to_nextsolver(interner)) } - TyKind::Adt(adt, _) if b.len() == 2 * size_of::() => match adt.0 { - hir_def::AdtId::StructId(s) => { + TyKind::Adt(adt, _) if b.len() == 2 * size_of::() => match adt.def_id() { + SolverDefId::AdtId(hir_def::AdtId::StructId(s)) => { let data = f.db.struct_signature(s); write!(f, "&{}", data.name.display(f.db, f.edition()))?; Ok(()) } - _ => f.write_str(""), + SolverDefId::AdtId(_) => f.write_str(""), + _ => unreachable!(), }, _ => { let addr = usize::from_le_bytes(match b.try_into() { @@ -850,7 +906,7 @@ fn render_const_scalar( return f.write_str(""); } }); - let Ok(layout) = f.db.layout_of_ty(t.clone(), trait_env) else { + let Ok(layout) = f.db.layout_of_ty_ns(t, trait_env) else { return f.write_str(""); }; let size = layout.size.bytes_usize(); @@ -858,37 +914,40 @@ fn render_const_scalar( return f.write_str(""); }; f.write_str("&")?; - render_const_scalar(f, bytes, memory_map, t) + render_const_scalar_ns(f, bytes, memory_map, t) } }, - TyKind::Tuple(_, subst) => { - let Ok(layout) = f.db.layout_of_ty(ty.clone(), trait_env.clone()) else { + TyKind::Tuple(tys) => { + let Ok(layout) = f.db.layout_of_ty_ns(ty, trait_env.clone()) else { return f.write_str(""); }; f.write_str("(")?; let mut first = true; - for (id, ty) in subst.iter(Interner).enumerate() { + for (id, ty) in tys.iter().enumerate() { if first { first = false; } else { f.write_str(", ")?; } - let ty = ty.assert_ty_ref(Interner); // Tuple only has type argument let offset = layout.fields.offset(id).bytes_usize(); - let Ok(layout) = f.db.layout_of_ty(ty.clone(), trait_env.clone()) else { + let Ok(layout) = f.db.layout_of_ty_ns(ty, trait_env.clone()) else { f.write_str("")?; continue; }; let size = layout.size.bytes_usize(); - render_const_scalar(f, &b[offset..offset + size], memory_map, ty)?; + render_const_scalar_ns(f, &b[offset..offset + size], memory_map, ty)?; } f.write_str(")") } - TyKind::Adt(adt, subst) => { - let Ok(layout) = f.db.layout_of_adt(adt.0, subst.clone(), trait_env.clone()) else { + TyKind::Adt(def, args) => { + let def = match def.def_id() { + SolverDefId::AdtId(def) => def, + _ => unreachable!(), + }; + let Ok(layout) = f.db.layout_of_adt_ns(def, args, trait_env.clone()) else { return f.write_str(""); }; - match adt.0 { + match def { hir_def::AdtId::StructId(s) => { let data = f.db.struct_signature(s); write!(f, "{}", data.name.display(f.db, f.edition()))?; @@ -897,9 +956,9 @@ fn render_const_scalar( s.fields(f.db), f, &field_types, - f.db.trait_environment(adt.0.into()), + f.db.trait_environment(def.into()), &layout, - subst, + args, b, memory_map, ) @@ -929,9 +988,9 @@ fn render_const_scalar( var_id.fields(f.db), f, &field_types, - f.db.trait_environment(adt.0.into()), + f.db.trait_environment(def.into()), var_layout, - subst, + args, b, memory_map, ) @@ -939,16 +998,16 @@ fn render_const_scalar( } } TyKind::FnDef(..) => ty.hir_fmt(f), - TyKind::Function(_) | TyKind::Raw(_, _) => { + TyKind::FnPtr(_, _) | TyKind::RawPtr(_, _) => { let it = u128::from_le_bytes(pad16(b, false)); write!(f, "{it:#X} as ")?; ty.hir_fmt(f) } TyKind::Array(ty, len) => { - let Some(len) = try_const_usize(f.db, len) else { + let Some(len) = consteval_nextsolver::try_const_usize(f.db, len) else { return f.write_str(""); }; - let Ok(layout) = f.db.layout_of_ty(ty.clone(), trait_env) else { + let Ok(layout) = f.db.layout_of_ty_ns(ty, trait_env) else { return f.write_str(""); }; let size_one = layout.size.bytes_usize(); @@ -961,7 +1020,7 @@ fn render_const_scalar( f.write_str(", ")?; } let offset = size_one * i; - render_const_scalar(f, &b[offset..offset + size_one], memory_map, ty)?; + render_const_scalar_ns(f, &b[offset..offset + size_one], memory_map, ty)?; } f.write_str("]") } @@ -969,17 +1028,19 @@ fn render_const_scalar( TyKind::Closure(_, _) => f.write_str(""), TyKind::Coroutine(_, _) => f.write_str(""), TyKind::CoroutineWitness(_, _) => f.write_str(""), + TyKind::CoroutineClosure(_, _) => f.write_str(""), + TyKind::UnsafeBinder(_) => f.write_str(""), // The below arms are unreachable, since const eval will bail out before here. TyKind::Foreign(_) => f.write_str(""), - TyKind::Error + TyKind::Pat(_, _) => f.write_str(""), + TyKind::Error(..) | TyKind::Placeholder(_) - | TyKind::Alias(_) - | TyKind::AssociatedType(_, _) - | TyKind::OpaqueType(_, _) - | TyKind::BoundVar(_) - | TyKind::InferenceVar(_, _) => f.write_str(""), + | TyKind::Alias(_, _) + | TyKind::Param(_) + | TyKind::Bound(_, _) + | TyKind::Infer(_) => f.write_str(""), // The below arms are unreachable, since we handled them in ref case. - TyKind::Slice(_) | TyKind::Str | TyKind::Dyn(_) => f.write_str(""), + TyKind::Slice(_) | TyKind::Str | TyKind::Dynamic(_, _, _) => f.write_str(""), } } @@ -989,15 +1050,18 @@ fn render_variant_after_name( field_types: &ArenaMap>, trait_env: Arc, layout: &Layout, - subst: &Substitution, + args: GenericArgs<'_>, b: &[u8], memory_map: &MemoryMap, ) -> Result<(), HirDisplayError> { + let interner = DbInterner::new_with(f.db, Some(trait_env.krate), trait_env.block); match data.shape { FieldsShape::Record | FieldsShape::Tuple => { let render_field = |f: &mut HirFormatter<'_>, id: LocalFieldId| { let offset = layout.fields.offset(u32::from(id.into_raw()) as usize).bytes_usize(); - let ty = field_types[id].clone().substitute(Interner, subst); + let ty = field_types[id] + .clone() + .substitute(Interner, &convert_args_for_result(interner, args.as_slice())); let Ok(layout) = f.db.layout_of_ty(ty.clone(), trait_env.clone()) else { return f.write_str(""); }; @@ -1045,18 +1109,30 @@ impl HirDisplay for Ty { &self, f @ &mut HirFormatter { db, .. }: &mut HirFormatter<'_>, ) -> Result<(), HirDisplayError> { + let ty = self.to_nextsolver(DbInterner::new_with(db, None, None)); + ty.hir_fmt(f) + } +} + +impl<'db> HirDisplay for crate::next_solver::Ty<'db> { + fn hir_fmt( + &self, + f @ &mut HirFormatter { db, .. }: &mut HirFormatter<'_>, + ) -> Result<(), HirDisplayError> { + let interner = DbInterner::new_with(db, None, None); if f.should_truncate() { return write!(f, "{TYPE_HINT_TRUNCATION}"); } - match self.kind(Interner) { + use rustc_type_ir::TyKind; + match self.kind() { TyKind::Never => write!(f, "!")?, TyKind::Str => write!(f, "str")?, - TyKind::Scalar(Scalar::Bool) => write!(f, "bool")?, - TyKind::Scalar(Scalar::Char) => write!(f, "char")?, - &TyKind::Scalar(Scalar::Float(t)) => write!(f, "{}", primitive::float_ty_to_string(t))?, - &TyKind::Scalar(Scalar::Int(t)) => write!(f, "{}", primitive::int_ty_to_string(t))?, - &TyKind::Scalar(Scalar::Uint(t)) => write!(f, "{}", primitive::uint_ty_to_string(t))?, + TyKind::Bool => write!(f, "bool")?, + TyKind::Char => write!(f, "char")?, + TyKind::Float(t) => write!(f, "{}", primitive::float_ty_to_string_ns(t))?, + TyKind::Int(t) => write!(f, "{}", primitive::int_ty_to_string_ns(t))?, + TyKind::Uint(t) => write!(f, "{}", primitive::uint_ty_to_string_ns(t))?, TyKind::Slice(t) => { write!(f, "[")?; t.hir_fmt(f)?; @@ -1066,27 +1142,27 @@ impl HirDisplay for Ty { write!(f, "[")?; t.hir_fmt(f)?; write!(f, "; ")?; - c.hir_fmt(f)?; + convert_const_for_result(interner, c).hir_fmt(f)?; write!(f, "]")?; } - kind @ (TyKind::Raw(m, t) | TyKind::Ref(m, _, t)) => { - if let TyKind::Ref(_, l, _) = kind { + kind @ (TyKind::RawPtr(t, m) | TyKind::Ref(_, t, m)) => { + if let TyKind::Ref(l, _, _) = kind { f.write_char('&')?; - if f.render_lifetime(l) { - l.hir_fmt(f)?; + if f.render_region(l) { + convert_region_for_result(l).hir_fmt(f)?; f.write_char(' ')?; } match m { - Mutability::Not => (), - Mutability::Mut => f.write_str("mut ")?, + rustc_ast_ir::Mutability::Not => (), + rustc_ast_ir::Mutability::Mut => f.write_str("mut ")?, } } else { write!( f, "*{}", match m { - Mutability::Not => "const ", - Mutability::Mut => "mut ", + rustc_ast_ir::Mutability::Not => "const ", + rustc_ast_ir::Mutability::Mut => "mut ", } )?; } @@ -1102,25 +1178,42 @@ impl HirDisplay for Ty { } }) }; - let (preds_to_print, has_impl_fn_pred) = match t.kind(Interner) { - TyKind::Dyn(dyn_ty) => { - let bounds = dyn_ty.bounds.skip_binders().interned(); - let render_lifetime = f.render_lifetime(&dyn_ty.lifetime); - (bounds.len() + render_lifetime as usize, contains_impl_fn(bounds)) + let contains_impl_fn_ns = |bounds: &[BoundExistentialPredicate<'_>]| { + bounds.iter().any(|bound| match bound.skip_binder() { + rustc_type_ir::ExistentialPredicate::Trait(trait_ref) => { + let trait_ = match trait_ref.def_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + fn_traits(db, trait_).any(|it| it == trait_) + } + _ => false, + }) + }; + let (preds_to_print, has_impl_fn_pred) = match t.kind() { + TyKind::Dynamic(bounds, region, _) => { + let render_lifetime = f.render_region(region); + ( + bounds.len() + render_lifetime as usize, + contains_impl_fn_ns(bounds.as_slice()), + ) } - TyKind::Alias(AliasTy::Opaque(OpaqueTy { - opaque_ty_id, - substitution: parameters, - })) - | TyKind::OpaqueType(opaque_ty_id, parameters) => { - let impl_trait_id = db.lookup_intern_impl_trait_id((*opaque_ty_id).into()); + TyKind::Alias(AliasTyKind::Opaque, ty) => { + let opaque_ty_id = match ty.def_id { + SolverDefId::InternedOpaqueTyId(id) => id, + _ => unreachable!(), + }; + let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty_id); if let ImplTraitId::ReturnTypeImplTrait(func, idx) = impl_trait_id { let datas = db .return_type_impl_traits(func) .expect("impl trait id without data"); let data = (*datas).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone()); - let bounds = data.substitute(Interner, parameters); + let bounds = data.substitute( + Interner, + &convert_args_for_result(interner, ty.args.as_slice()), + ); let mut len = bounds.skip_binders().len(); // Don't count Sized but count when it absent @@ -1167,24 +1260,31 @@ impl HirDisplay for Ty { t.hir_fmt(f)?; } } - TyKind::Tuple(_, substs) => { - if substs.len(Interner) == 1 { + TyKind::Tuple(tys) => { + if tys.len() == 1 { write!(f, "(")?; - substs.at(Interner, 0).hir_fmt(f)?; + tys.as_slice()[0].hir_fmt(f)?; write!(f, ",)")?; } else { write!(f, "(")?; - f.write_joined(substs.as_slice(Interner), ", ")?; + f.write_joined(tys.as_slice(), ", ")?; write!(f, ")")?; } } - TyKind::Function(fn_ptr) => { - let sig = CallableSig::from_fn_ptr(fn_ptr); + TyKind::FnPtr(sig, header) => { + let sig = CallableSig::from_fn_sig_and_header(interner, sig, header); sig.hir_fmt(f)?; } - TyKind::FnDef(def, parameters) => { - let def = from_chalk(db, *def); - let sig = db.callable_item_signature(def).substitute(Interner, parameters); + TyKind::FnDef(def, args) => { + let def = match def { + SolverDefId::FunctionId(id) => CallableDefId::FunctionId(id), + SolverDefId::Ctor(Ctor::Enum(e)) => CallableDefId::EnumVariantId(e), + SolverDefId::Ctor(Ctor::Struct(s)) => CallableDefId::StructId(s), + _ => unreachable!(), + }; + let sig = db + .callable_item_signature(def) + .substitute(Interner, &convert_args_for_result(interner, args.as_slice())); if f.display_kind.is_source_code() { // `FnDef` is anonymous and there's no surface syntax for it. Show it as a @@ -1222,6 +1322,7 @@ impl HirDisplay for Ty { }; f.end_location_link(); + let parameters = convert_args_for_result(interner, args.as_slice()); if parameters.len(Interner) > 0 { let generic_def_id = GenericDefId::from_callable(db, def); let generics = generics(db, generic_def_id); @@ -1280,11 +1381,15 @@ impl HirDisplay for Ty { ret.hir_fmt(f)?; } } - TyKind::Adt(AdtId(def_id), parameters) => { - f.start_location_link((*def_id).into()); + TyKind::Adt(def, parameters) => { + let def_id = match def.def_id() { + SolverDefId::AdtId(id) => id, + _ => unreachable!(), + }; + f.start_location_link(def_id.into()); match f.display_kind { DisplayKind::Diagnostics | DisplayKind::Test => { - let name = match *def_id { + let name = match def_id { hir_def::AdtId::StructId(it) => db.struct_signature(it).name.clone(), hir_def::AdtId::UnionId(it) => db.union_signature(it).name.clone(), hir_def::AdtId::EnumId(it) => db.enum_signature(it).name.clone(), @@ -1294,7 +1399,7 @@ impl HirDisplay for Ty { DisplayKind::SourceCode { target_module_id: module_id, allow_opaque: _ } => { if let Some(path) = find_path::find_path( db, - ItemInNs::Types((*def_id).into()), + ItemInNs::Types(def_id.into()), module_id, PrefixKind::Plain, false, @@ -1316,55 +1421,49 @@ impl HirDisplay for Ty { } f.end_location_link(); - let generic_def = self.as_generic_def(db); - - hir_fmt_generics(f, parameters.as_slice(Interner), generic_def, None)?; + hir_fmt_generics( + f, + convert_args_for_result(interner, parameters.as_slice()).as_slice(Interner), + def.def_id().try_into().ok(), + None, + )?; } - TyKind::AssociatedType(assoc_type_id, parameters) => { - let type_alias = from_assoc_type_id(*assoc_type_id); - let trait_ = match type_alias.lookup(db).container { - ItemContainerId::TraitId(it) => it, - _ => panic!("not an associated type"), + TyKind::Alias(AliasTyKind::Projection, alias_ty) => { + let type_alias = match alias_ty.def_id { + SolverDefId::TypeAliasId(id) => id, + _ => unreachable!(), }; - let trait_data = db.trait_signature(trait_); - let type_alias_data = db.type_alias_signature(type_alias); + let parameters = convert_args_for_result(interner, alias_ty.args.as_slice()); - // Use placeholder associated types when the target is test (https://rust-lang.github.io/chalk/book/clauses/type_equality.html#placeholder-associated-types) - if f.display_kind.is_test() { - f.start_location_link(trait_.into()); - write!(f, "{}", trait_data.name.display(f.db, f.edition()))?; - f.end_location_link(); - write!(f, "::")?; + let projection_ty = ProjectionTy { + associated_ty_id: to_assoc_type_id(type_alias), + substitution: parameters.clone(), + }; - f.start_location_link(type_alias.into()); - write!(f, "{}", type_alias_data.name.display(f.db, f.edition()))?; - f.end_location_link(); - // Note that the generic args for the associated type come before those for the - // trait (including the self type). - hir_fmt_generics(f, parameters.as_slice(Interner), None, None) - } else { - let projection_ty = ProjectionTy { - associated_ty_id: to_assoc_type_id(type_alias), - substitution: parameters.clone(), - }; - - projection_ty.hir_fmt(f) - }?; + projection_ty.hir_fmt(f)?; } TyKind::Foreign(type_alias) => { - let alias = from_foreign_def_id(*type_alias); + let alias = match type_alias { + SolverDefId::ForeignId(id) => id, + _ => unreachable!(), + }; let type_alias = db.type_alias_signature(alias); f.start_location_link(alias.into()); write!(f, "{}", type_alias.name.display(f.db, f.edition()))?; f.end_location_link(); } - TyKind::OpaqueType(opaque_ty_id, parameters) => { + TyKind::Alias(AliasTyKind::Opaque, alias_ty) => { + let opaque_ty_id = match alias_ty.def_id { + SolverDefId::InternedOpaqueTyId(id) => id, + _ => unreachable!(), + }; + let parameters = convert_args_for_result(interner, alias_ty.args.as_slice()); if !f.display_kind.allows_opaque() { return Err(HirDisplayError::DisplaySourceCodeError( DisplaySourceCodeError::OpaqueType, )); } - let impl_trait_id = db.lookup_intern_impl_trait_id((*opaque_ty_id).into()); + let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty_id); match impl_trait_id { ImplTraitId::ReturnTypeImplTrait(func, idx) => { let datas = @@ -1376,7 +1475,7 @@ impl HirDisplay for Ty { write_bounds_like_dyn_trait_with_prefix( f, "impl", - Either::Left(self), + Either::Left(&convert_ty_for_result(interner, *self)), bounds.skip_binders(), SizedByDefault::Sized { anchor: krate }, )?; @@ -1391,7 +1490,7 @@ impl HirDisplay for Ty { write_bounds_like_dyn_trait_with_prefix( f, "impl", - Either::Left(self), + Either::Left(&convert_ty_for_result(interner, *self)), bounds.skip_binders(), SizedByDefault::Sized { anchor: krate }, )?; @@ -1426,6 +1525,11 @@ impl HirDisplay for Ty { } } TyKind::Closure(id, substs) => { + let id = match id { + SolverDefId::InternedClosureId(id) => id, + _ => unreachable!(), + }; + let substs = convert_args_for_result(interner, substs.as_slice()); if f.display_kind.is_source_code() { if !f.display_kind.allows_opaque() { return Err(HirDisplayError::DisplaySourceCodeError( @@ -1435,22 +1539,23 @@ impl HirDisplay for Ty { never!("Only `impl Fn` is valid for displaying closures in source code"); } } + let chalk_id: chalk_ir::ClosureId<_> = id.into(); match f.closure_style { ClosureStyle::Hide => return write!(f, "{TYPE_HINT_TRUNCATION}"), ClosureStyle::ClosureWithId => { - return write!(f, "{{closure#{:?}}}", id.0.index()); + return write!(f, "{{closure#{:?}}}", chalk_id.0.index()); } ClosureStyle::ClosureWithSubst => { - write!(f, "{{closure#{:?}}}", id.0.index())?; + write!(f, "{{closure#{:?}}}", chalk_id.0.index())?; return hir_fmt_generics(f, substs.as_slice(Interner), None, None); } _ => (), } - let sig = ClosureSubst(substs).sig_ty().callable_sig(db); + let sig = ClosureSubst(&substs).sig_ty().callable_sig(db); if let Some(sig) = sig { - let InternedClosure(def, _) = db.lookup_intern_closure((*id).into()); + let InternedClosure(def, _) = db.lookup_intern_closure(id); let infer = db.infer(def); - let (_, kind) = infer.closure_info(id); + let (_, kind) = infer.closure_info(&chalk_id); match f.closure_style { ClosureStyle::ImplFn => write!(f, "impl {kind:?}(")?, ClosureStyle::RANotation => write!(f, "|")?, @@ -1478,7 +1583,11 @@ impl HirDisplay for Ty { } } TyKind::Placeholder(idx) => { - let id = from_placeholder_idx(db, *idx); + let placeholder_index = chalk_ir::PlaceholderIndex { + idx: idx.bound.var.as_usize(), + ui: chalk_ir::UniverseIndex { counter: idx.universe.as_usize() }, + }; + let id = from_placeholder_idx(db, placeholder_index); let generics = generics(db, id.parent); let param_data = &generics[id.local_id]; match param_data { @@ -1501,14 +1610,20 @@ impl HirDisplay for Ty { .map(|pred| pred.clone().substitute(Interner, &substs)) .filter(|wc| match wc.skip_binders() { WhereClause::Implemented(tr) => { - tr.self_type_parameter(Interner) == *self + tr.self_type_parameter(Interner) + == convert_ty_for_result(interner, *self) } WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(proj), ty: _, - }) => proj.self_type_parameter(db) == *self, + }) => { + proj.self_type_parameter(db) + == convert_ty_for_result(interner, *self) + } WhereClause::AliasEq(_) => false, - WhereClause::TypeOutlives(to) => to.ty == *self, + WhereClause::TypeOutlives(to) => { + to.ty == convert_ty_for_result(interner, *self) + } WhereClause::LifetimeOutlives(_) => false, }) .collect::>(); @@ -1516,7 +1631,7 @@ impl HirDisplay for Ty { write_bounds_like_dyn_trait_with_prefix( f, "impl", - Either::Left(self), + Either::Left(&convert_ty_for_result(interner, *self)), &bounds, SizedByDefault::Sized { anchor: krate }, )?; @@ -1527,8 +1642,17 @@ impl HirDisplay for Ty { } } } - TyKind::BoundVar(idx) => idx.hir_fmt(f)?, - TyKind::Dyn(dyn_ty) => { + TyKind::Param(_) => write!(f, "{{param}}")?, + TyKind::Bound(debruijn_index, ty) => { + let idx = chalk_ir::BoundVar { + debruijn: chalk_ir::DebruijnIndex::new(debruijn_index.as_u32()), + index: ty.var.as_usize(), + }; + idx.hir_fmt(f)? + } + TyKind::Dynamic(..) => { + let ty = convert_ty_for_result(interner, *self); + let chalk_ir::TyKind::Dyn(dyn_ty) = ty.kind(Interner) else { unreachable!() }; // Reorder bounds to satisfy `write_bounds_like_dyn_trait()`'s expectation. // FIXME: `Iterator::partition_in_place()` or `Vec::extract_if()` may make it // more efficient when either of them hits stable. @@ -1544,7 +1668,7 @@ impl HirDisplay for Ty { bounds.push(Binders::empty( Interner, chalk_ir::WhereClause::TypeOutlives(chalk_ir::TypeOutlives { - ty: self.clone(), + ty: ty.clone(), lifetime: dyn_ty.lifetime.clone(), }), )); @@ -1553,69 +1677,26 @@ impl HirDisplay for Ty { write_bounds_like_dyn_trait_with_prefix( f, "dyn", - Either::Left(self), + Either::Left(&ty), &bounds, SizedByDefault::NotSized, )?; } - TyKind::Alias(AliasTy::Projection(p_ty)) => p_ty.hir_fmt(f)?, - TyKind::Alias(AliasTy::Opaque(opaque_ty)) => { - if !f.display_kind.allows_opaque() { - return Err(HirDisplayError::DisplaySourceCodeError( - DisplaySourceCodeError::OpaqueType, - )); - } - let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty.opaque_ty_id.into()); - match impl_trait_id { - ImplTraitId::ReturnTypeImplTrait(func, idx) => { - let datas = - db.return_type_impl_traits(func).expect("impl trait id without data"); - let data = - (*datas).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone()); - let bounds = data.substitute(Interner, &opaque_ty.substitution); - let krate = func.krate(db); - write_bounds_like_dyn_trait_with_prefix( - f, - "impl", - Either::Left(self), - bounds.skip_binders(), - SizedByDefault::Sized { anchor: krate }, - )?; - } - ImplTraitId::TypeAliasImplTrait(alias, idx) => { - let datas = - db.type_alias_impl_traits(alias).expect("impl trait id without data"); - let data = - (*datas).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone()); - let bounds = data.substitute(Interner, &opaque_ty.substitution); - let krate = alias.krate(db); - write_bounds_like_dyn_trait_with_prefix( - f, - "impl", - Either::Left(self), - bounds.skip_binders(), - SizedByDefault::Sized { anchor: krate }, - )?; - } - ImplTraitId::AsyncBlockTypeImplTrait(..) => { - write!(f, "{{async block}}")?; - } - }; - } - TyKind::Error => { + TyKind::Error(_) => { if f.display_kind.is_source_code() { f.write_char('_')?; } else { write!(f, "{{unknown}}")?; } } - TyKind::InferenceVar(..) => write!(f, "_")?, + TyKind::Infer(..) => write!(f, "_")?, TyKind::Coroutine(_, subst) => { if f.display_kind.is_source_code() { return Err(HirDisplayError::DisplaySourceCodeError( DisplaySourceCodeError::Coroutine, )); } + let subst = convert_args_for_result(interner, subst.as_slice()); let subst = subst.as_slice(Interner); let a: Option> = subst .get(subst.len() - 3..) @@ -1637,6 +1718,10 @@ impl HirDisplay for Ty { } } TyKind::CoroutineWitness(..) => write!(f, "{{coroutine witness}}")?, + TyKind::Pat(_, _) => write!(f, "{{pat}}")?, + TyKind::UnsafeBinder(_) => write!(f, "{{unsafe binder}}")?, + TyKind::CoroutineClosure(_, _) => write!(f, "{{coroutine closure}}")?, + TyKind::Alias(_, _) => write!(f, "{{alias}}")?, } Ok(()) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index 237cc34d7099..8bd555f5c08f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -615,7 +615,6 @@ fn receiver_for_self_ty<'db>( }, ); - crate::next_solver::EarlyBinder::bind(receiver_ty).instantiate(interner, args) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index 2d0471d7e554..fb7b5e1c83e4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -153,7 +153,7 @@ fn layout_of_simd_ty<'db>( return Err(LayoutError::InvalidSimdType); }; - let e_len = try_const_usize(db, &e_len).ok_or(LayoutError::HasErrorConst)? as u64; + let e_len = try_const_usize(db, e_len).ok_or(LayoutError::HasErrorConst)? as u64; let e_ly = db.layout_of_ty_ns(e_ty, env)?; let cx = LayoutCx::new(dl); @@ -272,7 +272,7 @@ pub fn layout_of_ty_ns_query<'db>( cx.calc.univariant(&fields, &ReprOptions::default(), kind)? } TyKind::Array(element, count) => { - let count = try_const_usize(db, &count).ok_or(LayoutError::HasErrorConst)? as u64; + let count = try_const_usize(db, count).ok_or(LayoutError::HasErrorConst)? as u64; let element = db.layout_of_ty_ns(element, trait_env)?; cx.calc.array_like::<_, _, ()>(&element, Some(count))? } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 2b0dc931eaf4..81894830b5b4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -90,6 +90,7 @@ use intern::{Symbol, sym}; use la_arena::{Arena, Idx}; use mir::{MirEvalError, VTableMap}; use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet}; +use rustc_type_ir::inherent::SliceLike; use syntax::ast::{ConstArg, make}; use traits::FnTrait; use triomphe::Arc; @@ -100,6 +101,7 @@ use crate::{ display::{DisplayTarget, HirDisplay}, generics::Generics, infer::unify::InferenceTable, + next_solver::{DbInterner, mapping::convert_ty_for_result}, }; pub use autoderef::autoderef; @@ -560,6 +562,27 @@ impl CallableSig { abi: fn_ptr.sig.abi, } } + pub fn from_fn_sig_and_header<'db>( + interner: DbInterner<'db>, + sig: crate::next_solver::Binder<'db, rustc_type_ir::FnSigTys>>, + header: rustc_type_ir::FnHeader>, + ) -> CallableSig { + CallableSig { + // FIXME: what to do about lifetime params? -> return PolyFnSig + params_and_return: Arc::from_iter( + sig.skip_binder() + .inputs_and_output + .iter() + .map(|t| convert_ty_for_result(interner, t)), + ), + is_varargs: header.c_variadic, + safety: match header.safety { + next_solver::abi::Safety::Safe => chalk_ir::Safety::Safe, + next_solver::abi::Safety::Unsafe => chalk_ir::Safety::Unsafe, + }, + abi: header.abi, + } + } pub fn to_fn_ptr(&self) -> FnPointer { FnPointer { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index 20cd8626f299..b50fccb83262 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -1290,7 +1290,10 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) .intern(Interner) } -fn convert_const_for_result<'db>(interner: DbInterner<'db>, const_: Const<'db>) -> crate::Const { +pub fn convert_const_for_result<'db>( + interner: DbInterner<'db>, + const_: Const<'db>, +) -> crate::Const { let value: chalk_ir::ConstValue = match const_.kind() { rustc_type_ir::ConstKind::Param(_) => unimplemented!(), rustc_type_ir::ConstKind::Infer(rustc_type_ir::InferConst::Var(var)) => { @@ -1343,7 +1346,7 @@ fn convert_const_for_result<'db>(interner: DbInterner<'db>, const_: Const<'db>) chalk_ir::ConstData { ty: crate::TyKind::Error.intern(Interner), value }.intern(Interner) } -fn convert_region_for_result<'db>(region: Region<'db>) -> crate::Lifetime { +pub fn convert_region_for_result<'db>(region: Region<'db>) -> crate::Lifetime { match region.kind() { rustc_type_ir::RegionKind::ReEarlyParam(early) => unimplemented!(), rustc_type_ir::RegionKind::ReBound(db, bound) => chalk_ir::Lifetime::new( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project/solve_normalize.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project/solve_normalize.rs index a8fb2ae14f1c..42c238febf6f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project/solve_normalize.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/project/solve_normalize.rs @@ -11,16 +11,25 @@ use rustc_type_ir::{ use crate::next_solver::{ Binder, Const, ConstKind, DbInterner, Goal, ParamEnv, Predicate, PredicateKind, Span, Term, Ty, - TyKind, + TyKind, TypingMode, fulfill::{FulfillmentCtxt, NextSolverError}, infer::{ - InferCtxt, + DbInternerInferExt, InferCtxt, at::At, traits::{Obligation, ObligationCause}, }, util::PlaceholderReplacer, }; +pub fn normalize<'db, T>(interner: DbInterner<'db>, param_env: ParamEnv<'db>, value: T) -> T +where + T: TypeFoldable>, +{ + let infer_ctxt = interner.infer_ctxt().build(TypingMode::non_body_analysis()); + let cause = ObligationCause::dummy(); + deeply_normalize(infer_ctxt.at(&cause, param_env), value.clone()).unwrap_or(value) +} + /// Deeply normalize all aliases in `value`. This does not handle inference and expects /// its input to be already fully resolved. pub fn deeply_normalize<'db, T>(at: At<'_, 'db>, value: T) -> Result>> diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/primitive.rs b/src/tools/rust-analyzer/crates/hir-ty/src/primitive.rs index a4e077ba6359..d2901f7fc53d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/primitive.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/primitive.rs @@ -34,6 +34,40 @@ pub fn float_ty_to_string(ty: FloatTy) -> &'static str { } } +pub fn int_ty_to_string_ns(ty: rustc_type_ir::IntTy) -> &'static str { + use rustc_type_ir::IntTy; + match ty { + IntTy::Isize => "isize", + IntTy::I8 => "i8", + IntTy::I16 => "i16", + IntTy::I32 => "i32", + IntTy::I64 => "i64", + IntTy::I128 => "i128", + } +} + +pub fn uint_ty_to_string_ns(ty: rustc_type_ir::UintTy) -> &'static str { + use rustc_type_ir::UintTy; + match ty { + UintTy::Usize => "usize", + UintTy::U8 => "u8", + UintTy::U16 => "u16", + UintTy::U32 => "u32", + UintTy::U64 => "u64", + UintTy::U128 => "u128", + } +} + +pub fn float_ty_to_string_ns(ty: rustc_type_ir::FloatTy) -> &'static str { + use rustc_type_ir::FloatTy; + match ty { + FloatTy::F16 => "f16", + FloatTy::F32 => "f32", + FloatTy::F64 => "f64", + FloatTy::F128 => "f128", + } +} + pub(super) fn int_ty_from_builtin(t: BuiltinInt) -> IntTy { match t { BuiltinInt::Isize => IntTy::Isize, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs index d98b6602efe8..1c3da438cb36 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -157,11 +157,13 @@ fn check_impl( }; let range = node.as_ref().original_file_range_rooted(&db); if let Some(expected) = types.remove(&range) { - let actual = if display_source { - ty.display_source_code(&db, def.module(&db), true).unwrap() - } else { - ty.display_test(&db, display_target).to_string() - }; + let actual = salsa::attach(&db, || { + if display_source { + ty.display_source_code(&db, def.module(&db), true).unwrap() + } else { + ty.display_test(&db, display_target).to_string() + } + }); assert_eq!(actual, expected, "type annotation differs at {:#?}", range.range); } } @@ -173,11 +175,13 @@ fn check_impl( }; let range = node.as_ref().original_file_range_rooted(&db); if let Some(expected) = types.remove(&range) { - let actual = if display_source { - ty.display_source_code(&db, def.module(&db), true).unwrap() - } else { - ty.display_test(&db, display_target).to_string() - }; + let actual = salsa::attach(&db, || { + if display_source { + ty.display_source_code(&db, def.module(&db), true).unwrap() + } else { + ty.display_test(&db, display_target).to_string() + } + }); assert_eq!(actual, expected, "type annotation differs at {:#?}", range.range); } if let Some(expected) = adjustments.remove(&range) { @@ -203,11 +207,13 @@ fn check_impl( continue; }; let range = node.as_ref().original_file_range_rooted(&db); - let actual = format!( - "expected {}, got {}", - mismatch.expected.display_test(&db, display_target), - mismatch.actual.display_test(&db, display_target) - ); + let actual = salsa::attach(&db, || { + format!( + "expected {}, got {}", + mismatch.expected.display_test(&db, display_target), + mismatch.actual.display_test(&db, display_target) + ) + }); match mismatches.remove(&range) { Some(annotation) => assert_eq!(actual, annotation), None => format_to!(unexpected_type_mismatches, "{:?}: {}\n", range.range, actual), @@ -402,7 +408,9 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { for (def, krate) in defs { let (body, source_map) = db.body_with_source_map(def); let infer = db.infer(def); - infer_def(infer, body, source_map, krate); + salsa::attach(&db, || { + infer_def(infer, body, source_map, krate); + }) } buf.truncate(buf.trim_end().len()); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs index 5893894c33ab..b001ac1e82ea 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs @@ -67,11 +67,13 @@ fn check_closure_captures(#[rust_analyzer::rust_fixture] ra_fixture: &str, expec .join(", "), }; let place = capture.display_place(closure.0, db); - let capture_ty = capture - .ty - .skip_binders() - .display_test(db, DisplayTarget::from_crate(db, module.krate())) - .to_string(); + let capture_ty = salsa::attach(db, || { + capture + .ty + .skip_binders() + .display_test(db, DisplayTarget::from_crate(db, module.krate())) + .to_string() + }); let spans = capture .spans() .iter() diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs index 36578545a9f4..11d1a58e5367 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs @@ -71,7 +71,7 @@ fn test() { let x = S3.baz(); //^ Binary> let y = x.1.0.bar(); - //^ Unary> + //^ Unary<::Item> } "#, ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index 212fec4a4e4f..fd8c641d86eb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -2299,10 +2299,10 @@ trait Foo { } "#, expect![[r#" - 83..86 'bar': Foo::Bar + 83..86 'bar': ::Bar 105..133 '{ ... }': () - 119..120 '_': Foo::Bar - 123..126 'bar': Foo::Bar + 119..120 '_': ::Bar + 123..126 'bar': ::Bar "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index 9d72105624a0..a311e579744d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -408,11 +408,11 @@ fn test() { let x: ::Item = 1; // ^ u32 let y: ::Item = u; - // ^ Iterable::Item + // ^ ::Item let z: T::Item = u; - // ^ Iterable::Item + // ^ ::Item let a: ::Item = u; - // ^ Iterable::Item + // ^ ::Item }"#, ); } @@ -454,7 +454,7 @@ impl S { fn test() { let s: S; s.foo(); - // ^^^^^^^ Iterable::Item + // ^^^^^^^ ::Item }"#, ); } @@ -470,7 +470,7 @@ trait Foo { type A; fn test(a: Self::A, _: impl Bar) { a; - //^ Foo::A + //^ ::A } }"#, ); @@ -969,7 +969,7 @@ impl ApplyL for RefMutL { fn test() { let y: as ApplyL>::Out = no_matter; y; -} //^ ApplyL::Out +} //^ ::Out "#, ); } @@ -986,7 +986,7 @@ fn foo(t: T) -> ::Out; fn test(t: T) { let y = foo(t); y; -} //^ ApplyL::Out +} //^ ::Out "#, ); } @@ -1695,7 +1695,7 @@ fn test>(x: T, y: impl Trait) { }"#, expect![[r#" 49..50 't': T - 77..79 '{}': Trait::Type + 77..79 '{}': ::Type 111..112 't': T 122..124 '{}': U 154..155 't': T @@ -2293,7 +2293,7 @@ impl Trait for S2 { }"#, expect![[r#" 40..44 'self': &'? Self - 46..47 'x': Trait::Item + 46..47 'x': ::Item 126..130 'self': &'? S 132..133 'x': u32 147..161 '{ let y = x; }': () @@ -2410,7 +2410,7 @@ trait Trait2 {} fn test() where T: Trait2 { let x: T::Item = no_matter; -} //^^^^^^^^^ Trait::Item +} //^^^^^^^^^ ::Item "#, ); } @@ -2445,7 +2445,7 @@ trait Trait { fn test() where T: Trait { let x: T::Item = no_matter; -} //^^^^^^^^^ Trait::Item +} //^^^^^^^^^ ::Item "#, ); } @@ -2475,7 +2475,7 @@ fn test(t: T) where T: UnificationStoreMut { t.push(x); let y: Key; (x, y); -} //^^^^^^ (UnificationStoreBase::Key, UnificationStoreBase::Key) +} //^^^^^^ (::Key, ::Key) "#, ); } @@ -3488,7 +3488,7 @@ fn foo() { let x = ::boo(); }"#, expect![[r#" - 132..163 '{ ... }': Bar::Output + 132..163 '{ ... }': ::Output 146..153 'loop {}': ! 151..153 '{}': () 306..358 '{ ...o(); }': () @@ -4213,7 +4213,7 @@ fn g<'a, T: 'a>(v: impl Trait = &'a T>) { let a = v.get::(); //^ &'a T let a = v.get::<()>(); - //^ Trait::Assoc = &'a T>, ()> + //^ = &'a T> as Trait>::Assoc<()> } fn h<'a>(v: impl Trait = &'a i32> + Trait = &'a i64>) { let a = v.get::(); @@ -4280,7 +4280,7 @@ where let a = t.get::(); //^ usize let a = t.get::<()>(); - //^ Trait::Assoc + //^ ::Assoc<()> } "#, @@ -4857,29 +4857,29 @@ async fn baz i32>(c: T) { 37..38 'a': T 43..83 '{ ...ait; }': () 43..83 '{ ...ait; }': impl Future - 53..57 'fut1': AsyncFnMut::CallRefFuture<'?, T, (u32,)> + 53..57 'fut1': >::CallRefFuture<'?> 60..61 'a': T - 60..64 'a(0)': AsyncFnMut::CallRefFuture<'?, T, (u32,)> + 60..64 'a(0)': >::CallRefFuture<'?> 62..63 '0': u32 - 70..74 'fut1': AsyncFnMut::CallRefFuture<'?, T, (u32,)> + 70..74 'fut1': >::CallRefFuture<'?> 70..80 'fut1.await': i32 124..129 'mut b': T 134..174 '{ ...ait; }': () 134..174 '{ ...ait; }': impl Future - 144..148 'fut2': AsyncFnMut::CallRefFuture<'?, T, (u32,)> + 144..148 'fut2': >::CallRefFuture<'?> 151..152 'b': T - 151..155 'b(0)': AsyncFnMut::CallRefFuture<'?, T, (u32,)> + 151..155 'b(0)': >::CallRefFuture<'?> 153..154 '0': u32 - 161..165 'fut2': AsyncFnMut::CallRefFuture<'?, T, (u32,)> + 161..165 'fut2': >::CallRefFuture<'?> 161..171 'fut2.await': i32 216..217 'c': T 222..262 '{ ...ait; }': () 222..262 '{ ...ait; }': impl Future - 232..236 'fut3': AsyncFnOnce::CallOnceFuture + 232..236 'fut3': >::CallOnceFuture 239..240 'c': T - 239..243 'c(0)': AsyncFnOnce::CallOnceFuture + 239..243 'c(0)': >::CallOnceFuture 241..242 '0': u32 - 249..253 'fut3': AsyncFnOnce::CallOnceFuture + 249..253 'fut3': >::CallOnceFuture 249..259 'fut3.await': i32 "#]], ); @@ -4947,7 +4947,7 @@ where "#, expect![[r#" 84..86 'de': D - 135..138 '{ }': Deserializer::Error<'de, D> + 135..138 '{ }': >::Error "#]], ); } @@ -5020,7 +5020,7 @@ fn main() { 294..298 'iter': Box + 'static> 294..310 'iter.i...iter()': Box + 'static> 152..156 'self': &'? mut Box - 177..208 '{ ... }': Option> + 177..208 '{ ... }': Option<::Item> 191..198 'loop {}': ! 196..198 '{}': () "#]], diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 11486ec8d669..4dde019b5006 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -85,7 +85,9 @@ use hir_ty::{ layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding}, method_resolution, mir::{MutBorrowKind, interpret_mir}, - next_solver::{DbInterner, GenericArgs, SolverDefId, infer::InferCtxt}, + next_solver::{ + DbInterner, GenericArgs, SolverDefId, infer::InferCtxt, mapping::ChalkToNextSolver, + }, primitive::UintTy, traits::FnTrait, }; @@ -1814,12 +1816,15 @@ impl Adt { } pub fn layout(self, db: &dyn HirDatabase) -> Result { - db.layout_of_adt( + let env = db.trait_environment(self.into()); + let interner = DbInterner::new_with(db, Some(env.krate), env.block); + db.layout_of_adt_ns( self.into(), TyBuilder::adt(db, self.into()) .fill_with_defaults(db, || TyKind::Error.intern(Interner)) - .build_into_subst(), - db.trait_environment(self.into()), + .build_into_subst() + .to_nextsolver(interner), + env, ) .map(|layout| Layout(layout, db.target_data_layout(self.krate(db).id).unwrap())) } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs index 7a8c70f190ef..f03f61d10e5f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs @@ -1,3 +1,4 @@ +use base_db::salsa; use expect_test::{Expect, expect}; use hir::HirDisplay; @@ -13,7 +14,11 @@ fn check_expected_type_and_name(#[rust_analyzer::rust_fixture] ra_fixture: &str, let ty = completion_context .expected_type - .map(|t| t.display_test(&db, completion_context.krate.to_display_target(&db)).to_string()) + .map(|t| { + salsa::attach(&db, || { + t.display_test(&db, completion_context.krate.to_display_target(&db)).to_string() + }) + }) .unwrap_or("?".to_owned()); let name = diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index e4c7d42ffe3b..1f9d10c92b1d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -912,7 +912,7 @@ pub(super) fn literal( }; let ty = ty.display(sema.db, display_target); - let mut s = format!("```rust\n{ty}\n```\n___\n\n"); + let mut s = salsa::attach(sema.db, || format!("```rust\n{ty}\n```\n___\n\n")); match value { Ok(value) => { let backtick_len = value.chars().filter(|c| *c == '`').count(); diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index 424890fe370c..d7d317166494 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -738,44 +738,46 @@ fn label_of_ty( config: &InlayHintsConfig, display_target: DisplayTarget, ) -> Result<(), HirDisplayError> { - let iter_item_type = hint_iterator(sema, famous_defs, ty); - match iter_item_type { - Some((iter_trait, item, ty)) => { - const LABEL_START: &str = "impl "; - const LABEL_ITERATOR: &str = "Iterator"; - const LABEL_MIDDLE: &str = "<"; - const LABEL_ITEM: &str = "Item"; - const LABEL_MIDDLE2: &str = " = "; - const LABEL_END: &str = ">"; + salsa::attach(sema.db, || { + let iter_item_type = hint_iterator(sema, famous_defs, ty); + match iter_item_type { + Some((iter_trait, item, ty)) => { + const LABEL_START: &str = "impl "; + const LABEL_ITERATOR: &str = "Iterator"; + const LABEL_MIDDLE: &str = "<"; + const LABEL_ITEM: &str = "Item"; + const LABEL_MIDDLE2: &str = " = "; + const LABEL_END: &str = ">"; - max_length = max_length.map(|len| { - len.saturating_sub( - LABEL_START.len() - + LABEL_ITERATOR.len() - + LABEL_MIDDLE.len() - + LABEL_MIDDLE2.len() - + LABEL_END.len(), - ) - }); + max_length = max_length.map(|len| { + len.saturating_sub( + LABEL_START.len() + + LABEL_ITERATOR.len() + + LABEL_MIDDLE.len() + + LABEL_MIDDLE2.len() + + LABEL_END.len(), + ) + }); - label_builder.write_str(LABEL_START)?; - label_builder.start_location_link(ModuleDef::from(iter_trait).into()); - label_builder.write_str(LABEL_ITERATOR)?; - label_builder.end_location_link(); - label_builder.write_str(LABEL_MIDDLE)?; - label_builder.start_location_link(ModuleDef::from(item).into()); - label_builder.write_str(LABEL_ITEM)?; - label_builder.end_location_link(); - label_builder.write_str(LABEL_MIDDLE2)?; - rec(sema, famous_defs, max_length, &ty, label_builder, config, display_target)?; - label_builder.write_str(LABEL_END)?; - Ok(()) + label_builder.write_str(LABEL_START)?; + label_builder.start_location_link(ModuleDef::from(iter_trait).into()); + label_builder.write_str(LABEL_ITERATOR)?; + label_builder.end_location_link(); + label_builder.write_str(LABEL_MIDDLE)?; + label_builder.start_location_link(ModuleDef::from(item).into()); + label_builder.write_str(LABEL_ITEM)?; + label_builder.end_location_link(); + label_builder.write_str(LABEL_MIDDLE2)?; + rec(sema, famous_defs, max_length, &ty, label_builder, config, display_target)?; + label_builder.write_str(LABEL_END)?; + Ok(()) + } + None => ty + .display_truncated(sema.db, max_length, display_target) + .with_closure_style(config.closure_style) + .write_to(label_builder), } - None => ty - .display_truncated(sema.db, max_length, display_target) - .with_closure_style(config.closure_style) - .write_to(label_builder), - } + }) } let mut label_builder = InlayHintLabelBuilder { diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs index 39554d777795..e39a5f8889a2 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs @@ -10,7 +10,7 @@ use hir::{ Adjust, Adjustment, AutoBorrow, DisplayTarget, HirDisplay, Mutability, OverloadedDeref, PointerCast, Safety, }; -use ide_db::famous_defs::FamousDefs; +use ide_db::{base_db::salsa, famous_defs::FamousDefs}; use ide_db::text_edit::TextEditBuilder; use syntax::ast::{self, AstNode, prec::ExprPrecedence}; @@ -201,13 +201,15 @@ pub(super) fn hints( text: if postfix { format!(".{}", text.trim_end()) } else { text.to_owned() }, linked_location: None, tooltip: Some(config.lazy_tooltip(|| { - InlayTooltip::Markdown(format!( - "`{}` → `{}`\n\n**{}**\n\n{}", - source.display(sema.db, display_target), - target.display(sema.db, display_target), - coercion, - detailed_tooltip - )) + salsa::attach(sema.db, || { + InlayTooltip::Markdown(format!( + "`{}` → `{}`\n\n**{}**\n\n{}", + source.display(sema.db, display_target), + target.display(sema.db, display_target), + coercion, + detailed_tooltip + )) + }) })), }; if postfix { &mut post } else { &mut pre }.label.append_part(label); diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 5349ebb7c82a..1c66473bf987 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -67,7 +67,7 @@ use ide_db::{ FxHashMap, FxIndexSet, LineIndexDatabase, base_db::{ CrateOrigin, CrateWorkspaceData, Env, FileSet, RootQueryDb, SourceDatabase, VfsPath, - salsa::Cancelled, + salsa::{self, Cancelled}, }, prime_caches, symbol_index, }; @@ -461,7 +461,9 @@ impl Analysis { hasher: impl Fn(&InlayHint) -> u64 + Send + UnwindSafe, ) -> Cancellable> { self.with_db(|db| { - inlay_hints::inlay_hints_resolve(db, file_id, resolve_range, hash, config, hasher) + salsa::attach(db, || { + inlay_hints::inlay_hints_resolve(db, file_id, resolve_range, hash, config, hasher) + }) }) } @@ -536,7 +538,7 @@ impl Analysis { config: &HoverConfig, range: FileRange, ) -> Cancellable>> { - self.with_db(|db| hover::hover(db, range, config)) + self.with_db(|db| salsa::attach(db, || hover::hover(db, range, config))) } /// Returns moniker of symbol at position. @@ -544,7 +546,7 @@ impl Analysis { &self, position: FilePosition, ) -> Cancellable>>> { - self.with_db(|db| moniker::moniker(db, position)) + self.with_db(|db| salsa::attach(db, || moniker::moniker(db, position))) } /// Returns URL(s) for the documentation of the symbol under the cursor. @@ -640,7 +642,7 @@ impl Analysis { /// Returns the set of possible targets to run for the current file. pub fn runnables(&self, file_id: FileId) -> Cancellable> { - self.with_db(|db| runnables::runnables(db, file_id)) + self.with_db(|db| salsa::attach(db, || runnables::runnables(db, file_id))) } /// Returns the set of tests for the given file position. diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index 61b4de0e0e39..1b6a4b726e83 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -10,6 +10,7 @@ use hir::{ }; use ide_db::{ FileId, FileRange, RootDatabase, SymbolKind, + base_db::salsa, defs::Definition, documentation::{Documentation, HasDocs}, }; @@ -377,8 +378,9 @@ where ) .map(|mut res| { res.docs = self.docs(db); - res.description = - Some(self.display(db, self.krate(db).to_display_target(db)).to_string()); + res.description = salsa::attach(db, || { + Some(self.display(db, self.krate(db).to_display_target(db)).to_string()) + }); res.container_name = self.container_name(db); res }), @@ -485,8 +487,9 @@ impl TryToNav for hir::Field { NavigationTarget::from_named(db, src.with_value(it), SymbolKind::Field).map( |mut res| { res.docs = self.docs(db); - res.description = - Some(self.display(db, krate.to_display_target(db)).to_string()); + res.description = salsa::attach(db, || { + Some(self.display(db, krate.to_display_target(db)).to_string()) + }); res }, ) diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs index 77c84292cf3e..6d33ddcde047 100644 --- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs +++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs @@ -10,7 +10,7 @@ use hir::{ use ide_assists::utils::{has_test_related_attribute, test_related_attribute_syn}; use ide_db::{ FilePosition, FxHashMap, FxIndexMap, FxIndexSet, RootDatabase, SymbolKind, - base_db::RootQueryDb, + base_db::{RootQueryDb, salsa}, defs::Definition, documentation::docs_from_attrs, helpers::visit_file_defs, @@ -414,11 +414,13 @@ pub(crate) fn runnable_impl( let ty = def.self_ty(sema.db); let adt_name = ty.as_adt()?.name(sema.db); let mut ty_args = ty.generic_parameters(sema.db, display_target).peekable(); - let params = if ty_args.peek().is_some() { - format!("<{}>", ty_args.format_with(",", |ty, cb| cb(&ty))) - } else { - String::new() - }; + let params = salsa::attach(sema.db, || { + if ty_args.peek().is_some() { + format!("<{}>", ty_args.format_with(",", |ty, cb| cb(&ty))) + } else { + String::new() + } + }); let mut test_id = format!("{}{params}", adt_name.display(sema.db, edition)); test_id.retain(|c| c != ' '); let test_id = TestId::Path(test_id); @@ -521,7 +523,9 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option { let mut ty_args = ty.generic_parameters(db, display_target).peekable(); format_to!(path, "{}", name.display(db, edition)); if ty_args.peek().is_some() { - format_to!(path, "<{}>", ty_args.format_with(",", |ty, cb| cb(&ty))); + salsa::attach(db, || { + format_to!(path, "<{}>", ty_args.format_with(",", |ty, cb| cb(&ty))); + }); } format_to!(path, "::{}", def_name.display(db, edition)); path.retain(|c| c != ' '); diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs index 86d02b4098bd..f45d096ac190 100644 --- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs +++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs @@ -11,6 +11,7 @@ use hir::{ use ide_db::{ FilePosition, FxIndexMap, active_parameter::{callable_for_arg_list, generic_def_for_node}, + base_db::salsa, documentation::{Documentation, HasDocs}, }; use itertools::Itertools; @@ -266,12 +267,12 @@ fn signature_help_for_call( // In that case, fall back to render definitions of the respective parameters. // This is overly conservative: we do not substitute known type vars // (see FIXME in tests::impl_trait) and falling back on any unknowns. - match (p.ty().contains_unknown(), fn_params.as_deref()) { + salsa::attach(db, || match (p.ty().contains_unknown(), fn_params.as_deref()) { (true, Some(fn_params)) => { format_to!(buf, "{}", fn_params[idx].ty().display(db, display_target)) } _ => format_to!(buf, "{}", p.ty().display(db, display_target)), - } + }); res.push_call_param(&buf); } } diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs index 694ac22e1993..14b6529c61f8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs +++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs @@ -5,7 +5,7 @@ use arrayvec::ArrayVec; use hir::{Crate, Module, Semantics, db::HirDatabase}; use ide_db::{ FileId, FileRange, FxHashMap, FxHashSet, RootDatabase, - base_db::{RootQueryDb, SourceDatabase, VfsPath}, + base_db::{RootQueryDb, SourceDatabase, VfsPath, salsa}, defs::{Definition, IdentClass}, documentation::Documentation, famous_defs::FamousDefs, @@ -227,30 +227,32 @@ impl StaticIndex<'_> { let id = if let Some(it) = self.def_map.get(&def) { *it } else { - let it = self.tokens.insert(TokenStaticData { - documentation: documentation_for_definition(&sema, def, scope_node), - hover: Some(hover_for_definition( - &sema, - file_id, - def, - None, - scope_node, - None, - false, - &hover_config, - edition, - display_target, - )), - definition: def.try_to_nav(self.db).map(UpmappingResult::call_site).map(|it| { - FileRange { file_id: it.file_id, range: it.focus_or_full_range() } - }), - references: vec![], - moniker: current_crate.and_then(|cc| def_to_moniker(self.db, def, cc)), - display_name: def - .name(self.db) - .map(|name| name.display(self.db, edition).to_string()), - signature: Some(def.label(self.db, display_target)), - kind: def_to_kind(self.db, def), + let it = salsa::attach(sema.db, || { + self.tokens.insert(TokenStaticData { + documentation: documentation_for_definition(&sema, def, scope_node), + hover: Some(hover_for_definition( + &sema, + file_id, + def, + None, + scope_node, + None, + false, + &hover_config, + edition, + display_target, + )), + definition: def.try_to_nav(self.db).map(UpmappingResult::call_site).map( + |it| FileRange { file_id: it.file_id, range: it.focus_or_full_range() }, + ), + references: vec![], + moniker: current_crate.and_then(|cc| def_to_moniker(self.db, def, cc)), + display_name: def + .name(self.db) + .map(|name| name.display(self.db, edition).to_string()), + signature: Some(def.label(self.db, display_target)), + kind: def_to_kind(self.db, def), + }) }); self.def_map.insert(def, it); it diff --git a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs index 63701a4d15e9..1eb0fd4fd8b7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs @@ -3,6 +3,7 @@ use std::fmt; use hir::{DisplayTarget, Field, HirDisplay, Layout, Semantics, Type}; use ide_db::{ RootDatabase, + base_db::salsa, defs::Definition, helpers::{get_definition, pick_best_token}, }; @@ -141,7 +142,9 @@ pub(crate) fn view_memory_layout( if let Ok(child_layout) = child_ty.layout(db) { nodes.push(MemoryLayoutNode { item_name: field.name(db), - typename: child_ty.display(db, display_target).to_string(), + typename: salsa::attach(db, || { + child_ty.display(db, display_target).to_string() + }), size: child_layout.size(), alignment: child_layout.align(), offset: match *field { @@ -175,7 +178,7 @@ pub(crate) fn view_memory_layout( } } - ty.layout(db) + salsa::attach(db, || ty.layout(db)) .map(|layout| { let item_name = match def { // def is a datatype @@ -188,7 +191,7 @@ pub(crate) fn view_memory_layout( def => def.name(db).map(|n| n.as_str().to_owned()).unwrap_or("[ROOT]".to_owned()), }; - let typename = ty.display(db, display_target).to_string(); + let typename = salsa::attach(db, || ty.display(db, display_target).to_string()); let mut nodes = vec![MemoryLayoutNode { item_name, From faf0dd978dc16873e22e62acfe8c54fb81fafe36 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Sun, 10 Aug 2025 00:53:11 +0000 Subject: [PATCH 074/682] Deduplicate layout_of_adt --- .../rust-analyzer/crates/hir-ty/src/db.rs | 17 +++--------- .../crates/hir-ty/src/display.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/layout.rs | 9 +++---- .../crates/hir-ty/src/layout/adt.rs | 27 +++---------------- src/tools/rust-analyzer/crates/hir/src/lib.rs | 2 +- .../rust-analyzer/src/cli/analysis_stats.rs | 9 ++++--- 6 files changed, 19 insertions(+), 47 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 7a5daac69922..161ad31e579b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -94,11 +94,11 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { #[salsa::invoke(crate::layout::layout_of_adt_query)] #[salsa::cycle(cycle_result = crate::layout::layout_of_adt_cycle_result)] - fn layout_of_adt( - &self, + fn layout_of_adt<'db>( + &'db self, def: AdtId, - subst: Substitution, - env: Arc, + args: crate::next_solver::GenericArgs<'db>, + trait_env: Arc, ) -> Result, LayoutError>; #[salsa::invoke(crate::layout::layout_of_ty_query)] @@ -300,15 +300,6 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { // next trait solver - #[salsa::invoke(crate::layout::layout_of_adt_ns_query)] - #[salsa::cycle(cycle_result = crate::layout::layout_of_adt_ns_cycle_result)] - fn layout_of_adt_ns<'db>( - &'db self, - def: AdtId, - args: crate::next_solver::GenericArgs<'db>, - trait_env: Arc, - ) -> Result, LayoutError>; - #[salsa::invoke(crate::layout::layout_of_ty_ns_query)] #[salsa::cycle(cycle_result = crate::layout::layout_of_ty_ns_cycle_result)] fn layout_of_ty_ns<'db>( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 9f87c37834d7..81bc48eecfc8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -944,7 +944,7 @@ fn render_const_scalar_inner( SolverDefId::AdtId(def) => def, _ => unreachable!(), }; - let Ok(layout) = f.db.layout_of_adt_ns(def, args, trait_env.clone()) else { + let Ok(layout) = f.db.layout_of_adt(def, args, trait_env.clone()) else { return f.write_str(""); }; match def { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index fb7b5e1c83e4..0a8ec949b7c0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -31,11 +31,8 @@ use crate::{ }, }; -pub(crate) use self::adt::{layout_of_adt_cycle_result, layout_of_adt_ns_cycle_result}; -pub use self::{ - adt::{layout_of_adt_ns_query, layout_of_adt_query}, - target::target_data_layout_query, -}; +pub(crate) use self::adt::layout_of_adt_cycle_result; +pub use self::{adt::layout_of_adt_query, target::target_data_layout_query}; pub(crate) mod adt; pub(crate) mod target; @@ -197,7 +194,7 @@ pub fn layout_of_ty_ns_query<'db>( } _ => {} } - return db.layout_of_adt_ns(def.inner().id, args, trait_env); + return db.layout_of_adt(def.inner().id, args, trait_env); } TyKind::Bool => Layout::scalar( dl, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs index 2fa01b6b41ab..fefa3f261743 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs @@ -13,23 +13,13 @@ use smallvec::SmallVec; use triomphe::Arc; use crate::{ - Substitution, TraitEnvironment, + TraitEnvironment, db::HirDatabase, layout::{Layout, LayoutCx, LayoutError, field_ty}, - next_solver::{DbInterner, GenericArgs, mapping::ChalkToNextSolver}, + next_solver::GenericArgs, }; -pub fn layout_of_adt_query( - db: &dyn HirDatabase, - def: AdtId, - subst: Substitution, - trait_env: Arc, -) -> Result, LayoutError> { - let interner = DbInterner::new_with(db, Some(trait_env.krate), trait_env.block); - db.layout_of_adt_ns(def, subst.to_nextsolver(interner), trait_env) -} - -pub fn layout_of_adt_ns_query<'db>( +pub fn layout_of_adt_query<'db>( db: &'db dyn HirDatabase, def: AdtId, args: GenericArgs<'db>, @@ -105,16 +95,7 @@ pub fn layout_of_adt_ns_query<'db>( Ok(Arc::new(result)) } -pub(crate) fn layout_of_adt_cycle_result( - _: &dyn HirDatabase, - _: AdtId, - _: Substitution, - _: Arc, -) -> Result, LayoutError> { - Err(LayoutError::RecursiveTypeWithoutIndirection) -} - -pub(crate) fn layout_of_adt_ns_cycle_result<'db>( +pub(crate) fn layout_of_adt_cycle_result<'db>( _: &'db dyn HirDatabase, _def: AdtId, _args: GenericArgs<'db>, diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 4dde019b5006..9accb3336898 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -1818,7 +1818,7 @@ impl Adt { pub fn layout(self, db: &dyn HirDatabase) -> Result { let env = db.trait_environment(self.into()); let interner = DbInterner::new_with(db, Some(env.krate), env.block); - db.layout_of_adt_ns( + db.layout_of_adt( self.into(), TyBuilder::adt(db, self.into()) .fill_with_defaults(db, || TyKind::Error.intern(Interner)) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index 97886844a9f9..6c0aa19f5748 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -10,15 +10,17 @@ use std::{ use cfg::{CfgAtom, CfgDiff}; use hir::{ - Adt, AssocItem, Crate, DefWithBody, HasSource, HirDisplay, ImportPathConfig, ModuleDef, Name, + Adt, AssocItem, Crate, DefWithBody, HasCrate, HasSource, HirDisplay, ImportPathConfig, + ModuleDef, Name, db::{DefDatabase, ExpandDatabase, HirDatabase}, + next_solver::{DbInterner, GenericArgs}, }; use hir_def::{ SyntheticSyntax, expr_store::BodySourceMap, hir::{ExprId, PatId}, }; -use hir_ty::{Interner, Substitution, TyExt, TypeFlags}; +use hir_ty::{Interner, TyExt, TypeFlags}; use ide::{ Analysis, AnalysisHost, AnnotationConfig, DiagnosticsConfig, Edition, InlayFieldsToResolve, InlayHintsConfig, LineCol, RootDatabase, @@ -361,6 +363,7 @@ impl flags::AnalysisStats { let mut all = 0; let mut fail = 0; for &a in adts { + let interner = DbInterner::new_with(db, Some(a.krate(db).base()), None); let generic_params = db.generic_params(a.into()); if generic_params.iter_type_or_consts().next().is_some() || generic_params.iter_lt().next().is_some() @@ -371,7 +374,7 @@ impl flags::AnalysisStats { all += 1; let Err(e) = db.layout_of_adt( hir_def::AdtId::from(a), - Substitution::empty(Interner), + GenericArgs::new_from_iter(interner, []), db.trait_environment(a.into()), ) else { continue; From 1b03009e7c34db88899228fb7a47a655e4b77a3c Mon Sep 17 00:00:00 2001 From: jackh726 Date: Sun, 10 Aug 2025 18:00:40 +0000 Subject: [PATCH 075/682] Convert some of mir/eval to next-solver types --- .../crates/hir-ty/src/consteval/tests.rs | 2 +- .../crates/hir-ty/src/display.rs | 17 +- .../rust-analyzer/crates/hir-ty/src/lib.rs | 41 ++- .../rust-analyzer/crates/hir-ty/src/mir.rs | 2 +- .../crates/hir-ty/src/mir/eval.rs | 282 ++++++++++++------ .../crates/hir-ty/src/mir/eval/shim.rs | 25 +- .../crates/hir-ty/src/mir/eval/tests.rs | 2 +- .../crates/hir-ty/src/next_solver/consts.rs | 12 +- .../crates/hir-ty/src/next_solver/mapping.rs | 7 +- 9 files changed, 258 insertions(+), 132 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs index 22b152fe034a..299b73a7d6cc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs @@ -76,7 +76,7 @@ fn check_str(#[rust_analyzer::rust_fixture] ra_fixture: &str, answer: &str) { #[track_caller] fn check_answer( #[rust_analyzer::rust_fixture] ra_fixture: &str, - check: impl FnOnce(&[u8], &MemoryMap), + check: impl FnOnce(&[u8], &MemoryMap<'_>), ) { let (db, file_ids) = TestDB::with_many_files(ra_fixture); let file_id = *file_ids.last().unwrap(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 81bc48eecfc8..5adbea75a67d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -744,20 +744,20 @@ impl HirDisplay for Const { fn render_const_scalar( f: &mut HirFormatter<'_>, b: &[u8], - memory_map: &MemoryMap, + memory_map: &MemoryMap<'_>, ty: &Ty, ) -> Result<(), HirDisplayError> { let trait_env = TraitEnvironment::empty(f.krate()); let interner = DbInterner::new_with(f.db, Some(trait_env.krate), trait_env.block); let ty = normalize(f.db, trait_env.clone(), ty.clone()); let ty = ty.to_nextsolver(interner); - render_const_scalar_inner(f, b, memory_map, ty, trait_env, interner) + render_const_scalar_inner(f, b, memory_map, ty, trait_env) } fn render_const_scalar_ns( f: &mut HirFormatter<'_>, b: &[u8], - memory_map: &MemoryMap, + memory_map: &MemoryMap<'_>, ty: crate::next_solver::Ty<'_>, ) -> Result<(), HirDisplayError> { let trait_env = TraitEnvironment::empty(f.krate()); @@ -767,16 +767,15 @@ fn render_const_scalar_ns( trait_env.env.to_nextsolver(interner), ty, ); - render_const_scalar_inner(f, b, memory_map, ty, trait_env, interner) + render_const_scalar_inner(f, b, memory_map, ty, trait_env) } fn render_const_scalar_inner( f: &mut HirFormatter<'_>, b: &[u8], - memory_map: &MemoryMap, + memory_map: &MemoryMap<'_>, ty: crate::next_solver::Ty<'_>, trait_env: Arc, - interner: DbInterner<'_>, ) -> Result<(), HirDisplayError> { use rustc_type_ir::TyKind; match ty.kind() { @@ -875,7 +874,7 @@ fn render_const_scalar_inner( let Ok(t) = memory_map.vtable_ty(ty_id) else { return f.write_str(""); }; - let Ok(layout) = f.db.layout_of_ty(t.clone(), trait_env) else { + let Ok(layout) = f.db.layout_of_ty_ns(t, trait_env) else { return f.write_str(""); }; let size = layout.size.bytes_usize(); @@ -883,7 +882,7 @@ fn render_const_scalar_inner( return f.write_str(""); }; f.write_str("&")?; - render_const_scalar_ns(f, bytes, memory_map, t.to_nextsolver(interner)) + render_const_scalar_ns(f, bytes, memory_map, t) } TyKind::Adt(adt, _) if b.len() == 2 * size_of::() => match adt.def_id() { SolverDefId::AdtId(hir_def::AdtId::StructId(s)) => { @@ -1052,7 +1051,7 @@ fn render_variant_after_name( layout: &Layout, args: GenericArgs<'_>, b: &[u8], - memory_map: &MemoryMap, + memory_map: &MemoryMap<'_>, ) -> Result<(), HirDisplayError> { let interner = DbInterner::new_with(f.db, Some(trait_env.krate), trait_env.block); match data.shape { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 81894830b5b4..8ce0aeb5532a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -212,20 +212,20 @@ pub(crate) type ProgramClause = chalk_ir::ProgramClause; /// the necessary bits of memory of the const eval session to keep the constant /// meaningful. #[derive(Debug, Default, Clone, PartialEq, Eq)] -pub enum MemoryMap { +pub enum MemoryMap<'db> { #[default] Empty, Simple(Box<[u8]>), - Complex(Box), + Complex(Box>), } #[derive(Debug, Default, Clone, PartialEq, Eq)] -pub struct ComplexMemoryMap { +pub struct ComplexMemoryMap<'db> { memory: IndexMap, FxBuildHasher>, - vtable: VTableMap, + vtable: VTableMap<'db>, } -impl ComplexMemoryMap { +impl ComplexMemoryMap<'_> { fn insert(&mut self, addr: usize, val: Box<[u8]>) { match self.memory.entry(addr) { Entry::Occupied(mut e) => { @@ -240,8 +240,8 @@ impl ComplexMemoryMap { } } -impl MemoryMap { - pub fn vtable_ty(&self, id: usize) -> Result<&Ty, MirEvalError> { +impl<'db> MemoryMap<'db> { + pub fn vtable_ty(&self, id: usize) -> Result, MirEvalError> { match self { MemoryMap::Empty | MemoryMap::Simple(_) => Err(MirEvalError::InvalidVTableId(id)), MemoryMap::Complex(cm) => cm.vtable.ty(id), @@ -291,10 +291,11 @@ impl MemoryMap { } } +// FIXME(next-solver): /// A concrete constant value #[derive(Debug, Clone, PartialEq, Eq)] pub enum ConstScalar { - Bytes(Box<[u8]>, MemoryMap), + Bytes(Box<[u8]>, MemoryMap<'static>), // FIXME: this is a hack to get around chalk not being able to represent unevaluatable // constants UnevaluatedConst(GeneralConstId, Substitution), @@ -315,6 +316,30 @@ impl Hash for ConstScalar { } } +/// A concrete constant value +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ConstScalarNs<'db> { + Bytes(Box<[u8]>, MemoryMap<'db>), + // FIXME: this is a hack to get around chalk not being able to represent unevaluatable + // constants + UnevaluatedConst(GeneralConstId, Substitution), + /// Case of an unknown value that rustc might know but we don't + // FIXME: this is a hack to get around chalk not being able to represent unevaluatable + // constants + // https://github.com/rust-lang/rust-analyzer/pull/8813#issuecomment-840679177 + // https://rust-lang.zulipchat.com/#narrow/stream/144729-wg-traits/topic/Handling.20non.20evaluatable.20constants'.20equality/near/238386348 + Unknown, +} + +impl Hash for ConstScalarNs<'_> { + fn hash(&self, state: &mut H) { + core::mem::discriminant(self).hash(state); + if let ConstScalarNs::Bytes(b, _) = self { + b.hash(state) + } + } +} + /// Return an index of a parameter in the generic type parameter list by it's id. pub fn param_idx(db: &dyn HirDatabase, id: TypeOrConstParamId) -> Option { generics::generics(db, id.parent).type_or_const_param_idx(id) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs index 482b420279c9..8c48a16537a2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs @@ -107,7 +107,7 @@ pub enum OperandKind { } impl Operand { - fn from_concrete_const(data: Box<[u8]>, memory_map: MemoryMap, ty: Ty) -> Self { + fn from_concrete_const(data: Box<[u8]>, memory_map: MemoryMap<'static>, ty: Ty) -> Self { Operand { kind: OperandKind::Constant(intern_const_scalar( ConstScalar::Bytes(data, memory_map), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index dfb8ae704b99..d0ae92961efc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -25,21 +25,26 @@ use rustc_apfloat::{ ieee::{Half as f16, Quad as f128}, }; use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_type_ir::inherent::{AdtDef, IntoKind, SliceLike, Ty as _}; use span::FileId; use stdx::never; use syntax::{SyntaxNodePtr, TextRange}; use triomphe::Arc; use crate::{ - AliasTy, CallableDefId, ClosureId, ComplexMemoryMap, Const, ConstData, ConstScalar, FnDefId, - Interner, MemoryMap, Substitution, ToChalk, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, + AliasTy, CallableDefId, ClosureId, ComplexMemoryMap, Const, ConstData, ConstScalar, Interner, + MemoryMap, Substitution, ToChalk, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, consteval::{ConstEvalError, intern_const_scalar, try_const_usize}, + consteval_nextsolver, db::{HirDatabase, InternedClosure}, display::{ClosureStyle, DisplayTarget, HirDisplay}, infer::PointerCast, layout::{Layout, LayoutError, RustcEnumVariantIdx}, - mapping::from_chalk, method_resolution::{is_dyn_method, lookup_impl_const}, + next_solver::{ + Ctor, DbInterner, SolverDefId, + mapping::{ChalkToNextSolver, convert_args_for_result, convert_ty_for_result}, + }, static_lifetime, traits::FnTrait, utils::{ClosureSubst, detect_variant_from_bytes}, @@ -78,31 +83,31 @@ macro_rules! not_supported { } #[derive(Debug, Default, Clone, PartialEq, Eq)] -pub struct VTableMap { - ty_to_id: FxHashMap, - id_to_ty: Vec, +pub struct VTableMap<'db> { + ty_to_id: FxHashMap, usize>, + id_to_ty: Vec>, } -impl VTableMap { +impl<'db> VTableMap<'db> { const OFFSET: usize = 1000; // We should add some offset to ids to make 0 (null) an invalid id. - fn id(&mut self, ty: Ty) -> usize { + fn id(&mut self, ty: crate::next_solver::Ty<'db>) -> usize { if let Some(it) = self.ty_to_id.get(&ty) { return *it; } let id = self.id_to_ty.len() + VTableMap::OFFSET; - self.id_to_ty.push(ty.clone()); + self.id_to_ty.push(ty); self.ty_to_id.insert(ty, id); id } - pub(crate) fn ty(&self, id: usize) -> Result<&Ty> { + pub(crate) fn ty(&self, id: usize) -> Result> { id.checked_sub(VTableMap::OFFSET) - .and_then(|id| self.id_to_ty.get(id)) + .and_then(|id| self.id_to_ty.get(id).copied()) .ok_or(MirEvalError::InvalidVTableId(id)) } - fn ty_of_bytes(&self, bytes: &[u8]) -> Result<&Ty> { + fn ty_of_bytes(&self, bytes: &[u8]) -> Result> { let id = from_bytes!(usize, bytes); self.ty(id) } @@ -170,12 +175,12 @@ pub struct Evaluator<'a> { /// We don't really have function pointers, i.e. pointers to some assembly instructions that we can run. Instead, we /// store the type as an interned id in place of function and vtable pointers, and we recover back the type at the /// time of use. - vtable_map: VTableMap, + vtable_map: VTableMap<'a>, thread_local_storage: TlsData, random_state: oorandom::Rand64, stdout: Vec, stderr: Vec, - layout_cache: RefCell>>, + layout_cache: RefCell, Arc>>, projected_ty_cache: RefCell>, not_special_fn_cache: RefCell>, mir_or_dyn_index_cache: RefCell>, @@ -224,7 +229,7 @@ impl Interval { Self { addr, size } } - fn get<'a>(&self, memory: &'a Evaluator<'a>) -> Result<&'a [u8]> { + fn get<'a, 'db>(&self, memory: &'a Evaluator<'db>) -> Result<&'a [u8]> { memory.read_memory(self.addr, self.size) } @@ -242,7 +247,7 @@ impl Interval { } impl IntervalAndTy { - fn get<'a>(&self, memory: &'a Evaluator<'a>) -> Result<&'a [u8]> { + fn get<'a, 'db>(&self, memory: &'a Evaluator<'db>) -> Result<&'a [u8]> { memory.read_memory(self.interval.addr, self.interval.size) } @@ -269,7 +274,7 @@ impl From for IntervalOrOwned { } impl IntervalOrOwned { - fn get<'a>(&'a self, memory: &'a Evaluator<'a>) -> Result<&'a [u8]> { + fn get<'a, 'db>(&'a self, memory: &'a Evaluator<'db>) -> Result<&'a [u8]> { Ok(match self { IntervalOrOwned::Owned(o) => o, IntervalOrOwned::Borrowed(b) => b.get(memory)?, @@ -608,7 +613,13 @@ pub fn interpret_mir( memory_map.vtable.shrink_to_fit(); MemoryMap::Complex(Box::new(memory_map)) }; - Ok(intern_const_scalar(ConstScalar::Bytes(bytes, memory_map), ty)) + // SAFETY: will never use this without a db + Ok(intern_const_scalar( + ConstScalar::Bytes(bytes, unsafe { + std::mem::transmute::, MemoryMap<'static>>(memory_map) + }), + ty, + )) })(); Ok((it, MirOutput { stdout: evaluator.stdout, stderr: evaluator.stderr })) } @@ -618,7 +629,7 @@ const EXECUTION_LIMIT: usize = 100_000; #[cfg(not(test))] const EXECUTION_LIMIT: usize = 10_000_000; -impl Evaluator<'_> { +impl<'db> Evaluator<'db> { pub fn new( db: &dyn HirDatabase, owner: DefWithBodyId, @@ -719,6 +730,7 @@ impl Evaluator<'_> { p: &Place, locals: &'a Locals, ) -> Result<(Address, Ty, Option)> { + let interner = DbInterner::new_with(self.db, None, None); let mut addr = locals.ptr[p.local].addr; let mut ty: Ty = locals.body.locals[p.local].ty.clone(); let mut metadata: Option = None; // locals are always sized @@ -791,19 +803,19 @@ impl Evaluator<'_> { addr = addr.offset(ty_size * (from as usize)); } &ProjectionElem::ClosureField(f) => { - let layout = self.layout(&prev_ty)?; + let layout = self.layout(prev_ty.to_nextsolver(interner))?; let offset = layout.fields.offset(f).bytes_usize(); addr = addr.offset(offset); metadata = None; } ProjectionElem::Field(Either::Right(f)) => { - let layout = self.layout(&prev_ty)?; + let layout = self.layout(prev_ty.to_nextsolver(interner))?; let offset = layout.fields.offset(f.index as usize).bytes_usize(); addr = addr.offset(offset); metadata = None; // tuple field is always sized FIXME: This is wrong, the tail can be unsized } ProjectionElem::Field(Either::Left(f)) => { - let layout = self.layout(&prev_ty)?; + let layout = self.layout(prev_ty.to_nextsolver(interner))?; let variant_layout = match &layout.variants { Variants::Single { .. } | Variants::Empty => &layout, Variants::Multiple { variants, .. } => { @@ -835,20 +847,28 @@ impl Evaluator<'_> { Ok((addr, ty, metadata)) } - fn layout(&self, ty: &Ty) -> Result> { - if let Some(x) = self.layout_cache.borrow().get(ty) { + fn layout(&self, ty: crate::next_solver::Ty<'db>) -> Result> { + if let Some(x) = self.layout_cache.borrow().get(&ty) { return Ok(x.clone()); } + let interner = DbInterner::new_with(self.db, None, None); let r = self .db - .layout_of_ty(ty.clone(), self.trait_env.clone()) - .map_err(|e| MirEvalError::LayoutError(e, ty.clone()))?; - self.layout_cache.borrow_mut().insert(ty.clone(), r.clone()); + .layout_of_ty_ns(ty, self.trait_env.clone()) + .map_err(|e| MirEvalError::LayoutError(e, convert_ty_for_result(interner, ty)))?; + self.layout_cache.borrow_mut().insert(ty, r.clone()); Ok(r) } fn layout_adt(&self, adt: AdtId, subst: Substitution) -> Result> { - self.layout(&TyKind::Adt(chalk_ir::AdtId(adt), subst).intern(Interner)) + let interner = DbInterner::new_with(self.db, None, None); + self.layout(crate::next_solver::Ty::new( + interner, + rustc_type_ir::TyKind::Adt( + crate::next_solver::AdtDef::new(adt, interner), + subst.to_nextsolver(interner), + ), + )) } fn place_ty<'a>(&'a self, p: &Place, locals: &'a Locals) -> Result { @@ -952,7 +972,7 @@ impl Evaluator<'_> { )? } TyKind::FnDef(def, generic_args) => self.exec_fn_def( - *def, + CallableDefId::from_chalk(self.db, *def), generic_args, destination_interval, &args, @@ -1113,6 +1133,7 @@ impl Evaluator<'_> { } fn eval_rvalue(&mut self, r: &Rvalue, locals: &mut Locals) -> Result { + let interner = DbInterner::new_with(self.db, None, None); use IntervalOrOwned::*; Ok(match r { Rvalue::Use(it) => Borrowed(self.eval_operand(it, locals)?), @@ -1436,7 +1457,7 @@ impl Evaluator<'_> { Owned(r) } AggregateKind::Tuple(ty) => { - let layout = self.layout(ty)?; + let layout = self.layout(ty.to_nextsolver(interner))?; Owned(self.construct_with_layout( layout.size.bytes_usize(), &layout, @@ -1467,7 +1488,7 @@ impl Evaluator<'_> { )?) } AggregateKind::Closure(ty) => { - let layout = self.layout(ty)?; + let layout = self.layout(ty.to_nextsolver(interner))?; Owned(self.construct_with_layout( layout.size.bytes_usize(), &layout, @@ -1484,6 +1505,8 @@ impl Evaluator<'_> { if let TyKind::FnDef(_, _) | TyKind::Closure(_, _) = ¤t_ty.kind(Interner) { + let interner = DbInterner::new_with(self.db, None, None); + let current_ty = current_ty.to_nextsolver(interner); let id = self.vtable_map.id(current_ty); let ptr_size = self.ptr_size(); Owned(id.to_le_bytes()[0..ptr_size].to_vec()) @@ -1623,7 +1646,8 @@ impl Evaluator<'_> { } fn compute_discriminant(&self, ty: Ty, bytes: &[u8]) -> Result { - let layout = self.layout(&ty)?; + let interner = DbInterner::new_with(self.db, None, None); + let layout = self.layout(ty.to_nextsolver(interner))?; let &TyKind::Adt(chalk_ir::AdtId(AdtId::EnumId(e)), _) = ty.kind(Interner) else { return Ok(0); }; @@ -1732,6 +1756,8 @@ impl Evaluator<'_> { } }, TyKind::Dyn(_) => { + let interner = DbInterner::new_with(self.db, None, None); + let current_ty = current_ty.to_nextsolver(interner); let vtable = self.vtable_map.id(current_ty); let mut r = Vec::with_capacity(16); let addr = addr.get(self)?; @@ -1777,6 +1803,7 @@ impl Evaluator<'_> { subst: Substitution, locals: &Locals, ) -> Result<(usize, Arc, Option<(usize, usize, i128)>)> { + let interner = DbInterner::new_with(self.db, None, None); let adt = it.adt_id(self.db); if let DefWithBodyId::VariantId(f) = locals.body.owner && let VariantId::EnumVariantId(it) = it @@ -1786,7 +1813,11 @@ impl Evaluator<'_> { // Computing the exact size of enums require resolving the enum discriminants. In order to prevent loops (and // infinite sized type errors) we use a dummy layout let i = self.const_eval_discriminant(it)?; - return Ok((16, self.layout(&TyBuilder::unit())?, Some((0, 16, i)))); + return Ok(( + 16, + self.layout(crate::next_solver::Ty::new_tup(interner, &[]))?, + Some((0, 16, i)), + )); } let layout = self.layout_adt(adt, subst)?; Ok(match &layout.variants { @@ -1885,6 +1916,7 @@ impl Evaluator<'_> { #[allow(clippy::double_parens)] fn allocate_const_in_heap(&mut self, locals: &Locals, konst: &Const) -> Result { + let interner = DbInterner::new_with(self.db, None, None); let ConstData { ty, value: chalk_ir::ConstValue::Concrete(c) } = &konst.data(Interner) else { not_supported!("evaluating non concrete constant"); @@ -1945,7 +1977,7 @@ impl Evaluator<'_> { MemoryMap::Complex(cm) => cm.vtable.ty_of_bytes(bytes), }, addr, - ty, + ty.to_nextsolver(interner), locals, )?; Ok(Interval::new(addr, size)) @@ -2048,7 +2080,8 @@ impl Evaluator<'_> { } fn size_align_of(&self, ty: &Ty, locals: &Locals) -> Result> { - if let Some(layout) = self.layout_cache.borrow().get(ty) { + let interner = DbInterner::new_with(self.db, None, None); + if let Some(layout) = self.layout_cache.borrow().get(&ty.to_nextsolver(interner)) { return Ok(layout .is_sized() .then(|| (layout.size.bytes_usize(), layout.align.abi.bytes() as usize))); @@ -2061,7 +2094,7 @@ impl Evaluator<'_> { // infinite sized type errors) we use a dummy size return Ok(Some((16, 16))); } - let layout = self.layout(ty); + let layout = self.layout(ty.to_nextsolver(interner)); if self.assert_placeholder_ty_is_unused && matches!(layout, Err(MirEvalError::LayoutError(LayoutError::HasPlaceholder, _))) { @@ -2129,15 +2162,16 @@ impl Evaluator<'_> { bytes: &[u8], ty: &Ty, locals: &Locals, - ) -> Result { - fn rec( - this: &Evaluator<'_>, + ) -> Result> { + fn rec<'db>( + this: &Evaluator<'db>, bytes: &[u8], ty: &Ty, locals: &Locals, - mm: &mut ComplexMemoryMap, + mm: &mut ComplexMemoryMap<'db>, stack_depth_limit: usize, ) -> Result<()> { + let interner = DbInterner::new_with(this.db, None, None); if stack_depth_limit.checked_sub(1).is_none() { return Err(MirEvalError::StackOverflow); } @@ -2158,13 +2192,14 @@ impl Evaluator<'_> { let element_size = match t.kind(Interner) { TyKind::Str => 1, TyKind::Slice(t) => { - check_inner = Some(t); + check_inner = Some(t.clone()); this.size_of_sized(t, locals, "slice inner type")? } TyKind::Dyn(_) => { let t = this.vtable_map.ty_of_bytes(meta)?; - check_inner = Some(t); - this.size_of_sized(t, locals, "dyn concrete type")? + let t = convert_ty_for_result(interner, t); + check_inner = Some(t.clone()); + this.size_of_sized(&t, locals, "dyn concrete type")? } _ => return Ok(()), }; @@ -2176,7 +2211,7 @@ impl Evaluator<'_> { let addr = Address::from_bytes(addr)?; let b = this.read_memory(addr, size)?; mm.insert(addr.to_usize(), b.into()); - if let Some(ty) = check_inner { + if let Some(ty) = &check_inner { for i in 0..count { let offset = element_size * i; rec( @@ -2211,11 +2246,11 @@ impl Evaluator<'_> { } } TyKind::Tuple(_, subst) => { - let layout = this.layout(ty)?; + let layout = this.layout(ty.to_nextsolver(interner))?; for (id, ty) in subst.iter(Interner).enumerate() { let ty = ty.assert_ty_ref(Interner); // Tuple only has type argument let offset = layout.fields.offset(id).bytes_usize(); - let size = this.layout(ty)?.size.bytes_usize(); + let size = this.layout(ty.to_nextsolver(interner))?.size.bytes_usize(); rec( this, &bytes[offset..offset + size], @@ -2229,7 +2264,7 @@ impl Evaluator<'_> { TyKind::Adt(adt, subst) => match adt.0 { AdtId::StructId(s) => { let data = s.fields(this.db); - let layout = this.layout(ty)?; + let layout = this.layout(ty.to_nextsolver(interner))?; let field_types = this.db.field_types(s.into()); for (f, _) in data.fields().iter() { let offset = layout @@ -2237,7 +2272,7 @@ impl Evaluator<'_> { .offset(u32::from(f.into_raw()) as usize) .bytes_usize(); let ty = &field_types[f].clone().substitute(Interner, subst); - let size = this.layout(ty)?.size.bytes_usize(); + let size = this.layout(ty.to_nextsolver(interner))?.size.bytes_usize(); rec( this, &bytes[offset..offset + size], @@ -2249,7 +2284,7 @@ impl Evaluator<'_> { } } AdtId::EnumId(e) => { - let layout = this.layout(ty)?; + let layout = this.layout(ty.to_nextsolver(interner))?; if let Some((v, l)) = detect_variant_from_bytes( &layout, this.db, @@ -2263,7 +2298,8 @@ impl Evaluator<'_> { let offset = l.fields.offset(u32::from(f.into_raw()) as usize).bytes_usize(); let ty = &field_types[f].clone().substitute(Interner, subst); - let size = this.layout(ty)?.size.bytes_usize(); + let size = + this.layout(ty.to_nextsolver(interner))?.size.bytes_usize(); rec( this, &bytes[offset..offset + size], @@ -2290,20 +2326,26 @@ impl Evaluator<'_> { Ok(mm) } - fn patch_addresses<'vtable>( + fn patch_addresses( &mut self, patch_map: &FxHashMap, - ty_of_bytes: impl Fn(&[u8]) -> Result<&'vtable Ty> + Copy, + ty_of_bytes: impl Fn(&[u8]) -> Result> + Copy, addr: Address, - ty: &Ty, + ty: crate::next_solver::Ty<'db>, locals: &Locals, ) -> Result<()> { + let interner = DbInterner::new_with(self.db, None, None); // FIXME: support indirect references let layout = self.layout(ty)?; - let my_size = self.size_of_sized(ty, locals, "value to patch address")?; - match ty.kind(Interner) { - TyKind::Ref(_, _, t) => { - let size = self.size_align_of(t, locals)?; + let my_size = self.size_of_sized( + &convert_ty_for_result(interner, ty), + locals, + "value to patch address", + )?; + use rustc_type_ir::TyKind; + match ty.kind() { + TyKind::Ref(_, t, _) => { + let size = self.size_align_of(&convert_ty_for_result(interner, t), locals)?; match size { Some(_) => { let current = from_bytes!(usize, self.read_memory(addr, my_size)?); @@ -2319,27 +2361,27 @@ impl Evaluator<'_> { } } } - TyKind::Function(_) => { - let ty = ty_of_bytes(self.read_memory(addr, my_size)?)?.clone(); + TyKind::FnPtr(_, _) => { + let ty = ty_of_bytes(self.read_memory(addr, my_size)?)?; let new_id = self.vtable_map.id(ty); self.write_memory(addr, &new_id.to_le_bytes())?; } - TyKind::Adt(id, subst) => match id.0 { - AdtId::StructId(s) => { - for (i, (_, ty)) in self.db.field_types(s.into()).iter().enumerate() { + TyKind::Adt(id, args) => match id.def_id() { + SolverDefId::AdtId(AdtId::StructId(s)) => { + for (i, (_, ty)) in self.db.field_types_ns(s.into()).iter().enumerate() { let offset = layout.fields.offset(i).bytes_usize(); - let ty = ty.clone().substitute(Interner, subst); + let ty = ty.instantiate(interner, args); self.patch_addresses( patch_map, ty_of_bytes, addr.offset(offset), - &ty, + ty, locals, )?; } } - AdtId::UnionId(_) => (), - AdtId::EnumId(e) => { + SolverDefId::AdtId(AdtId::UnionId(_)) => (), + SolverDefId::AdtId(AdtId::EnumId(e)) => { if let Some((ev, layout)) = detect_variant_from_bytes( &layout, self.db, @@ -2347,33 +2389,37 @@ impl Evaluator<'_> { self.read_memory(addr, layout.size.bytes_usize())?, e, ) { - for (i, (_, ty)) in self.db.field_types(ev.into()).iter().enumerate() { + for (i, (_, ty)) in self.db.field_types_ns(ev.into()).iter().enumerate() { let offset = layout.fields.offset(i).bytes_usize(); - let ty = ty.clone().substitute(Interner, subst); + let ty = ty.instantiate(interner, args); self.patch_addresses( patch_map, ty_of_bytes, addr.offset(offset), - &ty, + ty, locals, )?; } } } + _ => unreachable!(), }, - TyKind::Tuple(_, subst) => { - for (id, ty) in subst.iter(Interner).enumerate() { - let ty = ty.assert_ty_ref(Interner); // Tuple only has type argument + TyKind::Tuple(tys) => { + for (id, ty) in tys.iter().enumerate() { let offset = layout.fields.offset(id).bytes_usize(); self.patch_addresses(patch_map, ty_of_bytes, addr.offset(offset), ty, locals)?; } } TyKind::Array(inner, len) => { - let len = match try_const_usize(self.db, len) { + let len = match consteval_nextsolver::try_const_usize(self.db, len) { Some(it) => it as usize, None => not_supported!("non evaluatable array len in patching addresses"), }; - let size = self.size_of_sized(inner, locals, "inner of array")?; + let size = self.size_of_sized( + &convert_ty_for_result(interner, inner), + locals, + "inner of array", + )?; for i in 0..len { self.patch_addresses( patch_map, @@ -2384,11 +2430,13 @@ impl Evaluator<'_> { )?; } } - TyKind::AssociatedType(_, _) - | TyKind::Scalar(_) + TyKind::Bool + | TyKind::Char + | TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) | TyKind::Slice(_) - | TyKind::Raw(_, _) - | TyKind::OpaqueType(_, _) + | TyKind::RawPtr(_, _) | TyKind::FnDef(_, _) | TyKind::Str | TyKind::Never @@ -2396,12 +2444,16 @@ impl Evaluator<'_> { | TyKind::Coroutine(_, _) | TyKind::CoroutineWitness(_, _) | TyKind::Foreign(_) - | TyKind::Error + | TyKind::Error(_) | TyKind::Placeholder(_) - | TyKind::Dyn(_) - | TyKind::Alias(_) - | TyKind::BoundVar(_) - | TyKind::InferenceVar(_, _) => (), + | TyKind::Dynamic(_, _, _) + | TyKind::Alias(_, _) + | TyKind::Bound(_, _) + | TyKind::Infer(_) + | TyKind::Pat(_, _) + | TyKind::Param(_) + | TyKind::UnsafeBinder(_) + | TyKind::CoroutineClosure(_, _) => (), } Ok(()) } @@ -2416,13 +2468,41 @@ impl Evaluator<'_> { span: MirSpan, ) -> Result> { let id = from_bytes!(usize, bytes.get(self)?); - let next_ty = self.vtable_map.ty(id)?.clone(); - match next_ty.kind(Interner) { + let next_ty = self.vtable_map.ty(id)?; + let interner = DbInterner::new_with(self.db, None, None); + use rustc_type_ir::TyKind; + match next_ty.kind() { TyKind::FnDef(def, generic_args) => { - self.exec_fn_def(*def, generic_args, destination, args, locals, target_bb, span) + let def = match def { + SolverDefId::FunctionId(id) => CallableDefId::FunctionId(id), + SolverDefId::Ctor(Ctor::Struct(s)) => CallableDefId::StructId(s), + SolverDefId::Ctor(Ctor::Enum(e)) => CallableDefId::EnumVariantId(e), + _ => unreachable!(), + }; + self.exec_fn_def( + def, + &convert_args_for_result(interner, generic_args.as_slice()), + destination, + args, + locals, + target_bb, + span, + ) } - TyKind::Closure(id, subst) => { - self.exec_closure(*id, bytes.slice(0..0), subst, destination, args, locals, span) + TyKind::Closure(id, generic_args) => { + let id = match id { + SolverDefId::InternedClosureId(id) => id, + _ => unreachable!(), + }; + self.exec_closure( + id.into(), + bytes.slice(0..0), + &convert_args_for_result(interner, generic_args.as_slice()), + destination, + args, + locals, + span, + ) } _ => Err(MirEvalError::InternalError("function pointer to non function".into())), } @@ -2469,7 +2549,7 @@ impl Evaluator<'_> { fn exec_fn_def( &mut self, - def: FnDefId, + def: CallableDefId, generic_args: &Substitution, destination: Interval, args: &[IntervalAndTy], @@ -2477,7 +2557,6 @@ impl Evaluator<'_> { target_bb: Option, span: MirSpan, ) -> Result> { - let def: CallableDefId = from_chalk(self.db, def); let generic_args = generic_args.clone(); match def { CallableDefId::FunctionId(def) => { @@ -2574,6 +2653,7 @@ impl Evaluator<'_> { target_bb: Option, span: MirSpan, ) -> Result> { + let interner = DbInterner::new_with(self.db, None, None); if self.detect_and_exec_special_function( def, args, @@ -2600,6 +2680,7 @@ impl Evaluator<'_> { .vtable_map .ty_of_bytes(&first_arg[self.ptr_size()..self.ptr_size() * 2])?; let mut args_for_target = args.to_vec(); + let ty = convert_ty_for_result(interner, ty); args_for_target[0] = IntervalAndTy { interval: args_for_target[0].interval.slice(0..self.ptr_size()), ty: ty.clone(), @@ -2672,6 +2753,7 @@ impl Evaluator<'_> { target_bb: Option, span: MirSpan, ) -> Result> { + let interner = DbInterner::new_with(self.db, None, None); let func = args .first() .ok_or_else(|| MirEvalError::InternalError("fn trait with no arg".into()))?; @@ -2683,15 +2765,21 @@ impl Evaluator<'_> { let id = from_bytes!(usize, &func_data.get(self)?[self.ptr_size()..self.ptr_size() * 2]); func_data = func_data.slice(0..self.ptr_size()); - func_ty = self.vtable_map.ty(id)?.clone(); + func_ty = convert_ty_for_result(interner, self.vtable_map.ty(id)?); } let size = self.size_of_sized(&func_ty, locals, "self type of fn trait")?; func_data = Interval { addr: Address::from_bytes(func_data.get(self)?)?, size }; } match &func_ty.kind(Interner) { - TyKind::FnDef(def, subst) => { - self.exec_fn_def(*def, subst, destination, &args[1..], locals, target_bb, span) - } + TyKind::FnDef(def, subst) => self.exec_fn_def( + CallableDefId::from_chalk(self.db, *def), + subst, + destination, + &args[1..], + locals, + target_bb, + span, + ), TyKind::Function(_) => { self.exec_fn_pointer(func_data, destination, &args[1..], locals, target_bb, span) } @@ -2714,7 +2802,7 @@ impl Evaluator<'_> { Substitution::from_iter(Interner, args.iter().map(|it| it.ty.clone())), ) .intern(Interner); - let layout = self.layout(&ty)?; + let layout = self.layout(ty.to_nextsolver(interner))?; let result = self.construct_with_layout( layout.size.bytes_usize(), &layout, @@ -2901,6 +2989,7 @@ pub fn render_const_using_debug_impl( owner: DefWithBodyId, c: &Const, ) -> Result { + let interner = DbInterner::new_with(db, None, None); let mut evaluator = Evaluator::new(db, owner, false, None)?; let locals = &Locals { ptr: ArenaMap::new(), @@ -2933,7 +3022,8 @@ pub fn render_const_using_debug_impl( CallableDefId::FunctionId(debug_fmt_fn).to_chalk(db), Substitution::from1(Interner, c.data(Interner).ty.clone()), ) - .intern(Interner)); + .intern(Interner) + .to_nextsolver(interner)); evaluator.write_memory(a2.offset(evaluator.ptr_size()), &debug_fmt_fn_ptr.to_le_bytes())?; // a3 = ::core::fmt::Arguments::new_v1(a1, a2) // FIXME: similarly, we should call function here, not directly working with memory. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs index bb4c963a8ae1..e27d334d2a99 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs @@ -23,6 +23,10 @@ use crate::{ LangItem, Layout, Locals, Lookup, MirEvalError, MirSpan, Mutability, Result, Substitution, Ty, TyBuilder, TyExt, pad16, }, + next_solver::{ + DbInterner, + mapping::{ChalkToNextSolver, convert_ty_for_result}, + }, }; mod simd; @@ -171,6 +175,7 @@ impl Evaluator<'_> { destination: Interval, span: MirSpan, ) -> Result<()> { + let interner = DbInterner::new_with(self.db, None, None); match self_ty.kind(Interner) { TyKind::Function(_) => { let [arg] = args else { @@ -188,7 +193,7 @@ impl Evaluator<'_> { let InternedClosure(closure_owner, _) = self.db.lookup_intern_closure((*id).into()); let infer = self.db.infer(closure_owner); let (captures, _) = infer.closure_info(id); - let layout = self.layout(&self_ty)?; + let layout = self.layout(self_ty.to_nextsolver(interner))?; let ty_iter = captures.iter().map(|c| c.ty(subst)); self.exec_clone_for_fields(ty_iter, layout, addr, def, locals, destination, span)?; } @@ -197,7 +202,7 @@ impl Evaluator<'_> { not_supported!("wrong arg count for clone"); }; let addr = Address::from_bytes(arg.get(self)?)?; - let layout = self.layout(&self_ty)?; + let layout = self.layout(self_ty.to_nextsolver(interner))?; let ty_iter = subst.iter(Interner).map(|ga| ga.assert_ty_ref(Interner).clone()); self.exec_clone_for_fields(ty_iter, layout, addr, def, locals, destination, span)?; } @@ -226,8 +231,9 @@ impl Evaluator<'_> { destination: Interval, span: MirSpan, ) -> Result<()> { + let interner = DbInterner::new_with(self.db, None, None); for (i, ty) in ty_iter.enumerate() { - let size = self.layout(&ty)?.size.bytes_usize(); + let size = self.layout(ty.to_nextsolver(interner))?.size.bytes_usize(); let tmp = self.heap_allocate(self.ptr_size(), self.ptr_size())?; let arg = IntervalAndTy { interval: Interval { addr: tmp, size: self.ptr_size() }, @@ -592,6 +598,7 @@ impl Evaluator<'_> { span: MirSpan, needs_override: bool, ) -> Result { + let interner = DbInterner::new_with(self.db, None, None); if let Some(name) = name.strip_prefix("atomic_") { return self .exec_atomic_intrinsic(name, args, generic_args, destination, locals, span) @@ -769,7 +776,7 @@ impl Evaluator<'_> { "align_of generic arg is not provided".into(), )); }; - let align = self.layout(ty)?.align.abi.bytes(); + let align = self.layout(ty.to_nextsolver(interner))?.align.abi.bytes(); destination.write_from_bytes(self, &align.to_le_bytes()[0..destination.size]) } "size_of_val" => { @@ -1025,7 +1032,7 @@ impl Evaluator<'_> { let is_overflow = u128overflow || ans.to_le_bytes()[op_size..].iter().any(|&it| it != 0 && it != 255); let is_overflow = vec![u8::from(is_overflow)]; - let layout = self.layout(&result_ty)?; + let layout = self.layout(result_ty.to_nextsolver(interner))?; let result = self.construct_with_layout( layout.size.bytes_usize(), &layout, @@ -1249,7 +1256,7 @@ impl Evaluator<'_> { "const_eval_select arg[0] is not a tuple".into(), )); }; - let layout = self.layout(&tuple.ty)?; + let layout = self.layout(tuple.ty.to_nextsolver(interner))?; for (i, field) in fields.iter(Interner).enumerate() { let field = field.assert_ty_ref(Interner).clone(); let offset = layout.fields.offset(i).bytes_usize(); @@ -1408,6 +1415,7 @@ impl Evaluator<'_> { metadata: Interval, locals: &Locals, ) -> Result<(usize, usize)> { + let interner = DbInterner::new_with(self.db, None, None); Ok(match ty.kind(Interner) { TyKind::Str => (from_bytes!(usize, metadata.get(self)?), 1), TyKind::Slice(inner) => { @@ -1416,7 +1424,7 @@ impl Evaluator<'_> { (size * len, align) } TyKind::Dyn(_) => self.size_align_of_sized( - self.vtable_map.ty_of_bytes(metadata.get(self)?)?, + &convert_ty_for_result(interner, self.vtable_map.ty_of_bytes(metadata.get(self)?)?), locals, "dyn concrete type", )?, @@ -1463,6 +1471,7 @@ impl Evaluator<'_> { locals: &Locals, _span: MirSpan, ) -> Result<()> { + let interner = DbInterner::new_with(self.db, None, None); // We are a single threaded runtime with no UB checking and no optimization, so // we can implement atomic intrinsics as normal functions. @@ -1560,7 +1569,7 @@ impl Evaluator<'_> { Substitution::from_iter(Interner, [ty.clone(), TyBuilder::bool()]), ) .intern(Interner); - let layout = self.layout(&result_ty)?; + let layout = self.layout(result_ty.to_nextsolver(interner))?; let result = self.construct_with_layout( layout.size.bytes_usize(), &layout, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs index eb5af58f2ea1..5a56d99fbaa2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs @@ -37,7 +37,7 @@ fn eval_main(db: &TestDB, file_id: EditionedFileId) -> Result<(String, String), ) .map_err(|e| MirEvalError::MirLowerError(func_id, e))?; - let (result, output) = interpret_mir(db, body, false, None)?; + let (result, output) = salsa::attach(db, || interpret_mir(db, body, false, None))?; result?; Ok((output.stdout().into_owned(), output.stderr().into_owned())) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs index 5698ff290f74..ce581cfad4b2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs @@ -103,7 +103,7 @@ pub struct ValueConst<'db> { } impl<'db> ValueConst<'db> { - pub fn new(ty: Ty<'db>, bytes: ConstBytes) -> Self { + pub fn new(ty: Ty<'db>, bytes: ConstBytes<'db>) -> Self { let value = Valtree::new(bytes); ValueConst { ty, value } } @@ -141,9 +141,9 @@ impl<'db> rustc_type_ir::TypeFoldable> for ValueConst<'db> { } #[derive(Debug, Clone, PartialEq, Eq)] -pub struct ConstBytes(pub Box<[u8]>, pub MemoryMap); +pub struct ConstBytes<'db>(pub Box<[u8]>, pub MemoryMap<'db>); -impl Hash for ConstBytes { +impl Hash for ConstBytes<'_> { fn hash(&self, state: &mut H) { self.0.hash(state) } @@ -152,11 +152,11 @@ impl Hash for ConstBytes { #[salsa::interned(constructor = new_, debug)] pub struct Valtree<'db> { #[returns(ref)] - bytes_: ConstBytes, + bytes_: ConstBytes<'db>, } impl<'db> Valtree<'db> { - pub fn new(bytes: ConstBytes) -> Self { + pub fn new(bytes: ConstBytes<'db>) -> Self { salsa::with_attached_database(|db| unsafe { // SAFETY: ¯\_(ツ)_/¯ std::mem::transmute(Valtree::new_(db, bytes)) @@ -164,7 +164,7 @@ impl<'db> Valtree<'db> { .unwrap() } - pub fn inner(&self) -> &ConstBytes { + pub fn inner(&self) -> &ConstBytes<'db> { salsa::with_attached_database(|db| { let inner = self.bytes_(db); // SAFETY: The caller already has access to a `Valtree<'db>`, so borrowchecking will diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index b50fccb83262..5fefb04a5e76 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -21,7 +21,7 @@ use rustc_type_ir::{ use salsa::plumbing::AsId; use crate::{ - ConcreteConst, ConstScalar, ImplTraitId, Interner, + ConcreteConst, ConstScalar, ImplTraitId, Interner, MemoryMap, db::{ HirDatabase, InternedClosureId, InternedCoroutineId, InternedOpaqueTyId, InternedTypeOrConstParamId, @@ -1328,7 +1328,10 @@ pub fn convert_const_for_result<'db>( rustc_type_ir::ConstKind::Value(value_const) => { let bytes = value_const.value.inner(); let value = chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { - interned: ConstScalar::Bytes(bytes.0.clone(), bytes.1.clone()), + // SAFETY: we will never actually use this without a database + interned: ConstScalar::Bytes(bytes.0.clone(), unsafe { + std::mem::transmute::, MemoryMap<'static>>(bytes.1.clone()) + }), }); return chalk_ir::ConstData { ty: convert_ty_for_result(interner, value_const.ty), From 73a5134722d362299bdecbd8418b4728f918a23e Mon Sep 17 00:00:00 2001 From: jackh726 Date: Mon, 11 Aug 2025 02:34:53 +0000 Subject: [PATCH 076/682] Change direct_super_traits to use generic_predicates_for_param_ns --- .../rust-analyzer/crates/hir-ty/src/utils.rs | 43 +++++++++++++------ src/tools/rust-analyzer/crates/hir/src/lib.rs | 16 ++++--- .../rust-analyzer/crates/ide/src/hover.rs | 12 +++--- src/tools/rust-analyzer/crates/ide/src/lib.rs | 4 +- 4 files changed, 50 insertions(+), 25 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs index 209ec7926e82..092d4e3a8d9e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs @@ -4,10 +4,7 @@ use std::{cell::LazyCell, iter}; use base_db::Crate; -use chalk_ir::{ - DebruijnIndex, - fold::{FallibleTypeFolder, Shift}, -}; +use chalk_ir::{DebruijnIndex, fold::FallibleTypeFolder}; use hir_def::{ EnumId, EnumVariantId, FunctionId, Lookup, TraitId, TypeAliasId, TypeOrConstParamId, db::DefDatabase, @@ -20,6 +17,7 @@ use hir_expand::name::Name; use intern::sym; use rustc_abi::TargetDataLayout; use rustc_hash::FxHashSet; +use rustc_type_ir::inherent::{IntoKind, SliceLike}; use smallvec::{SmallVec, smallvec}; use span::Edition; use stdx::never; @@ -31,6 +29,11 @@ use crate::{ db::HirDatabase, layout::{Layout, TagEncoding}, mir::pad16, + next_solver::{ + DbInterner, + mapping::{ChalkToNextSolver, convert_args_for_result}, + }, + to_chalk_trait_id, }; pub(crate) fn fn_traits(db: &dyn DefDatabase, krate: Crate) -> impl Iterator + '_ { @@ -191,25 +194,37 @@ fn direct_super_traits_cb(db: &dyn DefDatabase, trait_: TraitId, cb: impl FnMut( } fn direct_super_trait_refs(db: &dyn HirDatabase, trait_ref: &TraitRef, cb: impl FnMut(TraitRef)) { + let interner = DbInterner::new_with(db, None, None); let generic_params = db.generic_params(trait_ref.hir_trait_id().into()); let trait_self = match generic_params.trait_self_param() { Some(p) => TypeOrConstParamId { parent: trait_ref.hir_trait_id().into(), local_id: p }, None => return, }; - db.generic_predicates_for_param(trait_self.parent, trait_self, None) + let trait_ref_args: crate::next_solver::GenericArgs<'_> = + trait_ref.substitution.to_nextsolver(interner); + db.generic_predicates_for_param_ns(trait_self.parent, trait_self, None) .iter() .filter_map(|pred| { - pred.as_ref().filter_map(|pred| match pred.skip_binders() { - // FIXME: how to correctly handle higher-ranked bounds here? - WhereClause::Implemented(tr) => Some( - tr.clone() - .shifted_out_to(Interner, DebruijnIndex::ONE) - .expect("FIXME unexpected higher-ranked trait bound"), - ), + let pred = pred.kind(); + // FIXME: how to correctly handle higher-ranked bounds here? + let pred = pred.no_bound_vars().expect("FIXME unexpected higher-ranked trait bound"); + match pred { + rustc_type_ir::ClauseKind::Trait(t) => { + let t = + rustc_type_ir::EarlyBinder::bind(t).instantiate(interner, trait_ref_args); + let trait_id = match t.def_id() { + crate::next_solver::SolverDefId::TraitId(id) => to_chalk_trait_id(id), + _ => unreachable!(), + }; + + let substitution = + convert_args_for_result(interner, t.trait_ref.args.as_slice()); + let tr = chalk_ir::TraitRef { trait_id, substitution }; + Some(tr) + } _ => None, - }) + } }) - .map(|pred| pred.substitute(Interner, &trait_ref.substitution)) .for_each(cb); } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 9accb3336898..18c3ea05614a 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -86,7 +86,8 @@ use hir_ty::{ method_resolution, mir::{MutBorrowKind, interpret_mir}, next_solver::{ - DbInterner, GenericArgs, SolverDefId, infer::InferCtxt, mapping::ChalkToNextSolver, + ClauseKind, DbInterner, GenericArgs, SolverDefId, infer::InferCtxt, + mapping::ChalkToNextSolver, }, primitive::UintTy, traits::FnTrait, @@ -114,6 +115,7 @@ pub use crate::{ VisibleTraits, }, }; +use rustc_type_ir::inherent::IntoKind; // Be careful with these re-exports. // @@ -4245,11 +4247,15 @@ impl TypeParam { /// parameter, not additional bounds that might be added e.g. by a method if /// the parameter comes from an impl! pub fn trait_bounds(self, db: &dyn HirDatabase) -> Vec { - db.generic_predicates_for_param(self.id.parent(), self.id.into(), None) + db.generic_predicates_for_param_ns(self.id.parent(), self.id.into(), None) .iter() - .filter_map(|pred| match &pred.skip_binders().skip_binders() { - hir_ty::WhereClause::Implemented(trait_ref) => { - Some(Trait::from(trait_ref.hir_trait_id())) + .filter_map(|pred| match &pred.kind().skip_binder() { + ClauseKind::Trait(trait_ref) => { + let trait_ = match trait_ref.def_id() { + SolverDefId::TraitId(t) => t, + _ => unreachable!(), + }; + Some(Trait::from(trait_)) } _ => None, }) diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index a48fe43e8080..fc45dc3faf40 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -581,11 +581,13 @@ fn goto_type_action_for_def( }); } - if let Ok(generic_def) = GenericDef::try_from(def) { - generic_def.type_or_const_params(db).into_iter().for_each(|it| { - walk_and_push_ty(db, &it.ty(db), &mut push_new_def); - }); - } + salsa::attach(db, || { + if let Ok(generic_def) = GenericDef::try_from(def) { + generic_def.type_or_const_params(db).into_iter().for_each(|it| { + walk_and_push_ty(db, &it.ty(db), &mut push_new_def); + }); + } + }); let ty = match def { Definition::Local(it) => Some(it.ty(db)), diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 1c66473bf987..2afdf18b8368 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -674,7 +674,9 @@ impl Analysis { position: FilePosition, ) -> Cancellable>> { self.with_db(|db| { - highlight_related::highlight_related(&Semantics::new(db), config, position) + salsa::attach(db, || { + highlight_related::highlight_related(&Semantics::new(db), config, position) + }) }) } From 418f419d60240e7ed24953cab9089027fa666be4 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Mon, 11 Aug 2025 04:12:43 +0000 Subject: [PATCH 077/682] Cleanup assoc_type_shorthand_candidates --- .../rust-analyzer/crates/hir/src/semantics.rs | 21 ++++++++++--------- .../ide-completion/src/completions/expr.rs | 3 +-- .../ide-completion/src/completions/type.rs | 3 +-- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index b43165fd8ad7..6af0c2c3c56f 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -2288,18 +2288,19 @@ impl<'db> SemanticsScope<'db> { /// Iterates over associated types that may be specified after the given path (using /// `Ty::Assoc` syntax). - pub fn assoc_type_shorthand_candidates( + pub fn assoc_type_shorthand_candidates( &self, resolution: &PathResolution, - mut cb: impl FnMut(&Name, TypeAlias) -> Option, - ) -> Option { - let def = self.resolver.generic_def()?; - hir_ty::associated_type_shorthand_candidates( - self.db, - def, - resolution.in_type_ns()?, - |name, id| cb(name, id.into()), - ) + mut cb: impl FnMut(TypeAlias), + ) { + let (Some(def), Some(resolution)) = (self.resolver.generic_def(), resolution.in_type_ns()) + else { + return; + }; + hir_ty::associated_type_shorthand_candidates(self.db, def, resolution, |_, id| { + cb(id.into()); + None::<()> + }); } pub fn generic_def(&self) -> Option { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs index a84927f6e2c0..1972f166134a 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs @@ -140,9 +140,8 @@ pub(crate) fn complete_expr_path( Qualified::With { resolution: None, .. } => {} Qualified::With { resolution: Some(resolution), .. } => { // Add associated types on type parameters and `Self`. - ctx.scope.assoc_type_shorthand_candidates(resolution, |_, alias| { + ctx.scope.assoc_type_shorthand_candidates(resolution, |alias| { acc.add_type_alias(ctx, alias); - None::<()> }); match resolution { hir::PathResolution::Def(hir::ModuleDef::Module(module)) => { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs index fc27cbd65a1e..3112462cda4e 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs @@ -77,9 +77,8 @@ pub(crate) fn complete_type_path( Qualified::With { resolution: None, .. } => {} Qualified::With { resolution: Some(resolution), .. } => { // Add associated types on type parameters and `Self`. - ctx.scope.assoc_type_shorthand_candidates(resolution, |_, alias| { + ctx.scope.assoc_type_shorthand_candidates(resolution, |alias| { acc.add_type_alias(ctx, alias); - None::<()> }); match resolution { From 064f1c7c8321547e114d576f07ecdf0b1bee97e1 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Mon, 11 Aug 2025 05:19:44 +0000 Subject: [PATCH 078/682] Switch associated_type_shorthand_candidates to lower_nextsolver --- .../crates/hir-ty/src/dyn_compatibility.rs | 4 +- .../rust-analyzer/crates/hir-ty/src/lib.rs | 3 +- .../rust-analyzer/crates/hir-ty/src/lower.rs | 9 - .../crates/hir-ty/src/lower_nextsolver.rs | 133 +++++++++++++- .../hir-ty/src/lower_nextsolver/path.rs | 163 ++++-------------- .../hir-ty/src/next_solver/generic_arg.rs | 19 +- .../rust-analyzer/crates/hir/src/semantics.rs | 2 +- .../crates/hir/src/source_analyzer.rs | 17 +- src/tools/rust-analyzer/crates/ide/src/lib.rs | 6 +- 9 files changed, 197 insertions(+), 159 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index 8bd555f5c08f..48c0c81b4781 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -564,7 +564,7 @@ fn receiver_is_dispatchable<'db>( // U: Trait let trait_def_id = SolverDefId::TraitId(trait_); let args = GenericArgs::for_item(interner, trait_def_id, |name, index, kind, _| { - if index == 0 { unsized_self_ty.into() } else { mk_param(index, name, kind) } + if index == 0 { unsized_self_ty.into() } else { mk_param(interner, index, name, kind) } }); let trait_predicate = crate::next_solver::TraitRef::new_from_args(interner, trait_def_id, args); @@ -611,7 +611,7 @@ fn receiver_for_self_ty<'db>( interner, SolverDefId::FunctionId(func), |name, index, kind, _| { - if index == 0 { self_ty.into() } else { mk_param(index, name, kind) } + if index == 0 { self_ty.into() } else { mk_param(interner, index, name, kind) } }, ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 8ce0aeb5532a..2f8eb627462b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -118,8 +118,9 @@ pub use infer::{ pub use interner::Interner; pub use lower::{ ImplTraitLoweringMode, LifetimeElisionKind, ParamLoweringMode, TyDefId, TyLoweringContext, - ValueTyDefId, associated_type_shorthand_candidates, diagnostics::*, + ValueTyDefId, diagnostics::*, }; +pub use lower_nextsolver::associated_type_shorthand_candidates; pub use mapping::{ ToChalk, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx, lt_from_placeholder_idx, lt_to_placeholder_idx, to_assoc_type_id, to_chalk_trait_id, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 065d2ea08407..098c62ba97a2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -804,15 +804,6 @@ pub(crate) fn callable_item_signature_query(db: &dyn HirDatabase, def: CallableD } } -pub fn associated_type_shorthand_candidates( - db: &dyn HirDatabase, - def: GenericDefId, - res: TypeNs, - mut cb: impl FnMut(&Name, TypeAliasId) -> Option, -) -> Option { - named_associated_type_shorthand_candidates(db, def, res, None, |name, _, id| cb(name, id)) -} - fn named_associated_type_shorthand_candidates( db: &dyn HirDatabase, // If the type parameter is defined in an impl and we're in a method, there diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs index 24bda43ca634..15c675be5819 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -12,14 +12,14 @@ pub(crate) mod path; use std::{ cell::OnceCell, iter, mem, - ops::{self, Not as _}, + ops::{self, Deref, Not as _}, }; use base_db::Crate; use either::Either; use hir_def::{ AdtId, AssocItemId, CallableDefId, ConstParamId, EnumVariantId, FunctionId, GenericDefId, - GenericParamId, ImplId, ItemContainerId, LocalFieldId, Lookup, StructId, TypeAliasId, + GenericParamId, ImplId, ItemContainerId, LocalFieldId, Lookup, StructId, TraitId, TypeAliasId, TypeOrConstParamId, VariantId, expr_store::{ ExpressionStore, @@ -49,6 +49,7 @@ use rustc_type_ir::{ inherent::{GenericArg as _, GenericArgs as _, IntoKind as _, Region as _, SliceLike, Ty as _}, }; use salsa::plumbing::AsId; +use smallvec::{SmallVec, smallvec}; use stdx::never; use triomphe::Arc; @@ -1607,3 +1608,131 @@ pub(crate) fn associated_type_by_name_including_super_traits<'db>( Some((t.skip_binder(), assoc_type)) }) } + +pub fn associated_type_shorthand_candidates( + db: &dyn HirDatabase, + def: GenericDefId, + res: TypeNs, + mut cb: impl FnMut(&Name, TypeAliasId) -> bool, +) -> Option { + let interner = DbInterner::new_with(db, None, None); + named_associated_type_shorthand_candidates(interner, def, res, None, |name, _, id| { + cb(name, id).then_some(id) + }) +} + +#[tracing::instrument(skip(interner, check_alias))] +fn named_associated_type_shorthand_candidates<'db, R>( + interner: DbInterner<'db>, + // If the type parameter is defined in an impl and we're in a method, there + // might be additional where clauses to consider + def: GenericDefId, + res: TypeNs, + assoc_name: Option, + mut check_alias: impl FnMut(&Name, TraitRef<'db>, TypeAliasId) -> Option, +) -> Option { + let db = interner.db; + let mut search = |t: TraitRef<'db>| -> Option { + let trait_id = match t.def_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + let mut checked_traits = FxHashSet::default(); + let mut check_trait = |trait_id: TraitId| { + let name = &db.trait_signature(trait_id).name; + tracing::debug!(?trait_id, ?name); + if !checked_traits.insert(trait_id) { + return None; + } + let data = trait_id.trait_items(db); + + tracing::debug!(?data.items); + for (name, assoc_id) in &data.items { + if let &AssocItemId::TypeAliasId(alias) = assoc_id + && let Some(ty) = check_alias(name, t, alias) + { + return Some(ty); + } + } + None + }; + let mut stack: SmallVec<[_; 4]> = smallvec![trait_id]; + while let Some(trait_def_id) = stack.pop() { + if let Some(alias) = check_trait(trait_def_id) { + return Some(alias); + } + for pred in generic_predicates_filtered_by( + db, + GenericDefId::TraitId(trait_def_id), + PredicateFilter::SelfTrait, + |pred| pred == GenericDefId::TraitId(trait_def_id), + ) + .0 + .deref() + { + tracing::debug!(?pred); + let trait_id = match pred.kind().skip_binder() { + rustc_type_ir::ClauseKind::Trait(pred) => pred.def_id(), + _ => continue, + }; + let trait_id = match trait_id { + SolverDefId::TraitId(trait_id) => trait_id, + _ => continue, + }; + stack.push(trait_id); + } + tracing::debug!(?stack); + } + + None + }; + + match res { + TypeNs::SelfType(impl_id) => { + let trait_ref = db.impl_trait_ns(impl_id)?; + + // we're _in_ the impl -- the binders get added back later. Correct, + // but it would be nice to make this more explicit + search(trait_ref.skip_binder()) + } + TypeNs::GenericParam(param_id) => { + // Handle `Self::Type` referring to own associated type in trait definitions + // This *must* be done first to avoid cycles with + // `generic_predicates_for_param`, but not sure that it's sufficient, + // see FIXME in `search`. + if let GenericDefId::TraitId(trait_id) = param_id.parent() { + let trait_name = &db.trait_signature(trait_id).name; + tracing::debug!(?trait_name); + let trait_generics = generics(db, trait_id.into()); + tracing::debug!(?trait_generics); + if trait_generics[param_id.local_id()].is_trait_self() { + let args = crate::next_solver::GenericArgs::identity_for_item( + interner, + trait_id.into(), + ); + let trait_ref = TraitRef::new_from_args(interner, trait_id.into(), args); + tracing::debug!(?args, ?trait_ref); + return search(trait_ref); + } + } + + let predicates = + db.generic_predicates_for_param_ns(def, param_id.into(), assoc_name.clone()); + predicates + .iter() + .find_map(|pred| match (*pred).kind().skip_binder() { + rustc_type_ir::ClauseKind::Trait(trait_predicate) => Some(trait_predicate), + _ => None, + }) + .and_then(|trait_predicate| { + let trait_ref = trait_predicate.trait_ref; + assert!( + !trait_ref.has_escaping_bound_vars(), + "FIXME unexpected higher-ranked trait bound" + ); + search(trait_ref) + }) + } + _ => None, + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs index df67b2c59b51..e3efb3830640 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs @@ -4,7 +4,7 @@ use std::ops::Deref; use either::Either; use hir_def::{ - AssocItemId, GenericDefId, GenericParamId, Lookup, TraitId, + AssocItemId, GenericDefId, GenericParamId, Lookup, TraitId, TypeAliasId, builtin_type::BuiltinType, expr_store::{ ExpressionStore, HygieneId, @@ -17,6 +17,7 @@ use hir_def::{ signatures::TraitFlags, type_ref::{TypeRef, TypeRefId}, }; +use hir_expand::name::Name; use intern::sym; use rustc_hash::FxHashSet; use rustc_type_ir::{ @@ -33,7 +34,10 @@ use crate::{ db::HirDatabase, generics::{Generics, generics}, lower::PathDiagnosticCallbackData, - lower_nextsolver::{LifetimeElisionKind, PredicateFilter, generic_predicates_filtered_by}, + lower_nextsolver::{ + LifetimeElisionKind, PredicateFilter, generic_predicates_filtered_by, + named_associated_type_shorthand_candidates, + }, next_solver::{ AdtDef, Binder, Clause, Const, DbInterner, ErrorGuaranteed, Predicate, ProjectionPredicate, Region, SolverDefId, TraitRef, Ty, @@ -501,137 +505,40 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { let Some(res) = res else { return Ty::new_error(self.ctx.interner, ErrorGuaranteed); }; - let segment = self.current_or_prev_segment; - let assoc_name = segment.name; let db = self.ctx.db; let def = self.ctx.def; - let mut search = |t: TraitRef<'db>| { - let trait_id = match t.def_id { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; - let mut checked_traits = FxHashSet::default(); - let mut check_trait = |trait_id: TraitId| { - let name = &db.trait_signature(trait_id).name; - tracing::debug!(?trait_id, ?name); - if !checked_traits.insert(trait_id) { - return None; - } - let data = trait_id.trait_items(db); - - tracing::debug!(?data.items); - for (name, assoc_id) in &data.items { - if let &AssocItemId::TypeAliasId(alias) = assoc_id { - if name != assoc_name { - continue; - } - - // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent - // generic params. It's inefficient to splice the `Substitution`s, so we may want - // that method to optionally take parent `Substitution` as we already know them at - // this point (`t.substitution`). - let substs = self.substs_from_path_segment(alias.into(), false, None, true); - - let substs = crate::next_solver::GenericArgs::new_from_iter( - interner, - t.args.iter().chain(substs.iter().skip(t.args.len())), - ); - - return Some(Ty::new_alias( - interner, - AliasTyKind::Projection, - AliasTy::new(interner, alias.into(), substs), - )); - } - } - None - }; - let mut stack: SmallVec<[_; 4]> = smallvec![trait_id]; - while let Some(trait_def_id) = stack.pop() { - if let Some(alias) = check_trait(trait_def_id) { - return alias; - } - for pred in generic_predicates_filtered_by( - db, - GenericDefId::TraitId(trait_def_id), - PredicateFilter::SelfTrait, - |pred| pred == GenericDefId::TraitId(trait_def_id), - ) - .0 - .deref() - { - tracing::debug!(?pred); - let trait_id = match pred.kind().skip_binder() { - rustc_type_ir::ClauseKind::Trait(pred) => pred.def_id(), - _ => continue, - }; - let trait_id = match trait_id { - SolverDefId::TraitId(trait_id) => trait_id, - _ => continue, - }; - stack.push(trait_id); - } - tracing::debug!(?stack); + let segment = self.current_or_prev_segment; + let assoc_name = segment.name; + let mut check_alias = |name: &Name, t: TraitRef<'db>, associated_ty: TypeAliasId| { + if name != assoc_name { + return None; } - Ty::new_error(interner, ErrorGuaranteed) + // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent + // generic params. It's inefficient to splice the `Substitution`s, so we may want + // that method to optionally take parent `Substitution` as we already know them at + // this point (`t.substitution`). + let substs = self.substs_from_path_segment(associated_ty.into(), false, None, true); + + let substs = crate::next_solver::GenericArgs::new_from_iter( + interner, + t.args.iter().chain(substs.iter().skip(t.args.len())), + ); + + Some(Ty::new_alias( + interner, + AliasTyKind::Projection, + AliasTy::new(interner, associated_ty.into(), substs), + )) }; - - match res { - TypeNs::SelfType(impl_id) => { - let trait_ref = db.impl_trait_ns(impl_id); - let Some(trait_ref) = trait_ref else { - return Ty::new_error(interner, ErrorGuaranteed); - }; - - // we're _in_ the impl -- the binders get added back later. Correct, - // but it would be nice to make this more explicit - search(trait_ref.skip_binder()) - } - TypeNs::GenericParam(param_id) => { - // Handle `Self::Type` referring to own associated type in trait definitions - // This *must* be done first to avoid cycles with - // `generic_predicates_for_param`, but not sure that it's sufficient, - // see FIXME in `search`. - if let GenericDefId::TraitId(trait_id) = param_id.parent() { - let trait_name = &db.trait_signature(trait_id).name; - tracing::debug!(?trait_name); - let trait_generics = generics(db, trait_id.into()); - tracing::debug!(?trait_generics); - if trait_generics[param_id.local_id()].is_trait_self() { - let args = crate::next_solver::GenericArgs::identity_for_item( - interner, - trait_id.into(), - ); - let trait_ref = TraitRef::new_from_args(interner, trait_id.into(), args); - tracing::debug!(?args, ?trait_ref); - return search(trait_ref); - } - } - - let predicates = db.generic_predicates_for_param_ns( - def, - param_id.into(), - Some(segment.name.clone()), - ); - predicates - .iter() - .find_map(|pred| match (*pred).kind().skip_binder() { - rustc_type_ir::ClauseKind::Trait(trait_predicate) => Some(trait_predicate), - _ => None, - }) - .map(|trait_predicate| { - let trait_ref = trait_predicate.trait_ref; - assert!( - !trait_ref.has_escaping_bound_vars(), - "FIXME unexpected higher-ranked trait bound" - ); - search(trait_ref) - }) - .unwrap_or_else(|| Ty::new_error(interner, ErrorGuaranteed)) - } - _ => Ty::new_error(interner, ErrorGuaranteed), - } + named_associated_type_shorthand_candidates( + interner, + def, + res, + Some(assoc_name.clone()), + check_alias, + ) + .unwrap_or_else(|| Ty::new_error(interner, ErrorGuaranteed)) } fn lower_path_inner(&mut self, typeable: TyDefId, infer_args: bool) -> Ty<'db> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs index 85a79923a729..046b4303c326 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs @@ -263,7 +263,9 @@ impl<'db> rustc_type_ir::inherent::GenericArgs> for GenericArgs< interner: DbInterner<'db>, def_id: as rustc_type_ir::Interner>::DefId, ) -> as rustc_type_ir::Interner>::GenericArgs { - Self::for_item(interner, def_id, |name, index, kind, _| mk_param(index, name, kind)) + Self::for_item(interner, def_id, |name, index, kind, _| { + mk_param(interner, index, name, kind) + }) } fn extend_with_error( @@ -383,16 +385,19 @@ impl<'db> rustc_type_ir::inherent::GenericArgs> for GenericArgs< } } -pub fn mk_param<'db>(index: u32, name: &Symbol, kind: GenericParamDefKind) -> GenericArg<'db> { +pub fn mk_param<'db>( + interner: DbInterner<'db>, + index: u32, + name: &Symbol, + kind: GenericParamDefKind, +) -> GenericArg<'db> { let name = name.clone(); match kind { GenericParamDefKind::Lifetime => { - Region::new_early_param(DbInterner::conjure(), EarlyParamRegion { index }).into() - } - GenericParamDefKind::Type => Ty::new_param(DbInterner::conjure(), index, name).into(), - GenericParamDefKind::Const => { - Const::new_param(DbInterner::conjure(), ParamConst { index }).into() + Region::new_early_param(interner, EarlyParamRegion { index }).into() } + GenericParamDefKind::Type => Ty::new_param(interner, index, name).into(), + GenericParamDefKind::Const => Const::new_param(interner, ParamConst { index }).into(), } } diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 6af0c2c3c56f..fa239a28f7bc 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -2299,7 +2299,7 @@ impl<'db> SemanticsScope<'db> { }; hir_ty::associated_type_shorthand_candidates(self.db, def, resolution, |_, id| { cb(id.into()); - None::<()> + false }); } diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index 126392af4619..e3070c5f74ce 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -14,6 +14,7 @@ use crate::{ db::HirDatabase, semantics::{PathResolution, PathResolutionPerNs}, }; +use base_db::salsa; use either::Either; use hir_def::{ AdtId, AssocItemId, CallableDefId, ConstId, DefWithBodyId, FieldId, FunctionId, GenericDefId, @@ -1593,12 +1594,14 @@ fn resolve_hir_path_( Some(unresolved) => resolver .generic_def() .and_then(|def| { - hir_ty::associated_type_shorthand_candidates( - db, - def, - res.in_type_ns()?, - |name, id| (name == unresolved.name).then_some(id), - ) + salsa::attach(db, || { + hir_ty::associated_type_shorthand_candidates( + db, + def, + res.in_type_ns()?, + |name, _| name == unresolved.name, + ) + }) }) .map(TypeAlias::from) .map(Into::into) @@ -1746,7 +1749,7 @@ fn resolve_hir_path_qualifier( db, def, res.in_type_ns()?, - |name, id| (name == unresolved.name).then_some(id), + |name, _| name == unresolved.name, ) }) .map(TypeAlias::from) diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 2afdf18b8368..e491c9214b43 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -528,7 +528,9 @@ impl Analysis { let search_scope = AssertUnwindSafe(search_scope); self.with_db(|db| { let _ = &search_scope; - references::find_all_refs(&Semantics::new(db), position, search_scope.0) + salsa::attach(db, || { + references::find_all_refs(&Semantics::new(db), position, search_scope.0) + }) }) } @@ -574,7 +576,7 @@ impl Analysis { &self, position: FilePosition, ) -> Cancellable>>> { - self.with_db(|db| call_hierarchy::call_hierarchy(db, position)) + self.with_db(|db| salsa::attach(db, || call_hierarchy::call_hierarchy(db, position))) } /// Computes incoming calls for the given file position. From d10e5d10fe08b3b19e72891b6069c8eee0754e37 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Tue, 12 Aug 2025 06:07:02 +0000 Subject: [PATCH 079/682] Convert more of dyn_compatibility to next-solver --- .../crates/hir-ty/src/dyn_compatibility.rs | 187 ++++-------------- .../crates/hir-ty/src/lower_nextsolver.rs | 96 ++++++++- 2 files changed, 130 insertions(+), 153 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index 48c0c81b4781..54d78dea3d3b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -2,11 +2,7 @@ use std::ops::ControlFlow; -use chalk_ir::{ - DebruijnIndex, - visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, -}; -use chalk_solve::rust_ir::InlineBound; +use chalk_ir::DebruijnIndex; use hir_def::{ AssocItemId, ConstId, CrateRootModuleId, FunctionId, GenericDefId, HasModule, TraitId, TypeAliasId, lang_item::LangItem, signatures::TraitFlags, @@ -21,14 +17,14 @@ use rustc_type_ir::{ use smallvec::SmallVec; use crate::{ - AliasEq, AliasTy, Binders, BoundVar, ImplTraitId, Interner, ProjectionTyExt, Ty, TyKind, - WhereClause, all_super_traits, + ImplTraitId, Interner, TyKind, WhereClause, all_super_traits, db::{HirDatabase, InternedOpaqueTyId}, - from_assoc_type_id, from_chalk_trait_id, + from_chalk_trait_id, generics::trait_self_param_idx, + lower_nextsolver::associated_ty_item_bounds, next_solver::{ - Clauses, DbInterner, GenericArgs, ParamEnv, SolverDefId, TraitPredicate, TypingMode, - infer::DbInternerInferExt, mk_param, + Clause, Clauses, DbInterner, GenericArgs, ParamEnv, SolverDefId, TraitPredicate, + TypingMode, infer::DbInternerInferExt, mk_param, }, traits::next_trait_solve_in_ctxt, utils::elaborate_clause_supertraits, @@ -165,7 +161,7 @@ pub fn generics_require_sized_self(db: &dyn HirDatabase, def: GenericDefId) -> b // but we don't have good way to render such locations. // So, just return single boolean value for existence of such `Self` reference fn predicates_reference_self(db: &dyn HirDatabase, trait_: TraitId) -> bool { - db.generic_predicates(trait_.into()) + db.generic_predicates_ns(trait_.into()) .iter() .any(|pred| predicate_references_self(db, trait_, pred, AllowSelfProjection::No)) } @@ -177,37 +173,18 @@ fn bounds_reference_self(db: &dyn HirDatabase, trait_: TraitId) -> bool { .items .iter() .filter_map(|(_, it)| match *it { - AssocItemId::TypeAliasId(id) => { - let assoc_ty_data = db.associated_ty_data(id); - Some(assoc_ty_data) - } + AssocItemId::TypeAliasId(id) => Some(associated_ty_item_bounds(db, id)), _ => None, }) - .any(|assoc_ty_data| { - assoc_ty_data.binders.skip_binders().bounds.iter().any(|bound| { - let def = from_assoc_type_id(assoc_ty_data.id).into(); - match bound.skip_binders() { - InlineBound::TraitBound(it) => it.args_no_self.iter().any(|arg| { - contains_illegal_self_type_reference( - db, - def, - trait_, - arg, - DebruijnIndex::ONE, - AllowSelfProjection::Yes, - ) - }), - InlineBound::AliasEqBound(it) => it.parameters.iter().any(|arg| { - contains_illegal_self_type_reference( - db, - def, - trait_, - arg, - DebruijnIndex::ONE, - AllowSelfProjection::Yes, - ) - }), - } + .any(|bounds| { + bounds.skip_binder().iter().any(|pred| match pred.skip_binder() { + rustc_type_ir::ExistentialPredicate::Trait(it) => it.args.iter().any(|arg| { + contains_illegal_self_type_reference(db, trait_, &arg, AllowSelfProjection::Yes) + }), + rustc_type_ir::ExistentialPredicate::Projection(it) => it.args.iter().any(|arg| { + contains_illegal_self_type_reference(db, trait_, &arg, AllowSelfProjection::Yes) + }), + rustc_type_ir::ExistentialPredicate::AutoTrait(_) => false, }) }) } @@ -218,120 +195,26 @@ enum AllowSelfProjection { No, } -fn predicate_references_self( - db: &dyn HirDatabase, +fn predicate_references_self<'db>( + db: &'db dyn HirDatabase, trait_: TraitId, - predicate: &Binders>, + predicate: &Clause<'db>, allow_self_projection: AllowSelfProjection, ) -> bool { - match predicate.skip_binders().skip_binders() { - WhereClause::Implemented(trait_ref) => { - trait_ref.substitution.iter(Interner).skip(1).any(|arg| { - contains_illegal_self_type_reference( - db, - trait_.into(), - trait_, - arg, - DebruijnIndex::ONE, - allow_self_projection, - ) - }) - } - WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(proj), .. }) => { - proj.substitution.iter(Interner).skip(1).any(|arg| { - contains_illegal_self_type_reference( - db, - trait_.into(), - trait_, - arg, - DebruijnIndex::ONE, - allow_self_projection, - ) + match predicate.kind().skip_binder() { + ClauseKind::Trait(trait_pred) => trait_pred.trait_ref.args.iter().skip(1).any(|arg| { + contains_illegal_self_type_reference(db, trait_, &arg, allow_self_projection) + }), + ClauseKind::Projection(proj_pred) => { + proj_pred.projection_term.args.iter().skip(1).any(|arg| { + contains_illegal_self_type_reference(db, trait_, &arg, allow_self_projection) }) } _ => false, } } -fn contains_illegal_self_type_reference>( - db: &dyn HirDatabase, - def: GenericDefId, - trait_: TraitId, - t: &T, - outer_binder: DebruijnIndex, - allow_self_projection: AllowSelfProjection, -) -> bool { - let Some(trait_self_param_idx) = trait_self_param_idx(db, def) else { - return false; - }; - struct IllegalSelfTypeVisitor<'a> { - db: &'a dyn HirDatabase, - trait_: TraitId, - super_traits: Option>, - trait_self_param_idx: usize, - allow_self_projection: AllowSelfProjection, - } - impl TypeVisitor for IllegalSelfTypeVisitor<'_> { - type BreakTy = (); - - fn as_dyn(&mut self) -> &mut dyn TypeVisitor { - self - } - - fn interner(&self) -> Interner { - Interner - } - - fn visit_ty(&mut self, ty: &Ty, outer_binder: DebruijnIndex) -> ControlFlow { - match ty.kind(Interner) { - TyKind::BoundVar(BoundVar { debruijn, index }) => { - if *debruijn == outer_binder && *index == self.trait_self_param_idx { - ControlFlow::Break(()) - } else { - ty.super_visit_with(self.as_dyn(), outer_binder) - } - } - TyKind::Alias(AliasTy::Projection(proj)) => match self.allow_self_projection { - AllowSelfProjection::Yes => { - let trait_ = proj.trait_(self.db); - if self.super_traits.is_none() { - self.super_traits = Some(all_super_traits(self.db, self.trait_)); - } - if self.super_traits.as_ref().is_some_and(|s| s.contains(&trait_)) { - ControlFlow::Continue(()) - } else { - ty.super_visit_with(self.as_dyn(), outer_binder) - } - } - AllowSelfProjection::No => ty.super_visit_with(self.as_dyn(), outer_binder), - }, - _ => ty.super_visit_with(self.as_dyn(), outer_binder), - } - } - - fn visit_const( - &mut self, - constant: &chalk_ir::Const, - outer_binder: DebruijnIndex, - ) -> std::ops::ControlFlow { - constant.data(Interner).ty.super_visit_with(self.as_dyn(), outer_binder) - } - } - - let mut visitor = IllegalSelfTypeVisitor { - db, - trait_, - super_traits: None, - trait_self_param_idx, - allow_self_projection, - }; - t.visit_with(visitor.as_dyn(), outer_binder).is_break() -} - -fn contains_illegal_self_type_reference_ns< - 'db, - T: rustc_type_ir::TypeVisitable>, ->( +fn contains_illegal_self_type_reference<'db, T: rustc_type_ir::TypeVisitable>>( db: &'db dyn HirDatabase, trait_: TraitId, t: &T, @@ -440,13 +323,17 @@ where } let sig = db.callable_item_signature_ns(func.into()); - if sig.skip_binder().inputs().iter().skip(1).any(|ty| { - contains_illegal_self_type_reference_ns(db, trait_, &ty, AllowSelfProjection::Yes) - }) { + if sig + .skip_binder() + .inputs() + .iter() + .skip(1) + .any(|ty| contains_illegal_self_type_reference(db, trait_, &ty, AllowSelfProjection::Yes)) + { cb(MethodViolationCode::ReferencesSelfInput)?; } - if contains_illegal_self_type_reference_ns( + if contains_illegal_self_type_reference( db, trait_, &sig.skip_binder().output(), @@ -496,7 +383,7 @@ where continue; } - if contains_illegal_self_type_reference_ns(db, trait_, &pred, AllowSelfProjection::Yes) { + if contains_illegal_self_type_reference(db, trait_, &pred, AllowSelfProjection::Yes) { cb(MethodViolationCode::WhereClauseReferencesSelf)?; break; } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs index 15c675be5819..2777869bd48e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -62,9 +62,10 @@ use crate::{ lower::{Diagnostics, PathDiagnosticCallbackData, create_diagnostics}, next_solver::{ AdtDef, AliasTy, Binder, BoundExistentialPredicates, BoundRegionKind, BoundTyKind, - BoundVarKind, BoundVarKinds, Clause, Const, DbInterner, EarlyBinder, EarlyParamRegion, - ErrorGuaranteed, GenericArgs, PolyFnSig, Predicate, Region, SolverDefId, TraitPredicate, - TraitRef, Ty, Tys, abi::Safety, generics::GenericParamDefKind, mapping::ChalkToNextSolver, + BoundVarKind, BoundVarKinds, Clause, Clauses, Const, DbInterner, EarlyBinder, + EarlyParamRegion, ErrorGuaranteed, GenericArgs, PolyFnSig, Predicate, Region, SolverDefId, + TraitPredicate, TraitRef, Ty, Tys, abi::Safety, generics::GenericParamDefKind, + mapping::ChalkToNextSolver, }, }; @@ -1593,6 +1594,95 @@ fn fn_sig_for_enum_variant_constructor<'db>( })) } +pub(crate) fn associated_ty_item_bounds<'db>( + db: &'db dyn HirDatabase, + type_alias: TypeAliasId, +) -> EarlyBinder<'db, BoundExistentialPredicates<'db>> { + let trait_ = match type_alias.lookup(db).container { + ItemContainerId::TraitId(t) => t, + _ => panic!("associated type not in trait"), + }; + + let type_alias_data = db.type_alias_signature(type_alias); + let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db); + let interner = DbInterner::new_with(db, Some(resolver.krate()), None); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &type_alias_data.store, + type_alias.into(), + LifetimeElisionKind::AnonymousReportError, + ); + // FIXME: we should never create non-existential predicates in the first place + // For now, use an error type so we don't run into dummy binder issues + let self_ty = Ty::new_error(interner, ErrorGuaranteed); + + let mut bounds = Vec::new(); + for bound in &type_alias_data.bounds { + ctx.lower_type_bound(bound, self_ty, false).for_each(|pred| { + if let Some(bound) = pred + .kind() + .map_bound(|c| match c { + rustc_type_ir::ClauseKind::Trait(t) => { + let id = t.def_id(); + let id = match id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + let is_auto = db.trait_signature(id).flags.contains(TraitFlags::AUTO); + if is_auto { + Some(ExistentialPredicate::AutoTrait(t.def_id())) + } else { + Some(ExistentialPredicate::Trait(ExistentialTraitRef::new_from_args( + interner, + t.def_id(), + GenericArgs::new_from_iter( + interner, + t.trait_ref.args.iter().skip(1), + ), + ))) + } + } + rustc_type_ir::ClauseKind::Projection(p) => Some( + ExistentialPredicate::Projection(ExistentialProjection::new_from_args( + interner, + p.def_id(), + GenericArgs::new_from_iter( + interner, + p.projection_term.args.iter().skip(1), + ), + p.term, + )), + ), + rustc_type_ir::ClauseKind::TypeOutlives(outlives_predicate) => None, + rustc_type_ir::ClauseKind::RegionOutlives(_) + | rustc_type_ir::ClauseKind::ConstArgHasType(_, _) + | rustc_type_ir::ClauseKind::WellFormed(_) + | rustc_type_ir::ClauseKind::ConstEvaluatable(_) + | rustc_type_ir::ClauseKind::HostEffect(_) + | rustc_type_ir::ClauseKind::UnstableFeature(_) => unreachable!(), + }) + .transpose() + { + bounds.push(bound); + } + }); + } + + if !ctx.unsized_types.contains(&self_ty) { + let sized_trait = LangItem::Sized.resolve_trait(db, resolver.krate()); + let sized_clause = Binder::dummy(ExistentialPredicate::Trait(ExistentialTraitRef::new( + interner, + SolverDefId::TraitId(trait_), + [] as [crate::next_solver::GenericArg<'_>; 0], + ))); + bounds.push(sized_clause); + bounds.shrink_to_fit(); + } + + EarlyBinder::bind(BoundExistentialPredicates::new_from_iter(interner, bounds)) +} + pub(crate) fn associated_type_by_name_including_super_traits<'db>( db: &'db dyn HirDatabase, trait_ref: TraitRef<'db>, From cd0e0957bff2fca7f46b87477b46ba1e21535494 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Tue, 12 Aug 2025 20:46:19 +0000 Subject: [PATCH 080/682] Switch generics_require_sized_self to next solver --- .../crates/hir-ty/src/dyn_compatibility.rs | 42 ++++++++----------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index 54d78dea3d3b..be8e23f7ceb1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -2,7 +2,6 @@ use std::ops::ControlFlow; -use chalk_ir::DebruijnIndex; use hir_def::{ AssocItemId, ConstId, CrateRootModuleId, FunctionId, GenericDefId, HasModule, TraitId, TypeAliasId, lang_item::LangItem, signatures::TraitFlags, @@ -11,23 +10,20 @@ use intern::Symbol; use rustc_hash::FxHashSet; use rustc_type_ir::{ AliasTyKind, ClauseKind, PredicatePolarity, TypeSuperVisitable as _, TypeVisitable as _, - Upcast, + Upcast, elaborate, inherent::{IntoKind, SliceLike}, }; use smallvec::SmallVec; use crate::{ - ImplTraitId, Interner, TyKind, WhereClause, all_super_traits, + ImplTraitId, all_super_traits, db::{HirDatabase, InternedOpaqueTyId}, - from_chalk_trait_id, - generics::trait_self_param_idx, lower_nextsolver::associated_ty_item_bounds, next_solver::{ Clause, Clauses, DbInterner, GenericArgs, ParamEnv, SolverDefId, TraitPredicate, TypingMode, infer::DbInternerInferExt, mk_param, }, traits::next_trait_solve_in_ctxt, - utils::elaborate_clause_supertraits, }; #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -133,27 +129,23 @@ pub fn generics_require_sized_self(db: &dyn HirDatabase, def: GenericDefId) -> b return false; }; - let Some(trait_self_param_idx) = trait_self_param_idx(db, def) else { - return false; - }; - - let predicates = &*db.generic_predicates(def); - let predicates = predicates.iter().map(|p| p.skip_binders().skip_binders().clone()); - elaborate_clause_supertraits(db, predicates).any(|pred| match pred { - WhereClause::Implemented(trait_ref) => { - if from_chalk_trait_id(trait_ref.trait_id) == sized - && let TyKind::BoundVar(it) = - *trait_ref.self_type_parameter(Interner).kind(Interner) - { - // Since `generic_predicates` is `Binder>`, the `DebrujinIndex` of - // self-parameter is `1` - return it - .index_if_bound_at(DebruijnIndex::ONE) - .is_some_and(|idx| idx == trait_self_param_idx); + let interner = DbInterner::new_with(db, Some(krate), None); + let predicates = db.generic_predicates_ns(def); + elaborate::elaborate(interner, predicates.iter().copied()).any(|pred| { + match pred.kind().skip_binder() { + ClauseKind::Trait(trait_pred) => { + if SolverDefId::TraitId(sized) == trait_pred.def_id() + && let rustc_type_ir::TyKind::Param(param_ty) = + trait_pred.trait_ref.self_ty().kind() + && param_ty.index == 0 + { + true + } else { + false + } } - false + _ => false, } - _ => false, }) } From 00856fc250be4a8e9da6630f0ee23120486418e8 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Tue, 12 Aug 2025 21:45:03 +0000 Subject: [PATCH 081/682] Remove all_super_traits in dyn_compatibility --- .../crates/hir-ty/src/dyn_compatibility.rs | 40 +++++++++++++++---- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index be8e23f7ceb1..b0c61c29db0b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -16,7 +16,7 @@ use rustc_type_ir::{ use smallvec::SmallVec; use crate::{ - ImplTraitId, all_super_traits, + ImplTraitId, db::{HirDatabase, InternedOpaqueTyId}, lower_nextsolver::associated_ty_item_bounds, next_solver::{ @@ -53,13 +53,22 @@ pub fn dyn_compatibility( db: &dyn HirDatabase, trait_: TraitId, ) -> Option { - for super_trait in all_super_traits(db, trait_).into_iter().skip(1).rev() { - if db.dyn_compatibility_of_trait(super_trait).is_some() { - return Some(DynCompatibilityViolation::HasNonCompatibleSuperTrait(super_trait)); + let interner = DbInterner::new_with(db, Some(trait_.krate(db)), None); + for super_trait in elaborate::supertrait_def_ids(interner, SolverDefId::TraitId(trait_)) { + let super_trait = match super_trait { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + if let Some(v) = db.dyn_compatibility_of_trait(super_trait) { + return if super_trait == trait_ { + Some(v) + } else { + Some(DynCompatibilityViolation::HasNonCompatibleSuperTrait(super_trait)) + }; } } - db.dyn_compatibility_of_trait(trait_) + None } pub fn dyn_compatibility_with_callback( @@ -70,7 +79,13 @@ pub fn dyn_compatibility_with_callback( where F: FnMut(DynCompatibilityViolation) -> ControlFlow<()>, { - for super_trait in all_super_traits(db, trait_).into_iter().skip(1).rev() { + let interner = DbInterner::new_with(db, Some(trait_.krate(db)), None); + for super_trait in elaborate::supertrait_def_ids(interner, SolverDefId::TraitId(trait_)).skip(1) + { + let super_trait = match super_trait { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; if db.dyn_compatibility_of_trait(super_trait).is_some() { cb(DynCompatibilityViolation::HasNonCompatibleSuperTrait(trait_))?; } @@ -225,6 +240,7 @@ fn contains_illegal_self_type_reference<'db, T: rustc_type_ir::TypeVisitable as rustc_type_ir::Interner>::Ty, ) -> Self::Result { + let interner = DbInterner::new_with(self.db, None, None); match ty.kind() { rustc_type_ir::TyKind::Param(param) if param.index == 0 => ControlFlow::Break(()), rustc_type_ir::TyKind::Param(_) => ControlFlow::Continue(()), @@ -238,7 +254,17 @@ fn contains_illegal_self_type_reference<'db, T: rustc_type_ir::TypeVisitable unreachable!(), }; if self.super_traits.is_none() { - self.super_traits = Some(all_super_traits(self.db, self.trait_)); + self.super_traits = Some( + elaborate::supertrait_def_ids( + interner, + SolverDefId::TraitId(self.trait_), + ) + .map(|super_trait| match super_trait { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }) + .collect(), + ) } if self.super_traits.as_ref().is_some_and(|s| s.contains(&trait_)) { ControlFlow::Continue(()) From f92ca612a8bbb71159fbfc11510b5ce393534c2e Mon Sep 17 00:00:00 2001 From: jackh726 Date: Tue, 12 Aug 2025 22:21:48 +0000 Subject: [PATCH 082/682] Replace layout_of_ty with layout_of_ty_ns --- .../crates/hir-ty/src/consteval.rs | 13 ++++++-- .../crates/hir-ty/src/consteval_nextsolver.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/db.rs | 14 +++----- .../crates/hir-ty/src/display.rs | 15 +++++---- .../rust-analyzer/crates/hir-ty/src/layout.rs | 32 ++++--------------- .../crates/hir-ty/src/layout/adt.rs | 2 +- .../crates/hir-ty/src/layout/tests.rs | 23 ++++++++----- .../crates/hir-ty/src/mir/eval.rs | 2 +- .../crates/hir-ty/src/mir/lower.rs | 9 ++++-- src/tools/rust-analyzer/crates/hir/src/lib.rs | 6 ++-- .../rust-analyzer/crates/ide/src/hover.rs | 26 ++++++++------- .../crates/ide/src/view_memory_layout.rs | 4 +-- 12 files changed, 75 insertions(+), 73 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs index f30ec839a009..abf97f3d0e30 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -15,8 +15,14 @@ use triomphe::Arc; use crate::{ Const, ConstData, ConstScalar, ConstValue, GenericArg, Interner, MemoryMap, Substitution, - TraitEnvironment, Ty, TyBuilder, db::HirDatabase, display::DisplayTarget, generics::Generics, - infer::InferenceContext, lower::ParamLoweringMode, to_placeholder_idx, + TraitEnvironment, Ty, TyBuilder, + db::HirDatabase, + display::DisplayTarget, + generics::Generics, + infer::InferenceContext, + lower::ParamLoweringMode, + next_solver::{DbInterner, mapping::ChalkToNextSolver}, + to_placeholder_idx, }; use super::mir::{MirEvalError, MirLowerError, interpret_mir, lower_to_mir, pad16}; @@ -157,7 +163,8 @@ pub fn intern_const_ref( ty: Ty, krate: Crate, ) -> Const { - let layout = || db.layout_of_ty(ty.clone(), TraitEnvironment::empty(krate)); + let interner = DbInterner::new_with(db, Some(krate), None); + let layout = || db.layout_of_ty(ty.to_nextsolver(interner), TraitEnvironment::empty(krate)); let bytes = match value { LiteralConstRef::Int(i) => { // FIXME: We should handle failure of layout better. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs index cdf861290ab9..00fc4e5610dc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs @@ -92,7 +92,7 @@ pub fn intern_const_ref<'a>( krate: Crate, ) -> Const<'a> { let interner = DbInterner::new_with(db, Some(krate), None); - let layout = db.layout_of_ty_ns(ty, TraitEnvironment::empty(krate)); + let layout = db.layout_of_ty(ty, TraitEnvironment::empty(krate)); let kind = match value { LiteralConstRef::Int(i) => { // FIXME: We should handle failure of layout better. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 161ad31e579b..9affd3b48c58 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -103,7 +103,11 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { #[salsa::invoke(crate::layout::layout_of_ty_query)] #[salsa::cycle(cycle_result = crate::layout::layout_of_ty_cycle_result)] - fn layout_of_ty(&self, ty: Ty, env: Arc) -> Result, LayoutError>; + fn layout_of_ty<'db>( + &'db self, + ty: crate::next_solver::Ty<'db>, + env: Arc, + ) -> Result, LayoutError>; #[salsa::invoke(crate::layout::target_data_layout_query)] fn target_data_layout(&self, krate: Crate) -> Result, Arc>; @@ -300,14 +304,6 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { // next trait solver - #[salsa::invoke(crate::layout::layout_of_ty_ns_query)] - #[salsa::cycle(cycle_result = crate::layout::layout_of_ty_ns_cycle_result)] - fn layout_of_ty_ns<'db>( - &'db self, - ty: crate::next_solver::Ty<'db>, - env: Arc, - ) -> Result, LayoutError>; - #[salsa::invoke(crate::lower_nextsolver::ty_query)] #[salsa::transparent] fn ty_ns<'db>( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 5adbea75a67d..cdf6085b6534 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -840,7 +840,7 @@ fn render_const_scalar_inner( TyKind::Slice(ty) => { let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap()); let count = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap()); - let Ok(layout) = f.db.layout_of_ty_ns(ty, trait_env) else { + let Ok(layout) = f.db.layout_of_ty(ty, trait_env) else { return f.write_str(""); }; let size_one = layout.size.bytes_usize(); @@ -874,7 +874,7 @@ fn render_const_scalar_inner( let Ok(t) = memory_map.vtable_ty(ty_id) else { return f.write_str(""); }; - let Ok(layout) = f.db.layout_of_ty_ns(t, trait_env) else { + let Ok(layout) = f.db.layout_of_ty(t, trait_env) else { return f.write_str(""); }; let size = layout.size.bytes_usize(); @@ -905,7 +905,7 @@ fn render_const_scalar_inner( return f.write_str(""); } }); - let Ok(layout) = f.db.layout_of_ty_ns(t, trait_env) else { + let Ok(layout) = f.db.layout_of_ty(t, trait_env) else { return f.write_str(""); }; let size = layout.size.bytes_usize(); @@ -917,7 +917,7 @@ fn render_const_scalar_inner( } }, TyKind::Tuple(tys) => { - let Ok(layout) = f.db.layout_of_ty_ns(ty, trait_env.clone()) else { + let Ok(layout) = f.db.layout_of_ty(ty, trait_env.clone()) else { return f.write_str(""); }; f.write_str("(")?; @@ -929,7 +929,7 @@ fn render_const_scalar_inner( f.write_str(", ")?; } let offset = layout.fields.offset(id).bytes_usize(); - let Ok(layout) = f.db.layout_of_ty_ns(ty, trait_env.clone()) else { + let Ok(layout) = f.db.layout_of_ty(ty, trait_env.clone()) else { f.write_str("")?; continue; }; @@ -1006,7 +1006,7 @@ fn render_const_scalar_inner( let Some(len) = consteval_nextsolver::try_const_usize(f.db, len) else { return f.write_str(""); }; - let Ok(layout) = f.db.layout_of_ty_ns(ty, trait_env) else { + let Ok(layout) = f.db.layout_of_ty(ty, trait_env) else { return f.write_str(""); }; let size_one = layout.size.bytes_usize(); @@ -1061,7 +1061,8 @@ fn render_variant_after_name( let ty = field_types[id] .clone() .substitute(Interner, &convert_args_for_result(interner, args.as_slice())); - let Ok(layout) = f.db.layout_of_ty(ty.clone(), trait_env.clone()) else { + let Ok(layout) = f.db.layout_of_ty(ty.to_nextsolver(interner), trait_env.clone()) + else { return f.write_str(""); }; let size = layout.size.bytes_usize(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index 0a8ec949b7c0..e2ee8935a885 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -151,23 +151,13 @@ fn layout_of_simd_ty<'db>( }; let e_len = try_const_usize(db, e_len).ok_or(LayoutError::HasErrorConst)? as u64; - let e_ly = db.layout_of_ty_ns(e_ty, env)?; + let e_ly = db.layout_of_ty(e_ty, env)?; let cx = LayoutCx::new(dl); Ok(Arc::new(cx.calc.simd_type(e_ly, e_len, repr_packed)?)) } -pub fn layout_of_ty_query( - db: &dyn HirDatabase, - ty: crate::Ty, - trait_env: Arc, -) -> Result, LayoutError> { - let krate = trait_env.krate; - let interner = DbInterner::new_with(db, Some(krate), trait_env.block); - db.layout_of_ty_ns(ty.to_nextsolver(interner), trait_env) -} - -pub fn layout_of_ty_ns_query<'db>( +pub fn layout_of_ty_query<'db>( db: &'db dyn HirDatabase, ty: Ty<'db>, trait_env: Arc, @@ -262,7 +252,7 @@ pub fn layout_of_ty_ns_query<'db>( let fields = tys .iter() - .map(|k| db.layout_of_ty_ns(k, trait_env.clone())) + .map(|k| db.layout_of_ty(k, trait_env.clone())) .collect::, _>>()?; let fields = fields.iter().map(|it| &**it).collect::>(); let fields = fields.iter().collect::>(); @@ -270,11 +260,11 @@ pub fn layout_of_ty_ns_query<'db>( } TyKind::Array(element, count) => { let count = try_const_usize(db, count).ok_or(LayoutError::HasErrorConst)? as u64; - let element = db.layout_of_ty_ns(element, trait_env)?; + let element = db.layout_of_ty(element, trait_env)?; cx.calc.array_like::<_, _, ()>(&element, Some(count))? } TyKind::Slice(element) => { - let element = db.layout_of_ty_ns(element, trait_env)?; + let element = db.layout_of_ty(element, trait_env)?; cx.calc.array_like::<_, _, ()>(&element, None)? } TyKind::Str => { @@ -346,7 +336,7 @@ pub fn layout_of_ty_ns_query<'db>( let ty = convert_binder_to_early_binder(interner, it.ty.to_nextsolver(interner)) .instantiate(interner, args); - db.layout_of_ty_ns(ty, trait_env.clone()) + db.layout_of_ty(ty, trait_env.clone()) }) .collect::, _>>()?; let fields = fields.iter().map(|it| &**it).collect::>(); @@ -376,15 +366,7 @@ pub fn layout_of_ty_ns_query<'db>( Ok(Arc::new(result)) } -pub(crate) fn layout_of_ty_cycle_result( - _: &dyn HirDatabase, - _: crate::Ty, - _: Arc, -) -> Result, LayoutError> { - Err(LayoutError::RecursiveTypeWithoutIndirection) -} - -pub(crate) fn layout_of_ty_ns_cycle_result<'db>( +pub(crate) fn layout_of_ty_cycle_result<'db>( _: &dyn HirDatabase, _: Ty<'db>, _: Arc, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs index fefa3f261743..9a746ca88858 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs @@ -34,7 +34,7 @@ pub fn layout_of_adt_query<'db>( let handle_variant = |def: VariantId, var: &VariantFields| { var.fields() .iter() - .map(|(fd, _)| db.layout_of_ty_ns(field_ty(db, def, fd, &args), trait_env.clone())) + .map(|(fd, _)| db.layout_of_ty(field_ty(db, def, fd, &args), trait_env.clone())) .collect::, _>>() }; let (variants, repr, is_special_no_niche) = match def { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs index 93f2e123dca5..90de7e5ca633 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs @@ -11,6 +11,7 @@ use crate::{ Interner, Substitution, db::HirDatabase, layout::{Layout, LayoutError}, + next_solver::{DbInterner, mapping::ChalkToNextSolver}, setup_tracing, test_db::TestDB, }; @@ -85,13 +86,16 @@ fn eval_goal( db.ty(ty_id.into()).substitute(Interner, &Substitution::empty(Interner)) } }; - db.layout_of_ty( - goal_ty, - db.trait_environment(match adt_or_type_alias_id { - Either::Left(adt) => hir_def::GenericDefId::AdtId(adt), - Either::Right(ty) => hir_def::GenericDefId::TypeAliasId(ty), - }), - ) + salsa::attach(&db, || { + let interner = DbInterner::new_with(&db, None, None); + db.layout_of_ty( + goal_ty.to_nextsolver(interner), + db.trait_environment(match adt_or_type_alias_id { + Either::Left(adt) => hir_def::GenericDefId::AdtId(adt), + Either::Right(ty) => hir_def::GenericDefId::TypeAliasId(ty), + }), + ) + }) } /// A version of `eval_goal` for types that can not be expressed in ADTs, like closures and `impl Trait` @@ -128,7 +132,10 @@ fn eval_expr( .0; let infer = db.infer(function_id.into()); let goal_ty = infer.type_of_binding[b].clone(); - db.layout_of_ty(goal_ty, db.trait_environment(function_id.into())) + salsa::attach(&db, || { + let interner = DbInterner::new_with(&db, None, None); + db.layout_of_ty(goal_ty.to_nextsolver(interner), db.trait_environment(function_id.into())) + }) } #[track_caller] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index d0ae92961efc..9deaa4ac5a6b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -854,7 +854,7 @@ impl<'db> Evaluator<'db> { let interner = DbInterner::new_with(self.db, None, None); let r = self .db - .layout_of_ty_ns(ty, self.trait_env.clone()) + .layout_of_ty(ty, self.trait_env.clone()) .map_err(|e| MirEvalError::LayoutError(e, convert_ty_for_result(interner, ty)))?; self.layout_cache.borrow_mut().insert(ty, r.clone()); Ok(r) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 0bb8e6fe79d6..052be11e433f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -43,6 +43,7 @@ use crate::{ Terminator, TerminatorKind, TupleFieldId, Ty, UnOp, VariantId, intern_const_scalar, return_slot, }, + next_solver::{DbInterner, mapping::ChalkToNextSolver}, static_lifetime, traits::FnTrait, utils::ClosureSubst, @@ -1411,8 +1412,12 @@ impl<'ctx> MirLowerCtx<'ctx> { } fn lower_literal_to_operand(&mut self, ty: Ty, l: &Literal) -> Result { - let size = - || self.db.layout_of_ty(ty.clone(), self.env.clone()).map(|it| it.size.bytes_usize()); + let interner = DbInterner::new_with(self.db, None, None); + let size = || { + self.db + .layout_of_ty(ty.to_nextsolver(interner), self.env.clone()) + .map(|it| it.size.bytes_usize()) + }; const USIZE_SIZE: usize = size_of::(); let bytes: Box<[_]> = match l { hir_def::hir::Literal::String(b) => { diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 18c3ea05614a..e423d2c07c15 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -1389,8 +1389,9 @@ impl Field { } pub fn layout(&self, db: &dyn HirDatabase) -> Result { + let interner = DbInterner::new_with(db, None, None); db.layout_of_ty( - self.ty(db).ty, + self.ty(db).ty.to_nextsolver(interner), db.trait_environment(match hir_def::VariantId::from(self.parent) { hir_def::VariantId::EnumVariantId(id) => { GenericDefId::AdtId(id.lookup(db).parent.into()) @@ -5906,7 +5907,8 @@ impl<'db> Type<'db> { } pub fn layout(&self, db: &'db dyn HirDatabase) -> Result { - db.layout_of_ty(self.ty.clone(), self.env.clone()) + let interner = DbInterner::new_with(db, None, None); + db.layout_of_ty(self.ty.to_nextsolver(interner), self.env.clone()) .map(|layout| Layout(layout, db.target_data_layout(self.env.krate).unwrap())) } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index fc45dc3faf40..b0ef83e0501b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -137,18 +137,20 @@ pub(crate) fn hover( let edition = sema.attach_first_edition(file_id).map(|it| it.edition(db)).unwrap_or(Edition::CURRENT); let display_target = sema.first_crate(file_id)?.to_display_target(db); - let mut res = if range.is_empty() { - hover_offset( - sema, - FilePosition { file_id, offset: range.start() }, - file, - config, - edition, - display_target, - ) - } else { - hover_ranged(sema, frange, file, config, edition, display_target) - }?; + let mut res = salsa::attach(sema.db, || { + if range.is_empty() { + hover_offset( + sema, + FilePosition { file_id, offset: range.start() }, + file, + config, + edition, + display_target, + ) + } else { + hover_ranged(sema, frange, file, config, edition, display_target) + } + })?; if let HoverDocFormat::PlainText = config.format { res.info.markup = remove_markdown(res.info.markup.as_str()).into(); diff --git a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs index 1eb0fd4fd8b7..950f3f6c6470 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs @@ -139,7 +139,7 @@ pub(crate) fn view_memory_layout( nodes[parent_idx].children_len = fields.len() as u64; for (field, child_ty) in fields.iter() { - if let Ok(child_layout) = child_ty.layout(db) { + if let Ok(child_layout) = salsa::attach(db, || child_ty.layout(db)) { nodes.push(MemoryLayoutNode { item_name: field.name(db), typename: salsa::attach(db, || { @@ -172,7 +172,7 @@ pub(crate) fn view_memory_layout( } for (i, (_, child_ty)) in fields.iter().enumerate() { - if let Ok(child_layout) = child_ty.layout(db) { + if let Ok(child_layout) = salsa::attach(db, || child_ty.layout(db)) { read_layout(nodes, db, child_ty, &child_layout, children_start + i, display_target); } } From 05bc1818dac760fac07c9c6d562977cd9b51dab1 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Wed, 13 Aug 2025 03:13:45 +0000 Subject: [PATCH 083/682] Switch TraitRef in hir::TraitRef to next solver --- .../crates/hir-ty/src/display.rs | 238 ++++++++++++++++-- .../hir-ty/src/next_solver/generic_arg.rs | 23 ++ .../crates/hir-ty/src/next_solver/mapping.rs | 25 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 43 ++-- 4 files changed, 288 insertions(+), 41 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index cdf6085b6534..ae0113fcbd7f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -11,8 +11,8 @@ use base_db::Crate; use chalk_ir::{BoundVar, Safety, TyKind}; use either::Either; use hir_def::{ - GenericDefId, HasModule, ImportPathConfig, LocalFieldId, Lookup, ModuleDefId, ModuleId, - TraitId, + GeneralConstId, GenericDefId, HasModule, ImportPathConfig, LocalFieldId, Lookup, ModuleDefId, + ModuleId, TraitId, db::DefDatabase, expr_store::{ExpressionStore, path::Path}, find_path::{self, PrefixKind}, @@ -38,7 +38,7 @@ use rustc_apfloat::{ }; use rustc_hash::FxHashSet; use rustc_type_ir::{ - AliasTyKind, + AliasTyKind, RegionKind, inherent::{AdtDef, IntoKind, SliceLike}, }; use smallvec::SmallVec; @@ -61,8 +61,9 @@ use crate::{ next_solver::{ BoundExistentialPredicate, Ctor, DbInterner, GenericArgs, SolverDefId, mapping::{ - ChalkToNextSolver, convert_args_for_result, convert_const_for_result, - convert_region_for_result, convert_ty_for_result, + ChalkToNextSolver, bound_var_to_lifetime_idx, bound_var_to_type_or_const_param_idx, + convert_args_for_result, convert_const_for_result, convert_region_for_result, + convert_ty_for_result, }, }, primitive, to_assoc_type_id, @@ -715,28 +716,56 @@ impl HirDisplay for GenericArg { } } +impl<'db> HirDisplay for crate::next_solver::GenericArg<'db> { + fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { + match self.kind() { + rustc_type_ir::GenericArgKind::Type(ty) => ty.hir_fmt(f), + rustc_type_ir::GenericArgKind::Lifetime(lt) => lt.hir_fmt(f), + rustc_type_ir::GenericArgKind::Const(c) => c.hir_fmt(f), + } + } +} + impl HirDisplay for Const { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { - let data = self.interned(); - match &data.value { - ConstValue::BoundVar(idx) => idx.hir_fmt(f), - ConstValue::InferenceVar(..) => write!(f, "#c#"), - ConstValue::Placeholder(idx) => { - let id = from_placeholder_idx(f.db, *idx); + let c = self.to_nextsolver(DbInterner::new_with(f.db, None, None)); + c.hir_fmt(f) + } +} + +impl<'db> HirDisplay for crate::next_solver::Const<'db> { + fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { + match self.kind() { + rustc_type_ir::ConstKind::Bound(db, bound_const) => { + write!(f, "?{}.{}", db.as_u32(), bound_const.as_u32()) + } + rustc_type_ir::ConstKind::Infer(..) => write!(f, "#c#"), + rustc_type_ir::ConstKind::Placeholder(idx) => { + let id = bound_var_to_type_or_const_param_idx(f.db, idx.bound); let generics = generics(f.db, id.parent); let param_data = &generics[id.local_id]; write!(f, "{}", param_data.name().unwrap().display(f.db, f.edition()))?; Ok(()) } - ConstValue::Concrete(c) => match &c.interned { - ConstScalar::Bytes(b, m) => render_const_scalar(f, b, m, &data.ty), - ConstScalar::UnevaluatedConst(c, parameters) => { - write!(f, "{}", c.name(f.db))?; - hir_fmt_generics(f, parameters.as_slice(Interner), c.generic_def(f.db), None)?; - Ok(()) - } - ConstScalar::Unknown => f.write_char('_'), - }, + rustc_type_ir::ConstKind::Value(const_bytes) => render_const_scalar_ns( + f, + &const_bytes.value.inner().0, + &const_bytes.value.inner().1, + const_bytes.ty, + ), + rustc_type_ir::ConstKind::Unevaluated(unev) => { + let c = match unev.def { + SolverDefId::ConstId(id) => GeneralConstId::ConstId(id), + SolverDefId::StaticId(id) => GeneralConstId::StaticId(id), + _ => unreachable!(), + }; + write!(f, "{}", c.name(f.db))?; + hir_fmt_generics_ns(f, unev.args.as_slice(), c.generic_def(f.db), None)?; + Ok(()) + } + rustc_type_ir::ConstKind::Error(..) => f.write_char('_'), + rustc_type_ir::ConstKind::Expr(..) => write!(f, ""), + rustc_type_ir::ConstKind::Param(_) => write!(f, ""), } } } @@ -1748,6 +1777,27 @@ fn hir_fmt_generics( Ok(()) } +fn hir_fmt_generics_ns<'db>( + f: &mut HirFormatter<'_>, + parameters: &[crate::next_solver::GenericArg<'db>], + generic_def: Option, + self_: Option>, +) -> Result<(), HirDisplayError> { + if parameters.is_empty() { + return Ok(()); + } + + let parameters_to_write = generic_args_sans_defaults_ns(f, generic_def, parameters); + + if !parameters_to_write.is_empty() { + write!(f, "<")?; + hir_fmt_generic_arguments_ns(f, parameters_to_write, self_)?; + write!(f, ">")?; + } + + Ok(()) +} + fn generic_args_sans_defaults<'ga>( f: &mut HirFormatter<'_>, generic_def: Option, @@ -1803,6 +1853,87 @@ fn generic_args_sans_defaults<'ga>( } } +fn hir_fmt_generic_args<'db>( + f: &mut HirFormatter<'_>, + parameters: &[crate::next_solver::GenericArg<'db>], + generic_def: Option, + self_: Option>, +) -> Result<(), HirDisplayError> { + if parameters.is_empty() { + return Ok(()); + } + + let parameters_to_write = generic_args_sans_defaults_ns(f, generic_def, parameters); + + if !parameters_to_write.is_empty() { + write!(f, "<")?; + hir_fmt_generic_arguments_ns(f, parameters_to_write, self_)?; + write!(f, ">")?; + } + + Ok(()) +} + +fn generic_args_sans_defaults_ns<'ga, 'db>( + f: &mut HirFormatter<'_>, + generic_def: Option, + parameters: &'ga [crate::next_solver::GenericArg<'db>], +) -> &'ga [crate::next_solver::GenericArg<'db>] { + let interner = DbInterner::new_with(f.db, Some(f.krate()), None); + if f.display_kind.is_source_code() || f.omit_verbose_types() { + match generic_def + .map(|generic_def_id| f.db.generic_defaults(generic_def_id)) + .filter(|it| !it.is_empty()) + { + None => parameters, + Some(default_parameters) => { + let should_show = |arg: &crate::next_solver::GenericArg<'db>, i: usize| { + let is_err = |arg: &crate::next_solver::GenericArg<'db>| match arg.kind() { + rustc_type_ir::GenericArgKind::Lifetime(it) => { + matches!(it.kind(), RegionKind::ReError(..)) + } + rustc_type_ir::GenericArgKind::Type(it) => { + matches!(it.kind(), rustc_type_ir::TyKind::Error(..)) + } + rustc_type_ir::GenericArgKind::Const(it) => { + matches!(it.kind(), rustc_type_ir::ConstKind::Error(..),) + } + }; + // if the arg is error like, render it to inform the user + if is_err(arg) { + return true; + } + // otherwise, if the arg is equal to the param default, hide it (unless the + // default is an error which can happen for the trait Self type) + match default_parameters.get(i) { + None => true, + Some(default_parameter) => { + // !is_err(default_parameter.skip_binders()) + // && + arg != &default_parameter + .clone() + .substitute( + Interner, + &convert_args_for_result(interner, ¶meters[..i]), + ) + .to_nextsolver(interner) + } + } + }; + let mut default_from = 0; + for (i, parameter) in parameters.iter().enumerate() { + if should_show(parameter, i) { + default_from = i + 1; + } + } + ¶meters[0..default_from] + } + } + } else { + parameters + } +} + fn hir_fmt_generic_arguments( f: &mut HirFormatter<'_>, parameters: &[GenericArg], @@ -1827,6 +1958,30 @@ fn hir_fmt_generic_arguments( Ok(()) } +fn hir_fmt_generic_arguments_ns<'db>( + f: &mut HirFormatter<'_>, + parameters: &[crate::next_solver::GenericArg<'db>], + self_: Option>, +) -> Result<(), HirDisplayError> { + let mut first = true; + let lifetime_offset = parameters.iter().position(|arg| arg.region().is_some()); + + let (ty_or_const, lifetimes) = match lifetime_offset { + Some(offset) => parameters.split_at(offset), + None => (parameters, &[][..]), + }; + for generic_arg in lifetimes.iter().chain(ty_or_const) { + if !mem::take(&mut first) { + write!(f, ", ")?; + } + match self_ { + self_ @ Some(_) if generic_arg.ty() == self_ => write!(f, "Self")?, + _ => generic_arg.hir_fmt(f)?, + } + } + Ok(()) +} + impl HirDisplay for CallableSig { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { let CallableSig { params_and_return: _, is_varargs, safety, abi: _ } = *self; @@ -2067,6 +2222,20 @@ impl HirDisplay for TraitRef { } } +impl<'db> HirDisplay for crate::next_solver::TraitRef<'db> { + fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { + let trait_ = match self.def_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; + f.start_location_link(trait_.into()); + write!(f, "{}", f.db.trait_signature(trait_).name.display(f.db, f.edition()))?; + f.end_location_link(); + let substs = self.args.as_slice(); + hir_fmt_generic_args(f, &substs[1..], None, substs[0].ty()) + } +} + impl HirDisplay for WhereClause { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { if f.should_truncate() { @@ -2147,6 +2316,35 @@ impl HirDisplay for LifetimeData { } } +impl<'db> HirDisplay for crate::next_solver::Region<'db> { + fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { + match self.kind() { + rustc_type_ir::RegionKind::RePlaceholder(idx) => { + let id = bound_var_to_lifetime_idx(f.db, idx.bound.var); + let generics = generics(f.db, id.parent); + let param_data = &generics[id.local_id]; + write!(f, "{}", param_data.name.display(f.db, f.edition()))?; + Ok(()) + } + rustc_type_ir::RegionKind::ReBound(db, idx) => { + write!(f, "?{}.{}", db.as_u32(), idx.var.as_u32()) + } + rustc_type_ir::RegionKind::ReVar(_) => write!(f, "_"), + rustc_type_ir::RegionKind::ReStatic => write!(f, "'static"), + rustc_type_ir::RegionKind::ReError(..) => { + if cfg!(test) { + write!(f, "'?") + } else { + write!(f, "'_") + } + } + rustc_type_ir::RegionKind::ReErased => write!(f, "'"), + rustc_type_ir::RegionKind::ReEarlyParam(_) => write!(f, ""), + rustc_type_ir::RegionKind::ReLateParam(_) => write!(f, ""), + } + } +} + impl HirDisplay for DomainGoal { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { match self { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs index 046b4303c326..834f4e3765ee 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs @@ -35,6 +35,29 @@ impl<'db> std::fmt::Debug for GenericArg<'db> { } } +impl<'db> GenericArg<'db> { + pub fn ty(self) -> Option> { + match self.kind() { + GenericArgKind::Type(ty) => Some(ty), + _ => None, + } + } + + pub fn expect_ty(self) -> Ty<'db> { + match self.kind() { + GenericArgKind::Type(ty) => ty, + _ => panic!("Expected ty, got {:?}", self), + } + } + + pub fn region(self) -> Option> { + match self.kind() { + GenericArgKind::Lifetime(r) => Some(r), + _ => None, + } + } +} + impl<'db> From> for GenericArg<'db> { fn from(value: Term<'db>) -> Self { match value { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index 5fefb04a5e76..cad51fd85f55 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -18,13 +18,14 @@ use rustc_type_ir::{ shift_vars, solve::Goal, }; -use salsa::plumbing::AsId; +use salsa::plumbing::FromId; +use salsa::{Id, plumbing::AsId}; use crate::{ ConcreteConst, ConstScalar, ImplTraitId, Interner, MemoryMap, db::{ - HirDatabase, InternedClosureId, InternedCoroutineId, InternedOpaqueTyId, - InternedTypeOrConstParamId, + HirDatabase, InternedClosureId, InternedCoroutineId, InternedLifetimeParamId, + InternedOpaqueTyId, InternedTypeOrConstParamId, }, from_assoc_type_id, from_chalk_trait_id, mapping::ToChalk, @@ -55,6 +56,24 @@ pub fn to_placeholder_idx( } } +pub fn bound_var_to_type_or_const_param_idx( + db: &dyn HirDatabase, + var: rustc_type_ir::BoundVar, +) -> TypeOrConstParamId { + // SAFETY: We cannot really encapsulate this unfortunately, so just hope this is sound. + let interned_id = InternedTypeOrConstParamId::from_id(unsafe { Id::from_index(var.as_u32()) }); + interned_id.loc(db) +} + +pub fn bound_var_to_lifetime_idx( + db: &dyn HirDatabase, + var: rustc_type_ir::BoundVar, +) -> LifetimeParamId { + // SAFETY: We cannot really encapsulate this unfortunately, so just hope this is sound. + let interned_id = InternedLifetimeParamId::from_id(unsafe { Id::from_index(var.as_u32()) }); + interned_id.loc(db) +} + pub fn convert_binder_to_early_binder<'db, T: rustc_type_ir::TypeFoldable>>( interner: DbInterner<'db>, binder: rustc_type_ir::Binder, T>, diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index e423d2c07c15..46a0f584c013 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -115,7 +115,7 @@ pub use crate::{ VisibleTraits, }, }; -use rustc_type_ir::inherent::IntoKind; +use rustc_type_ir::inherent::{IntoKind, SliceLike}; // Be careful with these re-exports. // @@ -4513,14 +4513,20 @@ impl Impl { } pub fn trait_(self, db: &dyn HirDatabase) -> Option { - let trait_ref = db.impl_trait(self.id)?; - let id = trait_ref.skip_binders().hir_trait_id(); + let trait_ref = db.impl_trait_ns(self.id)?; + let id = trait_ref.skip_binder().def_id; + let id = match id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; Some(Trait { id }) } pub fn trait_ref(self, db: &dyn HirDatabase) -> Option> { + let interner = DbInterner::new_with(db, None, None); let substs = TyBuilder::placeholder_subst(db, self.id); - let trait_ref = db.impl_trait(self.id)?.substitute(Interner, &substs); + let trait_ref = + db.impl_trait(self.id)?.substitute(Interner, &substs).to_nextsolver(interner); let resolver = self.id.resolver(db); Some(TraitRef::new_with_resolver(db, &resolver, trait_ref)) } @@ -4589,7 +4595,7 @@ impl Impl { #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub struct TraitRef<'db> { env: Arc, - trait_ref: hir_ty::TraitRef, + trait_ref: hir_ty::next_solver::TraitRef<'db>, _pd: PhantomCovariantLifetime<'db>, } @@ -4597,7 +4603,7 @@ impl<'db> TraitRef<'db> { pub(crate) fn new_with_resolver( db: &'db dyn HirDatabase, resolver: &Resolver<'_>, - trait_ref: hir_ty::TraitRef, + trait_ref: hir_ty::next_solver::TraitRef<'db>, ) -> Self { let env = resolver .generic_def() @@ -4606,25 +4612,26 @@ impl<'db> TraitRef<'db> { } pub fn trait_(&self) -> Trait { - let id = self.trait_ref.hir_trait_id(); + let id = match self.trait_ref.def_id { + SolverDefId::TraitId(id) => id, + _ => unreachable!(), + }; Trait { id } } - pub fn self_ty(&self) -> Type<'_> { - let ty = self.trait_ref.self_type_parameter(Interner); - Type { env: self.env.clone(), ty, _pd: PhantomCovariantLifetime::new() } + pub fn self_ty(&self) -> TypeNs<'_> { + let ty = self.trait_ref.self_ty(); + TypeNs { env: self.env.clone(), ty, _pd: PhantomCovariantLifetime::new() } } /// Returns `idx`-th argument of this trait reference if it is a type argument. Note that the /// first argument is the `Self` type. - pub fn get_type_argument(&self, idx: usize) -> Option> { - self.trait_ref - .substitution - .as_slice(Interner) - .get(idx) - .and_then(|arg| arg.ty(Interner)) - .cloned() - .map(|ty| Type { env: self.env.clone(), ty, _pd: PhantomCovariantLifetime::new() }) + pub fn get_type_argument(&self, idx: usize) -> Option> { + self.trait_ref.args.as_slice().get(idx).and_then(|arg| arg.ty()).map(|ty| TypeNs { + env: self.env.clone(), + ty, + _pd: PhantomCovariantLifetime::new(), + }) } } From 49f166029f6956c6422b73bfa52f416cd2be12f0 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Wed, 13 Aug 2025 03:35:01 +0000 Subject: [PATCH 084/682] Use impl_trait_ns in Impl::trait_ref --- src/tools/rust-analyzer/crates/hir/src/lib.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 46a0f584c013..ff7d116dcce3 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -4523,10 +4523,7 @@ impl Impl { } pub fn trait_ref(self, db: &dyn HirDatabase) -> Option> { - let interner = DbInterner::new_with(db, None, None); - let substs = TyBuilder::placeholder_subst(db, self.id); - let trait_ref = - db.impl_trait(self.id)?.substitute(Interner, &substs).to_nextsolver(interner); + let trait_ref = db.impl_trait_ns(self.id)?.instantiate_identity(); let resolver = self.id.resolver(db); Some(TraitRef::new_with_resolver(db, &resolver, trait_ref)) } From e5d320fd6c28b5099389e0a34a688e8d37d90f4b Mon Sep 17 00:00:00 2001 From: jackh726 Date: Wed, 13 Aug 2025 04:23:00 +0000 Subject: [PATCH 085/682] Remove a bunch of stuff from chalk_db --- .../crates/hir-ty/src/chalk_db.rs | 425 +----------------- .../rust-analyzer/crates/hir-ty/src/db.rs | 29 -- .../crates/hir-ty/src/diagnostics/expr.rs | 7 +- .../crates/hir-ty/src/infer/cast.rs | 12 +- .../crates/hir-ty/src/infer/closure.rs | 26 +- .../crates/hir-ty/src/mapping.rs | 19 - src/tools/rust-analyzer/crates/hir/src/lib.rs | 75 +++- 7 files changed, 80 insertions(+), 513 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs index f523a5e8bfa0..546991cf6571 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs @@ -1,44 +1,13 @@ //! The implementation of `RustIrDatabase` for Chalk, which provides information //! about the code that Chalk needs. -use std::sync::Arc; +use hir_def::{CallableDefId, GenericDefId}; -use tracing::debug; - -use chalk_ir::{cast::Caster, fold::shift::Shift}; -use chalk_solve::rust_ir::{self, WellKnownTrait}; - -use base_db::Crate; -use hir_def::{ - AssocItemId, CallableDefId, GenericDefId, HasModule, ItemContainerId, Lookup, TypeAliasId, - VariantId, - lang_item::LangItem, - signatures::{ImplFlags, StructFlags, TraitFlags}, -}; - -use crate::{ - AliasEq, AliasTy, DebruijnIndex, Interner, ProjectionTyExt, QuantifiedWhereClause, - Substitution, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, WhereClause, - db::HirDatabase, - from_assoc_type_id, from_chalk_trait_id, - generics::generics, - lower::LifetimeElisionKind, - make_binders, - mapping::{ToChalk, TypeAliasAsValue, from_chalk}, - to_assoc_type_id, to_chalk_trait_id, -}; - -pub(crate) type AssociatedTyDatum = chalk_solve::rust_ir::AssociatedTyDatum; -pub(crate) type TraitDatum = chalk_solve::rust_ir::TraitDatum; -pub(crate) type AdtDatum = chalk_solve::rust_ir::AdtDatum; -pub(crate) type ImplDatum = chalk_solve::rust_ir::ImplDatum; +use crate::{Interner, Substitution, db::HirDatabase, mapping::from_chalk}; pub(crate) type AssocTypeId = chalk_ir::AssocTypeId; pub(crate) type TraitId = chalk_ir::TraitId; pub(crate) type AdtId = chalk_ir::AdtId; pub(crate) type ImplId = chalk_ir::ImplId; -pub(crate) type AssociatedTyValueId = chalk_solve::rust_ir::AssociatedTyValueId; -pub(crate) type AssociatedTyValue = chalk_solve::rust_ir::AssociatedTyValue; -pub(crate) type FnDefDatum = chalk_solve::rust_ir::FnDefDatum; pub(crate) type Variances = chalk_ir::Variances; impl chalk_ir::UnificationDatabase for &dyn HirDatabase { @@ -54,340 +23,6 @@ impl chalk_ir::UnificationDatabase for &dyn HirDatabase { } } -pub(crate) fn associated_ty_data_query( - db: &dyn HirDatabase, - type_alias: TypeAliasId, -) -> Arc { - debug!("associated_ty_data {:?}", type_alias); - let trait_ = match type_alias.lookup(db).container { - ItemContainerId::TraitId(t) => t, - _ => panic!("associated type not in trait"), - }; - - // Lower bounds -- we could/should maybe move this to a separate query in `lower` - let type_alias_data = db.type_alias_signature(type_alias); - let generic_params = generics(db, type_alias.into()); - let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db); - let mut ctx = crate::TyLoweringContext::new( - db, - &resolver, - &type_alias_data.store, - type_alias.into(), - LifetimeElisionKind::AnonymousReportError, - ) - .with_type_param_mode(crate::lower::ParamLoweringMode::Variable); - - let trait_subst = TyBuilder::subst_for_def(db, trait_, None) - .fill_with_bound_vars(crate::DebruijnIndex::INNERMOST, 0) - .build(); - let pro_ty = TyBuilder::assoc_type_projection(db, type_alias, Some(trait_subst)) - .fill_with_bound_vars( - crate::DebruijnIndex::INNERMOST, - generic_params.parent_generics().map_or(0, |it| it.len()), - ) - .build(); - let self_ty = TyKind::Alias(AliasTy::Projection(pro_ty)).intern(Interner); - - let mut bounds = Vec::new(); - for bound in &type_alias_data.bounds { - ctx.lower_type_bound(bound, self_ty.clone(), false).for_each(|pred| { - if let Some(pred) = generic_predicate_to_inline_bound(db, &pred, &self_ty) { - bounds.push(pred); - } - }); - } - - if !ctx.unsized_types.contains(&self_ty) { - let sized_trait = - LangItem::Sized.resolve_trait(db, resolver.krate()).map(to_chalk_trait_id); - let sized_bound = sized_trait.into_iter().map(|sized_trait| { - let trait_bound = - rust_ir::TraitBound { trait_id: sized_trait, args_no_self: Default::default() }; - let inline_bound = rust_ir::InlineBound::TraitBound(trait_bound); - chalk_ir::Binders::empty(Interner, inline_bound) - }); - bounds.extend(sized_bound); - bounds.shrink_to_fit(); - } - - // FIXME: Re-enable where clauses on associated types when an upstream chalk bug is fixed. - // (rust-analyzer#9052) - // let where_clauses = convert_where_clauses(db, type_alias.into(), &bound_vars); - let bound_data = rust_ir::AssociatedTyDatumBound { bounds, where_clauses: vec![] }; - let datum = AssociatedTyDatum { - trait_id: to_chalk_trait_id(trait_), - id: to_assoc_type_id(type_alias), - name: type_alias, - binders: make_binders(db, &generic_params, bound_data), - }; - Arc::new(datum) -} - -pub(crate) fn trait_datum_query( - db: &dyn HirDatabase, - krate: Crate, - trait_id: TraitId, -) -> Arc { - debug!("trait_datum {:?}", trait_id); - let trait_ = from_chalk_trait_id(trait_id); - let trait_data = db.trait_signature(trait_); - debug!("trait {:?} = {:?}", trait_id, trait_data.name); - let generic_params = generics(db, trait_.into()); - let bound_vars = generic_params.bound_vars_subst(db, DebruijnIndex::INNERMOST); - let flags = rust_ir::TraitFlags { - auto: trait_data.flags.contains(TraitFlags::AUTO), - upstream: trait_.lookup(db).container.krate() != krate, - non_enumerable: true, - coinductive: false, // only relevant for Chalk testing - // FIXME: set these flags correctly - marker: false, - fundamental: trait_data.flags.contains(TraitFlags::FUNDAMENTAL), - }; - let where_clauses = convert_where_clauses(db, trait_.into(), &bound_vars); - let associated_ty_ids = - trait_.trait_items(db).associated_types().map(to_assoc_type_id).collect(); - let trait_datum_bound = rust_ir::TraitDatumBound { where_clauses }; - let well_known = db.lang_attr(trait_.into()).and_then(well_known_trait_from_lang_item); - let trait_datum = TraitDatum { - id: trait_id, - binders: make_binders(db, &generic_params, trait_datum_bound), - flags, - associated_ty_ids, - well_known, - }; - Arc::new(trait_datum) -} - -fn well_known_trait_from_lang_item(item: LangItem) -> Option { - Some(match item { - LangItem::Clone => WellKnownTrait::Clone, - LangItem::CoerceUnsized => WellKnownTrait::CoerceUnsized, - LangItem::Copy => WellKnownTrait::Copy, - LangItem::DiscriminantKind => WellKnownTrait::DiscriminantKind, - LangItem::DispatchFromDyn => WellKnownTrait::DispatchFromDyn, - LangItem::Drop => WellKnownTrait::Drop, - LangItem::Fn => WellKnownTrait::Fn, - LangItem::FnMut => WellKnownTrait::FnMut, - LangItem::FnOnce => WellKnownTrait::FnOnce, - LangItem::AsyncFn => WellKnownTrait::AsyncFn, - LangItem::AsyncFnMut => WellKnownTrait::AsyncFnMut, - LangItem::AsyncFnOnce => WellKnownTrait::AsyncFnOnce, - LangItem::Coroutine => WellKnownTrait::Coroutine, - LangItem::Sized => WellKnownTrait::Sized, - LangItem::Unpin => WellKnownTrait::Unpin, - LangItem::Unsize => WellKnownTrait::Unsize, - LangItem::Tuple => WellKnownTrait::Tuple, - LangItem::PointeeTrait => WellKnownTrait::Pointee, - LangItem::FnPtrTrait => WellKnownTrait::FnPtr, - LangItem::Future => WellKnownTrait::Future, - _ => return None, - }) -} - -pub(crate) fn adt_datum_query( - db: &dyn HirDatabase, - krate: Crate, - chalk_ir::AdtId(adt_id): AdtId, -) -> Arc { - debug!("adt_datum {:?}", adt_id); - let generic_params = generics(db, adt_id.into()); - let bound_vars_subst = generic_params.bound_vars_subst(db, DebruijnIndex::INNERMOST); - let where_clauses = convert_where_clauses(db, adt_id.into(), &bound_vars_subst); - - let (fundamental, phantom_data) = match adt_id { - hir_def::AdtId::StructId(s) => { - let flags = db.struct_signature(s).flags; - (flags.contains(StructFlags::FUNDAMENTAL), flags.contains(StructFlags::IS_PHANTOM_DATA)) - } - // FIXME set fundamental flags correctly - hir_def::AdtId::UnionId(_) => (false, false), - hir_def::AdtId::EnumId(_) => (false, false), - }; - let flags = rust_ir::AdtFlags { - upstream: adt_id.module(db).krate() != krate, - fundamental, - phantom_data, - }; - - // this slows down rust-analyzer by quite a bit unfortunately, so enabling this is currently not worth it - let _variant_id_to_fields = |id: VariantId| { - let variant_data = &id.fields(db); - let fields = if variant_data.fields().is_empty() { - vec![] - } else { - let field_types = db.field_types(id); - variant_data - .fields() - .iter() - .map(|(idx, _)| field_types[idx].clone().substitute(Interner, &bound_vars_subst)) - .filter(|it| !it.contains_unknown()) - .collect() - }; - rust_ir::AdtVariantDatum { fields } - }; - let variant_id_to_fields = |_: VariantId| rust_ir::AdtVariantDatum { fields: vec![] }; - - let (kind, variants) = match adt_id { - hir_def::AdtId::StructId(id) => { - (rust_ir::AdtKind::Struct, vec![variant_id_to_fields(id.into())]) - } - hir_def::AdtId::EnumId(id) => { - let variants = id - .enum_variants(db) - .variants - .iter() - .map(|&(variant_id, _, _)| variant_id_to_fields(variant_id.into())) - .collect(); - (rust_ir::AdtKind::Enum, variants) - } - hir_def::AdtId::UnionId(id) => { - (rust_ir::AdtKind::Union, vec![variant_id_to_fields(id.into())]) - } - }; - - let struct_datum_bound = rust_ir::AdtDatumBound { variants, where_clauses }; - let struct_datum = AdtDatum { - kind, - id: chalk_ir::AdtId(adt_id), - binders: make_binders(db, &generic_params, struct_datum_bound), - flags, - }; - Arc::new(struct_datum) -} - -pub(crate) fn impl_datum_query( - db: &dyn HirDatabase, - krate: Crate, - impl_id: ImplId, -) -> Arc { - let _p = tracing::info_span!("impl_datum_query").entered(); - debug!("impl_datum {:?}", impl_id); - let impl_: hir_def::ImplId = from_chalk(db, impl_id); - impl_def_datum(db, krate, impl_) -} - -fn impl_def_datum(db: &dyn HirDatabase, krate: Crate, impl_id: hir_def::ImplId) -> Arc { - let trait_ref = db - .impl_trait(impl_id) - // ImplIds for impls where the trait ref can't be resolved should never reach Chalk - .expect("invalid impl passed to Chalk") - .into_value_and_skipped_binders() - .0; - let impl_data = db.impl_signature(impl_id); - - let generic_params = generics(db, impl_id.into()); - let bound_vars = generic_params.bound_vars_subst(db, DebruijnIndex::INNERMOST); - let trait_ = trait_ref.hir_trait_id(); - let impl_type = if impl_id.lookup(db).container.krate() == krate { - rust_ir::ImplType::Local - } else { - rust_ir::ImplType::External - }; - let where_clauses = convert_where_clauses(db, impl_id.into(), &bound_vars); - let negative = impl_data.flags.contains(ImplFlags::NEGATIVE); - let polarity = if negative { rust_ir::Polarity::Negative } else { rust_ir::Polarity::Positive }; - - let impl_datum_bound = rust_ir::ImplDatumBound { trait_ref, where_clauses }; - let trait_data = trait_.trait_items(db); - let associated_ty_value_ids = impl_id - .impl_items(db) - .items - .iter() - .filter_map(|(_, item)| match item { - AssocItemId::TypeAliasId(type_alias) => Some(*type_alias), - _ => None, - }) - .filter(|&type_alias| { - // don't include associated types that don't exist in the trait - let name = &db.type_alias_signature(type_alias).name; - trait_data.associated_type_by_name(name).is_some() - }) - .map(|type_alias| TypeAliasAsValue(type_alias).to_chalk(db)) - .collect(); - debug!("impl_datum: {:?}", impl_datum_bound); - let impl_datum = ImplDatum { - binders: make_binders(db, &generic_params, impl_datum_bound), - impl_type, - polarity, - associated_ty_value_ids, - }; - Arc::new(impl_datum) -} - -pub(crate) fn associated_ty_value_query( - db: &dyn HirDatabase, - krate: Crate, - id: AssociatedTyValueId, -) -> Arc { - let type_alias: TypeAliasAsValue = from_chalk(db, id); - type_alias_associated_ty_value(db, krate, type_alias.0) -} - -fn type_alias_associated_ty_value( - db: &dyn HirDatabase, - _krate: Crate, - type_alias: TypeAliasId, -) -> Arc { - let type_alias_data = db.type_alias_signature(type_alias); - let impl_id = match type_alias.lookup(db).container { - ItemContainerId::ImplId(it) => it, - _ => panic!("assoc ty value should be in impl"), - }; - - let trait_ref = db - .impl_trait(impl_id) - .expect("assoc ty value should not exist") - .into_value_and_skipped_binders() - .0; // we don't return any assoc ty values if the impl'd trait can't be resolved - - let assoc_ty = trait_ref - .hir_trait_id() - .trait_items(db) - .associated_type_by_name(&type_alias_data.name) - .expect("assoc ty value should not exist"); // validated when building the impl data as well - let (ty, binders) = db.ty(type_alias.into()).into_value_and_skipped_binders(); - let value_bound = rust_ir::AssociatedTyValueBound { ty }; - let value = rust_ir::AssociatedTyValue { - impl_id: impl_id.to_chalk(db), - associated_ty_id: to_assoc_type_id(assoc_ty), - value: chalk_ir::Binders::new(binders, value_bound), - }; - Arc::new(value) -} - -pub(crate) fn fn_def_datum_query( - db: &dyn HirDatabase, - callable_def: CallableDefId, -) -> Arc { - let generic_def = GenericDefId::from_callable(db, callable_def); - let generic_params = generics(db, generic_def); - let (sig, binders) = db.callable_item_signature(callable_def).into_value_and_skipped_binders(); - let bound_vars = generic_params.bound_vars_subst(db, DebruijnIndex::INNERMOST); - let where_clauses = convert_where_clauses(db, generic_def, &bound_vars); - let bound = rust_ir::FnDefDatumBound { - // Note: Chalk doesn't actually use this information yet as far as I am aware, but we provide it anyway - inputs_and_output: chalk_ir::Binders::empty( - Interner, - rust_ir::FnDefInputsAndOutputDatum { - argument_types: sig.params().to_vec(), - return_type: sig.ret().clone(), - } - .shifted_in(Interner), - ), - where_clauses, - }; - let datum = FnDefDatum { - id: callable_def.to_chalk(db), - sig: chalk_ir::FnSig { - abi: sig.abi, - safety: chalk_ir::Safety::Safe, - variadic: sig.is_varargs, - }, - binders: chalk_ir::Binders::new(binders, bound), - }; - Arc::new(datum) -} - pub(crate) fn fn_def_variance_query( db: &dyn HirDatabase, callable_def: CallableDefId, @@ -431,59 +66,3 @@ pub(super) fn convert_where_clauses( .map(|pred| pred.substitute(Interner, substs)) .collect() } - -pub(super) fn generic_predicate_to_inline_bound( - db: &dyn HirDatabase, - pred: &QuantifiedWhereClause, - self_ty: &Ty, -) -> Option>> { - // An InlineBound is like a GenericPredicate, except the self type is left out. - // We don't have a special type for this, but Chalk does. - let self_ty_shifted_in = self_ty.clone().shifted_in_from(Interner, DebruijnIndex::ONE); - let (pred, binders) = pred.as_ref().into_value_and_skipped_binders(); - match pred { - WhereClause::Implemented(trait_ref) => { - if trait_ref.self_type_parameter(Interner) != self_ty_shifted_in { - // we can only convert predicates back to type bounds if they - // have the expected self type - return None; - } - let args_no_self = trait_ref.substitution.as_slice(Interner)[1..] - .iter() - .cloned() - .casted(Interner) - .collect(); - let trait_bound = rust_ir::TraitBound { trait_id: trait_ref.trait_id, args_no_self }; - Some(chalk_ir::Binders::new(binders, rust_ir::InlineBound::TraitBound(trait_bound))) - } - WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection_ty), ty }) => { - let generics = generics(db, from_assoc_type_id(projection_ty.associated_ty_id).into()); - let parent_len = generics.parent_generics().map_or(0, |g| g.len_self()); - let (trait_args, assoc_args) = - projection_ty.substitution.as_slice(Interner).split_at(parent_len); - let (self_ty, args_no_self) = - trait_args.split_first().expect("projection without trait self type"); - if self_ty.assert_ty_ref(Interner) != &self_ty_shifted_in { - return None; - } - - let args_no_self = args_no_self.iter().cloned().casted(Interner).collect(); - let parameters = assoc_args.to_vec(); - - let alias_eq_bound = rust_ir::AliasEqBound { - value: ty.clone(), - trait_bound: rust_ir::TraitBound { - trait_id: to_chalk_trait_id(projection_ty.trait_(db)), - args_no_self, - }, - associated_ty_id: projection_ty.associated_ty_id, - parameters, - }; - Some(chalk_ir::Binders::new( - binders, - rust_ir::InlineBound::AliasEqBound(alias_eq_bound), - )) - } - _ => None, - } -} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 9affd3b48c58..97754f47233b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -1,8 +1,6 @@ //! The home of `HirDatabase`, which is the Salsa database containing all the //! type inference-related queries. -use std::sync; - use base_db::Crate; use hir_def::{ AdtId, BlockId, CallableDefId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, @@ -240,26 +238,6 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { #[salsa::interned] fn intern_coroutine(&self, id: InternedCoroutine) -> InternedCoroutineId; - #[salsa::invoke(chalk_db::associated_ty_data_query)] - fn associated_ty_data(&self, id: TypeAliasId) -> sync::Arc; - - #[salsa::invoke(chalk_db::trait_datum_query)] - fn trait_datum( - &self, - krate: Crate, - trait_id: chalk_db::TraitId, - ) -> sync::Arc; - - #[salsa::invoke(chalk_db::adt_datum_query)] - fn adt_datum(&self, krate: Crate, struct_id: chalk_db::AdtId) -> sync::Arc; - - #[salsa::invoke(chalk_db::impl_datum_query)] - fn impl_datum(&self, krate: Crate, impl_id: chalk_db::ImplId) - -> sync::Arc; - - #[salsa::invoke(chalk_db::fn_def_datum_query)] - fn fn_def_datum(&self, fn_def_id: CallableDefId) -> sync::Arc; - #[salsa::invoke(chalk_db::fn_def_variance_query)] fn fn_def_variance(&self, fn_def_id: CallableDefId) -> chalk_db::Variances; @@ -274,13 +252,6 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { )] fn variances_of(&self, def: GenericDefId) -> Option>; - #[salsa::invoke(chalk_db::associated_ty_value_query)] - fn associated_ty_value( - &self, - krate: Crate, - id: chalk_db::AssociatedTyValueId, - ) -> sync::Arc; - #[salsa::invoke(crate::traits::normalize_projection_query)] #[salsa::transparent] fn normalize_projection( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs index b26bd2b8fa9c..403ea05a4f53 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs @@ -5,7 +5,6 @@ use std::fmt; use base_db::Crate; -use chalk_solve::rust_ir::AdtKind; use either::Either; use hir_def::{ AdtId, AssocItemId, DefWithBodyId, HasModule, ItemContainerId, Lookup, @@ -300,11 +299,7 @@ impl ExprValidator { value_or_partial.is_none_or(|v| !matches!(v, ValueNs::StaticId(_))) } Expr::Field { expr, .. } => match self.infer.type_of_expr[*expr].kind(Interner) { - TyKind::Adt(adt, ..) - if db.adt_datum(self.owner.krate(db), *adt).kind == AdtKind::Union => - { - false - } + TyKind::Adt(adt, ..) if matches!(adt.0, AdtId::UnionId(_)) => false, _ => self.is_known_valid_scrutinee(*expr, db), }, Expr::Index { base, .. } => self.is_known_valid_scrutinee(*base, db), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs index f0a4167f8e25..09b983a580d9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs @@ -1,12 +1,12 @@ //! Type cast logic. Basically coercion + additional casts. use chalk_ir::{Mutability, Scalar, TyVariableKind, UintTy}; -use hir_def::{AdtId, hir::ExprId}; +use hir_def::{AdtId, hir::ExprId, signatures::TraitFlags}; use stdx::never; use crate::{ Adjustment, Binders, DynTy, InferenceDiagnostic, Interner, PlaceholderIndex, - QuantifiedWhereClauses, Ty, TyExt, TyKind, TypeFlags, WhereClause, + QuantifiedWhereClauses, Ty, TyExt, TyKind, TypeFlags, WhereClause, from_chalk_trait_id, infer::{coerce::CoerceNever, unify::InferenceTable}, }; @@ -290,10 +290,12 @@ impl CastCheck { return Ok(()); } let src_principal = - table.db.trait_datum(table.trait_env.krate, src_principal); + table.db.trait_signature(from_chalk_trait_id(src_principal)); let dst_principal = - table.db.trait_datum(table.trait_env.krate, dst_principal); - if src_principal.is_auto_trait() && dst_principal.is_auto_trait() { + table.db.trait_signature(from_chalk_trait_id(dst_principal)); + if src_principal.flags.contains(TraitFlags::AUTO) + && dst_principal.flags.contains(TraitFlags::AUTO) + { Ok(()) } else { Err(CastError::DifferingKinds) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index d8fc20e8741c..38ac2e117077 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -9,7 +9,6 @@ use chalk_ir::{ visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, }; use either::Either; -use hir_def::Lookup; use hir_def::{ DefWithBodyId, FieldId, HasModule, TupleFieldId, TupleId, VariantId, expr_store::path::Path, @@ -22,6 +21,7 @@ use hir_def::{ resolver::ValueNs, type_ref::TypeRefId, }; +use hir_def::{ItemContainerId, Lookup, TraitId}; use hir_expand::name::Name; use intern::sym; use rustc_hash::{FxHashMap, FxHashSet}; @@ -30,16 +30,16 @@ use stdx::{format_to, never}; use syntax::utils::is_raw_identifier; use crate::{ - Adjust, Adjustment, AliasEq, AliasTy, Binders, BindingMode, ChalkTraitId, ClosureId, DynTy, - DynTyExt, FnAbi, FnPointer, FnSig, Interner, OpaqueTy, ProjectionTy, ProjectionTyExt, - Substitution, Ty, TyBuilder, TyExt, WhereClause, + Adjust, Adjustment, AliasEq, AliasTy, Binders, BindingMode, ClosureId, DynTy, DynTyExt, FnAbi, + FnPointer, FnSig, Interner, OpaqueTy, ProjectionTy, ProjectionTyExt, Substitution, Ty, + TyBuilder, TyExt, WhereClause, db::{HirDatabase, InternedClosure, InternedCoroutine}, error_lifetime, from_assoc_type_id, from_chalk_trait_id, from_placeholder_idx, generics::Generics, infer::{BreakableKind, CoerceMany, Diverges, coerce::CoerceNever}, make_binders, mir::{BorrowKind, MirSpan, MutBorrowKind, ProjectionElem}, - to_assoc_type_id, to_chalk_trait_id, + to_assoc_type_id, traits::FnTrait, utils::{self, elaborate_clause_supertraits}, }; @@ -321,10 +321,8 @@ impl InferenceContext<'_> { fn deduce_sig_from_dyn_ty(&self, dyn_ty: &DynTy) -> Option { // Search for a predicate like `<$self as FnX>::Output == Ret` - let fn_traits: SmallVec<[ChalkTraitId; 3]> = - utils::fn_traits(self.db, self.owner.module(self.db).krate()) - .map(to_chalk_trait_id) - .collect(); + let fn_traits: SmallVec<[TraitId; 3]> = + utils::fn_traits(self.db, self.owner.module(self.db).krate()).collect(); let self_ty = self.result.standard_types.unknown.clone(); let bounds = dyn_ty.bounds.clone().substitute(Interner, &[self_ty.cast(Interner)]); @@ -333,9 +331,13 @@ impl InferenceContext<'_> { if let WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection), ty }) = bound.skip_binders() { - let assoc_data = - self.db.associated_ty_data(from_assoc_type_id(projection.associated_ty_id)); - if !fn_traits.contains(&assoc_data.trait_id) { + let trait_ = + match from_assoc_type_id(projection.associated_ty_id).lookup(self.db).container + { + ItemContainerId::TraitId(t) => t, + _ => panic!("associated type not in trait"), + }; + if !fn_traits.contains(&trait_) { return None; } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs index 9d3d2044c43e..448fbdf67365 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs @@ -3,8 +3,6 @@ //! Chalk (in both directions); plus some helper functions for more specialized //! conversions. -use chalk_solve::rust_ir; - use hir_def::{LifetimeParamId, TraitId, TypeAliasId, TypeOrConstParamId}; use salsa::{ Id, @@ -54,23 +52,6 @@ impl ToChalk for CallableDefId { } } -pub(crate) struct TypeAliasAsValue(pub(crate) TypeAliasId); - -impl ToChalk for TypeAliasAsValue { - type Chalk = chalk_db::AssociatedTyValueId; - - fn to_chalk(self, _db: &dyn HirDatabase) -> chalk_db::AssociatedTyValueId { - rust_ir::AssociatedTyValueId(self.0.as_id()) - } - - fn from_chalk( - _db: &dyn HirDatabase, - assoc_ty_value_id: chalk_db::AssociatedTyValueId, - ) -> TypeAliasAsValue { - TypeAliasAsValue(TypeAliasId::from_id(assoc_ty_value_id.0)) - } -} - impl From for crate::db::InternedOpaqueTyId { fn from(id: OpaqueTyId) -> Self { FromId::from_id(id.0) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index ff7d116dcce3..f03f542e5bce 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -66,7 +66,7 @@ use hir_def::{ }, per_ns::PerNs, resolver::{HasResolver, Resolver}, - signatures::{ImplFlags, StaticFlags, TraitFlags, VariantFields}, + signatures::{ImplFlags, StaticFlags, StructFlags, TraitFlags, VariantFields}, src::HasSource as _, visibility::visibility_from_ast, }; @@ -4945,42 +4945,79 @@ impl<'db> Type<'db> { } pub fn contains_reference(&self, db: &'db dyn HirDatabase) -> bool { - return go(db, self.env.krate, &self.ty); + return go(db, &self.ty); - fn go(db: &dyn HirDatabase, krate: base_db::Crate, ty: &Ty) -> bool { + fn is_phantom_data(db: &dyn HirDatabase, adt_id: AdtId) -> bool { + match adt_id { + hir_def::AdtId::StructId(s) => { + let flags = db.struct_signature(s).flags; + flags.contains(StructFlags::IS_PHANTOM_DATA) + } + hir_def::AdtId::UnionId(_) => false, + hir_def::AdtId::EnumId(_) => false, + } + } + + fn go(db: &dyn HirDatabase, ty: &Ty) -> bool { match ty.kind(Interner) { // Reference itself TyKind::Ref(_, _, _) => true, // For non-phantom_data adts we check variants/fields as well as generic parameters - TyKind::Adt(adt_id, substitution) - if !db.adt_datum(krate, *adt_id).flags.phantom_data => - { - let adt_datum = &db.adt_datum(krate, *adt_id); - let adt_datum_bound = - adt_datum.binders.clone().substitute(Interner, substitution); - adt_datum_bound - .variants + TyKind::Adt(adt_id, substitution) if !is_phantom_data(db, adt_id.0) => { + let _variant_id_to_fields = |id: VariantId| { + let variant_data = &id.fields(db); + if variant_data.fields().is_empty() { + vec![] + } else { + let field_types = db.field_types(id); + variant_data + .fields() + .iter() + .map(|(idx, _)| { + field_types[idx].clone().substitute(Interner, substitution) + }) + .filter(|it| !it.contains_unknown()) + .collect() + } + }; + let variant_id_to_fields = |_: VariantId| vec![]; + + let variants = match adt_id.0 { + hir_def::AdtId::StructId(id) => { + vec![variant_id_to_fields(id.into())] + } + hir_def::AdtId::EnumId(id) => id + .enum_variants(db) + .variants + .iter() + .map(|&(variant_id, _, _)| variant_id_to_fields(variant_id.into())) + .collect(), + hir_def::AdtId::UnionId(id) => { + vec![variant_id_to_fields(id.into())] + } + }; + + variants .into_iter() - .flat_map(|variant| variant.fields.into_iter()) - .any(|ty| go(db, krate, &ty)) + .flat_map(|variant| variant.into_iter()) + .any(|ty| go(db, &ty)) || substitution .iter(Interner) .filter_map(|x| x.ty(Interner)) - .any(|ty| go(db, krate, ty)) + .any(|ty| go(db, ty)) } // And for `PhantomData`, we check `T`. TyKind::Adt(_, substitution) | TyKind::Tuple(_, substitution) | TyKind::OpaqueType(_, substitution) | TyKind::AssociatedType(_, substitution) - | TyKind::FnDef(_, substitution) => substitution - .iter(Interner) - .filter_map(|x| x.ty(Interner)) - .any(|ty| go(db, krate, ty)), + | TyKind::FnDef(_, substitution) => { + substitution.iter(Interner).filter_map(|x| x.ty(Interner)).any(|ty| go(db, ty)) + } // For `[T]` or `*T` we check `T` - TyKind::Array(ty, _) | TyKind::Slice(ty) | TyKind::Raw(_, ty) => go(db, krate, ty), + TyKind::Array(ty, _) | TyKind::Slice(ty) | TyKind::Raw(_, ty) => go(db, ty), // Consider everything else as not reference _ => false, From 5c893461719c3d4da1201a0080b60f1ca95c5c88 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Fri, 15 Aug 2025 04:36:53 +0000 Subject: [PATCH 086/682] Add new_empty_tuple --- src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs | 4 ++-- src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index 9deaa4ac5a6b..c60ace85be12 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -25,7 +25,7 @@ use rustc_apfloat::{ ieee::{Half as f16, Quad as f128}, }; use rustc_hash::{FxHashMap, FxHashSet}; -use rustc_type_ir::inherent::{AdtDef, IntoKind, SliceLike, Ty as _}; +use rustc_type_ir::inherent::{AdtDef, IntoKind, SliceLike}; use span::FileId; use stdx::never; use syntax::{SyntaxNodePtr, TextRange}; @@ -1815,7 +1815,7 @@ impl<'db> Evaluator<'db> { let i = self.const_eval_discriminant(it)?; return Ok(( 16, - self.layout(crate::next_solver::Ty::new_tup(interner, &[]))?, + self.layout(crate::next_solver::Ty::new_empty_tuple(interner))?, Some((0, 16, i)), )); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index 0c0fe686b77d..5ffae981a609 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -9,6 +9,7 @@ use rustc_type_ir::{ WithCachedTypeInfo, inherent::{ AdtDef, BoundVarLike, GenericArgs as _, IntoKind, ParamLike, PlaceholderLike, SliceLike, + Ty as _, }, relate::Relate, solve::SizedTraitKind, @@ -107,6 +108,10 @@ impl<'db> Ty<'db> { Ty::new_infer(interner, InferTy::FreshFloatTy(n)) } + pub fn new_empty_tuple(interner: DbInterner<'db>) -> Self { + Ty::new_tup(interner, &[]) + } + /// Returns the `Size` for primitive types (bool, uint, int, char, float). pub fn primitive_size(self, interner: DbInterner<'db>) -> Size { match self.kind() { From 0e2b63cd875e0496b3a2da237282e02333b632ee Mon Sep 17 00:00:00 2001 From: jackh726 Date: Fri, 15 Aug 2025 04:38:50 +0000 Subject: [PATCH 087/682] Update fixme --- src/tools/rust-analyzer/crates/hir-ty/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 2f8eb627462b..7fdfb205721e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -292,7 +292,7 @@ impl<'db> MemoryMap<'db> { } } -// FIXME(next-solver): +// FIXME(next-solver): add a lifetime to this /// A concrete constant value #[derive(Debug, Clone, PartialEq, Eq)] pub enum ConstScalar { From 3e41e85b2761bbe21169e7dd72cb959fb63a31fa Mon Sep 17 00:00:00 2001 From: jackh726 Date: Fri, 15 Aug 2025 04:41:23 +0000 Subject: [PATCH 088/682] Add fixme to associated_ty_item_bounds --- src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs index 2777869bd48e..99411e4ab138 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -1594,6 +1594,7 @@ fn fn_sig_for_enum_variant_constructor<'db>( })) } +// FIXME(next-solver): should merge this with `explicit_item_bounds` in some way pub(crate) fn associated_ty_item_bounds<'db>( db: &'db dyn HirDatabase, type_alias: TypeAliasId, From 058a398f9fd11783aa62691bfb4b029c1a3d313c Mon Sep 17 00:00:00 2001 From: jackh726 Date: Fri, 15 Aug 2025 04:50:21 +0000 Subject: [PATCH 089/682] Add FIXME in named_associated_type_shorthand_candidates --- src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs index 99411e4ab138..7a2bd37bce6d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -1782,6 +1782,9 @@ fn named_associated_type_shorthand_candidates<'db, R>( TypeNs::SelfType(impl_id) => { let trait_ref = db.impl_trait_ns(impl_id)?; + // FIXME(next-solver): same method in `lower` checks for impl or not + // Is that needed here? + // we're _in_ the impl -- the binders get added back later. Correct, // but it would be nice to make this more explicit search(trait_ref.skip_binder()) From 3486a2c3e735d51750e1127607eefcf0496a4d00 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Fri, 15 Aug 2025 04:52:12 +0000 Subject: [PATCH 090/682] Remove fixme comment --- src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs index 7a2bd37bce6d..ce953fdcb829 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -1793,7 +1793,6 @@ fn named_associated_type_shorthand_candidates<'db, R>( // Handle `Self::Type` referring to own associated type in trait definitions // This *must* be done first to avoid cycles with // `generic_predicates_for_param`, but not sure that it's sufficient, - // see FIXME in `search`. if let GenericDefId::TraitId(trait_id) = param_id.parent() { let trait_name = &db.trait_signature(trait_id).name; tracing::debug!(?trait_name); From b9d225b6d8dff5eaccf8bac4ab969ab1fe98ea6d Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 16 Aug 2025 16:26:30 +0200 Subject: [PATCH 091/682] Auto-attach database in `Analysis` calls --- .../hir-ty/src/next_solver/generic_arg.rs | 2 +- .../crates/ide-assists/src/lib.rs | 9 +-- .../crates/ide-assists/src/tests.rs | 17 ++++- .../crates/ide-completion/src/lib.rs | 5 +- .../crates/ide-completion/src/tests.rs | 6 +- .../crates/ide-diagnostics/src/lib.rs | 12 ++- .../crates/ide-diagnostics/src/tests.rs | 75 ++++++++++++------- .../crates/ide/src/goto_definition.rs | 4 +- .../rust-analyzer/crates/ide/src/hover.rs | 43 +++++------ .../crates/ide/src/hover/render.rs | 5 +- src/tools/rust-analyzer/crates/ide/src/lib.rs | 57 +++++++++----- .../crates/parser/src/lexed_str.rs | 3 +- 12 files changed, 137 insertions(+), 101 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs index 834f4e3765ee..76186e374608 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs @@ -46,7 +46,7 @@ impl<'db> GenericArg<'db> { pub fn expect_ty(self) -> Ty<'db> { match self.kind() { GenericArgKind::Type(ty) => ty, - _ => panic!("Expected ty, got {:?}", self), + _ => panic!("Expected ty, got {self:?}"), } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs index 5008f97447b7..4682c0473238 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs @@ -67,7 +67,7 @@ mod tests; pub mod utils; use hir::Semantics; -use ide_db::{EditionedFileId, RootDatabase, base_db::salsa}; +use ide_db::{EditionedFileId, RootDatabase}; use syntax::{Edition, TextRange}; pub(crate) use crate::assist_context::{AssistContext, Assists}; @@ -93,11 +93,8 @@ pub fn assists( .unwrap_or_else(|| EditionedFileId::new(db, range.file_id, Edition::CURRENT)); let ctx = AssistContext::new(sema, config, hir::FileRange { file_id, range: range.range }); let mut acc = Assists::new(&ctx, resolve); - // the handlers may invoke trait solving related things which accesses salsa structs outside queries - salsa::attach(db, || { - handlers::all().iter().for_each(|handler| { - handler(&mut acc, &ctx); - }); + handlers::all().iter().for_each(|handler| { + handler(&mut acc, &ctx); }); acc.finish() } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs index f4daabfe915d..c7c322a15e54 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs @@ -1,7 +1,7 @@ mod generated; use expect_test::expect; -use hir::{Semantics, setup_tracing}; +use hir::{Semantics, db::HirDatabase, setup_tracing}; use ide_db::{ EditionedFileId, FileRange, RootDatabase, SnippetCap, assists::ExprFillDefaultMode, @@ -16,7 +16,7 @@ use test_utils::{assert_eq_text, extract_offset}; use crate::{ Assist, AssistConfig, AssistContext, AssistKind, AssistResolveStrategy, Assists, SingleResolve, - assists, handlers::Handler, + handlers::Handler, }; pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig { @@ -103,6 +103,18 @@ pub(crate) const TEST_CONFIG_IMPORT_ONE: AssistConfig = AssistConfig { prefer_self_ty: false, }; +fn assists( + db: &RootDatabase, + config: &AssistConfig, + resolve: AssistResolveStrategy, + range: ide_db::FileRange, +) -> Vec { + salsa::attach(db, || { + HirDatabase::zalsa_register_downcaster(db); + crate::assists(db, config, resolve, range) + }) +} + pub(crate) fn with_single_file(text: &str) -> (RootDatabase, EditionedFileId) { RootDatabase::with_single_file(text) } @@ -320,6 +332,7 @@ fn check_with_config( }; let mut acc = Assists::new(&ctx, resolve); salsa::attach(&db, || { + HirDatabase::zalsa_register_downcaster(&db); handler(&mut acc, &ctx); }); let mut res = acc.finish(); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs index 1a4c97e70b4a..a70a1138d2f4 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs @@ -10,7 +10,6 @@ mod snippet; #[cfg(test)] mod tests; -use base_db::salsa; use ide_db::{ FilePosition, FxHashSet, RootDatabase, imports::insert_use::{self, ImportScope}, @@ -229,7 +228,7 @@ pub fn completions( { let acc = &mut completions; - salsa::attach(db, || match analysis { + match analysis { CompletionAnalysis::Name(name_ctx) => completions::complete_name(acc, ctx, name_ctx), CompletionAnalysis::NameRef(name_ref_ctx) => { completions::complete_name_ref(acc, ctx, name_ref_ctx) @@ -257,7 +256,7 @@ pub fn completions( ); } CompletionAnalysis::UnexpandedAttrTT { .. } | CompletionAnalysis::String { .. } => (), - }) + } } Some(completions.into()) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs index 4b3b271ca20e..809a26bf5de4 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs @@ -24,7 +24,7 @@ mod type_pos; mod use_tree; mod visibility; -use base_db::SourceDatabase; +use base_db::{SourceDatabase, salsa}; use expect_test::Expect; use hir::{PrefixKind, setup_tracing}; use ide_db::{ @@ -243,7 +243,7 @@ pub(crate) fn check_edit_with_config( let ra_fixture_after = trim_indent(ra_fixture_after); let (db, position) = position(ra_fixture_before); let completions: Vec = - crate::completions(&db, &config, position, None).unwrap(); + salsa::attach(&db, || crate::completions(&db, &config, position, None).unwrap()); let (completion,) = completions .iter() .filter(|it| it.lookup() == what) @@ -306,7 +306,7 @@ pub(crate) fn get_all_items( trigger_character: Option, ) -> Vec { let (db, position) = position(code); - let res = crate::completions(&db, &config, position, trigger_character) + let res = salsa::attach(&db, || crate::completions(&db, &config, position, trigger_character)) .map_or_else(Vec::default, Into::into); // validate res.iter().for_each(|it| { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index a4eb3d47d708..a1db92641f5e 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -92,7 +92,7 @@ use hir::{ use ide_db::{ EditionedFileId, FileId, FileRange, FxHashMap, FxHashSet, RootDatabase, Severity, SnippetCap, assists::{Assist, AssistId, AssistResolveStrategy, ExprFillDefaultMode}, - base_db::{ReleaseChannel, RootQueryDb as _, salsa}, + base_db::{ReleaseChannel, RootQueryDb as _}, generated::lints::{CLIPPY_LINT_GROUPS, DEFAULT_LINT_GROUPS, DEFAULT_LINTS, Lint, LintGroup}, imports::insert_use::InsertUseConfig, label::Label, @@ -537,12 +537,10 @@ pub fn full_diagnostics( resolve: &AssistResolveStrategy, file_id: FileId, ) -> Vec { - salsa::attach(db, || { - let mut res = syntax_diagnostics(db, config, file_id); - let sema = semantic_diagnostics(db, config, resolve, file_id); - res.extend(sema); - res - }) + let mut res = syntax_diagnostics(db, config, file_id); + let sema = semantic_diagnostics(db, config, resolve, file_id); + res.extend(sema); + res } /// Returns whether to keep this diagnostic (or remove it). diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs index c3cc5a08b56b..1839ab1c58c1 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs @@ -6,7 +6,7 @@ use hir::setup_tracing; use ide_db::{ LineIndexDatabase, RootDatabase, assists::{AssistResolveStrategy, ExprFillDefaultMode}, - base_db::SourceDatabase, + base_db::{SourceDatabase, salsa}, }; use itertools::Itertools; use stdx::trim_indent; @@ -74,14 +74,16 @@ fn check_nth_fix_with_config( let after = trim_indent(ra_fixture_after); let (db, file_position) = RootDatabase::with_position(ra_fixture_before); - let diagnostic = super::full_diagnostics( - &db, - &config, - &AssistResolveStrategy::All, - file_position.file_id.file_id(&db), - ) - .pop() - .expect("no diagnostics"); + let diagnostic = salsa::attach(&db, || { + super::full_diagnostics( + &db, + &config, + &AssistResolveStrategy::All, + file_position.file_id.file_id(&db), + ) + .pop() + .expect("no diagnostics") + }); let fix = &diagnostic .fixes .unwrap_or_else(|| panic!("{:?} diagnostic misses fixes", diagnostic.code))[nth]; @@ -127,12 +129,14 @@ pub(crate) fn check_has_fix( let (db, file_position) = RootDatabase::with_position(ra_fixture_before); let mut conf = DiagnosticsConfig::test_sample(); conf.expr_fill_default = ExprFillDefaultMode::Default; - let fix = super::full_diagnostics( - &db, - &conf, - &AssistResolveStrategy::All, - file_position.file_id.file_id(&db), - ) + let fix = salsa::attach(&db, || { + super::full_diagnostics( + &db, + &conf, + &AssistResolveStrategy::All, + file_position.file_id.file_id(&db), + ) + }) .into_iter() .find(|d| { d.fixes @@ -166,12 +170,14 @@ pub(crate) fn check_has_fix( /// Checks that there's a diagnostic *without* fix at `$0`. pub(crate) fn check_no_fix(#[rust_analyzer::rust_fixture] ra_fixture: &str) { let (db, file_position) = RootDatabase::with_position(ra_fixture); - let diagnostic = super::full_diagnostics( - &db, - &DiagnosticsConfig::test_sample(), - &AssistResolveStrategy::All, - file_position.file_id.file_id(&db), - ) + let diagnostic = salsa::attach(&db, || { + super::full_diagnostics( + &db, + &DiagnosticsConfig::test_sample(), + &AssistResolveStrategy::All, + file_position.file_id.file_id(&db), + ) + }) .pop() .unwrap(); assert!(diagnostic.fixes.is_none(), "got a fix when none was expected: {diagnostic:?}"); @@ -206,7 +212,13 @@ pub(crate) fn check_diagnostics_with_config( .iter() .copied() .flat_map(|file_id| { - super::full_diagnostics(&db, &config, &AssistResolveStrategy::All, file_id.file_id(&db)) + salsa::attach(&db, || { + super::full_diagnostics( + &db, + &config, + &AssistResolveStrategy::All, + file_id.file_id(&db), + ) .into_iter() .map(|d| { let mut annotation = String::new(); @@ -224,6 +236,7 @@ pub(crate) fn check_diagnostics_with_config( annotation.push_str(&d.message); (d.range, annotation) }) + }) }) .map(|(diagnostic, annotation)| (diagnostic.file_id, (diagnostic.range, annotation))) .into_group_map(); @@ -275,15 +288,19 @@ fn test_disabled_diagnostics() { let (db, file_id) = RootDatabase::with_single_file(r#"mod foo;"#); let file_id = file_id.file_id(&db); - let diagnostics = super::full_diagnostics(&db, &config, &AssistResolveStrategy::All, file_id); + let diagnostics = salsa::attach(&db, || { + super::full_diagnostics(&db, &config, &AssistResolveStrategy::All, file_id) + }); assert!(diagnostics.is_empty()); - let diagnostics = super::full_diagnostics( - &db, - &DiagnosticsConfig::test_sample(), - &AssistResolveStrategy::All, - file_id, - ); + let diagnostics = salsa::attach(&db, || { + super::full_diagnostics( + &db, + &DiagnosticsConfig::test_sample(), + &AssistResolveStrategy::All, + file_id, + ) + }); assert!(!diagnostics.is_empty()); } diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index 633feec622cd..f768d4b68f46 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -10,7 +10,7 @@ use hir::{ }; use ide_db::{ RootDatabase, SymbolKind, - base_db::{AnchoredPath, SourceDatabase, salsa}, + base_db::{AnchoredPath, SourceDatabase}, defs::{Definition, IdentClass}, famous_defs::FamousDefs, helpers::pick_best_token, @@ -108,7 +108,7 @@ pub(crate) fn goto_definition( } Some( - salsa::attach(sema.db, || IdentClass::classify_node(sema, &parent))? + IdentClass::classify_node(sema, &parent)? .definitions() .into_iter() .flat_map(|(def, _)| { diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index b0ef83e0501b..44c98a43f694 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -12,7 +12,6 @@ use hir::{ }; use ide_db::{ FileRange, FxIndexSet, Ranker, RootDatabase, - base_db::salsa, defs::{Definition, IdentClass, NameRefClass, OperatorClass}, famous_defs::FamousDefs, helpers::pick_best_token, @@ -137,20 +136,18 @@ pub(crate) fn hover( let edition = sema.attach_first_edition(file_id).map(|it| it.edition(db)).unwrap_or(Edition::CURRENT); let display_target = sema.first_crate(file_id)?.to_display_target(db); - let mut res = salsa::attach(sema.db, || { - if range.is_empty() { - hover_offset( - sema, - FilePosition { file_id, offset: range.start() }, - file, - config, - edition, - display_target, - ) - } else { - hover_ranged(sema, frange, file, config, edition, display_target) - } - })?; + let mut res = if range.is_empty() { + hover_offset( + sema, + FilePosition { file_id, offset: range.start() }, + file, + config, + edition, + display_target, + ) + } else { + hover_ranged(sema, frange, file, config, edition, display_target) + }?; if let HoverDocFormat::PlainText = config.format { res.info.markup = remove_markdown(res.info.markup.as_str()).into(); @@ -293,7 +290,7 @@ fn hover_offset( .into_iter() .unique_by(|&((def, _), _, _, _)| def) .map(|((def, subst), macro_arm, hovered_definition, node)| { - salsa::attach(sema.db, || hover_for_definition( + hover_for_definition( sema, file_id, def, @@ -304,7 +301,7 @@ fn hover_offset( config, edition, display_target, - )) + ) }) .collect::>(), ) @@ -583,13 +580,11 @@ fn goto_type_action_for_def( }); } - salsa::attach(db, || { - if let Ok(generic_def) = GenericDef::try_from(def) { - generic_def.type_or_const_params(db).into_iter().for_each(|it| { - walk_and_push_ty(db, &it.ty(db), &mut push_new_def); - }); - } - }); + if let Ok(generic_def) = GenericDef::try_from(def) { + generic_def.type_or_const_params(db).into_iter().for_each(|it| { + walk_and_push_ty(db, &it.ty(db), &mut push_new_def); + }); + } let ty = match def { Definition::Local(it) => Some(it.ty(db)), diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index 1f9d10c92b1d..290ee80984ed 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -10,7 +10,6 @@ use hir::{ }; use ide_db::{ RootDatabase, - base_db::salsa, defs::Definition, documentation::{DocsRangeMap, HasDocs}, famous_defs::FamousDefs, @@ -45,7 +44,7 @@ pub(super) fn type_info_of( Either::Left(expr) => sema.type_of_expr(expr)?, Either::Right(pat) => sema.type_of_pat(pat)?, }; - salsa::attach(sema.db, || type_info(sema, _config, ty_info, edition, display_target)) + type_info(sema, _config, ty_info, edition, display_target) } pub(super) fn closure_expr( @@ -912,7 +911,7 @@ pub(super) fn literal( }; let ty = ty.display(sema.db, display_target); - let mut s = salsa::attach(sema.db, || format!("```rust\n{ty}\n```\n___\n\n")); + let mut s = format!("```rust\n{ty}\n```\n___\n\n"); match value { Ok(value) => { let backtick_len = value.chars().filter(|c| *c == '`').count(); diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index e491c9214b43..874e04702e24 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -62,7 +62,7 @@ use std::panic::{AssertUnwindSafe, UnwindSafe}; use cfg::CfgOptions; use fetch_crates::CrateInfo; -use hir::{ChangeWithProcMacros, EditionedFileId, crate_def_map, sym}; +use hir::{ChangeWithProcMacros, EditionedFileId, crate_def_map, db::HirDatabase, sym}; use ide_db::{ FxHashMap, FxIndexSet, LineIndexDatabase, base_db::{ @@ -478,10 +478,12 @@ impl Analysis { /// Fuzzy searches for a symbol. pub fn symbol_search(&self, query: Query, limit: usize) -> Cancellable> { - self.with_db(|db| { - symbol_index::world_symbols(db, query) - .into_iter() // xx: should we make this a par iter? - .filter_map(|s| s.try_to_nav(db)) + // `world_symbols` currently clones the database to run stuff in parallel, which will make any query panic + // if we were to attach it here. + Cancelled::catch(|| { + symbol_index::world_symbols(&self.db, query) + .into_iter() + .filter_map(|s| s.try_to_nav(&self.db)) .take(limit) .map(UpmappingResult::call_site) .collect::>() @@ -660,15 +662,6 @@ impl Analysis { }) } - /// Computes syntax highlighting for the given file - pub fn highlight( - &self, - highlight_config: HighlightConfig, - file_id: FileId, - ) -> Cancellable> { - self.with_db(|db| syntax_highlighting::highlight(db, highlight_config, file_id, None)) - } - /// Computes all ranges to highlight for a given item in a file. pub fn highlight_related( &self, @@ -682,20 +675,42 @@ impl Analysis { }) } + /// Computes syntax highlighting for the given file + pub fn highlight( + &self, + highlight_config: HighlightConfig, + file_id: FileId, + ) -> Cancellable> { + // highlighting may construct a new database for "speculative" execution, so we can't currently attach the database + // highlighting instead sets up the attach hook where neceesary for the trait solver + Cancelled::catch(|| { + syntax_highlighting::highlight(&self.db, highlight_config, file_id, None) + }) + } + /// Computes syntax highlighting for the given file range. pub fn highlight_range( &self, highlight_config: HighlightConfig, frange: FileRange, ) -> Cancellable> { - self.with_db(|db| { - syntax_highlighting::highlight(db, highlight_config, frange.file_id, Some(frange.range)) + // highlighting may construct a new database for "speculative" execution, so we can't currently attach the database + // highlighting instead sets up the attach hook where neceesary for the trait solver + Cancelled::catch(|| { + syntax_highlighting::highlight( + &self.db, + highlight_config, + frange.file_id, + Some(frange.range), + ) }) } /// Computes syntax highlighting for the given file. pub fn highlight_as_html(&self, file_id: FileId, rainbow: bool) -> Cancellable { - self.with_db(|db| syntax_highlighting::highlight_as_html(db, file_id, rainbow)) + // highlighting may construct a new database for "speculative" execution, so we can't currently attach the database + // highlighting instead sets up the attach hook where neceesary for the trait solver + Cancelled::catch(|| syntax_highlighting::highlight_as_html(&self.db, file_id, rainbow)) } /// Computes completions at the given position. @@ -873,8 +888,12 @@ impl Analysis { where F: FnOnce(&RootDatabase) -> T + std::panic::UnwindSafe, { - let snap = self.db.clone(); - Cancelled::catch(|| f(&snap)) + salsa::attach(&self.db, || { + // the trait solver code may invoke `as_view` outside of queries, + // so technically we might run into a panic in salsa if the downcaster has not yet been registered. + HirDatabase::zalsa_register_downcaster(&self.db); + Cancelled::catch(|| f(&self.db)) + }) } } diff --git a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs index dcf397142cac..edc3f406a67e 100644 --- a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs +++ b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs @@ -289,8 +289,7 @@ impl<'a> Converter<'a> { let error_msg = if has_unterminated { format!( - "unknown literal prefix `{}` (note: check for unterminated string literal)", - token_text + "unknown literal prefix `{token_text}` (note: check for unterminated string literal)" ) } else { "unknown literal prefix".to_owned() From ea44f12f72888528ddb5b26492c3b72ad4b7bcb4 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Fri, 15 Aug 2025 21:47:39 +0800 Subject: [PATCH 092/682] Rename and move tuple index suffix regression test To make it more obvious what it's testing. This is its own commit to make git blame easier. --- src/tools/tidy/src/issues.txt | 1 - tests/ui/parser/{issues/issue-59418.rs => tuple-index-suffix.rs} | 0 .../{issues/issue-59418.stderr => tuple-index-suffix.stderr} | 0 3 files changed, 1 deletion(-) rename tests/ui/parser/{issues/issue-59418.rs => tuple-index-suffix.rs} (100%) rename tests/ui/parser/{issues/issue-59418.stderr => tuple-index-suffix.stderr} (100%) diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt index ee06707415f5..849dcb9e88fb 100644 --- a/src/tools/tidy/src/issues.txt +++ b/src/tools/tidy/src/issues.txt @@ -2021,7 +2021,6 @@ ui/parser/issues/issue-5806.rs ui/parser/issues/issue-58094-missing-right-square-bracket.rs ui/parser/issues/issue-58856-1.rs ui/parser/issues/issue-58856-2.rs -ui/parser/issues/issue-59418.rs ui/parser/issues/issue-60075.rs ui/parser/issues/issue-61858.rs ui/parser/issues/issue-62524.rs diff --git a/tests/ui/parser/issues/issue-59418.rs b/tests/ui/parser/tuple-index-suffix.rs similarity index 100% rename from tests/ui/parser/issues/issue-59418.rs rename to tests/ui/parser/tuple-index-suffix.rs diff --git a/tests/ui/parser/issues/issue-59418.stderr b/tests/ui/parser/tuple-index-suffix.stderr similarity index 100% rename from tests/ui/parser/issues/issue-59418.stderr rename to tests/ui/parser/tuple-index-suffix.stderr From d7f7443a9540761c7cc2540c4b7b07b31b6120e5 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Fri, 15 Aug 2025 22:11:10 +0800 Subject: [PATCH 093/682] Expand `tuple-index-suffix` test coverage Actually, the accidentally accepted invalid suffixes also include tuple struct indexing positions, struct numeral field name positions. So before changing anything, first expand test coverage, so we can observe the effect of bumping the non-lint pseudo-FCW warning into a hard error. --- tests/ui/parser/tuple-index-suffix.rs | 75 ++++++++-- tests/ui/parser/tuple-index-suffix.stderr | 166 ++++++++++++++++++++-- 2 files changed, 222 insertions(+), 19 deletions(-) diff --git a/tests/ui/parser/tuple-index-suffix.rs b/tests/ui/parser/tuple-index-suffix.rs index 0fa191d4a7ef..31c5bc25063d 100644 --- a/tests/ui/parser/tuple-index-suffix.rs +++ b/tests/ui/parser/tuple-index-suffix.rs @@ -1,18 +1,77 @@ +//! See #60210. +//! +//! Check that we hard error on invalid suffixes in tuple indexing subexpressions and struct numeral +//! field names, modulo carve-outs for `{i,u}{32,usize}` at warning level to mitigate ecosystem +//! impact. + struct X(i32,i32,i32); fn main() { - let a = X(1, 2, 3); - let b = a.1suffix; + let tup_struct = X(1, 2, 3); + let invalid_tup_struct_suffix = tup_struct.0suffix; //~^ ERROR suffixes on a tuple index are invalid - println!("{}", b); - let c = (1, 2, 3); - let d = c.1suffix; + let carve_out_tup_struct_suffix = tup_struct.0i32; + //~^ WARN suffixes on a tuple index are invalid + + let tup = (1, 2, 3); + let invalid_tup_suffix = tup.0suffix; //~^ ERROR suffixes on a tuple index are invalid - println!("{}", d); - let s = X { 0suffix: 0, 1: 1, 2: 2 }; + let carve_out_tup_suffix = tup.0u32; + //~^ WARN suffixes on a tuple index are invalid + + numeral_struct_field_name_suffix_invalid(); + numeral_struct_field_name_suffix_carve_out(); +} + +// Very limited carve outs as a ecosystem impact mitigation implemented in #60186. *Only* +// `{i,u}{32,usize}` suffixes are temporarily accepted. +fn carve_outs() { + // Ok, only pseudo-FCW warnings. + + let carve_out_i32 = (42,).0i32; //~ WARN suffixes on a tuple index are invalid + let carve_out_i32 = (42,).0u32; //~ WARN suffixes on a tuple index are invalid + let carve_out_isize = (42,).0isize; //~ WARN suffixes on a tuple index are invalid + let carve_out_usize = (42,).0usize; //~ WARN suffixes on a tuple index are invalid + + // Not part of the carve outs! + let error_i8 = (42,).0i8; //~ ERROR suffixes on a tuple index are invalid + let error_u8 = (42,).0u8; //~ ERROR suffixes on a tuple index are invalid + let error_i16 = (42,).0i16; //~ ERROR suffixes on a tuple index are invalid + let error_u16 = (42,).0u16; //~ ERROR suffixes on a tuple index are invalid + let error_i64 = (42,).0i64; //~ ERROR suffixes on a tuple index are invalid + let error_u64 = (42,).0u64; //~ ERROR suffixes on a tuple index are invalid + let error_i128 = (42,).0i128; //~ ERROR suffixes on a tuple index are invalid + let error_u128 = (42,).0u128; //~ ERROR suffixes on a tuple index are invalid +} + +fn numeral_struct_field_name_suffix_invalid() { + let invalid_struct_name = X { 0suffix: 0, 1: 1, 2: 2 }; //~^ ERROR suffixes on a tuple index are invalid - match s { + match invalid_struct_name { X { 0suffix: _, .. } => {} //~^ ERROR suffixes on a tuple index are invalid } } + +fn numeral_struct_field_name_suffix_carve_out() { + let carve_out_struct_name = X { 0u32: 0, 1: 1, 2: 2 }; + //~^ WARN suffixes on a tuple index are invalid + match carve_out_struct_name { + X { 0u32: _, .. } => {} + //~^ WARN suffixes on a tuple index are invalid + } +} + +// Unfortunately, it turns out `std::mem::offset_of!` uses the same expect suffix code path. +fn offset_of_suffix() { + #[repr(C)] + pub struct Struct(u8, T); + + // Carve outs + assert_eq!(std::mem::offset_of!(Struct, 0usize), 0); + //~^ WARN suffixes on a tuple index are invalid + + // Not part of carve outs + assert_eq!(std::mem::offset_of!(Struct, 0u8), 0); + //~^ ERROR suffixes on a tuple index are invalid +} diff --git a/tests/ui/parser/tuple-index-suffix.stderr b/tests/ui/parser/tuple-index-suffix.stderr index 347051e9f921..3a499dd6a8df 100644 --- a/tests/ui/parser/tuple-index-suffix.stderr +++ b/tests/ui/parser/tuple-index-suffix.stderr @@ -1,26 +1,170 @@ error: suffixes on a tuple index are invalid - --> $DIR/issue-59418.rs:5:15 + --> $DIR/tuple-index-suffix.rs:11:48 | -LL | let b = a.1suffix; - | ^^^^^^^ invalid suffix `suffix` +LL | let invalid_tup_struct_suffix = tup_struct.0suffix; + | ^^^^^^^ invalid suffix `suffix` + +warning: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:13:50 + | +LL | let carve_out_tup_struct_suffix = tup_struct.0i32; + | ^^^^ invalid suffix `i32` + | + = help: `i32` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases + = help: on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access + = help: see issue #60210 for more information error: suffixes on a tuple index are invalid - --> $DIR/issue-59418.rs:9:15 + --> $DIR/tuple-index-suffix.rs:17:34 | -LL | let d = c.1suffix; - | ^^^^^^^ invalid suffix `suffix` +LL | let invalid_tup_suffix = tup.0suffix; + | ^^^^^^^ invalid suffix `suffix` + +warning: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:19:36 + | +LL | let carve_out_tup_suffix = tup.0u32; + | ^^^^ invalid suffix `u32` + | + = help: `u32` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases + = help: on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access + = help: see issue #60210 for more information + +warning: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:31:31 + | +LL | let carve_out_i32 = (42,).0i32; + | ^^^^ invalid suffix `i32` + | + = help: `i32` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases + = help: on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access + = help: see issue #60210 for more information + +warning: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:32:31 + | +LL | let carve_out_i32 = (42,).0u32; + | ^^^^ invalid suffix `u32` + | + = help: `u32` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases + = help: on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access + = help: see issue #60210 for more information + +warning: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:33:33 + | +LL | let carve_out_isize = (42,).0isize; + | ^^^^^^ invalid suffix `isize` + | + = help: `isize` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases + = help: on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access + = help: see issue #60210 for more information + +warning: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:34:33 + | +LL | let carve_out_usize = (42,).0usize; + | ^^^^^^ invalid suffix `usize` + | + = help: `usize` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases + = help: on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access + = help: see issue #60210 for more information error: suffixes on a tuple index are invalid - --> $DIR/issue-59418.rs:12:17 + --> $DIR/tuple-index-suffix.rs:37:26 | -LL | let s = X { 0suffix: 0, 1: 1, 2: 2 }; - | ^^^^^^^ invalid suffix `suffix` +LL | let error_i8 = (42,).0i8; + | ^^^ invalid suffix `i8` error: suffixes on a tuple index are invalid - --> $DIR/issue-59418.rs:15:13 + --> $DIR/tuple-index-suffix.rs:38:26 + | +LL | let error_u8 = (42,).0u8; + | ^^^ invalid suffix `u8` + +error: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:39:27 + | +LL | let error_i16 = (42,).0i16; + | ^^^^ invalid suffix `i16` + +error: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:40:27 + | +LL | let error_u16 = (42,).0u16; + | ^^^^ invalid suffix `u16` + +error: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:41:27 + | +LL | let error_i64 = (42,).0i64; + | ^^^^ invalid suffix `i64` + +error: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:42:27 + | +LL | let error_u64 = (42,).0u64; + | ^^^^ invalid suffix `u64` + +error: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:43:28 + | +LL | let error_i128 = (42,).0i128; + | ^^^^^ invalid suffix `i128` + +error: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:44:28 + | +LL | let error_u128 = (42,).0u128; + | ^^^^^ invalid suffix `u128` + +error: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:48:35 + | +LL | let invalid_struct_name = X { 0suffix: 0, 1: 1, 2: 2 }; + | ^^^^^^^ invalid suffix `suffix` + +error: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:51:13 | LL | X { 0suffix: _, .. } => {} | ^^^^^^^ invalid suffix `suffix` -error: aborting due to 4 previous errors +warning: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:57:37 + | +LL | let carve_out_struct_name = X { 0u32: 0, 1: 1, 2: 2 }; + | ^^^^ invalid suffix `u32` + | + = help: `u32` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases + = help: on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access + = help: see issue #60210 for more information + +warning: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:60:13 + | +LL | X { 0u32: _, .. } => {} + | ^^^^ invalid suffix `u32` + | + = help: `u32` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases + = help: on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access + = help: see issue #60210 for more information + +warning: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:71:50 + | +LL | assert_eq!(std::mem::offset_of!(Struct, 0usize), 0); + | ^^^^^^ invalid suffix `usize` + | + = help: `usize` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases + = help: on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access + = help: see issue #60210 for more information + +error: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:75:50 + | +LL | assert_eq!(std::mem::offset_of!(Struct, 0u8), 0); + | ^^^ invalid suffix `u8` + +error: aborting due to 13 previous errors; 9 warnings emitted From eb3441b25a7acbce8cec0571b3f8ec87cca8c349 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Fri, 15 Aug 2025 22:41:43 +0800 Subject: [PATCH 094/682] Add test coverage for proc-macro invalid tup index suffixes --- .../tuple-index-suffix-proc-macro-aux.rs | 33 ++++++++++++++++++ .../parser/tuple-index-suffix-proc-macro.rs | 31 +++++++++++++++++ .../tuple-index-suffix-proc-macro.stderr | 34 +++++++++++++++++++ 3 files changed, 98 insertions(+) create mode 100644 tests/ui/parser/auxiliary/tuple-index-suffix-proc-macro-aux.rs create mode 100644 tests/ui/parser/tuple-index-suffix-proc-macro.rs create mode 100644 tests/ui/parser/tuple-index-suffix-proc-macro.stderr diff --git a/tests/ui/parser/auxiliary/tuple-index-suffix-proc-macro-aux.rs b/tests/ui/parser/auxiliary/tuple-index-suffix-proc-macro-aux.rs new file mode 100644 index 000000000000..fa40ed948a62 --- /dev/null +++ b/tests/ui/parser/auxiliary/tuple-index-suffix-proc-macro-aux.rs @@ -0,0 +1,33 @@ +#![feature(proc_macro_quote, proc_macro_span)] + +extern crate proc_macro; + +use proc_macro::{Ident, Literal, Span, TokenStream, TokenTree, quote}; + +#[proc_macro] +pub fn bad_tup_indexing(input: TokenStream) -> TokenStream { + let tt = input.into_iter().next().unwrap(); + let TokenTree::Literal(indexing_expr) = tt else { + unreachable!(); + }; + quote! { (42,).$indexing_expr } +} + +// Expects {IDENT, COMMA, LITERAL} +#[proc_macro] +pub fn bad_tup_struct_indexing(input: TokenStream) -> TokenStream { + let mut input = input.into_iter(); + + let id_tt = input.next().unwrap(); + let _comma = input.next().unwrap(); + let tt = input.next().unwrap(); + + let TokenTree::Ident(ident) = id_tt else { + unreachable!("id"); + }; + let TokenTree::Literal(indexing_expr) = tt else { + unreachable!("lit"); + }; + + quote! { $ident.$indexing_expr } +} diff --git a/tests/ui/parser/tuple-index-suffix-proc-macro.rs b/tests/ui/parser/tuple-index-suffix-proc-macro.rs new file mode 100644 index 000000000000..feca6f9cdfb0 --- /dev/null +++ b/tests/ui/parser/tuple-index-suffix-proc-macro.rs @@ -0,0 +1,31 @@ +//! See #59418. +//! +//! Like `tuple-index-suffix.rs`, but exercises the proc-macro interaction. + +//@ proc-macro: tuple-index-suffix-proc-macro-aux.rs + +extern crate tuple_index_suffix_proc_macro_aux; +use tuple_index_suffix_proc_macro_aux as aux; + +fn main() { + struct TupStruct(i32); + let tup_struct = TupStruct(42); + + // #60186 carve outs `{i,u}{32,usize}` as non-lint pseudo-FCW warnings. + + aux::bad_tup_indexing!(0usize); + //~^ WARN suffixes on a tuple index are invalid + aux::bad_tup_struct_indexing!(tup_struct, 0isize); + //~^ WARN suffixes on a tuple index are invalid + + // Not part of the #60186 carve outs. + + aux::bad_tup_indexing!(0u8); + //~^ ERROR suffixes on a tuple index are invalid + aux::bad_tup_struct_indexing!(tup_struct, 0u64); + //~^ ERROR suffixes on a tuple index are invalid + + // NOTE: didn't bother with trying to figure out how to generate `struct P { 0u32: u32 }` using + // *only* `proc_macro` without help with `syn`/`quote`, looks like you can't with just + // `proc_macro::quote`? +} diff --git a/tests/ui/parser/tuple-index-suffix-proc-macro.stderr b/tests/ui/parser/tuple-index-suffix-proc-macro.stderr new file mode 100644 index 000000000000..c8bc3a4576b4 --- /dev/null +++ b/tests/ui/parser/tuple-index-suffix-proc-macro.stderr @@ -0,0 +1,34 @@ +warning: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix-proc-macro.rs:16:28 + | +LL | aux::bad_tup_indexing!(0usize); + | ^^^^^^ invalid suffix `usize` + | + = help: `usize` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases + = help: on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access + = help: see issue #60210 for more information + +warning: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix-proc-macro.rs:18:47 + | +LL | aux::bad_tup_struct_indexing!(tup_struct, 0isize); + | ^^^^^^ invalid suffix `isize` + | + = help: `isize` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases + = help: on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access + = help: see issue #60210 for more information + +error: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix-proc-macro.rs:23:28 + | +LL | aux::bad_tup_indexing!(0u8); + | ^^^ invalid suffix `u8` + +error: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix-proc-macro.rs:25:47 + | +LL | aux::bad_tup_struct_indexing!(tup_struct, 0u64); + | ^^^^ invalid suffix `u64` + +error: aborting due to 2 previous errors; 2 warnings emitted + From ddd99930f34b79f209c61cc25706a1dac1173762 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Fri, 15 Aug 2025 23:39:33 +0800 Subject: [PATCH 095/682] Turn invalid index suffixes into hard errors --- compiler/rustc_parse/messages.ftl | 3 - compiler/rustc_parse/src/errors.rs | 4 - compiler/rustc_parse/src/parser/expr.rs | 26 +--- compiler/rustc_parse/src/parser/mod.rs | 5 +- .../tuple-index-suffix-proc-macro-aux.rs | 8 +- .../parser/tuple-index-suffix-proc-macro.rs | 7 +- .../tuple-index-suffix-proc-macro.stderr | 22 +-- tests/ui/parser/tuple-index-suffix.rs | 44 +++--- tests/ui/parser/tuple-index-suffix.stderr | 140 +++++++----------- 9 files changed, 100 insertions(+), 159 deletions(-) diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 9e0075c21b9e..0d1a3c783890 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -473,9 +473,6 @@ parse_invalid_label = parse_invalid_literal_suffix_on_tuple_index = suffixes on a tuple index are invalid .label = invalid suffix `{$suffix}` - .tuple_exception_line_1 = `{$suffix}` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases - .tuple_exception_line_2 = on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access - .tuple_exception_line_3 = see issue #60210 for more information parse_invalid_logical_operator = `{$incorrect}` is not a logical operator .note = unlike in e.g., Python and PHP, `&&` and `||` are used for logical operators diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index a105dd1909e9..8a10e7d05ebe 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1016,10 +1016,6 @@ pub(crate) struct InvalidLiteralSuffixOnTupleIndex { #[label] pub span: Span, pub suffix: Symbol, - #[help(parse_tuple_exception_line_1)] - #[help(parse_tuple_exception_line_2)] - #[help(parse_tuple_exception_line_3)] - pub exception: bool, } #[derive(Diagnostic)] diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index d0604f763171..7d33f3de15cc 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1163,7 +1163,10 @@ impl<'a> Parser<'a> { suffix, }) => { if let Some(suffix) = suffix { - self.expect_no_tuple_index_suffix(current.span, suffix); + self.dcx().emit_err(errors::InvalidLiteralSuffixOnTupleIndex { + span: current.span, + suffix, + }); } match self.break_up_float(symbol, current.span) { // 1e2 @@ -1239,7 +1242,8 @@ impl<'a> Parser<'a> { suffix: Option, ) -> Box { if let Some(suffix) = suffix { - self.expect_no_tuple_index_suffix(ident_span, suffix); + self.dcx() + .emit_err(errors::InvalidLiteralSuffixOnTupleIndex { span: ident_span, suffix }); } self.mk_expr(lo.to(ident_span), ExprKind::Field(base, Ident::new(field, ident_span))) } @@ -2225,24 +2229,6 @@ impl<'a> Parser<'a> { }) } - pub(super) fn expect_no_tuple_index_suffix(&self, span: Span, suffix: Symbol) { - if [sym::i32, sym::u32, sym::isize, sym::usize].contains(&suffix) { - // #59553: warn instead of reject out of hand to allow the fix to percolate - // through the ecosystem when people fix their macros - self.dcx().emit_warn(errors::InvalidLiteralSuffixOnTupleIndex { - span, - suffix, - exception: true, - }); - } else { - self.dcx().emit_err(errors::InvalidLiteralSuffixOnTupleIndex { - span, - suffix, - exception: false, - }); - } - } - /// Matches `'-' lit | lit` (cf. `ast_validation::AstValidator::check_expr_within_pat`). /// Keep this in sync with `Token::can_begin_literal_maybe_minus`. pub fn parse_literal_maybe_minus(&mut self) -> PResult<'a, Box> { diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 41ed1f95a010..db72faf4ec7a 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -1333,7 +1333,10 @@ impl<'a> Parser<'a> { if let token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) = self.token.kind { if let Some(suffix) = suffix { - self.expect_no_tuple_index_suffix(self.token.span, suffix); + self.dcx().emit_err(errors::InvalidLiteralSuffixOnTupleIndex { + span: self.token.span, + suffix, + }); } self.bump(); Ok(Ident::new(symbol, self.prev_token.span)) diff --git a/tests/ui/parser/auxiliary/tuple-index-suffix-proc-macro-aux.rs b/tests/ui/parser/auxiliary/tuple-index-suffix-proc-macro-aux.rs index fa40ed948a62..a5084b55aac7 100644 --- a/tests/ui/parser/auxiliary/tuple-index-suffix-proc-macro-aux.rs +++ b/tests/ui/parser/auxiliary/tuple-index-suffix-proc-macro-aux.rs @@ -18,14 +18,14 @@ pub fn bad_tup_indexing(input: TokenStream) -> TokenStream { pub fn bad_tup_struct_indexing(input: TokenStream) -> TokenStream { let mut input = input.into_iter(); - let id_tt = input.next().unwrap(); + let ident = input.next().unwrap(); let _comma = input.next().unwrap(); - let tt = input.next().unwrap(); + let lit = input.next().unwrap(); - let TokenTree::Ident(ident) = id_tt else { + let TokenTree::Ident(ident) = ident else { unreachable!("id"); }; - let TokenTree::Literal(indexing_expr) = tt else { + let TokenTree::Literal(indexing_expr) = lit else { unreachable!("lit"); }; diff --git a/tests/ui/parser/tuple-index-suffix-proc-macro.rs b/tests/ui/parser/tuple-index-suffix-proc-macro.rs index feca6f9cdfb0..557c67738d30 100644 --- a/tests/ui/parser/tuple-index-suffix-proc-macro.rs +++ b/tests/ui/parser/tuple-index-suffix-proc-macro.rs @@ -11,12 +11,13 @@ fn main() { struct TupStruct(i32); let tup_struct = TupStruct(42); - // #60186 carve outs `{i,u}{32,usize}` as non-lint pseudo-FCW warnings. + // Previously, #60186 had carve outs for `{i,u}{32,usize}` as non-lint pseudo-FCW warnings. Now, + // they all hard error. aux::bad_tup_indexing!(0usize); - //~^ WARN suffixes on a tuple index are invalid + //~^ ERROR suffixes on a tuple index are invalid aux::bad_tup_struct_indexing!(tup_struct, 0isize); - //~^ WARN suffixes on a tuple index are invalid + //~^ ERROR suffixes on a tuple index are invalid // Not part of the #60186 carve outs. diff --git a/tests/ui/parser/tuple-index-suffix-proc-macro.stderr b/tests/ui/parser/tuple-index-suffix-proc-macro.stderr index c8bc3a4576b4..47d179d35551 100644 --- a/tests/ui/parser/tuple-index-suffix-proc-macro.stderr +++ b/tests/ui/parser/tuple-index-suffix-proc-macro.stderr @@ -1,34 +1,26 @@ -warning: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix-proc-macro.rs:16:28 +error: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix-proc-macro.rs:17:28 | LL | aux::bad_tup_indexing!(0usize); | ^^^^^^ invalid suffix `usize` - | - = help: `usize` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases - = help: on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access - = help: see issue #60210 for more information -warning: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix-proc-macro.rs:18:47 +error: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix-proc-macro.rs:19:47 | LL | aux::bad_tup_struct_indexing!(tup_struct, 0isize); | ^^^^^^ invalid suffix `isize` - | - = help: `isize` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases - = help: on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access - = help: see issue #60210 for more information error: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix-proc-macro.rs:23:28 + --> $DIR/tuple-index-suffix-proc-macro.rs:24:28 | LL | aux::bad_tup_indexing!(0u8); | ^^^ invalid suffix `u8` error: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix-proc-macro.rs:25:47 + --> $DIR/tuple-index-suffix-proc-macro.rs:26:47 | LL | aux::bad_tup_struct_indexing!(tup_struct, 0u64); | ^^^^ invalid suffix `u64` -error: aborting due to 2 previous errors; 2 warnings emitted +error: aborting due to 4 previous errors diff --git a/tests/ui/parser/tuple-index-suffix.rs b/tests/ui/parser/tuple-index-suffix.rs index 31c5bc25063d..c47695000054 100644 --- a/tests/ui/parser/tuple-index-suffix.rs +++ b/tests/ui/parser/tuple-index-suffix.rs @@ -1,8 +1,10 @@ -//! See #60210. +//! Regression test for both the original regression in #59418 where invalid suffixes in indexing +//! positions were accidentally accepted, and also for the removal of the temporary carve out that +//! mitigated ecosystem impact following trying to reject #59418 (this was implemented as a FCW +//! tracked in #60210). //! //! Check that we hard error on invalid suffixes in tuple indexing subexpressions and struct numeral -//! field names, modulo carve-outs for `{i,u}{32,usize}` at warning level to mitigate ecosystem -//! impact. +//! field names. struct X(i32,i32,i32); @@ -10,28 +12,28 @@ fn main() { let tup_struct = X(1, 2, 3); let invalid_tup_struct_suffix = tup_struct.0suffix; //~^ ERROR suffixes on a tuple index are invalid - let carve_out_tup_struct_suffix = tup_struct.0i32; - //~^ WARN suffixes on a tuple index are invalid + let previous_carve_out_tup_struct_suffix = tup_struct.0i32; + //~^ ERROR suffixes on a tuple index are invalid let tup = (1, 2, 3); let invalid_tup_suffix = tup.0suffix; //~^ ERROR suffixes on a tuple index are invalid - let carve_out_tup_suffix = tup.0u32; - //~^ WARN suffixes on a tuple index are invalid + let previous_carve_out_tup_suffix = tup.0u32; + //~^ ERROR suffixes on a tuple index are invalid numeral_struct_field_name_suffix_invalid(); - numeral_struct_field_name_suffix_carve_out(); + numeral_struct_field_name_suffix_previous_carve_out(); } -// Very limited carve outs as a ecosystem impact mitigation implemented in #60186. *Only* -// `{i,u}{32,usize}` suffixes are temporarily accepted. -fn carve_outs() { - // Ok, only pseudo-FCW warnings. +// Previously, there were very limited carve outs as a ecosystem impact mitigation implemented in +// #60186. *Only* `{i,u}{32,usize}` suffixes were temporarily accepted. Now, they all hard error. +fn previous_carve_outs() { + // Previously temporarily accepted by a pseudo-FCW (#60210), now hard error. - let carve_out_i32 = (42,).0i32; //~ WARN suffixes on a tuple index are invalid - let carve_out_i32 = (42,).0u32; //~ WARN suffixes on a tuple index are invalid - let carve_out_isize = (42,).0isize; //~ WARN suffixes on a tuple index are invalid - let carve_out_usize = (42,).0usize; //~ WARN suffixes on a tuple index are invalid + let previous_carve_out_i32 = (42,).0i32; //~ ERROR suffixes on a tuple index are invalid + let previous_carve_out_i32 = (42,).0u32; //~ ERROR suffixes on a tuple index are invalid + let previous_carve_out_isize = (42,).0isize; //~ ERROR suffixes on a tuple index are invalid + let previous_carve_out_usize = (42,).0usize; //~ ERROR suffixes on a tuple index are invalid // Not part of the carve outs! let error_i8 = (42,).0i8; //~ ERROR suffixes on a tuple index are invalid @@ -53,12 +55,12 @@ fn numeral_struct_field_name_suffix_invalid() { } } -fn numeral_struct_field_name_suffix_carve_out() { +fn numeral_struct_field_name_suffix_previous_carve_out() { let carve_out_struct_name = X { 0u32: 0, 1: 1, 2: 2 }; - //~^ WARN suffixes on a tuple index are invalid + //~^ ERROR suffixes on a tuple index are invalid match carve_out_struct_name { X { 0u32: _, .. } => {} - //~^ WARN suffixes on a tuple index are invalid + //~^ ERROR suffixes on a tuple index are invalid } } @@ -67,9 +69,9 @@ fn offset_of_suffix() { #[repr(C)] pub struct Struct(u8, T); - // Carve outs + // Previous pseudo-FCW carve outs assert_eq!(std::mem::offset_of!(Struct, 0usize), 0); - //~^ WARN suffixes on a tuple index are invalid + //~^ ERROR suffixes on a tuple index are invalid // Not part of carve outs assert_eq!(std::mem::offset_of!(Struct, 0u8), 0); diff --git a/tests/ui/parser/tuple-index-suffix.stderr b/tests/ui/parser/tuple-index-suffix.stderr index 3a499dd6a8df..6d96c6d3cbf8 100644 --- a/tests/ui/parser/tuple-index-suffix.stderr +++ b/tests/ui/parser/tuple-index-suffix.stderr @@ -1,170 +1,134 @@ error: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:11:48 + --> $DIR/tuple-index-suffix.rs:13:48 | LL | let invalid_tup_struct_suffix = tup_struct.0suffix; | ^^^^^^^ invalid suffix `suffix` -warning: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:13:50 +error: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:15:59 | -LL | let carve_out_tup_struct_suffix = tup_struct.0i32; - | ^^^^ invalid suffix `i32` - | - = help: `i32` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases - = help: on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access - = help: see issue #60210 for more information +LL | let previous_carve_out_tup_struct_suffix = tup_struct.0i32; + | ^^^^ invalid suffix `i32` error: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:17:34 + --> $DIR/tuple-index-suffix.rs:19:34 | LL | let invalid_tup_suffix = tup.0suffix; | ^^^^^^^ invalid suffix `suffix` -warning: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:19:36 +error: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:21:45 | -LL | let carve_out_tup_suffix = tup.0u32; - | ^^^^ invalid suffix `u32` - | - = help: `u32` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases - = help: on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access - = help: see issue #60210 for more information - -warning: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:31:31 - | -LL | let carve_out_i32 = (42,).0i32; - | ^^^^ invalid suffix `i32` - | - = help: `i32` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases - = help: on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access - = help: see issue #60210 for more information - -warning: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:32:31 - | -LL | let carve_out_i32 = (42,).0u32; - | ^^^^ invalid suffix `u32` - | - = help: `u32` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases - = help: on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access - = help: see issue #60210 for more information - -warning: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:33:33 - | -LL | let carve_out_isize = (42,).0isize; - | ^^^^^^ invalid suffix `isize` - | - = help: `isize` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases - = help: on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access - = help: see issue #60210 for more information - -warning: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:34:33 - | -LL | let carve_out_usize = (42,).0usize; - | ^^^^^^ invalid suffix `usize` - | - = help: `usize` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases - = help: on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access - = help: see issue #60210 for more information +LL | let previous_carve_out_tup_suffix = tup.0u32; + | ^^^^ invalid suffix `u32` error: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:37:26 + --> $DIR/tuple-index-suffix.rs:33:40 + | +LL | let previous_carve_out_i32 = (42,).0i32; + | ^^^^ invalid suffix `i32` + +error: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:34:40 + | +LL | let previous_carve_out_i32 = (42,).0u32; + | ^^^^ invalid suffix `u32` + +error: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:35:42 + | +LL | let previous_carve_out_isize = (42,).0isize; + | ^^^^^^ invalid suffix `isize` + +error: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:36:42 + | +LL | let previous_carve_out_usize = (42,).0usize; + | ^^^^^^ invalid suffix `usize` + +error: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:39:26 | LL | let error_i8 = (42,).0i8; | ^^^ invalid suffix `i8` error: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:38:26 + --> $DIR/tuple-index-suffix.rs:40:26 | LL | let error_u8 = (42,).0u8; | ^^^ invalid suffix `u8` error: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:39:27 + --> $DIR/tuple-index-suffix.rs:41:27 | LL | let error_i16 = (42,).0i16; | ^^^^ invalid suffix `i16` error: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:40:27 + --> $DIR/tuple-index-suffix.rs:42:27 | LL | let error_u16 = (42,).0u16; | ^^^^ invalid suffix `u16` error: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:41:27 + --> $DIR/tuple-index-suffix.rs:43:27 | LL | let error_i64 = (42,).0i64; | ^^^^ invalid suffix `i64` error: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:42:27 + --> $DIR/tuple-index-suffix.rs:44:27 | LL | let error_u64 = (42,).0u64; | ^^^^ invalid suffix `u64` error: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:43:28 + --> $DIR/tuple-index-suffix.rs:45:28 | LL | let error_i128 = (42,).0i128; | ^^^^^ invalid suffix `i128` error: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:44:28 + --> $DIR/tuple-index-suffix.rs:46:28 | LL | let error_u128 = (42,).0u128; | ^^^^^ invalid suffix `u128` error: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:48:35 + --> $DIR/tuple-index-suffix.rs:50:35 | LL | let invalid_struct_name = X { 0suffix: 0, 1: 1, 2: 2 }; | ^^^^^^^ invalid suffix `suffix` error: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:51:13 + --> $DIR/tuple-index-suffix.rs:53:13 | LL | X { 0suffix: _, .. } => {} | ^^^^^^^ invalid suffix `suffix` -warning: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:57:37 +error: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:59:37 | LL | let carve_out_struct_name = X { 0u32: 0, 1: 1, 2: 2 }; | ^^^^ invalid suffix `u32` - | - = help: `u32` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases - = help: on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access - = help: see issue #60210 for more information -warning: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:60:13 +error: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:62:13 | LL | X { 0u32: _, .. } => {} | ^^^^ invalid suffix `u32` - | - = help: `u32` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases - = help: on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access - = help: see issue #60210 for more information -warning: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:71:50 +error: suffixes on a tuple index are invalid + --> $DIR/tuple-index-suffix.rs:73:50 | LL | assert_eq!(std::mem::offset_of!(Struct, 0usize), 0); | ^^^^^^ invalid suffix `usize` - | - = help: `usize` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases - = help: on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access - = help: see issue #60210 for more information error: suffixes on a tuple index are invalid - --> $DIR/tuple-index-suffix.rs:75:50 + --> $DIR/tuple-index-suffix.rs:77:50 | LL | assert_eq!(std::mem::offset_of!(Struct, 0u8), 0); | ^^^ invalid suffix `u8` -error: aborting due to 13 previous errors; 9 warnings emitted +error: aborting due to 22 previous errors From 8eaa4ad7a4cf371114f9e26a92b41cd3fc25e27e Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 13 Aug 2025 10:31:57 +0200 Subject: [PATCH 096/682] user facing code should use not use `PostAnalysis` --- .../ide-assists/src/handlers/generate_from_impl_for_enum.rs | 2 +- .../src/handlers/generate_single_field_struct_from.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs index cb83c67c9953..d88b0f34b796 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs @@ -87,7 +87,7 @@ fn existing_from_impl( let from_trait = FamousDefs(sema, krate).core_convert_From()?; let interner = DbInterner::new_with(db, Some(krate.base()), None); use hir::next_solver::infer::DbInternerInferExt; - let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); + let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); let variant = variant.instantiate_infer(&infcx); let enum_ = variant.parent_enum(sema.db); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs index deef4a9aac6d..943795d40d55 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs @@ -216,7 +216,7 @@ fn from_impl_exists( let from_trait = FamousDefs(sema, krate).core_convert_From()?; let interner = DbInterner::new_with(db, Some(krate.base()), None); use hir::next_solver::infer::DbInternerInferExt; - let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); + let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); let strukt = strukt.instantiate_infer(&infcx); let field_ty = strukt.fields(db).get(main_field_i)?.ty(db); From 3141739bad36c517cd2425d06dc35df327ee1b3a Mon Sep 17 00:00:00 2001 From: lumiscosity Date: Tue, 19 Aug 2025 11:41:58 +0200 Subject: [PATCH 097/682] Optimize icon Losslessly optimizes the icon with: ``` oxipng -o max -a -s oxipng -o max --zopfli -a -s ``` --- src/tools/rust-analyzer/editors/code/icon.png | Bin 15341 -> 9761 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/tools/rust-analyzer/editors/code/icon.png b/src/tools/rust-analyzer/editors/code/icon.png index 072090c6f4a1fbebc0a2e1ba20d991d5a75d513a..29a9679039010500e7d2b4db1e832f904618081b 100644 GIT binary patch literal 9761 zcmeAS@N?(olHy`uVBq!ia0y~yV2S`?4kiW$216G9ZUzR%)J$jRfXw7%28N1TQ!g4X zm=-8;;A8!^x97dfqPOMl`H@rXoxJVr&SMs7)mb?w-@To)>znQ6_dgg-Y@{wUeLUu@ zAmMdH%x7*w)V7+|l{cfMF7%a|EM2(hsPg0IXJ@-^eSBJ2ior%kA9@5vE_qRLWq^%#CzN&*KE4_ho^XHx{u9w^}o+l}oNp zT~nFl_)xIHm%pv#VXDGfZ}BC~jZ4@VOcKSMzq%%V*E-M_S*W!?n|ATj$d`5thEbp1N-< z_D?OpJV)00{Qtvz8}9#)f1Ccg=KS}c=e~1Gc=KtA?5W&$|9PbygJMhXJiA&edwY9h zV`EcOQ$s_=|HkI#+js1E@%{JL{cH>ZPAp7~>i+X=YJPlpcXzjW{=J;6tX1pRuV1-R zQ(IeGORMVp-SU1J%T4L$=k@gTTwd-kE-rrhx_;cAi0EkT`ETC6larI17WePtarsZR zCJX{j7Ogt(Z{5DVH!k1L&o3rsPL-*lp`oUx=JWIO^Yikii7r{Awl$NXLBQ#k%6YR? z)#=lxtKTi!eOK#>lCrX`oPS@-nmtY*R1D$avAMzi-0Q*<6cj ze}9YJQIMFB(7?W&fn(uWz1Uq}?p41}O-oBlPk%0j^(;iDEd2B(ba7cT~; zF1d6msM}BZ#AT10&sX*+?k)oPYxfDU`qU+lkN2Nm>@6GQ?d^T_>eax&z*8#w|9t8; zlLndX;_iO>nwPBA-o1N^-uTa4zILteY;FdQg`dkO{`mWE)v8q;9UQ4k{{5@VwP8?b zF|#OrHKi_9T3R~3?&ngWWqLKoj~(0ck)c80mV%ktwfEomC$XNNW4V~;+q2pEcK<^) z7zCW0H2965+GYRsKY3NR$RcQ_Z=Oc&mlqSwC>tIn(I8Xv$OkFEXc^sz50w9`&8B&MqCUn z7ubrvpY^`G;P2n>_qE$6OkZF$!*e!21INPNsHmtf+e}SNJYuIfpGa8t^l56Y9mpH2 zR;;-3Pj%7m^7moq6!)*SIRP<#@nm)Xyyaq>H*e+?1(PO>J%0`Z6%IT&O86Ej>TaWP-ldaV-svia$R#nlUm6 zIDLNl{Q3HI>t6l;@Zezc1LweYEnVHKZ43-f8T)3;nDM`SRsfrXruyoYD=$6+3(hvr z|M%S1$kWsF)X8+a-zzqhwK{&)VP$B*Ay?bVZZ`8was%r#`Ij#xPYX7! zii?T4!pF+c5+LW-X5X@C&mJ2~%Sg-n4-dCrzjp1*IhFhfDqYmXmXr{yO^Z{{DPdj($1YQ`}BqsXfYD zRKH5Ui|Pa*F*LBHH#QQzS->-!E&GDmc*g_{eQ#SCBeS% z++FzC?ZeYKdlje1u95;dj^}g3@AS{f;T+ahR-kO{l=0u9VzyJo`+K&sav-A_s z0SV2RI8ktq-IWyrr&?-rO+lUx(Ay>_FMs{e27$7&vaHpN42mi(ckkYfnj!8q|NL^W z(^tpyo-zwGcFuUqpa#-nU~F7mTpY~(+37{m&ZhOcx?l&mxx2H+ADSt7XvdBn4}6z{ z9J?^IJd2%)G1+;>tXWbv=fKLMj8Y2{6CJsOohyEQd6@vV;n0QMH9t3rv9(`#{WVLQ z0px_t%*;>GGi45SbaWg$=9bF}vUkJYy|L}Ln%Vh-f`URiRMgb&?W>Ju2B}t@K7D%n z(LA61_wQf1^5vLxzKW`<>VBsF%J9A=V|l0HQY4APoT8NVf}pn`^mhIaG)A<7@lG)}*vzpI#0p`~TR z)~%uMBS3~cymDm<8^aARK__W`KOKGj*K;{R>EO=R?CXo&`%OUZaRLVj1E>@Mhafl= zvw&ig1LPn9kTHtb6dua+pKmv{bJFF@mzkNF@7}$utE*dDT5A92gL7zTXl-q6adB~V zb#;9F{2B*4HTi2|V3Tq-?Amqf;>Ey_kSArkxBt98cmDkPzrViz{8`D{AaJWuK#3(K z@%q)PrSI?z9nU!*;o5}itnPui*Mh$1vW6Zap}^f53Wf_A3b`sx4%CdlzEiS zT)Te#e%y>=1L>Cyu5Yy-(Lqsj7!A{y{Qk@WrS5Z`+UcaAJ=X^DK_uo#lT!vkP{Z|-W@Hue$}cjS4EJ& zb!Kh~2@6}+Gkw~$ZF}~t0R?o&g;_IaR=(YOeTpy-4^IY2lhT&D|9{J$3VbRFVOD5S ztk+3P=2*OFQ4YW2p1Acz9ZgM4ybS_v)85$asQ9?3cY52JHES}WAFkbgFNm-3%?-oK zwuOuWhXf|w{(FAD{rWe>|NqtI%Ae@fy?OKIDa91I);$-m=rc74`1$(&tl4+);KARw z735#Pef#&_?)NDlW^eLOOiXllKmIpBLQ3k@t5;Pk5877z`SJ1MV)v=eC)PZ$E`Rsp zIg0vUCrz4U68CK4frp3Nr#ip6xA*t2udjdYWDq#?@@3}q)2h){p#)djE1}6&}n>ACM!osHAGrP05+T74E(9%6MRdupPLx56C%LLKH zf)_2pF*}P|MHK~%S|$i2<>kG*u+aIGVp4K)bX1f{Lx5sSi$HdEwxp!wDa9vRJLb)k z<5dXZP*iNW|GxZ!rBnZLV@u1Gj4c9+DlI2YpAP0zWY%!1QI`iLwZeCIET0N&sz_B) z6i8~BAh2lRLPyIZ*W>HgGPZ~*sTCN1d2eKcJ7RnT)$&S#If`&21R!cPQ{dM6Hh;VwDScYtALYJhl4`Ft1FtR8<^So zHpsB7apvG$=oTM8|MuIkWxLAW&SFcDbn@UZeNq$W>e{;TIQv=~cYaXP`;(cJq@<{Uo@=sK0sVj!D+_GmXsb%a$+SdK6TabnN%> z^8+PhW*0ZNZCkfydNU}r=(}-TynkO`FsLj$D{E7cge`;9kE9lX@O3d8x3{XQszygg z--=@3c-SrI)ORC(U(L;9T&H+f+$w?yooHIKb!+MQdA3?Tixw?fwR*L=nVAWL(~nKZ zjvrsSz{A(~D#zU0+w;{`Rab84=gB+^LEIk26P6u!@4uvx-^DK(_RqVzw zSupIZ#$RLQJ@OI@Q(77X@6CO8o`X?&N{@hn(@82Y%KE8f5VagctT~y_so;BDmNm}Y zyZb|4mbbLQKmFL&s37w@vR%Pn9=&__F6z$R*Wm>jg)5hP+`YYobK&o5d-?gd9X6l$ zoOi$Kc)FEu+S*EsP2az56fm-9i@F`HENaEt- zeDbz=S8jB63aj=1Po4GW>xx3*_I}Cf21bP!HU!qPsg=ApUJ(5K-bLBnZEnZeBb0qV zujEu@aPs)05^MX5Z;nOkv1Mzm`DCn$UY^?O?!T$%^CxdFFKKD%SiR}D-~Rg)9~BjK z<^QDp`v1>7KcBb!a_08iuV20L_C6iAuWIg`S!I8&%*?g^z9wRGTwU7wf6clRH`=pA znA8V3aWn`B?OC_?SFfCT=DS0xLVJSK&;S4Sdv3Yq&ZVhOzw+|(wLkv+{Jide*ILt` zij(|)HVOs07Z=yo{#`z4lDK~ST`#}8)ekPrVeS6SFR53WbGzRM)TcR>-rFN>R&{5R z=<4sXHZ_mKioRV=m$#qlv2;q9(1}Az(N)jZSAUbAWs;i~(ysSu^{4&fiY)sWolex; zw_DgJMe4p+!&HmfLjQ-jzH( z=JVdV&)XI)3d-})63)uVsQCEm>qX(^ZvFE`WW;ixte-3Jie13zmw8Fbqhq?aFB{iq zXJmxfJueK~W^?+<+hC^^j1C;VHk(`=^Q!OdEZ@IuncKY^A^ZI7o*g{w{&a8fFL#c= z;v9;KQ(D<&cYT{>n*Z?W>g%~j-1Iw{Nw!2s$}&{IGDku{HdEsr2?c)gLyjTJ=kRx|PE|-b&GL(_e0W z@-)K9gM;zD3&(FhnP1P|T7Nz7n8)}2*6#b}+gB;JFmNo~Bk`zY*OmRv$Ne|4sq`qc zv^5Av=GQ<5hshvUpeiI zBmY`v)1m&gaJ+UT+rHpSoP3`mBHxL(7Fb zn~To=nl?@Bsh~~84-FNiHjpFK=B@alCntZEd-q+tgcAjzEHSHkQt;*5Uvh4%?cTd; zRn)u{drzON{rKwZZzrW*q{*SJzuCv>}b91Y|%iEr;`u6AOYx8_=?ryKsr)JN3 zlU7-=v-&e4w-?qIq(uK07rfYSclG^o z2k(T8kdou;9@o!Q`|qe(GI8yV2hj(gK6@$%cK79_>G77y9+h6R)bsDe?5QceyiNAf z9!F>QVv_dgU))a_ZvS$MrL%?k`;~9#=5+Ue2vukB^=XKjrb{_5^!INSG;a zSGu}CI51FiyVX4X6!k?=-(`z z)f2?MQd?L0#3OM0#0eq@BQ^!y+rvq=Rz|E4n<+P z%Nu|EtzO!{$A9(bG$T-QntAea`p&y~OHb})=T9|MRlLH_s0mck1 z6C$@|t-a*3Oea+&s7C;ePW)=Z3^FUDe?9>TZr?))aF?)&xNk{}*5`qFh@*a;NPzh3?k zNYLDL6RNBH-qRvZULbd>EUdH&w6^=sBBwX|H=!Qgb_1miY) z_SPt+$5v&pucjv)`e69@iN?W|#?MbJyV^Z{ZOrDn%g=JH%ir2=_Ba2C@jzd|$xZL2Qj&G)dt-&fCo@(WKR=;1XVMDq@b!0hmfLIpzPb8(eo@iqoNbRL zUAokD>eQ=Ovu4e@HS1Q?%w4~#B3WAA6`^!WSocXhu) z9+g;$awsx5z2JXetmZeT=HsWUU0q(R;Ltg{@qM7Ou)2(0*`fs-UYwq;&($LELA+%G zOO$e|r?Xd7+vS%*pg>h<`DeW8ME=!Xy+_~Q*9P@H1)TQJn$R(0tF-xPEBkj%pgZu+}Ihnf*AmeA%rC1)3FMq%DZXPQrqMTl&yYGsgwmvRAENtBaNTE=*>)WgU zZ+>#K@`IEA8{_12e-0_lZh8)Ka>lLiN!RjjyeY5{v2h2NZFVAC;&xTV9xMhIke@vp z!`I&r`5*%dlCyiVx6SEW{lF4z_0OFTo@Sd*saI+Nsl2lzb@wO#mIvziEwr+`gcHodTdtR&eze?-{=bge;&M(S#v-3+Y{V1IwE>!pMlIYTt zAA{356dC4nBu4nKzm@JiH)prQV{?P-drIG@>w8T$ey%U%#9$?OXhKKCTg5#kPot{6 zTc-8P-(Fe$@i)KM{Lj3{neEZAN=f|K9x~K^d^TQ z!Rtg2;~&b}@4pJ#VFThyld-y);7Uu|vQrWH7GIH)_#D0#BOh#~T*py2hDYwdGf za+dG@E~jz$$ET;>{}m5&g1q(c)K=*!hBB6QLECDwFSmZ)G=GlK4B6^$f?!u?Zhoef zJmGEZH+ic|V)G3zNbP+8AaVEIg+1!}`ws|7dUG&lICE5gk&TEBU*2=@ZQ1E{$rC@$ z-T&~UP*7E+ahTTX9xuI9pd7!VSo7i4*XC0v+MGKz?e>iL|3PO?9O2ymw=i7tUVH!i zM^@hzKrNr0297HK}7=sYXvvhlT?8#XCNBzDG2$%yG*U%$zB+n(Uh)0O|HWQ6=_ zXc6=dU$?X9?Xlnd@=F$<$_cu3>G4*XBaWa}==g>&7VH zh z9Ajz__$MM#rSNjar&6KcZ+CzDcX+koPqugWsvpd~pZM%fE2v5_xx>=xx-|XYvYP3K zAI>nY|G0mL`ptVn^ZvfsnU(y$sK~nPwJRuimHYOX75%%E`n0>K=<~lbnmZB=Wx2b% zLZYq89$n#Q6a!@|sggQAzUWO!rKP^T{IMKn@p}u6w*`N<|H&J%_|cO9j;m?Md}k^B z*9F`ArANlvaQ}jUneiWg^K(yM_rvhQtyTT^^;B#uO=GtF%6|P#&f>~Fp@%n*Pv1SgnqS4#)O2P3tA~q^e*)z_r;J(Qp5NctU%7E(h0oHR zH)qcK`TE2W&iMVTcb~l6ym`J|{?9UYfm3c~UX3iF0kjI1b&fBK^crj&5$z!JUFm6_t}_9PqeTzwLKhAePPO!X{YKko7;Oo9WqytZjrMp zidnSUW>e~KKTx3kI#lx7_Q8#lmrb{B^_qWa)27GE&+$l`hJAYT_3E^Vlb4;_Q}?;7 z(sk*t%Xjj&-$?)qcx22U393LV{W@UZ*(`&+(-6=l^qHhz|uP1#uZx=jCn z7$_jG6wH*6617)IJhEu1cAt#>%t_M}v-xw59^I7V@Yh;&>QvWlkJ8V_pYZzjSlvJW z-nQa@k5Ui2^>=r@*NJZM`hVsVFH3~7(qrq>myaCbocvTd=l0IuX+>RYoH+YmdVpf! z|15^=C3oy(?b7aCi7cM36R+AW@MME}}5t=RKC6-TUSW zT;IulUs~}G|F>5SEDQ_`4j2Akf6Fn&js-FX=jFyhH^!?Of=q52l8Ol}lSpF(RhI^^ zFfcSItf|j-{*hvB#Q-y=4)?$#o-s)pOZ{mS^t#z2&=W98uu0I%$jQLT(~ZMT<0NRf kR8mo5p^7pK(x7J(Z<+M=w1))@y`Tj~p00i_>zopr07_oMi2wiq literal 15341 zcmeAS@N?(olHy`uVBq!ia0y~yV2S`?4kiW$216G9ZUzSa?5dE6lAy$Lg@U5|w9K4T zg_6pGRE5-v%rpjuid$!AW(LXHZfg6VExMOQ(^TJmrQ)LUg6a4DU&$CGY}mM8`XXg?&yuXewF z_m%l!&=U!H`6+)b+75pG^SE)=yz5{8mWDrn>~ia~>v{fp>%U)g+xGeEoF|`M1LdwC z{+xe(K7Y@rG|ThyGtXbQi~slWeF*2VQ+xEgCd)d1`M%x8t*d|jLH`Hk9g%O=+|m9P z{Yd>|@%hS@^GrXgzA4uIOHKReZXu^%6&dzZVEOZ}6YOL9*B?HyTavX>HuBy%jc*^4 z^W-0upUZmw=J+c&t-gY(&lkl_D>}a;kTZQxzvw<~`dp^1I zJ00=&NNg4KyYb!Pa7vfwwvaxh;75C}B~8`)tfqXk+#)FUP2o{DHMN=DG1IQ^Y_ZPU z{!wC$;U}&;nvK&ZKK_3H-Q(8%f9t+4DqeGjv!J?H^s~@v{bh+WH{ZYG>U5~`n92Pg zY~L+@^v6%T;lcKqw>Y`+@axB-vkv#)>M}onG5z@3llH$mlBeqQpe(~VQl{5JigmYM3#*pZ-FBr&d+oMsRnoCl zEXOvzUV8mr)%#?F7aq;!yzk^=0xqub+$^^C)|S^ZrkwXNK5z6oXY=`uDMwjZc7%q- z=6*FTyD{aGpY{7quk&`lFI=%z*x%~slwg1BzdhI2*lzx1mo0Dn{q>sG^lMMlPQ6=g zzv~1#qrP13f{FpJG*9@W%|nB|4+=>d}Z&+JLfBZt7K=#?C3Rjyw-X|q@`zzyg6^? zpJu;*?_T_|uQ~YtQg@j}G*{v6pIcsC>u~vb#%I>2cPh(dJ-1k;2A-42jK2A1-t{wE zfA5;UUD{n@@wPRYCP51wW5Zd0`L7L}c5aJ`#N4ojH?ozjdEAZ49wc7ct~p=qEtjQr z8Oyv^GYu_cch7q|Rjy&F*N*;9jbIz~KxvV{XjNUV$kI$N)2wqMr=n)-Su-1aPPVAf zoMYtw`P-UzGy3Htw|8xj2=&U_=6rhX(uL|KPV$A*c0G=8Ja{bdwUnv#%g)w!6fxUU_;*&EEBD;nm^)eH znay82U!lWmw`#p?7j1vG_@hrC!;cp>M$;!8-BE12e8Pd0!*_!IJbF=Aez?3(eTs9c z@awso_6J{xPFY@Zwp1_4<<0j=(%OOr@5GYp?kvp=)L;5}l~qiTXKGG>-l+v&G8S&p zj85{XV+fj+rN!g?bKBICFj?D%r77*d45!@I4kN<4lU&e(g_L z>_KHGBC;0-E!({CxJT-hi857s+DjOOmJ|jY)v?X%S`m4*tyIS#rB?W&Xw}UrR~Pyx zeVyI6)bGWrxqG{pPhniNdC|2A)g=Y2wdGrO%{^eitp1=?STA!~?d@w#uaeIQF3wq1 zdO%5%%VTGPz=O^!Gaj&oZ7`b>F8Jfg?9d1Q5`XRq>#x?77e1o#Wj^U+vcBo zu8=W%g}|P2Lw7gn+1?f%7H%tgm)-5SdClQ<+{_uFDo?Go zjQItAH$45ms`VkGaBI5eB;Qi4RdPP=_8JwpL>7jw404TQ$W)M+e^B5pTf@cM7E=}{ zc2+h={`Q#67HK53V@~JsbMa(8b^R_dJIQ_}>)WQxVJ3)I#GF5^@bl8V~-mVN!A zt?7Alh57RrEocfjthXlbOohX}$<{mOI_BkEaExtyU3*A+T8WhG#xB=n#@BI??P@JD z^70nDzx?R=aPPdv&fX6$D&Kf@lz%0%7o_#L#;GdYP7w8NeEC%J*vUQnERW0zy0J_) z|DmtFKG!*xtH-Z1?w@n=w9B2@GbN1YiCuL_=YC?b=HlWMS?{THr0W9anB8aF8@$vt z*K8SgEN_VPj;UJpz0=BHSeuRP77$u9!aW{16yc(zAEERaw9(5{_^(=2ZP`EVxBIIlZn!Q1OvrEC|n zz6H6*OC4vJt@7>Yan6Y@(a|248U#KBoA?%u2jxjP;@LvyQ=O;Z{7VP zhcfE6e!MTR_)+PCzk)Jr1s#P0In5R>wVF5ORO4@s@?y~vmjCMJO)FHgoupb47>YU8 zzATo!Y1a9v^?-i=yK?iI??1Z5LtjMl*zGp7&oLEpng8;@rElMa?VmMTubXN&yZLGP z;$_Dj{wp5q@M~3_xc`soYv-qCE^j{x_Jtpo_L_R@*tZG$wWkWMc=zoTS5LxptDudy z+gQF-@A7o83va6X5Z``@?JVo_l!s}PUfix`GPRy!wf<8^NcM|duV&#}PxnbQ3M*Q% zwfF4kc=Y1uxlKY0UlQzB-eJGHTm60U!^08lou4{Oy{~?wyMFz-6TQ3cJpOiFeg65I z?=CN&{3omHYVC}N>E7J&{9ltNz1w(;|MMTsDf|1E{EpM8m-qZ$sS&(lA8*I%3km(< z_xv2?J^pQJySsf?Lu7f)fmk;wyStIiYt}!V#eXnv|6X4ImX~Y%IVbPlGu7by;@=ey z3(qi^v~k<70a~T)H>^W4yd5<5_2fZ*1#pyMwzs}c2-oD^7E7t0Y>KU)JP+NMzZ)(e@M6Y_ zcC%O8B-8FKI^W&FywchE+V<}|&gVG^ZtdGvBjV$@W$HPpi$BB9B|Q^=C(SosH2(UB z!`4<}JF}lpn_+!ZAm8Ker6Y5DI3#2uC(PE`#gw>TG?cOXbHURFpBJl>w@)<9erCJT zyTZN7{#W4PC#k2jUs)|&Ebdqx)b1puB3@Z#u31o-bc^+yHtXrP=MLoXtQMQWK2i3k z_TuS9oB!Wq2+PuLIO;g_#i5gmYu?wnbeKt7T5i)35PxRT-(h~>g6f&N4C(JHNdT;>Zt zGVJzORl0q`RE~v_L0UokdW=Z;a)q3H?*H2w3Qnkfop?IWwkNJ&VaLTiTwa_rv)g21 z;|??Y{9M}2aiZ_^Y7LGzMcNjJqFXugFD$=tj%il%M62ryC)x^qO{g|%X4+hPg<%1Y zkywfR=SJp=1Ml-?<~79~)R9?ud|jYq-O|0XGt4jZ`0TsVu=l_ZlWR7wLvG7D&rw<` zsvN|}+mXH?cH)CC{8tU9rJocz?31T;lp&?`71M!3|M;tnsCInd&_e8QPI zYoxkM+j7PC?0h9RUHG*6F1GUzZ`9wM;Ls9&NGCm@@n_()*`}AR6QBMwKEFf#V#ACZ zTioOJ{%dtB)3T7imIP~Xrb&ULzNd@oIKdw?)wSD>Q={f)Q znz1Z@d#=)RRk4zc=Hn{HA%Th*Vbal(OfQ(KB(X3S4zTX^|ZrkRV5%4ME3 zqifubjmL9N9~P>6RC{pBy;!je0<9;{RxD56-JhK7s&TZpE!VT}ZBgHr85J96io9Bw z&7(Nw)9%I7jgUbXiv(P%L7DVla`nR99LfyAsF@w*YBSfJ@x$E zVcz*6&kt>KI%aUnic`;~CG|ms;j+EWuV>v_C-))0J?z-m#|K28iG`RQ%vTN+-nQQN zKbK{DW5TP+J;$%DNMhk`Fsf%rt7?yo6O#_={=WQGhs{U5wQl**7lI@?k9e%mo5g(F zN8mKajc0*Xx_e5Qqi>ha7rm)`&1sp?ZKglzrV%NTstZm!)*PsgJgj*mBgE)?vbaF! zLP5(L%b5=gCQV#w5WBRv=JMpYJuHV6-|m#H?3nbyLvwQV#IRR4>~A-0?d9>jnz^|C z^tN6nZSi91liH7tZ!?WZU%>r0u0#CA^rM0`ozo?5ojY8^=xybF=UeSU?uDsaa~H}! zV~YJW=dRhm+;(eMtyOCr>JDcacTPw$+R)3ZFfn(x_A7xN7NgFM#%mqVU*ZgdHuws^ZV+1Y>>soGp4}D3w>G|IU|?*? zbaoE#basYyyBQcN=G0EK^*9_L(&`_)RcR_;nLy!*fVnyyT^3oQT>*+$xYk1#$OxH{ zR@%!vD{uD86sI%K!j4|hX6m?bo!K#MnxVR<=8tRFZA*ji9<7{Ke}Def_0M$KSX>@O zrp++>$oBEevfP4u>V2Cgtr40p+&{C#;gS0~ZMpr0db@N!oLMo`^SrQo=g}l~ZP_zk z%iL^SWr9uTerjEFT`k5NrQK`p>+zp)=-um~@D9!SADmc5V$9^e(KiKKFg& zV-x>|2gU_UxBtD$X7JgwYVX>keGMnf3JULDn_~N*h+U$_C?&XTsen;O(`+8Y@gjbpD?Q&Gc;8yg!nTx~-f1f;9(Mf&-F`C?JgxZpvhhR4PQCND#U zmw0e2R8djfn{(^i+`oSxzJK@i@2}8TYQ~#?SN<=SQ?Wj8M=1eh2cI2aieT84@=&0RZT-ZV{)h(#LLu3p_4 zqZcUR8h9mZYt-7X#TO-d-PTT%=V53O_;={_*Ijq>x{n4$M@NT;r*FQwCT#WDw9OGZ zV%e9r{wWSApY< zI)lA(#ooBIgZiQ2;o>49bF4~VotUW1&M$Xod;a~cHc@G5Y3t+m{`&Ir^4{w2eX`bS zDk>|k?pepzAmEhd61Zmldi$a$C*IxN&CSKtCvRW(<3r;8yhAx=-{0T2x3#r3Hs0(# zT`%|67Q>l7QoU^_*&R3(3z^Q(wceh4dz)*wn5=c#nycHav#zY*6juB4{=U7kaz3S|w_??*R}T(0zrMCMdUsiF za`NJk-@6kJw?*ggomwBrAadN_PA51v*0%7G3#aji>UX=}@4L9zefx#x=(X40ym>QK zJ6tb%o6jfPyZh_y6_MWCQ@#MU+ z_xI-3`!R4VOmPXE^8CQ3PoG}BdXE2&Sa<5{YimD0J8ND3Zcfd&E8Bl_FsyC! zKd^m`ZuGV(Q>Xs?z}PQuZ&&{A&hGd7qQgW(R)z=)3hJ0MICaFIK6_SIL*v6q=bd-o zU0ogCFJoEs^3qbHJHeAr9w}m0X!*fwXF2(N-gf=?eShXvzgxLx&6J0BWiKzOnwU)4 z%I3iFQF`C+ch=i)*Y=9m{QvuXx$kVX+qE*LSs?`l2HCs~0*}n+*w^pdwd>ca{d?D~ z%X@Za=6So{Yvz16)Ya`3U{YxLkvnzj)bPJ%iMhFN=U5ht>Bs4Ot~Hr`R#8!LjVXgu z$NjQTPduleE?s*6>YY0_(87%UH$Xv^rw~k9^@O%KI`b{ zsAI_B)ZzW)?8AbXPp7rCwB}Vlo7v3Hua$A#$jGR~O4ggFLEuqR-M^pDz2i!*JS|!q zySwc1vEEa+wrx4GnelK+cKYUH10< zT;Cb<=l`F+|Bq{@wQ1R#h;z%>9XJYi)_wkL$x$@_{y=B3J2B&zDzpf=ECa1YTMuKwFo;`o||KGl7QPR;a(XM;S z_Y_zhI8qf&Os-^@XeF(vtgQV1_dQ5m{QkI$#^-C*88qfzvoBM&vih}jdYo0kg9AT5 zKUY>#GLbrabpO4lMW!Yu9!2-=U0m#5|C4|B-F-Dbib28;oydZ;@uev`@fW=W@)#}yvYd)VXzgM~3w7u%diHU7(Y|~GdJ~=UQZS?kE2RB;$ z?_v;elKi=}VEWIaf`Wp!RbMim3O;-CL_{0}>OUS8_nQB9%^Dp$Bd!Jktr@r9=Kbw8 zGc%L3D#^IK%s2bmn!sNyWp8hVuD||yJqLs0k*SqWCc6JgcE5D_^46%j76uLJ<^Df* zu358YTJ?u-Z*RwcD)wYJAgH*+u#3y8k?b#6uSrI`gs}{{J_6_StJf(!G6s zeB9j0pBM$4JWl4A?f?7j_ThH^{{H^|d)F84jM3fr^2CV~_uuF-2smw-v}Vm3e)~Tg z!q>-5%DJ0i@`+86!Tx~o%%HQgOkdB=-#2kplh#x%Ma9NYu*W`LbeH#?Vc>WxT1%aB%sX8-;(rUO$~;Q+~Hpyj+RpfS_X3dF%Ik zl;?wr%6ogO+gn;T=rLF*xnxId&APgy@NtUik{>^QELfm0jg3LkrF*tj>8l-ukDrun zU)<8d^3IpPL159*yA_Xn_4oZ)#B;y*v5FJOtq<33zn67+SuYQ}n4sXnR}2a*8Ih~Q z)}A_ZX3Fy$J#Na}EDZvSSbgW){rz@3fBN&#wY_f2@{+6$92c8*mAo`cJteX?PXGMF z)U>p$t#c}vL6)4EY3zRJwpUEh@jh8jMFyviX_KZ-Ej_Kfouf(NwqROHN`X4aYTc(# zr^iqG`E||Nw9Rv@c^O&)4yyUjtGVoJezLNqu(0sXByk2s7w?&NwY$#i$EKM`x%Wss zTlQvr2no4zq*J)p?Qtu&_`0XgdW$r=7X7&3%wPKE z#>Asc4jd1wx8E(BeK6s}U#aY^huit(?P_*he=X|z@m0KS!0O6u0gz2QANN`BDtmit z(QE-0$2{@(Yz!wq$}Nns`S;`ToH^_aEd~YJ8qbYiFg6G*O1)cnTz2BbiD#G@1P(b& znL5?9;DN(4Mh1?=hFMmnTIJR33{DaY&ZK?*@bK_Vb_RtO#;h|l3^Ow`!5SD$Ykm}* zd&>+`V`Oan`|IoDHVh1kJOSVD*ViW{DSc*OXfbf$m9v>~zMF|bktaq|Q`3iuLEsQW zs@ZI3XJ?3sS}H0nV0oe&b01`NgBfB>14U%{+K(o!T)%$%?YF(Xy<*)*v$k?I2}nsv ziHV5`2?wY$BRR((3D zuI@YQ%I0)`M}Zm94o-WL8DpXx9S=6Ka$mZ1Y2DJM{h$8*exIJ23d)`z9v)t#VRVu~ zk>$yWr$xCpHauLpeBLbE>N0Pef7S2znkOD=aqSi}H8(fD4N`M7ZL@C7j)FfwKE~Jo zEj^v9YH$C)zy8l-{~Dk9=D-|p{e{rx$AedVpo zavmM&Z0D0r5@m4W@Hm{b(dO%w;MwN+)3O+L?%cUK{k)!@UYah06UP>_)mMLgyPd!N zg?qBz@qM+wpPic<9UgwXi=jmz!!~aH_sRZtm4Clpe`+w}yxs3NZ@1qs+kJNpw*yCG z*rdsmjqCr_{Ooz&Yku#>hD7GqOae{|Oe5A_+kUU=^{4f+x2!;U?BOBSFm{C&hwj_^ z>;KO(&3e#VkYooVU*~p1;D6}|muUfGpVo$|Jo8#XudF#)$D(y05;!wQMWR`bl zN7>t`%D%NrmOQ!aZ~t`F9R@{~sIIG7f1gbDe`0fp-~P{p%Uq0399yRR`SWM4b@{2v zmZ?*xw)4q8S|Fg%((ry~jGk-E?c&eR&i3~9t`T?OV3c3Ia%JS+s;z&H@2vUxY11a7 zYupMg4gI2`qPcf>g$g>`mc5a9%_`s|e>uZMC~xlS^plQtb^C(as=mF61S#Fezwr9& zsadZ>Lqlu+I2Nh<%?UUa;O*u0rp(%#O~5IB)%x}M85t9@UibF)hK7dz;qW@~_@l*K zzok9fwr#tXv~fdL!}-j=Y5rc`-fzoxAAcOUT0gctP~>gd?~jj<+y8!J%z1oI-rZdf z54XF!xyAiU3_`uaLKF>$jC9J+tu$`zB+ zS66za&9$;`6hA-r^(`}~iGFui>7R&rwcU5`UAeMk%EfEfL11%M!}*^ly%+cP_UOnY*H>wbI97qQ!$+4-+sz3Ta6otXp2(|@aNmrJh>S=H0SqxP_?E;6>y>^IidKAmH>IS!;w$HJoG!UD%n^3R`~oGkjKOwECVQT61B69!VfM)FS@ z7@13~WV=}foDO_{^!Rc5xjCB81@F~= zlw@CBS$Vlf()fwxw#dzCUteGE2PfG{|9P&wnyBo)Y}v9?1v%gERj+Sa$ILF^njn@i(zukU*zJ0viG@9fnSP@WXp8)sko z>dNkSyS&xhS8Pr@>*eE<5(|=<_vFbF`@b*!i*~e~J~dVQXAh492P0?6yE~N^7r9>A zR`Bpp>)|%u$7x>}6j_!Smc6;r$}R2{aj;#!E@JV;4FVuhMUF!^HYR_6ch|b)#f58k z^0KmCy}G(u-G82pjLaD=hL#VfGPXut3tAdvYGPu*X}9oV1{=TJpM&i3TW;;u{Ppea z?v#_0tl#f3*3jtq$gtUJ9F~n&7H;1kM&CHf0kzFm$OMZF+tI}t>pc^yE$gXO)L%^j7N*D=Blcy zrlzJY1%t}UN=r-2%F0Tusal4HA9uZ82kM-LgoHRbIn~tEczJPspUA+W_+WmF-t@~a zODtsg+7G8~UKygbH%?wiD9J#g#ApG{G&-W^d0K>_PFZ!a^kjwt z7rcWz4gVjsR4EwLJrDgHt8?gRz}8-8|yoL1Q--12s#bX5h76h zA&+g*Yq-n7C~6r4!@5?t#TT=-TFnhxe>uA}YHpagND659ry0~UQ_c8y`0>XIm;UGW z{~woF#p+Ez{j|tpuHW*@4-0O+EqgXufT_Xgk+_(cl&t8~Ia8+0x^yqa#LeAZRbAb+ zrM}Ah(VBYw_1CJsi(ZJj_8)&-xbu$L?6td=E!(nY z&8l6?wryLtaO1{3dls$PvuDw!O*?ODbt!GWdx>TJ_0u{G{}fdh-+uphYSdb@nLa)j zLs`zxnIj@EE-oxAD=j>E#*7J5CJ4yOi_6K$2??#a3>pM;Tl{lr+4UW3#T~)>POw7zS>V>abxbUIU{vZLT27yQQQ}s<$ zRCIN>*6!Zs+1Yv0v$K=)V_{)oC+ElO^LAxFEZgm^H$_ZDY@S{5yCWwj?^OT%>XnXM zf4i!33j@c)twJn{OKX4IMcP)Ul)b;Bscfkkxc>U-nlo|hzt;qQe7E=aw)c1be(sFv z0`(6MX$uGm$w&)d-sU^IPd3PU$#H3_ddDXoAdv7 z3aigv8+L8F08`5Zfq!}bFIktIcyn)UdjFYydp93C&W_B}pG4=In45q0Zw4uDaFUQ0ul`$l zI8VyZW^=XqTC)rOQ`hBZM11>r>iqRJrB<;m69kxAF3fknu`c(vVlx|$q>aH|=k^mP zI5Pc9Cy1`U9$m5T#d~h?xA!*dC(QrAsp!D**QP~M)_UKkOT}}IF8!4JVY^5}FY?>P zOHZ|#J_nhK^DBzqWm`}ocX9HpNr!A=TB6p@vwJ#GmHYp_rN!!L%>qmx%tb|~`ksxc zcyp;L{cF|VtJCvk4Cky=Xkqx{l^h&gcFzCI;innbx1E))uS-26pO>V?p~$fR)7@ul z%XT+c?vRn{P5bxxyx&Zl&VCU=Cx(BIA1svTIzK;MPH*zTgc-k1Eb-*O=g-Zl$Y7sX zZn3GD&zOI@|LUuDKaYH2SG#jKM8Jum?$L#QOP@nit5PmGoA>Q#6kuwwKVdub_*K*O zAr(6^&#h4x78d4EWYCDqjlFyA{dQyia4j>nXHTEcH*ISbbYckDlY6^-=iR(#552y= zzy7`PH`DjCPEH&L>gQ-{XlUsr%B?$enBP`l!}S%)EewB7?fc64Ubq|I_DBpBnRrKi^mV-p{8)y@g>x zLfGok>+|-WIehiy4aM9$H!ijaFigq)e(!H%<#pTn66R$+VXIX^>aLoa+CQI{BDb#3 z(lYZ$>0yu;W*mQf^T%7Y_18~_#LTrmeqTsT%!`B3;o!FScVg2w8t{iNuim~c_w2I+ zH{*4^`40RKxqjt|jNPT8?MdG&Z1|R6&f6TFYqt98tCF2__}Z_g74N(w*YCbGDAPo$ z*>U~#*I%vX`Yq??#UO1&AE| z^{egDrcH-FyB<|lZGGPDH{UWkHaL589S?z zZzjx{qVfFvk{*e=yN{Nh=2T?Ri1_!?`0H2RhYy#n{{3y2mG{S1?&s%3MZ`p>P7jaS z5fBv_np=KjuHW?AZ$DKWIbF|x>9Jt0+3d4vt5>bMSN>o@*y^hFep~nJ)div&qScgTX5_Dr{fv^>-h)->Lb@UciX}z{gp5{b-K6r?SDJ1(9w}?s#ogv+x}0Uy|peVxZl|PbNTrb1qY6*ipSOdU9ax{ z)ZX~B=iV#2J-xk#lD=itqM(TVo%DBl>+|lWrsh5w+qi&;8#ivK{J;L|+M3w-=xFQz zt(9tv0}myxxccf#U*^Ehs@|MyGJg9izm?HAYO-rmbt zQ18A@hnb;hOWEOY`Twu)U+PmdQ&SUIHcNo1!DsEyq}oEy&KBm5&Q39wMgb;JW#X^t%{GihiiQQOO~wJ<21>)y|uNq?brWaPEJLJ6H%FwH!mh1UvjG|!(+q! z^9G7643V2}zPZ`n@3Hmkf&~g7(<1JztXXNM+rE09kP`z(iK*$pd!~{fe_2VcLfIgEdmTo)Bofz_TXS- z&~+7YT_2tTR++~k%cuVNg3}IXTZS!vg4c#=|EUlEvzM>k_)bb!UssY8$f45}IHG#h zG%RkU@No0;@^W*VO7>=Lbz2+O-rB~(&MwWv)_kx)R6Ofr)zK;6-Y#Cusi?@p;Q6w| zDmfyeV#mo8BR6-~qsNXMIpT8s*s&wWj*rm^LY_V43Q* zG-&nJEJuMWuP#gwST<3Bso@X{E32xiT8W4E>p2tet;?N#<;s*NOI+4YQ}=h`VECu4 zYr3?2{k+)SCGVz8oHu2Pgp^Q`Y5qGa;m%Q96D z6?S6an83ou$}4A5aHUtdjaPEb_17~51e_Qe4`rD2{XFdZ{eE@0^yZm285CIr91b0F z%HLUeCiiikpSSPB&;S7^21XZwme=jPwN=L+=iV+5;c((mI8gHP&dZOP{d_O3hcCWp zAg$2Cz|`TkSg!uHZm`@u332h$ISm3#96E1rZL2N)_j%^s-G-tdHRpPj+wARfy?&SF z&g23)!BwDzv;4m0pCX&X-ok>)_;zA%l&!Y!lnlu0uBm7EDx7HpT9TtO~x`N89^ov7yf(Y`l2j~ECLJi zq$&?j_P04^12V~Fu63%7VQ!b(;mHypn-*Wqnm6BickXR7AJ&==kB^8j1kAC{+r2gV zx}KNhT8%EzsZ&EkO|P0>y=uCZmzOte_1Sj?J7V;fAI{u*bB^Bf+fU1O-<1pev;Y3O z%URXmUf6xTc#?_RpM%k1X;A4YkNGw)9cS`i%GxR>GEaYJK-I!6Z=5nSGczMAU0nkU zJAXv4+`Z(@oWqOV@7~zP>wcg`fFWd+=4AC_M^Am6Clt5-`IHhiUT!m>O2a-@n()#xvpeic_bx%iiDK|Ms_R`Z>`E9kKcD-U2LByTaW# z6&dDj*m6s<-&{1)w+JvqdAd~{PjqHG zov2c|{L}o}#Ja#j&Tc0T#sy#R6#Oma{8%`NU-|b3f8%*|$KDleP^xRzKFtu&mgvmp zY1ei+{oKAUr{Y&AD6%wM6O$I6JnK^2EyIpQKkl*r*IB6j zT0g5*0VHPjEG3h@;KlJbzuMTungo~{LKIEBca*Nb{`+%h%=X)7odld1Cb+p&>}ZJ= zcip&b+q6)SwwVtmByetj_pu@?>vaUH69>a30T#!_(>~2S_4s3iju_Y=lPytew_Km{ zvBKq|^2f?Ai!_S#>_JKNOV!>jieIYsHbp&J?&T=J!`2)!TdjpbVd;Yf3R{=RUV2$_ zY(qe%x}vVB>#FDg&$`ZVCk}-Ro^>vZE@v)Yyl9E7+P?4Wte4DUcj90WD&83@*YAFa zF~elmks~ae>;`{!C2sGL)_%xnsK~;gx$~~v$>RnY4)rVVE$Z&?u1@+T!f9{5`eFu$ zB7=vt)AZ}FrSe=GW|sksTH;^fmJ>sY8P|KB6%#IWhv$BH#!tDhX+QF%r_>g&qLwRMH}=FE1x zr|`4y`0-;xP7D(wl9MMtNtUpxc<}Xb+qNjy`Lm5*f4iMG@$eIOS9eLCDklzxAT7 zd!FBfdm9hi-}`<00OQ{j5zehuCmuAm2rw)O2@Y1CJkiGB;k?X#zr0R&K0dChq+eJ1 zr|Z4Dqv@RY**@zD$6~`C0j7o&F0S*ZpRj#wQt+wzuy1*~&AY?rgR-{X5MDKK1R*UH!=iGfZZ6^56M>{d%15`x_6V=GR|+Wf!?u zY|qzY_wUuLjh=q4Xy2Z%&fon#I{Lp~mp5?yWE{%)!^_KUd$-N=Sw3n{-`#o9N%S2F+lDr;R8a-1(SYpW5Thu^$Y^;zHU ztXh?&`tOg4iAl^8yD(6PiEF|Hf&W*<=b!wrYkF_5uYF4T^FJkDT;84JUb?hw_Fd&F zmy5qwFAwW=%e^2yY4T*N)Ft1~yt%hJJ*_Bc(xZ0;TXKK*vA);W(bf*XbDm$3g=66( zXN?sh+4lcUmeyKZ@?Uyc5*8XhecCj0GgDJDGcz+&R#sNY!-skj6FE6Qc6M^s73|zJ zxpMivs^@!^kM{{XF|=&hy7$YSn3;2u(fIpK_y}PZTmkJte+LuPOehF?}>Xq|KF!H$Hc?~=MKEmzxMy_-S7Xu=5zM-Y|pu!w>9c-rqR|v z-AoHyo_BXW+xy{Ed$+xTRBz+9_u1Fh)ql?C-V>K!Q1D^GLFU`rvu|h3F54M%yZqg! z%|D-X2s8veop1U1k#_yIDCXBpOXjbQ{Qd4Xcm2-zrn70$yZ$7*YsbA>vtq}Sn8y9G z9l}lwT-&PO|9h%!$2n*Bi=11V-rn8+|8Uy&|KI)h-QDu^b^gBGt&C6plpOh=Hd}xx zV4LXwU*Fxe?Z5uM`=x3xx43?7-M{&B^scY}_iFw7J>?G`@UJy)n{(LxPktV!B13BN z?z^Y$;x;B)-uE**KSw=G+qwMw9NUSry*U^i%GQM4 z=GNc8>)q+~e~RAw&9}79Je7Pu_h!|^>ngH*&(Bz%7oBH+f06P}L+j;h_JB%anVUJ= z%=V29pvZhy zwDVg1&#!l$KmUFFxKy{d-ky)&tk?hf+1qvM)agId+S-n(&zrUP>u*`zq8quJpU(So zUi-G76GNS<_45zS{Qft8?QP0`7#)9qL&?wM<#)ed2)|$R`eVhq7u9q9p6?NlkK0w# zdj5Q+eWB9zp!oOG?TzOI@E^Dzk-P2r^Zii)5hefkmbbRGX=Oa&YcKwPu=)SLoU>c^ z9MbH1v`AI{*}OBee;lZ_ttt_(F*FDiDSvzO`FeW;rw4QDnSbn^(v!FUdwAB?TPa4Y zw^~ZxP5Jnx=lJihFV}Wll>24)`*45QuMbt%qSk(!w>z&s=hWhb3l*>b2zV+wwf}6z zt`9X6o$5jJiF%*jse$sYy)etd>g#&^_v-WS-`jim@KXzBQ`4>1X=g*lwZC26e85bWP`=e-O^-62s-@mQhBDGhUoHzsy zIq*NM-p~b8)-> z`x`S}oc^QNfBf@N2wY9absi7cH>Ral2w)N7c zM~@yITc)=B^3smwkz1qIUVrW6?5up~aB^}|QqrQwkCUuRJhZj6e&&1?_u3x^Dxnn> z_ZT@kI&yw23=EtoC@A>QE!iW(V}jy|1cMi)R$JZH2CbYmd-m+vYxSm|&f6ZHyUnV5 z>5?T|_AF{z8JxsnxloRw?rh$6@p<||?=p*bY+9VSjA4kaRYV_&R)DUur0T-U6Emi1 zGBCL~J2Ova5O7j(=v;I{g<*f^bT>wP3qAi_|Ih5sR?AR$?uQry0|SGntDnm{r-UW| D1v;?K From f9bc63d78f48002d6d4e55c0a46a87a024b2549c Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 20 Aug 2025 10:10:56 +0200 Subject: [PATCH 098/682] Drop no longer needed feature gates --- library/stdarch/crates/core_arch/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/lib.rs b/library/stdarch/crates/core_arch/src/lib.rs index c58580f64178..7d3cbd55ad85 100644 --- a/library/stdarch/crates/core_arch/src/lib.rs +++ b/library/stdarch/crates/core_arch/src/lib.rs @@ -18,8 +18,6 @@ rustc_attrs, staged_api, doc_cfg, - tbm_target_feature, - sse4a_target_feature, riscv_target_feature, arm_target_feature, mips_target_feature, From 135de7c8df64b8ef85ad348db09b02194779e68e Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 14 Aug 2025 11:05:03 +0200 Subject: [PATCH 099/682] Use intrinsics for some s390x operations --- .../stdarch/crates/core_arch/src/s390x/vector.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/s390x/vector.rs b/library/stdarch/crates/core_arch/src/s390x/vector.rs index 0ce720a9244c..9927ff2c6255 100644 --- a/library/stdarch/crates/core_arch/src/s390x/vector.rs +++ b/library/stdarch/crates/core_arch/src/s390x/vector.rs @@ -114,6 +114,9 @@ unsafe extern "unadjusted" { #[link_name = "llvm.s390.vsumqf"] fn vsumqf(a: vector_unsigned_int, b: vector_unsigned_int) -> u128; #[link_name = "llvm.s390.vsumqg"] fn vsumqg(a: vector_unsigned_long_long, b: vector_unsigned_long_long) -> u128; + #[link_name = "llvm.s390.vaccq"] fn vaccq(a: u128, b: u128) -> u128; + #[link_name = "llvm.s390.vacccq"] fn vacccq(a: u128, b: u128, c: u128) -> u128; + #[link_name = "llvm.s390.vscbiq"] fn vscbiq(a: u128, b: u128) -> u128; #[link_name = "llvm.s390.vsbiq"] fn vsbiq(a: u128, b: u128, c: u128) -> u128; #[link_name = "llvm.s390.vsbcbiq"] fn vsbcbiq(a: u128, b: u128, c: u128) -> u128; @@ -4694,7 +4697,9 @@ pub unsafe fn vec_addc_u128( ) -> vector_unsigned_char { let a: u128 = transmute(a); let b: u128 = transmute(b); - transmute(a.overflowing_add(b).1 as u128) + // FIXME(llvm) https://github.com/llvm/llvm-project/pull/153557 + // transmute(a.overflowing_add(b).1 as u128) + transmute(vaccq(a, b)) } /// Vector Add With Carry unsigned 128-bits @@ -4729,8 +4734,10 @@ pub unsafe fn vec_addec_u128( let a: u128 = transmute(a); let b: u128 = transmute(b); let c: u128 = transmute(c); - let (_d, carry) = a.carrying_add(b, c & 1 != 0); - transmute(carry as u128) + // FIXME(llvm) https://github.com/llvm/llvm-project/pull/153557 + // let (_d, carry) = a.carrying_add(b, c & 1 != 0); + // transmute(carry as u128) + transmute(vacccq(a, b, c)) } /// Vector Subtract with Carryout From 4a8b8231b1e7263ae5d8da82ccd5c1490f1db9e7 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 20 Aug 2025 10:18:03 +0200 Subject: [PATCH 100/682] Add missing avx512vl target features --- .../crates/core_arch/src/x86/avx512fp16.rs | 376 +++++++++--------- 1 file changed, 188 insertions(+), 188 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/x86/avx512fp16.rs b/library/stdarch/crates/core_arch/src/x86/avx512fp16.rs index 8c914803c665..a86fc7199b83 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512fp16.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512fp16.rs @@ -17282,14 +17282,14 @@ mod tests { assert_eq_m512h(a, b); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_load_sh() { let a = _mm_set_sh(1.0); let b = _mm_load_sh(addr_of!(a).cast()); assert_eq_m128h(a, b); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_load_sh() { let a = _mm_set_sh(1.0); let src = _mm_set_sh(2.); @@ -17299,7 +17299,7 @@ mod tests { assert_eq_m128h(src, b); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_load_sh() { let a = _mm_set_sh(1.0); let b = _mm_maskz_load_sh(1, addr_of!(a).cast()); @@ -17344,7 +17344,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_move_sh() { let a = _mm_set_ph(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0); let b = _mm_set_sh(9.0); @@ -17353,7 +17353,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_move_sh() { let a = _mm_set_ph(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0); let b = _mm_set_sh(9.0); @@ -17363,7 +17363,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_move_sh() { let a = _mm_set_ph(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0); let b = _mm_set_sh(9.0); @@ -17402,7 +17402,7 @@ mod tests { assert_eq_m512h(a, b); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_store_sh() { let a = _mm_set_sh(1.0); let mut b = _mm_setzero_ph(); @@ -17410,7 +17410,7 @@ mod tests { assert_eq_m128h(a, b); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_store_sh() { let a = _mm_set_sh(1.0); let mut b = _mm_setzero_ph(); @@ -17655,7 +17655,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_add_round_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -17664,7 +17664,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_add_round_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -17681,7 +17681,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_add_round_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -17695,7 +17695,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_add_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -17704,7 +17704,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_add_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -17717,7 +17717,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_add_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -17945,7 +17945,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_sub_round_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -17954,7 +17954,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_sub_round_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -17971,7 +17971,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_sub_round_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -17985,7 +17985,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_sub_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -17994,7 +17994,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_sub_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -18007,7 +18007,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_sub_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -18235,7 +18235,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mul_round_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -18244,7 +18244,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_mul_round_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -18261,7 +18261,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_mul_round_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -18275,7 +18275,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mul_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -18284,7 +18284,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_mul_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -18297,7 +18297,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_mul_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -18457,7 +18457,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_div_round_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -18466,7 +18466,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_div_round_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -18483,7 +18483,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_div_round_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -18497,7 +18497,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_div_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -18506,7 +18506,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_div_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -18519,7 +18519,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_div_sh() { let a = _mm_set_sh(1.0); let b = _mm_set_sh(2.0); @@ -18680,7 +18680,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mul_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 1.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -18689,7 +18689,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_mul_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 1.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -18701,7 +18701,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_mul_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 1.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -18711,7 +18711,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mul_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 1.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -18720,7 +18720,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_mul_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 1.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -18730,7 +18730,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_mul_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 1.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -18888,7 +18888,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_fmul_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 1.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -18897,7 +18897,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_fmul_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 1.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -18909,7 +18909,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_fmul_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 1.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -18919,7 +18919,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_fmul_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 1.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -18928,7 +18928,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_fmul_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 1.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -18938,7 +18938,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_fmul_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 1.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -19096,7 +19096,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_cmul_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, -1.0, 8.0, -9.0, 10.0, -11.0, 12.0, -13.0); @@ -19105,7 +19105,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_cmul_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, -1.0, 8.0, -9.0, 10.0, -11.0, 12.0, -13.0); @@ -19115,7 +19115,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_cmul_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, -1.0, 8.0, -9.0, 10.0, -11.0, 12.0, -13.0); @@ -19124,7 +19124,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_cmul_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, -1.0, 8.0, -9.0, 10.0, -11.0, 12.0, -13.0); @@ -19133,7 +19133,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_cmul_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, -1.0, 8.0, -9.0, 10.0, -11.0, 12.0, -13.0); @@ -19145,7 +19145,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_cmul_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, -1.0, 8.0, -9.0, 10.0, -11.0, 12.0, -13.0); @@ -19304,7 +19304,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_fcmul_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, -1.0, 8.0, -9.0, 10.0, -11.0, 12.0, -13.0); @@ -19313,7 +19313,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_fcmul_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, -1.0, 8.0, -9.0, 10.0, -11.0, 12.0, -13.0); @@ -19323,7 +19323,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_fcmul_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, -1.0, 8.0, -9.0, 10.0, -11.0, 12.0, -13.0); @@ -19332,7 +19332,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_fcmul_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, -1.0, 8.0, -9.0, 10.0, -11.0, 12.0, -13.0); @@ -19341,7 +19341,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_fcmul_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, -1.0, 8.0, -9.0, 10.0, -11.0, 12.0, -13.0); @@ -19353,7 +19353,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_fcmul_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, -1.0, 8.0, -9.0, 10.0, -11.0, 12.0, -13.0); @@ -19692,7 +19692,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_fmadd_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 2.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -19702,7 +19702,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_fmadd_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 2.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -19715,7 +19715,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask3_fmadd_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 2.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -19728,7 +19728,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_fmadd_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 2.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -19741,7 +19741,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_fmadd_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 2.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -19751,7 +19751,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_fmadd_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 2.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -19768,7 +19768,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask3_fmadd_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 2.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -19785,7 +19785,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_fmadd_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 2.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -20002,7 +20002,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_fcmadd_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 2.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -20012,7 +20012,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_fcmadd_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 2.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -20025,7 +20025,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask3_fcmadd_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 2.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -20038,7 +20038,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_fcmadd_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 2.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -20051,7 +20051,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_fcmadd_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 2.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -20061,7 +20061,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_fcmadd_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 2.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -20078,7 +20078,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask3_fcmadd_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 2.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -20095,7 +20095,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_fcmadd_round_sch() { let a = _mm_setr_ph(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0); let b = _mm_setr_ph(0.0, 2.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0); @@ -20311,7 +20311,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_fmadd_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20321,7 +20321,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_fmadd_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20334,7 +20334,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask3_fmadd_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20347,7 +20347,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_fmadd_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20360,7 +20360,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_fmadd_round_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20370,7 +20370,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_fmadd_round_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20387,7 +20387,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask3_fmadd_round_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20404,7 +20404,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_fmadd_round_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20620,7 +20620,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_fmsub_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20630,7 +20630,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_fmsub_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20643,7 +20643,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask3_fmsub_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20656,7 +20656,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_fmsub_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20669,7 +20669,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_fmsub_round_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20930,7 +20930,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_fnmadd_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20940,7 +20940,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_fnmadd_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20953,7 +20953,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask3_fnmadd_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20966,7 +20966,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_fnmadd_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20979,7 +20979,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_fnmadd_round_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -20989,7 +20989,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_fnmadd_round_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -21006,7 +21006,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask3_fnmadd_round_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -21023,7 +21023,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_fnmadd_round_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -21240,7 +21240,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_fnmsub_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -21250,7 +21250,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_fnmsub_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -21263,7 +21263,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask3_fnmsub_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -21276,7 +21276,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_fnmsub_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -21289,7 +21289,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_fnmsub_round_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -21299,7 +21299,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_fnmsub_round_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -21316,7 +21316,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask3_fnmsub_round_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -21333,7 +21333,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_fnmsub_round_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(2.0, 20., 21., 22., 23., 24., 25., 26.); @@ -21851,7 +21851,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_rcp_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(2.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0); @@ -21860,7 +21860,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_rcp_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(2.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0); @@ -21873,7 +21873,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_rcp_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(2.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0); @@ -21970,7 +21970,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_rsqrt_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(4.0, 40.0, 41.0, 42.0, 43.0, 44.0, 45.0, 46.0); @@ -21979,7 +21979,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_rsqrt_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(4.0, 40.0, 41.0, 42.0, 43.0, 44.0, 45.0, 46.0); @@ -21992,7 +21992,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_rsqrt_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(4.0, 40.0, 41.0, 42.0, 43.0, 44.0, 45.0, 46.0); @@ -22127,7 +22127,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_sqrt_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(4.0, 40.0, 41.0, 42.0, 43.0, 44.0, 45.0, 46.0); @@ -22136,7 +22136,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_sqrt_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(4.0, 40.0, 41.0, 42.0, 43.0, 44.0, 45.0, 46.0); @@ -22149,7 +22149,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_sqrt_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(4.0, 40.0, 41.0, 42.0, 43.0, 44.0, 45.0, 46.0); @@ -22161,7 +22161,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_sqrt_round_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(4.0, 40.0, 41.0, 42.0, 43.0, 44.0, 45.0, 46.0); @@ -22170,7 +22170,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_sqrt_round_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(4.0, 40.0, 41.0, 42.0, 43.0, 44.0, 45.0, 46.0); @@ -22187,7 +22187,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_sqrt_round_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(4.0, 40.0, 41.0, 42.0, 43.0, 44.0, 45.0, 46.0); @@ -22338,7 +22338,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_max_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(2.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0); @@ -22347,7 +22347,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_max_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(2.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0); @@ -22360,7 +22360,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_max_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(2.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0); @@ -22372,7 +22372,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_max_round_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(2.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0); @@ -22381,7 +22381,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_max_round_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(2.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0); @@ -22398,7 +22398,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_max_round_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(2.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0); @@ -22549,7 +22549,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_min_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(2.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0); @@ -22558,7 +22558,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_min_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(2.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0); @@ -22571,7 +22571,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_min_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(2.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0); @@ -22583,7 +22583,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_min_round_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(2.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0); @@ -22592,7 +22592,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_min_round_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(2.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0); @@ -22609,7 +22609,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_min_round_sh() { let a = _mm_setr_ph(1.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0); let b = _mm_setr_ph(2.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0); @@ -22746,7 +22746,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_getexp_sh() { let a = _mm_setr_ph(4.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(3.0, 20., 21., 22., 23., 24., 25., 26.); @@ -22755,7 +22755,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_getexp_sh() { let a = _mm_setr_ph(4.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(3.0, 20., 21., 22., 23., 24., 25., 26.); @@ -22768,7 +22768,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_getexp_sh() { let a = _mm_setr_ph(4.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(3.0, 20., 21., 22., 23., 24., 25., 26.); @@ -22780,7 +22780,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_getexp_round_sh() { let a = _mm_setr_ph(4.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(3.0, 20., 21., 22., 23., 24., 25., 26.); @@ -22789,7 +22789,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_getexp_round_sh() { let a = _mm_setr_ph(4.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(3.0, 20., 21., 22., 23., 24., 25., 26.); @@ -22802,7 +22802,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_getexp_round_sh() { let a = _mm_setr_ph(4.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(3.0, 20., 21., 22., 23., 24., 25., 26.); @@ -22958,7 +22958,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_getmant_sh() { let a = _mm_setr_ph(15.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(10.0, 20., 21., 22., 23., 24., 25., 26.); @@ -22967,7 +22967,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_getmant_sh() { let a = _mm_setr_ph(15.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(10.0, 20., 21., 22., 23., 24., 25., 26.); @@ -22980,7 +22980,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_getmant_sh() { let a = _mm_setr_ph(15.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(10.0, 20., 21., 22., 23., 24., 25., 26.); @@ -22992,7 +22992,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_getmant_round_sh() { let a = _mm_setr_ph(15.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(10.0, 20., 21., 22., 23., 24., 25., 26.); @@ -23003,7 +23003,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_getmant_round_sh() { let a = _mm_setr_ph(15.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(10.0, 20., 21., 22., 23., 24., 25., 26.); @@ -23024,7 +23024,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_getmant_round_sh() { let a = _mm_setr_ph(15.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(10.0, 20., 21., 22., 23., 24., 25., 26.); @@ -23167,7 +23167,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_roundscale_sh() { let a = _mm_setr_ph(2.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(1.1, 20., 21., 22., 23., 24., 25., 26.); @@ -23176,7 +23176,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_roundscale_sh() { let a = _mm_setr_ph(2.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(1.1, 20., 21., 22., 23., 24., 25., 26.); @@ -23189,7 +23189,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_roundscale_sh() { let a = _mm_setr_ph(2.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(1.1, 20., 21., 22., 23., 24., 25., 26.); @@ -23201,7 +23201,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_roundscale_round_sh() { let a = _mm_setr_ph(2.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(1.1, 20., 21., 22., 23., 24., 25., 26.); @@ -23210,7 +23210,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_roundscale_round_sh() { let a = _mm_setr_ph(2.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(1.1, 20., 21., 22., 23., 24., 25., 26.); @@ -23223,7 +23223,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_roundscale_round_sh() { let a = _mm_setr_ph(2.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(1.1, 20., 21., 22., 23., 24., 25., 26.); @@ -23372,7 +23372,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_scalef_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(3.0, 20., 21., 22., 23., 24., 25., 26.); @@ -23381,7 +23381,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_scalef_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(3.0, 20., 21., 22., 23., 24., 25., 26.); @@ -23394,7 +23394,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_scalef_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(3.0, 20., 21., 22., 23., 24., 25., 26.); @@ -23406,7 +23406,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_scalef_round_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(3.0, 20., 21., 22., 23., 24., 25., 26.); @@ -23415,7 +23415,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_scalef_round_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(3.0, 20., 21., 22., 23., 24., 25., 26.); @@ -23432,7 +23432,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_scalef_round_sh() { let a = _mm_setr_ph(1.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(3.0, 20., 21., 22., 23., 24., 25., 26.); @@ -23576,7 +23576,7 @@ mod tests { assert_eq_m512h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_reduce_sh() { let a = _mm_setr_ph(3.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(1.25, 20., 21., 22., 23., 24., 25., 26.); @@ -23585,7 +23585,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_reduce_sh() { let a = _mm_setr_ph(3.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(1.25, 20., 21., 22., 23., 24., 25., 26.); @@ -23598,7 +23598,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_reduce_sh() { let a = _mm_setr_ph(3.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(1.25, 20., 21., 22., 23., 24., 25., 26.); @@ -23610,7 +23610,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_reduce_round_sh() { let a = _mm_setr_ph(3.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(1.25, 20., 21., 22., 23., 24., 25., 26.); @@ -23619,7 +23619,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_reduce_round_sh() { let a = _mm_setr_ph(3.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(1.25, 20., 21., 22., 23., 24., 25., 26.); @@ -23636,7 +23636,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_reduce_round_sh() { let a = _mm_setr_ph(3.0, 10., 11., 12., 13., 14., 15., 16.); let b = _mm_setr_ph(1.25, 20., 21., 22., 23., 24., 25., 26.); @@ -24505,7 +24505,7 @@ mod tests { assert_eq_m256h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_cvti32_sh() { let a = _mm_setr_ph(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0); let r = _mm_cvti32_sh(a, 10); @@ -24513,7 +24513,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_cvt_roundi32_sh() { let a = _mm_setr_ph(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0); let r = _mm_cvt_roundi32_sh::<{ _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC }>(a, 10); @@ -24645,7 +24645,7 @@ mod tests { assert_eq_m256h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_cvtu32_sh() { let a = _mm_setr_ph(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0); let r = _mm_cvtu32_sh(a, 10); @@ -24653,7 +24653,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_cvt_roundu32_sh() { let a = _mm_setr_ph(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0); let r = _mm_cvt_roundu32_sh::<{ _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC }>(a, 10); @@ -24711,7 +24711,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm512_cvtepi64_ph() { let a = _mm512_set_epi64(1, 2, 3, 4, 5, 6, 7, 8); let r = _mm512_cvtepi64_ph(a); @@ -24719,7 +24719,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm512_mask_cvtepi64_ph() { let a = _mm512_set_epi64(1, 2, 3, 4, 5, 6, 7, 8); let src = _mm_set_ph(10., 11., 12., 13., 14., 15., 16., 17.); @@ -24728,7 +24728,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm512_maskz_cvtepi64_ph() { let a = _mm512_set_epi64(1, 2, 3, 4, 5, 6, 7, 8); let r = _mm512_maskz_cvtepi64_ph(0b01010101, a); @@ -24736,7 +24736,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm512_cvt_roundepi64_ph() { let a = _mm512_set_epi64(1, 2, 3, 4, 5, 6, 7, 8); let r = _mm512_cvt_roundepi64_ph::<{ _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC }>(a); @@ -24755,7 +24755,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm512_maskz_cvt_roundepi64_ph() { let a = _mm512_set_epi64(1, 2, 3, 4, 5, 6, 7, 8); let r = _mm512_maskz_cvt_roundepi64_ph::<{ _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC }>( @@ -24815,7 +24815,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm512_cvtepu64_ph() { let a = _mm512_set_epi64(1, 2, 3, 4, 5, 6, 7, 8); let r = _mm512_cvtepu64_ph(a); @@ -24823,7 +24823,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm512_mask_cvtepu64_ph() { let a = _mm512_set_epi64(1, 2, 3, 4, 5, 6, 7, 8); let src = _mm_set_ph(10., 11., 12., 13., 14., 15., 16., 17.); @@ -24832,7 +24832,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm512_maskz_cvtepu64_ph() { let a = _mm512_set_epi64(1, 2, 3, 4, 5, 6, 7, 8); let r = _mm512_maskz_cvtepu64_ph(0b01010101, a); @@ -24840,7 +24840,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm512_cvt_roundepu64_ph() { let a = _mm512_set_epi64(1, 2, 3, 4, 5, 6, 7, 8); let r = _mm512_cvt_roundepu64_ph::<{ _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC }>(a); @@ -24848,7 +24848,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm512_mask_cvt_roundepu64_ph() { let a = _mm512_set_epi64(1, 2, 3, 4, 5, 6, 7, 8); let src = _mm_set_ph(10., 11., 12., 13., 14., 15., 16., 17.); @@ -24859,7 +24859,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm512_maskz_cvt_roundepu64_ph() { let a = _mm512_set_epi64(1, 2, 3, 4, 5, 6, 7, 8); let r = _mm512_maskz_cvt_roundepu64_ph::<{ _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC }>( @@ -25005,7 +25005,7 @@ mod tests { assert_eq_m256h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_cvtss_sh() { let a = _mm_setr_ph(10., 11., 12., 13., 14., 15., 16., 17.); let b = _mm_setr_ps(1.0, 2.0, 3.0, 4.0); @@ -25014,7 +25014,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_cvtss_sh() { let a = _mm_setr_ph(10., 11., 12., 13., 14., 15., 16., 17.); let b = _mm_setr_ps(1.0, 2.0, 3.0, 4.0); @@ -25027,7 +25027,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_cvtss_sh() { let a = _mm_setr_ph(10., 11., 12., 13., 14., 15., 16., 17.); let b = _mm_setr_ps(1.0, 2.0, 3.0, 4.0); @@ -25039,7 +25039,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_cvt_roundss_sh() { let a = _mm_setr_ph(10., 11., 12., 13., 14., 15., 16., 17.); let b = _mm_setr_ps(1.0, 2.0, 3.0, 4.0); @@ -25048,7 +25048,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_cvt_roundss_sh() { let a = _mm_setr_ph(10., 11., 12., 13., 14., 15., 16., 17.); let b = _mm_setr_ps(1.0, 2.0, 3.0, 4.0); @@ -25065,7 +25065,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_cvt_roundss_sh() { let a = _mm_setr_ph(10., 11., 12., 13., 14., 15., 16., 17.); let b = _mm_setr_ps(1.0, 2.0, 3.0, 4.0); @@ -25129,7 +25129,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm512_cvtpd_ph() { let a = _mm512_set_pd(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0); let r = _mm512_cvtpd_ph(a); @@ -25137,7 +25137,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm512_mask_cvtpd_ph() { let a = _mm512_set_pd(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0); let src = _mm_set_ph(10., 11., 12., 13., 14., 15., 16., 17.); @@ -25146,7 +25146,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm512_maskz_cvtpd_ph() { let a = _mm512_set_pd(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0); let r = _mm512_maskz_cvtpd_ph(0b01010101, a); @@ -25154,7 +25154,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm512_cvt_roundpd_ph() { let a = _mm512_set_pd(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0); let r = _mm512_cvt_roundpd_ph::<{ _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC }>(a); @@ -25162,7 +25162,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm512_mask_cvt_roundpd_ph() { let a = _mm512_set_pd(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0); let src = _mm_set_ph(10., 11., 12., 13., 14., 15., 16., 17.); @@ -25173,7 +25173,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm512_maskz_cvt_roundpd_ph() { let a = _mm512_set_pd(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0); let r = _mm512_maskz_cvt_roundpd_ph::<{ _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC }>( @@ -25183,7 +25183,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_cvtsd_sh() { let a = _mm_setr_ph(10., 11., 12., 13., 14., 15., 16., 17.); let b = _mm_setr_pd(1.0, 2.0); @@ -25192,7 +25192,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_cvtsd_sh() { let a = _mm_setr_ph(10., 11., 12., 13., 14., 15., 16., 17.); let b = _mm_setr_pd(1.0, 2.0); @@ -25205,7 +25205,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_cvtsd_sh() { let a = _mm_setr_ph(10., 11., 12., 13., 14., 15., 16., 17.); let b = _mm_setr_pd(1.0, 2.0); @@ -25217,7 +25217,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_cvt_roundsd_sh() { let a = _mm_setr_ph(10., 11., 12., 13., 14., 15., 16., 17.); let b = _mm_setr_pd(1.0, 2.0); @@ -25226,7 +25226,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_mask_cvt_roundsd_sh() { let a = _mm_setr_ph(10., 11., 12., 13., 14., 15., 16., 17.); let b = _mm_setr_pd(1.0, 2.0); @@ -25243,7 +25243,7 @@ mod tests { assert_eq_m128h(r, e); } - #[simd_test(enable = "avx512fp16")] + #[simd_test(enable = "avx512fp16,avx512vl")] unsafe fn test_mm_maskz_cvt_roundsd_sh() { let a = _mm_setr_ph(10., 11., 12., 13., 14., 15., 16., 17.); let b = _mm_setr_pd(1.0, 2.0); From 92f6310890e16790eb44abcb33cffa0f8090e0c3 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 20 Aug 2025 11:00:47 +0200 Subject: [PATCH 101/682] Work around selection failure without avx512vl --- library/stdarch/crates/core_arch/src/x86/avx512f.rs | 8 ++++++-- library/stdarch/crates/core_arch/src/x86_64/avx512f.rs | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/x86/avx512f.rs b/library/stdarch/crates/core_arch/src/x86/avx512f.rs index d53f83c0a10b..cad8ca0c63ce 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512f.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512f.rs @@ -49621,7 +49621,9 @@ mod tests { assert_eq_m512i(r, e); } - #[simd_test(enable = "avx512f")] + // FIXME(llvm): https://github.com/llvm/llvm-project/issues/154492 + //#[simd_test(enable = "avx512f")] + #[simd_test(enable = "avx512f,avx512vl")] unsafe fn test_mm512_mask_cvttps_epu32() { let a = _mm512_setr_ps( 0., -1.5, 2., -3.5, 4., -5.5, 6., -7.5, 8., 9.5, 10., 11.5, 12., 13.5, 14., 15.5, @@ -49634,7 +49636,9 @@ mod tests { assert_eq_m512i(r, e); } - #[simd_test(enable = "avx512f")] + // FIXME(llvm): https://github.com/llvm/llvm-project/issues/154492 + //#[simd_test(enable = "avx512f")] + #[simd_test(enable = "avx512f,avx512vl")] unsafe fn test_mm512_maskz_cvttps_epu32() { let a = _mm512_setr_ps( 0., -1.5, 2., -3.5, 4., -5.5, 6., -7.5, 8., 9.5, 10., 11.5, 12., 13.5, 14., 15.5, diff --git a/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs b/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs index 934c9e2812c4..a8dd340bd660 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs @@ -5311,7 +5311,9 @@ mod tests { assert_eq_m256i(r, e); } - #[simd_test(enable = "avx512f")] + // FIXME(llvm): https://github.com/llvm/llvm-project/issues/154492 + //#[simd_test(enable = "avx512f")] + #[simd_test(enable = "avx512f,avx512vl")] unsafe fn test_mm512_mask_cvttpd_epu32() { let a = _mm512_setr_pd(0., -1.5, 2., -3.5, 4., -5.5, 6., -7.5); let src = _mm256_set1_epi32(0); @@ -5322,7 +5324,9 @@ mod tests { assert_eq_m256i(r, e); } - #[simd_test(enable = "avx512f")] + // FIXME(llvm): https://github.com/llvm/llvm-project/issues/154492 + //#[simd_test(enable = "avx512f")] + #[simd_test(enable = "avx512f,avx512vl")] unsafe fn test_mm512_maskz_cvttpd_epu32() { let a = _mm512_setr_pd(0., -1.5, 2., -3.5, 4., -5.5, 6., -7.5); let r = _mm512_maskz_cvttpd_epu32(0, a); From 3302e3e09a2f843ba7a20102f0f5f18909bc0b47 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Wed, 20 Aug 2025 11:12:26 +0200 Subject: [PATCH 102/682] Adjust immediate for vrndscalepd tests The immediate here encodes both the rounding mode (in the low bits) and the scale (in the high bits). Make sure the scale is non-zero. --- library/stdarch/crates/core_arch/src/x86/avx512f.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/x86/avx512f.rs b/library/stdarch/crates/core_arch/src/x86/avx512f.rs index cad8ca0c63ce..0803f2f81b21 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512f.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512f.rs @@ -5823,7 +5823,7 @@ pub fn _mm512_maskz_roundscale_pd(k: __mmask8, a: __m512d) -> _ #[inline] #[target_feature(enable = "avx512f,avx512vl")] #[stable(feature = "stdarch_x86_avx512", since = "1.89")] -#[cfg_attr(test, assert_instr(vrndscalepd, IMM8 = 0))] +#[cfg_attr(test, assert_instr(vrndscalepd, IMM8 = 16))] #[rustc_legacy_const_generics(1)] pub fn _mm256_roundscale_pd(a: __m256d) -> __m256d { unsafe { @@ -5897,7 +5897,7 @@ pub fn _mm256_maskz_roundscale_pd(k: __mmask8, a: __m256d) -> _ #[inline] #[target_feature(enable = "avx512f,avx512vl")] #[stable(feature = "stdarch_x86_avx512", since = "1.89")] -#[cfg_attr(test, assert_instr(vrndscalepd, IMM8 = 0))] +#[cfg_attr(test, assert_instr(vrndscalepd, IMM8 = 16))] #[rustc_legacy_const_generics(1)] pub fn _mm_roundscale_pd(a: __m128d) -> __m128d { unsafe { From fa163a1fcaa1d46a62d4a19b9354da5583dbf5d1 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 20 Aug 2025 18:02:12 +0200 Subject: [PATCH 103/682] s390x: define `unpack_low` using `core::intrinsics::simd` --- .../crates/core_arch/src/s390x/vector.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/s390x/vector.rs b/library/stdarch/crates/core_arch/src/s390x/vector.rs index 9927ff2c6255..2969516c9ae4 100644 --- a/library/stdarch/crates/core_arch/src/s390x/vector.rs +++ b/library/stdarch/crates/core_arch/src/s390x/vector.rs @@ -169,13 +169,6 @@ unsafe extern "unadjusted" { #[link_name = "llvm.s390.vpklsfs"] fn vpklsfs(a: vector_unsigned_int, b: vector_unsigned_int) -> PackedTuple; #[link_name = "llvm.s390.vpklsgs"] fn vpklsgs(a: vector_unsigned_long_long, b: vector_unsigned_long_long) -> PackedTuple; - #[link_name = "llvm.s390.vuplb"] fn vuplb (a: vector_signed_char) -> vector_signed_short; - #[link_name = "llvm.s390.vuplhw"] fn vuplhw (a: vector_signed_short) -> vector_signed_int; - #[link_name = "llvm.s390.vuplf"] fn vuplf (a: vector_signed_int) -> vector_signed_long_long; - #[link_name = "llvm.s390.vupllb"] fn vupllb (a: vector_unsigned_char) -> vector_unsigned_short; - #[link_name = "llvm.s390.vupllh"] fn vupllh (a: vector_unsigned_short) -> vector_unsigned_int; - #[link_name = "llvm.s390.vupllf"] fn vupllf (a: vector_unsigned_int) -> vector_unsigned_long_long; - #[link_name = "llvm.s390.vavgb"] fn vavgb(a: vector_signed_char, b: vector_signed_char) -> vector_signed_char; #[link_name = "llvm.s390.vavgh"] fn vavgh(a: vector_signed_short, b: vector_signed_short) -> vector_signed_short; #[link_name = "llvm.s390.vavgf"] fn vavgf(a: vector_signed_int, b: vector_signed_int) -> vector_signed_int; @@ -2583,8 +2576,14 @@ mod sealed { unsafe fn vec_unpackl(self) -> Self::Result; } - // FIXME(llvm): a shuffle + simd_as does not currently optimize into a single instruction like - // unpachk above. Tracked in https://github.com/llvm/llvm-project/issues/129576. + // NOTE: `vuplh` is used for "unpack logical high", hence `vuplhw`. + impl_vec_unpack!(unpack_low vuplb vector_signed_char i8x8 vector_signed_short 8); + impl_vec_unpack!(unpack_low vuplhw vector_signed_short i16x4 vector_signed_int 4); + impl_vec_unpack!(unpack_low vuplf vector_signed_int i32x2 vector_signed_long_long 2); + + impl_vec_unpack!(unpack_low vupllb vector_unsigned_char u8x8 vector_unsigned_short 8); + impl_vec_unpack!(unpack_low vupllh vector_unsigned_short u16x4 vector_unsigned_int 4); + impl_vec_unpack!(unpack_low vupllf vector_unsigned_int u32x2 vector_unsigned_long_long 2); impl_vec_trait! {[VectorUnpackl vec_unpackl] vuplb (vector_signed_char) -> vector_signed_short} impl_vec_trait! {[VectorUnpackl vec_unpackl] vuplhw (vector_signed_short) -> vector_signed_int} From c5ec0960f0a1d78584b4957dd71613bdbcb91de8 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 20 Aug 2025 18:03:04 +0200 Subject: [PATCH 104/682] s390x: add `assert_instr` for `vec_round` --- library/stdarch/crates/core_arch/src/s390x/vector.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/s390x/vector.rs b/library/stdarch/crates/core_arch/src/s390x/vector.rs index 2969516c9ae4..066ae1607316 100644 --- a/library/stdarch/crates/core_arch/src/s390x/vector.rs +++ b/library/stdarch/crates/core_arch/src/s390x/vector.rs @@ -1194,10 +1194,8 @@ mod sealed { test_impl! { vec_roundc_f32 (a: vector_float) -> vector_float [nearbyint_v4f32, "vector-enhancements-1" vfisb] } test_impl! { vec_roundc_f64 (a: vector_double) -> vector_double [nearbyint_v2f64, vfidb] } - // FIXME(llvm) llvm trunk already lowers roundeven to vfidb, but rust does not use it yet - // use https://godbolt.org/z/cWq95fexe to check, and enable the instruction test when it works - test_impl! { vec_round_f32 (a: vector_float) -> vector_float [roundeven_v4f32, _] } - test_impl! { vec_round_f64 (a: vector_double) -> vector_double [roundeven_v2f64, _] } + test_impl! { vec_round_f32 (a: vector_float) -> vector_float [roundeven_v4f32, "vector-enhancements-1" vfisb] } + test_impl! { vec_round_f64 (a: vector_double) -> vector_double [roundeven_v2f64, vfidb] } #[unstable(feature = "stdarch_s390x", issue = "135681")] pub trait VectorRoundc { From 97d64665b9e4e390cd3cadf403de5cfa67b3216a Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 20 Aug 2025 18:03:41 +0200 Subject: [PATCH 105/682] s390x: add `assert_instr` for `vec_extend` --- library/stdarch/crates/core_arch/src/s390x/vector.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/s390x/vector.rs b/library/stdarch/crates/core_arch/src/s390x/vector.rs index 066ae1607316..b010b7e38267 100644 --- a/library/stdarch/crates/core_arch/src/s390x/vector.rs +++ b/library/stdarch/crates/core_arch/src/s390x/vector.rs @@ -3319,8 +3319,7 @@ mod sealed { #[inline] #[target_feature(enable = "vector")] - // FIXME(llvm): https://github.com/llvm/llvm-project/issues/129899 - // #[cfg_attr(test, assert_instr(vsegb))] + #[cfg_attr(test, assert_instr(vsegb))] pub unsafe fn vsegb(a: vector_signed_char) -> vector_signed_long_long { simd_as(simd_shuffle::<_, _, i8x2>( a, @@ -3331,8 +3330,7 @@ mod sealed { #[inline] #[target_feature(enable = "vector")] - // FIXME(llvm): https://github.com/llvm/llvm-project/issues/129899 - // #[cfg_attr(test, assert_instr(vsegh))] + #[cfg_attr(test, assert_instr(vsegh))] pub unsafe fn vsegh(a: vector_signed_short) -> vector_signed_long_long { simd_as(simd_shuffle::<_, _, i16x2>( a, @@ -3343,8 +3341,7 @@ mod sealed { #[inline] #[target_feature(enable = "vector")] - // FIXME(llvm): https://github.com/llvm/llvm-project/issues/129899 - // #[cfg_attr(test, assert_instr(vsegf))] + #[cfg_attr(test, assert_instr(vsegf))] pub unsafe fn vsegf(a: vector_signed_int) -> vector_signed_long_long { simd_as(simd_shuffle::<_, _, i32x2>( a, From 1cda88aca15307dbcd1f6404875999373105a93d Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 20 Aug 2025 18:04:15 +0200 Subject: [PATCH 106/682] s390x: implement `vec_mule` using `core::intrinsics::simd` --- .../crates/core_arch/src/s390x/vector.rs | 85 ++++++++++--------- 1 file changed, 47 insertions(+), 38 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/s390x/vector.rs b/library/stdarch/crates/core_arch/src/s390x/vector.rs index b010b7e38267..2af5edb9fb00 100644 --- a/library/stdarch/crates/core_arch/src/s390x/vector.rs +++ b/library/stdarch/crates/core_arch/src/s390x/vector.rs @@ -181,14 +181,6 @@ unsafe extern "unadjusted" { #[link_name = "llvm.s390.vcksm"] fn vcksm(a: vector_unsigned_int, b: vector_unsigned_int) -> vector_unsigned_int; - #[link_name = "llvm.s390.vmeb"] fn vmeb(a: vector_signed_char, b: vector_signed_char) -> vector_signed_short; - #[link_name = "llvm.s390.vmeh"] fn vmeh(a: vector_signed_short, b: vector_signed_short) -> vector_signed_int; - #[link_name = "llvm.s390.vmef"] fn vmef(a: vector_signed_int, b: vector_signed_int) -> vector_signed_long_long; - - #[link_name = "llvm.s390.vmleb"] fn vmleb(a: vector_unsigned_char, b: vector_unsigned_char) -> vector_unsigned_short; - #[link_name = "llvm.s390.vmleh"] fn vmleh(a: vector_unsigned_short, b: vector_unsigned_short) -> vector_unsigned_int; - #[link_name = "llvm.s390.vmlef"] fn vmlef(a: vector_unsigned_int, b: vector_unsigned_int) -> vector_unsigned_long_long; - #[link_name = "llvm.s390.vmob"] fn vmob(a: vector_signed_char, b: vector_signed_char) -> vector_signed_short; #[link_name = "llvm.s390.vmoh"] fn vmoh(a: vector_signed_short, b: vector_signed_short) -> vector_signed_int; #[link_name = "llvm.s390.vmof"] fn vmof(a: vector_signed_int, b: vector_signed_int) -> vector_signed_long_long; @@ -372,6 +364,19 @@ impl ShuffleMask { ShuffleMask(mask) } + const fn even() -> Self { + let mut mask = [0; N]; + let mut i = 0; + let mut index = 0; + while index < N { + mask[index] = i as u32; + + i += 2; + index += 1; + } + ShuffleMask(mask) + } + const fn pack() -> Self { let mut mask = [0; N]; let mut i = 1; @@ -2642,40 +2647,44 @@ mod sealed { unsafe fn vec_mule(self, b: Self) -> Result; } - // FIXME(llvm) sadly this does not yet work https://github.com/llvm/llvm-project/issues/129705 - // #[target_feature(enable = "vector")] - // #[cfg_attr(test, assert_instr(vmleh))] - // unsafe fn vec_vmleh(a: vector_unsigned_short, b: vector_unsigned_short) -> vector_unsigned_int { - // let even_a: vector_unsigned_int = simd_as(simd_shuffle::<_, _, u16x4>( - // a, - // a, - // const { ShuffleMask([0, 2, 4, 6]) }, - // )); - // - // let even_b: vector_unsigned_int = simd_as(simd_shuffle::<_, _, u16x4>( - // b, - // b, - // const { ShuffleMask([0, 2, 4, 6]) }, - // )); - // - // simd_mul(even_a, even_b) - // } + macro_rules! impl_vec_mule { + ($instr:ident $src:ident $shuffled:ident $dst:ident $width:literal) => { + #[inline] + #[target_feature(enable = "vector")] + #[cfg_attr(test, assert_instr($instr))] + unsafe fn $instr(a: $src, b: $src) -> $dst { + let even_a: $dst = simd_as(simd_shuffle::<_, _, $shuffled>( + a, + a, + const { ShuffleMask::<$width>::even() }, + )); - test_impl! { vec_vmeb(a: vector_signed_char, b: vector_signed_char) -> vector_signed_short [ vmeb, vmeb ] } - test_impl! { vec_vmeh(a: vector_signed_short, b: vector_signed_short) -> vector_signed_int[ vmeh, vmeh ] } - test_impl! { vec_vmef(a: vector_signed_int, b: vector_signed_int) -> vector_signed_long_long [ vmef, vmef ] } + let even_b: $dst = simd_as(simd_shuffle::<_, _, $shuffled>( + b, + b, + const { ShuffleMask::<$width>::even() }, + )); - test_impl! { vec_vmleb(a: vector_unsigned_char, b: vector_unsigned_char) -> vector_unsigned_short [ vmleb, vmleb ] } - test_impl! { vec_vmleh(a: vector_unsigned_short, b: vector_unsigned_short) -> vector_unsigned_int[ vmleh, vmleh ] } - test_impl! { vec_vmlef(a: vector_unsigned_int, b: vector_unsigned_int) -> vector_unsigned_long_long [ vmlef, vmlef ] } + simd_mul(even_a, even_b) + } + }; + } - impl_mul!([VectorMule vec_mule] vec_vmeb (vector_signed_char, vector_signed_char) -> vector_signed_short ); - impl_mul!([VectorMule vec_mule] vec_vmeh (vector_signed_short, vector_signed_short) -> vector_signed_int); - impl_mul!([VectorMule vec_mule] vec_vmef (vector_signed_int, vector_signed_int) -> vector_signed_long_long ); + impl_vec_mule! { vmeb vector_signed_char i8x8 vector_signed_short 8 } + impl_vec_mule! { vmeh vector_signed_short i16x4 vector_signed_int 4 } + impl_vec_mule! { vmef vector_signed_int i32x2 vector_signed_long_long 2 } - impl_mul!([VectorMule vec_mule] vec_vmleb (vector_unsigned_char, vector_unsigned_char) -> vector_unsigned_short ); - impl_mul!([VectorMule vec_mule] vec_vmleh (vector_unsigned_short, vector_unsigned_short) -> vector_unsigned_int); - impl_mul!([VectorMule vec_mule] vec_vmlef (vector_unsigned_int, vector_unsigned_int) -> vector_unsigned_long_long ); + impl_vec_mule! { vmleb vector_unsigned_char u8x8 vector_unsigned_short 8 } + impl_vec_mule! { vmleh vector_unsigned_short u16x4 vector_unsigned_int 4 } + impl_vec_mule! { vmlef vector_unsigned_int u32x2 vector_unsigned_long_long 2 } + + impl_mul!([VectorMule vec_mule] vmeb (vector_signed_char, vector_signed_char) -> vector_signed_short ); + impl_mul!([VectorMule vec_mule] vmeh (vector_signed_short, vector_signed_short) -> vector_signed_int); + impl_mul!([VectorMule vec_mule] vmef (vector_signed_int, vector_signed_int) -> vector_signed_long_long ); + + impl_mul!([VectorMule vec_mule] vmleb (vector_unsigned_char, vector_unsigned_char) -> vector_unsigned_short ); + impl_mul!([VectorMule vec_mule] vmleh (vector_unsigned_short, vector_unsigned_short) -> vector_unsigned_int); + impl_mul!([VectorMule vec_mule] vmlef (vector_unsigned_int, vector_unsigned_int) -> vector_unsigned_long_long ); #[unstable(feature = "stdarch_s390x", issue = "135681")] pub trait VectorMulo { From d5cb1c49fa415b512c0616730193467fd5fe2a9a Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 14 Aug 2025 18:51:39 +0200 Subject: [PATCH 107/682] wasm: use `{read, write}_unaligned` methods --- .../crates/core_arch/src/wasm32/simd128.rs | 38 +++++++------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/wasm32/simd128.rs b/library/stdarch/crates/core_arch/src/wasm32/simd128.rs index 108bc3125c5f..c864d6a516e0 100644 --- a/library/stdarch/crates/core_arch/src/wasm32/simd128.rs +++ b/library/stdarch/crates/core_arch/src/wasm32/simd128.rs @@ -141,16 +141,6 @@ unsafe extern "unadjusted" { fn llvm_f64x2_max(x: simd::f64x2, y: simd::f64x2) -> simd::f64x2; } -#[repr(C, packed)] -#[derive(Copy)] -struct Unaligned(T); - -impl Clone for Unaligned { - fn clone(&self) -> Unaligned { - *self - } -} - /// Loads a `v128` vector from the given heap address. /// /// This intrinsic will emit a load with an alignment of 1. While this is @@ -179,7 +169,7 @@ impl Clone for Unaligned { #[doc(alias("v128.load"))] #[stable(feature = "wasm_simd", since = "1.54.0")] pub unsafe fn v128_load(m: *const v128) -> v128 { - (*(m as *const Unaligned)).0 + m.read_unaligned() } /// Load eight 8-bit integers and sign extend each one to a 16-bit lane @@ -196,8 +186,8 @@ pub unsafe fn v128_load(m: *const v128) -> v128 { #[doc(alias("v128.load8x8_s"))] #[stable(feature = "wasm_simd", since = "1.54.0")] pub unsafe fn i16x8_load_extend_i8x8(m: *const i8) -> v128 { - let m = *(m as *const Unaligned); - simd_cast::<_, simd::i16x8>(m.0).v128() + let m = m.cast::().read_unaligned(); + simd_cast::<_, simd::i16x8>(m).v128() } /// Load eight 8-bit integers and zero extend each one to a 16-bit lane @@ -214,8 +204,8 @@ pub unsafe fn i16x8_load_extend_i8x8(m: *const i8) -> v128 { #[doc(alias("v128.load8x8_u"))] #[stable(feature = "wasm_simd", since = "1.54.0")] pub unsafe fn i16x8_load_extend_u8x8(m: *const u8) -> v128 { - let m = *(m as *const Unaligned); - simd_cast::<_, simd::u16x8>(m.0).v128() + let m = m.cast::().read_unaligned(); + simd_cast::<_, simd::u16x8>(m).v128() } #[stable(feature = "wasm_simd", since = "1.54.0")] @@ -235,8 +225,8 @@ pub use i16x8_load_extend_u8x8 as u16x8_load_extend_u8x8; #[doc(alias("v128.load16x4_s"))] #[stable(feature = "wasm_simd", since = "1.54.0")] pub unsafe fn i32x4_load_extend_i16x4(m: *const i16) -> v128 { - let m = *(m as *const Unaligned); - simd_cast::<_, simd::i32x4>(m.0).v128() + let m = m.cast::().read_unaligned(); + simd_cast::<_, simd::i32x4>(m).v128() } /// Load four 16-bit integers and zero extend each one to a 32-bit lane @@ -253,8 +243,8 @@ pub unsafe fn i32x4_load_extend_i16x4(m: *const i16) -> v128 { #[doc(alias("v128.load16x4_u"))] #[stable(feature = "wasm_simd", since = "1.54.0")] pub unsafe fn i32x4_load_extend_u16x4(m: *const u16) -> v128 { - let m = *(m as *const Unaligned); - simd_cast::<_, simd::u32x4>(m.0).v128() + let m = m.cast::().read_unaligned(); + simd_cast::<_, simd::u32x4>(m).v128() } #[stable(feature = "wasm_simd", since = "1.54.0")] @@ -274,8 +264,8 @@ pub use i32x4_load_extend_u16x4 as u32x4_load_extend_u16x4; #[doc(alias("v128.load32x2_s"))] #[stable(feature = "wasm_simd", since = "1.54.0")] pub unsafe fn i64x2_load_extend_i32x2(m: *const i32) -> v128 { - let m = *(m as *const Unaligned); - simd_cast::<_, simd::i64x2>(m.0).v128() + let m = m.cast::().read_unaligned(); + simd_cast::<_, simd::i64x2>(m).v128() } /// Load two 32-bit integers and zero extend each one to a 64-bit lane @@ -292,8 +282,8 @@ pub unsafe fn i64x2_load_extend_i32x2(m: *const i32) -> v128 { #[doc(alias("v128.load32x2_u"))] #[stable(feature = "wasm_simd", since = "1.54.0")] pub unsafe fn i64x2_load_extend_u32x2(m: *const u32) -> v128 { - let m = *(m as *const Unaligned); - simd_cast::<_, simd::u64x2>(m.0).v128() + let m = m.cast::().read_unaligned(); + simd_cast::<_, simd::u64x2>(m).v128() } #[stable(feature = "wasm_simd", since = "1.54.0")] @@ -453,7 +443,7 @@ pub unsafe fn v128_load64_zero(m: *const u64) -> v128 { #[doc(alias("v128.store"))] #[stable(feature = "wasm_simd", since = "1.54.0")] pub unsafe fn v128_store(m: *mut v128, a: v128) { - *(m as *mut Unaligned) = Unaligned(a); + m.write_unaligned(a) } /// Loads an 8-bit value from `m` and sets lane `L` of `v` to that value. From e1a1b1ded2d14c2bb40d236a93ad629222db4da2 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 20 Aug 2025 21:18:39 +0200 Subject: [PATCH 108/682] s390x: implement `vec_mulo` using `core::intrinsics::simd` --- .../crates/core_arch/src/s390x/vector.rs | 68 +++++++++---------- 1 file changed, 32 insertions(+), 36 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/s390x/vector.rs b/library/stdarch/crates/core_arch/src/s390x/vector.rs index 2af5edb9fb00..677522578892 100644 --- a/library/stdarch/crates/core_arch/src/s390x/vector.rs +++ b/library/stdarch/crates/core_arch/src/s390x/vector.rs @@ -181,14 +181,6 @@ unsafe extern "unadjusted" { #[link_name = "llvm.s390.vcksm"] fn vcksm(a: vector_unsigned_int, b: vector_unsigned_int) -> vector_unsigned_int; - #[link_name = "llvm.s390.vmob"] fn vmob(a: vector_signed_char, b: vector_signed_char) -> vector_signed_short; - #[link_name = "llvm.s390.vmoh"] fn vmoh(a: vector_signed_short, b: vector_signed_short) -> vector_signed_int; - #[link_name = "llvm.s390.vmof"] fn vmof(a: vector_signed_int, b: vector_signed_int) -> vector_signed_long_long; - - #[link_name = "llvm.s390.vmlob"] fn vmlob(a: vector_unsigned_char, b: vector_unsigned_char) -> vector_unsigned_short; - #[link_name = "llvm.s390.vmloh"] fn vmloh(a: vector_unsigned_short, b: vector_unsigned_short) -> vector_unsigned_int; - #[link_name = "llvm.s390.vmlof"] fn vmlof(a: vector_unsigned_int, b: vector_unsigned_int) -> vector_unsigned_long_long; - #[link_name = "llvm.s390.vmhb"] fn vmhb(a: vector_signed_char, b: vector_signed_char) -> vector_signed_char; #[link_name = "llvm.s390.vmhh"] fn vmhh(a: vector_signed_short, b: vector_signed_short) -> vector_signed_short; #[link_name = "llvm.s390.vmhf"] fn vmhf(a: vector_signed_int, b: vector_signed_int) -> vector_signed_int; @@ -377,7 +369,7 @@ impl ShuffleMask { ShuffleMask(mask) } - const fn pack() -> Self { + const fn odd() -> Self { let mut mask = [0; N]; let mut i = 1; let mut index = 0; @@ -390,6 +382,10 @@ impl ShuffleMask { ShuffleMask(mask) } + const fn pack() -> Self { + Self::odd() + } + const fn unpack_low() -> Self { let mut mask = [0; N]; let mut i = 0; @@ -2647,36 +2643,36 @@ mod sealed { unsafe fn vec_mule(self, b: Self) -> Result; } - macro_rules! impl_vec_mule { - ($instr:ident $src:ident $shuffled:ident $dst:ident $width:literal) => { + macro_rules! impl_vec_mul_even_odd { + ($mask:ident $instr:ident $src:ident $shuffled:ident $dst:ident $width:literal) => { #[inline] #[target_feature(enable = "vector")] #[cfg_attr(test, assert_instr($instr))] unsafe fn $instr(a: $src, b: $src) -> $dst { - let even_a: $dst = simd_as(simd_shuffle::<_, _, $shuffled>( + let elems_a: $dst = simd_as(simd_shuffle::<_, _, $shuffled>( a, - a, - const { ShuffleMask::<$width>::even() }, + a, // this argument is ignored entirely. + const { ShuffleMask::<$width>::$mask() }, )); - let even_b: $dst = simd_as(simd_shuffle::<_, _, $shuffled>( + let elems_b: $dst = simd_as(simd_shuffle::<_, _, $shuffled>( b, - b, - const { ShuffleMask::<$width>::even() }, + b, // this argument is ignored entirely. + const { ShuffleMask::<$width>::$mask() }, )); - simd_mul(even_a, even_b) + simd_mul(elems_a, elems_b) } }; } - impl_vec_mule! { vmeb vector_signed_char i8x8 vector_signed_short 8 } - impl_vec_mule! { vmeh vector_signed_short i16x4 vector_signed_int 4 } - impl_vec_mule! { vmef vector_signed_int i32x2 vector_signed_long_long 2 } + impl_vec_mul_even_odd! { even vmeb vector_signed_char i8x8 vector_signed_short 8 } + impl_vec_mul_even_odd! { even vmeh vector_signed_short i16x4 vector_signed_int 4 } + impl_vec_mul_even_odd! { even vmef vector_signed_int i32x2 vector_signed_long_long 2 } - impl_vec_mule! { vmleb vector_unsigned_char u8x8 vector_unsigned_short 8 } - impl_vec_mule! { vmleh vector_unsigned_short u16x4 vector_unsigned_int 4 } - impl_vec_mule! { vmlef vector_unsigned_int u32x2 vector_unsigned_long_long 2 } + impl_vec_mul_even_odd! { even vmleb vector_unsigned_char u8x8 vector_unsigned_short 8 } + impl_vec_mul_even_odd! { even vmleh vector_unsigned_short u16x4 vector_unsigned_int 4 } + impl_vec_mul_even_odd! { even vmlef vector_unsigned_int u32x2 vector_unsigned_long_long 2 } impl_mul!([VectorMule vec_mule] vmeb (vector_signed_char, vector_signed_char) -> vector_signed_short ); impl_mul!([VectorMule vec_mule] vmeh (vector_signed_short, vector_signed_short) -> vector_signed_int); @@ -2691,21 +2687,21 @@ mod sealed { unsafe fn vec_mulo(self, b: Self) -> Result; } - test_impl! { vec_vmob(a: vector_signed_char, b: vector_signed_char) -> vector_signed_short [ vmob, vmob ] } - test_impl! { vec_vmoh(a: vector_signed_short, b: vector_signed_short) -> vector_signed_int[ vmoh, vmoh ] } - test_impl! { vec_vmof(a: vector_signed_int, b: vector_signed_int) -> vector_signed_long_long [ vmof, vmof ] } + impl_vec_mul_even_odd! { odd vmob vector_signed_char i8x8 vector_signed_short 8 } + impl_vec_mul_even_odd! { odd vmoh vector_signed_short i16x4 vector_signed_int 4 } + impl_vec_mul_even_odd! { odd vmof vector_signed_int i32x2 vector_signed_long_long 2 } - test_impl! { vec_vmlob(a: vector_unsigned_char, b: vector_unsigned_char) -> vector_unsigned_short [ vmlob, vmlob ] } - test_impl! { vec_vmloh(a: vector_unsigned_short, b: vector_unsigned_short) -> vector_unsigned_int[ vmloh, vmloh ] } - test_impl! { vec_vmlof(a: vector_unsigned_int, b: vector_unsigned_int) -> vector_unsigned_long_long [ vmlof, vmlof ] } + impl_vec_mul_even_odd! { odd vmlob vector_unsigned_char u8x8 vector_unsigned_short 8 } + impl_vec_mul_even_odd! { odd vmloh vector_unsigned_short u16x4 vector_unsigned_int 4 } + impl_vec_mul_even_odd! { odd vmlof vector_unsigned_int u32x2 vector_unsigned_long_long 2 } - impl_mul!([VectorMulo vec_mulo] vec_vmob (vector_signed_char, vector_signed_char) -> vector_signed_short ); - impl_mul!([VectorMulo vec_mulo] vec_vmoh (vector_signed_short, vector_signed_short) -> vector_signed_int); - impl_mul!([VectorMulo vec_mulo] vec_vmof (vector_signed_int, vector_signed_int) -> vector_signed_long_long ); + impl_mul!([VectorMulo vec_mulo] vmob (vector_signed_char, vector_signed_char) -> vector_signed_short ); + impl_mul!([VectorMulo vec_mulo] vmoh (vector_signed_short, vector_signed_short) -> vector_signed_int); + impl_mul!([VectorMulo vec_mulo] vmof (vector_signed_int, vector_signed_int) -> vector_signed_long_long ); - impl_mul!([VectorMulo vec_mulo] vec_vmlob (vector_unsigned_char, vector_unsigned_char) -> vector_unsigned_short ); - impl_mul!([VectorMulo vec_mulo] vec_vmloh (vector_unsigned_short, vector_unsigned_short) -> vector_unsigned_int); - impl_mul!([VectorMulo vec_mulo] vec_vmlof (vector_unsigned_int, vector_unsigned_int) -> vector_unsigned_long_long ); + impl_mul!([VectorMulo vec_mulo] vmlob (vector_unsigned_char, vector_unsigned_char) -> vector_unsigned_short ); + impl_mul!([VectorMulo vec_mulo] vmloh (vector_unsigned_short, vector_unsigned_short) -> vector_unsigned_int); + impl_mul!([VectorMulo vec_mulo] vmlof (vector_unsigned_int, vector_unsigned_int) -> vector_unsigned_long_long ); #[unstable(feature = "stdarch_s390x", issue = "135681")] pub trait VectorMulh { From dfa95c6fa40374df410d3215d99d697c58f5da50 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 20 Aug 2025 18:08:51 +0200 Subject: [PATCH 109/682] s390x: implement `vec_subc_u128` using `overflowing_sub` --- library/stdarch/crates/core_arch/src/s390x/vector.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/s390x/vector.rs b/library/stdarch/crates/core_arch/src/s390x/vector.rs index 677522578892..4e4161dbb7f7 100644 --- a/library/stdarch/crates/core_arch/src/s390x/vector.rs +++ b/library/stdarch/crates/core_arch/src/s390x/vector.rs @@ -4678,11 +4678,9 @@ pub unsafe fn vec_subc_u128( a: vector_unsigned_char, b: vector_unsigned_char, ) -> vector_unsigned_char { - // FIXME(llvm) sadly this does not work https://github.com/llvm/llvm-project/issues/129608 - // let a: u128 = transmute(a); - // let b: u128 = transmute(b); - // transmute(!a.overflowing_sub(b).1 as u128) - transmute(vscbiq(transmute(a), transmute(b))) + let a: u128 = transmute(a); + let b: u128 = transmute(b); + transmute(!a.overflowing_sub(b).1 as u128) } /// Vector Add Compute Carryout unsigned 128-bits @@ -4714,7 +4712,7 @@ pub unsafe fn vec_adde_u128( let a: u128 = transmute(a); let b: u128 = transmute(b); let c: u128 = transmute(c); - // FIXME(llvm) sadly this does not work + // FIXME(llvm) https://github.com/llvm/llvm-project/pull/153557 // let (d, _carry) = a.carrying_add(b, c & 1 != 0); // transmute(d) transmute(vacq(a, b, c)) From e9162f221a154ff8dbf353275f3288f69c2c431f Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 20 Aug 2025 20:00:22 +0200 Subject: [PATCH 110/682] s390x: implement `vec_sld` using `fshl` --- .../crates/core_arch/src/s390x/vector.rs | 55 ++++++++++++++++--- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/s390x/vector.rs b/library/stdarch/crates/core_arch/src/s390x/vector.rs index 4e4161dbb7f7..9c2941129c86 100644 --- a/library/stdarch/crates/core_arch/src/s390x/vector.rs +++ b/library/stdarch/crates/core_arch/src/s390x/vector.rs @@ -94,8 +94,6 @@ unsafe extern "unadjusted" { #[link_name = "llvm.s390.vsrlb"] fn vsrlb(a: vector_signed_char, b: vector_signed_char) -> vector_signed_char; #[link_name = "llvm.s390.vslb"] fn vslb(a: vector_signed_char, b: vector_signed_char) -> vector_signed_char; - #[link_name = "llvm.s390.vsldb"] fn vsldb(a: i8x16, b: i8x16, c: u32) -> i8x16; - #[link_name = "llvm.s390.vsld"] fn vsld(a: i8x16, b: i8x16, c: u32) -> i8x16; #[link_name = "llvm.s390.vsrd"] fn vsrd(a: i8x16, b: i8x16, c: u32) -> i8x16; #[link_name = "llvm.s390.verimb"] fn verimb(a: vector_signed_char, b: vector_signed_char, c: vector_signed_char, d: i32) -> vector_signed_char; @@ -3484,10 +3482,44 @@ mod sealed { unsafe fn vec_sldb(self, b: Self) -> Self; } - // FIXME(llvm) https://github.com/llvm/llvm-project/issues/129955 - // ideally we could implement this in terms of llvm.fshl.i128 - // #[link_name = "llvm.fshl.i128"] fn fshl_i128(a: u128, b: u128, c: u128) -> u128; - // transmute(fshl_i128(transmute(a), transmute(b), const { C * 8 } )) + #[inline] + #[target_feature(enable = "vector")] + #[cfg_attr(test, assert_instr(vsldb))] + unsafe fn test_vec_sld(a: vector_signed_int, b: vector_signed_int) -> vector_signed_int { + a.vec_sld::<13>(b) + } + + #[inline] + #[target_feature(enable = "vector")] + #[cfg_attr(test, assert_instr(vsldb))] + unsafe fn test_vec_sldw(a: vector_signed_int, b: vector_signed_int) -> vector_signed_int { + a.vec_sldw::<3>(b) + } + + #[inline] + #[target_feature(enable = "vector-enhancements-2")] + #[cfg_attr(test, assert_instr(vsld))] + unsafe fn test_vec_sldb(a: vector_signed_int, b: vector_signed_int) -> vector_signed_int { + a.vec_sldb::<7>(b) + } + + #[inline] + #[target_feature(enable = "vector-enhancements-2")] + #[cfg_attr(test, assert_instr(vsrd))] + unsafe fn test_vec_srdb(a: vector_signed_int, b: vector_signed_int) -> vector_signed_int { + a.vec_srdb::<7>(b) + } + + unsafe fn funnel_shl_u128(a: u128, b: u128, c: u128) -> u128 { + #[repr(simd)] + struct Single([u128; 1]); + + transmute(simd_funnel_shl::( + transmute(a), + transmute(b), + transmute(c), + )) + } macro_rules! impl_vec_sld { ($($ty:ident)*) => { @@ -3498,21 +3530,21 @@ mod sealed { #[target_feature(enable = "vector")] unsafe fn vec_sld(self, b: Self) -> Self { static_assert_uimm_bits!(C, 4); - transmute(vsldb(transmute(self), transmute(b), C)) + transmute(funnel_shl_u128(transmute(self), transmute(b), const { C as u128 * 8 })) } #[inline] #[target_feature(enable = "vector")] unsafe fn vec_sldw(self, b: Self) -> Self { static_assert_uimm_bits!(C, 2); - transmute(vsldb(transmute(self), transmute(b), const { 4 * C })) + transmute(funnel_shl_u128(transmute(self), transmute(b), const { C as u128 * 4 * 8 })) } #[inline] #[target_feature(enable = "vector-enhancements-2")] unsafe fn vec_sldb(self, b: Self) -> Self { static_assert_uimm_bits!(C, 3); - transmute(vsld(transmute(self), transmute(b), C)) + transmute(funnel_shl_u128(transmute(self), transmute(b), const { C as u128 })) } } @@ -3523,6 +3555,11 @@ mod sealed { unsafe fn vec_srdb(self, b: Self) -> Self { static_assert_uimm_bits!(C, 3); transmute(vsrd(transmute(self), transmute(b), C)) + // FIXME(llvm): https://github.com/llvm/llvm-project/issues/129955#issuecomment-3207488190 + // LLVM currently rewrites `fshr` to `fshl`, and the logic in the s390x + // backend cannot deal with that yet. + // #[link_name = "llvm.fshr.i128"] fn fshr_i128(a: u128, b: u128, c: u128) -> u128; + // transmute(fshr_i128(transmute(self), transmute(b), const { C as u128 })) } } )* From 45af206618d516571e6f526e4600ebaca92de6c0 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 20 Aug 2025 21:12:52 +0200 Subject: [PATCH 111/682] s390x: link to a missed optimization --- library/stdarch/crates/core_arch/src/s390x/vector.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/stdarch/crates/core_arch/src/s390x/vector.rs b/library/stdarch/crates/core_arch/src/s390x/vector.rs index 9c2941129c86..f6d14c45e0d2 100644 --- a/library/stdarch/crates/core_arch/src/s390x/vector.rs +++ b/library/stdarch/crates/core_arch/src/s390x/vector.rs @@ -2352,6 +2352,9 @@ mod sealed { unsafe fn vec_packs(self, b: Other) -> Self::Result; } + // FIXME(llvm): https://github.com/llvm/llvm-project/issues/153655 + // Other targets can use a min/max for the saturation + a truncation. + impl_vec_trait! { [VectorPacks vec_packs] vpksh (vector_signed_short, vector_signed_short) -> vector_signed_char } impl_vec_trait! { [VectorPacks vec_packs] vpklsh (vector_unsigned_short, vector_unsigned_short) -> vector_unsigned_char } impl_vec_trait! { [VectorPacks vec_packs] vpksf (vector_signed_int, vector_signed_int) -> vector_signed_short } From 4a9b73bc98417d2335b7d3d86edf301bdd7be230 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 21 Aug 2025 07:14:55 +0300 Subject: [PATCH 112/682] Attach the DB in symbol queries --- .../crates/ide-db/src/symbol_index.rs | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs index 78ade30c7e3e..76b647f8e9f2 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs @@ -133,23 +133,27 @@ pub trait SymbolsDatabase: HirDatabase + SourceDatabase { fn library_symbols(db: &dyn SymbolsDatabase, source_root_id: SourceRootId) -> Arc { let _p = tracing::info_span!("library_symbols").entered(); - let mut symbol_collector = SymbolCollector::new(db); + // We call this without attaching because this runs in parallel, so we need to attach here. + salsa::attach(db, || { + let mut symbol_collector = SymbolCollector::new(db); - db.source_root_crates(source_root_id) - .iter() - .flat_map(|&krate| Crate::from(krate).modules(db)) - // we specifically avoid calling other SymbolsDatabase queries here, even though they do the same thing, - // as the index for a library is not going to really ever change, and we do not want to store each - // the module or crate indices for those in salsa unless we need to. - .for_each(|module| symbol_collector.collect(module)); + db.source_root_crates(source_root_id) + .iter() + .flat_map(|&krate| Crate::from(krate).modules(db)) + // we specifically avoid calling other SymbolsDatabase queries here, even though they do the same thing, + // as the index for a library is not going to really ever change, and we do not want to store each + // the module or crate indices for those in salsa unless we need to. + .for_each(|module| symbol_collector.collect(module)); - Arc::new(SymbolIndex::new(symbol_collector.finish())) + Arc::new(SymbolIndex::new(symbol_collector.finish())) + }) } fn module_symbols(db: &dyn SymbolsDatabase, module: Module) -> Arc { let _p = tracing::info_span!("module_symbols").entered(); - Arc::new(SymbolIndex::new(SymbolCollector::new_module(db, module))) + // We call this without attaching because this runs in parallel, so we need to attach here. + salsa::attach(db, || Arc::new(SymbolIndex::new(SymbolCollector::new_module(db, module)))) } pub fn crate_symbols(db: &dyn SymbolsDatabase, krate: Crate) -> Box<[Arc]> { From 73d1aa9e8248d22cc6425714b9f3f44d34257e7a Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 21 Aug 2025 08:06:43 +0300 Subject: [PATCH 113/682] Attach the DB when mapping the result of `world_symbols()` We call `try_to_nav()` there. --- src/tools/rust-analyzer/crates/ide/src/lib.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 874e04702e24..4b93535c8943 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -481,12 +481,15 @@ impl Analysis { // `world_symbols` currently clones the database to run stuff in parallel, which will make any query panic // if we were to attach it here. Cancelled::catch(|| { - symbol_index::world_symbols(&self.db, query) - .into_iter() - .filter_map(|s| s.try_to_nav(&self.db)) - .take(limit) - .map(UpmappingResult::call_site) - .collect::>() + let symbols = symbol_index::world_symbols(&self.db, query); + salsa::attach(&self.db, || { + symbols + .into_iter() + .filter_map(|s| s.try_to_nav(&self.db)) + .take(limit) + .map(UpmappingResult::call_site) + .collect::>() + }) }) } From 98bd1d744562947b54174668423bee572ed5b7ee Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Mon, 2 Jun 2025 00:02:35 +0200 Subject: [PATCH 114/682] use `simd_saturating_{add, sub}` on neon --- .../src/arm_shared/neon/generated.rs | 320 ++---------------- .../spec/neon/arm_shared.spec.yml | 44 +-- 2 files changed, 48 insertions(+), 316 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs b/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs index fd150bcaf2b7..22d31d4e2568 100644 --- a/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs +++ b/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs @@ -36978,15 +36978,7 @@ pub fn vqabsq_s32(a: int32x4_t) -> int32x4_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqadd_s8(a: int8x8_t, b: int8x8_t) -> int8x8_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.sqadd.v8i8" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.sadd.sat.v8i8")] - fn _vqadd_s8(a: int8x8_t, b: int8x8_t) -> int8x8_t; - } - unsafe { _vqadd_s8(a, b) } + unsafe { simd_saturating_add(a, b) } } #[doc = "Saturating add"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqaddq_s8)"] @@ -37007,15 +36999,7 @@ pub fn vqadd_s8(a: int8x8_t, b: int8x8_t) -> int8x8_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqaddq_s8(a: int8x16_t, b: int8x16_t) -> int8x16_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.sqadd.v16i8" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.sadd.sat.v16i8")] - fn _vqaddq_s8(a: int8x16_t, b: int8x16_t) -> int8x16_t; - } - unsafe { _vqaddq_s8(a, b) } + unsafe { simd_saturating_add(a, b) } } #[doc = "Saturating add"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqadd_s16)"] @@ -37036,15 +37020,7 @@ pub fn vqaddq_s8(a: int8x16_t, b: int8x16_t) -> int8x16_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqadd_s16(a: int16x4_t, b: int16x4_t) -> int16x4_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.sqadd.v4i16" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.sadd.sat.v4i16")] - fn _vqadd_s16(a: int16x4_t, b: int16x4_t) -> int16x4_t; - } - unsafe { _vqadd_s16(a, b) } + unsafe { simd_saturating_add(a, b) } } #[doc = "Saturating add"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqaddq_s16)"] @@ -37065,15 +37041,7 @@ pub fn vqadd_s16(a: int16x4_t, b: int16x4_t) -> int16x4_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqaddq_s16(a: int16x8_t, b: int16x8_t) -> int16x8_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.sqadd.v8i16" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.sadd.sat.v8i16")] - fn _vqaddq_s16(a: int16x8_t, b: int16x8_t) -> int16x8_t; - } - unsafe { _vqaddq_s16(a, b) } + unsafe { simd_saturating_add(a, b) } } #[doc = "Saturating add"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqadd_s32)"] @@ -37094,15 +37062,7 @@ pub fn vqaddq_s16(a: int16x8_t, b: int16x8_t) -> int16x8_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqadd_s32(a: int32x2_t, b: int32x2_t) -> int32x2_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.sqadd.v2i32" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.sadd.sat.v2i32")] - fn _vqadd_s32(a: int32x2_t, b: int32x2_t) -> int32x2_t; - } - unsafe { _vqadd_s32(a, b) } + unsafe { simd_saturating_add(a, b) } } #[doc = "Saturating add"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqaddq_s32)"] @@ -37123,15 +37083,7 @@ pub fn vqadd_s32(a: int32x2_t, b: int32x2_t) -> int32x2_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqaddq_s32(a: int32x4_t, b: int32x4_t) -> int32x4_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.sqadd.v4i32" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.sadd.sat.v4i32")] - fn _vqaddq_s32(a: int32x4_t, b: int32x4_t) -> int32x4_t; - } - unsafe { _vqaddq_s32(a, b) } + unsafe { simd_saturating_add(a, b) } } #[doc = "Saturating add"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqadd_s64)"] @@ -37152,15 +37104,7 @@ pub fn vqaddq_s32(a: int32x4_t, b: int32x4_t) -> int32x4_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqadd_s64(a: int64x1_t, b: int64x1_t) -> int64x1_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.sqadd.v1i64" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.sadd.sat.v1i64")] - fn _vqadd_s64(a: int64x1_t, b: int64x1_t) -> int64x1_t; - } - unsafe { _vqadd_s64(a, b) } + unsafe { simd_saturating_add(a, b) } } #[doc = "Saturating add"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqaddq_s64)"] @@ -37181,15 +37125,7 @@ pub fn vqadd_s64(a: int64x1_t, b: int64x1_t) -> int64x1_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqaddq_s64(a: int64x2_t, b: int64x2_t) -> int64x2_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.sqadd.v2i64" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.sadd.sat.v2i64")] - fn _vqaddq_s64(a: int64x2_t, b: int64x2_t) -> int64x2_t; - } - unsafe { _vqaddq_s64(a, b) } + unsafe { simd_saturating_add(a, b) } } #[doc = "Saturating add"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqadd_u8)"] @@ -37210,15 +37146,7 @@ pub fn vqaddq_s64(a: int64x2_t, b: int64x2_t) -> int64x2_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqadd_u8(a: uint8x8_t, b: uint8x8_t) -> uint8x8_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.uqadd.v8i8" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.uadd.sat.v8i8")] - fn _vqadd_u8(a: uint8x8_t, b: uint8x8_t) -> uint8x8_t; - } - unsafe { _vqadd_u8(a, b) } + unsafe { simd_saturating_add(a, b) } } #[doc = "Saturating add"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqaddq_u8)"] @@ -37239,15 +37167,7 @@ pub fn vqadd_u8(a: uint8x8_t, b: uint8x8_t) -> uint8x8_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqaddq_u8(a: uint8x16_t, b: uint8x16_t) -> uint8x16_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.uqadd.v16i8" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.uadd.sat.v16i8")] - fn _vqaddq_u8(a: uint8x16_t, b: uint8x16_t) -> uint8x16_t; - } - unsafe { _vqaddq_u8(a, b) } + unsafe { simd_saturating_add(a, b) } } #[doc = "Saturating add"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqadd_u16)"] @@ -37268,15 +37188,7 @@ pub fn vqaddq_u8(a: uint8x16_t, b: uint8x16_t) -> uint8x16_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqadd_u16(a: uint16x4_t, b: uint16x4_t) -> uint16x4_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.uqadd.v4i16" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.uadd.sat.v4i16")] - fn _vqadd_u16(a: uint16x4_t, b: uint16x4_t) -> uint16x4_t; - } - unsafe { _vqadd_u16(a, b) } + unsafe { simd_saturating_add(a, b) } } #[doc = "Saturating add"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqaddq_u16)"] @@ -37297,15 +37209,7 @@ pub fn vqadd_u16(a: uint16x4_t, b: uint16x4_t) -> uint16x4_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqaddq_u16(a: uint16x8_t, b: uint16x8_t) -> uint16x8_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.uqadd.v8i16" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.uadd.sat.v8i16")] - fn _vqaddq_u16(a: uint16x8_t, b: uint16x8_t) -> uint16x8_t; - } - unsafe { _vqaddq_u16(a, b) } + unsafe { simd_saturating_add(a, b) } } #[doc = "Saturating add"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqadd_u32)"] @@ -37326,15 +37230,7 @@ pub fn vqaddq_u16(a: uint16x8_t, b: uint16x8_t) -> uint16x8_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqadd_u32(a: uint32x2_t, b: uint32x2_t) -> uint32x2_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.uqadd.v2i32" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.uadd.sat.v2i32")] - fn _vqadd_u32(a: uint32x2_t, b: uint32x2_t) -> uint32x2_t; - } - unsafe { _vqadd_u32(a, b) } + unsafe { simd_saturating_add(a, b) } } #[doc = "Saturating add"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqaddq_u32)"] @@ -37355,15 +37251,7 @@ pub fn vqadd_u32(a: uint32x2_t, b: uint32x2_t) -> uint32x2_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqaddq_u32(a: uint32x4_t, b: uint32x4_t) -> uint32x4_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.uqadd.v4i32" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.uadd.sat.v4i32")] - fn _vqaddq_u32(a: uint32x4_t, b: uint32x4_t) -> uint32x4_t; - } - unsafe { _vqaddq_u32(a, b) } + unsafe { simd_saturating_add(a, b) } } #[doc = "Saturating add"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqadd_u64)"] @@ -37384,15 +37272,7 @@ pub fn vqaddq_u32(a: uint32x4_t, b: uint32x4_t) -> uint32x4_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqadd_u64(a: uint64x1_t, b: uint64x1_t) -> uint64x1_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.uqadd.v1i64" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.uadd.sat.v1i64")] - fn _vqadd_u64(a: uint64x1_t, b: uint64x1_t) -> uint64x1_t; - } - unsafe { _vqadd_u64(a, b) } + unsafe { simd_saturating_add(a, b) } } #[doc = "Saturating add"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqaddq_u64)"] @@ -37413,15 +37293,7 @@ pub fn vqadd_u64(a: uint64x1_t, b: uint64x1_t) -> uint64x1_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqaddq_u64(a: uint64x2_t, b: uint64x2_t) -> uint64x2_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.uqadd.v2i64" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.uadd.sat.v2i64")] - fn _vqaddq_u64(a: uint64x2_t, b: uint64x2_t) -> uint64x2_t; - } - unsafe { _vqaddq_u64(a, b) } + unsafe { simd_saturating_add(a, b) } } #[doc = "Vector widening saturating doubling multiply accumulate with scalar"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqdmlal_lane_s16)"] @@ -41259,15 +41131,7 @@ pub fn vqshrun_n_s64(a: int64x2_t) -> uint32x2_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqsub_s8(a: int8x8_t, b: int8x8_t) -> int8x8_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.sqsub.v8i8" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.ssub.sat.v8i8")] - fn _vqsub_s8(a: int8x8_t, b: int8x8_t) -> int8x8_t; - } - unsafe { _vqsub_s8(a, b) } + unsafe { simd_saturating_sub(a, b) } } #[doc = "Saturating subtract"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqsubq_s8)"] @@ -41288,15 +41152,7 @@ pub fn vqsub_s8(a: int8x8_t, b: int8x8_t) -> int8x8_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqsubq_s8(a: int8x16_t, b: int8x16_t) -> int8x16_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.sqsub.v16i8" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.ssub.sat.v16i8")] - fn _vqsubq_s8(a: int8x16_t, b: int8x16_t) -> int8x16_t; - } - unsafe { _vqsubq_s8(a, b) } + unsafe { simd_saturating_sub(a, b) } } #[doc = "Saturating subtract"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqsub_s16)"] @@ -41317,15 +41173,7 @@ pub fn vqsubq_s8(a: int8x16_t, b: int8x16_t) -> int8x16_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqsub_s16(a: int16x4_t, b: int16x4_t) -> int16x4_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.sqsub.v4i16" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.ssub.sat.v4i16")] - fn _vqsub_s16(a: int16x4_t, b: int16x4_t) -> int16x4_t; - } - unsafe { _vqsub_s16(a, b) } + unsafe { simd_saturating_sub(a, b) } } #[doc = "Saturating subtract"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqsubq_s16)"] @@ -41346,15 +41194,7 @@ pub fn vqsub_s16(a: int16x4_t, b: int16x4_t) -> int16x4_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqsubq_s16(a: int16x8_t, b: int16x8_t) -> int16x8_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.sqsub.v8i16" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.ssub.sat.v8i16")] - fn _vqsubq_s16(a: int16x8_t, b: int16x8_t) -> int16x8_t; - } - unsafe { _vqsubq_s16(a, b) } + unsafe { simd_saturating_sub(a, b) } } #[doc = "Saturating subtract"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqsub_s32)"] @@ -41375,15 +41215,7 @@ pub fn vqsubq_s16(a: int16x8_t, b: int16x8_t) -> int16x8_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqsub_s32(a: int32x2_t, b: int32x2_t) -> int32x2_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.sqsub.v2i32" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.ssub.sat.v2i32")] - fn _vqsub_s32(a: int32x2_t, b: int32x2_t) -> int32x2_t; - } - unsafe { _vqsub_s32(a, b) } + unsafe { simd_saturating_sub(a, b) } } #[doc = "Saturating subtract"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqsubq_s32)"] @@ -41404,15 +41236,7 @@ pub fn vqsub_s32(a: int32x2_t, b: int32x2_t) -> int32x2_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqsubq_s32(a: int32x4_t, b: int32x4_t) -> int32x4_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.sqsub.v4i32" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.ssub.sat.v4i32")] - fn _vqsubq_s32(a: int32x4_t, b: int32x4_t) -> int32x4_t; - } - unsafe { _vqsubq_s32(a, b) } + unsafe { simd_saturating_sub(a, b) } } #[doc = "Saturating subtract"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqsub_s64)"] @@ -41433,15 +41257,7 @@ pub fn vqsubq_s32(a: int32x4_t, b: int32x4_t) -> int32x4_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqsub_s64(a: int64x1_t, b: int64x1_t) -> int64x1_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.sqsub.v1i64" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.ssub.sat.v1i64")] - fn _vqsub_s64(a: int64x1_t, b: int64x1_t) -> int64x1_t; - } - unsafe { _vqsub_s64(a, b) } + unsafe { simd_saturating_sub(a, b) } } #[doc = "Saturating subtract"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqsubq_s64)"] @@ -41462,15 +41278,7 @@ pub fn vqsub_s64(a: int64x1_t, b: int64x1_t) -> int64x1_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqsubq_s64(a: int64x2_t, b: int64x2_t) -> int64x2_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.sqsub.v2i64" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.ssub.sat.v2i64")] - fn _vqsubq_s64(a: int64x2_t, b: int64x2_t) -> int64x2_t; - } - unsafe { _vqsubq_s64(a, b) } + unsafe { simd_saturating_sub(a, b) } } #[doc = "Saturating subtract"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqsub_u8)"] @@ -41491,15 +41299,7 @@ pub fn vqsubq_s64(a: int64x2_t, b: int64x2_t) -> int64x2_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqsub_u8(a: uint8x8_t, b: uint8x8_t) -> uint8x8_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.uqsub.v8i8" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.usub.sat.v8i8")] - fn _vqsub_u8(a: uint8x8_t, b: uint8x8_t) -> uint8x8_t; - } - unsafe { _vqsub_u8(a, b) } + unsafe { simd_saturating_sub(a, b) } } #[doc = "Saturating subtract"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqsubq_u8)"] @@ -41520,15 +41320,7 @@ pub fn vqsub_u8(a: uint8x8_t, b: uint8x8_t) -> uint8x8_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqsubq_u8(a: uint8x16_t, b: uint8x16_t) -> uint8x16_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.uqsub.v16i8" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.usub.sat.v16i8")] - fn _vqsubq_u8(a: uint8x16_t, b: uint8x16_t) -> uint8x16_t; - } - unsafe { _vqsubq_u8(a, b) } + unsafe { simd_saturating_sub(a, b) } } #[doc = "Saturating subtract"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqsub_u16)"] @@ -41549,15 +41341,7 @@ pub fn vqsubq_u8(a: uint8x16_t, b: uint8x16_t) -> uint8x16_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqsub_u16(a: uint16x4_t, b: uint16x4_t) -> uint16x4_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.uqsub.v4i16" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.usub.sat.v4i16")] - fn _vqsub_u16(a: uint16x4_t, b: uint16x4_t) -> uint16x4_t; - } - unsafe { _vqsub_u16(a, b) } + unsafe { simd_saturating_sub(a, b) } } #[doc = "Saturating subtract"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqsubq_u16)"] @@ -41578,15 +41362,7 @@ pub fn vqsub_u16(a: uint16x4_t, b: uint16x4_t) -> uint16x4_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqsubq_u16(a: uint16x8_t, b: uint16x8_t) -> uint16x8_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.uqsub.v8i16" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.usub.sat.v8i16")] - fn _vqsubq_u16(a: uint16x8_t, b: uint16x8_t) -> uint16x8_t; - } - unsafe { _vqsubq_u16(a, b) } + unsafe { simd_saturating_sub(a, b) } } #[doc = "Saturating subtract"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqsub_u32)"] @@ -41607,15 +41383,7 @@ pub fn vqsubq_u16(a: uint16x8_t, b: uint16x8_t) -> uint16x8_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqsub_u32(a: uint32x2_t, b: uint32x2_t) -> uint32x2_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.uqsub.v2i32" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.usub.sat.v2i32")] - fn _vqsub_u32(a: uint32x2_t, b: uint32x2_t) -> uint32x2_t; - } - unsafe { _vqsub_u32(a, b) } + unsafe { simd_saturating_sub(a, b) } } #[doc = "Saturating subtract"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqsubq_u32)"] @@ -41636,15 +41404,7 @@ pub fn vqsub_u32(a: uint32x2_t, b: uint32x2_t) -> uint32x2_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqsubq_u32(a: uint32x4_t, b: uint32x4_t) -> uint32x4_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.uqsub.v4i32" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.usub.sat.v4i32")] - fn _vqsubq_u32(a: uint32x4_t, b: uint32x4_t) -> uint32x4_t; - } - unsafe { _vqsubq_u32(a, b) } + unsafe { simd_saturating_sub(a, b) } } #[doc = "Saturating subtract"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqsub_u64)"] @@ -41665,15 +41425,7 @@ pub fn vqsubq_u32(a: uint32x4_t, b: uint32x4_t) -> uint32x4_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqsub_u64(a: uint64x1_t, b: uint64x1_t) -> uint64x1_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.uqsub.v1i64" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.usub.sat.v1i64")] - fn _vqsub_u64(a: uint64x1_t, b: uint64x1_t) -> uint64x1_t; - } - unsafe { _vqsub_u64(a, b) } + unsafe { simd_saturating_sub(a, b) } } #[doc = "Saturating subtract"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqsubq_u64)"] @@ -41694,15 +41446,7 @@ pub fn vqsub_u64(a: uint64x1_t, b: uint64x1_t) -> uint64x1_t { unstable(feature = "stdarch_arm_neon_intrinsics", issue = "111800") )] pub fn vqsubq_u64(a: uint64x2_t, b: uint64x2_t) -> uint64x2_t { - unsafe extern "unadjusted" { - #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), - link_name = "llvm.aarch64.neon.uqsub.v2i64" - )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.usub.sat.v2i64")] - fn _vqsubq_u64(a: uint64x2_t, b: uint64x2_t) -> uint64x2_t; - } - unsafe { _vqsubq_u64(a, b) } + unsafe { simd_saturating_sub(a, b) } } #[doc = "Rounding Add returning High Narrow (high half)."] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vraddhn_high_s16)"] diff --git a/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml b/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml index f16a257399bc..1543ab1f866e 100644 --- a/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml +++ b/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml @@ -2290,13 +2290,10 @@ intrinsics: - [uint64x1_t, u64, i64] - [uint64x2_t, u64, i64] compose: - - LLVMLink: - name: "uqsub.{neon_type[0]}" - links: - - link: "llvm.aarch64.neon.uqsub.v{neon_type[0].lane}{type[2]}" - arch: aarch64,arm64ec - - link: "llvm.usub.sat.v{neon_type[0].lane}{type[2]}" - arch: arm + - FnCall: + - simd_saturating_sub + - - a + - b - name: "vqsub{neon_type[0].no}" doc: Saturating subtract @@ -2319,13 +2316,10 @@ intrinsics: - [int64x1_t, s64, i64] - [int64x2_t, s64, i64] compose: - - LLVMLink: - name: "sqsub.{neon_type[0]}" - links: - - link: "llvm.aarch64.neon.sqsub.v{neon_type[0].lane}{type[2]}" - arch: aarch64,arm64ec - - link: "llvm.ssub.sat.v{neon_type[0].lane}{type[2]}" - arch: arm + - FnCall: + - simd_saturating_sub + - - a + - b - name: "vhadd{neon_type.no}" doc: Halving add @@ -2541,13 +2535,10 @@ intrinsics: - uint64x1_t - uint64x2_t compose: - - LLVMLink: - name: "uqadd.{neon_type}" - links: - - link: "llvm.aarch64.neon.uqadd.{neon_type}" - arch: aarch64,arm64ec - - link: "llvm.uadd.sat.{neon_type}" - arch: arm + - FnCall: + - simd_saturating_add + - - a + - b - name: "vqadd{neon_type.no}" doc: Saturating add @@ -2570,13 +2561,10 @@ intrinsics: - int64x1_t - int64x2_t compose: - - LLVMLink: - name: "sqadd.{neon_type}" - links: - - link: "llvm.aarch64.neon.sqadd.{neon_type}" - arch: aarch64,arm64ec - - link: "llvm.sadd.sat.{neon_type}" - arch: arm + - FnCall: + - simd_saturating_add + - - a + - b - name: "vld1{neon_type[1].no}" doc: "Load multiple single-element structures to one, two, three, or four registers" From b5e3230f8f8f66de3f5db05a28ac496fa4a05ad4 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 21 Aug 2025 22:40:58 +0300 Subject: [PATCH 115/682] Remove unnecessary `salsa::attach()` calls --- src/tools/rust-analyzer/crates/ide/src/lib.rs | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 4b93535c8943..cddf5f04f244 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -461,9 +461,7 @@ impl Analysis { hasher: impl Fn(&InlayHint) -> u64 + Send + UnwindSafe, ) -> Cancellable> { self.with_db(|db| { - salsa::attach(db, || { - inlay_hints::inlay_hints_resolve(db, file_id, resolve_range, hash, config, hasher) - }) + inlay_hints::inlay_hints_resolve(db, file_id, resolve_range, hash, config, hasher) }) } @@ -533,9 +531,7 @@ impl Analysis { let search_scope = AssertUnwindSafe(search_scope); self.with_db(|db| { let _ = &search_scope; - salsa::attach(db, || { - references::find_all_refs(&Semantics::new(db), position, search_scope.0) - }) + references::find_all_refs(&Semantics::new(db), position, search_scope.0) }) } @@ -545,7 +541,7 @@ impl Analysis { config: &HoverConfig, range: FileRange, ) -> Cancellable>> { - self.with_db(|db| salsa::attach(db, || hover::hover(db, range, config))) + self.with_db(|db| hover::hover(db, range, config)) } /// Returns moniker of symbol at position. @@ -553,7 +549,7 @@ impl Analysis { &self, position: FilePosition, ) -> Cancellable>>> { - self.with_db(|db| salsa::attach(db, || moniker::moniker(db, position))) + self.with_db(|db| moniker::moniker(db, position)) } /// Returns URL(s) for the documentation of the symbol under the cursor. @@ -581,7 +577,7 @@ impl Analysis { &self, position: FilePosition, ) -> Cancellable>>> { - self.with_db(|db| salsa::attach(db, || call_hierarchy::call_hierarchy(db, position))) + self.with_db(|db| call_hierarchy::call_hierarchy(db, position)) } /// Computes incoming calls for the given file position. @@ -649,7 +645,7 @@ impl Analysis { /// Returns the set of possible targets to run for the current file. pub fn runnables(&self, file_id: FileId) -> Cancellable> { - self.with_db(|db| salsa::attach(db, || runnables::runnables(db, file_id))) + self.with_db(|db| runnables::runnables(db, file_id)) } /// Returns the set of tests for the given file position. @@ -672,9 +668,7 @@ impl Analysis { position: FilePosition, ) -> Cancellable>> { self.with_db(|db| { - salsa::attach(db, || { - highlight_related::highlight_related(&Semantics::new(db), config, position) - }) + highlight_related::highlight_related(&Semantics::new(db), config, position) }) } From 99576aa3f7f6148480608fb62fa10a38cd64f1fd Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 22 Aug 2025 05:24:33 +0900 Subject: [PATCH 116/682] fix: Infinite recursion while lowering assoc type bounds from supertraits --- .../crates/hir-ty/src/lower_nextsolver.rs | 6 ++- .../crates/hir-ty/src/tests/regression.rs | 53 +++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs index ce953fdcb829..d87181f545d8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -1756,7 +1756,11 @@ fn named_associated_type_shorthand_candidates<'db, R>( db, GenericDefId::TraitId(trait_def_id), PredicateFilter::SelfTrait, - |pred| pred == GenericDefId::TraitId(trait_def_id), + // We are likely in the midst of lowering generic predicates of `def`. + // So, if we allow `pred == def` we might fall into an infinite recursion. + // Actually, we have already checked for the case `pred == def` above as we started + // with a stack including `trait_id` + |pred| pred != def && pred == GenericDefId::TraitId(trait_def_id), ) .0 .deref() diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index fd8c641d86eb..966433369aa8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -2439,3 +2439,56 @@ pub fn null_mut() -> *mut T { "#, ); } + +#[test] +fn issue_20484() { + check_no_mismatches( + r#" +struct Eth; + +trait FullBlockBody { + type Transaction; +} + +impl FullBlockBody for () { + type Transaction = (); +} + +trait NodePrimitives { + type BlockBody; + type SignedTx; +} + +impl NodePrimitives for () { + type BlockBody = (); + type SignedTx = (); +} + +impl NodePrimitives for Eth { + type BlockBody = (); + type SignedTx = (); +} + +trait FullNodePrimitives +where + Self: NodePrimitives>, +{ +} + +impl FullNodePrimitives for T where + T: NodePrimitives>, +{ +} + +fn node(_: N) +where + N: FullNodePrimitives, +{ +} + +fn main() { + node(Eth); +} +"#, + ); +} From b20ddc2d3f029bdbade7d86cdcf021a7cb8c3d8b Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 22 Aug 2025 11:38:19 +0800 Subject: [PATCH 117/682] Fix indent for move_guard_to_arm_body Input: ```rust fn main() { match 92 { x $0if true && true && true => { { false } }, _ => true } } ``` Old output: ```rust fn main() { match 92 { x => if true && true && true { { { false } } }, _ => true }; } ``` This PR fixed: ```rust fn main() { match 92 { x => if true && true && true { { { false } } }, _ => true } } ``` --- .../ide-assists/src/handlers/move_guard.rs | 70 +++++++++++++++---- 1 file changed, 57 insertions(+), 13 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs index 644d1f6cafef..6b50718424c7 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs @@ -40,28 +40,34 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext<'_>) return None; } let space_before_guard = guard.syntax().prev_sibling_or_token(); + let space_after_arrow = match_arm.fat_arrow_token()?.next_sibling_or_token(); - let guard_condition = guard.condition()?; + let guard_condition = guard.condition()?.reset_indent(); let arm_expr = match_arm.expr()?; - let if_expr = - make::expr_if(guard_condition, make::block_expr(None, Some(arm_expr.clone())), None) - .indent(arm_expr.indent_level()); + let then_branch = make::block_expr(None, Some(arm_expr.reset_indent().indent(1.into()))); + let if_expr = make::expr_if(guard_condition, then_branch, None).indent(arm_expr.indent_level()); let target = guard.syntax().text_range(); acc.add( AssistId::refactor_rewrite("move_guard_to_arm_body"), "Move guard to arm body", target, - |edit| { - match space_before_guard { - Some(element) if element.kind() == WHITESPACE => { - edit.delete(element.text_range()); - } - _ => (), - }; + |builder| { + let mut edit = builder.make_editor(match_arm.syntax()); + if let Some(element) = space_before_guard + && element.kind() == WHITESPACE + { + edit.delete(element); + } + if let Some(element) = space_after_arrow + && element.kind() == WHITESPACE + { + edit.replace(element, make::tokens::single_space()); + } - edit.delete(guard.syntax().text_range()); - edit.replace_ast(arm_expr, if_expr.into()); + edit.delete(guard.syntax()); + edit.replace(arm_expr.syntax(), if_expr.syntax()); + builder.add_file_edits(ctx.vfs_file_id(), edit); }, ) } @@ -298,6 +304,44 @@ fn main() { ); } + #[test] + fn move_multiline_guard_to_arm_body_works() { + check_assist( + move_guard_to_arm_body, + r#" +fn main() { + match 92 { + x $0if true + && true + && true => + { + { + false + } + }, + _ => true + } +} +"#, + r#" +fn main() { + match 92 { + x => if true + && true + && true { + { + { + false + } + } + }, + _ => true + } +} +"#, + ); + } + #[test] fn move_guard_to_arm_body_works_complex_match() { check_assist( From cf478a02c74aac664cc2e2cfa7ce91e50be1d3a4 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 22 Aug 2025 08:58:48 +0200 Subject: [PATCH 118/682] Fix panic in syntax_highlighting --- .../crates/ide/src/syntax_highlighting.rs | 4 +- .../ide/src/syntax_highlighting/inject.rs | 54 ++++++++++--------- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs index e3dc3ed3c742..f98770805a45 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs @@ -452,10 +452,10 @@ fn traverse( } hl } - NodeOrToken::Token(token) => { + NodeOrToken::Token(token) => salsa::attach(sema.db, || { highlight::token(sema, token, edition, &is_unsafe_node, tt_level > 0) .zip(Some(None)) - } + }), }; if let Some((mut highlight, binding_hash)) = element { if is_unlinked && highlight.tag == HlTag::UnresolvedReference { diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs index fb33307249a7..7785891169c6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs @@ -5,7 +5,7 @@ use std::mem; use either::Either; use hir::{EditionedFileId, HirFileId, InFile, Semantics, sym}; use ide_db::{ - SymbolKind, active_parameter::ActiveParameter, defs::Definition, + SymbolKind, active_parameter::ActiveParameter, base_db::salsa, defs::Definition, documentation::docs_with_rangemap, rust_doc::is_rust_fence, }; use syntax::{ @@ -126,32 +126,34 @@ pub(super) fn doc_comment( // Extract intra-doc links and emit highlights for them. if let Some((docs, doc_mapping)) = docs_with_rangemap(sema.db, &attributes) { - extract_definitions_from_docs(&docs) - .into_iter() - .filter_map(|(range, link, ns)| { - doc_mapping - .map(range) - .filter(|(mapping, _)| mapping.file_id == src_file_id) - .and_then(|(InFile { value: mapped_range, .. }, attr_id)| { - Some(mapped_range).zip(resolve_doc_path_for_def( - sema.db, - def, - &link, - ns, - attr_id.is_inner_attr(), - )) - }) - }) - .for_each(|(range, def)| { - hl.add(HlRange { - range, - highlight: module_def_to_hl_tag(def) - | HlMod::Documentation - | HlMod::Injected - | HlMod::IntraDocLink, - binding_hash: None, + salsa::attach(sema.db, || { + extract_definitions_from_docs(&docs) + .into_iter() + .filter_map(|(range, link, ns)| { + doc_mapping + .map(range) + .filter(|(mapping, _)| mapping.file_id == src_file_id) + .and_then(|(InFile { value: mapped_range, .. }, attr_id)| { + Some(mapped_range).zip(resolve_doc_path_for_def( + sema.db, + def, + &link, + ns, + attr_id.is_inner_attr(), + )) + }) }) - }); + .for_each(|(range, def)| { + hl.add(HlRange { + range, + highlight: module_def_to_hl_tag(def) + | HlMod::Documentation + | HlMod::Injected + | HlMod::IntraDocLink, + binding_hash: None, + }) + }) + }); } // Extract doc-test sources from the docs and calculate highlighting for them. From 3868f8ad5c2ef77fa823dc17da14ba13c2d508f7 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 22 Aug 2025 17:06:21 +0800 Subject: [PATCH 119/682] fix: convert_integer_literal not on selected `convert_integer_literal` can only convert the first literal, it is not reasonable to apply it when selected Example --- ```rust fn main() { $01+1$0; } ``` **Assist old outputs**: ``` Convert 1 to 0b1 Convert 1 to 0o1 Convert 1 to 0x1 Replace arithmetic with call to checked_* Replace arithmetic with call to saturating_* Replace arithmetic with call to wrapping_* Extract into variable Extract into constant Extract into static Extract into function ``` **Assist this PR outputs**: ``` Replace arithmetic with call to checked_* Replace arithmetic with call to saturating_* Replace arithmetic with call to wrapping_* Extract into variable Extract into constant Extract into static Extract into function ``` --- .../ide-assists/src/handlers/convert_integer_literal.rs | 8 ++++++++ src/tools/rust-analyzer/crates/ide-assists/src/tests.rs | 2 -- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_integer_literal.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_integer_literal.rs index 846f4e9b258a..bc76ade97f69 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_integer_literal.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_integer_literal.rs @@ -14,6 +14,9 @@ use crate::{AssistContext, AssistId, Assists, GroupLabel}; // const _: i32 = 0b1010; // ``` pub(crate) fn convert_integer_literal(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + if !ctx.has_empty_selection() { + return None; + } let literal = ctx.find_node_at_offset::()?; let literal = match literal.kind() { ast::LiteralKind::IntNumber(it) => it, @@ -265,4 +268,9 @@ mod tests { 111111111111111111111111111111111111111111111111111111111111111111111111$0;"; check_assist_not_applicable(convert_integer_literal, before); } + + #[test] + fn convert_non_empty_selection_literal() { + check_assist_not_applicable(convert_integer_literal, "const _: i32 = $00b1010$0;"); + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs index f4daabfe915d..44bf6a431694 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs @@ -456,7 +456,6 @@ pub fn test_some_range(a: int) -> bool { let expected = labels(&assists); expect![[r#" - Convert integer base Extract into... Replace if let with match "#]] @@ -489,7 +488,6 @@ pub fn test_some_range(a: int) -> bool { let expected = labels(&assists); expect![[r#" - Convert integer base Extract into... Replace if let with match "#]] From cd2d2a034d4e88ac6a3635c6a0cf2079eedecc21 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 6 Aug 2025 23:03:55 +0200 Subject: [PATCH 120/682] init by copying `ty_search_pat` --- clippy_utils/src/check_proc_macro.rs | 42 ++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index c4a759e919b1..d569865553aa 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -13,6 +13,7 @@ //! if the span is not from a `macro_rules` based macro. use rustc_abi::ExternAbi; +use rustc_ast as ast; use rustc_ast::AttrStyle; use rustc_ast::ast::{AttrKind, Attribute, IntTy, LitIntType, LitKind, StrStyle, TraitObjectSyntax, UintTy}; use rustc_ast::token::CommentKind; @@ -411,6 +412,47 @@ fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) { } } +fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { + match ty.kind { + TyKind::Slice(..) | TyKind::Array(..) => (Pat::Str("["), Pat::Str("]")), + TyKind::Ptr(MutTy { ty, .. }) => (Pat::Str("*"), ty_search_pat(ty).1), + TyKind::Ref(_, MutTy { ty, .. }) => (Pat::Str("&"), ty_search_pat(ty).1), + TyKind::FnPtr(fn_ptr) => ( + if fn_ptr.safety.is_unsafe() { + Pat::Str("unsafe") + } else if fn_ptr.abi != ExternAbi::Rust { + Pat::Str("extern") + } else { + Pat::MultiStr(&["fn", "extern"]) + }, + match fn_ptr.decl.output { + FnRetTy::DefaultReturn(_) => { + if let [.., ty] = fn_ptr.decl.inputs { + ty_search_pat(ty).1 + } else { + Pat::Str("(") + } + }, + FnRetTy::Return(ty) => ty_search_pat(ty).1, + }, + ), + TyKind::Never => (Pat::Str("!"), Pat::Str("!")), + // Parenthesis are trimmed from the text before the search patterns are matched. + // See: `span_matches_pat` + TyKind::Tup([]) => (Pat::Str(")"), Pat::Str("(")), + TyKind::Tup([ty]) => ty_search_pat(ty), + TyKind::Tup([head, .., tail]) => (ty_search_pat(head).0, ty_search_pat(tail).1), + TyKind::OpaqueDef(..) => (Pat::Str("impl"), Pat::Str("")), + TyKind::Path(qpath) => qpath_search_pat(&qpath), + TyKind::Infer(()) => (Pat::Str("_"), Pat::Str("_")), + TyKind::TraitObject(_, tagged_ptr) if let TraitObjectSyntax::Dyn = tagged_ptr.tag() => { + (Pat::Str("dyn"), Pat::Str("")) + }, + // NOTE: `TraitObject` is incomplete. It will always return true then. + _ => (Pat::Str(""), Pat::Str("")), + } +} + fn ident_search_pat(ident: Ident) -> (Pat, Pat) { (Pat::Sym(ident.name), Pat::Sym(ident.name)) } From a4dbbc9bf1cea10b396eec28e052a5096dc7da29 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 6 Aug 2025 23:04:49 +0200 Subject: [PATCH 121/682] rename recursive function call --- clippy_utils/src/check_proc_macro.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index d569865553aa..1ea68c1c2212 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -413,10 +413,12 @@ fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) { } fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { - match ty.kind { + use ast::{FnRetTy, MutTy, TyKind}; + + match &ty.kind { TyKind::Slice(..) | TyKind::Array(..) => (Pat::Str("["), Pat::Str("]")), - TyKind::Ptr(MutTy { ty, .. }) => (Pat::Str("*"), ty_search_pat(ty).1), - TyKind::Ref(_, MutTy { ty, .. }) => (Pat::Str("&"), ty_search_pat(ty).1), + TyKind::Ptr(MutTy { ty, .. }) => (Pat::Str("*"), ast_ty_search_pat(ty).1), + TyKind::Ref(_, MutTy { ty, .. }) => (Pat::Str("&"), ast_ty_search_pat(ty).1), TyKind::FnPtr(fn_ptr) => ( if fn_ptr.safety.is_unsafe() { Pat::Str("unsafe") @@ -428,20 +430,20 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { match fn_ptr.decl.output { FnRetTy::DefaultReturn(_) => { if let [.., ty] = fn_ptr.decl.inputs { - ty_search_pat(ty).1 + ast_ty_search_pat(ty).1 } else { Pat::Str("(") } }, - FnRetTy::Return(ty) => ty_search_pat(ty).1, + FnRetTy::Return(ty) => ast_ty_search_pat(ty).1, }, ), TyKind::Never => (Pat::Str("!"), Pat::Str("!")), // Parenthesis are trimmed from the text before the search patterns are matched. // See: `span_matches_pat` TyKind::Tup([]) => (Pat::Str(")"), Pat::Str("(")), - TyKind::Tup([ty]) => ty_search_pat(ty), - TyKind::Tup([head, .., tail]) => (ty_search_pat(head).0, ty_search_pat(tail).1), + TyKind::Tup([ty]) => ast_ty_search_pat(ty), + TyKind::Tup([head, .., tail]) => (ast_ty_search_pat(head).0, ast_ty_search_pat(tail).1), TyKind::OpaqueDef(..) => (Pat::Str("impl"), Pat::Str("")), TyKind::Path(qpath) => qpath_search_pat(&qpath), TyKind::Infer(()) => (Pat::Str("_"), Pat::Str("_")), From 2de81f56ec835d45858d0b2d80e8f1d7e1f1d1e3 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 6 Aug 2025 23:06:06 +0200 Subject: [PATCH 122/682] Infer is a unit variant --- clippy_utils/src/check_proc_macro.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index 1ea68c1c2212..792c546e8ffc 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -446,7 +446,7 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { TyKind::Tup([head, .., tail]) => (ast_ty_search_pat(head).0, ast_ty_search_pat(tail).1), TyKind::OpaqueDef(..) => (Pat::Str("impl"), Pat::Str("")), TyKind::Path(qpath) => qpath_search_pat(&qpath), - TyKind::Infer(()) => (Pat::Str("_"), Pat::Str("_")), + TyKind::Infer => (Pat::Str("_"), Pat::Str("_")), TyKind::TraitObject(_, tagged_ptr) if let TraitObjectSyntax::Dyn = tagged_ptr.tag() => { (Pat::Str("dyn"), Pat::Str("")) }, From 73d04ba29f2db9073cc37bb43e9d0faf921344b2 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 6 Aug 2025 23:06:19 +0200 Subject: [PATCH 123/682] fix Path --- clippy_utils/src/check_proc_macro.rs | 51 ++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index 792c546e8ffc..d0a769e96b72 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -15,7 +15,9 @@ use rustc_abi::ExternAbi; use rustc_ast as ast; use rustc_ast::AttrStyle; -use rustc_ast::ast::{AttrKind, Attribute, IntTy, LitIntType, LitKind, StrStyle, TraitObjectSyntax, UintTy}; +use rustc_ast::ast::{ + AttrKind, Attribute, GenericArgs, IntTy, LitIntType, LitKind, StrStyle, TraitObjectSyntax, UintTy, +}; use rustc_ast::token::CommentKind; use rustc_hir::intravisit::FnKind; use rustc_hir::{ @@ -445,7 +447,52 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { TyKind::Tup([ty]) => ast_ty_search_pat(ty), TyKind::Tup([head, .., tail]) => (ast_ty_search_pat(head).0, ast_ty_search_pat(tail).1), TyKind::OpaqueDef(..) => (Pat::Str("impl"), Pat::Str("")), - TyKind::Path(qpath) => qpath_search_pat(&qpath), + TyKind::Path(qself_path, path) => { + let start = if qself_path.is_some() { + Pat::Str("<") + } else if let Some(first) = path.segments.first() { + ident_search_pat(first.ident).0 + } else { + // this shouldn't be possible, but sure + Pat::Str("") + }; + let end = if let Some(last) = path.segments.last() { + match last.args.as_deref() { + // last `>` in `std::foo::Bar` + Some(GenericArgs::AngleBracketed(_)) => Pat::Str(">"), + Some(GenericArgs::Parenthesized(par_args)) => match &par_args.output { + FnRetTy::Default(_) => { + if let Some(last) = par_args.inputs.last() { + // `B` in `(A, B)` -- `)` gets stripped + ast_ty_search_pat(last).1 + } else { + // `(` in `()` -- `)` gets stripped + Pat::Str("(") + } + }, + // `C` in `(A, B) -> C` + FnRetTy::Ty(ty) => ast_ty_search_pat(ty).1, + }, + // last `..` in `(..)` -- `)` gets stripped + Some(GenericArgs::ParenthesizedElided(_)) => Pat::Str(".."), + // `bar` in `std::foo::bar` + None => ident_search_pat(last.ident).1, + } + } else { + // this shouldn't be possible, but sure + #[allow( + clippy::collapsible_else_if, + reason = "we want to keep these cases together, since they are both impossible" + )] + if qself_path.is_some() { + // last `>` in `` + Pat::Str(">") + } else { + Pat::Str("") + } + }; + (start, end) + }, TyKind::Infer => (Pat::Str("_"), Pat::Str("_")), TyKind::TraitObject(_, tagged_ptr) if let TraitObjectSyntax::Dyn = tagged_ptr.tag() => { (Pat::Str("dyn"), Pat::Str("")) From baf09e2fa2d6ea3a45b4b03ddbd10566c13f3c4b Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 6 Aug 2025 23:07:19 +0200 Subject: [PATCH 124/682] OpaqueDef is called ImplTrait --- clippy_utils/src/check_proc_macro.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index d0a769e96b72..a57c7c60cdae 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -446,7 +446,7 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { TyKind::Tup([]) => (Pat::Str(")"), Pat::Str("(")), TyKind::Tup([ty]) => ast_ty_search_pat(ty), TyKind::Tup([head, .., tail]) => (ast_ty_search_pat(head).0, ast_ty_search_pat(tail).1), - TyKind::OpaqueDef(..) => (Pat::Str("impl"), Pat::Str("")), + TyKind::ImplTrait(..) => (Pat::Str("impl"), Pat::Str("")), TyKind::Path(qself_path, path) => { let start = if qself_path.is_some() { Pat::Str("<") From f5292331c3f6c631b0c5544f8c4401c50d032fb7 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 6 Aug 2025 23:09:26 +0200 Subject: [PATCH 125/682] fix Tup --- clippy_utils/src/check_proc_macro.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index a57c7c60cdae..c34d6c72f4ca 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -443,9 +443,11 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { TyKind::Never => (Pat::Str("!"), Pat::Str("!")), // Parenthesis are trimmed from the text before the search patterns are matched. // See: `span_matches_pat` - TyKind::Tup([]) => (Pat::Str(")"), Pat::Str("(")), - TyKind::Tup([ty]) => ast_ty_search_pat(ty), - TyKind::Tup([head, .., tail]) => (ast_ty_search_pat(head).0, ast_ty_search_pat(tail).1), + TyKind::Tup(tup) => match &**tup { + [] => (Pat::Str(")"), Pat::Str("(")), + [ty] => ast_ty_search_pat(ty), + [head, .., tail] => (ast_ty_search_pat(head).0, ast_ty_search_pat(tail).1), + }, TyKind::ImplTrait(..) => (Pat::Str("impl"), Pat::Str("")), TyKind::Path(qself_path, path) => { let start = if qself_path.is_some() { From c7b75880dc541b298918928a55e56aec2359f4a5 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 6 Aug 2025 23:09:38 +0200 Subject: [PATCH 126/682] fix TraitObject --- clippy_utils/src/check_proc_macro.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index c34d6c72f4ca..2de131f05c26 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -415,7 +415,7 @@ fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) { } fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { - use ast::{FnRetTy, MutTy, TyKind}; + use ast::{FnRetTy, MutTy, TraitObjectSyntax, TyKind}; match &ty.kind { TyKind::Slice(..) | TyKind::Array(..) => (Pat::Str("["), Pat::Str("]")), @@ -496,10 +496,14 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { (start, end) }, TyKind::Infer => (Pat::Str("_"), Pat::Str("_")), - TyKind::TraitObject(_, tagged_ptr) if let TraitObjectSyntax::Dyn = tagged_ptr.tag() => { - (Pat::Str("dyn"), Pat::Str("")) + TyKind::TraitObject(_, trait_obj_syntax) => { + if let TraitObjectSyntax::Dyn = trait_obj_syntax { + (Pat::Str("dyn"), Pat::Str("")) + } else { + // NOTE: `TraitObject` is incomplete. It will always return true then. + (Pat::Str(""), Pat::Str("")) + } }, - // NOTE: `TraitObject` is incomplete. It will always return true then. _ => (Pat::Str(""), Pat::Str("")), } } From 742cba339a572a77809dd15bda0714e46b1e64ca Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 7 Aug 2025 00:21:06 +0200 Subject: [PATCH 127/682] fix FnPtr --- clippy_utils/src/check_proc_macro.rs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index 2de131f05c26..00583d6766e0 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -29,7 +29,7 @@ use rustc_lint::{EarlyContext, LateContext, LintContext}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_span::symbol::{Ident, kw}; -use rustc_span::{Span, Symbol}; +use rustc_span::{Span, Symbol, sym}; /// The search pattern to look for. Used by `span_matches_pat` #[derive(Clone)] @@ -415,29 +415,31 @@ fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) { } fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { - use ast::{FnRetTy, MutTy, TraitObjectSyntax, TyKind}; + use ast::{Extern, FnRetTy, MutTy, Safety, TraitObjectSyntax, TyKind}; match &ty.kind { TyKind::Slice(..) | TyKind::Array(..) => (Pat::Str("["), Pat::Str("]")), TyKind::Ptr(MutTy { ty, .. }) => (Pat::Str("*"), ast_ty_search_pat(ty).1), TyKind::Ref(_, MutTy { ty, .. }) => (Pat::Str("&"), ast_ty_search_pat(ty).1), TyKind::FnPtr(fn_ptr) => ( - if fn_ptr.safety.is_unsafe() { + if let Safety::Unsafe(_) = fn_ptr.safety { Pat::Str("unsafe") - } else if fn_ptr.abi != ExternAbi::Rust { - Pat::Str("extern") - } else { + } else if let Extern::Explicit(strlit, _) = fn_ptr.ext + && strlit.symbol == sym::rust + { Pat::MultiStr(&["fn", "extern"]) + } else { + Pat::Str("extern") }, - match fn_ptr.decl.output { - FnRetTy::DefaultReturn(_) => { - if let [.., ty] = fn_ptr.decl.inputs { - ast_ty_search_pat(ty).1 + match &fn_ptr.decl.output { + FnRetTy::Default(_) => { + if let [.., param] = &*fn_ptr.decl.inputs { + ast_ty_search_pat(¶m.ty).1 } else { Pat::Str("(") } }, - FnRetTy::Return(ty) => ast_ty_search_pat(ty).1, + FnRetTy::Ty(ty) => ast_ty_search_pat(ty).1, }, ), TyKind::Never => (Pat::Str("!"), Pat::Str("!")), From 41614fcc88d3e11d61f94172537b9c9840a2a970 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 7 Aug 2025 00:29:36 +0200 Subject: [PATCH 128/682] add Paren --- clippy_utils/src/check_proc_macro.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index 00583d6766e0..10f7fe649436 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -498,6 +498,7 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { (start, end) }, TyKind::Infer => (Pat::Str("_"), Pat::Str("_")), + TyKind::Paren(ty) => ast_ty_search_pat(ty), TyKind::TraitObject(_, trait_obj_syntax) => { if let TraitObjectSyntax::Dyn = trait_obj_syntax { (Pat::Str("dyn"), Pat::Str("")) From 2bd9f98928e051dbb6e065c64410599de6e7f93c Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 7 Aug 2025 00:34:50 +0200 Subject: [PATCH 129/682] add UnsafeBinder to the existing function as well --- clippy_utils/src/check_proc_macro.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index 10f7fe649436..f82c643151e8 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -406,6 +406,7 @@ fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) { TyKind::OpaqueDef(..) => (Pat::Str("impl"), Pat::Str("")), TyKind::Path(qpath) => qpath_search_pat(&qpath), TyKind::Infer(()) => (Pat::Str("_"), Pat::Str("_")), + TyKind::UnsafeBinder(binder_ty) => (Pat::Str("unsafe"), ty_search_pat(binder_ty.inner_ty).1), TyKind::TraitObject(_, tagged_ptr) if let TraitObjectSyntax::Dyn = tagged_ptr.tag() => { (Pat::Str("dyn"), Pat::Str("")) }, @@ -499,6 +500,7 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { }, TyKind::Infer => (Pat::Str("_"), Pat::Str("_")), TyKind::Paren(ty) => ast_ty_search_pat(ty), + TyKind::UnsafeBinder(binder_ty) => (Pat::Str("unsafe"), ast_ty_search_pat(&binder_ty.inner_ty).1), TyKind::TraitObject(_, trait_obj_syntax) => { if let TraitObjectSyntax::Dyn = trait_obj_syntax { (Pat::Str("dyn"), Pat::Str("")) From 0512002f12f3321526237e9026fafd56c2c99a3a Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 11 Aug 2025 15:21:04 +0200 Subject: [PATCH 130/682] add PinnedRef --- clippy_utils/src/check_proc_macro.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index f82c643151e8..ac4164943b40 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -421,7 +421,9 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { match &ty.kind { TyKind::Slice(..) | TyKind::Array(..) => (Pat::Str("["), Pat::Str("]")), TyKind::Ptr(MutTy { ty, .. }) => (Pat::Str("*"), ast_ty_search_pat(ty).1), - TyKind::Ref(_, MutTy { ty, .. }) => (Pat::Str("&"), ast_ty_search_pat(ty).1), + TyKind::Ref(_, MutTy { ty, .. }) | TyKind::PinnedRef(_, MutTy { ty, .. }) => { + (Pat::Str("&"), ast_ty_search_pat(ty).1) + }, TyKind::FnPtr(fn_ptr) => ( if let Safety::Unsafe(_) = fn_ptr.safety { Pat::Str("unsafe") From bbd0d46e1995354517f15baafccc84269e9f5476 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 11 Aug 2025 15:35:09 +0200 Subject: [PATCH 131/682] add MacCall --- clippy_utils/src/check_proc_macro.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index ac4164943b40..d4335689c1ea 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -511,6 +511,14 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { (Pat::Str(""), Pat::Str("")) } }, + TyKind::MacCall(mac_call) => { + let start = if let Some(first) = mac_call.path.segments.first() { + ident_search_pat(first.ident).0 + } else { + Pat::Str("") + }; + (start, Pat::Str("")) + }, _ => (Pat::Str(""), Pat::Str("")), } } From 17c0cabf988a0a68808b5e78d9c3bd0c2ed3d1cb Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 12 Aug 2025 15:54:39 +0200 Subject: [PATCH 132/682] add ImplicitSelf --- clippy_utils/src/check_proc_macro.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index d4335689c1ea..2f1d380be566 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -519,7 +519,9 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { }; (start, Pat::Str("")) }, - _ => (Pat::Str(""), Pat::Str("")), + + // implicit, so has no contents to match against + TyKind::ImplicitSelf | _ => (Pat::Str(""), Pat::Str("")), } } From 457e0208dfbe916ed441586943195c294cd14162 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 12 Aug 2025 15:55:20 +0200 Subject: [PATCH 133/682] add misc kinds: Pat, CVarArgs, Typeof, Dummy, Err --- clippy_utils/src/check_proc_macro.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index 2f1d380be566..649460dd8750 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -521,7 +521,18 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { }, // implicit, so has no contents to match against - TyKind::ImplicitSelf | _ => (Pat::Str(""), Pat::Str("")), + TyKind::ImplicitSelf + + // experimental + |TyKind::Pat(..) + + // unused + | TyKind::CVarArgs + | TyKind::Typeof(_) + + // placeholder + | TyKind::Dummy + | TyKind::Err(_) => (Pat::Str(""), Pat::Str("")), } } From 217fc30fb65bdbdc46f17ab1eee9bbd6d9286d21 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 11 Aug 2025 16:04:45 +0200 Subject: [PATCH 134/682] (finally) implement `WithSearchPat` --- clippy_utils/src/check_proc_macro.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index 649460dd8750..47f9d0591e92 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -570,6 +570,7 @@ impl_with_search_pat!((_cx: LateContext<'tcx>, self: Lit) => lit_search_pat(&sel impl_with_search_pat!((_cx: LateContext<'tcx>, self: Path<'_>) => path_search_pat(self)); impl_with_search_pat!((_cx: EarlyContext<'tcx>, self: Attribute) => attr_search_pat(self)); +impl_with_search_pat!((_cx: EarlyContext<'tcx>, self: ast::Ty) => ast_ty_search_pat(self)); impl<'cx> WithSearchPat<'cx> for (&FnKind<'cx>, &Body<'cx>, HirId, Span) { type Context = LateContext<'cx>; From 2459ad66a48d2d693f4f30a599c28bfc36603333 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 7 Aug 2025 00:38:50 +0200 Subject: [PATCH 135/682] make the lint early-pass --- clippy_lints/src/let_with_type_underscore.rs | 16 ++++++++-------- clippy_lints/src/lib.rs | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/let_with_type_underscore.rs b/clippy_lints/src/let_with_type_underscore.rs index 5b0f95ffc377..24a4c321bdab 100644 --- a/clippy_lints/src/let_with_type_underscore.rs +++ b/clippy_lints/src/let_with_type_underscore.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use clippy_utils::source::{IntoSpan, SpanRangeExt}; +use rustc_ast::{Local, TyKind}; use rustc_errors::Applicability; -use rustc_hir::{LetStmt, TyKind}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::declare_lint_pass; declare_clippy_lint! { @@ -26,14 +26,14 @@ declare_clippy_lint! { } declare_lint_pass!(UnderscoreTyped => [LET_WITH_TYPE_UNDERSCORE]); -impl<'tcx> LateLintPass<'tcx> for UnderscoreTyped { - fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'_>) { - if let Some(ty) = local.ty // Ensure that it has a type defined - && let TyKind::Infer(()) = &ty.kind // that type is '_' +impl EarlyLintPass for UnderscoreTyped { + fn check_local(&mut self, cx: &EarlyContext<'_>, local: &Local) { + if let Some(ty) = &local.ty // Ensure that it has a type defined + && let TyKind::Infer = ty.kind // that type is '_' && local.span.eq_ctxt(ty.span) - && let sm = cx.tcx.sess.source_map() + && let sm = cx.sess().source_map() && !local.span.in_external_macro(sm) - && !is_from_proc_macro(cx, ty) + && !is_from_proc_macro(cx, &**ty) { let span_to_remove = sm .span_extend_to_prev_char_before(ty.span, ':', true) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d468993e7444..10f0faa44088 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -743,7 +743,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage)); store.register_late_pass(|_| Box::new(needless_maybe_sized::NeedlessMaybeSized)); store.register_late_pass(|_| Box::new(redundant_async_block::RedundantAsyncBlock)); - store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped)); + store.register_early_pass(|| Box::new(let_with_type_underscore::UnderscoreTyped)); store.register_late_pass(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(conf))); store.register_late_pass(|_| Box::new(unnecessary_struct_initialization::UnnecessaryStruct)); store.register_late_pass(move |_| Box::new(unnecessary_box_returns::UnnecessaryBoxReturns::new(conf))); From c9fbcdcfcdc5290639c2a76929bec1ed07c08f8e Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 22 Aug 2025 21:58:49 +0800 Subject: [PATCH 136/682] Add let in let-chain completion support Example --- ```rust fn f() { if true && $0 {} } ``` -> ```rust fn f() { if true && let $1 = $0 {} } ``` --- .../crates/ide-completion/src/context/analysis.rs | 10 +++++++--- .../crates/ide-completion/src/tests/expression.rs | 10 ++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 6eb1727b3319..7d6fa553001c 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -1171,19 +1171,23 @@ fn classify_name_ref<'db>( Some(res) }; - let is_in_condition = |it: &ast::Expr| { + fn is_in_condition(it: &ast::Expr) -> bool { (|| { let parent = it.syntax().parent()?; if let Some(expr) = ast::WhileExpr::cast(parent.clone()) { Some(expr.condition()? == *it) - } else if let Some(expr) = ast::IfExpr::cast(parent) { + } else if let Some(expr) = ast::IfExpr::cast(parent.clone()) { Some(expr.condition()? == *it) + } else if let Some(expr) = ast::BinExpr::cast(parent) + && expr.op_token()?.kind() == T![&&] + { + Some(is_in_condition(&expr.into())) } else { None } })() .unwrap_or(false) - }; + } let make_path_kind_expr = |expr: ast::Expr| { let it = expr.syntax(); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index 33f729f01664..07eaeea97e55 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -2275,3 +2275,13 @@ fn foo() { "#]], ); } + +#[test] +fn let_in_condition() { + check_edit("let", r#"fn f() { if $0 {} }"#, r#"fn f() { if let $1 = $0 {} }"#); +} + +#[test] +fn let_in_let_chain() { + check_edit("let", r#"fn f() { if true && $0 {} }"#, r#"fn f() { if true && let $1 = $0 {} }"#); +} From 183fbdc89b5f7f3944644a5d3d1c762d7ea35f90 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 23 Aug 2025 15:52:54 +0800 Subject: [PATCH 137/682] Fix `else` completion in `let _ = if x {} $0` --- .../ide-completion/src/context/analysis.rs | 11 +- .../ide-completion/src/tests/expression.rs | 347 ++++++++++++++++++ 2 files changed, 355 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 6eb1727b3319..fc2d9e41989b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -943,9 +943,14 @@ fn classify_name_ref<'db>( }; let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?; - ast::ExprStmt::cast(prev_sibling.clone()) - .and_then(|it| it.expr()) - .or_else(|| ast::Expr::cast(prev_sibling)) + match_ast! { + match prev_sibling { + ast::ExprStmt(stmt) => stmt.expr().filter(|_| stmt.semicolon_token().is_none()), + ast::LetStmt(stmt) => stmt.initializer().filter(|_| stmt.semicolon_token().is_none()), + ast::Expr(expr) => Some(expr), + _ => None, + } + } })(); matches!(prev_expr, Some(ast::Expr::IfExpr(_))) }; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index 33f729f01664..da5dddc6e8d7 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -1210,6 +1210,353 @@ fn foo() { if foo {} el$0 { let x = 92; } } sn ppd "#]], ); + check( + r#" +fn foo() { let x = if foo {} $0 } +"#, + expect![[r#" + fn foo() fn() + bt u32 u32 + kw async + kw const + kw crate:: + kw else + kw else if + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); + check( + r#" +fn foo() { let x = if foo {} el$0 } +"#, + expect![[r#" + fn foo() fn() + lc x () + bt u32 u32 + kw async + kw const + kw crate:: + kw else + kw else if + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); + check( + r#" +fn foo() { let x = if foo {} $0 let y = 92; } +"#, + expect![[r#" + fn foo() fn() + bt u32 u32 + kw async + kw const + kw crate:: + kw else + kw else if + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); + check( + r#" +fn foo() { let x = if foo {} el$0 let y = 92; } +"#, + expect![[r#" + fn foo() fn() + lc x () + bt u32 u32 + kw async + kw const + kw crate:: + kw else + kw else if + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); + check( + r#" +fn foo() { let x = if foo {} $0; } +"#, + expect![[r#" + fn foo() fn() + bt u32 u32 + kw async + kw const + kw crate:: + kw else + kw else if + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); + check( + r#" +fn foo() { let x = if foo {} el$0; } +"#, + expect![[r#" + fn foo() fn() + lc x () + bt u32 u32 + kw async + kw const + kw crate:: + kw else + kw else if + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); + check( + r#" +fn foo() { let x = if foo {} $0; let y = 92; } +"#, + expect![[r#" + fn foo() fn() + bt u32 u32 + kw async + kw const + kw crate:: + kw else + kw else if + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); + check( + r#" +fn foo() { let x = if foo {} $0 else {}; } +"#, + expect![[r#" + fn foo fn() + bt u32 u32 + kw async + kw const + kw crate:: + kw else + kw else if + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); } #[test] From 4715c6d41a949358e8dd8dd96de0650939c8bf6b Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 24 Aug 2025 02:26:37 +0300 Subject: [PATCH 138/682] Add an option to remove reborrows from adjustment inlay hints Reborrows are consecutive deref then ref. Make it the default because reborrows are mostly useless to the programmer. Also rename `rust-analyzer.inlayHints.expressionAdjustmentHints.enable: "reborrow"` to `rust-analyzer.inlayHints.expressionAdjustmentHints.enable: "borrows"`, as it's not about reborrows but about any ref/deref and it's confusing with the new setting. --- .../crates/ide/src/inlay_hints.rs | 4 +- .../crates/ide/src/inlay_hints/adjustment.rs | 49 ++++++++++++++++++- .../crates/ide/src/static_index.rs | 1 + .../rust-analyzer/src/cli/analysis_stats.rs | 1 + .../crates/rust-analyzer/src/config.rs | 17 +++++-- .../docs/book/src/configuration_generated.md | 11 +++++ .../rust-analyzer/editors/code/package.json | 10 ++++ 7 files changed, 88 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index d7d317166494..cc44ae8fc8b9 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -306,6 +306,7 @@ pub struct InlayHintsConfig { pub generic_parameter_hints: GenericParameterHints, pub chaining_hints: bool, pub adjustment_hints: AdjustmentHints, + pub adjustment_hints_disable_reborrows: bool, pub adjustment_hints_mode: AdjustmentHintsMode, pub adjustment_hints_hide_outside_unsafe: bool, pub closure_return_type_hints: ClosureReturnTypeHints, @@ -430,7 +431,7 @@ pub enum LifetimeElisionHints { #[derive(Clone, Debug, PartialEq, Eq)] pub enum AdjustmentHints { Always, - ReborrowOnly, + BorrowsOnly, Never, } @@ -886,6 +887,7 @@ mod tests { closure_return_type_hints: ClosureReturnTypeHints::Never, closure_capture_hints: false, adjustment_hints: AdjustmentHints::Never, + adjustment_hints_disable_reborrows: false, adjustment_hints_mode: AdjustmentHintsMode::Prefix, adjustment_hints_hide_outside_unsafe: false, binding_mode_hints: false, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs index e39a5f8889a2..0fd587a72840 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs @@ -47,7 +47,22 @@ pub(super) fn hints( let descended = sema.descend_node_into_attributes(expr.clone()).pop(); let desc_expr = descended.as_ref().unwrap_or(expr); - let adjustments = sema.expr_adjustments(desc_expr).filter(|it| !it.is_empty())?; + let mut adjustments = sema.expr_adjustments(desc_expr).filter(|it| !it.is_empty())?; + + if config.adjustment_hints_disable_reborrows { + // Remove consecutive deref-ref, i.e. reborrows. + let mut i = 0; + while i < adjustments.len().saturating_sub(1) { + let [current, next, ..] = &adjustments[i..] else { unreachable!() }; + if matches!(current.kind, Adjust::Deref(None)) + && matches!(next.kind, Adjust::Borrow(AutoBorrow::Ref(_))) + { + adjustments.splice(i..i + 2, []); + } else { + i += 1; + } + } + } if let ast::Expr::BlockExpr(_) | ast::Expr::IfExpr(_) | ast::Expr::MatchExpr(_) = desc_expr { // Don't show unnecessary reborrows for these, they will just repeat the inner ones again @@ -716,6 +731,38 @@ fn hello(it: &&[impl T]) { //^^(&** //^^) } +"#, + ); + } + + #[test] + fn disable_reborrows() { + check_with_config( + InlayHintsConfig { + adjustment_hints: AdjustmentHints::Always, + adjustment_hints_disable_reborrows: true, + ..DISABLED_CONFIG + }, + r#" +#![rustc_coherence_is_core] + +trait ToOwned { + type Owned; + fn to_owned(&self) -> Self::Owned; +} + +struct String; +impl ToOwned for str { + type Owned = String; + fn to_owned(&self) -> Self::Owned { String } +} + +fn a(s: &String) {} + +fn main() { + let s = "".to_owned(); + a(&s) +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs index 14b6529c61f8..8c3275df1baa 100644 --- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs +++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs @@ -169,6 +169,7 @@ impl StaticIndex<'_> { closure_return_type_hints: crate::ClosureReturnTypeHints::WithBlock, lifetime_elision_hints: crate::LifetimeElisionHints::Never, adjustment_hints: crate::AdjustmentHints::Never, + adjustment_hints_disable_reborrows: true, adjustment_hints_mode: AdjustmentHintsMode::Prefix, adjustment_hints_hide_outside_unsafe: false, implicit_drop_hints: false, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index 6c0aa19f5748..b17186f8d7b1 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -1137,6 +1137,7 @@ impl flags::AnalysisStats { }, chaining_hints: true, adjustment_hints: ide::AdjustmentHints::Always, + adjustment_hints_disable_reborrows: true, adjustment_hints_mode: ide::AdjustmentHintsMode::Postfix, adjustment_hints_hide_outside_unsafe: false, closure_return_type_hints: ide::ClosureReturnTypeHints::Always, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index d4cd56dc55bf..c2252185a3aa 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -226,6 +226,14 @@ config_data! { inlayHints_discriminantHints_enable: DiscriminantHintsDef = DiscriminantHintsDef::Never, + /// Disable reborrows in expression adjustments inlay hints. + /// + /// Reborrows are a pair of a builtin deref then borrow, i.e. `&*`. They are inserted by the compiler but are mostly useless to the programmer. + /// + /// Note: if the deref is not builtin (an overloaded deref), or the borrow is `&raw const`/`&raw mut`, they are not removed. + inlayHints_expressionAdjustmentHints_disableReborrows: bool = + true, + /// Show inlay hints for type adjustments. inlayHints_expressionAdjustmentHints_enable: AdjustmentHintsDef = AdjustmentHintsDef::Never, @@ -1888,12 +1896,14 @@ impl Config { AdjustmentHintsDef::Always => ide::AdjustmentHints::Always, AdjustmentHintsDef::Never => match self.inlayHints_reborrowHints_enable() { ReborrowHintsDef::Always | ReborrowHintsDef::Mutable => { - ide::AdjustmentHints::ReborrowOnly + ide::AdjustmentHints::BorrowsOnly } ReborrowHintsDef::Never => ide::AdjustmentHints::Never, }, - AdjustmentHintsDef::Reborrow => ide::AdjustmentHints::ReborrowOnly, + AdjustmentHintsDef::Borrows => ide::AdjustmentHints::BorrowsOnly, }, + adjustment_hints_disable_reborrows: *self + .inlayHints_expressionAdjustmentHints_disableReborrows(), adjustment_hints_mode: match self.inlayHints_expressionAdjustmentHints_mode() { AdjustmentHintsModeDef::Prefix => ide::AdjustmentHintsMode::Prefix, AdjustmentHintsModeDef::Postfix => ide::AdjustmentHintsMode::Postfix, @@ -2822,7 +2832,8 @@ enum ReborrowHintsDef { #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "snake_case")] enum AdjustmentHintsDef { - Reborrow, + #[serde(alias = "Reborrow")] + Borrows, #[serde(with = "true_or_always")] #[serde(untagged)] Always, diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md index 6ee956fe0dbf..9a51212462db 100644 --- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -959,6 +959,17 @@ Default: `"never"` Show enum variant discriminant hints. +## rust-analyzer.inlayHints.expressionAdjustmentHints.disableReborrows {#inlayHints.expressionAdjustmentHints.disableReborrows} + +Default: `true` + +Disable reborrows in expression adjustments inlay hints. + +Reborrows are a pair of a builtin deref then borrow, i.e. `&*`. They are inserted by the compiler but are mostly useless to the programmer. + +Note: if the deref is not builtin (an overloaded deref), or the borrow is `&raw const`/`&raw mut`, they are not removed. + + ## rust-analyzer.inlayHints.expressionAdjustmentHints.enable {#inlayHints.expressionAdjustmentHints.enable} Default: `"never"` diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 4975ca858671..2b2e25e11c88 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -2209,6 +2209,16 @@ } } }, + { + "title": "Inlay Hints", + "properties": { + "rust-analyzer.inlayHints.expressionAdjustmentHints.disableReborrows": { + "markdownDescription": "Disable reborrows in expression adjustments inlay hints.\n\nReborrows are a pair of a builtin deref then borrow, i.e. `&*`. They are inserted by the compiler but are mostly useless to the programmer.\n\nNote: if the deref is not builtin (an overloaded deref), or the borrow is `&raw const`/`&raw mut`, they are not removed.", + "default": true, + "type": "boolean" + } + } + }, { "title": "Inlay Hints", "properties": { From 5e81912dae790647395834a027263a8c6160fd0b Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 22 Aug 2025 17:31:55 +0800 Subject: [PATCH 139/682] replace_arith_op not applicable on selected --- .../src/handlers/replace_arith_op.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_arith_op.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_arith_op.rs index 6b385a03625b..440ab4d4604f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_arith_op.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_arith_op.rs @@ -102,6 +102,9 @@ fn is_primitive_int(ctx: &AssistContext<'_>, expr: &ast::Expr) -> bool { /// Extract the operands of an arithmetic expression (e.g. `1 + 2` or `1.checked_add(2)`) fn parse_binary_op(ctx: &AssistContext<'_>) -> Option<(ast::Expr, ArithOp, ast::Expr)> { + if !ctx.has_empty_selection() { + return None; + } let expr = ctx.find_node_at_offset::()?; let op = match expr.op_kind() { @@ -163,7 +166,7 @@ impl ArithKind { #[cfg(test)] mod tests { - use crate::tests::check_assist; + use crate::tests::{check_assist, check_assist_not_applicable}; use super::*; @@ -220,6 +223,18 @@ fn main() { fn main() { let x = 1.wrapping_add(2); } +"#, + ) + } + + #[test] + fn replace_arith_not_applicable_with_non_empty_selection() { + check_assist_not_applicable( + replace_arith_with_checked, + r#" +fn main() { + let x = 1 $0+$0 2; +} "#, ) } From c92af4d23285428c92e67fedb6d73a9a86a90cff Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 22 Aug 2025 14:21:52 +0800 Subject: [PATCH 140/682] Add ReturnExpr completion suggest --- .../ide-completion/src/context/analysis.rs | 21 +++++++++-- .../ide-completion/src/context/tests.rs | 36 +++++++++++++++++++ 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 6eb1727b3319..19f3d157beb7 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -559,7 +559,7 @@ fn expected_type_and_name<'db>( token: &SyntaxToken, name_like: &ast::NameLike, ) -> (Option>, Option) { - let token = prev_assign_token_at_trivia(token.clone()); + let token = prev_special_biased_token_at_trivia(token.clone()); let mut node = match token.parent() { Some(it) => it, None => return (None, None), @@ -724,6 +724,18 @@ fn expected_type_and_name<'db>( let def = sema.to_def(&it); (def.map(|def| def.ret_type(sema.db)), None) }, + ast::ReturnExpr(it) => { + let fn_ = sema.ancestors_with_macros(it.syntax().clone()) + .find_map(Either::::cast); + let ty = fn_.and_then(|f| match f { + Either::Left(f) => Some(sema.to_def(&f)?.ret_type(sema.db)), + Either::Right(f) => { + let ty = sema.type_of_expr(&f.into())?.original.as_callable(sema.db)?; + Some(ty.return_type()) + }, + }); + (ty, None) + }, ast::ClosureExpr(it) => { let ty = sema.type_of_expr(&it.into()); ty.and_then(|ty| ty.original.as_callable(sema.db)) @@ -1868,7 +1880,7 @@ fn next_non_trivia_sibling(ele: SyntaxElement) -> Option { None } -fn prev_assign_token_at_trivia(mut token: SyntaxToken) -> SyntaxToken { +fn prev_special_biased_token_at_trivia(mut token: SyntaxToken) -> SyntaxToken { while token.kind().is_trivia() && let Some(prev) = token.prev_token() && let T![=] @@ -1881,7 +1893,10 @@ fn prev_assign_token_at_trivia(mut token: SyntaxToken) -> SyntaxToken { | T![-=] | T![|=] | T![&=] - | T![^=] = prev.kind() + | T![^=] + | T![return] + | T![break] + | T![continue] = prev.kind() { token = prev } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs index 7a8c70f190ef..9b1e086b666b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs @@ -484,3 +484,39 @@ fn foo() { expect![[r#"ty: State, name: ?"#]], ); } + +#[test] +fn expected_type_return_expr() { + check_expected_type_and_name( + r#" +enum State { Stop } +fn foo() -> State { + let _: i32 = if true { + 8 + } else { + return $0; + }; +} +"#, + expect![[r#"ty: State, name: ?"#]], + ); +} + +#[test] +fn expected_type_return_expr_in_closure() { + check_expected_type_and_name( + r#" +enum State { Stop } +fn foo() { + let _f: fn() -> State = || { + let _: i32 = if true { + 8 + } else { + return $0; + }; + }; +} +"#, + expect![[r#"ty: State, name: ?"#]], + ); +} From 31245cabecfc21c837745ec6253d63d5fc9ac75f Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 24 Aug 2025 08:49:40 +0300 Subject: [PATCH 141/682] Fix opaque generics The parent generics were incorrectly not considered for TAIT. I'm not convinced we should follow rustc here, also there are items (opaques) with more than 1 parent (opaque -> fn/type alias -> impl/trait) and I'm not sure we properly account for that in all places, but for now I left it as-is. Also fix a bug where lifetimes' indices were incorrect when there is a self param (they started from 0 instead of 1). --- .../crates/hir-ty/src/next_solver/generics.rs | 68 +++++++------------ .../crates/hir-ty/src/next_solver/util.rs | 1 - .../crates/hir-ty/src/tests/regression.rs | 2 + .../hir-ty/src/tests/regression/new_solver.rs | 26 +++++++ 4 files changed, 54 insertions(+), 43 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generics.rs index 48f5e73f2562..a3ba8eb83453 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generics.rs @@ -12,7 +12,7 @@ use hir_def::{ }, }; use hir_expand::name::Name; -use intern::Symbol; +use intern::{Symbol, sym}; use la_arena::Arena; use rustc_type_ir::inherent::Ty as _; use triomphe::Arc; @@ -24,18 +24,13 @@ use super::{Const, EarlyParamRegion, ErrorGuaranteed, ParamConst, Region, Solver use super::{DbInterner, GenericArg}; pub(crate) fn generics(db: &dyn HirDatabase, def: SolverDefId) -> Generics { - let mk_lt = |(index, (_, lt)): (usize, (_, &LifetimeParamData))| { + let mk_lt = |index, lt: &LifetimeParamData| { let name = lt.name.symbol().clone(); - let index = index as u32; let kind = GenericParamDefKind::Lifetime; GenericParamDef { name, index, kind } }; - let mk_ty = |len_lt, (index, p): (usize, &TypeOrConstParamData)| { - let name = p - .name() - .map(|n| n.symbol().clone()) - .unwrap_or_else(|| Name::missing().symbol().clone()); - let index = (len_lt + index) as u32; + let mk_ty = |index, p: &TypeOrConstParamData| { + let name = p.name().map(|n| n.symbol().clone()).unwrap_or_else(|| sym::MISSING_NAME); let kind = match p { TypeOrConstParamData::TypeParamData(_) => GenericParamDefKind::Type, TypeOrConstParamData::ConstParamData(_) => GenericParamDefKind::Const, @@ -43,33 +38,25 @@ pub(crate) fn generics(db: &dyn HirDatabase, def: SolverDefId) -> Generics { GenericParamDef { name, index, kind } }; let own_params_for_generic_params = |params: &GenericParams| { - if params.trait_self_param().is_some() { - let len_lt = params.len_lifetimes() + 1; - params - .iter_type_or_consts() - .take(1) - .enumerate() - .map(|t| mk_ty(0, (t.0, t.1.1))) - .chain(params.iter_lt().enumerate().map(mk_lt)) - .chain( - params - .iter_type_or_consts() - .skip(1) - .enumerate() - .map(|t| mk_ty(len_lt, (t.0, t.1.1))), - ) - .collect() - } else { - let len_lt = params.len_lifetimes(); - params - .iter_lt() - .enumerate() - .map(mk_lt) - .chain( - params.iter_type_or_consts().enumerate().map(|t| mk_ty(len_lt, (t.0, t.1.1))), - ) - .collect() + let mut result = Vec::with_capacity(params.len()); + let mut type_and_consts = params.iter_type_or_consts(); + let mut index = 0; + if let Some(self_param) = params.trait_self_param() { + result.push(mk_ty(0, ¶ms[self_param])); + type_and_consts.next(); + index += 1; } + result.extend(params.iter_lt().map(|(_, data)| { + let lt = mk_lt(index, data); + index += 1; + lt + })); + result.extend(type_and_consts.map(|(_, data)| { + let ty = mk_ty(index, data); + index += 1; + ty + })); + result }; let (parent, own_params) = match (def.try_into(), def) { @@ -82,12 +69,9 @@ pub(crate) fn generics(db: &dyn HirDatabase, def: SolverDefId) -> Generics { // The opaque type itself does not have generics - only the parent function (Some(GenericDefId::FunctionId(function_id)), vec![]) } - crate::ImplTraitId::TypeAliasImplTrait(type_alias_id, _) => ( - None, - own_params_for_generic_params( - &db.generic_params(GenericDefId::TypeAliasId(type_alias_id)), - ), - ), + crate::ImplTraitId::TypeAliasImplTrait(type_alias_id, _) => { + (Some(type_alias_id.into()), Vec::new()) + } crate::ImplTraitId::AsyncBlockTypeImplTrait(def, _) => { let param = TypeOrConstParamData::TypeParamData(TypeParamData { name: None, @@ -95,7 +79,7 @@ pub(crate) fn generics(db: &dyn HirDatabase, def: SolverDefId) -> Generics { provenance: TypeParamProvenance::TypeParamList, }); // Yes, there is a parent but we don't include it in the generics - (None, vec![mk_ty(0, (0, ¶m))]) + (None, vec![mk_ty(0, ¶m)]) } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs index cedc203f7f5a..1db02e9eb611 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs @@ -685,7 +685,6 @@ pub fn explicit_item_bounds<'db>( LifetimeElisionKind::AnonymousReportError, ); - let trait_args = GenericArgs::identity_for_item(interner, trait_.into()); let item_args = GenericArgs::identity_for_item(interner, def_id); let interner_ty = Ty::new_projection_from_args(interner, def_id, item_args); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index 966433369aa8..6a3f2286215f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -1,3 +1,5 @@ +mod new_solver; + use expect_test::expect; use super::{check_infer, check_no_mismatches, check_types}; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs new file mode 100644 index 000000000000..059f4ad32a53 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -0,0 +1,26 @@ +use expect_test::expect; + +use super::check_infer; + +#[test] +fn opaque_generics() { + check_infer( + r#" +//- minicore: iterator +pub struct Grid {} + +impl<'a> IntoIterator for &'a Grid { + type Item = &'a (); + + type IntoIter = impl Iterator; + + fn into_iter(self) -> Self::IntoIter { + } +} + "#, + expect![[r#" + 150..154 'self': &'a Grid + 174..181 '{ }': impl Iterator + "#]], + ); +} From 5ddf6846d5659ed28e1ea25e273e4c84082cf730 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Sun, 24 Aug 2025 23:17:23 +0900 Subject: [PATCH 142/682] fix: Masquerade as nightly cargo when invoking flycheck with `-Zscript` --- src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index 233bedec6900..01ac75c09aee 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -588,6 +588,7 @@ impl FlycheckActor { cmd.arg("--manifest-path"); cmd.arg(manifest_path); if manifest_path.extension() == Some("rs") { + cmd.env("__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS", "nightly"); cmd.arg("-Zscript"); } } From ca42d0775a33ae6f977e4cd42fab17f9aced6a59 Mon Sep 17 00:00:00 2001 From: Emmanuel Ferdman Date: Sun, 24 Aug 2025 08:03:02 -0700 Subject: [PATCH 143/682] Fix rust-analyzer-contributors reference Signed-off-by: Emmanuel Ferdman --- src/tools/rust-analyzer/docs/book/src/contributing/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/docs/book/src/contributing/README.md b/src/tools/rust-analyzer/docs/book/src/contributing/README.md index 57c7a9c5996d..ad2816b18ac1 100644 --- a/src/tools/rust-analyzer/docs/book/src/contributing/README.md +++ b/src/tools/rust-analyzer/docs/book/src/contributing/README.md @@ -276,7 +276,7 @@ There are two sets of people with extra permissions: Feel free to request a review or assign any PR to a reviewer with the relevant expertise to bring the work to their attention. Don't feel pressured to review assigned PRs though. If you don't feel like reviewing for whatever reason, someone else will pick the review up (but please speak up if you don't feel like it)! -* The [rust-lang](https://github.com/rust-lang) team [t-rust-analyzer-contributors]([https://github.com/orgs/rust-analyzer/teams/triage](https://github.com/rust-lang/team/blob/master/teams/rust-analyzer-contributors.toml)). +* The [rust-lang](https://github.com/rust-lang) team [t-rust-analyzer-contributors](https://github.com/rust-lang/team/blob/master/teams/rust-analyzer-contributors.toml). This team has general triaging permissions allowing to label, close and re-open issues. ## Synchronizing subtree changes From 40ff07622e5122aecc2cc03a61152782bc2a0568 Mon Sep 17 00:00:00 2001 From: yanglsh Date: Sun, 27 Jul 2025 18:06:59 +0800 Subject: [PATCH 144/682] fix: `never_loop` forget to remove break in nested loop --- clippy_lints/src/loops/never_loop.rs | 59 +++++++++++++---- tests/ui/never_loop.rs | 24 +++++++ tests/ui/never_loop.stderr | 97 +++++++++++++++++++++++++++- tests/ui/never_loop_fixable.fixed | 2 +- tests/ui/never_loop_fixable.rs | 2 +- 5 files changed, 170 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 8a253ae5810f..0ac66fa98650 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -22,7 +22,10 @@ pub(super) fn check<'tcx>( for_loop: Option<&ForLoop<'_>>, ) { match never_loop_block(cx, block, &mut Vec::new(), loop_id) { - NeverLoopResult::Diverging { ref break_spans } => { + NeverLoopResult::Diverging { + ref break_spans, + ref never_spans, + } => { span_lint_and_then(cx, NEVER_LOOP, span, "this loop never actually loops", |diag| { if let Some(ForLoop { arg: iterator, @@ -34,12 +37,16 @@ pub(super) fn check<'tcx>( { // If the block contains a break or continue, or if the loop has a label, `MachineApplicable` is not // appropriate. - let app = if !contains_any_break_or_continue(block) && label.is_none() { + let mut app = if !contains_any_break_or_continue(block) && label.is_none() { Applicability::MachineApplicable } else { Applicability::Unspecified }; + if !never_spans.is_empty() { + app = Applicability::HasPlaceholders; + } + let mut suggestions = vec![( for_span.with_hi(iterator.span.hi()), for_to_if_let_sugg(cx, iterator, pat), @@ -51,6 +58,13 @@ pub(super) fn check<'tcx>( suggestions, app, ); + + for span in never_spans { + diag.span_help( + *span, + "this code is unreachable. Consider moving the reachable parts out", + ); + } } }); }, @@ -77,13 +91,16 @@ fn contains_any_break_or_continue(block: &Block<'_>) -> bool { /// The first two bits of information are in this enum, and the last part is in the /// `local_labels` variable, which contains a list of `(block_id, reachable)` pairs ordered by /// scope. -#[derive(Clone)] +#[derive(Clone, Debug)] enum NeverLoopResult { /// A continue may occur for the main loop. MayContinueMainLoop, /// We have not encountered any main loop continue, /// but we are diverging (subsequent control flow is not reachable) - Diverging { break_spans: Vec }, + Diverging { + break_spans: Vec, + never_spans: Vec, + }, /// We have not encountered any main loop continue, /// and subsequent control flow is (possibly) reachable Normal, @@ -128,14 +145,18 @@ fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult ( NeverLoopResult::Diverging { break_spans: mut break_spans1, + never_spans: mut never_spans1, }, NeverLoopResult::Diverging { break_spans: mut break_spans2, + never_spans: mut never_spans2, }, ) => { break_spans1.append(&mut break_spans2); + never_spans1.append(&mut never_spans2); NeverLoopResult::Diverging { break_spans: break_spans1, + never_spans: never_spans1, } }, } @@ -207,6 +228,8 @@ fn all_spans_after_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> Vec { } return vec![stmt.span]; + } else if let Node::Block(_) = cx.tcx.parent_hir_node(expr.hir_id) { + return vec![expr.span]; } vec![] @@ -270,10 +293,13 @@ fn never_loop_expr<'tcx>( ExprKind::Match(e, arms, _) => { let e = never_loop_expr(cx, e, local_labels, main_loop_id); combine_seq(e, || { - arms.iter() - .fold(NeverLoopResult::Diverging { break_spans: vec![] }, |a, b| { - combine_branches(a, never_loop_expr(cx, b.body, local_labels, main_loop_id)) - }) + arms.iter().fold( + NeverLoopResult::Diverging { + break_spans: vec![], + never_spans: vec![], + }, + |a, b| combine_branches(a, never_loop_expr(cx, b.body, local_labels, main_loop_id)), + ) }) }, ExprKind::Block(b, _) => { @@ -296,6 +322,7 @@ fn never_loop_expr<'tcx>( } else { NeverLoopResult::Diverging { break_spans: all_spans_after_expr(cx, expr), + never_spans: vec![], } } }, @@ -306,7 +333,10 @@ fn never_loop_expr<'tcx>( combine_seq(first, || { // checks if break targets a block instead of a loop mark_block_as_reachable(expr, local_labels); - NeverLoopResult::Diverging { break_spans: vec![] } + NeverLoopResult::Diverging { + break_spans: vec![], + never_spans: vec![], + } }) }, ExprKind::Break(dest, e) => { @@ -322,11 +352,15 @@ fn never_loop_expr<'tcx>( } else { all_spans_after_expr(cx, expr) }, + never_spans: vec![], } }) }, ExprKind::Become(e) => combine_seq(never_loop_expr(cx, e, local_labels, main_loop_id), || { - NeverLoopResult::Diverging { break_spans: vec![] } + NeverLoopResult::Diverging { + break_spans: vec![], + never_spans: vec![], + } }), ExprKind::InlineAsm(asm) => combine_seq_many(asm.operands.iter().map(|(o, _)| match o { InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => { @@ -356,7 +390,10 @@ fn never_loop_expr<'tcx>( }; let result = combine_seq(result, || { if cx.typeck_results().expr_ty(expr).is_never() { - NeverLoopResult::Diverging { break_spans: vec![] } + NeverLoopResult::Diverging { + break_spans: vec![], + never_spans: all_spans_after_expr(cx, expr), + } } else { NeverLoopResult::Normal } diff --git a/tests/ui/never_loop.rs b/tests/ui/never_loop.rs index 48d4b8ad1519..01db64a446c0 100644 --- a/tests/ui/never_loop.rs +++ b/tests/ui/never_loop.rs @@ -498,3 +498,27 @@ fn issue15059() { () } } + +fn issue15350() { + 'bar: for _ in 0..100 { + //~^ never_loop + loop { + //~^ never_loop + println!("This will still run"); + break 'bar; + } + } + + 'foo: for _ in 0..100 { + //~^ never_loop + loop { + //~^ never_loop + println!("This will still run"); + loop { + //~^ never_loop + println!("This will still run"); + break 'foo; + } + } + } +} diff --git a/tests/ui/never_loop.stderr b/tests/ui/never_loop.stderr index 54b463266a3a..4fda06cff4ab 100644 --- a/tests/ui/never_loop.stderr +++ b/tests/ui/never_loop.stderr @@ -193,6 +193,19 @@ LL | | return; LL | | } | |_____^ | +help: this code is unreachable. Consider moving the reachable parts out + --> tests/ui/never_loop.rs:436:9 + | +LL | / loop { +LL | | +LL | | break 'outer; +LL | | } + | |_________^ +help: this code is unreachable. Consider moving the reachable parts out + --> tests/ui/never_loop.rs:440:9 + | +LL | return; + | ^^^^^^^ help: if you need the first element of the iterator, try writing | LL - 'outer: for v in 0..10 { @@ -297,5 +310,87 @@ LL ~ LL ~ | -error: aborting due to 24 previous errors +error: this loop never actually loops + --> tests/ui/never_loop.rs:503:5 + | +LL | / 'bar: for _ in 0..100 { +LL | | +LL | | loop { +... | +LL | | } + | |_____^ + | +help: this code is unreachable. Consider moving the reachable parts out + --> tests/ui/never_loop.rs:505:9 + | +LL | / loop { +LL | | +LL | | println!("This will still run"); +LL | | break 'bar; +LL | | } + | |_________^ +help: if you need the first element of the iterator, try writing + | +LL - 'bar: for _ in 0..100 { +LL + if let Some(_) = (0..100).next() { + | + +error: this loop never actually loops + --> tests/ui/never_loop.rs:505:9 + | +LL | / loop { +LL | | +LL | | println!("This will still run"); +LL | | break 'bar; +LL | | } + | |_________^ + +error: this loop never actually loops + --> tests/ui/never_loop.rs:512:5 + | +LL | / 'foo: for _ in 0..100 { +LL | | +LL | | loop { +... | +LL | | } + | |_____^ + | +help: this code is unreachable. Consider moving the reachable parts out + --> tests/ui/never_loop.rs:514:9 + | +LL | / loop { +LL | | +LL | | println!("This will still run"); +LL | | loop { +... | +LL | | } + | |_________^ +help: if you need the first element of the iterator, try writing + | +LL - 'foo: for _ in 0..100 { +LL + if let Some(_) = (0..100).next() { + | + +error: this loop never actually loops + --> tests/ui/never_loop.rs:514:9 + | +LL | / loop { +LL | | +LL | | println!("This will still run"); +LL | | loop { +... | +LL | | } + | |_________^ + +error: this loop never actually loops + --> tests/ui/never_loop.rs:517:13 + | +LL | / loop { +LL | | +LL | | println!("This will still run"); +LL | | break 'foo; +LL | | } + | |_____________^ + +error: aborting due to 29 previous errors diff --git a/tests/ui/never_loop_fixable.fixed b/tests/ui/never_loop_fixable.fixed index 5bc9ff1bb4df..00c2af93a28f 100644 --- a/tests/ui/never_loop_fixable.fixed +++ b/tests/ui/never_loop_fixable.fixed @@ -1,4 +1,4 @@ -#![allow(clippy::iter_next_slice, clippy::needless_return)] +#![allow(clippy::iter_next_slice, clippy::needless_return, clippy::redundant_pattern_matching)] fn no_break_or_continue_loop() { if let Some(i) = [1, 2, 3].iter().next() { diff --git a/tests/ui/never_loop_fixable.rs b/tests/ui/never_loop_fixable.rs index 9782bc107e9a..de85599f094d 100644 --- a/tests/ui/never_loop_fixable.rs +++ b/tests/ui/never_loop_fixable.rs @@ -1,4 +1,4 @@ -#![allow(clippy::iter_next_slice, clippy::needless_return)] +#![allow(clippy::iter_next_slice, clippy::needless_return, clippy::redundant_pattern_matching)] fn no_break_or_continue_loop() { for i in [1, 2, 3].iter() { From 56bc9c35b9f625154f42f53f4b1e7843f02533ad Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 25 Aug 2025 16:44:42 +0800 Subject: [PATCH 145/682] Fix ExprStmt delete semicolon for toggle_macro_delimiter --- .../src/handlers/toggle_macro_delimiter.rs | 36 +++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs index 504e12f93df6..bf1546986ed2 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs @@ -1,6 +1,6 @@ use ide_db::assists::AssistId; use syntax::{ - AstNode, T, + AstNode, SyntaxToken, T, ast::{self, syntax_factory::SyntaxFactory}, }; @@ -39,7 +39,7 @@ pub(crate) fn toggle_macro_delimiter(acc: &mut Assists, ctx: &AssistContext<'_>) let makro = ctx.find_node_at_offset::()?; let cursor_offset = ctx.offset(); - let semicolon = makro.semicolon_token(); + let semicolon = macro_semicolon(&makro); let token_tree = makro.token_tree()?; let ltoken = token_tree.left_delimiter_token()?; @@ -95,6 +95,14 @@ pub(crate) fn toggle_macro_delimiter(acc: &mut Assists, ctx: &AssistContext<'_>) ) } +fn macro_semicolon(makro: &ast::MacroCall) -> Option { + makro.semicolon_token().or_else(|| { + let macro_expr = ast::MacroExpr::cast(makro.syntax().parent()?)?; + let expr_stmt = ast::ExprStmt::cast(macro_expr.syntax().parent()?)?; + expr_stmt.semicolon_token() + }) +} + #[cfg(test)] mod tests { use crate::tests::{check_assist, check_assist_not_applicable}; @@ -119,7 +127,29 @@ macro_rules! sth { sth!{ } "#, - ) + ); + + check_assist( + toggle_macro_delimiter, + r#" +macro_rules! sth { + () => {}; +} + +fn foo() { + sth!$0( ); +} + "#, + r#" +macro_rules! sth { + () => {}; +} + +fn foo() { + sth!{ } +} + "#, + ); } #[test] From a0aa1c1360916590f9a10d1b3d8586771c839d38 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 24 Aug 2025 16:44:28 +0300 Subject: [PATCH 146/682] Cache trait solving across queries in the same revision Caching trait solving can do a lot to speed. Unfortunately it also consume a huge amount of memory. Therefore, as part of the migration to the new solver Jack Huey disabled caching of trait solving (he made the query transparent). The PR proposes a middle ground: do cache trait solving, but only for the same revision. This allows us to be safe because during a revision the inputs cannot change. The result is hopefully much better performance to features that tend to do a bulk of trait solving, and also repeat the same query (e.g. inference then IDE features). There is another limitation: results are only cached in the same thread, to remove the need for synchronization which will be expensive. More measurements are required to check whether it's better to use a synchronized global cache, or maybe stay with a thread-local cache but batch multiple feature requests (highlighting, inlay hints etc.) of the same file to the same thread. Alongside the actual cache we store the revision, because we need to verify it (we can't eagerly clear caches when incrementing the revision), and also the address of the db to prevent multiple dbs from interleaving (this is mostly relevant in tests, although injected highlighting also uses a new db, therefore maybe it's better to move it to a separate thread). This "games" analysis-stats to both be way faster and use way more memory; the former is because analysis-stats doesn't increment revisions, therefore all queries share the cache and hit ratio is way too good, the latter is because analysis-stats doesn't increment revisions and therefore the cache isn't cleared. Both are not representative of a typical IDE scenario. --- .../rust-analyzer/crates/base-db/src/lib.rs | 28 ++++++- .../crates/hir-def/src/test_db.rs | 21 ++++- .../rust-analyzer/crates/hir-ty/src/infer.rs | 82 +++++++++---------- .../crates/hir-ty/src/mir/lower.rs | 4 +- .../crates/hir-ty/src/next_solver/interner.rs | 39 +++++---- .../crates/hir-ty/src/test_db.rs | 23 +++++- .../rust-analyzer/crates/ide-db/src/lib.rs | 9 +- 7 files changed, 140 insertions(+), 66 deletions(-) diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs index dbf949c470c4..14544acc11bd 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs @@ -7,7 +7,12 @@ pub use salsa_macros; mod change; mod input; -use std::{cell::RefCell, hash::BuildHasherDefault, panic, sync::Once}; +use std::{ + cell::RefCell, + hash::BuildHasherDefault, + panic, + sync::{Once, atomic::AtomicUsize}, +}; pub use crate::{ change::FileChange, @@ -328,6 +333,27 @@ pub trait SourceDatabase: salsa::Database { #[doc(hidden)] fn crates_map(&self) -> Arc; + + fn nonce_and_revision(&self) -> (Nonce, salsa::Revision); +} + +static NEXT_NONCE: AtomicUsize = AtomicUsize::new(0); + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct Nonce(usize); + +impl Default for Nonce { + #[inline] + fn default() -> Self { + Nonce::new() + } +} + +impl Nonce { + #[inline] + pub fn new() -> Nonce { + Nonce(NEXT_NONCE.fetch_add(1, std::sync::atomic::Ordering::SeqCst)) + } } /// Crate related data shared by the whole workspace. diff --git a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs index e30a5b65a1f7..1e2f354f975c 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs @@ -3,7 +3,7 @@ use std::{fmt, panic, sync::Mutex}; use base_db::{ - Crate, CrateGraphBuilder, CratesMap, FileSourceRootInput, FileText, RootQueryDb, + Crate, CrateGraphBuilder, CratesMap, FileSourceRootInput, FileText, Nonce, RootQueryDb, SourceDatabase, SourceRoot, SourceRootId, SourceRootInput, }; use hir_expand::{InFile, files::FilePosition}; @@ -20,12 +20,12 @@ use crate::{ }; #[salsa_macros::db] -#[derive(Clone)] pub(crate) struct TestDB { storage: salsa::Storage, files: Arc, crates_map: Arc, events: Arc>>>, + nonce: Nonce, } impl Default for TestDB { @@ -44,6 +44,7 @@ impl Default for TestDB { events, files: Default::default(), crates_map: Default::default(), + nonce: Nonce::new(), }; this.set_expand_proc_attr_macros_with_durability(true, Durability::HIGH); // This needs to be here otherwise `CrateGraphBuilder` panics. @@ -53,6 +54,18 @@ impl Default for TestDB { } } +impl Clone for TestDB { + fn clone(&self) -> Self { + Self { + storage: self.storage.clone(), + files: self.files.clone(), + crates_map: self.crates_map.clone(), + events: self.events.clone(), + nonce: Nonce::new(), + } + } +} + #[salsa_macros::db] impl salsa::Database for TestDB {} @@ -117,6 +130,10 @@ impl SourceDatabase for TestDB { fn crates_map(&self) -> Arc { self.crates_map.clone() } + + fn nonce_and_revision(&self) -> (Nonce, salsa::Revision) { + (self.nonce, salsa::plumbing::ZalsaDatabase::zalsa(self).current_revision()) + } } impl TestDB { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index d778cc0e30ed..71b33a0e9035 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -88,54 +88,52 @@ pub(crate) use closure::{CaptureKind, CapturedItem, CapturedItemWithoutTy}; /// The entry point of type inference. pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc { - crate::next_solver::with_new_cache(|| { - let _p = tracing::info_span!("infer_query").entered(); - let resolver = def.resolver(db); - let body = db.body(def); - let mut ctx = InferenceContext::new(db, def, &body, resolver); + let _p = tracing::info_span!("infer_query").entered(); + let resolver = def.resolver(db); + let body = db.body(def); + let mut ctx = InferenceContext::new(db, def, &body, resolver); - match def { - DefWithBodyId::FunctionId(f) => { - ctx.collect_fn(f); - } - DefWithBodyId::ConstId(c) => ctx.collect_const(c, &db.const_signature(c)), - DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_signature(s)), - DefWithBodyId::VariantId(v) => { - ctx.return_ty = TyBuilder::builtin( - match db.enum_signature(v.lookup(db).parent).variant_body_type() { - hir_def::layout::IntegerType::Pointer(signed) => match signed { - true => BuiltinType::Int(BuiltinInt::Isize), - false => BuiltinType::Uint(BuiltinUint::Usize), - }, - hir_def::layout::IntegerType::Fixed(size, signed) => match signed { - true => BuiltinType::Int(match size { - Integer::I8 => BuiltinInt::I8, - Integer::I16 => BuiltinInt::I16, - Integer::I32 => BuiltinInt::I32, - Integer::I64 => BuiltinInt::I64, - Integer::I128 => BuiltinInt::I128, - }), - false => BuiltinType::Uint(match size { - Integer::I8 => BuiltinUint::U8, - Integer::I16 => BuiltinUint::U16, - Integer::I32 => BuiltinUint::U32, - Integer::I64 => BuiltinUint::U64, - Integer::I128 => BuiltinUint::U128, - }), - }, - }, - ); - } + match def { + DefWithBodyId::FunctionId(f) => { + ctx.collect_fn(f); } + DefWithBodyId::ConstId(c) => ctx.collect_const(c, &db.const_signature(c)), + DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_signature(s)), + DefWithBodyId::VariantId(v) => { + ctx.return_ty = TyBuilder::builtin( + match db.enum_signature(v.lookup(db).parent).variant_body_type() { + hir_def::layout::IntegerType::Pointer(signed) => match signed { + true => BuiltinType::Int(BuiltinInt::Isize), + false => BuiltinType::Uint(BuiltinUint::Usize), + }, + hir_def::layout::IntegerType::Fixed(size, signed) => match signed { + true => BuiltinType::Int(match size { + Integer::I8 => BuiltinInt::I8, + Integer::I16 => BuiltinInt::I16, + Integer::I32 => BuiltinInt::I32, + Integer::I64 => BuiltinInt::I64, + Integer::I128 => BuiltinInt::I128, + }), + false => BuiltinType::Uint(match size { + Integer::I8 => BuiltinUint::U8, + Integer::I16 => BuiltinUint::U16, + Integer::I32 => BuiltinUint::U32, + Integer::I64 => BuiltinUint::U64, + Integer::I128 => BuiltinUint::U128, + }), + }, + }, + ); + } + } - ctx.infer_body(); + ctx.infer_body(); - ctx.infer_mut_body(); + ctx.infer_mut_body(); - ctx.infer_closures(); + ctx.infer_closures(); - Arc::new(ctx.resolve_all()) - }) + Arc::new(ctx.resolve_all()) } pub(crate) fn infer_cycle_result(_: &dyn HirDatabase, _: DefWithBodyId) -> Arc { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 052be11e433f..8c03ca939e37 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -2168,9 +2168,7 @@ pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result, } -pub(crate) use tls_cache::with_new_cache; mod tls_cache { use crate::db::HirDatabase; use super::DbInterner; + use base_db::Nonce; use rustc_type_ir::search_graph::GlobalCache; + use salsa::Revision; use std::cell::RefCell; - scoped_tls::scoped_thread_local!(static GLOBAL_CACHE: RefCell>>); + struct Cache { + cache: GlobalCache>, + revision: Revision, + db_nonce: Nonce, + } - pub(crate) fn with_new_cache(f: impl FnOnce() -> T) -> T { - GLOBAL_CACHE.set(&RefCell::new(GlobalCache::default()), f) + thread_local! { + static GLOBAL_CACHE: RefCell> = const { RefCell::new(None) }; } pub(super) fn with_cache<'db, T>( - _db: &'db dyn HirDatabase, + db: &'db dyn HirDatabase, f: impl FnOnce(&mut GlobalCache>) -> T, ) -> T { - // SAFETY: No idea - let call = move |slot: &RefCell<_>| { + GLOBAL_CACHE.with_borrow_mut(|handle| { + let (db_nonce, revision) = db.nonce_and_revision(); + let handle = match handle { + Some(handle) => { + if handle.revision != revision || db_nonce != handle.db_nonce { + *handle = Cache { cache: GlobalCache::default(), revision, db_nonce }; + } + handle + } + None => handle.insert(Cache { cache: GlobalCache::default(), revision, db_nonce }), + }; + + // SAFETY: No idea f(unsafe { std::mem::transmute::< &mut GlobalCache>, &mut GlobalCache>, - >(&mut *slot.borrow_mut()) + >(&mut handle.cache) }) - }; - if GLOBAL_CACHE.is_set() { - GLOBAL_CACHE.with(call) - } else { - GLOBAL_CACHE.set(&RefCell::new(GlobalCache::default()), || GLOBAL_CACHE.with(call)) - } + }) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs index 775136dc0cbf..2a92aa52e0cd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs @@ -3,8 +3,8 @@ use std::{fmt, panic, sync::Mutex}; use base_db::{ - CrateGraphBuilder, CratesMap, FileSourceRootInput, FileText, RootQueryDb, SourceDatabase, - SourceRoot, SourceRootId, SourceRootInput, + CrateGraphBuilder, CratesMap, FileSourceRootInput, FileText, Nonce, RootQueryDb, + SourceDatabase, SourceRoot, SourceRootId, SourceRootInput, }; use hir_def::{ModuleId, db::DefDatabase, nameres::crate_def_map}; @@ -17,12 +17,12 @@ use test_utils::extract_annotations; use triomphe::Arc; #[salsa_macros::db] -#[derive(Clone)] pub(crate) struct TestDB { storage: salsa::Storage, files: Arc, crates_map: Arc, events: Arc>>>, + nonce: Nonce, } impl Default for TestDB { @@ -41,6 +41,7 @@ impl Default for TestDB { events, files: Default::default(), crates_map: Default::default(), + nonce: Nonce::new(), }; this.set_expand_proc_attr_macros_with_durability(true, Durability::HIGH); // This needs to be here otherwise `CrateGraphBuilder` panics. @@ -50,6 +51,18 @@ impl Default for TestDB { } } +impl Clone for TestDB { + fn clone(&self) -> Self { + Self { + storage: self.storage.clone(), + files: self.files.clone(), + crates_map: self.crates_map.clone(), + events: self.events.clone(), + nonce: Nonce::new(), + } + } +} + impl fmt::Debug for TestDB { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("TestDB").finish() @@ -109,6 +122,10 @@ impl SourceDatabase for TestDB { fn crates_map(&self) -> Arc { self.crates_map.clone() } + + fn nonce_and_revision(&self) -> (Nonce, salsa::Revision) { + (self.nonce, salsa::plumbing::ZalsaDatabase::zalsa(self).current_revision()) + } } #[salsa_macros::db] diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index 9d2474d91da6..44bccd86d870 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -51,7 +51,7 @@ use salsa::Durability; use std::{fmt, mem::ManuallyDrop}; use base_db::{ - CrateGraphBuilder, CratesMap, FileSourceRootInput, FileText, Files, RootQueryDb, + CrateGraphBuilder, CratesMap, FileSourceRootInput, FileText, Files, Nonce, RootQueryDb, SourceDatabase, SourceRoot, SourceRootId, SourceRootInput, query_group, }; use hir::{ @@ -83,6 +83,7 @@ pub struct RootDatabase { storage: ManuallyDrop>, files: Arc, crates_map: Arc, + nonce: Nonce, } impl std::panic::RefUnwindSafe for RootDatabase {} @@ -102,6 +103,7 @@ impl Clone for RootDatabase { storage: self.storage.clone(), files: self.files.clone(), crates_map: self.crates_map.clone(), + nonce: Nonce::new(), } } } @@ -165,6 +167,10 @@ impl SourceDatabase for RootDatabase { fn crates_map(&self) -> Arc { self.crates_map.clone() } + + fn nonce_and_revision(&self) -> (Nonce, salsa::Revision) { + (self.nonce, salsa::plumbing::ZalsaDatabase::zalsa(self).current_revision()) + } } impl Default for RootDatabase { @@ -179,6 +185,7 @@ impl RootDatabase { storage: ManuallyDrop::new(salsa::Storage::default()), files: Default::default(), crates_map: Default::default(), + nonce: Nonce::new(), }; // This needs to be here otherwise `CrateGraphBuilder` will panic. db.set_all_crates(Arc::new(Box::new([]))); From aa49c0b8bb27700ab77fd8cb7231d18f4f6d2e97 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 25 Aug 2025 20:19:33 +0300 Subject: [PATCH 147/682] Normalize all types when finishing inference The new solver does not eagerly normalize, but things after inference expect types to be normalized. rustc does the same. Also, I'm afraid other things in r-a don't expect results of the solver to be unnormalized. We'll need to handle that. --- .../crates/hir-ty/src/infer/unify.rs | 3 +++ .../hir-ty/src/tests/regression/new_solver.rs | 26 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index a709aebfa9c1..bb4782bd4194 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -621,6 +621,9 @@ impl<'a> InferenceTable<'a> { where T: HasInterner + TypeFoldable, { + let t = self.resolve_with_fallback(t, &|_, _, d, _| d); + let t = self.normalize_associated_types_in(t); + // Resolve again, because maybe normalization inserted infer vars. self.resolve_with_fallback(t, &|_, _, d, _| d) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs index 059f4ad32a53..20190fbc0456 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -24,3 +24,29 @@ impl<'a> IntoIterator for &'a Grid { "#]], ); } + +#[test] +fn normalization() { + check_infer( + r#" +//- minicore: iterator, iterators +fn main() { + _ = [0i32].into_iter().filter_map(|_n| Some(1i32)); +} + "#, + expect![[r#" + 10..69 '{ ...2)); }': () + 16..17 '_': FilterMap, impl FnMut(i32) -> Option> + 16..66 '_ = [0...1i32))': () + 20..26 '[0i32]': [i32; 1] + 20..38 '[0i32]...iter()': IntoIter + 20..66 '[0i32]...1i32))': FilterMap, impl FnMut(i32) -> Option> + 21..25 '0i32': i32 + 50..65 '|_n| Some(1i32)': impl FnMut(i32) -> Option + 51..53 '_n': i32 + 55..59 'Some': fn Some(i32) -> Option + 55..65 'Some(1i32)': Option + 60..64 '1i32': i32 + "#]], + ); +} From 4b5bb18c57270a5b823235397067ae82c64bf1f1 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 25 Aug 2025 20:56:45 +0300 Subject: [PATCH 148/682] Don't map Chalk's `Normalize` to next solver's `NormalizesTo` `NormalizesTo` is a private predicate that should not be used outside the solver. For normalization, rustc uses `AliasRelate`, so replace with that. --- .../crates/hir-ty/src/next_solver/mapping.rs | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index cad51fd85f55..8f6296b14540 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -808,14 +808,24 @@ impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> for chalk_ir::DomainGoal unimplemented!(), }; let args: GenericArgs<'db> = proj_ty.substitution.to_nextsolver(interner); - let alias = rustc_type_ir::AliasTerm::new( + let alias = Ty::new( interner, - from_assoc_type_id(proj_ty.associated_ty_id).into(), - args, - ); + rustc_type_ir::TyKind::Alias( + rustc_type_ir::AliasTyKind::Projection, + rustc_type_ir::AliasTy::new( + interner, + from_assoc_type_id(proj_ty.associated_ty_id).into(), + args, + ), + ), + ) + .into(); let term = normalize.ty.to_nextsolver(interner).into(); - let normalizes_to = rustc_type_ir::NormalizesTo { alias, term }; - PredicateKind::NormalizesTo(normalizes_to) + PredicateKind::AliasRelate( + alias, + term, + rustc_type_ir::AliasRelationDirection::Equate, + ) } chalk_ir::DomainGoal::WellFormed(well_formed) => { let term = match well_formed { From 169eb87d6088d0c63884b9faa0287ee474a4101b Mon Sep 17 00:00:00 2001 From: Alexey Semenyuk Date: Mon, 25 Aug 2025 16:06:32 +0500 Subject: [PATCH 149/682] Remove expect collapsible_span_lint_calls from span_lint_and_then --- clippy_lints/src/uninit_vec.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs index cee4a53f03cb..51116b5eba9e 100644 --- a/clippy_lints/src/uninit_vec.rs +++ b/clippy_lints/src/uninit_vec.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::higher::{VecInitKind, get_vec_init_kind}; use clippy_utils::ty::{is_type_diagnostic_item, is_uninit_value_valid_for_ty}; use clippy_utils::{SpanlessEq, is_integer_literal, is_lint_allowed, path_to_local_id, peel_hir_expr_while, sym}; @@ -95,16 +95,13 @@ fn handle_uninit_vec_pair<'tcx>( // Check T of Vec if !is_uninit_value_valid_for_ty(cx, args.type_at(0)) { - // FIXME: #7698, false positive of the internal lints - #[expect(clippy::collapsible_span_lint_calls)] - span_lint_and_then( + span_lint_and_help( cx, UNINIT_VEC, vec![call_span, maybe_init_or_reserve.span], "calling `set_len()` immediately after reserving a buffer creates uninitialized values", - |diag| { - diag.help("initialize the buffer or wrap the content in `MaybeUninit`"); - }, + None, + "initialize the buffer or wrap the content in `MaybeUninit`", ); } } else { From 9fd57df6397b0e2b0cd4db32884ed5e5443c7906 Mon Sep 17 00:00:00 2001 From: dianne Date: Tue, 12 Aug 2025 23:55:29 -0700 Subject: [PATCH 150/682] add tests, some with incorrect lifetime extension behavior --- .../format-args-temporary-scopes.e2024.stderr | 15 +++ .../borrowck/format-args-temporary-scopes.rs | 20 +++ .../ui/drop/super-let-tail-expr-drop-order.rs | 122 ++++++++++++++++++ 3 files changed, 157 insertions(+) create mode 100644 tests/ui/borrowck/format-args-temporary-scopes.e2024.stderr create mode 100644 tests/ui/borrowck/format-args-temporary-scopes.rs create mode 100644 tests/ui/drop/super-let-tail-expr-drop-order.rs diff --git a/tests/ui/borrowck/format-args-temporary-scopes.e2024.stderr b/tests/ui/borrowck/format-args-temporary-scopes.e2024.stderr new file mode 100644 index 000000000000..923c929a68d6 --- /dev/null +++ b/tests/ui/borrowck/format-args-temporary-scopes.e2024.stderr @@ -0,0 +1,15 @@ +error[E0716]: temporary value dropped while borrowed + --> $DIR/format-args-temporary-scopes.rs:13:25 + | +LL | println!("{:?}", { &temp() }); + | ---^^^^^--- + | | | | + | | | temporary value is freed at the end of this statement + | | creates a temporary value which is freed while still in use + | borrow later used here + | + = note: consider using a `let` binding to create a longer lived value + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0716`. diff --git a/tests/ui/borrowck/format-args-temporary-scopes.rs b/tests/ui/borrowck/format-args-temporary-scopes.rs new file mode 100644 index 000000000000..e6223d7c5518 --- /dev/null +++ b/tests/ui/borrowck/format-args-temporary-scopes.rs @@ -0,0 +1,20 @@ +//! Test for #145784 as it relates to format arguments: arguments to macros such as `println!` +//! should obey normal temporary scoping rules. +//@ revisions: e2021 e2024 +//@ [e2021] check-pass +//@ [e2021] edition: 2021 +//@ [e2024] edition: 2024 + +fn temp() {} + +fn main() { + // In Rust 2024, block tail expressions are temporary scopes, so the result of `temp()` is + // dropped after evaluating `&temp()`. + println!("{:?}", { &temp() }); + //[e2024]~^ ERROR: temporary value dropped while borrowed [E0716] + + // In Rust 1.89, `format_args!` extended the lifetime of all extending expressions in its + // arguments when provided with two or more arguments. This caused the result of `temp()` to + // outlive the result of the block, making this compile. + println!("{:?}{:?}", { &temp() }, ()); +} diff --git a/tests/ui/drop/super-let-tail-expr-drop-order.rs b/tests/ui/drop/super-let-tail-expr-drop-order.rs new file mode 100644 index 000000000000..5aa17bba21f1 --- /dev/null +++ b/tests/ui/drop/super-let-tail-expr-drop-order.rs @@ -0,0 +1,122 @@ +//! Test for #145784: the argument to `pin!` should be treated as an extending expression if and +//! only if the whole `pin!` invocation is an extending expression. Likewise, since `pin!` is +//! implemented in terms of `super let`, test the same for `super let` initializers. Since the +//! argument to `pin!` and the initializer of `super let` are not temporary drop scopes, this only +//! affects lifetimes in two cases: +//! +//! - Block tail expressions in Rust 2024, which are both extending expressions and temporary drop +//! scopes; treating them as extending expressions within a non-extending `pin!` resulted in borrow +//! expression operands living past the end of the block. +//! +//! - Nested `super let` statements, which can have their binding and temporary lifetimes extended +//! when the block they're in is an extending expression. +//! +//! For more information on extending expressions, see +//! https://doc.rust-lang.org/reference/destructors.html#extending-based-on-expressions +//! +//! For tests that `super let` initializers aren't temporary drop scopes, and tests for +//! lifetime-extended `super let`, see tests/ui/borrowck/super-let-lifetime-and-drop.rs +//@ run-pass +//@ revisions: e2021 e2024 +//@ [e2021] edition: 2021 +//@ [e2024] edition: 2024 + +#![feature(super_let)] + +use std::cell::RefCell; +use std::pin::pin; + +fn main() { + // Test block arguments to `pin!` in non-extending expressions. + // In Rust 2021, block tail expressions aren't temporary drop scopes, so their temporaries + // should outlive the `pin!` invocation. + // In Rust 2024, block tail expressions are temporary drop scopes, so their temporaries should + // be dropped after evaluating the tail expression within the `pin!` invocation. + // By nesting two `pin!` calls, this ensures non-extended `pin!` doesn't extend an inner `pin!`. + assert_drop_order(1..=3, |o| { + #[cfg(e2021)] + ( + pin!(( + pin!({ &o.log(3) as *const LogDrop<'_> }), + drop(o.log(1)), + )), + drop(o.log(2)), + ); + #[cfg(e2024)] + ( + pin!(( + pin!({ &o.log(3) as *const LogDrop<'_> }), + drop(o.log(1)), + )), + drop(o.log(2)), + ); + }); + + // The same holds for `super let` initializers in non-extending expressions. + assert_drop_order(1..=4, |o| { + #[cfg(e2021)] + ( + { + super let _ = { + super let _ = { &o.log(4) as *const LogDrop<'_> }; + drop(o.log(1)) + }; + drop(o.log(2)) + }, + drop(o.log(3)), + ); + #[cfg(e2024)] + ( + { + super let _ = { + super let _ = { &o.log(4) as *const LogDrop<'_> }; + drop(o.log(1)) + }; + drop(o.log(2)) + }, + drop(o.log(3)), + ); + }); + + // Within an extending expression, the argument to `pin!` is also an extending expression, + // allowing borrow operands in block tail expressions to have extended lifetimes. + assert_drop_order(1..=2, |o| { + let _ = pin!({ &o.log(2) as *const LogDrop<'_> }); + drop(o.log(1)); + }); + + // The same holds for `super let` initializers in extending expressions. + assert_drop_order(1..=2, |o| { + let _ = { super let _ = { &o.log(2) as *const LogDrop<'_> }; }; + drop(o.log(1)); + }); +} + +// # Test scaffolding... + +struct DropOrder(RefCell>); +struct LogDrop<'o>(&'o DropOrder, u64); + +impl DropOrder { + fn log(&self, n: u64) -> LogDrop<'_> { + LogDrop(self, n) + } +} + +impl<'o> Drop for LogDrop<'o> { + fn drop(&mut self) { + self.0.0.borrow_mut().push(self.1); + } +} + +#[track_caller] +fn assert_drop_order( + ex: impl IntoIterator, + f: impl Fn(&DropOrder), +) { + let order = DropOrder(RefCell::new(Vec::new())); + f(&order); + let order = order.0.into_inner(); + let expected: Vec = ex.into_iter().collect(); + assert_eq!(order, expected); +} From 83f22cc0f87263c0aa3057d55b994d75c6151834 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 26 Aug 2025 19:33:46 +0300 Subject: [PATCH 151/682] Remove `SolverDefId::ForeignId` Replace it with normal `SolverDefId::TypeAliasId`. The split caused a very funny bug where code was getting `TypeAliasId` where it expected `ForeignId`, because `TypeAliasId` had a `From` impl from `hir_def::TypeAliasId` and `ForeignId` had not, plus a careless `into()`. I could've fixed this specific bug but opted to remove the split instead; currently, it just provides more room for bugs, as we don't have typed IDs for the solver anyway, and even when we'll have (hopefully), that doesn't seem like a very useful distinction, for example in hir-def foreign types are just `TypeAliasId` with some flags. Constructing a test for this isn't trivial; the trivial test (creating a foreign type, even proving a trait bound for it) fails to fail before the change, probably because we don't use the new solver everywhere yet so we don't trigger this specific code path. --- src/tools/rust-analyzer/crates/hir-ty/src/display.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/method_resolution.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs | 2 -- .../rust-analyzer/crates/hir-ty/src/next_solver/interner.rs | 1 - .../rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs | 4 ++-- 5 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index ae0113fcbd7f..0df727a8e5c0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -1473,7 +1473,7 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { } TyKind::Foreign(type_alias) => { let alias = match type_alias { - SolverDefId::ForeignId(id) => id, + SolverDefId::TypeAliasId(id) => id, _ => unreachable!(), }; let type_alias = db.type_alias_signature(alias); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index 49438151bbff..8bd71df7c191 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -155,7 +155,7 @@ impl TyFingerprint { rustc_ast_ir::Mutability::Not => TyFingerprint::RawPtr(Mutability::Not), }, TyKind::Foreign(def) => { - let SolverDefId::ForeignId(def) = def else { unreachable!() }; + let SolverDefId::TypeAliasId(def) = def else { unreachable!() }; TyFingerprint::ForeignType(crate::to_foreign_def_id(def)) } TyKind::Dynamic(bounds, _, _) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs index 64eab2d6b818..c9632ddcd4bb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs @@ -26,7 +26,6 @@ pub enum SolverDefId { StaticId(StaticId), TraitId(TraitId), TypeAliasId(TypeAliasId), - ForeignId(TypeAliasId), InternedClosureId(InternedClosureId), InternedCoroutineId(InternedCoroutineId), InternedOpaqueTyId(InternedOpaqueTyId), @@ -73,7 +72,6 @@ impl TryFrom for GenericDefId { SolverDefId::StaticId(static_id) => GenericDefId::StaticId(static_id), SolverDefId::TraitId(trait_id) => GenericDefId::TraitId(trait_id), SolverDefId::TypeAliasId(type_alias_id) => GenericDefId::TypeAliasId(type_alias_id), - SolverDefId::ForeignId(_) => return Err(value), SolverDefId::InternedClosureId(_) => return Err(value), SolverDefId::InternedCoroutineId(_) => return Err(value), SolverDefId::InternedOpaqueTyId(_) => return Err(value), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 4fe0b54d6846..11c79ff742d6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -1148,7 +1148,6 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { let container = match def_id { SolverDefId::FunctionId(it) => it.lookup(self.db()).container, SolverDefId::TypeAliasId(it) => it.lookup(self.db()).container, - SolverDefId::ForeignId(it) => it.lookup(self.db()).container, SolverDefId::ConstId(it) => it.lookup(self.db()).container, SolverDefId::InternedClosureId(it) => { return self diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index 8f6296b14540..de2671e28fb5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -271,7 +271,7 @@ impl<'db> ChalkToNextSolver<'db, Ty<'db>> for chalk_ir::Ty { ) } chalk_ir::TyKind::Foreign(foreign_def_id) => rustc_type_ir::TyKind::Foreign( - SolverDefId::ForeignId(crate::from_foreign_def_id(*foreign_def_id)), + SolverDefId::TypeAliasId(crate::from_foreign_def_id(*foreign_def_id)), ), chalk_ir::TyKind::Error => rustc_type_ir::TyKind::Error(ErrorGuaranteed), chalk_ir::TyKind::Placeholder(placeholder_index) => { @@ -1262,7 +1262,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) rustc_type_ir::TyKind::Foreign(foreign) => { let def_id = match foreign { - SolverDefId::ForeignId(id) => id, + SolverDefId::TypeAliasId(id) => id, _ => unreachable!(), }; TyKind::Foreign(to_foreign_def_id(def_id)) From 28a248f4f3a2252942b46bcd611cf4c207f10926 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 26 Aug 2025 21:46:04 +0300 Subject: [PATCH 152/682] In highlight_related, when on an unsafe block, don't highlight unsafe operations of other unsafe blocks --- .../hir-ty/src/diagnostics/unsafe_check.rs | 6 ++--- .../rust-analyzer/crates/hir/src/semantics.rs | 23 +++++++++++++++++-- .../crates/hir/src/source_analyzer.rs | 2 +- .../crates/ide/src/highlight_related.rs | 23 +++++++++++++++---- 4 files changed, 44 insertions(+), 10 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs index 827585e50693..d6d669258cc1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs @@ -119,11 +119,11 @@ pub fn unsafe_operations( def: DefWithBodyId, body: &Body, current: ExprId, - callback: &mut dyn FnMut(InsideUnsafeBlock), + callback: &mut dyn FnMut(ExprOrPatId, InsideUnsafeBlock), ) { let mut visitor_callback = |diag| { - if let UnsafeDiagnostic::UnsafeOperation { inside_unsafe_block, .. } = diag { - callback(inside_unsafe_block); + if let UnsafeDiagnostic::UnsafeOperation { inside_unsafe_block, node, .. } = diag { + callback(node, inside_unsafe_block); } }; let mut visitor = UnsafeVisitor::new(db, infer, body, def, &mut visitor_callback); diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 6d6e08100b04..3acbf81ada08 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -28,13 +28,13 @@ use hir_expand::{ mod_path::{ModPath, PathKind}, name::AsName, }; -use hir_ty::diagnostics::unsafe_operations_for_body; +use hir_ty::diagnostics::{unsafe_operations, unsafe_operations_for_body}; use intern::{Interned, Symbol, sym}; use itertools::Itertools; use rustc_hash::{FxHashMap, FxHashSet}; use smallvec::{SmallVec, smallvec}; use span::{Edition, FileId, SyntaxContext}; -use stdx::TupleExt; +use stdx::{TupleExt, always}; use syntax::{ AstNode, AstToken, Direction, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextSize, @@ -1765,6 +1765,25 @@ impl<'db> SemanticsImpl<'db> { res } + pub fn get_unsafe_ops_for_unsafe_block(&self, block: ast::BlockExpr) -> Vec { + always!(block.unsafe_token().is_some()); + let block = self.wrap_node_infile(ast::Expr::from(block)); + let Some(def) = self.body_for(block.syntax()) else { return Vec::new() }; + let def = def.into(); + let (body, source_map) = self.db.body_with_source_map(def); + let infer = self.db.infer(def); + let Some(ExprOrPatId::ExprId(block)) = source_map.node_expr(block.as_ref()) else { + return Vec::new(); + }; + let mut res = Vec::default(); + unsafe_operations(self.db, &infer, def, &body, block, &mut |node, _| { + if let Ok(node) = source_map.expr_or_pat_syntax(node) { + res.push(node); + } + }); + res + } + pub fn is_unsafe_macro_call(&self, macro_call: &ast::MacroCall) -> bool { let Some(mac) = self.resolve_macro_call(macro_call) else { return false }; if mac.is_asm_like(self.db) { diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index e3070c5f74ce..dbc2539a9b91 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -1283,7 +1283,7 @@ impl<'db> SourceAnalyzer<'db> { { let mut is_unsafe = false; let mut walk_expr = |expr_id| { - unsafe_operations(db, infer, def, body, expr_id, &mut |inside_unsafe_block| { + unsafe_operations(db, infer, def, body, expr_id, &mut |_, inside_unsafe_block| { is_unsafe |= inside_unsafe_block == InsideUnsafeBlock::No }) }; diff --git a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs index 9960e79a5380..af1557f5a9a1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs +++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs @@ -805,10 +805,8 @@ pub(crate) fn highlight_unsafe_points( push_to_highlights(unsafe_token_file_id, Some(unsafe_token.text_range())); // highlight unsafe operations - if let Some(block) = block_expr - && let Some(body) = sema.body_for(InFile::new(unsafe_token_file_id, block.syntax())) - { - let unsafe_ops = sema.get_unsafe_ops(body); + if let Some(block) = block_expr { + let unsafe_ops = sema.get_unsafe_ops_for_unsafe_block(block); for unsafe_op in unsafe_ops { push_to_highlights(unsafe_op.file_id, Some(unsafe_op.value.text_range())); } @@ -2535,4 +2533,21 @@ fn foo() { "#, ); } + + #[test] + fn different_unsafe_block() { + check( + r#" +fn main() { + unsafe$0 { + // ^^^^^^ + *(0 as *const u8) + // ^^^^^^^^^^^^^^^^^ + }; + unsafe { *(1 as *const u8) }; + unsafe { *(2 as *const u8) }; +} + "#, + ); + } } From f8a7f82bdab57302ca07ea5f3b3fa5173ed9f9ae Mon Sep 17 00:00:00 2001 From: Sidney Cammeresi Date: Fri, 15 Aug 2025 08:45:50 -0700 Subject: [PATCH 153/682] Stabilize BTree{Map,Set}::extract_if --- library/alloc/src/collections/btree/map.rs | 11 +++++------ library/alloc/src/collections/btree/set.rs | 11 +++++------ library/alloctests/benches/lib.rs | 1 - library/alloctests/tests/lib.rs | 1 - src/tools/miri/tests/pass/btreemap.rs | 1 - .../run_pass/lit-pattern-matching-with-methods.rs | 1 - 6 files changed, 10 insertions(+), 16 deletions(-) diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 8b6d86a28886..98f11e2ea57f 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -1433,7 +1433,6 @@ impl BTreeMap { /// # Examples /// /// ``` - /// #![feature(btree_extract_if)] /// use std::collections::BTreeMap; /// /// // Splitting a map into even and odd keys, reusing the original map: @@ -1450,7 +1449,7 @@ impl BTreeMap { /// assert_eq!(low.keys().copied().collect::>(), [0, 1, 2, 3]); /// assert_eq!(high.keys().copied().collect::>(), [4, 5, 6, 7]); /// ``` - #[unstable(feature = "btree_extract_if", issue = "70530")] + #[stable(feature = "btree_extract_if", since = "CURRENT_RUSTC_VERSION")] pub fn extract_if(&mut self, range: R, pred: F) -> ExtractIf<'_, K, V, R, F, A> where K: Ord, @@ -1937,7 +1936,7 @@ impl Default for Values<'_, K, V> { } /// An iterator produced by calling `extract_if` on BTreeMap. -#[unstable(feature = "btree_extract_if", issue = "70530")] +#[stable(feature = "btree_extract_if", since = "CURRENT_RUSTC_VERSION")] #[must_use = "iterators are lazy and do nothing unless consumed"] pub struct ExtractIf< 'a, @@ -1970,7 +1969,7 @@ pub(super) struct ExtractIfInner<'a, K, V, R> { range: R, } -#[unstable(feature = "btree_extract_if", issue = "70530")] +#[stable(feature = "btree_extract_if", since = "CURRENT_RUSTC_VERSION")] impl fmt::Debug for ExtractIf<'_, K, V, R, F, A> where K: fmt::Debug, @@ -1982,7 +1981,7 @@ where } } -#[unstable(feature = "btree_extract_if", issue = "70530")] +#[stable(feature = "btree_extract_if", since = "CURRENT_RUSTC_VERSION")] impl Iterator for ExtractIf<'_, K, V, R, F, A> where K: PartialOrd, @@ -2056,7 +2055,7 @@ impl<'a, K, V, R> ExtractIfInner<'a, K, V, R> { } } -#[unstable(feature = "btree_extract_if", issue = "70530")] +#[stable(feature = "btree_extract_if", since = "CURRENT_RUSTC_VERSION")] impl FusedIterator for ExtractIf<'_, K, V, R, F> where K: PartialOrd, diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index d50ce02bda74..e6b0a1f63232 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -1202,7 +1202,6 @@ impl BTreeSet { /// # Examples /// /// ``` - /// #![feature(btree_extract_if)] /// use std::collections::BTreeSet; /// /// // Splitting a set into even and odd values, reusing the original set: @@ -1219,7 +1218,7 @@ impl BTreeSet { /// assert_eq!(low.into_iter().collect::>(), [0, 1, 2, 3]); /// assert_eq!(high.into_iter().collect::>(), [4, 5, 6, 7]); /// ``` - #[unstable(feature = "btree_extract_if", issue = "70530")] + #[stable(feature = "btree_extract_if", since = "CURRENT_RUSTC_VERSION")] pub fn extract_if(&mut self, range: R, pred: F) -> ExtractIf<'_, T, R, F, A> where T: Ord, @@ -1554,7 +1553,7 @@ impl<'a, T, A: Allocator + Clone> IntoIterator for &'a BTreeSet { } /// An iterator produced by calling `extract_if` on BTreeSet. -#[unstable(feature = "btree_extract_if", issue = "70530")] +#[stable(feature = "btree_extract_if", since = "CURRENT_RUSTC_VERSION")] #[must_use = "iterators are lazy and do nothing unless consumed"] pub struct ExtractIf< 'a, @@ -1569,7 +1568,7 @@ pub struct ExtractIf< alloc: A, } -#[unstable(feature = "btree_extract_if", issue = "70530")] +#[stable(feature = "btree_extract_if", since = "CURRENT_RUSTC_VERSION")] impl fmt::Debug for ExtractIf<'_, T, R, F, A> where T: fmt::Debug, @@ -1582,7 +1581,7 @@ where } } -#[unstable(feature = "btree_extract_if", issue = "70530")] +#[stable(feature = "btree_extract_if", since = "CURRENT_RUSTC_VERSION")] impl Iterator for ExtractIf<'_, T, R, F, A> where T: PartialOrd, @@ -1602,7 +1601,7 @@ where } } -#[unstable(feature = "btree_extract_if", issue = "70530")] +#[stable(feature = "btree_extract_if", since = "CURRENT_RUSTC_VERSION")] impl FusedIterator for ExtractIf<'_, T, R, F, A> where T: PartialOrd, diff --git a/library/alloctests/benches/lib.rs b/library/alloctests/benches/lib.rs index 2633154318c1..721d685527fe 100644 --- a/library/alloctests/benches/lib.rs +++ b/library/alloctests/benches/lib.rs @@ -1,6 +1,5 @@ // Disabling in Miri as these would take too long. #![cfg(not(miri))] -#![feature(btree_extract_if)] #![feature(iter_next_chunk)] #![feature(repr_simd)] #![feature(slice_partition_dedup)] diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index 447af240a4b9..c0a0022c0080 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -2,7 +2,6 @@ #![feature(alloc_layout_extra)] #![feature(iter_array_chunks)] #![feature(assert_matches)] -#![feature(btree_extract_if)] #![feature(wtf8_internals)] #![feature(char_max_len)] #![feature(cow_is_borrowed)] diff --git a/src/tools/miri/tests/pass/btreemap.rs b/src/tools/miri/tests/pass/btreemap.rs index 1d65e69bf726..7af6d7b5551c 100644 --- a/src/tools/miri/tests/pass/btreemap.rs +++ b/src/tools/miri/tests/pass/btreemap.rs @@ -1,7 +1,6 @@ //@revisions: stack tree //@[tree]compile-flags: -Zmiri-tree-borrows //@compile-flags: -Zmiri-strict-provenance -#![feature(btree_extract_if)] use std::collections::{BTreeMap, BTreeSet}; use std::mem; diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.rs b/tests/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.rs index afb16cf58e87..4ae77ab64392 100644 --- a/tests/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.rs +++ b/tests/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.rs @@ -2,7 +2,6 @@ //@check-pass #![warn(unused)] #![feature(rustc_attrs)] -#![feature(btree_extract_if)] use std::collections::BTreeMap; use std::panic::{catch_unwind, AssertUnwindSafe}; From f31a378b0a5a612e0d2a91ab1a9ad237d442888c Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 28 Aug 2025 01:25:09 +0300 Subject: [PATCH 154/682] Attach the db in one more place in highlighting --- .../rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs index 7785891169c6..abe7be8c6888 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs @@ -26,7 +26,8 @@ pub(super) fn ra_fixture( literal: &ast::String, expanded: &ast::String, ) -> Option<()> { - let active_parameter = ActiveParameter::at_token(sema, expanded.syntax().clone())?; + let active_parameter = + salsa::attach(sema.db, || ActiveParameter::at_token(sema, expanded.syntax().clone()))?; let has_rust_fixture_attr = active_parameter.attrs().is_some_and(|attrs| { attrs.filter_map(|attr| attr.as_simple_path()).any(|path| { path.segments() From 0a5502208d7c7625f3a4806c422dfed47cc364ba Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 28 Aug 2025 18:54:46 +0300 Subject: [PATCH 155/682] Add progress bars to more places in analysis-stats Namely, mir lowering, const eval and IDE things. --- .../rust-analyzer/src/cli/analysis_stats.rs | 55 ++++++++++++++++++- 1 file changed, 52 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index b17186f8d7b1..d685f160ff09 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -333,7 +333,7 @@ impl flags::AnalysisStats { } if self.run_all_ide_things { - self.run_ide_things(host.analysis(), file_ids.clone()); + self.run_ide_things(host.analysis(), file_ids.clone(), db, &vfs, verbosity); } if self.run_term_search { @@ -393,15 +393,27 @@ impl flags::AnalysisStats { } fn run_const_eval(&self, db: &RootDatabase, bodies: &[DefWithBody], verbosity: Verbosity) { + let len = bodies + .iter() + .filter(|body| matches!(body, DefWithBody::Const(_) | DefWithBody::Static(_))) + .count(); + let mut bar = match verbosity { + Verbosity::Quiet | Verbosity::Spammy => ProgressReport::hidden(), + _ if self.parallel || self.output.is_some() => ProgressReport::hidden(), + _ => ProgressReport::new(len), + }; + let mut sw = self.stop_watch(); let mut all = 0; let mut fail = 0; for &b in bodies { + bar.set_message(move || format!("const eval: {}", full_name(db, b, b.module(db)))); let res = match b { DefWithBody::Const(c) => c.eval(db), DefWithBody::Static(s) => s.eval(db), _ => continue, }; + bar.inc(1); all += 1; let Err(error) = res else { continue; @@ -409,10 +421,11 @@ impl flags::AnalysisStats { if verbosity.is_spammy() { let full_name = full_name_of_item(db, b.module(db), b.name(db).unwrap_or(Name::missing())); - println!("Const eval for {full_name} failed due {error:?}"); + bar.println(format!("Const eval for {full_name} failed due {error:?}")); } fail += 1; } + bar.finish_and_clear(); let const_eval_time = sw.elapsed(); eprintln!("{:<20} {}", "Const evaluation:", const_eval_time); eprintln!("Failed const evals: {fail} ({}%)", percentage(fail, all)); @@ -662,6 +675,10 @@ impl flags::AnalysisStats { let mut all = 0; let mut fail = 0; for &body_id in bodies { + bar.set_message(move || { + format!("mir lowering: {}", full_name(db, body_id, body_id.module(db))) + }); + bar.inc(1); if matches!(body_id, DefWithBody::Variant(_)) { continue; } @@ -1089,12 +1106,29 @@ impl flags::AnalysisStats { report_metric("body lowering time", body_lowering_time.time.as_millis() as u64, "ms"); } - fn run_ide_things(&self, analysis: Analysis, mut file_ids: Vec) { + fn run_ide_things( + &self, + analysis: Analysis, + mut file_ids: Vec, + db: &RootDatabase, + vfs: &Vfs, + verbosity: Verbosity, + ) { + let len = file_ids.len(); + let create_bar = || match verbosity { + Verbosity::Quiet | Verbosity::Spammy => ProgressReport::hidden(), + _ if self.parallel || self.output.is_some() => ProgressReport::hidden(), + _ => ProgressReport::new(len), + }; + file_ids.sort(); file_ids.dedup(); let mut sw = self.stop_watch(); + let mut bar = create_bar(); for &file_id in &file_ids { + let msg = format!("diagnostics: {}", vfs.file_path(file_id.file_id(db))); + bar.set_message(move || msg.clone()); _ = analysis.full_diagnostics( &DiagnosticsConfig { enabled: true, @@ -1121,8 +1155,14 @@ impl flags::AnalysisStats { ide::AssistResolveStrategy::All, analysis.editioned_file_id_to_vfs(file_id), ); + bar.inc(1); } + bar.finish_and_clear(); + + let mut bar = create_bar(); for &file_id in &file_ids { + let msg = format!("inlay hints: {}", vfs.file_path(file_id.file_id(db))); + bar.set_message(move || msg.clone()); _ = analysis.inlay_hints( &InlayHintsConfig { render_colons: false, @@ -1158,8 +1198,14 @@ impl flags::AnalysisStats { analysis.editioned_file_id_to_vfs(file_id), None, ); + bar.inc(1); } + bar.finish_and_clear(); + + let mut bar = create_bar(); for &file_id in &file_ids { + let msg = format!("annotations: {}", vfs.file_path(file_id.file_id(db))); + bar.set_message(move || msg.clone()); analysis .annotations( &AnnotationConfig { @@ -1178,7 +1224,10 @@ impl flags::AnalysisStats { .for_each(|annotation| { _ = analysis.resolve_annotation(annotation); }); + bar.inc(1); } + bar.finish_and_clear(); + let ide_time = sw.elapsed(); eprintln!("{:<20} {} ({} files)", "IDE:", ide_time, file_ids.len()); } From c36b75bdc8634f0d9cba824410df7a8c3785bda4 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 28 Aug 2025 19:24:46 +0300 Subject: [PATCH 156/682] Don't require a full `InferenceTable` for `CastTy` A DB is enough. --- .../crates/hir-ty/src/infer/cast.rs | 98 +++++++++---------- .../crates/hir-ty/src/mir/lower.rs | 11 +-- 2 files changed, 51 insertions(+), 58 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs index 09b983a580d9..43364963eb05 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs @@ -6,7 +6,9 @@ use stdx::never; use crate::{ Adjustment, Binders, DynTy, InferenceDiagnostic, Interner, PlaceholderIndex, - QuantifiedWhereClauses, Ty, TyExt, TyKind, TypeFlags, WhereClause, from_chalk_trait_id, + QuantifiedWhereClauses, Ty, TyExt, TyKind, TypeFlags, WhereClause, + db::HirDatabase, + from_chalk_trait_id, infer::{coerce::CoerceNever, unify::InferenceTable}, }; @@ -30,7 +32,7 @@ pub(crate) enum CastTy { } impl CastTy { - pub(crate) fn from_ty(table: &mut InferenceTable<'_>, t: &Ty) -> Option { + pub(crate) fn from_ty(db: &dyn HirDatabase, t: &Ty) -> Option { match t.kind(Interner) { TyKind::Scalar(Scalar::Bool) => Some(Self::Int(Int::Bool)), TyKind::Scalar(Scalar::Char) => Some(Self::Int(Int::Char)), @@ -43,8 +45,8 @@ impl CastTy { let (AdtId::EnumId(id), _) = t.as_adt()? else { return None; }; - let enum_data = id.enum_variants(table.db); - if enum_data.is_payload_free(table.db) { Some(Self::Int(Int::CEnum)) } else { None } + let enum_data = id.enum_variants(db); + if enum_data.is_payload_free(db) { Some(Self::Int(Int::CEnum)) } else { None } } TyKind::Raw(m, ty) => Some(Self::Ptr(ty.clone(), *m)), TyKind::Function(_) => Some(Self::FnPtr), @@ -142,58 +144,50 @@ impl CastCheck { where F: FnMut(ExprId, Vec), { - let (t_from, t_cast) = - match (CastTy::from_ty(table, &self.expr_ty), CastTy::from_ty(table, &self.cast_ty)) { - (Some(t_from), Some(t_cast)) => (t_from, t_cast), - (None, Some(t_cast)) => match self.expr_ty.kind(Interner) { - TyKind::FnDef(..) => { - let sig = self.expr_ty.callable_sig(table.db).expect("FnDef had no sig"); - let sig = table.eagerly_normalize_and_resolve_shallow_in(sig); - let fn_ptr = TyKind::Function(sig.to_fn_ptr()).intern(Interner); - if let Ok((adj, _)) = table.coerce(&self.expr_ty, &fn_ptr, CoerceNever::Yes) - { - apply_adjustments(self.source_expr, adj); - } else { - return Err(CastError::IllegalCast); - } - - (CastTy::FnPtr, t_cast) + let (t_from, t_cast) = match ( + CastTy::from_ty(table.db, &self.expr_ty), + CastTy::from_ty(table.db, &self.cast_ty), + ) { + (Some(t_from), Some(t_cast)) => (t_from, t_cast), + (None, Some(t_cast)) => match self.expr_ty.kind(Interner) { + TyKind::FnDef(..) => { + let sig = self.expr_ty.callable_sig(table.db).expect("FnDef had no sig"); + let sig = table.eagerly_normalize_and_resolve_shallow_in(sig); + let fn_ptr = TyKind::Function(sig.to_fn_ptr()).intern(Interner); + if let Ok((adj, _)) = table.coerce(&self.expr_ty, &fn_ptr, CoerceNever::Yes) { + apply_adjustments(self.source_expr, adj); + } else { + return Err(CastError::IllegalCast); } - TyKind::Ref(mutbl, _, inner_ty) => { - return match t_cast { - CastTy::Int(_) | CastTy::Float => match inner_ty.kind(Interner) { - TyKind::Scalar( - Scalar::Int(_) | Scalar::Uint(_) | Scalar::Float(_), - ) - | TyKind::InferenceVar( - _, - TyVariableKind::Integer | TyVariableKind::Float, - ) => Err(CastError::NeedDeref), - _ => Err(CastError::NeedViaPtr), - }, - // array-ptr-cast - CastTy::Ptr(t, m) => { - let t = table.eagerly_normalize_and_resolve_shallow_in(t); - if !table.is_sized(&t) { - return Err(CastError::IllegalCast); - } - self.check_ref_cast( - table, - inner_ty, - *mutbl, - &t, - m, - apply_adjustments, - ) + (CastTy::FnPtr, t_cast) + } + TyKind::Ref(mutbl, _, inner_ty) => { + return match t_cast { + CastTy::Int(_) | CastTy::Float => match inner_ty.kind(Interner) { + TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_) | Scalar::Float(_)) + | TyKind::InferenceVar( + _, + TyVariableKind::Integer | TyVariableKind::Float, + ) => Err(CastError::NeedDeref), + + _ => Err(CastError::NeedViaPtr), + }, + // array-ptr-cast + CastTy::Ptr(t, m) => { + let t = table.eagerly_normalize_and_resolve_shallow_in(t); + if !table.is_sized(&t) { + return Err(CastError::IllegalCast); } - _ => Err(CastError::NonScalar), - }; - } - _ => return Err(CastError::NonScalar), - }, + self.check_ref_cast(table, inner_ty, *mutbl, &t, m, apply_adjustments) + } + _ => Err(CastError::NonScalar), + }; + } _ => return Err(CastError::NonScalar), - }; + }, + _ => return Err(CastError::NonScalar), + }; // rustc checks whether the `expr_ty` is foreign adt with `non_exhaustive` sym diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 8c03ca939e37..2d53d16c57a3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -31,7 +31,7 @@ use crate::{ display::{DisplayTarget, HirDisplay, hir_display_with_store}, error_lifetime, generics::generics, - infer::{CaptureKind, CapturedItem, TypeMismatch, cast::CastTy, unify::InferenceTable}, + infer::{CaptureKind, CapturedItem, TypeMismatch, cast::CastTy}, inhabitedness::is_ty_uninhabited_from, layout::LayoutError, mapping::ToChalk, @@ -948,8 +948,7 @@ impl<'ctx> MirLowerCtx<'ctx> { let cast_kind = if source_ty.as_reference().is_some() { CastKind::PointerCoercion(PointerCast::ArrayToPointer) } else { - let mut table = InferenceTable::new(self.db, self.env.clone()); - cast_kind(&mut table, &source_ty, &target_ty)? + cast_kind(self.db, &source_ty, &target_ty)? }; Rvalue::Cast(cast_kind, it, target_ty) @@ -2017,9 +2016,9 @@ impl<'ctx> MirLowerCtx<'ctx> { } } -fn cast_kind(table: &mut InferenceTable<'_>, source_ty: &Ty, target_ty: &Ty) -> Result { - let from = CastTy::from_ty(table, source_ty); - let cast = CastTy::from_ty(table, target_ty); +fn cast_kind(db: &dyn HirDatabase, source_ty: &Ty, target_ty: &Ty) -> Result { + let from = CastTy::from_ty(db, source_ty); + let cast = CastTy::from_ty(db, target_ty); Ok(match (from, cast) { (Some(CastTy::Ptr(..) | CastTy::FnPtr), Some(CastTy::Int(_))) => { CastKind::PointerExposeAddress From dce4f301042c53f91d85befda03b0405d5922916 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 28 Aug 2025 23:53:55 +0300 Subject: [PATCH 157/682] When mapping next-solver's `dyn` type, add `Self` (aka. bound var ^1.0) to auto traits' substitutions Chalk represents dyn types as a list of predicate, the self type should be there. The next solver represents them quite differently. The `Self` was forgotten for the auto trait case. --- .../crates/hir-ty/src/mir/lower.rs | 2 + .../crates/hir-ty/src/mir/lower/tests.rs | 64 +++++++++++++++++++ .../crates/hir-ty/src/next_solver/mapping.rs | 5 +- 3 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 8c03ca939e37..7fbd496e51f2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -53,6 +53,8 @@ use super::OperandKind; mod as_place; mod pattern_matching; +#[cfg(test)] +mod tests; #[derive(Debug, Clone)] struct LoopBlocks { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs new file mode 100644 index 000000000000..1d7a16ed72d0 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/tests.rs @@ -0,0 +1,64 @@ +use hir_def::db::DefDatabase; +use rustc_hash::FxHashMap; +use span::Edition; +use test_fixture::WithFixture; +use triomphe::Arc; + +use crate::{ + db::HirDatabase, + mir::{MirBody, MirLowerError}, + setup_tracing, + test_db::TestDB, +}; + +fn lower_mir( + #[rust_analyzer::rust_fixture] ra_fixture: &str, +) -> FxHashMap, MirLowerError>> { + let _tracing = setup_tracing(); + let (db, file_ids) = TestDB::with_many_files(ra_fixture); + let file_id = *file_ids.last().unwrap(); + let module_id = db.module_for_file(file_id.file_id(&db)); + let def_map = module_id.def_map(&db); + let scope = &def_map[module_id.local_id].scope; + let funcs = scope.declarations().filter_map(|x| match x { + hir_def::ModuleDefId::FunctionId(it) => Some(it), + _ => None, + }); + funcs + .map(|func| { + let name = db.function_signature(func).name.display(&db, Edition::CURRENT).to_string(); + let mir = db.mir_body(func.into()); + (name, mir) + }) + .collect() +} + +#[test] +fn dyn_projection_with_auto_traits_regression_next_solver() { + lower_mir( + r#" +//- minicore: sized, send +pub trait Deserializer {} + +pub trait Strictest { + type Object: ?Sized; +} + +impl Strictest for dyn CustomValue { + type Object = dyn CustomValue + Send; +} + +pub trait CustomValue: Send {} + +impl CustomValue for () {} + +struct Box; + +type DeserializeFn = fn(&mut dyn Deserializer) -> Box; + +fn foo() { + (|deserializer| Box::new(())) as DeserializeFn<::Object>; +} + "#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index de2671e28fb5..203f030dfd61 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -1207,7 +1207,10 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) SolverDefId::TraitId(id) => to_chalk_trait_id(id), _ => unreachable!(), }; - let substitution = chalk_ir::Substitution::empty(Interner); + let substitution = chalk_ir::Substitution::from1( + Interner, + convert_ty_for_result(interner, self_ty), + ); let trait_ref = chalk_ir::TraitRef { trait_id, substitution }; chalk_ir::WhereClause::Implemented(trait_ref) } From 46727f4cb06c3496a541ee8517a24ef5cb77627c Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Wed, 20 Aug 2025 15:04:34 +0000 Subject: [PATCH 158/682] Stabilize file_as_c_str --- library/core/src/panic/location.rs | 3 ++- tests/ui/rfcs/rfc-2091-track-caller/file-is-nul-terminated.rs | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/panic/location.rs b/library/core/src/panic/location.rs index 7a68d393906c..593584934447 100644 --- a/library/core/src/panic/location.rs +++ b/library/core/src/panic/location.rs @@ -193,8 +193,9 @@ impl<'a> Location<'a> { /// 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] + #[stable(feature = "file_with_nul", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "file_with_nul", since = "CURRENT_RUSTC_VERSION")] pub const fn file_as_c_str(&self) -> &'a CStr { let filename = self.filename.as_ptr(); 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 index 7902f40b09bd..698492966adf 100644 --- 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 @@ -1,5 +1,4 @@ //@ run-pass -#![feature(file_with_nul)] #[track_caller] const fn assert_file_has_trailing_zero() { From ae648be78350638873248b348f4fca3b9bc916e8 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Fri, 29 Aug 2025 12:15:41 +0200 Subject: [PATCH 159/682] use `llvm.roundeven` on arm --- .../core_arch/src/arm_shared/neon/generated.rs | 12 ++++-------- .../stdarch-gen-arm/spec/neon/arm_shared.spec.yml | 8 ++------ 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs b/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs index 22d31d4e2568..b5ba792b18ae 100644 --- a/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs +++ b/library/stdarch/crates/core_arch/src/arm_shared/neon/generated.rs @@ -58256,10 +58256,9 @@ pub fn vrhaddq_u32(a: uint32x4_t, b: uint32x4_t) -> uint32x4_t { pub fn vrndn_f16(a: float16x4_t) -> float16x4_t { unsafe extern "unadjusted" { #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), + any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "arm"), link_name = "llvm.roundeven.v4f16" )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vrintn.v4f16")] fn _vrndn_f16(a: float16x4_t) -> float16x4_t; } unsafe { _vrndn_f16(a) } @@ -58279,10 +58278,9 @@ pub fn vrndn_f16(a: float16x4_t) -> float16x4_t { pub fn vrndnq_f16(a: float16x8_t) -> float16x8_t { unsafe extern "unadjusted" { #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), + any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "arm"), link_name = "llvm.roundeven.v8f16" )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vrintn.v8f16")] fn _vrndnq_f16(a: float16x8_t) -> float16x8_t; } unsafe { _vrndnq_f16(a) } @@ -58308,10 +58306,9 @@ pub fn vrndnq_f16(a: float16x8_t) -> float16x8_t { pub fn vrndn_f32(a: float32x2_t) -> float32x2_t { unsafe extern "unadjusted" { #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), + any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "arm"), link_name = "llvm.roundeven.v2f32" )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vrintn.v2f32")] fn _vrndn_f32(a: float32x2_t) -> float32x2_t; } unsafe { _vrndn_f32(a) } @@ -58337,10 +58334,9 @@ pub fn vrndn_f32(a: float32x2_t) -> float32x2_t { pub fn vrndnq_f32(a: float32x4_t) -> float32x4_t { unsafe extern "unadjusted" { #[cfg_attr( - any(target_arch = "aarch64", target_arch = "arm64ec"), + any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "arm"), link_name = "llvm.roundeven.v4f32" )] - #[cfg_attr(target_arch = "arm", link_name = "llvm.arm.neon.vrintn.v4f32")] fn _vrndnq_f32(a: float32x4_t) -> float32x4_t; } unsafe { _vrndnq_f32(a) } diff --git a/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml b/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml index 1543ab1f866e..43dd3b903150 100644 --- a/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml +++ b/library/stdarch/crates/stdarch-gen-arm/spec/neon/arm_shared.spec.yml @@ -2486,9 +2486,7 @@ intrinsics: name: "llvm.frinn.{neon_type}" links: - link: "llvm.roundeven.{neon_type}" - arch: aarch64,arm64ec - - link: "llvm.arm.neon.vrintn.{neon_type}" - arch: arm + arch: aarch64,arm64ec,arm - name: "vrndn{neon_type.no}" doc: "Floating-point round to integral, to nearest with ties to even" @@ -2510,9 +2508,7 @@ intrinsics: name: "llvm.frinn.{neon_type}" links: - link: "llvm.roundeven.{neon_type}" - arch: aarch64,arm64ec - - link: "llvm.arm.neon.vrintn.{neon_type}" - arch: arm + arch: aarch64,arm64ec,arm - name: "vqadd{neon_type.no}" doc: Saturating add From 00b27f49eb8be37424fb5b21ce5aaac842a1d8b4 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Fri, 29 Aug 2025 11:24:34 +0100 Subject: [PATCH 160/682] Remove FreeBSD CI It's historically been flaky and is no longer needed now that std_detect has been moved out of this repository. --- library/stdarch/.cirrus.yml | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 library/stdarch/.cirrus.yml diff --git a/library/stdarch/.cirrus.yml b/library/stdarch/.cirrus.yml deleted file mode 100644 index a0ecc03b953f..000000000000 --- a/library/stdarch/.cirrus.yml +++ /dev/null @@ -1,16 +0,0 @@ -task: - name: x86_64-unknown-freebsd - freebsd_instance: - image_family: freebsd-13-4 - env: - # FIXME(freebsd): FreeBSD has a segfault when `RUST_BACKTRACE` is set - # https://github.com/rust-lang/rust/issues/132185 - RUST_BACKTRACE: "0" - setup_script: - - curl https://sh.rustup.rs -sSf --output rustup.sh - - sh rustup.sh --default-toolchain nightly -y - - . $HOME/.cargo/env - - rustup default nightly - test_script: - - . $HOME/.cargo/env - - cargo build --all From b0897f31b998472d024a2e00a7b00c5fc875351e Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 29 Aug 2025 15:06:54 +0000 Subject: [PATCH 161/682] Update getopts to remove unicode-width dependency --- Cargo.lock | 6 +++--- library/Cargo.lock | 15 ++------------- library/test/Cargo.toml | 2 +- src/tools/tidy/src/deps.rs | 1 - 4 files changed, 6 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 91528a4135e3..82ba9ce178aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -674,7 +674,7 @@ checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" dependencies = [ "serde", "termcolor", - "unicode-width 0.2.1", + "unicode-width 0.1.14", ] [[package]] @@ -1470,9 +1470,9 @@ dependencies = [ [[package]] name = "getopts" -version = "0.2.23" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cba6ae63eb948698e300f645f87c70f76630d505f23b8907cf1e193ee85048c1" +checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df" dependencies = [ "unicode-width 0.2.1", ] diff --git a/library/Cargo.lock b/library/Cargo.lock index f0ac9d259c0d..3fb6af2c5982 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -99,13 +99,12 @@ dependencies = [ [[package]] name = "getopts" -version = "0.2.23" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cba6ae63eb948698e300f645f87c70f76630d505f23b8907cf1e193ee85048c1" +checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df" dependencies = [ "rustc-std-workspace-core", "rustc-std-workspace-std", - "unicode-width", ] [[package]] @@ -360,16 +359,6 @@ dependencies = [ "std", ] -[[package]] -name = "unicode-width" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" -dependencies = [ - "rustc-std-workspace-core", - "rustc-std-workspace-std", -] - [[package]] name = "unwind" version = "0.0.0" diff --git a/library/test/Cargo.toml b/library/test/Cargo.toml index 2a32a7dd76ee..fe749847b7c0 100644 --- a/library/test/Cargo.toml +++ b/library/test/Cargo.toml @@ -6,7 +6,7 @@ version = "0.0.0" edition = "2024" [dependencies] -getopts = { version = "0.2.21", features = ['rustc-dep-of-std'] } +getopts = { version = "0.2.24", default-features = false, features = ['rustc-dep-of-std'] } std = { path = "../std", public = true } core = { path = "../core", public = true } diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 80b6d54ce1c8..4efaf1fb7716 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -478,7 +478,6 @@ const PERMITTED_STDLIB_DEPENDENCIES: &[&str] = &[ "rustc-demangle", "rustc-literal-escaper", "shlex", - "unicode-width", "unwinding", "wasi", "windows-sys", From d396d2f79753b7db793a940d8da3ea74d82077d6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 29 Aug 2025 20:55:43 +0000 Subject: [PATCH 162/682] Bump tracing-subscriber from 0.3.19 to 0.3.20 Bumps [tracing-subscriber](https://github.com/tokio-rs/tracing) from 0.3.19 to 0.3.20. - [Release notes](https://github.com/tokio-rs/tracing/releases) - [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20) --- updated-dependencies: - dependency-name: tracing-subscriber dependency-version: 0.3.20 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- src/tools/rust-analyzer/Cargo.lock | 4 ++-- src/tools/rust-analyzer/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index f72e698f6063..b008aa1c50f8 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -2738,9 +2738,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.19" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" dependencies = [ "sharded-slab", "thread_local", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index bb8444ed7342..05ee190480c9 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -163,7 +163,7 @@ temp-dir = "0.1.16" text-size = "1.1.1" tracing = "0.1.41" tracing-tree = "0.4.0" -tracing-subscriber = { version = "0.3.19", default-features = false, features = [ +tracing-subscriber = { version = "0.3.20", default-features = false, features = [ "registry", "fmt", "local-time", From 2ce6154b32c50b3588e0c58b6f106647a8831de8 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Sat, 30 Aug 2025 10:38:23 +0200 Subject: [PATCH 163/682] compiler: Include span of too huge array with `-Cdebuginfo=2` We have a few ui tests to ensure we emit an error if we encounter too big arrays. Before this fix, compiling the tests with `-Cdebuginfo=2` would not include the spans of the instantiation sites, because the error is then emitted from a different code path that does not include the span. Propagate the span to the error also in the debuginfo case, so the tests passes regardless of debuginfo level. --- compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs | 7 ++++--- compiler/rustc_codegen_llvm/src/type_of.rs | 7 ++++++- ....stderr => huge-array-simple-64.full-debuginfo.stderr} | 2 +- tests/ui/limits/huge-array-simple-64.no-debuginfo.stderr | 8 ++++++++ tests/ui/limits/huge-array-simple-64.rs | 5 +++++ ...huge-array.stderr => huge-array.full-debuginfo.stderr} | 2 +- tests/ui/limits/huge-array.no-debuginfo.stderr | 8 ++++++++ tests/ui/limits/huge-array.rs | 5 +++++ ...919-64.stderr => issue-15919-64.full-debuginfo.stderr} | 2 +- tests/ui/limits/issue-15919-64.no-debuginfo.stderr | 8 ++++++++ tests/ui/limits/issue-15919-64.rs | 5 +++++ 11 files changed, 52 insertions(+), 7 deletions(-) rename tests/ui/limits/{huge-array-simple-64.stderr => huge-array-simple-64.full-debuginfo.stderr} (83%) create mode 100644 tests/ui/limits/huge-array-simple-64.no-debuginfo.stderr rename tests/ui/limits/{huge-array.stderr => huge-array.full-debuginfo.stderr} (87%) create mode 100644 tests/ui/limits/huge-array.no-debuginfo.stderr rename tests/ui/limits/{issue-15919-64.stderr => issue-15919-64.full-debuginfo.stderr} (85%) create mode 100644 tests/ui/limits/issue-15919-64.no-debuginfo.stderr diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index caa3369f413a..1790e2122526 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -103,16 +103,17 @@ fn build_fixed_size_array_di_node<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, unique_type_id: UniqueTypeId<'tcx>, array_type: Ty<'tcx>, + span: Span, ) -> DINodeCreationResult<'ll> { let ty::Array(element_type, len) = array_type.kind() else { bug!("build_fixed_size_array_di_node() called with non-ty::Array type `{:?}`", array_type) }; - let element_type_di_node = type_di_node(cx, *element_type); + let element_type_di_node = spanned_type_di_node(cx, *element_type, span); return_if_di_node_created_in_meantime!(cx, unique_type_id); - let (size, align) = cx.size_and_align_of(array_type); + let (size, align) = cx.spanned_size_and_align_of(array_type, span); let upper_bound = len .try_to_target_usize(cx.tcx) @@ -447,7 +448,7 @@ pub(crate) fn spanned_type_di_node<'ll, 'tcx>( build_basic_type_di_node(cx, t) } ty::Tuple(elements) if elements.is_empty() => build_basic_type_di_node(cx, t), - ty::Array(..) => build_fixed_size_array_di_node(cx, unique_type_id, t), + ty::Array(..) => build_fixed_size_array_di_node(cx, unique_type_id, t, span), ty::Slice(_) | ty::Str => build_slice_type_di_node(cx, t, unique_type_id), ty::Dynamic(..) => build_dyn_type_di_node(cx, t, unique_type_id), ty::Foreign(..) => build_foreign_type_di_node(cx, t, unique_type_id), diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs index 4e7096da502d..53218340414e 100644 --- a/compiler/rustc_codegen_llvm/src/type_of.rs +++ b/compiler/rustc_codegen_llvm/src/type_of.rs @@ -7,6 +7,7 @@ use rustc_middle::bug; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; use rustc_middle::ty::{self, CoroutineArgsExt, Ty, TypeVisitableExt}; +use rustc_span::{DUMMY_SP, Span}; use tracing::debug; use crate::common::*; @@ -149,7 +150,11 @@ impl<'a, 'tcx> CodegenCx<'a, 'tcx> { } pub(crate) fn size_and_align_of(&self, ty: Ty<'tcx>) -> (Size, Align) { - let layout = self.layout_of(ty); + self.spanned_size_and_align_of(ty, DUMMY_SP) + } + + pub(crate) fn spanned_size_and_align_of(&self, ty: Ty<'tcx>, span: Span) -> (Size, Align) { + let layout = self.spanned_layout_of(ty, span); (layout.size, layout.align.abi) } } diff --git a/tests/ui/limits/huge-array-simple-64.stderr b/tests/ui/limits/huge-array-simple-64.full-debuginfo.stderr similarity index 83% rename from tests/ui/limits/huge-array-simple-64.stderr rename to tests/ui/limits/huge-array-simple-64.full-debuginfo.stderr index 46df288d4f78..8ce93ab1884e 100644 --- a/tests/ui/limits/huge-array-simple-64.stderr +++ b/tests/ui/limits/huge-array-simple-64.full-debuginfo.stderr @@ -1,5 +1,5 @@ error: values of the type `[u8; 2305843011361177600]` are too big for the target architecture - --> $DIR/huge-array-simple-64.rs:7:9 + --> $DIR/huge-array-simple-64.rs:12:9 | LL | let _fat: [u8; (1<<61)+(1<<31)] = | ^^^^ diff --git a/tests/ui/limits/huge-array-simple-64.no-debuginfo.stderr b/tests/ui/limits/huge-array-simple-64.no-debuginfo.stderr new file mode 100644 index 000000000000..8ce93ab1884e --- /dev/null +++ b/tests/ui/limits/huge-array-simple-64.no-debuginfo.stderr @@ -0,0 +1,8 @@ +error: values of the type `[u8; 2305843011361177600]` are too big for the target architecture + --> $DIR/huge-array-simple-64.rs:12:9 + | +LL | let _fat: [u8; (1<<61)+(1<<31)] = + | ^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/limits/huge-array-simple-64.rs b/tests/ui/limits/huge-array-simple-64.rs index d2838e0d41e7..0c000940062f 100644 --- a/tests/ui/limits/huge-array-simple-64.rs +++ b/tests/ui/limits/huge-array-simple-64.rs @@ -1,3 +1,8 @@ +// FIXME(#61117): Remove revisions once x86_64-gnu-debug CI job sets rust.debuginfo-level-tests=2 +// NOTE: The .stderr for both revisions shall be identical. +//@ revisions: no-debuginfo full-debuginfo +//@[no-debuginfo] compile-flags: -Cdebuginfo=0 +//@[full-debuginfo] compile-flags: -Cdebuginfo=2 //@ build-fail //@ ignore-32bit diff --git a/tests/ui/limits/huge-array.stderr b/tests/ui/limits/huge-array.full-debuginfo.stderr similarity index 87% rename from tests/ui/limits/huge-array.stderr rename to tests/ui/limits/huge-array.full-debuginfo.stderr index ce0c0d650c2f..0a9c8c67ce9a 100644 --- a/tests/ui/limits/huge-array.stderr +++ b/tests/ui/limits/huge-array.full-debuginfo.stderr @@ -1,5 +1,5 @@ error: values of the type `[[u8; 1518599999]; 1518600000]` are too big for the target architecture - --> $DIR/huge-array.rs:4:9 + --> $DIR/huge-array.rs:9:9 | LL | let s: [T; 1518600000] = [t; 1518600000]; | ^ diff --git a/tests/ui/limits/huge-array.no-debuginfo.stderr b/tests/ui/limits/huge-array.no-debuginfo.stderr new file mode 100644 index 000000000000..0a9c8c67ce9a --- /dev/null +++ b/tests/ui/limits/huge-array.no-debuginfo.stderr @@ -0,0 +1,8 @@ +error: values of the type `[[u8; 1518599999]; 1518600000]` are too big for the target architecture + --> $DIR/huge-array.rs:9:9 + | +LL | let s: [T; 1518600000] = [t; 1518600000]; + | ^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/limits/huge-array.rs b/tests/ui/limits/huge-array.rs index 97cfd1ff8fb2..0cf590276947 100644 --- a/tests/ui/limits/huge-array.rs +++ b/tests/ui/limits/huge-array.rs @@ -1,3 +1,8 @@ +// FIXME(#61117): Remove revisions once x86_64-gnu-debug CI job sets rust.debuginfo-level-tests=2 +// NOTE: The .stderr for both revisions shall be identical. +//@ revisions: no-debuginfo full-debuginfo +//@[no-debuginfo] compile-flags: -Cdebuginfo=0 +//@[full-debuginfo] compile-flags: -Cdebuginfo=2 //@ build-fail fn generic(t: T) { diff --git a/tests/ui/limits/issue-15919-64.stderr b/tests/ui/limits/issue-15919-64.full-debuginfo.stderr similarity index 85% rename from tests/ui/limits/issue-15919-64.stderr rename to tests/ui/limits/issue-15919-64.full-debuginfo.stderr index cd443f2065bb..54434675d250 100644 --- a/tests/ui/limits/issue-15919-64.stderr +++ b/tests/ui/limits/issue-15919-64.full-debuginfo.stderr @@ -1,5 +1,5 @@ error: values of the type `[usize; usize::MAX]` are too big for the target architecture - --> $DIR/issue-15919-64.rs:5:9 + --> $DIR/issue-15919-64.rs:10:9 | LL | let x = [0usize; 0xffff_ffff_ffff_ffff]; | ^ diff --git a/tests/ui/limits/issue-15919-64.no-debuginfo.stderr b/tests/ui/limits/issue-15919-64.no-debuginfo.stderr new file mode 100644 index 000000000000..54434675d250 --- /dev/null +++ b/tests/ui/limits/issue-15919-64.no-debuginfo.stderr @@ -0,0 +1,8 @@ +error: values of the type `[usize; usize::MAX]` are too big for the target architecture + --> $DIR/issue-15919-64.rs:10:9 + | +LL | let x = [0usize; 0xffff_ffff_ffff_ffff]; + | ^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/limits/issue-15919-64.rs b/tests/ui/limits/issue-15919-64.rs index 7e6200882a96..c1eee1d95ba1 100644 --- a/tests/ui/limits/issue-15919-64.rs +++ b/tests/ui/limits/issue-15919-64.rs @@ -1,3 +1,8 @@ +// FIXME(#61117): Remove revisions once x86_64-gnu-debug CI job sets rust.debuginfo-level-tests=2 +// NOTE: The .stderr for both revisions shall be identical. +//@ revisions: no-debuginfo full-debuginfo +//@[no-debuginfo] compile-flags: -Cdebuginfo=0 +//@[full-debuginfo] compile-flags: -Cdebuginfo=2 //@ build-fail //@ ignore-32bit From 823101e0895d4ce17576a2aa487ba6fe3e38ea69 Mon Sep 17 00:00:00 2001 From: Elliot Roberts Date: Sat, 30 Aug 2025 13:36:29 -0700 Subject: [PATCH 164/682] Pass `--target` before `--` for `cargo rustc` --- .../project-model/src/toolchain_info/target_data_layout.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data_layout.rs b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data_layout.rs index a28f468e692d..f1d99cb8b002 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data_layout.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data_layout.rs @@ -23,14 +23,11 @@ pub fn get( QueryConfig::Cargo(sysroot, cargo_toml, _) => { let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent(), extra_env); cmd.env("RUSTC_BOOTSTRAP", "1"); - cmd.args(["rustc", "-Z", "unstable-options"]).args(RUSTC_ARGS).args([ - "--", - "-Z", - "unstable-options", - ]); + cmd.args(["rustc", "-Z", "unstable-options"]).args(RUSTC_ARGS); if let Some(target) = target { cmd.args(["--target", target]); } + cmd.args(["--", "-Z", "unstable-options"]); match utf8_stdout(&mut cmd) { Ok(output) => return process(output), Err(e) => { From a324fe956981f4ad736ffc268f222037fd853e83 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 2 Sep 2025 06:39:32 +0300 Subject: [PATCH 165/682] Make sense of the mess that were (are) different kind of generics in the solver To the extent possible. Previously they were confused. Sometimes generic params were treated as `Param` and sometimes as `Placeholder`. A completely redundant (in the new solver) mapping of salsa::Id to ints to intern some info where we could just store it uninterned (not in Chalk though, for some weird reason). Plus fix a cute bug in closure substitution that was caught by the assertions of Chalk but the next solver did not have such assertions. Do we need more assertions? --- .../crates/hir-ty/src/chalk_ext.rs | 2 +- .../crates/hir-ty/src/consteval.rs | 29 ++- .../crates/hir-ty/src/consteval_nextsolver.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/db.rs | 6 +- .../crates/hir-ty/src/display.rs | 46 ++--- .../crates/hir-ty/src/dyn_compatibility.rs | 20 +- .../crates/hir-ty/src/generics.rs | 8 +- .../crates/hir-ty/src/infer/closure.rs | 4 +- .../rust-analyzer/crates/hir-ty/src/layout.rs | 17 +- .../rust-analyzer/crates/hir-ty/src/lib.rs | 4 +- .../rust-analyzer/crates/hir-ty/src/lower.rs | 12 +- .../crates/hir-ty/src/lower/path.rs | 8 +- .../crates/hir-ty/src/lower_nextsolver.rs | 13 +- .../hir-ty/src/lower_nextsolver/path.rs | 2 + .../crates/hir-ty/src/mapping.rs | 36 +++- .../crates/hir-ty/src/mir/monomorphization.rs | 4 +- .../crates/hir-ty/src/next_solver/consts.rs | 3 + .../hir-ty/src/next_solver/generic_arg.rs | 38 ++-- .../crates/hir-ty/src/next_solver/generics.rs | 63 +++--- .../hir-ty/src/next_solver/infer/mod.rs | 13 +- .../crates/hir-ty/src/next_solver/mapping.rs | 191 ++++++++++-------- .../crates/hir-ty/src/next_solver/region.rs | 3 + .../crates/hir-ty/src/next_solver/ty.rs | 12 +- .../crates/hir-ty/src/variance.rs | 4 +- .../rust-analyzer/crates/hir/src/display.rs | 11 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 6 +- 26 files changed, 324 insertions(+), 233 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs index 8fa1aff0ef63..33ae099c690e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs @@ -306,7 +306,7 @@ impl TyExt for Ty { predicates.map(|it| it.into_value_and_skipped_binders().0) } TyKind::Placeholder(idx) => { - let id = from_placeholder_idx(db, *idx); + let id = from_placeholder_idx(db, *idx).0; let generic_params = db.generic_params(id.parent); let param_data = &generic_params[id.local_id]; match param_data { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs index abf97f3d0e30..0f2cc17f563d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -107,25 +107,24 @@ pub(crate) fn path_to_const<'g>( match resolver.resolve_path_in_value_ns_fully(db, path, HygieneId::ROOT) { Some(ValueNs::GenericParam(p)) => { let ty = db.const_param_ty(p); + let args = args(); let value = match mode { ParamLoweringMode::Placeholder => { - ConstValue::Placeholder(to_placeholder_idx(db, p.into())) + let idx = args.type_or_const_param_idx(p.into()).unwrap(); + ConstValue::Placeholder(to_placeholder_idx(db, p.into(), idx as u32)) } - ParamLoweringMode::Variable => { - let args = args(); - match args.type_or_const_param_idx(p.into()) { - Some(it) => ConstValue::BoundVar(BoundVar::new(debruijn, it)), - None => { - never!( - "Generic list doesn't contain this param: {:?}, {:?}, {:?}", - args, - path, - p - ); - return None; - } + ParamLoweringMode::Variable => match args.type_or_const_param_idx(p.into()) { + Some(it) => ConstValue::BoundVar(BoundVar::new(debruijn, it)), + None => { + never!( + "Generic list doesn't contain this param: {:?}, {:?}, {:?}", + args, + path, + p + ); + return None; } - } + }, }; Some(ConstData { ty, value }.intern(Interner)) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs index 00fc4e5610dc..4f95c9a13acd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval_nextsolver.rs @@ -49,7 +49,7 @@ pub(crate) fn path_to_const<'a, 'g>( .and_then(|(idx, p)| p.const_param().map(|p| (idx, p.clone()))) { Some((idx, _param)) => { - Some(Const::new_param(interner, ParamConst { index: idx as u32 })) + Some(Const::new_param(interner, ParamConst { index: idx as u32, id: p })) } None => { never!( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 97754f47233b..0b7213d785c9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -410,13 +410,15 @@ fn hir_database_is_dyn_compatible() { #[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] #[derive(PartialOrd, Ord)] pub struct InternedTypeOrConstParamId { - pub loc: TypeOrConstParamId, + /// This stores the param and its index. + pub loc: (TypeOrConstParamId, u32), } #[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] #[derive(PartialOrd, Ord)] pub struct InternedLifetimeParamId { - pub loc: LifetimeParamId, + /// This stores the param and its index. + pub loc: (LifetimeParamId, u32), } #[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 0df727a8e5c0..5b8093f6b724 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -61,9 +61,8 @@ use crate::{ next_solver::{ BoundExistentialPredicate, Ctor, DbInterner, GenericArgs, SolverDefId, mapping::{ - ChalkToNextSolver, bound_var_to_lifetime_idx, bound_var_to_type_or_const_param_idx, - convert_args_for_result, convert_const_for_result, convert_region_for_result, - convert_ty_for_result, + ChalkToNextSolver, convert_args_for_result, convert_const_for_result, + convert_region_for_result, convert_ty_for_result, }, }, primitive, to_assoc_type_id, @@ -641,7 +640,7 @@ impl HirDisplay for ProjectionTy { && !f.bounds_formatting_ctx.contains(self) { let db = f.db; - let id = from_placeholder_idx(db, *idx); + let id = from_placeholder_idx(db, *idx).0; let generics = generics(db, id.parent); let substs = generics.placeholder_subst(db); @@ -736,14 +735,14 @@ impl HirDisplay for Const { impl<'db> HirDisplay for crate::next_solver::Const<'db> { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { match self.kind() { + rustc_type_ir::ConstKind::Placeholder(_) => write!(f, ""), rustc_type_ir::ConstKind::Bound(db, bound_const) => { write!(f, "?{}.{}", db.as_u32(), bound_const.as_u32()) } rustc_type_ir::ConstKind::Infer(..) => write!(f, "#c#"), - rustc_type_ir::ConstKind::Placeholder(idx) => { - let id = bound_var_to_type_or_const_param_idx(f.db, idx.bound); - let generics = generics(f.db, id.parent); - let param_data = &generics[id.local_id]; + rustc_type_ir::ConstKind::Param(param) => { + let generics = generics(f.db, param.id.parent()); + let param_data = &generics[param.id.local_id()]; write!(f, "{}", param_data.name().unwrap().display(f.db, f.edition()))?; Ok(()) } @@ -765,7 +764,6 @@ impl<'db> HirDisplay for crate::next_solver::Const<'db> { } rustc_type_ir::ConstKind::Error(..) => f.write_char('_'), rustc_type_ir::ConstKind::Expr(..) => write!(f, ""), - rustc_type_ir::ConstKind::Param(_) => write!(f, ""), } } } @@ -1178,7 +1176,7 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { if let TyKind::Ref(l, _, _) = kind { f.write_char('&')?; if f.render_region(l) { - convert_region_for_result(l).hir_fmt(f)?; + convert_region_for_result(interner, l).hir_fmt(f)?; f.write_char(' ')?; } match m { @@ -1611,14 +1609,10 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { write!(f, "{{closure}}")?; } } - TyKind::Placeholder(idx) => { - let placeholder_index = chalk_ir::PlaceholderIndex { - idx: idx.bound.var.as_usize(), - ui: chalk_ir::UniverseIndex { counter: idx.universe.as_usize() }, - }; - let id = from_placeholder_idx(db, placeholder_index); - let generics = generics(db, id.parent); - let param_data = &generics[id.local_id]; + TyKind::Placeholder(_) => write!(f, "{{placeholder}}")?, + TyKind::Param(param) => { + let generics = generics(db, param.id.parent()); + let param_data = &generics[param.id.local_id()]; match param_data { TypeOrConstParamData::TypeParamData(p) => match p.provenance { TypeParamProvenance::TypeParamList | TypeParamProvenance::TraitSelf => { @@ -1634,7 +1628,7 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { TypeParamProvenance::ArgumentImplTrait => { let substs = generics.placeholder_subst(db); let bounds = db - .generic_predicates(id.parent) + .generic_predicates(param.id.parent()) .iter() .map(|pred| pred.clone().substitute(Interner, &substs)) .filter(|wc| match wc.skip_binders() { @@ -1656,7 +1650,7 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { WhereClause::LifetimeOutlives(_) => false, }) .collect::>(); - let krate = id.parent.module(db).krate(); + let krate = param.id.parent().module(db).krate(); write_bounds_like_dyn_trait_with_prefix( f, "impl", @@ -1671,7 +1665,6 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { } } } - TyKind::Param(_) => write!(f, "{{param}}")?, TyKind::Bound(debruijn_index, ty) => { let idx = chalk_ir::BoundVar { debruijn: chalk_ir::DebruijnIndex::new(debruijn_index.as_u32()), @@ -2294,7 +2287,7 @@ impl HirDisplay for LifetimeData { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { match self { LifetimeData::Placeholder(idx) => { - let id = lt_from_placeholder_idx(f.db, *idx); + let id = lt_from_placeholder_idx(f.db, *idx).0; let generics = generics(f.db, id.parent); let param_data = &generics[id.local_id]; write!(f, "{}", param_data.name.display(f.db, f.edition()))?; @@ -2319,10 +2312,9 @@ impl HirDisplay for LifetimeData { impl<'db> HirDisplay for crate::next_solver::Region<'db> { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { match self.kind() { - rustc_type_ir::RegionKind::RePlaceholder(idx) => { - let id = bound_var_to_lifetime_idx(f.db, idx.bound.var); - let generics = generics(f.db, id.parent); - let param_data = &generics[id.local_id]; + rustc_type_ir::RegionKind::ReEarlyParam(param) => { + let generics = generics(f.db, param.id.parent); + let param_data = &generics[param.id.local_id]; write!(f, "{}", param_data.name.display(f.db, f.edition()))?; Ok(()) } @@ -2339,7 +2331,7 @@ impl<'db> HirDisplay for crate::next_solver::Region<'db> { } } rustc_type_ir::RegionKind::ReErased => write!(f, "'"), - rustc_type_ir::RegionKind::ReEarlyParam(_) => write!(f, ""), + rustc_type_ir::RegionKind::RePlaceholder(_) => write!(f, ""), rustc_type_ir::RegionKind::ReLateParam(_) => write!(f, ""), } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index b0c61c29db0b..23280b1f3b42 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -2,10 +2,12 @@ use std::ops::ControlFlow; +use hir_def::hir::generics::LocalTypeOrConstParamId; use hir_def::{ AssocItemId, ConstId, CrateRootModuleId, FunctionId, GenericDefId, HasModule, TraitId, TypeAliasId, lang_item::LangItem, signatures::TraitFlags, }; +use hir_def::{TypeOrConstParamId, TypeParamId}; use intern::Symbol; use rustc_hash::FxHashSet; use rustc_type_ir::{ @@ -384,7 +386,6 @@ where } // Allow `impl AutoTrait` predicates - let interner = DbInterner::new_with(db, Some(trait_.krate(db)), None); if let ClauseKind::Trait(TraitPredicate { trait_ref: pred_trait_ref, polarity: PredicatePolarity::Positive, @@ -392,11 +393,8 @@ where && let SolverDefId::TraitId(trait_id) = pred_trait_ref.def_id && let trait_data = db.trait_signature(trait_id) && trait_data.flags.contains(TraitFlags::AUTO) - && pred_trait_ref.self_ty() - == crate::next_solver::Ty::new( - interner, - rustc_type_ir::TyKind::Param(crate::next_solver::ParamTy { index: 0 }), - ) + && let rustc_type_ir::TyKind::Param(crate::next_solver::ParamTy { index: 0, .. }) = + pred_trait_ref.self_ty().kind() { continue; } @@ -422,9 +420,13 @@ fn receiver_is_dispatchable<'db>( let sig = sig.instantiate_identity(); let interner: DbInterner<'_> = DbInterner::new_with(db, Some(trait_.krate(db)), None); + let self_param_id = TypeParamId::from_unchecked(TypeOrConstParamId { + parent: trait_.into(), + local_id: LocalTypeOrConstParamId::from_raw(la_arena::RawIdx::from_u32(0)), + }); let self_param_ty = crate::next_solver::Ty::new( interner, - rustc_type_ir::TyKind::Param(crate::next_solver::ParamTy { index: 0 }), + rustc_type_ir::TyKind::Param(crate::next_solver::ParamTy { index: 0, id: self_param_id }), ); // `self: Self` can't be dispatched on, but this is already considered dyn-compatible @@ -452,7 +454,9 @@ fn receiver_is_dispatchable<'db>( }; // Type `U` - let unsized_self_ty = crate::next_solver::Ty::new_param(interner, u32::MAX, Symbol::empty()); + // FIXME: That seems problematic to fake a generic param like that? + let unsized_self_ty = + crate::next_solver::Ty::new_param(interner, self_param_id, u32::MAX, Symbol::empty()); // `Receiver[Self => U]` let unsized_receiver_ty = receiver_for_self_ty(interner, func, receiver_ty, unsized_self_ty); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs index 412b3ac42502..e179e41b1cbe 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs @@ -256,15 +256,15 @@ impl Generics { pub fn placeholder_subst(&self, db: &dyn HirDatabase) -> Substitution { Substitution::from_iter( Interner, - self.iter_id().map(|id| match id { + self.iter_id().enumerate().map(|(index, id)| match id { GenericParamId::TypeParamId(id) => { - to_placeholder_idx(db, id.into()).to_ty(Interner).cast(Interner) + to_placeholder_idx(db, id.into(), index as u32).to_ty(Interner).cast(Interner) } - GenericParamId::ConstParamId(id) => to_placeholder_idx(db, id.into()) + GenericParamId::ConstParamId(id) => to_placeholder_idx(db, id.into(), index as u32) .to_const(Interner, db.const_param_ty(id)) .cast(Interner), GenericParamId::LifetimeParamId(id) => { - lt_to_placeholder_idx(db, id).to_lifetime(Interner).cast(Interner) + lt_to_placeholder_idx(db, id, index as u32).to_lifetime(Interner).cast(Interner) } }), ) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 38ac2e117077..9ef046afdb3d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -866,7 +866,7 @@ impl CapturedItemWithoutTy { idx: chalk_ir::PlaceholderIndex, outer_binder: DebruijnIndex, ) -> Result, Self::Error> { - let x = from_placeholder_idx(self.db, idx); + let x = from_placeholder_idx(self.db, idx).0; let Some(idx) = self.generics.type_or_const_param_idx(x) else { return Err(()); }; @@ -878,7 +878,7 @@ impl CapturedItemWithoutTy { idx: chalk_ir::PlaceholderIndex, outer_binder: DebruijnIndex, ) -> std::result::Result { - let x = from_placeholder_idx(self.db, idx); + let x = from_placeholder_idx(self.db, idx).0; let Some(idx) = self.generics.type_or_const_param_idx(x) else { return Err(()); }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index e2ee8935a885..f8abb3b7f6fe 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -19,14 +19,15 @@ use rustc_type_ir::{ }; use triomphe::Arc; +use crate::utils::ClosureSubst; use crate::{ - TraitEnvironment, + Interner, TraitEnvironment, consteval_nextsolver::try_const_usize, db::HirDatabase, next_solver::{ DbInterner, GenericArgs, ParamEnv, SolverDefId, Ty, TyKind, TypingMode, infer::{DbInternerInferExt, traits::ObligationCause}, - mapping::{ChalkToNextSolver, convert_binder_to_early_binder}, + mapping::{ChalkToNextSolver, convert_args_for_result}, project::solve_normalize::deeply_normalize, }, }; @@ -333,9 +334,15 @@ pub fn layout_of_ty_query<'db>( let fields = captures .iter() .map(|it| { - let ty = - convert_binder_to_early_binder(interner, it.ty.to_nextsolver(interner)) - .instantiate(interner, args); + let ty = it + .ty + .clone() + .substitute( + Interner, + ClosureSubst(&convert_args_for_result(interner, args.inner())) + .parent_subst(), + ) + .to_nextsolver(interner); db.layout_of_ty(ty, trait_env.clone()) }) .collect::, _>>()?; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 7fdfb205721e..c16bbb7b9922 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -124,7 +124,7 @@ pub use lower_nextsolver::associated_type_shorthand_candidates; pub use mapping::{ ToChalk, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx, lt_from_placeholder_idx, lt_to_placeholder_idx, to_assoc_type_id, to_chalk_trait_id, - to_foreign_def_id, to_placeholder_idx, + to_foreign_def_id, to_placeholder_idx, to_placeholder_idx_no_index, }; pub use method_resolution::check_orphan_rules; pub use target_feature::TargetFeatures; @@ -1007,7 +1007,7 @@ struct PlaceholderCollector<'db> { impl PlaceholderCollector<'_> { fn collect(&mut self, idx: PlaceholderIndex) { - let id = from_placeholder_idx(self.db, idx); + let id = from_placeholder_idx(self.db, idx).0; self.placeholders.insert(id); } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 098c62ba97a2..79f78c545e59 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -340,7 +340,13 @@ impl<'a> TyLoweringContext<'a> { res = Some(TypeNs::GenericParam(type_param_id)); match self.type_param_mode { ParamLoweringMode::Placeholder => { - TyKind::Placeholder(to_placeholder_idx(self.db, type_param_id.into())) + let generics = self.generics(); + let idx = generics.type_or_const_param_idx(type_param_id.into()).unwrap(); + TyKind::Placeholder(to_placeholder_idx( + self.db, + type_param_id.into(), + idx as u32, + )) } ParamLoweringMode::Variable => { let idx = @@ -777,7 +783,9 @@ impl<'a> TyLoweringContext<'a> { LifetimeNs::Static => static_lifetime(), LifetimeNs::LifetimeParam(id) => match self.type_param_mode { ParamLoweringMode::Placeholder => { - LifetimeData::Placeholder(lt_to_placeholder_idx(self.db, id)) + let generics = self.generics(); + let idx = generics.lifetime_idx(id).unwrap(); + LifetimeData::Placeholder(lt_to_placeholder_idx(self.db, id, idx as u32)) } ParamLoweringMode::Variable => { let idx = match self.generics().lifetime_idx(id) { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs index fb85909d7f41..b0132e4dcbc4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs @@ -222,7 +222,13 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { } TypeNs::GenericParam(param_id) => match self.ctx.type_param_mode { ParamLoweringMode::Placeholder => { - TyKind::Placeholder(to_placeholder_idx(self.ctx.db, param_id.into())) + let generics = self.ctx.generics(); + let idx = generics.type_or_const_param_idx(param_id.into()).unwrap(); + TyKind::Placeholder(to_placeholder_idx( + self.ctx.db, + param_id.into(), + idx as u32, + )) } ParamLoweringMode::Variable => { let idx = match self.ctx.generics().type_or_const_param_idx(param_id.into()) { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs index d87181f545d8..4578922ce321 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -64,8 +64,7 @@ use crate::{ AdtDef, AliasTy, Binder, BoundExistentialPredicates, BoundRegionKind, BoundTyKind, BoundVarKind, BoundVarKinds, Clause, Clauses, Const, DbInterner, EarlyBinder, EarlyParamRegion, ErrorGuaranteed, GenericArgs, PolyFnSig, Predicate, Region, SolverDefId, - TraitPredicate, TraitRef, Ty, Tys, abi::Safety, generics::GenericParamDefKind, - mapping::ChalkToNextSolver, + TraitPredicate, TraitRef, Ty, Tys, abi::Safety, mapping::ChalkToNextSolver, }, }; @@ -322,6 +321,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { }; Ty::new_param( self.interner, + type_param_id, idx as u32, type_data .name @@ -866,7 +866,10 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { None => return Region::error(self.interner), Some(idx) => idx, }; - Region::new_early_param(self.interner, EarlyParamRegion { index: idx as u32 }) + Region::new_early_param( + self.interner, + EarlyParamRegion { index: idx as u32, id }, + ) } }, None => Region::error(self.interner), @@ -1344,11 +1347,11 @@ where { continue; } - let GenericParamDefKind::Type = p.kind else { + let GenericParamId::TypeParamId(param_id) = p.id else { continue; }; let idx = idx as u32 + generics.parent_count as u32; - let param_ty = Ty::new_param(interner, idx, p.name.clone()); + let param_ty = Ty::new_param(interner, param_id, idx, p.name.clone()); if explicitly_unsized_tys.contains(¶m_ty) { continue; } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs index e3efb3830640..ccdb5a0bdb4a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs @@ -281,6 +281,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { }; Ty::new_param( self.ctx.interner, + param_id, idx as u32, p.name .as_ref() @@ -758,6 +759,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { self.ctx.ctx.db.generic_defaults(def).get(preceding_args.len()).map(|default| { convert_binder_to_early_binder( self.ctx.ctx.interner, + def, default.to_nextsolver(self.ctx.ctx.interner), ) .instantiate(self.ctx.ctx.interner, preceding_args) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs index 448fbdf67365..5125a38825cb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs @@ -104,7 +104,10 @@ pub fn from_assoc_type_id(id: AssocTypeId) -> TypeAliasId { FromId::from_id(id.0) } -pub fn from_placeholder_idx(db: &dyn HirDatabase, idx: PlaceholderIndex) -> TypeOrConstParamId { +pub fn from_placeholder_idx( + db: &dyn HirDatabase, + idx: PlaceholderIndex, +) -> (TypeOrConstParamId, u32) { assert_eq!(idx.ui, chalk_ir::UniverseIndex::ROOT); // SAFETY: We cannot really encapsulate this unfortunately, so just hope this is sound. let interned_id = @@ -112,15 +115,32 @@ pub fn from_placeholder_idx(db: &dyn HirDatabase, idx: PlaceholderIndex) -> Type interned_id.loc(db) } -pub fn to_placeholder_idx(db: &dyn HirDatabase, id: TypeOrConstParamId) -> PlaceholderIndex { - let interned_id = InternedTypeOrConstParamId::new(db, id); +pub fn to_placeholder_idx( + db: &dyn HirDatabase, + id: TypeOrConstParamId, + idx: u32, +) -> PlaceholderIndex { + let interned_id = InternedTypeOrConstParamId::new(db, (id, idx)); PlaceholderIndex { ui: chalk_ir::UniverseIndex::ROOT, idx: interned_id.as_id().index() as usize, } } -pub fn lt_from_placeholder_idx(db: &dyn HirDatabase, idx: PlaceholderIndex) -> LifetimeParamId { +pub fn to_placeholder_idx_no_index( + db: &dyn HirDatabase, + id: TypeOrConstParamId, +) -> PlaceholderIndex { + let index = crate::generics::generics(db, id.parent) + .type_or_const_param_idx(id) + .expect("param not found"); + to_placeholder_idx(db, id, index as u32) +} + +pub fn lt_from_placeholder_idx( + db: &dyn HirDatabase, + idx: PlaceholderIndex, +) -> (LifetimeParamId, u32) { assert_eq!(idx.ui, chalk_ir::UniverseIndex::ROOT); // SAFETY: We cannot really encapsulate this unfortunately, so just hope this is sound. let interned_id = @@ -128,8 +148,12 @@ pub fn lt_from_placeholder_idx(db: &dyn HirDatabase, idx: PlaceholderIndex) -> L interned_id.loc(db) } -pub fn lt_to_placeholder_idx(db: &dyn HirDatabase, id: LifetimeParamId) -> PlaceholderIndex { - let interned_id = InternedLifetimeParamId::new(db, id); +pub fn lt_to_placeholder_idx( + db: &dyn HirDatabase, + id: LifetimeParamId, + idx: u32, +) -> PlaceholderIndex { + let interned_id = InternedLifetimeParamId::new(db, (id, idx)); PlaceholderIndex { ui: chalk_ir::UniverseIndex::ROOT, idx: interned_id.as_id().index() as usize, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs index d8f443145ca0..b0b922316914 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs @@ -99,7 +99,7 @@ impl FallibleTypeFolder for Filler<'_> { idx: chalk_ir::PlaceholderIndex, _outer_binder: DebruijnIndex, ) -> std::result::Result, Self::Error> { - let it = from_placeholder_idx(self.db, idx); + let it = from_placeholder_idx(self.db, idx).0; let Some(idx) = self.generics.as_ref().and_then(|g| g.type_or_const_param_idx(it)) else { not_supported!("missing idx in generics"); }; @@ -117,7 +117,7 @@ impl FallibleTypeFolder for Filler<'_> { idx: chalk_ir::PlaceholderIndex, _outer_binder: DebruijnIndex, ) -> std::result::Result { - let it = from_placeholder_idx(self.db, idx); + let it = from_placeholder_idx(self.db, idx).0; let Some(idx) = self.generics.as_ref().and_then(|g| g.type_or_const_param_idx(it)) else { not_supported!("missing idx in generics"); }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs index ce581cfad4b2..cfafc65d1844 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs @@ -2,6 +2,7 @@ use std::hash::Hash; +use hir_def::{ConstParamId, TypeOrConstParamId}; use intern::{Interned, Symbol}; use rustc_ast_ir::try_visit; use rustc_ast_ir::visit::VisitorResult; @@ -84,6 +85,8 @@ pub type PlaceholderConst = Placeholder; #[derive(Copy, Clone, Hash, Eq, PartialEq)] pub struct ParamConst { + // FIXME: See `ParamTy`. + pub id: ConstParamId, pub index: u32, } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs index 76186e374608..d284eb9c6b41 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs @@ -1,5 +1,6 @@ //! Things related to generic args in the next-trait-solver. +use hir_def::GenericParamId; use intern::{Interned, Symbol}; use rustc_type_ir::{ ClosureArgs, CollectAndApply, ConstVid, CoroutineArgs, CoroutineClosureArgs, FnSig, FnSigTys, @@ -14,7 +15,7 @@ use crate::db::HirDatabase; use super::{ Const, DbInterner, EarlyParamRegion, ErrorGuaranteed, ParamConst, Region, SolverDefId, Ty, Tys, - generics::{GenericParamDef, GenericParamDefKind, Generics}, + generics::{GenericParamDef, Generics}, interned_vec_db, }; @@ -203,7 +204,7 @@ impl<'db> GenericArgs<'db> { mut mk_kind: F, ) -> GenericArgs<'db> where - F: FnMut(&Symbol, u32, GenericParamDefKind, &[GenericArg<'db>]) -> GenericArg<'db>, + F: FnMut(&Symbol, u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, { let defs = interner.generics_of(def_id); let count = defs.count(); @@ -218,7 +219,7 @@ impl<'db> GenericArgs<'db> { defs: Generics, mk_kind: &mut F, ) where - F: FnMut(&Symbol, u32, GenericParamDefKind, &[GenericArg<'db>]) -> GenericArg<'db>, + F: FnMut(&Symbol, u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, { let self_len = defs.own_params.len() as u32; if let Some(def_id) = defs.parent { @@ -230,12 +231,12 @@ impl<'db> GenericArgs<'db> { fn fill_single(args: &mut SmallVec<[GenericArg<'db>; 8]>, defs: &Generics, mk_kind: &mut F) where - F: FnMut(&Symbol, u32, GenericParamDefKind, &[GenericArg<'db>]) -> GenericArg<'db>, + F: FnMut(&Symbol, u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>, { let start_len = args.len(); args.reserve(defs.own_params.len()); for param in &defs.own_params { - let kind = mk_kind(¶m.name, args.len() as u32, param.kind, args); + let kind = mk_kind(¶m.name, args.len() as u32, param.id, args); args.push(kind); } } @@ -412,26 +413,25 @@ pub fn mk_param<'db>( interner: DbInterner<'db>, index: u32, name: &Symbol, - kind: GenericParamDefKind, + id: GenericParamId, ) -> GenericArg<'db> { let name = name.clone(); - match kind { - GenericParamDefKind::Lifetime => { - Region::new_early_param(interner, EarlyParamRegion { index }).into() + match id { + GenericParamId::LifetimeParamId(id) => { + Region::new_early_param(interner, EarlyParamRegion { index, id }).into() + } + GenericParamId::TypeParamId(id) => Ty::new_param(interner, id, index, name).into(), + GenericParamId::ConstParamId(id) => { + Const::new_param(interner, ParamConst { index, id }).into() } - GenericParamDefKind::Type => Ty::new_param(interner, index, name).into(), - GenericParamDefKind::Const => Const::new_param(interner, ParamConst { index }).into(), } } -pub fn error_for_param_kind<'db>( - kind: GenericParamDefKind, - interner: DbInterner<'db>, -) -> GenericArg<'db> { - match kind { - GenericParamDefKind::Lifetime => Region::error(interner).into(), - GenericParamDefKind::Type => Ty::new_error(interner, ErrorGuaranteed).into(), - GenericParamDefKind::Const => Const::error(interner).into(), +pub fn error_for_param_kind<'db>(id: GenericParamId, interner: DbInterner<'db>) -> GenericArg<'db> { + match id { + GenericParamId::LifetimeParamId(_) => Region::error(interner).into(), + GenericParamId::TypeParamId(_) => Ty::new_error(interner, ErrorGuaranteed).into(), + GenericParamId::ConstParamId(_) => Const::error(interner).into(), } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generics.rs index a3ba8eb83453..5ec9a18a6c20 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generics.rs @@ -24,35 +24,40 @@ use super::{Const, EarlyParamRegion, ErrorGuaranteed, ParamConst, Region, Solver use super::{DbInterner, GenericArg}; pub(crate) fn generics(db: &dyn HirDatabase, def: SolverDefId) -> Generics { - let mk_lt = |index, lt: &LifetimeParamData| { + let mk_lt = |parent, index, local_id, lt: &LifetimeParamData| { let name = lt.name.symbol().clone(); - let kind = GenericParamDefKind::Lifetime; - GenericParamDef { name, index, kind } + let id = GenericParamId::LifetimeParamId(LifetimeParamId { parent, local_id }); + GenericParamDef { name, index, id } }; - let mk_ty = |index, p: &TypeOrConstParamData| { + let mk_ty = |parent, index, local_id, p: &TypeOrConstParamData| { let name = p.name().map(|n| n.symbol().clone()).unwrap_or_else(|| sym::MISSING_NAME); - let kind = match p { - TypeOrConstParamData::TypeParamData(_) => GenericParamDefKind::Type, - TypeOrConstParamData::ConstParamData(_) => GenericParamDefKind::Const, + let id = TypeOrConstParamId { parent, local_id }; + let id = match p { + TypeOrConstParamData::TypeParamData(_) => { + GenericParamId::TypeParamId(TypeParamId::from_unchecked(id)) + } + TypeOrConstParamData::ConstParamData(_) => { + GenericParamId::ConstParamId(ConstParamId::from_unchecked(id)) + } }; - GenericParamDef { name, index, kind } + GenericParamDef { name, index, id } }; - let own_params_for_generic_params = |params: &GenericParams| { + let own_params_for_generic_params = |parent, params: &GenericParams| { let mut result = Vec::with_capacity(params.len()); let mut type_and_consts = params.iter_type_or_consts(); let mut index = 0; if let Some(self_param) = params.trait_self_param() { - result.push(mk_ty(0, ¶ms[self_param])); + result.push(mk_ty(parent, 0, self_param, ¶ms[self_param])); type_and_consts.next(); index += 1; } - result.extend(params.iter_lt().map(|(_, data)| { - let lt = mk_lt(index, data); + result.extend(params.iter_lt().map(|(local_id, data)| { + let lt = mk_lt(parent, index, local_id, data); index += 1; lt })); - result.extend(type_and_consts.map(|(_, data)| { - let ty = mk_ty(index, data); + result.extend(type_and_consts.map(|(local_id, data)| { + let ty = mk_ty(parent, index, local_id, data); index += 1; ty })); @@ -60,9 +65,10 @@ pub(crate) fn generics(db: &dyn HirDatabase, def: SolverDefId) -> Generics { }; let (parent, own_params) = match (def.try_into(), def) { - (Ok(def), _) => { - (parent_generic_def(db, def), own_params_for_generic_params(&db.generic_params(def))) - } + (Ok(def), _) => ( + parent_generic_def(db, def), + own_params_for_generic_params(def, &db.generic_params(def)), + ), (_, SolverDefId::InternedOpaqueTyId(id)) => { match db.lookup_intern_impl_trait_id(id) { crate::ImplTraitId::ReturnTypeImplTrait(function_id, _) => { @@ -79,7 +85,19 @@ pub(crate) fn generics(db: &dyn HirDatabase, def: SolverDefId) -> Generics { provenance: TypeParamProvenance::TypeParamList, }); // Yes, there is a parent but we don't include it in the generics - (None, vec![mk_ty(0, ¶m)]) + // FIXME: It seems utterly sensitive to fake a generic param here. + // Also, what a horrible mess! + ( + None, + vec![mk_ty( + GenericDefId::FunctionId(salsa::plumbing::FromId::from_id(unsafe { + salsa::Id::from_index(salsa::Id::MAX_U32 - 1) + })), + 0, + LocalTypeOrConstParamId::from_raw(la_arena::RawIdx::from_u32(0)), + ¶m, + )], + ) } } } @@ -106,7 +124,7 @@ pub struct GenericParamDef { pub(crate) name: Symbol, //def_id: GenericDefId, index: u32, - pub(crate) kind: GenericParamDefKind, + pub(crate) id: GenericParamId, } impl GenericParamDef { @@ -117,13 +135,6 @@ impl GenericParamDef { } } -#[derive(Copy, Clone, Debug)] -pub enum GenericParamDefKind { - Lifetime, - Type, - Const, -} - impl<'db> rustc_type_ir::inherent::GenericsOf> for Generics { fn count(&self) -> usize { self.parent_count + self.own_params.len() diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs index 19ae76f7e358..58832aef8952 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs @@ -8,6 +8,7 @@ pub use BoundRegionConversionTime::*; pub use at::DefineOpaqueTypes; use ena::undo_log::UndoLogs; use ena::unify as ut; +use hir_def::GenericParamId; use intern::Symbol; use opaque_types::{OpaqueHiddenType, OpaqueTypeStorage}; use region_constraints::{ @@ -38,7 +39,7 @@ use crate::next_solver::fold::BoundVarReplacerDelegate; use crate::next_solver::infer::opaque_types::table::OpaqueTypeStorageEntries; use crate::next_solver::{BoundRegion, BoundTy, BoundVarKind}; -use super::generics::{GenericParamDef, GenericParamDefKind}; +use super::generics::GenericParamDef; use super::{ AliasTerm, Binder, BoundRegionKind, CanonicalQueryInput, CanonicalVarValues, Const, ConstKind, DbInterner, ErrorGuaranteed, FxIndexMap, GenericArg, GenericArgs, OpaqueTypeKey, ParamEnv, @@ -600,14 +601,14 @@ impl<'db> InferCtxt<'db> { self.next_region_var_in_universe(universe) } - fn var_for_def(&self, kind: GenericParamDefKind, name: &Symbol) -> GenericArg<'db> { - match kind { - GenericParamDefKind::Lifetime => { + fn var_for_def(&self, id: GenericParamId, name: &Symbol) -> GenericArg<'db> { + match id { + GenericParamId::LifetimeParamId(_) => { // Create a region inference variable for the given // region parameter definition. self.next_region_var().into() } - GenericParamDefKind::Type => { + GenericParamId::TypeParamId(_) => { // Create a type inference variable for the given // type parameter definition. The generic parameters are // for actual parameters that may be referred to by @@ -624,7 +625,7 @@ impl<'db> InferCtxt<'db> { Ty::new_var(self.interner, ty_var_id).into() } - GenericParamDefKind::Const => { + GenericParamId::ConstParamId(_) => { let origin = ConstVariableOrigin { param_def_id: None }; let const_var_id = self .inner diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index 203f030dfd61..6755d065e10d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -9,6 +9,7 @@ use hir_def::{ CallableDefId, ConstParamId, FunctionId, GeneralConstId, LifetimeParamId, TypeAliasId, TypeOrConstParamId, TypeParamId, signatures::TraitFlags, }; +use hir_def::{GenericDefId, GenericParamId}; use intern::sym; use rustc_type_ir::{ AliasTerm, BoundVar, DebruijnIndex, ExistentialProjection, ExistentialTraitRef, Interner as _, @@ -35,6 +36,9 @@ use crate::{ }, to_assoc_type_id, to_chalk_trait_id, to_foreign_def_id, }; +use crate::{ + from_placeholder_idx, lt_from_placeholder_idx, lt_to_placeholder_idx, to_placeholder_idx, +}; use super::{ BoundExistentialPredicate, BoundExistentialPredicates, BoundRegion, BoundRegionKind, BoundTy, @@ -44,47 +48,24 @@ use super::{ Region, SolverDefId, SubtypePredicate, Term, TraitRef, Ty, Tys, ValueConst, VariancesOf, }; -pub fn to_placeholder_idx( - db: &dyn HirDatabase, - id: TypeOrConstParamId, - map: impl Fn(BoundVar) -> T, -) -> Placeholder { - let interned_id = InternedTypeOrConstParamId::new(db, id); - Placeholder { - universe: UniverseIndex::ZERO, - bound: map(BoundVar::from_usize(interned_id.as_id().index() as usize)), - } -} - -pub fn bound_var_to_type_or_const_param_idx( - db: &dyn HirDatabase, - var: rustc_type_ir::BoundVar, -) -> TypeOrConstParamId { - // SAFETY: We cannot really encapsulate this unfortunately, so just hope this is sound. - let interned_id = InternedTypeOrConstParamId::from_id(unsafe { Id::from_index(var.as_u32()) }); - interned_id.loc(db) -} - -pub fn bound_var_to_lifetime_idx( - db: &dyn HirDatabase, - var: rustc_type_ir::BoundVar, -) -> LifetimeParamId { - // SAFETY: We cannot really encapsulate this unfortunately, so just hope this is sound. - let interned_id = InternedLifetimeParamId::from_id(unsafe { Id::from_index(var.as_u32()) }); - interned_id.loc(db) -} - +// FIXME: This should urgently go (as soon as we finish the migration off Chalk, that is). pub fn convert_binder_to_early_binder<'db, T: rustc_type_ir::TypeFoldable>>( interner: DbInterner<'db>, + def: GenericDefId, binder: rustc_type_ir::Binder, T>, ) -> rustc_type_ir::EarlyBinder, T> { - let mut folder = BinderToEarlyBinder { interner, debruijn: rustc_type_ir::DebruijnIndex::ZERO }; + let mut folder = BinderToEarlyBinder { + interner, + debruijn: rustc_type_ir::DebruijnIndex::ZERO, + params: crate::generics::generics(interner.db, def).iter_id().collect(), + }; rustc_type_ir::EarlyBinder::bind(binder.skip_binder().fold_with(&mut folder)) } struct BinderToEarlyBinder<'db> { interner: DbInterner<'db>, debruijn: rustc_type_ir::DebruijnIndex, + params: Vec, } impl<'db> rustc_type_ir::TypeFolder> for BinderToEarlyBinder<'db> { @@ -109,7 +90,13 @@ impl<'db> rustc_type_ir::TypeFolder> for BinderToEarlyBinder<'db match t.kind() { rustc_type_ir::TyKind::Bound(debruijn, bound_ty) if self.debruijn == debruijn => { let var: rustc_type_ir::BoundVar = bound_ty.var(); - Ty::new(self.cx(), rustc_type_ir::TyKind::Param(ParamTy { index: var.as_u32() })) + let GenericParamId::TypeParamId(id) = self.params[bound_ty.var.as_usize()] else { + unreachable!() + }; + Ty::new( + self.cx(), + rustc_type_ir::TyKind::Param(ParamTy { index: var.as_u32(), id }), + ) } _ => t.super_fold_with(self), } @@ -119,10 +106,15 @@ impl<'db> rustc_type_ir::TypeFolder> for BinderToEarlyBinder<'db match r.kind() { rustc_type_ir::ReBound(debruijn, bound_region) if self.debruijn == debruijn => { let var: rustc_type_ir::BoundVar = bound_region.var(); + let GenericParamId::LifetimeParamId(id) = self.params[bound_region.var.as_usize()] + else { + unreachable!() + }; Region::new( self.cx(), rustc_type_ir::RegionKind::ReEarlyParam(EarlyParamRegion { index: var.as_u32(), + id, }), ) } @@ -133,9 +125,12 @@ impl<'db> rustc_type_ir::TypeFolder> for BinderToEarlyBinder<'db fn fold_const(&mut self, c: Const<'db>) -> Const<'db> { match c.kind() { rustc_type_ir::ConstKind::Bound(debruijn, var) if self.debruijn == debruijn => { + let GenericParamId::ConstParamId(id) = self.params[var.as_usize()] else { + unreachable!() + }; Const::new( self.cx(), - rustc_type_ir::ConstKind::Param(ParamConst { index: var.as_u32() }), + rustc_type_ir::ConstKind::Param(ParamConst { index: var.as_u32(), id }), ) } _ => c.super_fold_with(self), @@ -274,12 +269,6 @@ impl<'db> ChalkToNextSolver<'db, Ty<'db>> for chalk_ir::Ty { SolverDefId::TypeAliasId(crate::from_foreign_def_id(*foreign_def_id)), ), chalk_ir::TyKind::Error => rustc_type_ir::TyKind::Error(ErrorGuaranteed), - chalk_ir::TyKind::Placeholder(placeholder_index) => { - rustc_type_ir::TyKind::Placeholder(PlaceholderTy::new_anon( - placeholder_index.ui.to_nextsolver(interner), - rustc_type_ir::BoundVar::from_usize(placeholder_index.idx), - )) - } chalk_ir::TyKind::Dyn(dyn_ty) => { // exists { for<...> ^1.0: ... } let bounds = BoundExistentialPredicates::new_from_iter( @@ -405,6 +394,26 @@ impl<'db> ChalkToNextSolver<'db, Ty<'db>> for chalk_ir::Ty { rustc_type_ir::TyKind::FnPtr(sig_tys, header) } + // The schema here is quite confusing. + // The new solver, like rustc, uses `Param` and `EarlyBinder` for generic params. It uses `BoundVar` + // and `Placeholder` together with `Binder` for HRTB, which we mostly don't handle. + // Chalk uses `Placeholder` for generic params and `BoundVar` quite liberally, and this is quite a + // problem. `chalk_ir::TyKind::BoundVar` can represent either HRTB or generic params, depending on the + // context. When returned from signature queries, the outer `Binders` represent the generic params. + // But there are also inner `Binders` for HRTB. + // AFAIK there is no way to tell which of the meanings is relevant, so we just use `rustc_type_ir::Bound` + // here, and hope for the best. If you are working with new solver types, therefore, use the new solver + // lower queries. + // Hopefully sooner than later Chalk will be ripped from the codebase and we can avoid that problem. + // For details about the rustc setup, read: https://rustc-dev-guide.rust-lang.org/generic_parameters_summary.html + // and the following chapters. + chalk_ir::TyKind::Placeholder(placeholder_index) => { + let (id, index) = from_placeholder_idx(interner.db, *placeholder_index); + rustc_type_ir::TyKind::Param(ParamTy { + id: TypeParamId::from_unchecked(id), + index, + }) + } chalk_ir::TyKind::BoundVar(bound_var) => rustc_type_ir::TyKind::Bound( bound_var.debruijn.to_nextsolver(interner), BoundTy { @@ -440,10 +449,8 @@ impl<'db> ChalkToNextSolver<'db, Region<'db>> for chalk_ir::Lifetime { )) } chalk_ir::LifetimeData::Placeholder(placeholder_index) => { - rustc_type_ir::RegionKind::RePlaceholder(PlaceholderRegion::new_anon( - rustc_type_ir::UniverseIndex::from_u32(placeholder_index.ui.counter as u32), - rustc_type_ir::BoundVar::from_u32(placeholder_index.idx as u32), - )) + let (id, index) = lt_from_placeholder_idx(interner.db, *placeholder_index); + rustc_type_ir::RegionKind::ReEarlyParam(EarlyParamRegion { id, index }) } chalk_ir::LifetimeData::Static => rustc_type_ir::RegionKind::ReStatic, chalk_ir::LifetimeData::Erased => rustc_type_ir::RegionKind::ReErased, @@ -474,10 +481,11 @@ impl<'db> ChalkToNextSolver<'db, Const<'db>> for chalk_ir::Const { )) } chalk_ir::ConstValue::Placeholder(placeholder_index) => { - rustc_type_ir::ConstKind::Placeholder(PlaceholderConst::new( - placeholder_index.ui.to_nextsolver(interner), - rustc_type_ir::BoundVar::from_usize(placeholder_index.idx), - )) + let (id, index) = from_placeholder_idx(interner.db, *placeholder_index); + rustc_type_ir::ConstKind::Param(ParamConst { + id: ConstParamId::from_unchecked(id), + index, + }) } chalk_ir::ConstValue::Concrete(concrete_const) => match &concrete_const.interned { ConstScalar::Bytes(bytes, memory) => { @@ -971,7 +979,7 @@ pub fn convert_args_for_result<'db>( substs.push(chalk_ir::GenericArgData::Ty(ty).intern(Interner)); } rustc_type_ir::GenericArgKind::Lifetime(region) => { - let lifetime = convert_region_for_result(region); + let lifetime = convert_region_for_result(interner, region); substs.push(chalk_ir::GenericArgData::Lifetime(lifetime).intern(Interner)); } rustc_type_ir::GenericArgKind::Const(const_) => { @@ -1074,7 +1082,7 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) rustc_ast_ir::Mutability::Mut => chalk_ir::Mutability::Mut, rustc_ast_ir::Mutability::Not => chalk_ir::Mutability::Not, }; - let r = convert_region_for_result(r); + let r = convert_region_for_result(interner, r); let ty = convert_ty_for_result(interner, ty); TyKind::Ref(mutability, r, ty) } @@ -1122,17 +1130,23 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) rustc_type_ir::AliasTyKind::Free => unimplemented!(), }, + // For `Placeholder`, `Bound` and `Param`, see the comment on the reverse conversion. rustc_type_ir::TyKind::Placeholder(placeholder) => { - let ui = chalk_ir::UniverseIndex { counter: placeholder.universe.as_usize() }; - let placeholder_index = - chalk_ir::PlaceholderIndex { idx: placeholder.bound.var.as_usize(), ui }; - TyKind::Placeholder(placeholder_index) + unimplemented!( + "A `rustc_type_ir::TyKind::Placeholder` doesn't have a direct \ + correspondence in Chalk, as it represents a universally instantiated `Bound`.\n\ + It therefore feels safer to leave it panicking, but if you hit this panic \ + feel free to do the same as in `rustc_type_ir::TyKind::Bound` here." + ) } - rustc_type_ir::TyKind::Bound(debruijn_index, ty) => TyKind::BoundVar(chalk_ir::BoundVar { debruijn: chalk_ir::DebruijnIndex::new(debruijn_index.as_u32()), index: ty.var.as_usize(), }), + rustc_type_ir::TyKind::Param(param) => { + let placeholder = to_placeholder_idx(interner.db, param.id.into(), param.index); + TyKind::Placeholder(placeholder) + } rustc_type_ir::TyKind::FnPtr(bound_sig, fn_header) => { let num_binders = bound_sig.bound_vars().len(); @@ -1254,7 +1268,8 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General), ); let bounds = chalk_ir::Binders::new(binders, bounds); - let dyn_ty = chalk_ir::DynTy { bounds, lifetime: convert_region_for_result(region) }; + let dyn_ty = + chalk_ir::DynTy { bounds, lifetime: convert_region_for_result(interner, region) }; TyKind::Dyn(dyn_ty) } @@ -1316,7 +1331,6 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) TyKind::CoroutineWitness(id.into(), subst) } - rustc_type_ir::TyKind::Param(_) => unimplemented!(), rustc_type_ir::TyKind::UnsafeBinder(_) => unimplemented!(), } .intern(Interner) @@ -1327,13 +1341,16 @@ pub fn convert_const_for_result<'db>( const_: Const<'db>, ) -> crate::Const { let value: chalk_ir::ConstValue = match const_.kind() { - rustc_type_ir::ConstKind::Param(_) => unimplemented!(), rustc_type_ir::ConstKind::Infer(rustc_type_ir::InferConst::Var(var)) => { chalk_ir::ConstValue::InferenceVar(chalk_ir::InferenceVar::from(var.as_u32())) } rustc_type_ir::ConstKind::Infer(rustc_type_ir::InferConst::Fresh(fresh)) => { panic!("Vars should not be freshened.") } + rustc_type_ir::ConstKind::Param(param) => { + let placeholder = to_placeholder_idx(interner.db, param.id.into(), param.index); + chalk_ir::ConstValue::Placeholder(placeholder) + } rustc_type_ir::ConstKind::Bound(debruijn_index, var) => { chalk_ir::ConstValue::BoundVar(chalk_ir::BoundVar::new( chalk_ir::DebruijnIndex::new(debruijn_index.as_u32()), @@ -1341,10 +1358,12 @@ pub fn convert_const_for_result<'db>( )) } rustc_type_ir::ConstKind::Placeholder(placeholder_const) => { - chalk_ir::ConstValue::Placeholder(chalk_ir::PlaceholderIndex { - ui: chalk_ir::UniverseIndex { counter: placeholder_const.universe.as_usize() }, - idx: placeholder_const.bound.as_usize(), - }) + unimplemented!( + "A `rustc_type_ir::ConstKind::Placeholder` doesn't have a direct \ + correspondence in Chalk, as it represents a universally instantiated `Bound`.\n\ + It therefore feels safer to leave it panicking, but if you hit this panic \ + feel free to do the same as in `rustc_type_ir::ConstKind::Bound` here." + ) } rustc_type_ir::ConstKind::Unevaluated(unevaluated_const) => { let id = match unevaluated_const.def { @@ -1381,36 +1400,34 @@ pub fn convert_const_for_result<'db>( chalk_ir::ConstData { ty: crate::TyKind::Error.intern(Interner), value }.intern(Interner) } -pub fn convert_region_for_result<'db>(region: Region<'db>) -> crate::Lifetime { - match region.kind() { - rustc_type_ir::RegionKind::ReEarlyParam(early) => unimplemented!(), - rustc_type_ir::RegionKind::ReBound(db, bound) => chalk_ir::Lifetime::new( - Interner, +pub fn convert_region_for_result<'db>( + interner: DbInterner<'db>, + region: Region<'db>, +) -> crate::Lifetime { + let lifetime = match region.kind() { + rustc_type_ir::RegionKind::ReEarlyParam(early) => { + let placeholder = lt_to_placeholder_idx(interner.db, early.id, early.index); + chalk_ir::LifetimeData::Placeholder(placeholder) + } + rustc_type_ir::RegionKind::ReBound(db, bound) => { chalk_ir::LifetimeData::BoundVar(chalk_ir::BoundVar::new( chalk_ir::DebruijnIndex::new(db.as_u32()), bound.var.as_usize(), - )), + )) + } + rustc_type_ir::RegionKind::RePlaceholder(placeholder) => unimplemented!( + "A `rustc_type_ir::RegionKind::RePlaceholder` doesn't have a direct \ + correspondence in Chalk, as it represents a universally instantiated `Bound`.\n\ + It therefore feels safer to leave it panicking, but if you hit this panic \ + feel free to do the same as in `rustc_type_ir::RegionKind::ReBound` here." ), rustc_type_ir::RegionKind::ReLateParam(_) => unimplemented!(), - rustc_type_ir::RegionKind::ReStatic => { - chalk_ir::Lifetime::new(Interner, chalk_ir::LifetimeData::Static) + rustc_type_ir::RegionKind::ReStatic => chalk_ir::LifetimeData::Static, + rustc_type_ir::RegionKind::ReVar(vid) => { + chalk_ir::LifetimeData::InferenceVar(chalk_ir::InferenceVar::from(vid.as_u32())) } - rustc_type_ir::RegionKind::ReVar(vid) => chalk_ir::Lifetime::new( - Interner, - chalk_ir::LifetimeData::InferenceVar(chalk_ir::InferenceVar::from(vid.as_u32())), - ), - rustc_type_ir::RegionKind::RePlaceholder(placeholder) => chalk_ir::Lifetime::new( - Interner, - chalk_ir::LifetimeData::Placeholder(chalk_ir::PlaceholderIndex { - idx: placeholder.bound.var.as_usize(), - ui: chalk_ir::UniverseIndex { counter: placeholder.universe.as_usize() }, - }), - ), - rustc_type_ir::RegionKind::ReErased => { - chalk_ir::Lifetime::new(Interner, chalk_ir::LifetimeData::Erased) - } - rustc_type_ir::RegionKind::ReError(_) => { - chalk_ir::Lifetime::new(Interner, chalk_ir::LifetimeData::Error) - } - } + rustc_type_ir::RegionKind::ReErased => chalk_ir::LifetimeData::Erased, + rustc_type_ir::RegionKind::ReError(_) => chalk_ir::LifetimeData::Error, + }; + chalk_ir::Lifetime::new(Interner, lifetime) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs index c59cdac5f99f..d6214d991560 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/region.rs @@ -1,5 +1,6 @@ //! Things related to regions. +use hir_def::LifetimeParamId; use intern::{Interned, Symbol}; use rustc_type_ir::{ BoundVar, Flags, INNERMOST, RegionVid, TypeFlags, TypeFoldable, TypeVisitable, VisitorResult, @@ -110,6 +111,8 @@ pub type PlaceholderRegion = Placeholder; #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct EarlyParamRegion { + // FIXME: See `ParamTy`. + pub id: LifetimeParamId, pub index: u32, } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index 5ffae981a609..92f1076e75ec 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -1,5 +1,6 @@ //! Things related to tys in the next-trait-solver. +use hir_def::{GenericDefId, TypeOrConstParamId, TypeParamId}; use intern::{Interned, Symbol, sym}; use rustc_abi::{Float, Integer, Size}; use rustc_ast_ir::{Mutability, try_visit, visit::VisitorResult}; @@ -64,8 +65,8 @@ impl<'db> Ty<'db> { .unwrap() } - pub fn new_param(interner: DbInterner<'db>, index: u32, name: Symbol) -> Self { - Ty::new(interner, TyKind::Param(ParamTy { index })) + pub fn new_param(interner: DbInterner<'db>, id: TypeParamId, index: u32, name: Symbol) -> Self { + Ty::new(interner, TyKind::Param(ParamTy { id, index })) } pub fn new_placeholder(interner: DbInterner<'db>, placeholder: PlaceholderTy) -> Self { @@ -847,12 +848,16 @@ pub type PlaceholderTy = Placeholder; #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct ParamTy { + // FIXME: I'm not pleased with this. Ideally a `Param` should only know its index - the defining item + // is known from the `EarlyBinder`. This should also be beneficial for memory usage. But code currently + // assumes it can get the definition from `Param` alone - so that's what we got. + pub id: TypeParamId, pub index: u32, } impl ParamTy { pub fn to_ty<'db>(self, interner: DbInterner<'db>) -> Ty<'db> { - Ty::new_param(interner, self.index, sym::MISSING_NAME.clone()) + Ty::new_param(interner, self.id, self.index, sym::MISSING_NAME.clone()) } } @@ -865,6 +870,7 @@ impl std::fmt::Debug for ParamTy { #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct BoundTy { pub var: BoundVar, + // FIXME: This is for diagnostics in rustc, do we really need it? pub kind: BoundTyKind, } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs index d8a058533f76..a1ebff04bbd4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs @@ -344,7 +344,7 @@ impl Context<'_> { // Chalk has no params, so use placeholders for now? TyKind::Placeholder(index) => { - let idx = crate::from_placeholder_idx(self.db, *index); + let idx = crate::from_placeholder_idx(self.db, *index).0; let index = self.generics.type_or_const_param_idx(idx).unwrap(); self.constrain(index, variance); } @@ -445,7 +445,7 @@ impl Context<'_> { ); match region.data(Interner) { LifetimeData::Placeholder(index) => { - let idx = crate::lt_from_placeholder_idx(self.db, *index); + let idx = crate::lt_from_placeholder_idx(self.db, *index).0; let inferred = self.generics.lifetime_idx(idx).unwrap(); self.constrain(inferred, variance); } diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index b9c5131ffbb5..833a9ef03065 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -474,8 +474,8 @@ impl HirDisplay for TypeParam { let param_data = ¶ms[self.id.local_id()]; let substs = TyBuilder::placeholder_subst(f.db, self.id.parent()); let krate = self.id.parent().krate(f.db).id; - let ty = - TyKind::Placeholder(hir_ty::to_placeholder_idx(f.db, self.id.into())).intern(Interner); + let ty = TyKind::Placeholder(hir_ty::to_placeholder_idx_no_index(f.db, self.id.into())) + .intern(Interner); let predicates = f.db.generic_predicates(self.id.parent()); let predicates = predicates .iter() @@ -528,8 +528,11 @@ impl HirDisplay for TypeParam { f, ":", Either::Left( - &hir_ty::TyKind::Placeholder(hir_ty::to_placeholder_idx(f.db, self.id.into())) - .intern(Interner), + &hir_ty::TyKind::Placeholder(hir_ty::to_placeholder_idx_no_index( + f.db, + self.id.into(), + )) + .intern(Interner), ), &predicates, default_sized, diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index f03f542e5bce..46d3a2bcd67c 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -4239,8 +4239,8 @@ impl TypeParam { pub fn ty(self, db: &dyn HirDatabase) -> Type<'_> { let resolver = self.id.parent().resolver(db); - let ty = - TyKind::Placeholder(hir_ty::to_placeholder_idx(db, self.id.into())).intern(Interner); + let ty = TyKind::Placeholder(hir_ty::to_placeholder_idx_no_index(db, self.id.into())) + .intern(Interner); Type::new_with_resolver_inner(db, &resolver, ty) } @@ -5933,7 +5933,7 @@ impl<'db> Type<'db> { pub fn as_type_param(&self, db: &'db dyn HirDatabase) -> Option { match self.ty.kind(Interner) { TyKind::Placeholder(p) => Some(TypeParam { - id: TypeParamId::from_unchecked(hir_ty::from_placeholder_idx(db, *p)), + id: TypeParamId::from_unchecked(hir_ty::from_placeholder_idx(db, *p).0), }), _ => None, } From dd75ad436927ac30bf204953d24b07bff09225bd Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 2 Sep 2025 08:15:17 +0300 Subject: [PATCH 166/682] Deduplicate methods in completion by function ID and not by name Because duplicates can be found with traits. Worse, the inherent methods could be private, and we'll discover that only later. But even if they're not they're different methods, and its seems worthy to present them all to the user. --- .../ide-completion/src/completions/dot.rs | 15 ++++--- .../ide-completion/src/tests/expression.rs | 40 +++++++++++++++++++ 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index 129964f8387a..b9ef68cc2d2a 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -2,7 +2,7 @@ use std::ops::ControlFlow; -use hir::{Complete, HasContainer, ItemContainer, MethodCandidateCallback, Name}; +use hir::{Complete, Function, HasContainer, ItemContainer, MethodCandidateCallback}; use ide_db::FxHashSet; use syntax::SmolStr; @@ -237,7 +237,10 @@ fn complete_methods( struct Callback<'a, F> { ctx: &'a CompletionContext<'a>, f: F, - seen_methods: FxHashSet, + // We deliberately deduplicate by function ID and not name, because while inherent methods cannot be + // duplicated, trait methods can. And it is still useful to show all of them (even when there + // is also an inherent method, especially considering that it may be private, and filtered later). + seen_methods: FxHashSet, } impl MethodCandidateCallback for Callback<'_, F> @@ -247,9 +250,7 @@ fn complete_methods( // We don't want to exclude inherent trait methods - that is, methods of traits available from // `where` clauses or `dyn Trait`. fn on_inherent_method(&mut self, func: hir::Function) -> ControlFlow<()> { - if func.self_param(self.ctx.db).is_some() - && self.seen_methods.insert(func.name(self.ctx.db)) - { + if func.self_param(self.ctx.db).is_some() && self.seen_methods.insert(func) { (self.f)(func); } ControlFlow::Continue(()) @@ -265,9 +266,7 @@ fn complete_methods( return ControlFlow::Continue(()); } - if func.self_param(self.ctx.db).is_some() - && self.seen_methods.insert(func.name(self.ctx.db)) - { + if func.self_param(self.ctx.db).is_some() && self.seen_methods.insert(func) { (self.f)(func); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index 7a0d00444129..af6ef34761ed 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -2632,3 +2632,43 @@ fn let_in_condition() { fn let_in_let_chain() { check_edit("let", r#"fn f() { if true && $0 {} }"#, r#"fn f() { if true && let $1 = $0 {} }"#); } + +#[test] +fn private_inherent_and_public_trait() { + check( + r#" +struct Foo; + +mod private { + impl super::Foo { + fn method(&self) {} + } +} + +trait Trait { + fn method(&self) {} +} +impl Trait for Foo {} + +fn main() { + Foo.$0 +} + "#, + expect![[r#" + me method() (as Trait) fn(&self) + sn box Box::new(expr) + sn call function(expr) + sn const const {} + sn dbg dbg!(expr) + sn dbgr dbg!(&expr) + sn deref *expr + sn let let + sn letm let mut + sn match match expr {} + sn ref &expr + sn refm &mut expr + sn return return expr + sn unsafe unsafe {} + "#]], + ); +} From 6f0ebdb71a1d4f0fced5b10f1ddbbd227042bd5e Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 2 Sep 2025 11:35:25 +0300 Subject: [PATCH 167/682] Migrate more things in the new solver to specific `DefId`s --- compiler/rustc_middle/src/ty/context.rs | 28 +++++++- .../rustc_next_trait_solver/src/coherence.rs | 16 ++++- .../rustc_next_trait_solver/src/delegate.rs | 2 +- .../src/solve/assembly/mod.rs | 2 +- .../src/solve/assembly/structural_traits.rs | 6 +- .../src/solve/effect_goals.rs | 10 +-- .../src/solve/eval_ctxt/mod.rs | 2 +- .../src/solve/normalizes_to/mod.rs | 21 +++--- .../src/solve/trait_goals.rs | 6 +- compiler/rustc_type_ir/src/fast_reject.rs | 14 ++-- compiler/rustc_type_ir/src/inherent.rs | 30 +++++--- compiler/rustc_type_ir/src/interner.rs | 70 +++++++++++-------- compiler/rustc_type_ir/src/lang_items.rs | 5 ++ compiler/rustc_type_ir/src/relate.rs | 4 +- compiler/rustc_type_ir/src/solve/mod.rs | 2 +- compiler/rustc_type_ir/src/ty_kind.rs | 12 ++-- compiler/rustc_type_ir/src/ty_kind/closure.rs | 4 +- 17 files changed, 154 insertions(+), 80 deletions(-) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 5dbbc7297ab6..07cfc87250bf 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -52,7 +52,7 @@ use rustc_session::{Limit, Session}; use rustc_span::def_id::{CRATE_DEF_ID, DefPathHash, StableCrateId}; use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; use rustc_type_ir::TyKind::*; -use rustc_type_ir::lang_items::{SolverLangItem, SolverTraitLangItem}; +use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem}; pub use rustc_type_ir::lift::Lift; use rustc_type_ir::{ CollectAndApply, Interner, TypeFlags, TypeFoldable, WithCachedTypeInfo, elaborate, search_graph, @@ -94,6 +94,13 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type DefId = DefId; type LocalDefId = LocalDefId; type TraitId = DefId; + type ForeignId = DefId; + type FunctionId = DefId; + type ClosureId = DefId; + type CoroutineClosureId = DefId; + type CoroutineId = DefId; + type AdtId = DefId; + type ImplId = DefId; type Span = Span; type GenericArgs = ty::GenericArgsRef<'tcx>; @@ -492,6 +499,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.require_lang_item(solver_trait_lang_item_to_lang_item(lang_item), DUMMY_SP) } + fn require_adt_lang_item(self, lang_item: SolverAdtLangItem) -> DefId { + self.require_lang_item(solver_adt_lang_item_to_lang_item(lang_item), DUMMY_SP) + } + fn is_lang_item(self, def_id: DefId, lang_item: SolverLangItem) -> bool { self.is_lang_item(def_id, solver_lang_item_to_lang_item(lang_item)) } @@ -500,6 +511,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.is_lang_item(def_id, solver_trait_lang_item_to_lang_item(lang_item)) } + fn is_adt_lang_item(self, def_id: DefId, lang_item: SolverAdtLangItem) -> bool { + self.is_lang_item(def_id, solver_adt_lang_item_to_lang_item(lang_item)) + } + fn is_default_trait(self, def_id: DefId) -> bool { self.is_default_trait(def_id) } @@ -512,6 +527,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { lang_item_to_solver_trait_lang_item(self.lang_items().from_def_id(def_id)?) } + fn as_adt_lang_item(self, def_id: DefId) -> Option { + lang_item_to_solver_adt_lang_item(self.lang_items().from_def_id(def_id)?) + } + fn associated_type_def_ids(self, def_id: DefId) -> impl IntoIterator { self.associated_items(def_id) .in_definition_order() @@ -772,6 +791,13 @@ bidirectional_lang_item_map! { DynMetadata, FutureOutput, Metadata, +// tidy-alphabetical-end +} + +bidirectional_lang_item_map! { + SolverAdtLangItem, lang_item_to_solver_adt_lang_item, solver_adt_lang_item_to_lang_item; + +// tidy-alphabetical-start Option, Poll, // tidy-alphabetical-end diff --git a/compiler/rustc_next_trait_solver/src/coherence.rs b/compiler/rustc_next_trait_solver/src/coherence.rs index 79219b34e290..b949deb11926 100644 --- a/compiler/rustc_next_trait_solver/src/coherence.rs +++ b/compiler/rustc_next_trait_solver/src/coherence.rs @@ -435,7 +435,21 @@ where } } ty::Error(_) => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)), - ty::Closure(did, ..) | ty::CoroutineClosure(did, ..) | ty::Coroutine(did, ..) => { + ty::Closure(did, ..) => { + if self.def_id_is_local(did) { + ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) + } else { + self.found_non_local_ty(ty) + } + } + ty::CoroutineClosure(did, ..) => { + if self.def_id_is_local(did) { + ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) + } else { + self.found_non_local_ty(ty) + } + } + ty::Coroutine(did, ..) => { if self.def_id_is_local(did) { ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) } else { diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs index 7b932010d498..a7ecbfcb7c4f 100644 --- a/compiler/rustc_next_trait_solver/src/delegate.rs +++ b/compiler/rustc_next_trait_solver/src/delegate.rs @@ -76,7 +76,7 @@ pub trait SolverDelegate: Deref + Sized { &self, goal_trait_ref: ty::TraitRef, trait_assoc_def_id: ::DefId, - impl_def_id: ::DefId, + impl_def_id: ::ImplId, ) -> Result< Option<::DefId>, ::ErrorGuaranteed, diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index be7e4dd4cdaf..e3cf0330b14a 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -186,7 +186,7 @@ where fn consider_impl_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - impl_def_id: I::DefId, + impl_def_id: I::ImplId, ) -> Result, NoSolution>; /// If the predicate contained an error, we want to avoid emitting unnecessary trait diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index 7c5940828daa..f6eab286ba70 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -605,7 +605,7 @@ fn coroutine_closure_to_certain_coroutine( cx: I, goal_kind: ty::ClosureKind, goal_region: I::Region, - def_id: I::DefId, + def_id: I::CoroutineClosureId, args: ty::CoroutineClosureArgs, sig: ty::CoroutineClosureSignature, ) -> I::Ty { @@ -629,7 +629,7 @@ fn coroutine_closure_to_ambiguous_coroutine( cx: I, goal_kind: ty::ClosureKind, goal_region: I::Region, - def_id: I::DefId, + def_id: I::CoroutineClosureId, args: ty::CoroutineClosureArgs, sig: ty::CoroutineClosureSignature, ) -> I::Ty { @@ -664,7 +664,7 @@ fn coroutine_closure_to_ambiguous_coroutine( pub(in crate::solve) fn extract_fn_def_from_const_callable( cx: I, self_ty: I::Ty, -) -> Result<(ty::Binder, I::DefId, I::GenericArgs), NoSolution> { +) -> Result<(ty::Binder, I::FunctionId, I::GenericArgs), NoSolution> { match self_ty.kind() { ty::FnDef(def_id, args) => { let sig = cx.fn_sig(def_id); diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index 229345065b19..6646857a1368 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -123,7 +123,7 @@ where fn consider_impl_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal, - impl_def_id: I::DefId, + impl_def_id: I::ImplId, ) -> Result, NoSolution> { let cx = ecx.cx(); @@ -152,20 +152,20 @@ where } ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| { - let impl_args = ecx.fresh_args_for_item(impl_def_id); + let impl_args = ecx.fresh_args_for_item(impl_def_id.into()); ecx.record_impl_args(impl_args); let impl_trait_ref = impl_trait_ref.instantiate(cx, impl_args); ecx.eq(goal.param_env, goal.predicate.trait_ref, impl_trait_ref)?; let where_clause_bounds = cx - .predicates_of(impl_def_id) + .predicates_of(impl_def_id.into()) .iter_instantiated(cx, impl_args) .map(|pred| goal.with(cx, pred)); ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds); // For this impl to be `const`, we need to check its `[const]` bounds too. let const_conditions = cx - .const_conditions(impl_def_id) + .const_conditions(impl_def_id.into()) .iter_instantiated(cx, impl_args) .map(|bound_trait_ref| { goal.with( @@ -240,7 +240,7 @@ where ty::TraitRef::new(cx, cx.require_trait_lang_item(SolverTraitLangItem::Sized), [output]) }); let requirements = cx - .const_conditions(def_id) + .const_conditions(def_id.into()) .iter_instantiated(cx, args) .map(|trait_ref| { ( diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 4f87902e46e9..606fbcab4b3b 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -1122,7 +1122,7 @@ where &self, goal_trait_ref: ty::TraitRef, trait_assoc_def_id: I::DefId, - impl_def_id: I::DefId, + impl_def_id: I::ImplId, ) -> Result, I::ErrorGuaranteed> { self.delegate.fetch_eligible_assoc_item(goal_trait_ref, trait_assoc_def_id, impl_def_id) } diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index cfdf2007391a..6e6be829f521 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -5,7 +5,7 @@ mod opaque_types; use rustc_type_ir::fast_reject::DeepRejectCtxt; use rustc_type_ir::inherent::*; -use rustc_type_ir::lang_items::{SolverLangItem, SolverTraitLangItem}; +use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem}; use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::{self as ty, Interner, NormalizesTo, PredicateKind, Upcast as _}; use tracing::instrument; @@ -193,7 +193,7 @@ where fn consider_impl_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal>, - impl_def_id: I::DefId, + impl_def_id: I::ImplId, ) -> Result, NoSolution> { let cx = ecx.cx(); @@ -217,13 +217,13 @@ where }; ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| { - let impl_args = ecx.fresh_args_for_item(impl_def_id); + let impl_args = ecx.fresh_args_for_item(impl_def_id.into()); let impl_trait_ref = impl_trait_ref.instantiate(cx, impl_args); ecx.eq(goal.param_env, goal_trait_ref, impl_trait_ref)?; let where_clause_bounds = cx - .predicates_of(impl_def_id) + .predicates_of(impl_def_id.into()) .iter_instantiated(cx, impl_args) .map(|pred| goal.with(cx, pred)); ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds); @@ -824,10 +824,10 @@ where // coroutine yield ty `Poll>`. let wrapped_expected_ty = Ty::new_adt( cx, - cx.adt_def(cx.require_lang_item(SolverLangItem::Poll)), + cx.adt_def(cx.require_adt_lang_item(SolverAdtLangItem::Poll)), cx.mk_args(&[Ty::new_adt( cx, - cx.adt_def(cx.require_lang_item(SolverLangItem::Option)), + cx.adt_def(cx.require_adt_lang_item(SolverAdtLangItem::Option)), cx.mk_args(&[expected_ty.into()]), ) .into()]), @@ -979,7 +979,7 @@ where fn translate_args( &mut self, goal: Goal>, - impl_def_id: I::DefId, + impl_def_id: I::ImplId, impl_args: I::GenericArgs, impl_trait_ref: rustc_type_ir::TraitRef, target_container_def_id: I::DefId, @@ -988,14 +988,15 @@ where Ok(if target_container_def_id == impl_trait_ref.def_id.into() { // Default value from the trait definition. No need to rebase. goal.predicate.alias.args - } else if target_container_def_id == impl_def_id { + } else if target_container_def_id == impl_def_id.into() { // Same impl, no need to fully translate, just a rebase from // the trait is sufficient. goal.predicate.alias.args.rebase_onto(cx, impl_trait_ref.def_id.into(), impl_args) } else { let target_args = self.fresh_args_for_item(target_container_def_id); - let target_trait_ref = - cx.impl_trait_ref(target_container_def_id).instantiate(cx, target_args); + let target_trait_ref = cx + .impl_trait_ref(target_container_def_id.try_into().unwrap()) + .instantiate(cx, target_args); // Relate source impl to target impl by equating trait refs. self.eq(goal.param_env, impl_trait_ref, target_trait_ref)?; // Also add predicates since they may be needed to constrain the diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index cdcfebf29090..3e720a47f32e 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -54,7 +54,7 @@ where fn consider_impl_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal>, - impl_def_id: I::DefId, + impl_def_id: I::ImplId, ) -> Result, NoSolution> { let cx = ecx.cx(); @@ -91,13 +91,13 @@ where }; ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| { - let impl_args = ecx.fresh_args_for_item(impl_def_id); + let impl_args = ecx.fresh_args_for_item(impl_def_id.into()); ecx.record_impl_args(impl_args); let impl_trait_ref = impl_trait_ref.instantiate(cx, impl_args); ecx.eq(goal.param_env, goal.predicate.trait_ref, impl_trait_ref)?; let where_clause_bounds = cx - .predicates_of(impl_def_id) + .predicates_of(impl_def_id.into()) .iter_instantiated(cx, impl_args) .map(|pred| goal.with(cx, pred)); ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds); diff --git a/compiler/rustc_type_ir/src/fast_reject.rs b/compiler/rustc_type_ir/src/fast_reject.rs index 5a05630e1bd1..ed6416a7f55f 100644 --- a/compiler/rustc_type_ir/src/fast_reject.rs +++ b/compiler/rustc_type_ir/src/fast_reject.rs @@ -122,7 +122,7 @@ pub fn simplify_type( ty::Int(int_type) => Some(SimplifiedType::Int(int_type)), ty::Uint(uint_type) => Some(SimplifiedType::Uint(uint_type)), ty::Float(float_type) => Some(SimplifiedType::Float(float_type)), - ty::Adt(def, _) => Some(SimplifiedType::Adt(def.def_id())), + ty::Adt(def, _) => Some(SimplifiedType::Adt(def.def_id().into())), ty::Str => Some(SimplifiedType::Str), ty::Array(..) => Some(SimplifiedType::Array), ty::Slice(..) => Some(SimplifiedType::Slice), @@ -135,11 +135,11 @@ pub fn simplify_type( _ => Some(SimplifiedType::MarkerTraitObject), }, ty::Ref(_, _, mutbl) => Some(SimplifiedType::Ref(mutbl)), - ty::FnDef(def_id, _) | ty::Closure(def_id, _) | ty::CoroutineClosure(def_id, _) => { - Some(SimplifiedType::Closure(def_id)) - } - ty::Coroutine(def_id, _) => Some(SimplifiedType::Coroutine(def_id)), - ty::CoroutineWitness(def_id, _) => Some(SimplifiedType::CoroutineWitness(def_id)), + ty::FnDef(def_id, _) => Some(SimplifiedType::Closure(def_id.into())), + ty::Closure(def_id, _) => Some(SimplifiedType::Closure(def_id.into())), + ty::CoroutineClosure(def_id, _) => Some(SimplifiedType::Closure(def_id.into())), + ty::Coroutine(def_id, _) => Some(SimplifiedType::Coroutine(def_id.into())), + ty::CoroutineWitness(def_id, _) => Some(SimplifiedType::CoroutineWitness(def_id.into())), ty::Never => Some(SimplifiedType::Never), ty::Tuple(tys) => Some(SimplifiedType::Tuple(tys.len())), ty::FnPtr(sig_tys, _hdr) => { @@ -161,7 +161,7 @@ pub fn simplify_type( } TreatParams::AsRigid | TreatParams::InstantiateWithInfer => None, }, - ty::Foreign(def_id) => Some(SimplifiedType::Foreign(def_id)), + ty::Foreign(def_id) => Some(SimplifiedType::Foreign(def_id.into())), ty::Error(_) => Some(SimplifiedType::Error), ty::Bound(..) | ty::Infer(_) => None, } diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 64063b1a3650..ecfc05a6e20e 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -74,7 +74,7 @@ pub trait Ty>: fn new_adt(interner: I, adt_def: I::AdtDef, args: I::GenericArgs) -> Self; - fn new_foreign(interner: I, def_id: I::DefId) -> Self; + fn new_foreign(interner: I, def_id: I::ForeignId) -> Self; fn new_dynamic( interner: I, @@ -83,17 +83,21 @@ pub trait Ty>: kind: ty::DynKind, ) -> Self; - fn new_coroutine(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self; + fn new_coroutine(interner: I, def_id: I::CoroutineId, args: I::GenericArgs) -> Self; - fn new_coroutine_closure(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self; + fn new_coroutine_closure( + interner: I, + def_id: I::CoroutineClosureId, + args: I::GenericArgs, + ) -> Self; - fn new_closure(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self; + fn new_closure(interner: I, def_id: I::ClosureId, args: I::GenericArgs) -> Self; - fn new_coroutine_witness(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self; + fn new_coroutine_witness(interner: I, def_id: I::CoroutineId, args: I::GenericArgs) -> Self; fn new_coroutine_witness_for_coroutine( interner: I, - def_id: I::DefId, + def_id: I::CoroutineId, coroutine_args: I::GenericArgs, ) -> Self; @@ -112,7 +116,7 @@ pub trait Ty>: It: Iterator, T: CollectAndApply; - fn new_fn_def(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self; + fn new_fn_def(interner: I, def_id: I::FunctionId, args: I::GenericArgs) -> Self; fn new_fn_ptr(interner: I, sig: ty::Binder>) -> Self; @@ -599,7 +603,7 @@ pub trait ParamLike: Copy + Debug + Hash + Eq { } pub trait AdtDef: Copy + Debug + Hash + Eq { - fn def_id(self) -> I::DefId; + fn def_id(self) -> I::AdtId; fn is_struct(self) -> bool; @@ -646,6 +650,16 @@ pub trait DefId: Copy + Debug + Hash + Eq + TypeFoldable { fn as_local(self) -> Option; } +pub trait SpecificDefId: + DefId + Into + TryFrom +{ +} + +impl + Into + TryFrom> + SpecificDefId for T +{ +} + pub trait BoundExistentialPredicates: Copy + Debug + Hash + Eq + Relate + SliceLike>> { diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 21fe7879b298..03a8f5e74b4b 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -8,7 +8,7 @@ use rustc_index::bit_set::DenseBitSet; use crate::fold::TypeFoldable; use crate::inherent::*; use crate::ir_print::IrPrint; -use crate::lang_items::{SolverLangItem, SolverTraitLangItem}; +use crate::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem}; use crate::relate::Relate; use crate::solve::{CanonicalInput, ExternalConstraintsData, PredefinedOpaquesData, QueryResult}; use crate::visit::{Flags, TypeVisitable}; @@ -38,13 +38,20 @@ pub trait Interner: type DefId: DefId; type LocalDefId: Copy + Debug + Hash + Eq + Into + TypeFoldable; - /// A `DefId` of a trait. - /// - /// In rustc this is just a `DefId`, but rust-analyzer uses different types for different items. - /// - /// Note: The `TryFrom` always succeeds (in rustc), so don't use it to check if some `DefId` - /// is a trait! - type TraitId: DefId + Into + TryFrom; + // Various more specific `DefId`s. + // + // rustc just defines them all to be `DefId`, but rust-analyzer uses different types so this is convenient for it. + // + // Note: The `TryFrom` always succeeds (in rustc), so don't use it to check if some `DefId` + // is of some specific type! + type TraitId: SpecificDefId; + type ForeignId: SpecificDefId; + type FunctionId: SpecificDefId; + type ClosureId: SpecificDefId; + type CoroutineClosureId: SpecificDefId; + type CoroutineId: SpecificDefId; + type AdtId: SpecificDefId; + type ImplId: SpecificDefId; type Span: Span; type GenericArgs: GenericArgs; @@ -196,7 +203,7 @@ pub trait Interner: -> ty::EarlyBinder; type AdtDef: AdtDef; - fn adt_def(self, adt_def_id: Self::DefId) -> Self::AdtDef; + fn adt_def(self, adt_def_id: Self::AdtId) -> Self::AdtDef; fn alias_ty_kind(self, alias: ty::AliasTy) -> ty::AliasTyKind; @@ -237,17 +244,17 @@ pub trait Interner: fn coroutine_hidden_types( self, - def_id: Self::DefId, + def_id: Self::CoroutineId, ) -> ty::EarlyBinder>>; fn fn_sig( self, - def_id: Self::DefId, + def_id: Self::FunctionId, ) -> ty::EarlyBinder>>; - fn coroutine_movability(self, def_id: Self::DefId) -> Movability; + fn coroutine_movability(self, def_id: Self::CoroutineId) -> Movability; - fn coroutine_for_closure(self, def_id: Self::DefId) -> Self::DefId; + fn coroutine_for_closure(self, def_id: Self::CoroutineClosureId) -> Self::CoroutineId; fn generics_require_sized_self(self, def_id: Self::DefId) -> bool; @@ -290,11 +297,11 @@ pub trait Interner: /// and filtering them to the outlives predicates. This is purely for performance. fn impl_super_outlives( self, - impl_def_id: Self::DefId, + impl_def_id: Self::ImplId, ) -> ty::EarlyBinder>; - fn impl_is_const(self, def_id: Self::DefId) -> bool; - fn fn_is_const(self, def_id: Self::DefId) -> bool; + fn impl_is_const(self, def_id: Self::ImplId) -> bool; + fn fn_is_const(self, def_id: Self::FunctionId) -> bool; fn alias_has_const_conditions(self, def_id: Self::DefId) -> bool; fn const_conditions( self, @@ -305,42 +312,49 @@ pub trait Interner: def_id: Self::DefId, ) -> ty::EarlyBinder>>>; - fn impl_self_is_guaranteed_unsized(self, def_id: Self::DefId) -> bool; + fn impl_self_is_guaranteed_unsized(self, def_id: Self::ImplId) -> bool; - fn has_target_features(self, def_id: Self::DefId) -> bool; + fn has_target_features(self, def_id: Self::FunctionId) -> bool; fn require_lang_item(self, lang_item: SolverLangItem) -> Self::DefId; fn require_trait_lang_item(self, lang_item: SolverTraitLangItem) -> Self::TraitId; + fn require_adt_lang_item(self, lang_item: SolverAdtLangItem) -> Self::AdtId; + fn is_lang_item(self, def_id: Self::DefId, lang_item: SolverLangItem) -> bool; fn is_trait_lang_item(self, def_id: Self::TraitId, lang_item: SolverTraitLangItem) -> bool; + fn is_adt_lang_item(self, def_id: Self::AdtId, lang_item: SolverAdtLangItem) -> bool; + fn is_default_trait(self, def_id: Self::TraitId) -> bool; fn as_lang_item(self, def_id: Self::DefId) -> Option; fn as_trait_lang_item(self, def_id: Self::TraitId) -> Option; + fn as_adt_lang_item(self, def_id: Self::AdtId) -> Option; + fn associated_type_def_ids(self, def_id: Self::DefId) -> impl IntoIterator; fn for_each_relevant_impl( self, trait_def_id: Self::TraitId, self_ty: Self::Ty, - f: impl FnMut(Self::DefId), + f: impl FnMut(Self::ImplId), ); fn has_item_definition(self, def_id: Self::DefId) -> bool; - fn impl_specializes(self, impl_def_id: Self::DefId, victim_def_id: Self::DefId) -> bool; + fn impl_specializes(self, impl_def_id: Self::ImplId, victim_def_id: Self::ImplId) -> bool; - fn impl_is_default(self, impl_def_id: Self::DefId) -> bool; + fn impl_is_default(self, impl_def_id: Self::ImplId) -> bool; - fn impl_trait_ref(self, impl_def_id: Self::DefId) -> ty::EarlyBinder>; + fn impl_trait_ref(self, impl_def_id: Self::ImplId) + -> ty::EarlyBinder>; - fn impl_polarity(self, impl_def_id: Self::DefId) -> ty::ImplPolarity; + fn impl_polarity(self, impl_def_id: Self::ImplId) -> ty::ImplPolarity; fn trait_is_auto(self, trait_def_id: Self::TraitId) -> bool; @@ -361,13 +375,13 @@ pub trait Interner: fn delay_bug(self, msg: impl ToString) -> Self::ErrorGuaranteed; - fn is_general_coroutine(self, coroutine_def_id: Self::DefId) -> bool; - fn coroutine_is_async(self, coroutine_def_id: Self::DefId) -> bool; - fn coroutine_is_gen(self, coroutine_def_id: Self::DefId) -> bool; - fn coroutine_is_async_gen(self, coroutine_def_id: Self::DefId) -> bool; + fn is_general_coroutine(self, coroutine_def_id: Self::CoroutineId) -> bool; + fn coroutine_is_async(self, coroutine_def_id: Self::CoroutineId) -> bool; + fn coroutine_is_gen(self, coroutine_def_id: Self::CoroutineId) -> bool; + fn coroutine_is_async_gen(self, coroutine_def_id: Self::CoroutineId) -> bool; type UnsizingParams: Deref>; - fn unsizing_params_for_adt(self, adt_def_id: Self::DefId) -> Self::UnsizingParams; + fn unsizing_params_for_adt(self, adt_def_id: Self::AdtId) -> Self::UnsizingParams; fn anonymize_bound_vars>( self, diff --git a/compiler/rustc_type_ir/src/lang_items.rs b/compiler/rustc_type_ir/src/lang_items.rs index 31f8f5be588f..5f503d8b912e 100644 --- a/compiler/rustc_type_ir/src/lang_items.rs +++ b/compiler/rustc_type_ir/src/lang_items.rs @@ -11,6 +11,11 @@ pub enum SolverLangItem { DynMetadata, FutureOutput, Metadata, + // tidy-alphabetical-end +} + +pub enum SolverAdtLangItem { + // tidy-alphabetical-start Option, Poll, // tidy-alphabetical-end diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index 223230fde9e7..690a5f65e084 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -405,7 +405,7 @@ pub fn structurally_relate_tys>( Ok(if a_args.is_empty() { a } else { - let args = relation.relate_item_args(a_def.def_id(), a_args, b_args)?; + let args = relation.relate_item_args(a_def.def_id().into(), a_args, b_args)?; if args == a_args { a } else { Ty::new_adt(cx, a_def, args) } }) } @@ -524,7 +524,7 @@ pub fn structurally_relate_tys>( Ok(if a_args.is_empty() { a } else { - let args = relation.relate_item_args(a_def_id, a_args, b_args)?; + let args = relation.relate_item_args(a_def_id.into(), a_args, b_args)?; if args == a_args { a } else { Ty::new_fn_def(cx, a_def_id, args) } }) } diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index b6d362d77c44..b48a8f46ebef 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -136,7 +136,7 @@ pub enum CandidateSource { /// let y = x.clone(); /// } /// ``` - Impl(I::DefId), + Impl(I::ImplId), /// A builtin impl generated by the compiler. When adding a new special /// trait, try to use actual impls whenever possible. Builtin impls should /// only be used in cases where the impl cannot be manually be written. diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index bde506ffd938..225d85f79c3d 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -101,7 +101,7 @@ pub enum TyKind { Adt(I::AdtDef, I::GenericArgs), /// An unsized FFI type that is opaque to Rust. Written as `extern type T`. - Foreign(I::DefId), + Foreign(I::ForeignId), /// The pointee of a string slice. Written as `str`. Str, @@ -137,7 +137,7 @@ pub enum TyKind { /// fn foo() -> i32 { 1 } /// let bar = foo; // bar: fn() -> i32 {foo} /// ``` - FnDef(I::DefId, I::GenericArgs), + FnDef(I::FunctionId, I::GenericArgs), /// A pointer to a function. Written as `fn() -> i32`. /// @@ -172,21 +172,21 @@ pub enum TyKind { /// Closure args contain both the - potentially instantiated - generic parameters /// of its parent and some synthetic parameters. See the documentation for /// `ClosureArgs` for more details. - Closure(I::DefId, I::GenericArgs), + Closure(I::ClosureId, I::GenericArgs), /// The anonymous type of a closure. Used to represent the type of `async |a| a`. /// /// Coroutine-closure args contain both the - potentially instantiated - generic /// parameters of its parent and some synthetic parameters. See the documentation /// for `CoroutineClosureArgs` for more details. - CoroutineClosure(I::DefId, I::GenericArgs), + CoroutineClosure(I::CoroutineClosureId, I::GenericArgs), /// The anonymous type of a coroutine. Used to represent the type of /// `|a| yield a`. /// /// For more info about coroutine args, visit the documentation for /// `CoroutineArgs`. - Coroutine(I::DefId, I::GenericArgs), + Coroutine(I::CoroutineId, I::GenericArgs), /// A type representing the types stored inside a coroutine. /// This should only appear as part of the `CoroutineArgs`. @@ -211,7 +211,7 @@ pub enum TyKind { /// } /// # ; /// ``` - CoroutineWitness(I::DefId, I::GenericArgs), + CoroutineWitness(I::CoroutineId, I::GenericArgs), /// The never type `!`. Never, diff --git a/compiler/rustc_type_ir/src/ty_kind/closure.rs b/compiler/rustc_type_ir/src/ty_kind/closure.rs index a2e16d917a9e..3a6d1acfa8d9 100644 --- a/compiler/rustc_type_ir/src/ty_kind/closure.rs +++ b/compiler/rustc_type_ir/src/ty_kind/closure.rs @@ -391,7 +391,7 @@ impl CoroutineClosureSignature { cx: I, parent_args: I::GenericArgsSlice, coroutine_kind_ty: I::Ty, - coroutine_def_id: I::DefId, + coroutine_def_id: I::CoroutineId, tupled_upvars_ty: I::Ty, ) -> I::Ty { let coroutine_args = ty::CoroutineArgs::new( @@ -418,7 +418,7 @@ impl CoroutineClosureSignature { self, cx: I, parent_args: I::GenericArgsSlice, - coroutine_def_id: I::DefId, + coroutine_def_id: I::CoroutineId, goal_kind: ty::ClosureKind, env_region: I::Region, closure_tupled_upvars_ty: I::Ty, From 18fa6d917cb39758429d47015fc83e72445e2199 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 2 Sep 2025 10:48:42 +0200 Subject: [PATCH 168/682] Remove some llvm workarounds --- library/stdarch/crates/core_arch/src/x86/avx512f.rs | 8 ++------ library/stdarch/crates/core_arch/src/x86_64/avx512f.rs | 8 ++------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/x86/avx512f.rs b/library/stdarch/crates/core_arch/src/x86/avx512f.rs index 0803f2f81b21..52c6a11a43f0 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512f.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512f.rs @@ -49621,9 +49621,7 @@ mod tests { assert_eq_m512i(r, e); } - // FIXME(llvm): https://github.com/llvm/llvm-project/issues/154492 - //#[simd_test(enable = "avx512f")] - #[simd_test(enable = "avx512f,avx512vl")] + #[simd_test(enable = "avx512f")] unsafe fn test_mm512_mask_cvttps_epu32() { let a = _mm512_setr_ps( 0., -1.5, 2., -3.5, 4., -5.5, 6., -7.5, 8., 9.5, 10., 11.5, 12., 13.5, 14., 15.5, @@ -49636,9 +49634,7 @@ mod tests { assert_eq_m512i(r, e); } - // FIXME(llvm): https://github.com/llvm/llvm-project/issues/154492 - //#[simd_test(enable = "avx512f")] - #[simd_test(enable = "avx512f,avx512vl")] + #[simd_test(enable = "avx512f")] unsafe fn test_mm512_maskz_cvttps_epu32() { let a = _mm512_setr_ps( 0., -1.5, 2., -3.5, 4., -5.5, 6., -7.5, 8., 9.5, 10., 11.5, 12., 13.5, 14., 15.5, diff --git a/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs b/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs index a8dd340bd660..934c9e2812c4 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs @@ -5311,9 +5311,7 @@ mod tests { assert_eq_m256i(r, e); } - // FIXME(llvm): https://github.com/llvm/llvm-project/issues/154492 - //#[simd_test(enable = "avx512f")] - #[simd_test(enable = "avx512f,avx512vl")] + #[simd_test(enable = "avx512f")] unsafe fn test_mm512_mask_cvttpd_epu32() { let a = _mm512_setr_pd(0., -1.5, 2., -3.5, 4., -5.5, 6., -7.5); let src = _mm256_set1_epi32(0); @@ -5324,9 +5322,7 @@ mod tests { assert_eq_m256i(r, e); } - // FIXME(llvm): https://github.com/llvm/llvm-project/issues/154492 - //#[simd_test(enable = "avx512f")] - #[simd_test(enable = "avx512f,avx512vl")] + #[simd_test(enable = "avx512f")] unsafe fn test_mm512_maskz_cvttpd_epu32() { let a = _mm512_setr_pd(0., -1.5, 2., -3.5, 4., -5.5, 6., -7.5); let r = _mm512_maskz_cvttpd_epu32(0, a); From bb3598e481a752b85872fcbea9dab805907c2d81 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 2 Sep 2025 10:52:49 +0200 Subject: [PATCH 169/682] use `qemu-user` instead of `qemu-user-static` for loongarch CI --- .../ci/docker/loongarch64-unknown-linux-gnu/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/stdarch/ci/docker/loongarch64-unknown-linux-gnu/Dockerfile b/library/stdarch/ci/docker/loongarch64-unknown-linux-gnu/Dockerfile index b5c6874ca52e..e803dae1006a 100644 --- a/library/stdarch/ci/docker/loongarch64-unknown-linux-gnu/Dockerfile +++ b/library/stdarch/ci/docker/loongarch64-unknown-linux-gnu/Dockerfile @@ -2,11 +2,11 @@ FROM ubuntu:25.10 RUN apt-get update && \ apt-get install -y --no-install-recommends \ - gcc libc6-dev qemu-user-static ca-certificates \ + gcc libc6-dev qemu-user ca-certificates \ gcc-loongarch64-linux-gnu libc6-dev-loong64-cross ENV CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNU_LINKER=loongarch64-linux-gnu-gcc \ - CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNU_RUNNER="qemu-loongarch64-static -cpu max -L /usr/loongarch64-linux-gnu" \ + CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNU_RUNNER="qemu-loongarch64 -cpu max -L /usr/loongarch64-linux-gnu" \ OBJDUMP=loongarch64-linux-gnu-objdump \ STDARCH_TEST_SKIP_FEATURE=frecipe From 418900562cb189e68b21acd49116ed8235646578 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 3 Sep 2025 00:05:50 +0200 Subject: [PATCH 170/682] explicitly start `va_list` lifetime --- compiler/rustc_codegen_ssa/src/mir/block.rs | 2 +- compiler/rustc_codegen_ssa/src/mir/mod.rs | 4 ++++ tests/codegen-llvm/c-variadic-lifetime.rs | 21 +++++++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 tests/codegen-llvm/c-variadic-lifetime.rs diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 5f6976f5d005..6492ef73956b 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -520,7 +520,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { LocalRef::Place(va_list) => { bx.va_end(va_list.val.llval); - // Explicitly end the lifetime of the `va_list`, this matters for LLVM. + // Explicitly end the lifetime of the `va_list`, improves LLVM codegen. bx.lifetime_end(va_list.val.llval, va_list.layout.size); } _ => bug!("C-variadic function must have a `VaList` place"), diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 06873313e2ec..6b109e8b8e2f 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -438,6 +438,10 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( if fx.fn_abi.c_variadic && arg_index == fx.fn_abi.args.len() { let va_list = PlaceRef::alloca(bx, bx.layout_of(arg_ty)); + + // Explicitly start the lifetime of the `va_list`, improves LLVM codegen. + bx.lifetime_start(va_list.val.llval, va_list.layout.size); + bx.va_start(va_list.val.llval); return LocalRef::Place(va_list); diff --git a/tests/codegen-llvm/c-variadic-lifetime.rs b/tests/codegen-llvm/c-variadic-lifetime.rs new file mode 100644 index 000000000000..5b2f8af18c81 --- /dev/null +++ b/tests/codegen-llvm/c-variadic-lifetime.rs @@ -0,0 +1,21 @@ +//@ add-core-stubs +//@ compile-flags: -Copt-level=3 +#![feature(c_variadic)] +#![crate_type = "lib"] + +// Check that `%args` explicitly has its lifetime start and end. Being explicit can improve +// instruction and register selection, see e.g. https://github.com/rust-lang/rust/pull/144549 + +#[unsafe(no_mangle)] +unsafe extern "C" fn variadic(a: f64, mut args: ...) -> f64 { + // CHECK: call void @llvm.lifetime.start.p0(i64 {{[0-9]+}}, ptr nonnull %args) + // CHECK: call void @llvm.va_start.p0(ptr nonnull %args) + + let b = args.arg::(); + let c = args.arg::(); + + a + b + c + + // CHECK: call void @llvm.va_end.p0(ptr nonnull %args) + // CHECK: call void @llvm.lifetime.end.p0(i64 {{[0-9]+}}, ptr nonnull %args) +} From 46795337322b5852438417549a5fe9dce3f75479 Mon Sep 17 00:00:00 2001 From: Tsukasa OI Date: Sat, 30 Aug 2025 05:05:12 +0000 Subject: [PATCH 171/682] RISC-V: Lower requirements of `clmul` and `clmulh` They don't need full "Zbc" extension but only its subset: the "Zbkc" extension. Since the compiler implies `zbkc` from `zbc`, it's safe to use `#[target_feature(enable = "zbkc")]`. --- library/stdarch/crates/core_arch/src/riscv_shared/zb.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/riscv_shared/zb.rs b/library/stdarch/crates/core_arch/src/riscv_shared/zb.rs index 9472e3c8be9f..514afd908092 100644 --- a/library/stdarch/crates/core_arch/src/riscv_shared/zb.rs +++ b/library/stdarch/crates/core_arch/src/riscv_shared/zb.rs @@ -68,7 +68,7 @@ pub fn orc_b(rs: usize) -> usize { /// /// Section: 2.11 #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] -#[target_feature(enable = "zbc")] +#[target_feature(enable = "zbkc")] #[cfg_attr(test, assert_instr(clmul))] #[inline] pub fn clmul(rs1: usize, rs2: usize) -> usize { @@ -93,7 +93,7 @@ pub fn clmul(rs1: usize, rs2: usize) -> usize { /// /// Section: 2.12 #[unstable(feature = "riscv_ext_intrinsics", issue = "114544")] -#[target_feature(enable = "zbc")] +#[target_feature(enable = "zbkc")] #[cfg_attr(test, assert_instr(clmulh))] #[inline] pub fn clmulh(rs1: usize, rs2: usize) -> usize { From 39d51d04e706dee462ac4a17508ed87eb52e7715 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 26 Aug 2025 18:12:36 +0200 Subject: [PATCH 172/682] thread parking: fix docs and examples --- library/std/src/sys/sync/once/queue.rs | 4 +- library/std/src/thread/mod.rs | 65 ++++++++++++++++++++------ library/std/src/thread/tests.rs | 4 ++ src/tools/miri/README.md | 1 + 4 files changed, 59 insertions(+), 15 deletions(-) diff --git a/library/std/src/sys/sync/once/queue.rs b/library/std/src/sys/sync/once/queue.rs index 49e15d65f25a..17d99cdb3859 100644 --- a/library/std/src/sys/sync/once/queue.rs +++ b/library/std/src/sys/sync/once/queue.rs @@ -276,7 +276,9 @@ fn wait( // If the managing thread happens to signal and unpark us before we // can park ourselves, the result could be this thread never gets // unparked. Luckily `park` comes with the guarantee that if it got - // an `unpark` just before on an unparked thread it does not park. + // an `unpark` just before on an unparked thread it does not park. Crucially, we know + // the `unpark` must have happened between the `compare_exchange_weak` above and here, + // and there's no other `park` in that code that could steal our token. // SAFETY: we retrieved this handle on the current thread above. unsafe { node.thread.park() } } diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index b6059c28cec7..2847f94f9fd5 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -1021,13 +1021,23 @@ impl Drop for PanicGuard { /// specifying a maximum time to block the thread for. /// /// * The [`unpark`] method on a [`Thread`] atomically makes the token available -/// if it wasn't already. Because the token is initially absent, [`unpark`] -/// followed by [`park`] will result in the second call returning immediately. +/// if it wasn't already. Because the token can be held by a thread even if it is currently not +/// parked, [`unpark`] followed by [`park`] will result in the second call returning immediately. +/// However, note that to rely on this guarantee, you need to make sure that your `unpark` happens +/// after all `park` that may be done by other data structures! /// -/// The API is typically used by acquiring a handle to the current thread, -/// placing that handle in a shared data structure so that other threads can -/// find it, and then `park`ing in a loop. When some desired condition is met, another -/// thread calls [`unpark`] on the handle. +/// The API is typically used by acquiring a handle to the current thread, placing that handle in a +/// shared data structure so that other threads can find it, and then `park`ing in a loop. When some +/// desired condition is met, another thread calls [`unpark`] on the handle. The last bullet point +/// above guarantees that even if the `unpark` occurs before the thread is finished `park`ing, it +/// will be woken up properly. +/// +/// Note that the coordination via the shared data structure is crucial: If you `unpark` a thread +/// without first establishing that it is about to be `park`ing within your code, that `unpark` may +/// get consumed by a *different* `park` in the same thread, leading to a deadlock. This also means +/// you must not call unknown code between setting up for parking and calling `park`; for instance, +/// if you invoke `println!`, that may itself call `park` and thus consume your `unpark` and cause a +/// deadlock. /// /// The motivation for this design is twofold: /// @@ -1058,21 +1068,24 @@ impl Drop for PanicGuard { /// /// ``` /// use std::thread; -/// use std::sync::{Arc, atomic::{Ordering, AtomicBool}}; +/// use std::sync::atomic::{Ordering, AtomicBool}; /// use std::time::Duration; /// -/// let flag = Arc::new(AtomicBool::new(false)); -/// let flag2 = Arc::clone(&flag); +/// static QUEUED: AtomicBool = AtomicBool::new(false); +/// static FLAG: AtomicBool = AtomicBool::new(false); /// /// let parked_thread = thread::spawn(move || { +/// println!("Thread spawned"); +/// // Signal that we are going to `park`. Between this store and our `park`, there may +/// // be no other `park`, or else that `park` could consume our `unpark` token! +/// QUEUED.store(true, Ordering::Release); /// // We want to wait until the flag is set. We *could* just spin, but using /// // park/unpark is more efficient. -/// while !flag2.load(Ordering::Relaxed) { -/// println!("Parking thread"); +/// while !FLAG.load(Ordering::Acquire) { +/// // We can *not* use `println!` here since that could use thread parking internally. /// thread::park(); /// // We *could* get here spuriously, i.e., way before the 10ms below are over! /// // But that is no problem, we are in a loop until the flag is set anyway. -/// println!("Thread unparked"); /// } /// println!("Flag received"); /// }); @@ -1080,11 +1093,22 @@ impl Drop for PanicGuard { /// // Let some time pass for the thread to be spawned. /// thread::sleep(Duration::from_millis(10)); /// +/// // Ensure the thread is about to park. +/// // This is crucial! It guarantees that the `unpark` below is not consumed +/// // by some other code in the parked thread (e.g. inside `println!`). +/// while !QUEUED.load(Ordering::Acquire) { +/// // Spinning is of course inefficient; in practice, this would more likely be +/// // a dequeue where we have no work to do if there's nobody queued. +/// std::hint::spin_loop(); +/// } +/// /// // Set the flag, and let the thread wake up. -/// // There is no race condition here, if `unpark` +/// // There is no race condition here: if `unpark` /// // happens first, `park` will return immediately. +/// // There is also no other `park` that could consume this token, +/// // since we waited until the other thread got queued. /// // Hence there is no risk of a deadlock. -/// flag.store(true, Ordering::Relaxed); +/// FLAG.store(true, Ordering::Release); /// println!("Unpark the thread"); /// parked_thread.thread().unpark(); /// @@ -1494,10 +1518,14 @@ impl Thread { /// ``` /// use std::thread; /// use std::time::Duration; + /// use std::sync::atomic::{AtomicBool, Ordering}; + /// + /// static QUEUED: AtomicBool = AtomicBool::new(false); /// /// let parked_thread = thread::Builder::new() /// .spawn(|| { /// println!("Parking thread"); + /// QUEUED.store(true, Ordering::Release); /// thread::park(); /// println!("Thread unparked"); /// }) @@ -1506,6 +1534,15 @@ impl Thread { /// // Let some time pass for the thread to be spawned. /// thread::sleep(Duration::from_millis(10)); /// + /// // Wait until the other thread is queued. + /// // This is crucial! It guarantees that the `unpark` below is not consumed + /// // by some other code in the parked thread (e.g. inside `println!`). + /// while !QUEUED.load(Ordering::Acquire) { + /// // Spinning is of course inefficient; in practice, this would more likely be + /// // a dequeue where we have no work to do if there's nobody queued. + /// std::hint::spin_loop(); + /// } + /// /// println!("Unpark the thread"); /// parked_thread.thread().unpark(); /// diff --git a/library/std/src/thread/tests.rs b/library/std/src/thread/tests.rs index ae889f1e7782..2117f5f93ce2 100644 --- a/library/std/src/thread/tests.rs +++ b/library/std/src/thread/tests.rs @@ -287,6 +287,8 @@ fn test_park_unpark_called_other_thread() { for _ in 0..10 { let th = thread::current(); + // Here we rely on `thread::spawn` (specifically the part that runs after spawning + // the thread) to not consume the parking token. let _guard = thread::spawn(move || { super::sleep(Duration::from_millis(50)); th.unpark(); @@ -316,6 +318,8 @@ fn test_park_timeout_unpark_called_other_thread() { for _ in 0..10 { let th = thread::current(); + // Here we rely on `thread::spawn` (specifically the part that runs after spawning + // the thread) to not consume the parking token. let _guard = thread::spawn(move || { super::sleep(Duration::from_millis(50)); th.unpark(); diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index 7ccd27d7b83e..2aa56baddf78 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -600,6 +600,7 @@ Definite bugs found: * [A bug in the new `RwLock::downgrade` implementation](https://rust-lang.zulipchat.com/#narrow/channel/269128-miri/topic/Miri.20error.20library.20test) (caught by Miri before it landed in the Rust repo) * [Mockall reading uninitialized memory when mocking `std::io::Read::read`, even if all expectations are satisfied](https://github.com/asomers/mockall/issues/647) (caught by Miri running Tokio's test suite) * [`ReentrantLock` not correctly dealing with reuse of addresses for TLS storage of different threads](https://github.com/rust-lang/rust/pull/141248) +* [Rare Deadlock in the thread (un)parking example code](https://github.com/rust-lang/rust/issues/145816) Violations of [Stacked Borrows] found that are likely bugs (but Stacked Borrows is currently just an experiment): From 5b71113a64d576abd6a14c8d7201ff1164a6c44e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 3 Sep 2025 11:40:46 +0200 Subject: [PATCH 173/682] re-balance CI --- src/tools/miri/ci/ci.sh | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index b66530e77b8a..224ca0ae2994 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -164,12 +164,15 @@ case $HOST_TARGET in MANY_SEEDS=16 TEST_TARGET=arm-unknown-linux-gnueabi run_tests # 32bit ARM MANY_SEEDS=16 TEST_TARGET=aarch64-pc-windows-gnullvm run_tests # gnullvm ABI MANY_SEEDS=16 TEST_TARGET=s390x-unknown-linux-gnu run_tests # big-endian architecture of choice - # Custom target JSON file - TEST_TARGET=tests/x86_64-unknown-kernel.json MIRI_NO_STD=1 run_tests_minimal no_std + # Not officially supported tier 2 + MANY_SEEDS=16 TEST_TARGET=x86_64-unknown-freebsd run_tests + MANY_SEEDS=16 TEST_TARGET=i686-unknown-freebsd run_tests ;; armv7-unknown-linux-gnueabihf) # Host GC_STRESS=1 MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests + # Custom target JSON file + TEST_TARGET=tests/x86_64-unknown-kernel.json MIRI_NO_STD=1 run_tests_minimal no_std ;; aarch64-apple-darwin) # Host @@ -181,8 +184,6 @@ case $HOST_TARGET in MANY_SEEDS=16 TEST_TARGET=mips-unknown-linux-gnu run_tests # a 32bit big-endian target, and also a target without 64bit atomics MANY_SEEDS=16 TEST_TARGET=x86_64-unknown-illumos run_tests MANY_SEEDS=16 TEST_TARGET=x86_64-pc-solaris run_tests - MANY_SEEDS=16 TEST_TARGET=x86_64-unknown-freebsd run_tests - MANY_SEEDS=16 TEST_TARGET=i686-unknown-freebsd run_tests ;; i686-pc-windows-msvc) # Host From a880c46f198e9601e70cac9286154286906699ce Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 3 Sep 2025 11:47:30 +0200 Subject: [PATCH 174/682] no need to run GC_STRESS more than once for each OS --- src/tools/miri/ci/ci.sh | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index 224ca0ae2994..6356e33f61e8 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -137,6 +137,7 @@ function run_tests_minimal { # In particular, fully cover all tier 1 targets. # We also want to run the many-seeds tests on all tier 1 targets. +# We run GC_STRESS only once for each tier 1 OS. case $HOST_TARGET in x86_64-unknown-linux-gnu) # Host @@ -147,7 +148,6 @@ case $HOST_TARGET in ;; i686-unknown-linux-gnu) # Host - # Without GC_STRESS as this is a slow runner. MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests # Partially supported targets (tier 2) BASIC="empty_main integer heap_alloc libc-mem vec string btreemap" # ensures we have the basics: pre-main code, system allocator @@ -159,7 +159,7 @@ case $HOST_TARGET in ;; aarch64-unknown-linux-gnu) # Host - GC_STRESS=1 MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests + MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests # Extra tier 2 MANY_SEEDS=16 TEST_TARGET=arm-unknown-linux-gnueabi run_tests # 32bit ARM MANY_SEEDS=16 TEST_TARGET=aarch64-pc-windows-gnullvm run_tests # gnullvm ABI @@ -170,7 +170,7 @@ case $HOST_TARGET in ;; armv7-unknown-linux-gnueabihf) # Host - GC_STRESS=1 MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests + MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests # Custom target JSON file TEST_TARGET=tests/x86_64-unknown-kernel.json MIRI_NO_STD=1 run_tests_minimal no_std ;; @@ -187,7 +187,6 @@ case $HOST_TARGET in ;; i686-pc-windows-msvc) # Host - # Without GC_STRESS as this is a very slow runner. MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 run_tests # Extra tier 1 # We really want to ensure a Linux target works on a Windows host, @@ -196,8 +195,7 @@ case $HOST_TARGET in ;; aarch64-pc-windows-msvc) # Host - # Without GC_STRESS as this is a very slow runner. - MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests + GC_STRESS=1 MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests # Extra tier 1 MANY_SEEDS=64 TEST_TARGET=i686-unknown-linux-gnu run_tests ;; From d366f26421125e11bfcec5e4a9f202813cbd3229 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Mon, 14 Apr 2025 08:53:50 +0200 Subject: [PATCH 175/682] Extract address generator struct from memory allocator. --- .../src/alloc_addresses/address_generator.rs | 78 +++++++++++++++++++ src/tools/miri/src/alloc_addresses/mod.rs | 63 ++++----------- src/tools/miri/src/machine.rs | 4 +- 3 files changed, 94 insertions(+), 51 deletions(-) create mode 100644 src/tools/miri/src/alloc_addresses/address_generator.rs diff --git a/src/tools/miri/src/alloc_addresses/address_generator.rs b/src/tools/miri/src/alloc_addresses/address_generator.rs new file mode 100644 index 000000000000..3764f5dcb824 --- /dev/null +++ b/src/tools/miri/src/alloc_addresses/address_generator.rs @@ -0,0 +1,78 @@ +use std::ops::Range; + +use rand::Rng; +use rustc_abi::{Align, Size}; +use rustc_const_eval::interpret::{InterpResult, interp_ok}; +use rustc_middle::{err_exhaust, throw_exhaust}; + +/// Shifts `addr` to make it aligned with `align` by rounding `addr` to the smallest multiple +/// of `align` that is larger or equal to `addr` +fn align_addr(addr: u64, align: u64) -> u64 { + match addr % align { + 0 => addr, + rem => addr.strict_add(align) - rem, + } +} + +/// This provides the logic to generate addresses for memory allocations in a given address range. +#[derive(Debug)] +pub struct AddressGenerator { + /// This is used as a memory address when a new pointer is casted to an integer. It + /// is always larger than any address that was previously made part of a block. + next_base_addr: u64, + /// This is the last address that can be allocated. + end: u64, +} + +impl AddressGenerator { + pub fn new(addr_range: Range) -> Self { + Self { next_base_addr: addr_range.start, end: addr_range.end } + } + + /// Get the remaining range where this `AddressGenerator` can still allocate addresses. + pub fn get_remaining(&self) -> Range { + self.next_base_addr..self.end + } + + /// Generate a new address with the specified size and alignment, using the given Rng to add some randomness. + /// The returned allocation is guaranteed not to overlap with any address ranges given out by the generator before. + /// Returns an error if the allocation request cannot be fulfilled. + pub fn generate<'tcx, R: Rng>( + &mut self, + size: Size, + align: Align, + rng: &mut R, + ) -> InterpResult<'tcx, u64> { + // Leave some space to the previous allocation, to give it some chance to be less aligned. + // We ensure that `(self.next_base_addr + slack) % 16` is uniformly distributed. + let slack = rng.random_range(0..16); + // From next_base_addr + slack, round up to adjust for alignment. + let base_addr = + self.next_base_addr.checked_add(slack).ok_or_else(|| err_exhaust!(AddressSpaceFull))?; + let base_addr = align_addr(base_addr, align.bytes()); + + // Remember next base address. If this allocation is zero-sized, leave a gap of at + // least 1 to avoid two allocations having the same base address. (The logic in + // `alloc_id_from_addr` assumes unique addresses, and different function/vtable pointers + // need to be distinguishable!) + self.next_base_addr = base_addr + .checked_add(size.bytes().max(1)) + .ok_or_else(|| err_exhaust!(AddressSpaceFull))?; + // Even if `Size` didn't overflow, we might still have filled up the address space. + if self.next_base_addr > self.end { + throw_exhaust!(AddressSpaceFull); + } + interp_ok(base_addr) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_align_addr() { + assert_eq!(align_addr(37, 4), 40); + assert_eq!(align_addr(44, 4), 44); + } +} diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs index 334503d2994b..afd8afbaeab0 100644 --- a/src/tools/miri/src/alloc_addresses/mod.rs +++ b/src/tools/miri/src/alloc_addresses/mod.rs @@ -1,15 +1,16 @@ //! This module is responsible for managing the absolute addresses that allocations are located at, //! and for casting between pointers and integers based on those addresses. +mod address_generator; mod reuse_pool; use std::cell::RefCell; -use std::cmp::max; -use rand::Rng; use rustc_abi::{Align, Size}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_middle::ty::TyCtxt; +pub use self::address_generator::AddressGenerator; use self::reuse_pool::ReusePool; use crate::concurrency::VClock; use crate::*; @@ -49,9 +50,8 @@ pub struct GlobalStateInner { /// Whether an allocation has been exposed or not. This cannot be put /// into `AllocExtra` for the same reason as `base_addr`. exposed: FxHashSet, - /// This is used as a memory address when a new pointer is casted to an integer. It - /// is always larger than any address that was previously made part of a block. - next_base_addr: u64, + /// The generator for new addresses in a given range. + address_generator: AddressGenerator, /// The provenance to use for int2ptr casts provenance_mode: ProvenanceMode, } @@ -64,7 +64,7 @@ impl VisitProvenance for GlobalStateInner { prepared_alloc_bytes: _, reuse: _, exposed: _, - next_base_addr: _, + address_generator: _, provenance_mode: _, } = self; // Though base_addr, int_to_ptr_map, and exposed contain AllocIds, we do not want to visit them. @@ -77,14 +77,14 @@ impl VisitProvenance for GlobalStateInner { } impl GlobalStateInner { - pub fn new(config: &MiriConfig, stack_addr: u64) -> Self { + pub fn new<'tcx>(config: &MiriConfig, stack_addr: u64, tcx: TyCtxt<'tcx>) -> Self { GlobalStateInner { int_to_ptr_map: Vec::default(), base_addr: FxHashMap::default(), prepared_alloc_bytes: FxHashMap::default(), reuse: ReusePool::new(config), exposed: FxHashSet::default(), - next_base_addr: stack_addr, + address_generator: AddressGenerator::new(stack_addr..tcx.target_usize_max()), provenance_mode: config.provenance_mode, } } @@ -96,15 +96,6 @@ impl GlobalStateInner { } } -/// Shifts `addr` to make it aligned with `align` by rounding `addr` to the smallest multiple -/// of `align` that is larger or equal to `addr` -fn align_addr(addr: u64, align: u64) -> u64 { - match addr % align { - 0 => addr, - rem => addr.strict_add(align) - rem, - } -} - impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {} trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { fn addr_from_alloc_id_uncached( @@ -194,34 +185,17 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { interp_ok(reuse_addr) } else { // We have to pick a fresh address. - // Leave some space to the previous allocation, to give it some chance to be less aligned. - // We ensure that `(global_state.next_base_addr + slack) % 16` is uniformly distributed. - let slack = rng.random_range(0..16); - // From next_base_addr + slack, round up to adjust for alignment. - let base_addr = global_state - .next_base_addr - .checked_add(slack) - .ok_or_else(|| err_exhaust!(AddressSpaceFull))?; - let base_addr = align_addr(base_addr, info.align.bytes()); + let new_addr = + global_state.address_generator.generate(info.size, info.align, &mut rng)?; - // Remember next base address. If this allocation is zero-sized, leave a gap of at - // least 1 to avoid two allocations having the same base address. (The logic in - // `alloc_id_from_addr` assumes unique addresses, and different function/vtable pointers - // need to be distinguishable!) - global_state.next_base_addr = base_addr - .checked_add(max(info.size.bytes(), 1)) - .ok_or_else(|| err_exhaust!(AddressSpaceFull))?; - // Even if `Size` didn't overflow, we might still have filled up the address space. - if global_state.next_base_addr > this.target_usize_max() { - throw_exhaust!(AddressSpaceFull); - } // If we filled up more than half the address space, start aggressively reusing // addresses to avoid running out. - if global_state.next_base_addr > u64::try_from(this.target_isize_max()).unwrap() { + let remaining_range = global_state.address_generator.get_remaining(); + if remaining_range.start > remaining_range.end / 2 { global_state.reuse.address_space_shortage(); } - interp_ok(base_addr) + interp_ok(new_addr) } } } @@ -519,14 +493,3 @@ impl<'tcx> MiriMachine<'tcx> { }) } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_align_addr() { - assert_eq!(align_addr(37, 4), 40); - assert_eq!(align_addr(44, 4), 44); - } -} diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 8f0814a070cb..9482769a2fc7 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -674,11 +674,13 @@ impl<'tcx> MiriMachine<'tcx> { thread_cpu_affinity .insert(threads.active_thread(), CpuAffinityMask::new(&layout_cx, config.num_cpus)); } + let alloc_addresses = + RefCell::new(alloc_addresses::GlobalStateInner::new(config, stack_addr, tcx)); MiriMachine { tcx, borrow_tracker, data_race, - alloc_addresses: RefCell::new(alloc_addresses::GlobalStateInner::new(config, stack_addr)), + alloc_addresses, // `env_vars` depends on a full interpreter so we cannot properly initialize it yet. env_vars: EnvVars::default(), main_fn_ret_place: None, From c1be740564dd4a3aaafca93539ca428223226278 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Mon, 14 Apr 2025 08:53:50 +0200 Subject: [PATCH 176/682] Implement basic support for running Miri with GenMC. - Implement memory allocation compatible with GenMC. - Extract address generator struct from Miri's allocator. - Support thread creation and joining. - Support atomic load and store. - Support scheduling through GenMC. - Add tests for GenMC mode. - Add clang-format file for C++ code in Miri. - Update genmc-sys crate license to MIT/Apache to match GenMC dependency. - Add documentation for GenMC mode. Note: this commit depends on changes to GenMC not yet upstreamed to its official repository. Co-authored-by: Ralf Jung --- src/tools/miri/Cargo.lock | 24 +- src/tools/miri/Cargo.toml | 2 +- src/tools/miri/doc/genmc.md | 28 +- src/tools/miri/genmc-sys/.clang-format | 52 ++ src/tools/miri/genmc-sys/Cargo.toml | 8 +- src/tools/miri/genmc-sys/build.rs | 42 +- .../genmc-sys/cpp/include/MiriInterface.hpp | 274 +++++++ .../genmc-sys/cpp/include/ResultHandling.hpp | 21 + .../cpp/src/MiriInterface/EventHandling.cpp | 120 +++ .../cpp/src/MiriInterface/Exploration.cpp | 42 + .../genmc-sys/cpp/src/MiriInterface/Setup.cpp | 185 +++++ .../src/MiriInterface/ThreadManagement.cpp | 54 ++ src/tools/miri/genmc-sys/src/lib.rs | 297 ++++++- .../miri/genmc-sys/src_cpp/MiriInterface.cpp | 50 -- .../miri/genmc-sys/src_cpp/MiriInterface.hpp | 44 - src/tools/miri/src/alloc_addresses/mod.rs | 19 +- src/tools/miri/src/bin/miri.rs | 22 +- .../src/borrow_tracker/stacked_borrows/mod.rs | 1 + src/tools/miri/src/concurrency/data_race.rs | 69 +- .../miri/src/concurrency/genmc/config.rs | 19 +- src/tools/miri/src/concurrency/genmc/dummy.rs | 122 +-- .../concurrency/genmc/global_allocations.rs | 118 +++ .../miri/src/concurrency/genmc/helper.rs | 97 +++ src/tools/miri/src/concurrency/genmc/mod.rs | 764 ++++++++++++++---- src/tools/miri/src/concurrency/genmc/run.rs | 71 ++ .../miri/src/concurrency/genmc/scheduling.rs | 69 ++ .../src/concurrency/genmc/thread_id_map.rs | 63 ++ src/tools/miri/src/concurrency/init_once.rs | 4 +- src/tools/miri/src/concurrency/mod.rs | 2 +- src/tools/miri/src/concurrency/thread.rs | 53 +- src/tools/miri/src/diagnostics.rs | 17 +- src/tools/miri/src/eval.rs | 30 +- src/tools/miri/src/lib.rs | 2 +- src/tools/miri/src/machine.rs | 7 +- src/tools/miri/src/shims/foreign_items.rs | 11 +- .../miri/src/shims/unix/linux_like/epoll.rs | 2 +- .../miri/src/shims/unix/linux_like/eventfd.rs | 2 +- .../miri/src/shims/unix/unnamed_socket.rs | 2 +- src/tools/miri/src/shims/windows/sync.rs | 2 +- .../data_race/weak_orderings.rel_rlx.stderr | 19 + .../data_race/weak_orderings.rlx_acq.stderr | 19 + .../data_race/weak_orderings.rlx_rlx.stderr | 19 + .../genmc/fail/data_race/weak_orderings.rs | 40 + .../fail/simple/2w2w_weak.relaxed4.stderr | 15 + .../fail/simple/2w2w_weak.release4.stderr | 15 + .../miri/tests/genmc/fail/simple/2w2w_weak.rs | 73 ++ .../fail/simple/2w2w_weak.sc3_rel1.stderr | 15 + .../miri/tests/genmc/pass/litmus/2cowr.rs | 51 ++ .../miri/tests/genmc/pass/litmus/2cowr.stderr | 2 + .../pass/litmus/2w2w_3sc_1rel.release1.stderr | 2 + .../pass/litmus/2w2w_3sc_1rel.release2.stderr | 2 + .../tests/genmc/pass/litmus/2w2w_3sc_1rel.rs | 54 ++ .../miri/tests/genmc/pass/litmus/2w2w_4rel.rs | 45 ++ .../tests/genmc/pass/litmus/2w2w_4rel.stderr | 2 + .../miri/tests/genmc/pass/litmus/2w2w_4sc.rs | 45 ++ .../tests/genmc/pass/litmus/2w2w_4sc.stderr | 2 + .../tests/genmc/pass/litmus/IRIW-acq-sc.rs | 64 ++ .../genmc/pass/litmus/IRIW-acq-sc.stderr | 2 + src/tools/miri/tests/genmc/pass/litmus/LB.rs | 47 ++ .../miri/tests/genmc/pass/litmus/LB.stderr | 2 + src/tools/miri/tests/genmc/pass/litmus/MP.rs | 47 ++ .../miri/tests/genmc/pass/litmus/MP.stderr | 2 + src/tools/miri/tests/genmc/pass/litmus/SB.rs | 47 ++ .../miri/tests/genmc/pass/litmus/SB.stderr | 2 + .../miri/tests/genmc/pass/litmus/corr.rs | 45 ++ .../miri/tests/genmc/pass/litmus/corr.stderr | 2 + .../miri/tests/genmc/pass/litmus/corr0.rs | 46 ++ .../miri/tests/genmc/pass/litmus/corr0.stderr | 2 + .../miri/tests/genmc/pass/litmus/corr1.rs | 58 ++ .../miri/tests/genmc/pass/litmus/corr1.stderr | 2 + .../miri/tests/genmc/pass/litmus/corr2.rs | 70 ++ .../miri/tests/genmc/pass/litmus/corr2.stderr | 2 + .../miri/tests/genmc/pass/litmus/corw.rs | 43 + .../miri/tests/genmc/pass/litmus/corw.stderr | 2 + .../miri/tests/genmc/pass/litmus/cowr.rs | 40 + .../miri/tests/genmc/pass/litmus/cowr.stderr | 2 + .../miri/tests/genmc/pass/litmus/default.rs | 47 ++ .../tests/genmc/pass/litmus/default.stderr | 2 + .../genmc/pass/litmus/detour.join.stderr | 2 + .../genmc/pass/litmus/detour.no_join.stderr | 2 + .../miri/tests/genmc/pass/litmus/detour.rs | 68 ++ .../tests/genmc/pass/litmus/fr_w_w_w_reads.rs | 54 ++ .../genmc/pass/litmus/fr_w_w_w_reads.stderr | 2 + .../miri/tests/genmc/pass/test_cxx_build.rs | 8 - .../tests/genmc/pass/test_cxx_build.stderr | 5 - src/tools/miri/tests/utils/genmc.rs | 57 ++ 86 files changed, 3574 insertions(+), 431 deletions(-) create mode 100644 src/tools/miri/genmc-sys/.clang-format create mode 100644 src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp create mode 100644 src/tools/miri/genmc-sys/cpp/include/ResultHandling.hpp create mode 100644 src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp create mode 100644 src/tools/miri/genmc-sys/cpp/src/MiriInterface/Exploration.cpp create mode 100644 src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp create mode 100644 src/tools/miri/genmc-sys/cpp/src/MiriInterface/ThreadManagement.cpp delete mode 100644 src/tools/miri/genmc-sys/src_cpp/MiriInterface.cpp delete mode 100644 src/tools/miri/genmc-sys/src_cpp/MiriInterface.hpp create mode 100644 src/tools/miri/src/concurrency/genmc/global_allocations.rs create mode 100644 src/tools/miri/src/concurrency/genmc/helper.rs create mode 100644 src/tools/miri/src/concurrency/genmc/run.rs create mode 100644 src/tools/miri/src/concurrency/genmc/scheduling.rs create mode 100644 src/tools/miri/src/concurrency/genmc/thread_id_map.rs create mode 100644 src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rel_rlx.stderr create mode 100644 src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_acq.stderr create mode 100644 src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_rlx.stderr create mode 100644 src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rs create mode 100644 src/tools/miri/tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr create mode 100644 src/tools/miri/tests/genmc/fail/simple/2w2w_weak.release4.stderr create mode 100644 src/tools/miri/tests/genmc/fail/simple/2w2w_weak.rs create mode 100644 src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/2cowr.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/2cowr.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release1.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release2.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/2w2w_4sc.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/2w2w_4sc.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/LB.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/LB.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/MP.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/MP.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/SB.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/SB.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/corr.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/corr.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/corr0.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/corr0.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/corr1.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/corr1.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/corr2.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/corr2.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/corw.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/corw.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/cowr.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/cowr.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/default.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/default.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/detour.join.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/detour.no_join.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/detour.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.stderr delete mode 100644 src/tools/miri/tests/genmc/pass/test_cxx_build.rs delete mode 100644 src/tools/miri/tests/genmc/pass/test_cxx_build.stderr create mode 100644 src/tools/miri/tests/utils/genmc.rs diff --git a/src/tools/miri/Cargo.lock b/src/tools/miri/Cargo.lock index ece51f2ba74a..c4280f5e87a6 100644 --- a/src/tools/miri/Cargo.lock +++ b/src/tools/miri/Cargo.lock @@ -363,9 +363,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.161" +version = "1.0.173" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3523cc02ad831111491dd64b27ad999f1ae189986728e477604e61b81f828df" +checksum = "6c64ed3da1c337cbaae7223cdcff8b4dddf698d188cd3eaddd1116f6b0295950" dependencies = [ "cc", "cxxbridge-cmd", @@ -377,9 +377,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.161" +version = "1.0.173" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212b754247a6f07b10fa626628c157593f0abf640a3dd04cce2760eca970f909" +checksum = "ae0a26a75a05551f5ae3d75b3557543d06682284eaa7419113162d602cb45766" dependencies = [ "cc", "codespan-reporting", @@ -392,9 +392,9 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "1.0.161" +version = "1.0.173" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f426a20413ec2e742520ba6837c9324b55ffac24ead47491a6e29f933c5b135a" +checksum = "952d408b6002b7db4b36da07c682a9cbb34f2db0efa03e976ae50a388414e16c" dependencies = [ "clap", "codespan-reporting", @@ -406,15 +406,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.161" +version = "1.0.173" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258b6069020b4e5da6415df94a50ee4f586a6c38b037a180e940a43d06a070d" +checksum = "ccbd201b471c75c6abb6321cace706d1982d270e308b891c11a3262d320f5265" [[package]] name = "cxxbridge-macro" -version = "1.0.161" +version = "1.0.173" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8dec184b52be5008d6eaf7e62fc1802caf1ad1227d11b3b7df2c409c7ffc3f4" +checksum = "2bea8b915bbc4cb4288f242aa7ca18b23ecc6965e4d6e7c1b07905e3fe2e0c41" dependencies = [ "indexmap", "proc-macro2", @@ -501,9 +501,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" -version = "0.1.5" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" [[package]] name = "form_urlencoded" diff --git a/src/tools/miri/Cargo.toml b/src/tools/miri/Cargo.toml index 91dadf78a2f7..5afde9b23d5c 100644 --- a/src/tools/miri/Cargo.toml +++ b/src/tools/miri/Cargo.toml @@ -70,7 +70,7 @@ harness = false [features] default = ["stack-cache", "native-lib"] -genmc = ["dep:genmc-sys"] # this enables a GPL dependency! +genmc = ["dep:genmc-sys"] stack-cache = [] stack-cache-consistency-check = ["stack-cache"] tracing = ["serde_json"] diff --git a/src/tools/miri/doc/genmc.md b/src/tools/miri/doc/genmc.md index 5aabe90b5dab..fdbf2defe42d 100644 --- a/src/tools/miri/doc/genmc.md +++ b/src/tools/miri/doc/genmc.md @@ -9,9 +9,7 @@ Miri-GenMC integrates that model checker into Miri. ## Usage -**IMPORTANT: The license of GenMC and thus the `genmc-sys` crate in the Miri repo is currently "GPL-3.0-or-later", so a binary produced with the `genmc` feature is subject to the requirements of the GPL. As long as that remains the case, the `genmc` feature of Miri is OFF-BY-DEFAULT and must be OFF for all Miri releases.** - -For testing/developing Miri-GenMC (while keeping in mind the licensing issues): +For testing/developing Miri-GenMC: - clone the Miri repo. - build Miri-GenMC with `./miri build --features=genmc`. - OR: install Miri-GenMC in the current system with `./miri install --features=genmc` @@ -21,6 +19,21 @@ Basic usage: MIRIFLAGS="-Zmiri-genmc" cargo miri run ``` +Note that `cargo miri test` in GenMC mode is currently not supported. + +### Supported Parameters + +- `-Zmiri-genmc`: Enable GenMC mode (not required if any other GenMC options are used). +- `-Zmiri-genmc-log=LOG_LEVEL`: Change the log level for GenMC. Default: `warning`. + - `quiet`: Disable logging. + - `error`: Print errors. + - `warning`: Print errors and warnings. + - `tip`: Print errors, warnings and tips. + - If Miri is built with debug assertions, there are additional log levels available (downgraded to `tip` without debug assertions): + - `debug1`: Print revisits considered by GenMC. + - `debug2`: Print the execution graph after every memory access. + - `debug3`: Print reads-from values considered by GenMC. + @@ -57,6 +70,15 @@ The process for obtaining them is as follows: If you place this directory inside the Miri folder, it is recommended to call it `genmc-src` as that tells `./miri fmt` to avoid formatting the Rust files inside that folder. +### Formatting the C++ code + +For formatting the C++ code we provide a `.clang-format` file in the `genmc-sys` directory. +With `clang-format` installed, run this command to format the c++ files (replace the `-i` with `--dry-run` to just see the changes.): +``` +find ./genmc-sys/cpp/ -name "*.cpp" -o -name "*.hpp" | xargs clang-format --style=file:"./genmc-sys/.clang-format" -i +``` +NOTE: this is currently not done automatically on pull requests to Miri. + diff --git a/src/tools/miri/genmc-sys/.clang-format b/src/tools/miri/genmc-sys/.clang-format new file mode 100644 index 000000000000..669d76ea6c9b --- /dev/null +++ b/src/tools/miri/genmc-sys/.clang-format @@ -0,0 +1,52 @@ +# .clang-format + +BasedOnStyle: LLVM +Standard: c++20 + +ColumnLimit: 100 +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AllowShortBlocksOnASingleLine: Empty +BreakBeforeBraces: Attach + +BinPackArguments: false +BinPackParameters: false +AllowAllParametersOfDeclarationOnNextLine: false +AlwaysBreakAfterReturnType: None + +# Force parameters to break and align +AlignAfterOpenBracket: BlockIndent +AllowAllArgumentsOnNextLine: false + +# Spacing around braces and parentheses +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceInEmptyBlock: false +SpacesInContainerLiterals: true +SpacesInParensOptions: + InCStyleCasts: false + InConditionalStatements: false + InEmptyParentheses: false + Other: false +SpacesInSquareBrackets: false + +# Brace spacing for initializers +Cpp11BracedListStyle: false +SpaceBeforeCpp11BracedList: true + +# Import grouping: group standard, external, and project includes. +IncludeBlocks: Regroup +SortIncludes: true + +# Granularity: sort includes per module/file. +IncludeIsMainRegex: '([-_](test|unittest))?$' + +# Miscellaneous +SpaceAfterCStyleCast: true +SpaceBeforeParens: ControlStatements +PointerAlignment: Left +IndentCaseLabels: true +IndentWidth: 4 +TabWidth: 4 +UseTab: Never \ No newline at end of file diff --git a/src/tools/miri/genmc-sys/Cargo.toml b/src/tools/miri/genmc-sys/Cargo.toml index 95aef75afc4f..aa6374b7b373 100644 --- a/src/tools/miri/genmc-sys/Cargo.toml +++ b/src/tools/miri/genmc-sys/Cargo.toml @@ -1,17 +1,15 @@ [package] authors = ["Miri Team"] -# The parts in this repo are MIT OR Apache-2.0, but we are linking in -# code from https://github.com/MPI-SWS/genmc which is GPL-3.0-or-later. -license = "(MIT OR Apache-2.0) AND GPL-3.0-or-later" +license = "MIT OR Apache-2.0" name = "genmc-sys" version = "0.1.0" edition = "2024" [dependencies] -cxx = { version = "1.0.160", features = ["c++20"] } +cxx = { version = "1.0.173", features = ["c++20"] } [build-dependencies] cc = "1.2.30" cmake = "0.1.54" git2 = { version = "0.20.2", default-features = false, features = ["https"] } -cxx-build = { version = "1.0.160", features = ["parallel"] } +cxx-build = { version = "1.0.173", features = ["parallel"] } diff --git a/src/tools/miri/genmc-sys/build.rs b/src/tools/miri/genmc-sys/build.rs index f20e0e8d525f..af42c70431bf 100644 --- a/src/tools/miri/genmc-sys/build.rs +++ b/src/tools/miri/genmc-sys/build.rs @@ -26,9 +26,9 @@ mod downloading { use super::GENMC_DOWNLOAD_PATH; /// The GenMC repository the we get our commit from. - pub(crate) const GENMC_GITHUB_URL: &str = "https://github.com/MPI-SWS/genmc.git"; + pub(crate) const GENMC_GITHUB_URL: &str = "https://gitlab.inf.ethz.ch/public-plf/genmc.git"; /// The GenMC commit we depend on. It must be available on the specified GenMC repository. - pub(crate) const GENMC_COMMIT: &str = "3438dd2c1202cd4a47ed7881d099abf23e4167ab"; + pub(crate) const GENMC_COMMIT: &str = "af9cc9ccd5d412b16defc35dbf36571c63a19c76"; pub(crate) fn download_genmc() -> PathBuf { let Ok(genmc_download_path) = PathBuf::from_str(GENMC_DOWNLOAD_PATH); @@ -176,6 +176,11 @@ fn link_to_llvm(config_file: &Path) -> (String, String) { /// Build the GenMC model checker library and the Rust-C++ interop library with cxx.rs fn compile_cpp_dependencies(genmc_path: &Path) { + // Give each step a separate build directory to prevent interference. + let out_dir = PathBuf::from(std::env::var("OUT_DIR").as_deref().unwrap()); + let genmc_build_dir = out_dir.join("genmc"); + let interface_build_dir = out_dir.join("miri_genmc"); + // Part 1: // Compile the GenMC library using cmake. @@ -186,8 +191,11 @@ fn compile_cpp_dependencies(genmc_path: &Path) { let enable_genmc_debug = matches!(std::env::var("PROFILE").as_deref().unwrap(), "debug"); let mut config = cmake::Config::new(cmakelists_path); - config.profile(GENMC_CMAKE_PROFILE); - config.define("GENMC_DEBUG", if enable_genmc_debug { "ON" } else { "OFF" }); + config + .always_configure(false) // We don't need to reconfigure on subsequent compilation runs. + .out_dir(genmc_build_dir) + .profile(GENMC_CMAKE_PROFILE) + .define("GENMC_DEBUG", if enable_genmc_debug { "ON" } else { "OFF" }); // The actual compilation happens here: let genmc_install_dir = config.build(); @@ -210,6 +218,13 @@ fn compile_cpp_dependencies(genmc_path: &Path) { // These definitions are parsed into a cmake list and then printed to the config.h file, so they are ';' separated. let definitions = llvm_definitions.split(";"); + let cpp_files = [ + "./cpp/src/MiriInterface/EventHandling.cpp", + "./cpp/src/MiriInterface/Exploration.cpp", + "./cpp/src/MiriInterface/Setup.cpp", + "./cpp/src/MiriInterface/ThreadManagement.cpp", + ]; + let mut bridge = cxx_build::bridge("src/lib.rs"); // FIXME(genmc,cmake): Remove once the GenMC debug setting is available in the config.h file. if enable_genmc_debug { @@ -223,9 +238,9 @@ fn compile_cpp_dependencies(genmc_path: &Path) { .std("c++23") .include(genmc_include_dir) .include(llvm_include_dirs) - .include("./src_cpp") - .file("./src_cpp/MiriInterface.hpp") - .file("./src_cpp/MiriInterface.cpp") + .include("./cpp/include") + .files(&cpp_files) + .out_dir(interface_build_dir) .compile("genmc_interop"); // Link the Rust-C++ interface library generated by cxx_build: @@ -233,15 +248,8 @@ fn compile_cpp_dependencies(genmc_path: &Path) { } fn main() { - // Make sure we don't accidentally distribute a binary with GPL code. - if option_env!("RUSTC_STAGE").is_some() { - panic!( - "genmc should not be enabled in the rustc workspace since it includes a GPL dependency" - ); - } - // Select which path to use for the GenMC repo: - let genmc_path = if let Ok(genmc_src_path) = std::env::var("GENMC_SRC_PATH") { + let genmc_path = if let Some(genmc_src_path) = option_env!("GENMC_SRC_PATH") { let genmc_src_path = PathBuf::from_str(&genmc_src_path).expect("GENMC_SRC_PATH should contain a valid path"); assert!( @@ -249,6 +257,8 @@ fn main() { "GENMC_SRC_PATH={} does not exist!", genmc_src_path.display() ); + // Rebuild files in the given path change. + println!("cargo::rerun-if-changed={}", genmc_src_path.display()); genmc_src_path } else { downloading::download_genmc() @@ -263,5 +273,5 @@ fn main() { // cloned (since cargo detects that as a file modification). println!("cargo::rerun-if-changed={RUST_CXX_BRIDGE_FILE_PATH}"); println!("cargo::rerun-if-changed=./src"); - println!("cargo::rerun-if-changed=./src_cpp"); + println!("cargo::rerun-if-changed=./cpp"); } diff --git a/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp new file mode 100644 index 000000000000..4484f0b73cae --- /dev/null +++ b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp @@ -0,0 +1,274 @@ +#ifndef GENMC_MIRI_INTERFACE_HPP +#define GENMC_MIRI_INTERFACE_HPP + +// CXX.rs generated headers: +#include "rust/cxx.h" + +// GenMC generated headers: +#include "config.h" + +// Miri `genmc-sys/src_cpp` headers: +#include "ResultHandling.hpp" + +// GenMC headers: +#include "ExecutionGraph/EventLabel.hpp" +#include "Static/ModuleID.hpp" +#include "Support/MemOrdering.hpp" +#include "Support/RMWOps.hpp" +#include "Verification/Config.hpp" +#include "Verification/GenMCDriver.hpp" + +// C++ headers: +#include +#include +#include +#include + +/**** Types available to both Rust and C++ ****/ + +struct GenmcParams; +enum class LogLevel : std::uint8_t; + +struct GenmcScalar; +struct SchedulingResult; +struct LoadResult; +struct StoreResult; + +// GenMC uses `int` for its thread IDs. +using ThreadId = int; + +/// Set the log level for GenMC. +/// +/// # Safety +/// +/// This function is not thread safe, since it writes to the global, mutable, non-atomic `logLevel` +/// variable. Any GenMC function may read from `logLevel` unsynchronized. +/// The safest way to use this function is to set the log level exactly once before first calling +/// `create_handle`. +/// Never calling this function is safe, GenMC will fall back to its default log level. +/* unsafe */ void set_log_level_raw(LogLevel log_level); + +struct MiriGenmcShim : private GenMCDriver { + + public: + MiriGenmcShim(std::shared_ptr conf, Mode mode /* = VerificationMode{} */) + : GenMCDriver(std::move(conf), nullptr, mode) {} + + /// Create a new `MiriGenmcShim`, which wraps a `GenMCDriver`. + /// + /// # Safety + /// + /// This function is marked as unsafe since the `logLevel` global variable is non-atomic. + /// This function should not be called in an unsynchronized way with `set_log_level_raw`, + /// since this function and any methods on the returned `MiriGenmcShim` may read the + /// `logLevel`, causing a data race. The safest way to use these functions is to call + /// `set_log_level_raw` once, and only then start creating handles. There should not be any + /// other (safe) way to create a `MiriGenmcShim`. + /* unsafe */ static auto create_handle(const GenmcParams& params) + -> std::unique_ptr; + + virtual ~MiriGenmcShim() {} + + /**** Execution start/end handling ****/ + + // This function must be called at the start of any execution, before any events are + // reported to GenMC. + void handle_execution_start(); + // This function must be called at the end of any execution, even if an error was found + // during the execution. + // Returns `null`, or a string containing an error message if an error occured. + std::unique_ptr handle_execution_end(); + + /***** Functions for handling events encountered during program execution. *****/ + + /**** Memory access handling ****/ + + [[nodiscard]] LoadResult handle_load( + ThreadId thread_id, + uint64_t address, + uint64_t size, + MemOrdering ord, + GenmcScalar old_val + ); + [[nodiscard]] StoreResult handle_store( + ThreadId thread_id, + uint64_t address, + uint64_t size, + GenmcScalar value, + GenmcScalar old_val, + MemOrdering ord + ); + + /**** Memory (de)allocation ****/ + auto handle_malloc(ThreadId thread_id, uint64_t size, uint64_t alignment) -> uint64_t; + void handle_free(ThreadId thread_id, uint64_t address); + + /**** Thread management ****/ + void handle_thread_create(ThreadId thread_id, ThreadId parent_id); + void handle_thread_join(ThreadId thread_id, ThreadId child_id); + void handle_thread_finish(ThreadId thread_id, uint64_t ret_val); + void handle_thread_kill(ThreadId thread_id); + + /***** Exploration related functionality *****/ + + /** Ask the GenMC scheduler for a new thread to schedule and return whether the execution is + * finished, blocked, or can continue. + * Updates the next instruction kind for the given thread id. */ + auto schedule_next(const int curr_thread_id, const ActionKind curr_thread_next_instr_kind) + -> SchedulingResult; + + /** + * Check whether there are more executions to explore. + * If there are more executions, this method prepares for the next execution and returns + * `true`. Returns true if there are no more executions to explore. */ + auto is_exploration_done() -> bool { + return GenMCDriver::done(); + } + + /**** Result querying functionality. ****/ + + // NOTE: We don't want to share the `VerificationResult` type with the Rust side, since it + // is very large, uses features that CXX.rs doesn't support and may change as GenMC changes. + // Instead, we only use the result on the C++ side, and only expose these getter function to + // the Rust side. + + // Note that CXX.rs doesn't support returning a C++ string to Rust by value, + // it must be behind an indirection like a `unique_ptr` (tested with CXX 1.0.170). + + /// Get the number of blocked executions encountered by GenMC (cast into a fixed with + /// integer) + auto get_blocked_execution_count() const -> uint64_t { + return static_cast(getResult().exploredBlocked); + } + + /// Get the number of executions explored by GenMC (cast into a fixed with integer) + auto get_explored_execution_count() const -> uint64_t { + return static_cast(getResult().explored); + } + + /// Get all messages that GenMC produced (errors, warnings), combined into one string. + auto get_result_message() const -> std::unique_ptr { + return std::make_unique(getResult().message); + } + + /// If an error occurred, return a string describing the error, otherwise, return `nullptr`. + auto get_error_string() const -> std::unique_ptr { + const auto& result = GenMCDriver::getResult(); + if (result.status.has_value()) + return format_error(result.status.value()); + return nullptr; + } + + private: + /** Increment the event index in the given thread by 1 and return the new event. */ + [[nodiscard]] inline auto inc_pos(ThreadId tid) -> Event { + ERROR_ON(tid >= threads_action_.size(), "ThreadId out of bounds"); + return ++threads_action_[tid].event; + } + /** Decrement the event index in the given thread by 1 and return the new event. */ + inline auto dec_pos(ThreadId tid) -> Event { + ERROR_ON(tid >= threads_action_.size(), "ThreadId out of bounds"); + return --threads_action_[tid].event; + } + + /** + * Helper function for loads that need to reset the event counter when no value is returned. + * Same syntax as `GenMCDriver::handleLoad`, but this takes a thread id instead of an Event. + * Automatically calls `inc_pos` and `dec_pos` where needed for the given thread. + */ + template + auto handle_load_reset_if_none(ThreadId tid, Ts&&... params) -> HandleResult { + const auto pos = inc_pos(tid); + const auto ret = GenMCDriver::handleLoad(pos, std::forward(params)...); + // If we didn't get a value, we have to reset the index of the current thread. + if (!std::holds_alternative(ret)) { + dec_pos(tid); + } + return ret; + } + + /** + * GenMC uses the term `Action` to refer to a struct of: + * - `ActionKind`, storing whether the next instruction in a thread may be a load + * - `Event`, storing the most recent event index added for a thread + * + * Here we store the "action" for each thread. In particular we use this to assign event + * indices, since GenMC expects us to do that. + */ + std::vector threads_action_; +}; + +/// Get the bit mask that GenMC expects for global memory allocations. +/// FIXME(genmc): currently we use `get_global_alloc_static_mask()` to ensure the +/// `SAddr::staticMask` constant is consistent between Miri and GenMC, but if +/// https://github.com/dtolnay/cxx/issues/1051 is fixed we could share the constant +/// directly. +constexpr auto get_global_alloc_static_mask() -> uint64_t { + return SAddr::staticMask; +} + +// CXX.rs generated headers: +// NOTE: this must be included *after* `MiriGenmcShim` and all the other types we use are defined, +// otherwise there will be compilation errors due to missing definitions. +#include "genmc-sys/src/lib.rs.h" + +/**** Result handling ****/ +// NOTE: these must come after the cxx_bridge generated code, since that contains the actual +// definitions of these types. + +namespace GenmcScalarExt { +inline GenmcScalar uninit() { + return GenmcScalar { + /* value: */ 0, + /* is_init: */ false, + }; +} + +inline GenmcScalar from_sval(SVal sval) { + return GenmcScalar { + /* value: */ sval.get(), + /* is_init: */ true, + }; +} + +inline SVal to_sval(GenmcScalar scalar) { + ERROR_ON(!scalar.is_init, "Cannot convert an uninitialized `GenmcScalar` into an `SVal`\n"); + return SVal(scalar.value); +} +} // namespace GenmcScalarExt + +namespace LoadResultExt { +inline LoadResult no_value() { + return LoadResult { + /* error: */ std::unique_ptr(nullptr), + /* has_value: */ false, + /* read_value: */ GenmcScalarExt::uninit(), + }; +} + +inline LoadResult from_value(SVal read_value) { + return LoadResult { /* error: */ std::unique_ptr(nullptr), + /* has_value: */ true, + /* read_value: */ GenmcScalarExt::from_sval(read_value) }; +} + +inline LoadResult from_error(std::unique_ptr error) { + return LoadResult { /* error: */ std::move(error), + /* has_value: */ false, + /* read_value: */ GenmcScalarExt::uninit() }; +} +} // namespace LoadResultExt + +namespace StoreResultExt { +inline StoreResult ok(bool is_coherence_order_maximal_write) { + return StoreResult { /* error: */ std::unique_ptr(nullptr), + is_coherence_order_maximal_write }; +} + +inline StoreResult from_error(std::unique_ptr error) { + return StoreResult { /* error: */ std::move(error), + /* is_coherence_order_maximal_write: */ false }; +} +} // namespace StoreResultExt + +#endif /* GENMC_MIRI_INTERFACE_HPP */ diff --git a/src/tools/miri/genmc-sys/cpp/include/ResultHandling.hpp b/src/tools/miri/genmc-sys/cpp/include/ResultHandling.hpp new file mode 100644 index 000000000000..189f32e6f513 --- /dev/null +++ b/src/tools/miri/genmc-sys/cpp/include/ResultHandling.hpp @@ -0,0 +1,21 @@ +#ifndef GENMC_RESULT_HANDLING_HPP +#define GENMC_RESULT_HANDLING_HPP + +// CXX.rs generated headers: +#include "rust/cxx.h" + +// GenMC headers: +#include "Verification/VerificationError.hpp" + +#include + +/** Information about an error, formatted as a string to avoid having to share an error enum and + * printing functionality with the Rust side. */ +static auto format_error(VerificationError err) -> std::unique_ptr { + auto buf = std::string(); + auto s = llvm::raw_string_ostream(buf); + s << err; + return std::make_unique(s.str()); +} + +#endif /* GENMC_RESULT_HANDLING_HPP */ diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp new file mode 100644 index 000000000000..7d8f0b16b49a --- /dev/null +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp @@ -0,0 +1,120 @@ +/** This file contains functionality related to handling events encountered + * during an execution, such as loads, stores or memory (de)allocation. */ + +#include "MiriInterface.hpp" + +// CXX.rs generated headers: +#include "genmc-sys/src/lib.rs.h" + +// GenMC headers: +#include "ADT/value_ptr.hpp" +#include "ExecutionGraph/EventLabel.hpp" +#include "ExecutionGraph/LoadAnnotation.hpp" +#include "Runtime/InterpreterEnumAPI.hpp" +#include "Static/ModuleID.hpp" +#include "Support/ASize.hpp" +#include "Support/Error.hpp" +#include "Support/Logger.hpp" +#include "Support/MemAccess.hpp" +#include "Support/RMWOps.hpp" +#include "Support/SAddr.hpp" +#include "Support/SVal.hpp" +#include "Support/ThreadInfo.hpp" +#include "Support/Verbosity.hpp" +#include "Verification/GenMCDriver.hpp" +#include "Verification/MemoryModel.hpp" + +// C++ headers: +#include +#include +#include +#include + +/**** Memory access handling ****/ + +[[nodiscard]] auto MiriGenmcShim::handle_load( + ThreadId thread_id, + uint64_t address, + uint64_t size, + MemOrdering ord, + GenmcScalar old_val +) -> LoadResult { + // `type` is only used for printing. + const auto type = AType::Unsigned; + const auto ret = handle_load_reset_if_none( + thread_id, + ord, + SAddr(address), + ASize(size), + type + ); + + if (const auto* err = std::get_if(&ret)) + return LoadResultExt::from_error(format_error(*err)); + const auto* ret_val = std::get_if(&ret); + if (ret_val == nullptr) + ERROR("Unimplemented: load returned unexpected result."); + return LoadResultExt::from_value(*ret_val); +} + +[[nodiscard]] auto MiriGenmcShim::handle_store( + ThreadId thread_id, + uint64_t address, + uint64_t size, + GenmcScalar value, + GenmcScalar old_val, + MemOrdering ord +) -> StoreResult { + const auto pos = inc_pos(thread_id); + const auto ret = GenMCDriver::handleStore( + pos, + ord, + SAddr(address), + ASize(size), + /* type */ AType::Unsigned, // `type` is only used for printing. + GenmcScalarExt::to_sval(value), + EventDeps() + ); + + if (const auto* err = std::get_if(&ret)) + return StoreResultExt::from_error(format_error(*err)); + if (!std::holds_alternative(ret)) + ERROR("store returned unexpected result"); + + // FIXME(genmc,mixed-accesses): Use the value that GenMC returns from handleStore (once + // available). + const auto& g = getExec().getGraph(); + return StoreResultExt::ok( + /* is_coherence_order_maximal_write */ g.co_max(SAddr(address))->getPos() == pos + ); +} + +/**** Memory (de)allocation ****/ + +auto MiriGenmcShim::handle_malloc(ThreadId thread_id, uint64_t size, uint64_t alignment) + -> uint64_t { + const auto pos = inc_pos(thread_id); + + // These are only used for printing and features Miri-GenMC doesn't support (yet). + const auto storage_duration = StorageDuration::SD_Heap; + // Volatile, as opposed to "persistent" (i.e., non-volatile memory that persists over reboots) + const auto storage_type = StorageType::ST_Volatile; + const auto address_space = AddressSpace::AS_User; + + const SVal ret_val = GenMCDriver::handleMalloc( + pos, + size, + alignment, + storage_duration, + storage_type, + address_space, + EventDeps() + ); + return ret_val.get(); +} + +void MiriGenmcShim::handle_free(ThreadId thread_id, uint64_t address) { + const auto pos = inc_pos(thread_id); + GenMCDriver::handleFree(pos, SAddr(address), EventDeps()); + // FIXME(genmc): add error handling once GenMC returns errors from `handleFree` +} diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Exploration.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Exploration.cpp new file mode 100644 index 000000000000..c51b59e86517 --- /dev/null +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Exploration.cpp @@ -0,0 +1,42 @@ +/** This file contains functionality related to exploration, such as scheduling. */ + +#include "MiriInterface.hpp" + +// CXX.rs generated headers: +#include "genmc-sys/src/lib.rs.h" + +// GenMC headers: +#include "Support/Error.hpp" +#include "Support/Verbosity.hpp" + +// C++ headers: +#include + +auto MiriGenmcShim::schedule_next( + const int curr_thread_id, + const ActionKind curr_thread_next_instr_kind +) -> SchedulingResult { + // The current thread is the only one where the `kind` could have changed since we last made + // a scheduling decision. + threads_action_[curr_thread_id].kind = curr_thread_next_instr_kind; + + if (const auto result = GenMCDriver::scheduleNext(threads_action_)) + return SchedulingResult { ExecutionState::Ok, static_cast(result.value()) }; + if (GenMCDriver::isExecutionBlocked()) + return SchedulingResult { ExecutionState::Blocked, 0 }; + return SchedulingResult { ExecutionState::Finished, 0 }; +} + +/**** Execution start/end handling ****/ + +void MiriGenmcShim::handle_execution_start() { + threads_action_.clear(); + threads_action_.push_back(Action(ActionKind::Load, Event::getInit())); + GenMCDriver::handleExecutionStart(); +} + +auto MiriGenmcShim::handle_execution_end() -> std::unique_ptr { + // FIXME(genmc): add error handling once GenMC returns an error here. + GenMCDriver::handleExecutionEnd(); + return {}; +} diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp new file mode 100644 index 000000000000..7194ed02da43 --- /dev/null +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp @@ -0,0 +1,185 @@ +/** This file contains functionality related to creation of the GenMCDriver, + * including translating settings set by Miri. */ + +#include "MiriInterface.hpp" + +// CXX.rs generated headers: +#include "genmc-sys/src/lib.rs.h" + +// GenMC headers: +#include "Support/Error.hpp" +#include "Support/Verbosity.hpp" +#include "Verification/InterpreterCallbacks.hpp" + +// C++ headers: +#include + +/** + * Translate the Miri-GenMC `LogLevel` to the GenMC `VerbosityLevel`. + * Downgrade any debug options to `Tip` if `ENABLE_GENMC_DEBUG` is not enabled. + */ +static auto to_genmc_verbosity_level(const LogLevel log_level) -> VerbosityLevel { + switch (log_level) { + case LogLevel::Quiet: + return VerbosityLevel::Quiet; + case LogLevel::Error: + return VerbosityLevel::Error; + case LogLevel::Warning: + return VerbosityLevel::Warning; + case LogLevel::Tip: + return VerbosityLevel::Tip; +#ifdef ENABLE_GENMC_DEBUG + case LogLevel::Debug1Revisits: + return VerbosityLevel::Debug1; + case LogLevel::Debug2MemoryAccesses: + return VerbosityLevel::Debug2; + case LogLevel::Debug3ReadsFrom: + return VerbosityLevel::Debug3; +#else + // Downgrade to `Tip` if the debug levels are not available. + case LogLevel::Debug1Revisits: + case LogLevel::Debug2MemoryAccesses: + case LogLevel::Debug3ReadsFrom: + return VerbosityLevel::Tip; +#endif + default: + WARN_ONCE( + "unknown-log-level", + "Unknown `LogLevel`, defaulting to `VerbosityLevel::Tip`." + ); + return VerbosityLevel::Tip; + } +} + +/* unsafe */ void set_log_level_raw(LogLevel log_level) { + // The `logLevel` is a static, non-atomic variable. + // It should never be changed if `MiriGenmcShim` still exists, since any of its methods may read + // the `logLevel`, otherwise it may cause data races. + logLevel = to_genmc_verbosity_level(log_level); +} + +/* unsafe */ auto MiriGenmcShim::create_handle(const GenmcParams& params) + -> std::unique_ptr { + auto conf = std::make_shared(); + + // Set whether GenMC should print execution graphs after every explored/blocked execution. + // FIXME(genmc): pass these settings from Miri. + conf->printExecGraphs = false; + conf->printBlockedExecs = false; + + // `1024` is the default value that GenMC uses. + // If any thread has at least this many events, a warning/tip will be printed. + // + // Miri produces a lot more events than GenMC, so the graph size warning triggers on almost + // all programs. The current value is large enough so the warning is not be triggered by any + // reasonable programs. + // FIXME(genmc): The emitted warning mentions features not supported by Miri ('--unroll' + // parameter). + // FIXME(genmc): A more appropriate limit should be chosen once the warning is useful for + // Miri. + conf->warnOnGraphSize = 1024 * 1024; + + // We only support the RC11 memory model for Rust. + conf->model = ModelType::RC11; + + // This prints the seed that GenMC picks for randomized scheduling during estimation mode. + conf->printRandomScheduleSeed = params.print_random_schedule_seed; + + // FIXME(genmc): supporting IPR requires annotations for `assume` and `spinloops`. + conf->ipr = false; + // FIXME(genmc): supporting BAM requires `Barrier` support + detecting whether return value + // of barriers are used. + conf->disableBAM = true; + + // Instruction caching could help speed up verification by filling the graph from cache, if + // the list of values read by all load events in a thread have been seen before. Combined + // with not replaying completed threads, this can also reducing the amount of Mir + // interpretation required by Miri. With the current setup, this would be incorrect, since + // Miri doesn't give GenMC the actual values read by non-atomic reads. + conf->instructionCaching = false; + // Many of Miri's checks work under the assumption that threads are only executed in an + // order that could actually happen during a normal execution. Formally, this means that + // replaying an execution needs to respect the po-rf-relation of the executiongraph (po == + // program-order, rf == reads-from). This means, any event in the graph, when replayed, must + // happen after any events that happen before it in the same graph according to the program + // code, and all (non-atomic) reads must happen after the write event they read from. + // + // Not replaying completed threads means any read event from that thread never happens in + // Miri's memory, so this would only work if there are never any non-atomic reads from any + // value written by the skipped thread. + conf->replayCompletedThreads = true; + + // FIXME(genmc): implement symmetry reduction. + ERROR_ON( + params.do_symmetry_reduction, + "Symmetry reduction is currently unsupported in GenMC mode." + ); + conf->symmetryReduction = params.do_symmetry_reduction; + + // FIXME(genmc): expose this setting to Miri (useful for testing Miri-GenMC). + conf->schedulePolicy = SchedulePolicy::WF; + + // Set the mode used for this driver, either estimation or verification. + // FIXME(genmc): implement estimation mode. + const auto mode = GenMCDriver::Mode(GenMCDriver::VerificationMode {}); + + // Running Miri-GenMC without race detection is not supported. + // Disabling this option also changes the behavior of the replay scheduler to only schedule + // at atomic operations, which is required with Miri. This happens because Miri can generate + // multiple GenMC events for a single MIR terminator. Without this option, the scheduler + // might incorrectly schedule an atomic MIR terminator because the first event it creates is + // a non-atomic (e.g., `StorageLive`). + conf->disableRaceDetection = false; + + // Miri can already check for unfreed memory. Also, GenMC cannot distinguish between memory + // that is allowed to leak and memory that is not. + conf->warnUnfreedMemory = false; + + // FIXME(genmc,error handling): This function currently exits on error, but will return an + // error value in the future. The return value should be checked once this change is made. + checkConfig(*conf); + + // Create the actual driver and Miri-GenMC communication shim. + auto driver = std::make_unique(std::move(conf), mode); + + // FIXME(genmc,HACK): Until a proper solution is implemented in GenMC, these callbacks will + // allow Miri to return information about global allocations and override uninitialized memory + // checks for non-atomic loads (Miri handles those without GenMC, so the error would be wrong). + auto interpreter_callbacks = InterpreterCallbacks { + // Miri already ensures that memory accesses are valid, so this check doesn't matter. + // We check that the address is static, but skip checking if it is part of an actual + // allocation. + /* isStaticallyAllocated: */ [](SAddr addr) { return addr.isStatic(); }, + // FIXME(genmc,error reporting): Once a proper a proper API for passing such information is + // implemented in GenMC, Miri should use it to improve the produced error messages. + /* getStaticName: */ [](SAddr addr) { return "[UNKNOWN STATIC]"; }, + // This function is called to get the initial value stored at the given address. + // + // From a Miri perspective, this API doesn't work very well: most memory starts out + // "uninitialized"; + // only statics have an initial value. And their initial value is just a sequence of bytes, + // but GenMC + // expect this to be already split into separate atomic variables. So we return a dummy + // value. + // This value should never be visible to the interpreted program. + // GenMC does not understand uninitialized memory the same way Miri does, which may cause + // this function to be called. The returned value can be visible to Miri or the user: + // - Printing the execution graph may contain this value in place of uninitialized values. + // FIXME(genmc): NOTE: printing the execution graph is not yet implemented. + // - Non-atomic loads may return this value, but Miri ignores values of non-atomic loads. + // - Atomic loads will *not* see this value once mixed atomic-non-atomic support is added. + // Currently, atomic loads can see this value, unless initialized by an *atomic* store. + // FIXME(genmc): update this comment once mixed atomic-non-atomic support is added. + // + // FIXME(genmc): implement proper support for uninitialized memory in GenMC. Ideally, the + // initial value getter would return an `optional`, since the memory location may be + // uninitialized. + /* initValGetter: */ [](const AAccess& a) { return SVal(0xDEAD); }, + // Miri serves non-atomic loads from its own memory and these GenMC checks are wrong in + // that case. This should no longer be required with proper mixed-size access support. + /* skipUninitLoadChecks: */ [](MemOrdering ord) { return ord == MemOrdering::NotAtomic; }, + }; + driver->setInterpCallbacks(std::move(interpreter_callbacks)); + + return driver; +} diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/ThreadManagement.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/ThreadManagement.cpp new file mode 100644 index 000000000000..352d27adc3e8 --- /dev/null +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/ThreadManagement.cpp @@ -0,0 +1,54 @@ + +/** This file contains functionality related thread management (creation, finishing, join, etc.) */ + +#include "MiriInterface.hpp" + +// CXX.rs generated headers: +#include "genmc-sys/src/lib.rs.h" + +// GenMC headers: +#include "Support/Error.hpp" +#include "Support/Verbosity.hpp" + +// C++ headers: +#include + +void MiriGenmcShim::handle_thread_create(ThreadId thread_id, ThreadId parent_id) { + // NOTE: The threadCreate event happens in the parent: + const auto pos = inc_pos(parent_id); + // FIXME(genmc): for supporting symmetry reduction, these will need to be properly set: + const unsigned fun_id = 0; + const SVal arg = SVal(0); + const ThreadInfo child_info = ThreadInfo { thread_id, parent_id, fun_id, arg }; + + // NOTE: Default memory ordering (`Release`) used here. + const auto child_tid = GenMCDriver::handleThreadCreate(pos, child_info, EventDeps()); + // Sanity check the thread id, which is the index in the `threads_action_` array. + BUG_ON(child_tid != thread_id || child_tid <= 0 || child_tid != threads_action_.size()); + threads_action_.push_back(Action(ActionKind::Load, Event(child_tid, 0))); +} + +void MiriGenmcShim::handle_thread_join(ThreadId thread_id, ThreadId child_id) { + // The thread join event happens in the parent. + const auto pos = inc_pos(thread_id); + + // NOTE: Default memory ordering (`Acquire`) used here. + const auto ret = GenMCDriver::handleThreadJoin(pos, child_id, EventDeps()); + // If the join failed, decrease the event index again: + if (!std::holds_alternative(ret)) { + dec_pos(thread_id); + } + + // NOTE: Thread return value is ignored, since Miri doesn't need it. +} + +void MiriGenmcShim::handle_thread_finish(ThreadId thread_id, uint64_t ret_val) { + const auto pos = inc_pos(thread_id); + // NOTE: Default memory ordering (`Release`) used here. + GenMCDriver::handleThreadFinish(pos, SVal(ret_val)); +} + +void MiriGenmcShim::handle_thread_kill(ThreadId thread_id) { + const auto pos = inc_pos(thread_id); + GenMCDriver::handleThreadKill(pos); +} diff --git a/src/tools/miri/genmc-sys/src/lib.rs b/src/tools/miri/genmc-sys/src/lib.rs index ab46d729ea16..406c90809fcd 100644 --- a/src/tools/miri/genmc-sys/src/lib.rs +++ b/src/tools/miri/genmc-sys/src/lib.rs @@ -1,17 +1,90 @@ +use std::str::FromStr; +use std::sync::OnceLock; + +pub use cxx::UniquePtr; + pub use self::ffi::*; +/// Defined in "genmc/src/Support/SAddr.hpp". +/// The first bit of all global addresses must be set to `1`. +/// This means the mask, interpreted as an address, is the lower bound of where the global address space starts. +/// +/// FIXME(genmc): rework this if non-64bit support is added to GenMC (the current allocation scheme only allows for 64bit addresses). +/// FIXME(genmc): currently we use `get_global_alloc_static_mask()` to ensure the constant is consistent between Miri and GenMC, +/// but if https://github.com/dtolnay/cxx/issues/1051 is fixed we could share the constant directly. +pub const GENMC_GLOBAL_ADDRESSES_MASK: u64 = 1 << 63; + +/// GenMC thread ids are C++ type `int`, which is equivalent to Rust's `i32` on most platforms. +/// The main thread always has thread id 0. +pub const GENMC_MAIN_THREAD_ID: i32 = 0; + +/// Changing GenMC's log level is not thread safe, so we limit it to only be set once to prevent any data races. +/// This value will be initialized when the first `MiriGenmcShim` is created. +static GENMC_LOG_LEVEL: OnceLock = OnceLock::new(); + +// Create a new handle to the GenMC model checker. +// The first call to this function determines the log level of GenMC, any future call with a different log level will panic. +pub fn create_genmc_driver_handle( + params: &GenmcParams, + genmc_log_level: LogLevel, +) -> UniquePtr { + // SAFETY: Only setting the GenMC log level once is guaranteed by the `OnceLock`. + // No other place calls `set_log_level_raw`, so the `logLevel` value in GenMC will not change once we initialize it once. + // All functions that use GenMC's `logLevel` can only be accessed in safe Rust through a `MiriGenmcShim`. + // There is no way to get `MiriGenmcShim` other than through `create_handle`, and we only call it *after* setting the log level, preventing any possible data races. + assert_eq!( + &genmc_log_level, + GENMC_LOG_LEVEL.get_or_init(|| { + unsafe { set_log_level_raw(genmc_log_level) }; + genmc_log_level + }), + "Attempt to change the GenMC log level after it was already set" + ); + unsafe { MiriGenmcShim::create_handle(params) } +} + +impl GenmcScalar { + pub const UNINIT: Self = Self { value: 0, is_init: false }; + + pub const fn from_u64(value: u64) -> Self { + Self { value, is_init: true } + } +} + impl Default for GenmcParams { fn default() -> Self { - Self { - print_random_schedule_seed: false, - do_symmetry_reduction: false, - // FIXME(GenMC): Add defaults for remaining parameters - } + Self { print_random_schedule_seed: false, do_symmetry_reduction: false } + } +} + +impl Default for LogLevel { + fn default() -> Self { + // FIXME(genmc): set `Tip` by default once the GenMC tips are relevant to Miri. + Self::Warning + } +} + +impl FromStr for LogLevel { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + Ok(match s { + "quiet" => LogLevel::Quiet, + "error" => LogLevel::Error, + "warning" => LogLevel::Warning, + "tip" => LogLevel::Tip, + "debug1" => LogLevel::Debug1Revisits, + "debug2" => LogLevel::Debug2MemoryAccesses, + "debug3" => LogLevel::Debug3ReadsFrom, + _ => return Err("invalid log level"), + }) } } #[cxx::bridge] mod ffi { + /**** Types shared between Miri/Rust and Miri/C++ through cxx_bridge: ****/ + /// Parameters that will be given to GenMC for setting up the model checker. /// (The fields of this struct are visible to both Rust and C++) #[derive(Clone, Debug)] @@ -20,11 +93,221 @@ mod ffi { pub do_symmetry_reduction: bool, // FIXME(GenMC): Add remaining parameters. } + + /// This is mostly equivalent to GenMC `VerbosityLevel`, but the debug log levels are always present (not conditionally compiled based on `ENABLE_GENMC_DEBUG`). + /// We add this intermediate type to prevent changes to the GenMC log-level from breaking the Miri + /// build, and to have a stable type for the C++-Rust interface, independent of `ENABLE_GENMC_DEBUG`. + #[derive(Debug)] + enum LogLevel { + /// Disable *all* logging (including error messages on a crash). + Quiet, + /// Log errors. + Error, + /// Log errors and warnings. + Warning, + /// Log errors, warnings and tips. + Tip, + /// Debug print considered revisits. + /// Downgraded to `Tip` if `GENMC_DEBUG` is not enabled. + Debug1Revisits, + /// Print the execution graph after every memory access. + /// Also includes the previous debug log level. + /// Downgraded to `Tip` if `GENMC_DEBUG` is not enabled. + Debug2MemoryAccesses, + /// Print reads-from values considered by GenMC. + /// Also includes the previous debug log level. + /// Downgraded to `Tip` if `GENMC_DEBUG` is not enabled. + Debug3ReadsFrom, + } + + /// This type corresponds to `Option` (or `std::optional`), where `SVal` is the type that GenMC uses for storing values. + /// CXX doesn't support `std::optional` currently, so we need to use an extra `bool` to define whether this value is initialized or not. + #[derive(Debug, Clone, Copy)] + struct GenmcScalar { + value: u64, + is_init: bool, + } + + #[must_use] + #[derive(Debug, Clone, Copy)] + enum ExecutionState { + Ok, + Blocked, + Finished, + } + + #[must_use] + #[derive(Debug)] + struct SchedulingResult { + exec_state: ExecutionState, + next_thread: i32, + } + + #[must_use] + #[derive(Debug)] + struct LoadResult { + /// If not null, contains the error encountered during the handling of the load. + error: UniquePtr, + /// Indicates whether a value was read or not. + has_value: bool, + /// The value that was read. Should not be used if `has_value` is `false`. + read_value: GenmcScalar, + } + + #[must_use] + #[derive(Debug)] + struct StoreResult { + /// If not null, contains the error encountered during the handling of the store. + error: UniquePtr, + /// `true` if the write should also be reflected in Miri's memory representation. + is_coherence_order_maximal_write: bool, + } + + /**** Types shared between Miri/Rust and GenMC/C++ through cxx_bridge: ****/ + + #[derive(Debug)] + /// Corresponds to GenMC's type with the same name. + /// Should only be modified if changed by GenMC. + enum ActionKind { + /// Any Mir terminator that's atomic and has load semantics. + Load, + /// Anything that's not a `Load`. + NonLoad, + } + + #[derive(Debug)] + /// Corresponds to GenMC's type with the same name. + /// Should only be modified if changed by GenMC. + enum MemOrdering { + NotAtomic = 0, + Relaxed = 1, + // We skip 2 in case we support consume. + Acquire = 3, + Release = 4, + AcquireRelease = 5, + SequentiallyConsistent = 6, + } + + // # Safety + // + // This block is unsafe to allow defining safe methods inside. + // + // `get_global_alloc_static_mask` is safe since it just returns a constant. + // All methods on `MiriGenmcShim` are safe by the correct usage of the two unsafe functions + // `set_log_level_raw` and `MiriGenmcShim::create_handle`. + // See the doc comment on those two functions for their safety requirements. unsafe extern "C++" { include!("MiriInterface.hpp"); - type MiriGenMCShim; + /**** Types shared between Miri/Rust and Miri/C++: ****/ + type MiriGenmcShim; - fn createGenmcHandle(config: &GenmcParams) -> UniquePtr; + /**** Types shared between Miri/Rust and GenMC/C++: ****/ + type ActionKind; + type MemOrdering; + + /// Set the log level for GenMC. + /// + /// # Safety + /// + /// This function is not thread safe, since it writes to the global, mutable, non-atomic `logLevel` variable. + /// Any GenMC function may read from `logLevel` unsynchronized. + /// The safest way to use this function is to set the log level exactly once before first calling `create_handle`. + /// Never calling this function is safe, GenMC will fall back to its default log level. + unsafe fn set_log_level_raw(log_level: LogLevel); + + /// Create a new `MiriGenmcShim`, which wraps a `GenMCDriver`. + /// + /// # Safety + /// + /// This function is marked as unsafe since the `logLevel` global variable is non-atomic. + /// This function should not be called in an unsynchronized way with `set_log_level_raw`, since + /// this function and any methods on the returned `MiriGenmcShim` may read the `logLevel`, + /// causing a data race. + /// The safest way to use these functions is to call `set_log_level_raw` once, and only then + /// start creating handles. + /// There should not be any other (safe) way to create a `MiriGenmcShim`. + #[Self = "MiriGenmcShim"] + unsafe fn create_handle(params: &GenmcParams) -> UniquePtr; + /// Get the bit mask that GenMC expects for global memory allocations. + fn get_global_alloc_static_mask() -> u64; + + /// This function must be called at the start of any execution, before any events are reported to GenMC. + fn handle_execution_start(self: Pin<&mut MiriGenmcShim>); + /// This function must be called at the end of any execution, even if an error was found during the execution. + /// Returns `null`, or a string containing an error message if an error occured. + fn handle_execution_end(self: Pin<&mut MiriGenmcShim>) -> UniquePtr; + + /***** Functions for handling events encountered during program execution. *****/ + + /**** Memory access handling ****/ + fn handle_load( + self: Pin<&mut MiriGenmcShim>, + thread_id: i32, + address: u64, + size: u64, + memory_ordering: MemOrdering, + old_value: GenmcScalar, + ) -> LoadResult; + fn handle_store( + self: Pin<&mut MiriGenmcShim>, + thread_id: i32, + address: u64, + size: u64, + value: GenmcScalar, + old_value: GenmcScalar, + memory_ordering: MemOrdering, + ) -> StoreResult; + + /**** Memory (de)allocation ****/ + fn handle_malloc( + self: Pin<&mut MiriGenmcShim>, + thread_id: i32, + size: u64, + alignment: u64, + ) -> u64; + fn handle_free(self: Pin<&mut MiriGenmcShim>, thread_id: i32, address: u64); + + /**** Thread management ****/ + fn handle_thread_create(self: Pin<&mut MiriGenmcShim>, thread_id: i32, parent_id: i32); + fn handle_thread_join(self: Pin<&mut MiriGenmcShim>, thread_id: i32, child_id: i32); + fn handle_thread_finish(self: Pin<&mut MiriGenmcShim>, thread_id: i32, ret_val: u64); + fn handle_thread_kill(self: Pin<&mut MiriGenmcShim>, thread_id: i32); + + /***** Exploration related functionality *****/ + + /// Ask the GenMC scheduler for a new thread to schedule and + /// return whether the execution is finished, blocked, or can continue. + /// Updates the next instruction kind for the given thread id. + fn schedule_next( + self: Pin<&mut MiriGenmcShim>, + curr_thread_id: i32, + curr_thread_next_instr_kind: ActionKind, + ) -> SchedulingResult; + + /// Check whether there are more executions to explore. + /// If there are more executions, this method prepares for the next execution and returns `true`. + fn is_exploration_done(self: Pin<&mut MiriGenmcShim>) -> bool; + + /**** Result querying functionality. ****/ + + // NOTE: We don't want to share the `VerificationResult` type with the Rust side, since it + // is very large, uses features that CXX.rs doesn't support and may change as GenMC changes. + // Instead, we only use the result on the C++ side, and only expose these getter function to + // the Rust side. + // Each `GenMCDriver` contains one `VerificationResult`, and each `MiriGenmcShim` contains on `GenMCDriver`. + // GenMC builds up the content of the `struct VerificationResult` over the course of an exploration, + // but it's safe to look at it at any point, since it is only accessible through exactly one `MiriGenmcShim`. + // All these functions for querying the result can be safely called repeatedly and at any time, + // though the results may be incomplete if called before `handle_execution_end`. + + /// Get the number of blocked executions encountered by GenMC (cast into a fixed with integer) + fn get_blocked_execution_count(self: &MiriGenmcShim) -> u64; + /// Get the number of executions explored by GenMC (cast into a fixed with integer) + fn get_explored_execution_count(self: &MiriGenmcShim) -> u64; + /// Get all messages that GenMC produced (errors, warnings), combined into one string. + fn get_result_message(self: &MiriGenmcShim) -> UniquePtr; + /// If an error occurred, return a string describing the error, otherwise, return `nullptr`. + fn get_error_string(self: &MiriGenmcShim) -> UniquePtr; } } diff --git a/src/tools/miri/genmc-sys/src_cpp/MiriInterface.cpp b/src/tools/miri/genmc-sys/src_cpp/MiriInterface.cpp deleted file mode 100644 index 0827bb3d4074..000000000000 --- a/src/tools/miri/genmc-sys/src_cpp/MiriInterface.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "MiriInterface.hpp" - -#include "genmc-sys/src/lib.rs.h" - -auto MiriGenMCShim::createHandle(const GenmcParams &config) - -> std::unique_ptr -{ - auto conf = std::make_shared(); - - // Miri needs all threads to be replayed, even fully completed ones. - conf->replayCompletedThreads = true; - - // We only support the RC11 memory model for Rust. - conf->model = ModelType::RC11; - - conf->printRandomScheduleSeed = config.print_random_schedule_seed; - - // FIXME(genmc): disable any options we don't support currently: - conf->ipr = false; - conf->disableBAM = true; - conf->instructionCaching = false; - - ERROR_ON(config.do_symmetry_reduction, "Symmetry reduction is currently unsupported in GenMC mode."); - conf->symmetryReduction = config.do_symmetry_reduction; - - // FIXME(genmc): Should there be a way to change this option from Miri? - conf->schedulePolicy = SchedulePolicy::WF; - - // FIXME(genmc): implement estimation mode: - conf->estimate = false; - conf->estimationMax = 1000; - const auto mode = conf->estimate ? GenMCDriver::Mode(GenMCDriver::EstimationMode{}) - : GenMCDriver::Mode(GenMCDriver::VerificationMode{}); - - // Running Miri-GenMC without race detection is not supported. - // Disabling this option also changes the behavior of the replay scheduler to only schedule at atomic operations, which is required with Miri. - // This happens because Miri can generate multiple GenMC events for a single MIR terminator. Without this option, - // the scheduler might incorrectly schedule an atomic MIR terminator because the first event it creates is a non-atomic (e.g., `StorageLive`). - conf->disableRaceDetection = false; - - // Miri can already check for unfreed memory. Also, GenMC cannot distinguish between memory - // that is allowed to leak and memory that is not. - conf->warnUnfreedMemory = false; - - // FIXME(genmc): check config: - // checkConfigOptions(*conf); - - auto driver = std::make_unique(std::move(conf), mode); - return driver; -} diff --git a/src/tools/miri/genmc-sys/src_cpp/MiriInterface.hpp b/src/tools/miri/genmc-sys/src_cpp/MiriInterface.hpp deleted file mode 100644 index e55522ef4189..000000000000 --- a/src/tools/miri/genmc-sys/src_cpp/MiriInterface.hpp +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef GENMC_MIRI_INTERFACE_HPP -#define GENMC_MIRI_INTERFACE_HPP - -#include "rust/cxx.h" - -#include "config.h" - -#include "Config/Config.hpp" -#include "Verification/GenMCDriver.hpp" - -#include - -/**** Types available to Miri ****/ - -// Config struct defined on the Rust side and translated to C++ by cxx.rs: -struct GenmcParams; - -struct MiriGenMCShim : private GenMCDriver -{ - -public: - MiriGenMCShim(std::shared_ptr conf, Mode mode /* = VerificationMode{} */) - : GenMCDriver(std::move(conf), nullptr, mode) - { - std::cerr << "C++: GenMC handle created!" << std::endl; - } - - virtual ~MiriGenMCShim() - { - std::cerr << "C++: GenMC handle destroyed!" << std::endl; - } - - static std::unique_ptr createHandle(const GenmcParams &config); -}; - -/**** Functions available to Miri ****/ - -// NOTE: CXX doesn't support exposing static methods to Rust currently, so we expose this function instead. -static inline auto createGenmcHandle(const GenmcParams &config) -> std::unique_ptr -{ - return MiriGenMCShim::createHandle(config); -} - -#endif /* GENMC_MIRI_INTERFACE_HPP */ diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs index afd8afbaeab0..5d94a1f4138a 100644 --- a/src/tools/miri/src/alloc_addresses/mod.rs +++ b/src/tools/miri/src/alloc_addresses/mod.rs @@ -34,6 +34,8 @@ pub struct GlobalStateInner { /// sorted by address. We cannot use a `HashMap` since we can be given an address that is offset /// from the base address, and we need to find the `AllocId` it belongs to. This is not the /// *full* inverse of `base_addr`; dead allocations have been removed. + /// Note that in GenMC mode, dead allocations are *not* removed -- and also, addresses are never + /// reused. This lets us use the address as a cross-execution-stable identifier for an allocation. int_to_ptr_map: Vec<(u64, AllocId)>, /// The base address for each allocation. We cannot put that into /// `AllocExtra` because function pointers also have a base address, and @@ -123,7 +125,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // Miri's address assignment leaks state across thread boundaries, which is incompatible // with GenMC execution. So we instead let GenMC assign addresses to allocations. if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { - let addr = genmc_ctx.handle_alloc(&this.machine, info.size, info.align, memory_kind)?; + let addr = + genmc_ctx.handle_alloc(this, alloc_id, info.size, info.align, memory_kind)?; return interp_ok(addr); } @@ -180,7 +183,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this.active_thread(), ) { if let Some(clock) = clock { - this.acquire_clock(&clock); + this.acquire_clock(&clock)?; } interp_ok(reuse_addr) } else { @@ -242,7 +245,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // We only use this provenance if it has been exposed, or if the caller requested also non-exposed allocations if !only_exposed_allocations || global_state.exposed.contains(&alloc_id) { // This must still be live, since we remove allocations from `int_to_ptr_map` when they get freed. - debug_assert!(this.is_alloc_live(alloc_id)); + // In GenMC mode, we keep all allocations, so this check doesn't apply there. + if this.machine.data_race.as_genmc_ref().is_none() { + debug_assert!(this.is_alloc_live(alloc_id)); + } Some(alloc_id) } else { None @@ -459,6 +465,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { impl<'tcx> MiriMachine<'tcx> { pub fn free_alloc_id(&mut self, dead_id: AllocId, size: Size, align: Align, kind: MemoryKind) { + // In GenMC mode, we can't remove dead allocation info since such pointers can + // still be stored in atomics and we need this info to convert GenMC pointers to Miri pointers. + // `global_state.reuse` is also unused so we can just skip this entire function. + if self.data_race.as_genmc_ref().is_some() { + return; + } + let global_state = self.alloc_addresses.get_mut(); let rng = self.rng.get_mut(); diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index ae1b25f8857a..b96359316b92 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -39,7 +39,7 @@ use std::sync::atomic::{AtomicI32, AtomicU32, Ordering}; use miri::{ BacktraceStyle, BorrowTrackerMethod, GenmcConfig, GenmcCtx, MiriConfig, MiriEntryFnType, - ProvenanceMode, RetagFields, TreeBorrowsParams, ValidationMode, + ProvenanceMode, RetagFields, TreeBorrowsParams, ValidationMode, run_genmc_mode, }; use rustc_abi::ExternAbi; use rustc_data_structures::sync; @@ -187,9 +187,18 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { } if let Some(_genmc_config) = &config.genmc_config { - let _genmc_ctx = Rc::new(GenmcCtx::new(&config)); + let eval_entry_once = |genmc_ctx: Rc| { + miri::eval_entry(tcx, entry_def_id, entry_type, &config, Some(genmc_ctx)) + }; - todo!("GenMC mode not yet implemented"); + // FIXME(genmc): add estimation mode here. + + let return_code = run_genmc_mode(&config, eval_entry_once, tcx).unwrap_or_else(|| { + tcx.dcx().abort_if_errors(); + rustc_driver::EXIT_FAILURE + }); + + exit(return_code); }; if let Some(many_seeds) = self.many_seeds.take() { @@ -735,9 +744,11 @@ fn main() { // Validate settings for data race detection and GenMC mode. if miri_config.genmc_config.is_some() { if !miri_config.data_race_detector { - fatal_error!("Cannot disable data race detection in GenMC mode (currently)"); + fatal_error!("Cannot disable data race detection in GenMC mode"); } else if !miri_config.weak_memory_emulation { fatal_error!("Cannot disable weak memory emulation in GenMC mode"); + } else if !miri_config.native_lib.is_empty() { + fatal_error!("native-lib not supported in GenMC mode."); } if miri_config.borrow_tracker.is_some() { eprintln!( @@ -745,6 +756,9 @@ fn main() { ); miri_config.borrow_tracker = None; } + // We enable fixed scheduling so Miri doesn't randomly yield before a terminator, which anyway + // would be a NOP in GenMC mode. + miri_config.fixed_scheduling = true; } else if miri_config.weak_memory_emulation && !miri_config.data_race_detector { fatal_error!( "Weak memory emulation cannot be enabled when the data race detector is disabled" diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs index 2977efaae04a..5fe00ab02c4b 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs @@ -740,6 +740,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> { if let Some(access) = access { assert_eq!(access, AccessKind::Write); // Make sure the data race model also knows about this. + // FIXME(genmc): Ensure this is still done in GenMC mode. Check for other places where GenMC may need to be informed. if let Some(data_race) = alloc_extra.data_race.as_vclocks_mut() { data_race.write( alloc_id, diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs index 38d76f5cf73b..19d37a691d90 100644 --- a/src/tools/miri/src/concurrency/data_race.rs +++ b/src/tools/miri/src/concurrency/data_race.rs @@ -719,8 +719,7 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { // Only metadata on the location itself is used. if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { - // FIXME(GenMC): Inform GenMC what a non-atomic read here would return, to support mixed atomics/non-atomics - let old_val = None; + let old_val = this.run_for_validation_ref(|this| this.read_scalar(place)).discard_err(); return genmc_ctx.atomic_load( this, place.ptr().addr(), @@ -751,11 +750,22 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { // The program didn't actually do a read, so suppress the memory access hooks. // This is also a very special exception where we just ignore an error -- if this read // was UB e.g. because the memory is uninitialized, we don't want to know! - let old_val = this.run_for_validation_mut(|this| this.read_scalar(dest)).discard_err(); + let old_val = this.run_for_validation_ref(|this| this.read_scalar(dest)).discard_err(); + // Inform GenMC about the atomic store. if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { - // FIXME(GenMC): Inform GenMC what a non-atomic read here would return, to support mixed atomics/non-atomics - genmc_ctx.atomic_store(this, dest.ptr().addr(), dest.layout.size, val, atomic)?; + if genmc_ctx.atomic_store( + this, + dest.ptr().addr(), + dest.layout.size, + val, + old_val, + atomic, + )? { + // The store might be the latest store in coherence order (determined by GenMC). + // If it is, we need to update the value in Miri's memory: + this.allow_data_races_mut(|this| this.write_scalar(val, dest))?; + } return interp_ok(()); } this.allow_data_races_mut(move |this| this.write_scalar(val, dest))?; @@ -779,7 +789,6 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { // Inform GenMC about the atomic rmw operation. if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { - // FIXME(GenMC): Inform GenMC what a non-atomic read here would return, to support mixed atomics/non-atomics let (old_val, new_val) = genmc_ctx.atomic_rmw_op( this, place.ptr().addr(), @@ -787,8 +796,11 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { atomic, (op, not), rhs.to_scalar(), + old.to_scalar(), )?; - this.allow_data_races_mut(|this| this.write_scalar(new_val, place))?; + if let Some(new_val) = new_val { + this.allow_data_races_mut(|this| this.write_scalar(new_val, place))?; + } return interp_ok(ImmTy::from_scalar(old_val, old.layout)); } @@ -818,14 +830,19 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { // Inform GenMC about the atomic atomic exchange. if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { - // FIXME(GenMC): Inform GenMC what a non-atomic read here would return, to support mixed atomics/non-atomics - let (old_val, _is_success) = genmc_ctx.atomic_exchange( + let (old_val, new_val) = genmc_ctx.atomic_exchange( this, place.ptr().addr(), place.layout.size, new, atomic, + old, )?; + // The store might be the latest store in coherence order (determined by GenMC). + // If it is, we need to update the value in Miri's memory: + if let Some(new_val) = new_val { + this.allow_data_races_mut(|this| this.write_scalar(new_val, place))?; + } return interp_ok(old_val); } @@ -851,7 +868,6 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { // Inform GenMC about the atomic min/max operation. if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { - // FIXME(GenMC): Inform GenMC what a non-atomic read here would return, to support mixed atomics/non-atomics let (old_val, new_val) = genmc_ctx.atomic_min_max_op( this, place.ptr().addr(), @@ -860,8 +876,13 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { min, old.layout.backend_repr.is_signed(), rhs.to_scalar(), + old.to_scalar(), )?; - this.allow_data_races_mut(|this| this.write_scalar(new_val, place))?; + // The store might be the latest store in coherence order (determined by GenMC). + // If it is, we need to update the value in Miri's memory: + if let Some(new_val) = new_val { + this.allow_data_races_mut(|this| this.write_scalar(new_val, place))?; + } return interp_ok(ImmTy::from_scalar(old_val, old.layout)); } @@ -903,15 +924,12 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); this.atomic_access_check(place, AtomicAccessType::Rmw)?; - // Failure ordering cannot be stronger than success ordering, therefore first attempt - // to read with the failure ordering and if successful then try again with the success - // read ordering and write in the success case. // Read as immediate for the sake of `binary_op()` let old = this.allow_data_races_mut(|this| this.read_immediate(place))?; // Inform GenMC about the atomic atomic compare exchange. if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { - let (old, cmpxchg_success) = genmc_ctx.atomic_compare_exchange( + let (old_value, new_value, cmpxchg_success) = genmc_ctx.atomic_compare_exchange( this, place.ptr().addr(), place.layout.size, @@ -920,11 +938,15 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { success, fail, can_fail_spuriously, + old.to_scalar(), )?; - if cmpxchg_success { - this.allow_data_races_mut(|this| this.write_scalar(new, place))?; + + // The store might be the latest store in coherence order (determined by GenMC). + // If it is, we need to update the value in Miri's memory: + if let Some(new_value) = new_value { + this.allow_data_races_mut(|this| this.write_scalar(new_value, place))?; } - return interp_ok(Immediate::ScalarPair(old, Scalar::from_bool(cmpxchg_success))); + return interp_ok(Immediate::ScalarPair(old_value, Scalar::from_bool(cmpxchg_success))); } // `binary_op` will bail if either of them is not a scalar. @@ -985,11 +1007,16 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { /// Acquire the given clock into the current thread, establishing synchronization with /// the moment when that clock snapshot was taken via `release_clock`. - fn acquire_clock(&self, clock: &VClock) { + fn acquire_clock(&self, clock: &VClock) -> InterpResult<'tcx> { let this = self.eval_context_ref(); - if let Some(data_race) = this.machine.data_race.as_vclocks_ref() { - data_race.acquire_clock(clock, &this.machine.threads); + match &this.machine.data_race { + GlobalDataRaceHandler::None => {} + GlobalDataRaceHandler::Genmc(_genmc_ctx) => + throw_unsup_format!("acquire_clock is not (yet) supported in GenMC mode."), + GlobalDataRaceHandler::Vclocks(data_race) => + data_race.acquire_clock(clock, &this.machine.threads), } + interp_ok(()) } } diff --git a/src/tools/miri/src/concurrency/genmc/config.rs b/src/tools/miri/src/concurrency/genmc/config.rs index c56adab90fe2..66116d5cf23c 100644 --- a/src/tools/miri/src/concurrency/genmc/config.rs +++ b/src/tools/miri/src/concurrency/genmc/config.rs @@ -1,13 +1,14 @@ -use super::GenmcParams; +use genmc_sys::{GenmcParams, LogLevel}; /// Configuration for GenMC mode. /// The `params` field is shared with the C++ side. /// The remaining options are kept on the Rust side. #[derive(Debug, Default, Clone)] pub struct GenmcConfig { + /// Parameters sent to the C++ side to create a new handle to the GenMC model checker. pub(super) params: GenmcParams, - do_estimation: bool, - // FIXME(GenMC): add remaining options. + /// The log level for GenMC. + pub(super) log_level: LogLevel, } impl GenmcConfig { @@ -29,7 +30,15 @@ impl GenmcConfig { if trimmed_arg.is_empty() { return Ok(()); // this corresponds to "-Zmiri-genmc" } - // FIXME(GenMC): implement remaining parameters. - todo!(); + let genmc_config = genmc_config.as_mut().unwrap(); + let Some(trimmed_arg) = trimmed_arg.strip_prefix("-") else { + return Err(format!("Invalid GenMC argument \"-Zmiri-genmc{trimmed_arg}\"")); + }; + if let Some(log_level) = trimmed_arg.strip_prefix("log=") { + genmc_config.log_level = log_level.parse()?; + } else { + return Err(format!("Invalid GenMC argument: \"-Zmiri-genmc-{trimmed_arg}\"")); + } + Ok(()) } } diff --git a/src/tools/miri/src/concurrency/genmc/dummy.rs b/src/tools/miri/src/concurrency/genmc/dummy.rs index 79d27c4be159..1960ef37cc9f 100644 --- a/src/tools/miri/src/concurrency/genmc/dummy.rs +++ b/src/tools/miri/src/concurrency/genmc/dummy.rs @@ -1,30 +1,49 @@ -#![allow(unused)] - use rustc_abi::{Align, Size}; -use rustc_const_eval::interpret::{InterpCx, InterpResult}; +use rustc_const_eval::interpret::{AllocId, InterpCx, InterpResult}; use rustc_middle::mir; +pub use self::run::run_genmc_mode; use crate::{ - AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, MemoryKind, MiriConfig, - MiriMachine, Scalar, ThreadId, ThreadManager, VisitProvenance, VisitWith, + AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, MemoryKind, MiriMachine, Scalar, + ThreadId, ThreadManager, VisitProvenance, VisitWith, }; +#[derive(Clone, Copy, Debug)] +pub enum ExitType { + MainThreadFinish, + ExitCalled, +} + #[derive(Debug)] pub struct GenmcCtx {} #[derive(Debug, Default, Clone)] pub struct GenmcConfig {} +mod run { + use std::rc::Rc; + + use rustc_middle::ty::TyCtxt; + + use crate::{GenmcCtx, MiriConfig}; + + pub fn run_genmc_mode<'tcx>( + _config: &MiriConfig, + _eval_entry: impl Fn(Rc) -> Option, + _tcx: TyCtxt<'tcx>, + ) -> Option { + unreachable!(); + } +} + impl GenmcCtx { - pub fn new(_miri_config: &MiriConfig) -> Self { + // We don't provide the `new` function in the dummy module. + + pub fn get_blocked_execution_count(&self) -> usize { unreachable!() } - pub fn get_stuck_execution_count(&self) -> usize { - unreachable!() - } - - pub fn print_genmc_graph(&self) { + pub fn get_explored_execution_count(&self) -> usize { unreachable!() } @@ -34,17 +53,6 @@ impl GenmcCtx { /**** Memory access handling ****/ - pub(crate) fn handle_execution_start(&self) { - unreachable!() - } - - pub(crate) fn handle_execution_end<'tcx>( - &self, - _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - ) -> Result<(), String> { - unreachable!() - } - pub(super) fn set_ongoing_action_data_race_free(&self, _enable: bool) { unreachable!() } @@ -67,8 +75,9 @@ impl GenmcCtx { _address: Size, _size: Size, _value: Scalar, + _old_value: Option, _ordering: AtomicWriteOrd, - ) -> InterpResult<'tcx, ()> { + ) -> InterpResult<'tcx, bool> { unreachable!() } @@ -76,7 +85,7 @@ impl GenmcCtx { &self, _machine: &MiriMachine<'tcx>, _ordering: AtomicFenceOrd, - ) -> InterpResult<'tcx, ()> { + ) -> InterpResult<'tcx> { unreachable!() } @@ -86,22 +95,24 @@ impl GenmcCtx { _address: Size, _size: Size, _ordering: AtomicRwOrd, - (rmw_op, not): (mir::BinOp, bool), + (_rmw_op, _not): (mir::BinOp, bool), _rhs_scalar: Scalar, - ) -> InterpResult<'tcx, (Scalar, Scalar)> { + _old_value: Scalar, + ) -> InterpResult<'tcx, (Scalar, Option)> { unreachable!() } pub(crate) fn atomic_min_max_op<'tcx>( &self, - ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - address: Size, - size: Size, - ordering: AtomicRwOrd, - min: bool, - is_signed: bool, - rhs_scalar: Scalar, - ) -> InterpResult<'tcx, (Scalar, Scalar)> { + _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + _address: Size, + _size: Size, + _ordering: AtomicRwOrd, + _min: bool, + _is_signed: bool, + _rhs_scalar: Scalar, + _old_value: Scalar, + ) -> InterpResult<'tcx, (Scalar, Option)> { unreachable!() } @@ -112,7 +123,8 @@ impl GenmcCtx { _size: Size, _rhs_scalar: Scalar, _ordering: AtomicRwOrd, - ) -> InterpResult<'tcx, (Scalar, bool)> { + _old_value: Scalar, + ) -> InterpResult<'tcx, (Scalar, Option)> { unreachable!() } @@ -126,7 +138,8 @@ impl GenmcCtx { _success: AtomicRwOrd, _fail: AtomicReadOrd, _can_fail_spuriously: bool, - ) -> InterpResult<'tcx, (Scalar, bool)> { + _old_value: Scalar, + ) -> InterpResult<'tcx, (Scalar, Option, bool)> { unreachable!() } @@ -135,7 +148,7 @@ impl GenmcCtx { _machine: &MiriMachine<'tcx>, _address: Size, _size: Size, - ) -> InterpResult<'tcx, ()> { + ) -> InterpResult<'tcx> { unreachable!() } @@ -144,7 +157,7 @@ impl GenmcCtx { _machine: &MiriMachine<'tcx>, _address: Size, _size: Size, - ) -> InterpResult<'tcx, ()> { + ) -> InterpResult<'tcx> { unreachable!() } @@ -152,7 +165,8 @@ impl GenmcCtx { pub(crate) fn handle_alloc<'tcx>( &self, - _machine: &MiriMachine<'tcx>, + _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + _alloc_id: AllocId, _size: Size, _alignment: Align, _memory_kind: MemoryKind, @@ -163,11 +177,10 @@ impl GenmcCtx { pub(crate) fn handle_dealloc<'tcx>( &self, _machine: &MiriMachine<'tcx>, + _alloc_id: AllocId, _address: Size, - _size: Size, - _align: Align, _kind: MemoryKind, - ) -> InterpResult<'tcx, ()> { + ) -> InterpResult<'tcx> { unreachable!() } @@ -176,8 +189,10 @@ impl GenmcCtx { pub(crate) fn handle_thread_create<'tcx>( &self, _threads: &ThreadManager<'tcx>, + _start_routine: crate::Pointer, + _func_arg: &crate::ImmTy<'tcx>, _new_thread_id: ThreadId, - ) -> InterpResult<'tcx, ()> { + ) -> InterpResult<'tcx> { unreachable!() } @@ -185,24 +200,26 @@ impl GenmcCtx { &self, _active_thread_id: ThreadId, _child_thread_id: ThreadId, - ) -> InterpResult<'tcx, ()> { + ) -> InterpResult<'tcx> { unreachable!() } - pub(crate) fn handle_thread_stack_empty(&self, _thread_id: ThreadId) { + pub(crate) fn handle_thread_finish<'tcx>(&self, _threads: &ThreadManager<'tcx>) { unreachable!() } - pub(crate) fn handle_thread_finish<'tcx>( + pub(crate) fn handle_exit<'tcx>( &self, - _threads: &ThreadManager<'tcx>, - ) -> InterpResult<'tcx, ()> { + _thread: ThreadId, + _exit_code: i32, + _exit_type: ExitType, + ) -> InterpResult<'tcx> { unreachable!() } /**** Scheduling functionality ****/ - pub(crate) fn schedule_thread<'tcx>( + pub fn schedule_thread<'tcx>( &self, _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, ) -> InterpResult<'tcx, ThreadId> { @@ -211,6 +228,7 @@ impl GenmcCtx { /**** Blocking instructions ****/ + #[allow(unused)] pub(crate) fn handle_verifier_assume<'tcx>( &self, _machine: &MiriMachine<'tcx>, @@ -229,7 +247,7 @@ impl VisitProvenance for GenmcCtx { impl GenmcConfig { pub fn parse_arg( _genmc_config: &mut Option, - trimmed_arg: &str, + _trimmed_arg: &str, ) -> Result<(), String> { if cfg!(feature = "genmc") { Err(format!("GenMC is disabled in this build of Miri")) @@ -237,8 +255,4 @@ impl GenmcConfig { Err(format!("GenMC is not supported on this target")) } } - - pub fn should_print_graph(&self, _rep: usize) -> bool { - unreachable!() - } } diff --git a/src/tools/miri/src/concurrency/genmc/global_allocations.rs b/src/tools/miri/src/concurrency/genmc/global_allocations.rs new file mode 100644 index 000000000000..272f8d248403 --- /dev/null +++ b/src/tools/miri/src/concurrency/genmc/global_allocations.rs @@ -0,0 +1,118 @@ +use std::collections::hash_map::Entry; +use std::sync::RwLock; + +use genmc_sys::{GENMC_GLOBAL_ADDRESSES_MASK, get_global_alloc_static_mask}; +use rand::SeedableRng; +use rand::rngs::StdRng; +use rustc_const_eval::interpret::{AllocId, AllocInfo, InterpResult, interp_ok}; +use rustc_data_structures::fx::FxHashMap; +use tracing::debug; + +use crate::alloc_addresses::AddressGenerator; + +#[derive(Debug)] +struct GlobalStateInner { + /// The base address for each *global* allocation. + base_addr: FxHashMap, + /// We use the same address generator that Miri uses in normal operation. + address_generator: AddressGenerator, + /// The address generator needs an Rng to randomize the offsets between allocations. + /// We don't use the `MiriMachine` Rng since this is global, cross-machine state. + rng: StdRng, +} + +/// Allocator for global memory in GenMC mode. +/// Miri doesn't discover all global allocations statically like LLI does for GenMC. +/// The existing global memory allocator in GenMC doesn't support this, so we take over these allocations. +/// Global allocations need to be in a specific address range, with the lower limit given by the `GENMC_GLOBAL_ADDRESSES_MASK` constant. +/// +/// Every global allocation must have the same addresses across all executions of a single program. +/// Therefore there is only 1 global allocator, and it syncs new globals across executions, even if they are explored in parallel. +#[derive(Debug)] +pub struct GlobalAllocationHandler(RwLock); + +impl GlobalAllocationHandler { + /// Create a new global address generator with a given max address `last_addr` + /// (corresponding to the highest address available on the target platform, unless another limit exists). + /// No addresses higher than this will be allocated. + /// Will panic if the given address limit is too small to allocate any addresses. + pub fn new(last_addr: u64) -> GlobalAllocationHandler { + assert_eq!(GENMC_GLOBAL_ADDRESSES_MASK, get_global_alloc_static_mask()); + assert_ne!(GENMC_GLOBAL_ADDRESSES_MASK, 0); + // FIXME(genmc): Remove if non-64bit targets are supported. + assert!( + GENMC_GLOBAL_ADDRESSES_MASK < last_addr, + "only 64bit platforms are currently supported (highest address {last_addr:#x} <= minimum global address {GENMC_GLOBAL_ADDRESSES_MASK:#x})." + ); + Self(RwLock::new(GlobalStateInner { + base_addr: FxHashMap::default(), + address_generator: AddressGenerator::new(GENMC_GLOBAL_ADDRESSES_MASK..last_addr), + // FIXME(genmc): We could provide a way to changes this seed, to allow for different global addresses. + rng: StdRng::seed_from_u64(0), + })) + } +} + +impl GlobalStateInner { + fn global_allocate_addr<'tcx>( + &mut self, + alloc_id: AllocId, + info: AllocInfo, + ) -> InterpResult<'tcx, u64> { + let entry = match self.base_addr.entry(alloc_id) { + Entry::Occupied(occupied_entry) => { + // Looks like some other thread allocated this for us + // between when we released the read lock and aquired the write lock, + // so we just return that value. + return interp_ok(*occupied_entry.get()); + } + Entry::Vacant(vacant_entry) => vacant_entry, + }; + + // This allocation does not have a base address yet, pick or reuse one. + // We are not in native lib mode (incompatible with GenMC mode), so we control the addresses ourselves. + let new_addr = self.address_generator.generate(info.size, info.align, &mut self.rng)?; + + // Cache the address for future use. + entry.insert(new_addr); + + interp_ok(new_addr) + } +} + +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + /// Allocate a new address for the given alloc id, or return the cached address. + /// Each alloc id is assigned one unique allocation which will not change if this function is called again with the same alloc id. + fn get_global_allocation_address( + &self, + global_allocation_handler: &GlobalAllocationHandler, + alloc_id: AllocId, + ) -> InterpResult<'tcx, u64> { + let this = self.eval_context_ref(); + let info = this.get_alloc_info(alloc_id); + + let global_state = global_allocation_handler.0.read().unwrap(); + if let Some(base_addr) = global_state.base_addr.get(&alloc_id) { + debug!( + "GenMC: address for global with alloc id {alloc_id:?} was cached: {base_addr} == {base_addr:#x}" + ); + return interp_ok(*base_addr); + } + + // We need to upgrade to a write lock. `std::sync::RwLock` doesn't support this, so we drop the guard and lock again + // Note that another thread might allocate the address while the `RwLock` is unlocked, but we handle this case in the allocation function. + drop(global_state); + let mut global_state = global_allocation_handler.0.write().unwrap(); + // With the write lock, we can safely allocate an address only once per `alloc_id`. + let new_addr = global_state.global_allocate_addr(alloc_id, info)?; + debug!("GenMC: global with alloc id {alloc_id:?} got address: {new_addr} == {new_addr:#x}"); + assert_eq!( + GENMC_GLOBAL_ADDRESSES_MASK, + new_addr & GENMC_GLOBAL_ADDRESSES_MASK, + "Global address allocated outside global address space." + ); + + interp_ok(new_addr) + } +} diff --git a/src/tools/miri/src/concurrency/genmc/helper.rs b/src/tools/miri/src/concurrency/genmc/helper.rs new file mode 100644 index 000000000000..b70ca8faadfe --- /dev/null +++ b/src/tools/miri/src/concurrency/genmc/helper.rs @@ -0,0 +1,97 @@ +use genmc_sys::MemOrdering; +use rustc_abi::Size; +use rustc_const_eval::interpret::{InterpResult, interp_ok}; +use rustc_middle::ty::ScalarInt; +use tracing::debug; + +use super::GenmcScalar; +use crate::{AtomicReadOrd, AtomicWriteOrd, MiriInterpCx, Scalar, throw_unsup_format}; + +/// Maximum size memory access in bytes that GenMC supports. +pub(super) const MAX_ACCESS_SIZE: u64 = 8; + +/// This function is used to split up a large memory access into aligned, non-overlapping chunks of a limited size. +/// Returns an iterator over the chunks, yielding `(base address, size)` of each chunk, ordered by address. +pub fn split_access(address: Size, size: Size) -> impl Iterator { + let start_address = address.bytes(); + let end_address = start_address + size.bytes(); + + let start_address_aligned = start_address.next_multiple_of(MAX_ACCESS_SIZE); + let end_address_aligned = (end_address / MAX_ACCESS_SIZE) * MAX_ACCESS_SIZE; // prev_multiple_of + + debug!( + "GenMC: splitting NA memory access into {MAX_ACCESS_SIZE} byte chunks: {}B + {} * {MAX_ACCESS_SIZE}B + {}B = {size:?}", + start_address_aligned - start_address, + (end_address_aligned - start_address_aligned) / MAX_ACCESS_SIZE, + end_address - end_address_aligned, + ); + + // FIXME(genmc): could make remaining accesses powers-of-2, instead of 1 byte. + let start_chunks = (start_address..start_address_aligned).map(|address| (address, 1)); + let aligned_chunks = (start_address_aligned..end_address_aligned) + .step_by(MAX_ACCESS_SIZE.try_into().unwrap()) + .map(|address| (address, MAX_ACCESS_SIZE)); + let end_chunks = (end_address_aligned..end_address).map(|address| (address, 1)); + + start_chunks.chain(aligned_chunks).chain(end_chunks) +} + +/// Inverse function to `scalar_to_genmc_scalar`. +/// +/// Convert a Miri `Scalar` to a `GenmcScalar`. +/// To be able to restore pointer provenance from a `GenmcScalar`, the base address of the allocation of the pointer is also stored in the `GenmcScalar`. +/// We cannot use the `AllocId` instead of the base address, since Miri has no control over the `AllocId`, and it may change across executions. +/// Pointers with `Wildcard` provenance are not supported. +pub fn scalar_to_genmc_scalar<'tcx>( + _ecx: &MiriInterpCx<'tcx>, + scalar: Scalar, +) -> InterpResult<'tcx, GenmcScalar> { + interp_ok(match scalar { + rustc_const_eval::interpret::Scalar::Int(scalar_int) => { + // FIXME(genmc): Add u128 support once GenMC supports it. + let value: u64 = scalar_int.to_uint(scalar_int.size()).try_into().unwrap(); + GenmcScalar { value, is_init: true } + } + rustc_const_eval::interpret::Scalar::Ptr(_pointer, _size) => + throw_unsup_format!( + "FIXME(genmc): Implement sending pointers (with provenance) to GenMC." + ), + }) +} + +/// Inverse function to `scalar_to_genmc_scalar`. +/// +/// Convert a `GenmcScalar` back into a Miri `Scalar`. +/// For pointers, attempt to convert the stored base address of their allocation back into an `AllocId`. +pub fn genmc_scalar_to_scalar<'tcx>( + _ecx: &MiriInterpCx<'tcx>, + scalar: GenmcScalar, + size: Size, +) -> InterpResult<'tcx, Scalar> { + // FIXME(genmc): Add GenmcScalar to Miri Pointer conversion. + + // NOTE: GenMC always returns 64 bit values, and the upper bits are not yet truncated. + // FIXME(genmc): GenMC should be doing the truncation, not Miri. + let (value_scalar_int, _got_truncated) = ScalarInt::truncate_from_uint(scalar.value, size); + interp_ok(Scalar::Int(value_scalar_int)) +} + +impl AtomicReadOrd { + pub(super) fn to_genmc(self) -> MemOrdering { + match self { + AtomicReadOrd::Relaxed => MemOrdering::Relaxed, + AtomicReadOrd::Acquire => MemOrdering::Acquire, + AtomicReadOrd::SeqCst => MemOrdering::SequentiallyConsistent, + } + } +} + +impl AtomicWriteOrd { + pub(super) fn to_genmc(self) -> MemOrdering { + match self { + AtomicWriteOrd::Relaxed => MemOrdering::Relaxed, + AtomicWriteOrd::Release => MemOrdering::Release, + AtomicWriteOrd::SeqCst => MemOrdering::SequentiallyConsistent, + } + } +} diff --git a/src/tools/miri/src/concurrency/genmc/mod.rs b/src/tools/miri/src/concurrency/genmc/mod.rs index 3617775e27ea..b5d20a439c01 100644 --- a/src/tools/miri/src/concurrency/genmc/mod.rs +++ b/src/tools/miri/src/concurrency/genmc/mod.rs @@ -1,78 +1,171 @@ -#![allow(unused)] // FIXME(GenMC): remove this +use std::cell::{Cell, RefCell}; +use std::sync::Arc; -use std::cell::Cell; - -use genmc_sys::{GenmcParams, createGenmcHandle}; +use genmc_sys::{ + GENMC_GLOBAL_ADDRESSES_MASK, GenmcScalar, MemOrdering, MiriGenmcShim, UniquePtr, + create_genmc_driver_handle, +}; use rustc_abi::{Align, Size}; -use rustc_const_eval::interpret::{InterpCx, InterpResult, interp_ok}; -use rustc_middle::mir; +use rustc_const_eval::interpret::{AllocId, InterpCx, InterpResult, interp_ok}; +use rustc_middle::{mir, throw_machine_stop, throw_ub_format, throw_unsup_format}; +// FIXME(genmc,tracing): Implement some work-around for enabling debug/trace level logging (currently disabled statically in rustc). +use tracing::{debug, info}; +use self::global_allocations::{EvalContextExt as _, GlobalAllocationHandler}; +use self::helper::{MAX_ACCESS_SIZE, genmc_scalar_to_scalar, scalar_to_genmc_scalar}; +use self::thread_id_map::ThreadIdMap; +use crate::concurrency::genmc::helper::split_access; use crate::{ AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, MemoryKind, MiriConfig, - MiriMachine, Scalar, ThreadId, ThreadManager, VisitProvenance, VisitWith, + MiriMachine, MiriMemoryKind, Scalar, TerminationInfo, ThreadId, ThreadManager, VisitProvenance, + VisitWith, }; mod config; +mod global_allocations; +mod helper; +mod run; +pub(crate) mod scheduling; +mod thread_id_map; pub use self::config::GenmcConfig; +pub use self::run::run_genmc_mode; -// FIXME(GenMC): add fields -pub struct GenmcCtx { - /// Some actions Miri does are allowed to cause data races. - /// GenMC will not be informed about certain actions (e.g. non-atomic loads) when this flag is set. - allow_data_races: Cell, +#[derive(Clone, Copy, Debug)] +pub enum ExitType { + MainThreadFinish, + ExitCalled, } +/// The exit status of a program. +/// GenMC must store this if a thread exits while any others can still run. +/// The other threads must also be explored before the program is terminated. +#[derive(Clone, Copy, Debug)] +struct ExitStatus { + exit_code: i32, + exit_type: ExitType, +} + +impl ExitStatus { + fn do_leak_check(self) -> bool { + matches!(self.exit_type, ExitType::MainThreadFinish) + } +} + +/// State that is reset at the start of every execution. +#[derive(Debug, Default)] +struct PerExecutionState { + /// Thread id management, such as mapping between Miri `ThreadId` and GenMC's thread ids, or selecting GenMC thread ids. + thread_id_manager: RefCell, + + /// A flag to indicate that we should not forward non-atomic accesses to genmc, e.g. because we + /// are executing an atomic operation. + allow_data_races: Cell, + + /// The exit status of the program. We keep running other threads even after `exit` to ensure + /// we cover all possible executions. + /// `None` if no thread has called `exit` and the main thread isn't finished yet. + exit_status: Cell>, +} + +impl PerExecutionState { + fn reset(&self) { + self.allow_data_races.replace(false); + self.thread_id_manager.borrow_mut().reset(); + self.exit_status.set(None); + } +} + +#[derive(Debug)] +struct GlobalState { + /// Keep track of global allocations, to ensure they keep the same address across different executions, even if the order of allocations changes. + /// The `AllocId` for globals is stable across executions, so we can use it as an identifier. + global_allocations: GlobalAllocationHandler, +} + +impl GlobalState { + fn new(target_usize_max: u64) -> Self { + Self { global_allocations: GlobalAllocationHandler::new(target_usize_max) } + } +} + +/// The main interface with GenMC. +/// Each `GenmcCtx` owns one `MiriGenmcShim`, which owns one `GenMCDriver` (the GenMC model checker). +/// For each GenMC run (estimation or verification), one or more `GenmcCtx` can be created (one per Miri thread). +/// However, for now, we only ever have one `GenmcCtx` per run. +/// +/// In multithreading, each worker thread has its own `GenmcCtx`, which will have their results combined in the end. +/// FIXME(genmc): implement multithreading. +/// +/// Some data is shared across all `GenmcCtx` in the same run, namely data for global allocation handling. +/// Globals must be allocated in a consistent manner, i.e., each global allocation must have the same address in each execution. +/// +/// Some state is reset between each execution in the same run. +pub struct GenmcCtx { + /// Handle to the GenMC model checker. + handle: RefCell>, + + /// State that is reset at the start of every execution. + exec_state: PerExecutionState, + + /// State that persists across executions. + /// All `GenmcCtx` in one verification step share this state. + global_state: Arc, +} + +/// GenMC Context creation and administrative / query actions impl GenmcCtx { /// Create a new `GenmcCtx` from a given config. - pub fn new(miri_config: &MiriConfig) -> Self { + fn new(miri_config: &MiriConfig, global_state: Arc) -> Self { let genmc_config = miri_config.genmc_config.as_ref().unwrap(); - - let handle = createGenmcHandle(&genmc_config.params); - assert!(!handle.is_null()); - - eprintln!("Miri: GenMC handle creation successful!"); - - drop(handle); - eprintln!("Miri: Dropping GenMC handle successful!"); - - // FIXME(GenMC): implement - std::process::exit(0); + let handle = + RefCell::new(create_genmc_driver_handle(&genmc_config.params, genmc_config.log_level)); + Self { handle, exec_state: Default::default(), global_state } } - pub fn get_stuck_execution_count(&self) -> usize { - todo!() + /// Get the number of blocked executions encountered by GenMC. + pub fn get_blocked_execution_count(&self) -> u64 { + let mc = self.handle.borrow(); + mc.as_ref().unwrap().get_blocked_execution_count() } - pub fn print_genmc_graph(&self) { - todo!() + /// Get the number of explored executions encountered by GenMC. + pub fn get_explored_execution_count(&self) -> u64 { + let mc = self.handle.borrow(); + mc.as_ref().unwrap().get_explored_execution_count() + } + + /// Check if GenMC encountered an error that wasn't immediately returned during execution. + /// Returns a string representation of the error if one occurred. + pub fn try_get_error(&self) -> Option { + let mc = self.handle.borrow(); + mc.as_ref() + .unwrap() + .get_error_string() + .as_ref() + .map(|error| error.to_string_lossy().to_string()) + } + + /// Check if GenMC encountered an error that wasn't immediately returned during execution. + /// Returns a string representation of the error if one occurred. + pub fn get_result_message(&self) -> String { + let mc = self.handle.borrow(); + mc.as_ref() + .unwrap() + .get_result_message() + .as_ref() + .map(|error| error.to_string_lossy().to_string()) + .expect("there should always be a message") } /// This function determines if we should continue exploring executions or if we are done. /// /// In GenMC mode, the input program should be repeatedly executed until this function returns `true` or an error is found. pub fn is_exploration_done(&self) -> bool { - todo!() + let mut mc = self.handle.borrow_mut(); + mc.as_mut().unwrap().is_exploration_done() } - /// Inform GenMC that a new program execution has started. - /// This function should be called at the start of every execution. - pub(crate) fn handle_execution_start(&self) { - todo!() - } - - /// Inform GenMC that the program's execution has ended. - /// - /// This function must be called even when the execution got stuck (i.e., it returned a `InterpErrorKind::MachineStop` with error kind `TerminationInfo::GenmcStuckExecution`). - pub(crate) fn handle_execution_end<'tcx>( - &self, - ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - ) -> Result<(), String> { - todo!() - } - - /**** Memory access handling ****/ - /// Select whether data race free actions should be allowed. This function should be used carefully! /// /// If `true` is passed, allow for data races to happen without triggering an error, until this function is called again with argument `false`. @@ -83,10 +176,59 @@ impl GenmcCtx { /// # Panics /// If data race free is attempted to be set more than once (i.e., no nesting allowed). pub(super) fn set_ongoing_action_data_race_free(&self, enable: bool) { - let old = self.allow_data_races.replace(enable); + debug!("GenMC: set_ongoing_action_data_race_free ({enable})"); + let old = self.exec_state.allow_data_races.replace(enable); assert_ne!(old, enable, "cannot nest allow_data_races"); } + /// Check whether data races are currently allowed (e.g., for loading values for validation which are not actually loaded by the program). + fn get_alloc_data_races(&self) -> bool { + self.exec_state.allow_data_races.get() + } +} + +/// GenMC event handling. These methods are used to inform GenMC about events happening in the program, and to handle scheduling decisions. +impl GenmcCtx { + /// Prepare for the next execution and inform GenMC about it. + /// Must be called before at the start of every execution. + fn prepare_next_execution(&self) { + // Reset per-execution state. + self.exec_state.reset(); + // Inform GenMC about the new execution. + let mut mc = self.handle.borrow_mut(); + mc.as_mut().unwrap().handle_execution_start(); + } + + /// Inform GenMC that the program's execution has ended. + /// + /// This function must be called even when the execution is blocked + /// (i.e., it returned a `InterpErrorKind::MachineStop` with error kind `TerminationInfo::GenmcBlockedExecution`). + /// Don't call this function if an error was found. + /// + /// GenMC detects certain errors only when the execution ends. + /// If an error occured, a string containing a short error description is returned. + /// + /// GenMC currently doesn't return an error in all cases immediately when one happens. + /// This function will also check for those, and return their error description. + /// + /// To get the all messages (warnings, errors) that GenMC produces, use the `get_result_message` method. + fn handle_execution_end(&self) -> Option { + let mut mc = self.handle.borrow_mut(); + let result = mc.as_mut().unwrap().handle_execution_end(); + result.as_ref().map(|msg| msg.to_string_lossy().to_string())?; + + // GenMC currently does not return an error value immediately in all cases. + // We manually query for any errors here to ensure we don't miss any. + drop(mc); // `try_get_error` needs access to the `RefCell`. + self.try_get_error() + } + + /**** Memory access handling ****/ + + /// Inform GenMC about an atomic load. + /// Returns that value that the load should read. + /// + /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized. pub(crate) fn atomic_load<'tcx>( &self, ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, @@ -95,89 +237,143 @@ impl GenmcCtx { ordering: AtomicReadOrd, old_val: Option, ) -> InterpResult<'tcx, Scalar> { - assert!(!self.allow_data_races.get()); - todo!() + assert!(!self.get_alloc_data_races(), "atomic load with data race checking disabled."); + let genmc_old_value = if let Some(scalar) = old_val { + scalar_to_genmc_scalar(ecx, scalar)? + } else { + GenmcScalar::UNINIT + }; + let read_value = + self.handle_load(&ecx.machine, address, size, ordering.to_genmc(), genmc_old_value)?; + genmc_scalar_to_scalar(ecx, read_value, size) } + /// Inform GenMC about an atomic store. + /// Returns `true` if the stored value should be reflected in Miri's memory. + /// + /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized. pub(crate) fn atomic_store<'tcx>( &self, ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, address: Size, size: Size, value: Scalar, + old_value: Option, ordering: AtomicWriteOrd, - ) -> InterpResult<'tcx, ()> { - assert!(!self.allow_data_races.get()); - todo!() + ) -> InterpResult<'tcx, bool> { + assert!(!self.get_alloc_data_races(), "atomic store with data race checking disabled."); + let genmc_value = scalar_to_genmc_scalar(ecx, value)?; + let genmc_old_value = if let Some(scalar) = old_value { + scalar_to_genmc_scalar(ecx, scalar)? + } else { + GenmcScalar::UNINIT + }; + self.handle_store( + &ecx.machine, + address, + size, + genmc_value, + genmc_old_value, + ordering.to_genmc(), + ) } + /// Inform GenMC about an atomic fence. pub(crate) fn atomic_fence<'tcx>( &self, - machine: &MiriMachine<'tcx>, - ordering: AtomicFenceOrd, - ) -> InterpResult<'tcx, ()> { - assert!(!self.allow_data_races.get()); - todo!() + _machine: &MiriMachine<'tcx>, + _ordering: AtomicFenceOrd, + ) -> InterpResult<'tcx> { + assert!(!self.get_alloc_data_races(), "atomic fence with data race checking disabled."); + throw_unsup_format!("FIXME(genmc): Add support for atomic fences.") } /// Inform GenMC about an atomic read-modify-write operation. /// - /// Returns `(old_val, new_val)`. + /// Returns `(old_val, Option)`. `new_val` might not be the latest write in coherence order, which is indicated by `None`. + /// + /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized. pub(crate) fn atomic_rmw_op<'tcx>( &self, - ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - address: Size, - size: Size, - ordering: AtomicRwOrd, - (rmw_op, not): (mir::BinOp, bool), - rhs_scalar: Scalar, - ) -> InterpResult<'tcx, (Scalar, Scalar)> { - assert!(!self.allow_data_races.get()); - todo!() + _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + _address: Size, + _size: Size, + _ordering: AtomicRwOrd, + (_rmw_op, _not): (mir::BinOp, bool), + _rhs_scalar: Scalar, + _old_value: Scalar, + ) -> InterpResult<'tcx, (Scalar, Option)> { + assert!( + !self.get_alloc_data_races(), + "atomic read-modify-write operation with data race checking disabled." + ); + throw_unsup_format!("FIXME(genmc): Add support for atomic RMW.") } /// Inform GenMC about an atomic `min` or `max` operation. /// - /// Returns `(old_val, new_val)`. + /// Returns `(old_val, Option)`. `new_val` might not be the latest write in coherence order, which is indicated by `None`. + /// + /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized. pub(crate) fn atomic_min_max_op<'tcx>( &self, - ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - address: Size, - size: Size, - ordering: AtomicRwOrd, - min: bool, - is_signed: bool, - rhs_scalar: Scalar, - ) -> InterpResult<'tcx, (Scalar, Scalar)> { - assert!(!self.allow_data_races.get()); - todo!() + _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + _address: Size, + _size: Size, + _ordering: AtomicRwOrd, + _min: bool, + _is_signed: bool, + _rhs_scalar: Scalar, + _old_value: Scalar, + ) -> InterpResult<'tcx, (Scalar, Option)> { + assert!( + !self.get_alloc_data_races(), + "atomic min/max operation with data race checking disabled." + ); + throw_unsup_format!("FIXME(genmc): Add support for atomic min/max.") } + /// Returns `(old_val, Option)`. `new_val` might not be the latest write in coherence order, which is indicated by `None`. + /// + /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized. pub(crate) fn atomic_exchange<'tcx>( &self, - ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - address: Size, - size: Size, - rhs_scalar: Scalar, - ordering: AtomicRwOrd, - ) -> InterpResult<'tcx, (Scalar, bool)> { - assert!(!self.allow_data_races.get()); - todo!() + _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + _address: Size, + _size: Size, + _rhs_scalar: Scalar, + _ordering: AtomicRwOrd, + _old_value: Scalar, + ) -> InterpResult<'tcx, (Scalar, Option)> { + assert!( + !self.get_alloc_data_races(), + "atomic swap operation with data race checking disabled." + ); + throw_unsup_format!("FIXME(genmc): Add support for atomic swap.") } + /// Inform GenMC about an atomic compare-exchange operation. + /// + /// Returns the old value read by the compare exchange, optionally the value that Miri should write back to its memory, and whether the compare-exchange was a success or not. + /// + /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized. pub(crate) fn atomic_compare_exchange<'tcx>( &self, - ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - address: Size, - size: Size, - expected_old_value: Scalar, - new_value: Scalar, - success: AtomicRwOrd, - fail: AtomicReadOrd, - can_fail_spuriously: bool, - ) -> InterpResult<'tcx, (Scalar, bool)> { - assert!(!self.allow_data_races.get()); - todo!() + _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + _address: Size, + _size: Size, + _expected_old_value: Scalar, + _new_value: Scalar, + _success: AtomicRwOrd, + _fail: AtomicReadOrd, + _can_fail_spuriously: bool, + _old_value: Scalar, + ) -> InterpResult<'tcx, (Scalar, Option, bool)> { + assert!( + !self.get_alloc_data_races(), + "atomic compare-exchange with data race checking disabled." + ); + throw_unsup_format!("FIXME(genmc): Add support for atomic compare_exchange.") } /// Inform GenMC about a non-atomic memory load @@ -188,40 +384,178 @@ impl GenmcCtx { machine: &MiriMachine<'tcx>, address: Size, size: Size, - ) -> InterpResult<'tcx, ()> { - todo!() + ) -> InterpResult<'tcx> { + debug!( + "GenMC: received memory_load (non-atomic): address: {:#x}, size: {}", + address.bytes(), + size.bytes() + ); + if self.get_alloc_data_races() { + debug!("GenMC: data race checking disabled, ignoring non-atomic load."); + return interp_ok(()); + } + // GenMC doesn't like ZSTs, and they can't have any data races, so we skip them + if size.bytes() == 0 { + return interp_ok(()); + } + + let handle_load = |address, size| { + // NOTE: Values loaded non-atomically are still handled by Miri, so we discard whatever we get from GenMC + let _read_value = self.handle_load( + machine, + address, + size, + MemOrdering::NotAtomic, + // This value is used to update the co-maximal store event to the same location. + // We don't need to update that store, since if it is ever read by any atomic loads, the value will be updated then. + // We use uninit for lack of a better value, since we don't know whether the location we currently load from is initialized or not. + GenmcScalar::UNINIT, + )?; + interp_ok(()) + }; + + // This load is small enough so GenMC can handle it. + if size.bytes() <= MAX_ACCESS_SIZE { + return handle_load(address, size); + } + + // This load is too big to be a single GenMC access, we have to split it. + // FIXME(genmc): This will misbehave if there are non-64bit-atomics in there. + // Needs proper support on the GenMC side for large and mixed atomic accesses. + for (address, size) in split_access(address, size) { + handle_load(Size::from_bytes(address), Size::from_bytes(size))?; + } + interp_ok(()) } + /// Inform GenMC about a non-atomic memory store + /// + /// NOTE: Unlike for *atomic* stores, we don't provide the actual stored values to GenMC here. pub(crate) fn memory_store<'tcx>( &self, machine: &MiriMachine<'tcx>, address: Size, size: Size, - ) -> InterpResult<'tcx, ()> { - todo!() + ) -> InterpResult<'tcx> { + debug!( + "GenMC: received memory_store (non-atomic): address: {:#x}, size: {}", + address.bytes(), + size.bytes() + ); + if self.get_alloc_data_races() { + debug!("GenMC: data race checking disabled, ignoring non-atomic store."); + return interp_ok(()); + } + // GenMC doesn't like ZSTs, and they can't have any data races, so we skip them + if size.bytes() == 0 { + return interp_ok(()); + } + + let handle_store = |address, size| { + // We always write the the stored values to Miri's memory, whether GenMC says the write is co-maximal or not. + // The GenMC scheduler ensures that replaying an execution happens in porf-respecting order (po := program order, rf: reads-from order). + // This means that for any non-atomic read Miri performs, the corresponding write has already been replayed. + let _is_co_max_write = self.handle_store( + machine, + address, + size, + // We don't know the value that this store will write, but GenMC expects that we give it an actual value. + // Unfortunately, there are situations where this value can actually become visible + // to the program: when there is an atomic load reading from a non-atomic store. + // FIXME(genmc): update once mixed atomic-non-atomic support is added. Afterwards, this value should never be readable. + GenmcScalar::from_u64(0xDEADBEEF), + // This value is used to update the co-maximal store event to the same location. + // This old value cannot be read anymore by any future loads, since we are doing another non-atomic store to the same location. + // Any future load will either see the store we are adding now, or we have a data race (there can only be one possible non-atomic value to read from at any time). + // We use uninit for lack of a better value, since we don't know whether the location we currently write to is initialized or not. + GenmcScalar::UNINIT, + MemOrdering::NotAtomic, + )?; + interp_ok(()) + }; + + // This store is small enough so GenMC can handle it. + if size.bytes() <= MAX_ACCESS_SIZE { + return handle_store(address, size); + } + + // This store is too big to be a single GenMC access, we have to split it. + // FIXME(genmc): This will misbehave if there are non-64bit-atomics in there. + // Needs proper support on the GenMC side for large and mixed atomic accesses. + for (address, size) in split_access(address, size) { + handle_store(Size::from_bytes(address), Size::from_bytes(size))?; + } + interp_ok(()) } /**** Memory (de)allocation ****/ + /// This is also responsible for determining the address of the new allocation. pub(crate) fn handle_alloc<'tcx>( &self, - machine: &MiriMachine<'tcx>, + ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + alloc_id: AllocId, size: Size, alignment: Align, memory_kind: MemoryKind, ) -> InterpResult<'tcx, u64> { - todo!() + assert!( + !self.get_alloc_data_races(), + "memory allocation with data race checking disabled." + ); + let machine = &ecx.machine; + if memory_kind == MiriMemoryKind::Global.into() { + return ecx + .get_global_allocation_address(&self.global_state.global_allocations, alloc_id); + } + let thread_infos = self.exec_state.thread_id_manager.borrow(); + let curr_thread = machine.threads.active_thread(); + let genmc_tid = thread_infos.get_genmc_tid(curr_thread); + // GenMC doesn't support ZSTs, so we set the minimum size to 1 byte + let genmc_size = size.bytes().max(1); + + let mut mc = self.handle.borrow_mut(); + let pinned_mc = mc.as_mut().unwrap(); + let chosen_address = pinned_mc.handle_malloc(genmc_tid, genmc_size, alignment.bytes()); + + // Non-global addresses should not be in the global address space or null. + assert_ne!(0, chosen_address, "GenMC malloc returned nullptr."); + assert_eq!(0, chosen_address & GENMC_GLOBAL_ADDRESSES_MASK); + // Sanity check the address alignment: + assert!( + chosen_address.is_multiple_of(alignment.bytes()), + "GenMC returned address {chosen_address:#x} with lower alignment than requested ({}).", + alignment.bytes() + ); + + interp_ok(chosen_address) } pub(crate) fn handle_dealloc<'tcx>( &self, machine: &MiriMachine<'tcx>, + alloc_id: AllocId, address: Size, - size: Size, - align: Align, kind: MemoryKind, - ) -> InterpResult<'tcx, ()> { - todo!() + ) -> InterpResult<'tcx> { + assert_ne!( + kind, + MiriMemoryKind::Global.into(), + "we probably shouldn't try to deallocate global allocations (alloc_id: {alloc_id:?})" + ); + assert!( + !self.get_alloc_data_races(), + "memory deallocation with data race checking disabled." + ); + let thread_infos = self.exec_state.thread_id_manager.borrow(); + let curr_thread = machine.threads.active_thread(); + let genmc_tid = thread_infos.get_genmc_tid(curr_thread); + + let mut mc = self.handle.borrow_mut(); + let pinned_mc = mc.as_mut().unwrap(); + pinned_mc.handle_free(genmc_tid, address.bytes()); + + interp_ok(()) } /**** Thread management ****/ @@ -229,50 +563,95 @@ impl GenmcCtx { pub(crate) fn handle_thread_create<'tcx>( &self, threads: &ThreadManager<'tcx>, + // FIXME(genmc,symmetry reduction): pass info to GenMC + _start_routine: crate::Pointer, + _func_arg: &crate::ImmTy<'tcx>, new_thread_id: ThreadId, - ) -> InterpResult<'tcx, ()> { - assert!(!self.allow_data_races.get()); - todo!() + ) -> InterpResult<'tcx> { + assert!(!self.get_alloc_data_races(), "thread creation with data race checking disabled."); + let mut thread_infos = self.exec_state.thread_id_manager.borrow_mut(); + + let curr_thread_id = threads.active_thread(); + let genmc_parent_tid = thread_infos.get_genmc_tid(curr_thread_id); + let genmc_new_tid = thread_infos.add_thread(new_thread_id); + + let mut mc = self.handle.borrow_mut(); + mc.as_mut().unwrap().handle_thread_create(genmc_new_tid, genmc_parent_tid); + + interp_ok(()) } pub(crate) fn handle_thread_join<'tcx>( &self, active_thread_id: ThreadId, child_thread_id: ThreadId, - ) -> InterpResult<'tcx, ()> { - assert!(!self.allow_data_races.get()); - todo!() + ) -> InterpResult<'tcx> { + assert!(!self.get_alloc_data_races(), "thread join with data race checking disabled."); + let thread_infos = self.exec_state.thread_id_manager.borrow(); + + let genmc_curr_tid = thread_infos.get_genmc_tid(active_thread_id); + let genmc_child_tid = thread_infos.get_genmc_tid(child_thread_id); + + let mut mc = self.handle.borrow_mut(); + mc.as_mut().unwrap().handle_thread_join(genmc_curr_tid, genmc_child_tid); + + interp_ok(()) } - pub(crate) fn handle_thread_stack_empty(&self, thread_id: ThreadId) { - todo!() + pub(crate) fn handle_thread_finish<'tcx>(&self, threads: &ThreadManager<'tcx>) { + assert!(!self.get_alloc_data_races(), "thread finish with data race checking disabled."); + let curr_thread_id = threads.active_thread(); + + let thread_infos = self.exec_state.thread_id_manager.borrow(); + let genmc_tid = thread_infos.get_genmc_tid(curr_thread_id); + + debug!("GenMC: thread {curr_thread_id:?} ({genmc_tid:?}) finished."); + let mut mc = self.handle.borrow_mut(); + // NOTE: Miri doesn't support return values for threads, but GenMC expects one, so we return 0 + mc.as_mut().unwrap().handle_thread_finish(genmc_tid, /* ret_val */ 0); } - pub(crate) fn handle_thread_finish<'tcx>( + /// Handle a call to `libc::exit` or the exit of the main thread. + /// Unless an error is returned, the program should continue executing (in a different thread, chosen by the next scheduling call). + pub(crate) fn handle_exit<'tcx>( &self, - threads: &ThreadManager<'tcx>, - ) -> InterpResult<'tcx, ()> { - assert!(!self.allow_data_races.get()); - todo!() - } + thread: ThreadId, + exit_code: i32, + exit_type: ExitType, + ) -> InterpResult<'tcx> { + // Calling `libc::exit` doesn't do cleanup, so we skip the leak check in that case. + let exit_status = ExitStatus { exit_code, exit_type }; - /**** Scheduling functionality ****/ + if let Some(old_exit_status) = self.exec_state.exit_status.get() { + throw_ub_format!( + "`exit` called twice, first with status {old_exit_status:?}, now with status {exit_status:?}", + ); + } - /// Ask for a scheduling decision. This should be called before every MIR instruction. - /// - /// GenMC may realize that the execution got stuck, then this function will return a `InterpErrorKind::MachineStop` with error kind `TerminationInfo::GenmcStuckExecution`). - /// - /// This is **not** an error by iself! Treat this as if the program ended normally: `handle_execution_end` should be called next, which will determine if were are any actual errors. - pub(crate) fn schedule_thread<'tcx>( - &self, - ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - ) -> InterpResult<'tcx, ThreadId> { - assert!(!self.allow_data_races.get()); - todo!() + // FIXME(genmc): Add a flag to continue exploration even when the program exits with a non-zero exit code. + if exit_code != 0 { + info!("GenMC: 'exit' called with non-zero argument, aborting execution."); + let leak_check = exit_status.do_leak_check(); + throw_machine_stop!(TerminationInfo::Exit { code: exit_code, leak_check }); + } + + if matches!(exit_type, ExitType::ExitCalled) { + let thread_infos = self.exec_state.thread_id_manager.borrow(); + let genmc_tid = thread_infos.get_genmc_tid(thread); + + let mut mc = self.handle.borrow_mut(); + mc.as_mut().unwrap().handle_thread_kill(genmc_tid); + } else { + assert_eq!(thread, ThreadId::MAIN_THREAD); + } + // We continue executing now, so we store the exit status. + self.exec_state.exit_status.set(Some(exit_status)); + interp_ok(()) } /**** Blocking instructions ****/ + #[allow(unused)] pub(crate) fn handle_verifier_assume<'tcx>( &self, machine: &MiriMachine<'tcx>, @@ -282,14 +661,119 @@ impl GenmcCtx { } } +impl GenmcCtx { + /// Inform GenMC about a load (atomic or non-atomic). + /// Returns the value that GenMC wants this load to read. + fn handle_load<'tcx>( + &self, + machine: &MiriMachine<'tcx>, + address: Size, + size: Size, + memory_ordering: MemOrdering, + genmc_old_value: GenmcScalar, + ) -> InterpResult<'tcx, GenmcScalar> { + assert!( + size.bytes() != 0 + && (memory_ordering == MemOrdering::NotAtomic || size.bytes().is_power_of_two()) + ); + if size.bytes() > MAX_ACCESS_SIZE { + throw_unsup_format!( + "GenMC mode currently does not support atomics larger than {MAX_ACCESS_SIZE} bytes.", + ); + } + let thread_infos = self.exec_state.thread_id_manager.borrow(); + let curr_thread_id = machine.threads.active_thread(); + let genmc_tid = thread_infos.get_genmc_tid(curr_thread_id); + + debug!( + "GenMC: load, thread: {curr_thread_id:?} ({genmc_tid:?}), address: {addr} == {addr:#x}, size: {size:?}, ordering: {memory_ordering:?}, old_value: {genmc_old_value:x?}", + addr = address.bytes() + ); + + let mut mc = self.handle.borrow_mut(); + let pinned_mc = mc.as_mut().unwrap(); + let load_result = pinned_mc.handle_load( + genmc_tid, + address.bytes(), + size.bytes(), + memory_ordering, + genmc_old_value, + ); + + if let Some(error) = load_result.error.as_ref() { + // FIXME(genmc): error handling + throw_ub_format!("{}", error.to_string_lossy()); + } + + if !load_result.has_value { + // FIXME(GenMC): Implementing certain GenMC optimizations will lead to this. + unimplemented!("GenMC: load returned no value."); + } + + debug!("GenMC: load returned value: {:?}", load_result.read_value); + interp_ok(load_result.read_value) + } + + /// Inform GenMC about a store (atomic or non-atomic). + /// Returns true if the store is co-maximal, i.e., it should be written to Miri's memory too. + fn handle_store<'tcx>( + &self, + machine: &MiriMachine<'tcx>, + address: Size, + size: Size, + genmc_value: GenmcScalar, + genmc_old_value: GenmcScalar, + memory_ordering: MemOrdering, + ) -> InterpResult<'tcx, bool> { + assert!( + size.bytes() != 0 + && (memory_ordering == MemOrdering::NotAtomic || size.bytes().is_power_of_two()) + ); + if size.bytes() > MAX_ACCESS_SIZE { + throw_unsup_format!( + "GenMC mode currently does not support atomics larger than {MAX_ACCESS_SIZE} bytes." + ); + } + let thread_infos = self.exec_state.thread_id_manager.borrow(); + let curr_thread_id = machine.threads.active_thread(); + let genmc_tid = thread_infos.get_genmc_tid(curr_thread_id); + + debug!( + "GenMC: store, thread: {curr_thread_id:?} ({genmc_tid:?}), address: {addr} = {addr:#x}, size: {size:?}, ordering {memory_ordering:?}, value: {genmc_value:?}", + addr = address.bytes() + ); + + let mut mc = self.handle.borrow_mut(); + let pinned_mc = mc.as_mut().unwrap(); + let store_result = pinned_mc.handle_store( + genmc_tid, + address.bytes(), + size.bytes(), + genmc_value, + genmc_old_value, + memory_ordering, + ); + + if let Some(error) = store_result.error.as_ref() { + // FIXME(genmc): error handling + throw_ub_format!("{}", error.to_string_lossy()); + } + + interp_ok(store_result.is_coherence_order_maximal_write) + } + + /**** Blocking functionality ****/ + + /// Handle a user thread getting blocked. + /// This may happen due to an manual `assume` statement added by a user + /// or added by some automated program transformation, e.g., for spinloops. + fn handle_user_block<'tcx>(&self, _machine: &MiriMachine<'tcx>) -> InterpResult<'tcx> { + todo!() + } +} + impl VisitProvenance for GenmcCtx { fn visit_provenance(&self, _visit: &mut VisitWith<'_>) { // We don't have any tags. } } - -impl GenmcCtx { - fn handle_user_block<'tcx>(&self, machine: &MiriMachine<'tcx>) -> InterpResult<'tcx, ()> { - todo!() - } -} diff --git a/src/tools/miri/src/concurrency/genmc/run.rs b/src/tools/miri/src/concurrency/genmc/run.rs new file mode 100644 index 000000000000..62553297a202 --- /dev/null +++ b/src/tools/miri/src/concurrency/genmc/run.rs @@ -0,0 +1,71 @@ +use std::rc::Rc; +use std::sync::Arc; + +use rustc_middle::ty::TyCtxt; + +use super::GlobalState; +use crate::rustc_const_eval::interpret::PointerArithmetic; +use crate::{GenmcCtx, MiriConfig}; + +/// Do a complete run of the program in GenMC mode. +/// This will call `eval_entry` multiple times, until either: +/// - An error is detected (indicated by a `None` return value) +/// - All possible executions are explored. +/// +/// FIXME(genmc): add estimation mode setting. +pub fn run_genmc_mode<'tcx>( + config: &MiriConfig, + eval_entry: impl Fn(Rc) -> Option, + tcx: TyCtxt<'tcx>, +) -> Option { + // There exists only one `global_state` per full run in GenMC mode. + // It is shared by all `GenmcCtx` in this run. + // FIXME(genmc): implement multithreading once GenMC supports it. + let global_state = Arc::new(GlobalState::new(tcx.target_usize_max())); + let genmc_ctx = Rc::new(GenmcCtx::new(config, global_state)); + + // `rep` is used to report the progress, Miri will panic on wrap-around. + for rep in 0u64.. { + tracing::info!("Miri-GenMC loop {}", rep + 1); + + // Prepare for the next execution and inform GenMC about it. + genmc_ctx.prepare_next_execution(); + + // Execute the program until completion to get the return value, or return if an error happens: + // FIXME(genmc): add an option to allow the user to see the GenMC output message when the verification is done. + let return_code = eval_entry(genmc_ctx.clone())?; + + // We inform GenMC that the execution is complete. If there was an error, we print it. + if let Some(error) = genmc_ctx.handle_execution_end() { + // This can be reached for errors that affect the entire execution, not just a specific event. + // For instance, linearizability checking and liveness checking report their errors this way. + // Neither are supported by Miri-GenMC at the moment though. However, GenMC also + // treats races on deallocation as global errors, so this code path is still reachable. + // Since we don't have any span information for the error at this point, + // we just print GenMC's error message. + eprintln!("(GenMC) Error detected: {error}"); + eprintln!(); + eprintln!("{}", genmc_ctx.get_result_message()); + return None; + } + + // Check if we've explored enough executions: + if !genmc_ctx.is_exploration_done() { + continue; + } + + eprintln!("(GenMC) Verification complete. No errors were detected."); + + let explored_execution_count = genmc_ctx.get_explored_execution_count(); + let blocked_execution_count = genmc_ctx.get_blocked_execution_count(); + + eprintln!("Number of complete executions explored: {explored_execution_count}"); + if blocked_execution_count > 0 { + eprintln!("Number of blocked executions seen: {blocked_execution_count}"); + } + + // Return the return code of the last execution. + return Some(return_code); + } + unreachable!() +} diff --git a/src/tools/miri/src/concurrency/genmc/scheduling.rs b/src/tools/miri/src/concurrency/genmc/scheduling.rs new file mode 100644 index 000000000000..b5c23f2d0845 --- /dev/null +++ b/src/tools/miri/src/concurrency/genmc/scheduling.rs @@ -0,0 +1,69 @@ +use genmc_sys::{ActionKind, ExecutionState}; + +use super::GenmcCtx; +use crate::{ + InterpCx, InterpResult, MiriMachine, TerminationInfo, ThreadId, interp_ok, throw_machine_stop, +}; + +impl GenmcCtx { + pub(crate) fn schedule_thread<'tcx>( + &self, + ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + ) -> InterpResult<'tcx, ThreadId> { + let thread_manager = &ecx.machine.threads; + let active_thread_id = thread_manager.active_thread(); + + // Determine whether the next instruction in the current thread might be a load. + // This is used for the "writes-first" scheduling in GenMC. + // Scheduling writes before reads can be beneficial for verification performance. + // `Load` is a safe default for the next instruction type if we cannot guarantee that it isn't a load. + let curr_thread_next_instr_kind = if !thread_manager.active_thread_ref().is_enabled() { + // The current thread can get blocked (e.g., due to a thread join, `Mutex::lock`, assume statement, ...), then we need to ask GenMC for another thread to schedule. + // Most to all blocking operations have load semantics, since they wait on something to change in another thread, + // e.g., a thread join waiting on another thread to finish (join loads the return value(s) of the other thread), + // or a thread waiting for another thread to unlock a `Mutex`, which loads the mutex state (Locked, Unlocked). + ActionKind::Load + } else { + // This thread is still enabled. If it executes a terminator next, we consider yielding, + // but in all other cases we just keep running this thread since it never makes sense + // to yield before a non-atomic operation. + let Some(frame) = thread_manager.active_thread_stack().last() else { + return interp_ok(active_thread_id); + }; + let either::Either::Left(loc) = frame.current_loc() else { + // We are unwinding, so the next step is definitely not atomic. + return interp_ok(active_thread_id); + }; + let basic_block = &frame.body().basic_blocks[loc.block]; + if let Some(_statement) = basic_block.statements.get(loc.statement_index) { + // Statements can't be atomic. + return interp_ok(active_thread_id); + } + + // FIXME(genmc): determine terminator kind. + ActionKind::Load + }; + + let thread_infos = self.exec_state.thread_id_manager.borrow(); + let genmc_tid = thread_infos.get_genmc_tid(active_thread_id); + + let mut mc = self.handle.borrow_mut(); + let pinned_mc = mc.as_mut().unwrap(); + let result = pinned_mc.schedule_next(genmc_tid, curr_thread_next_instr_kind); + // Depending on the exec_state, we either schedule the given thread, or we are finished with this execution. + match result.exec_state { + ExecutionState::Ok => interp_ok(thread_infos.get_miri_tid(result.next_thread)), + ExecutionState::Blocked => throw_machine_stop!(TerminationInfo::GenmcBlockedExecution), + ExecutionState::Finished => { + let exit_status = self.exec_state.exit_status.get().expect( + "If the execution is finished, we should have a return value from the program.", + ); + throw_machine_stop!(TerminationInfo::Exit { + code: exit_status.exit_code, + leak_check: exit_status.do_leak_check() + }); + } + _ => unreachable!(), + } + } +} diff --git a/src/tools/miri/src/concurrency/genmc/thread_id_map.rs b/src/tools/miri/src/concurrency/genmc/thread_id_map.rs new file mode 100644 index 000000000000..9676496fe7fe --- /dev/null +++ b/src/tools/miri/src/concurrency/genmc/thread_id_map.rs @@ -0,0 +1,63 @@ +use genmc_sys::GENMC_MAIN_THREAD_ID; +use rustc_data_structures::fx::FxHashMap; + +use crate::ThreadId; + +#[derive(Debug)] +pub struct ThreadIdMap { + /// Map from Miri thread IDs to GenMC thread IDs. + /// We assume as little as possible about Miri thread IDs, so we use a map. + miri_to_genmc: FxHashMap, + /// Map from GenMC thread IDs to Miri thread IDs. + /// We control which thread IDs are used, so we choose them in as an incrementing counter. + genmc_to_miri: Vec, // FIXME(genmc): check if this assumption is (and will stay) correct. +} + +impl Default for ThreadIdMap { + fn default() -> Self { + let miri_to_genmc = [(ThreadId::MAIN_THREAD, GENMC_MAIN_THREAD_ID)].into_iter().collect(); + let genmc_to_miri = vec![ThreadId::MAIN_THREAD]; + Self { miri_to_genmc, genmc_to_miri } + } +} + +impl ThreadIdMap { + pub fn reset(&mut self) { + self.miri_to_genmc.clear(); + self.miri_to_genmc.insert(ThreadId::MAIN_THREAD, GENMC_MAIN_THREAD_ID); + self.genmc_to_miri.clear(); + self.genmc_to_miri.push(ThreadId::MAIN_THREAD); + } + + #[must_use] + /// Add a new Miri thread to the mapping and dispense a new thread ID for GenMC to use. + pub fn add_thread(&mut self, thread_id: ThreadId) -> i32 { + // NOTE: We select the new thread ids as integers incremented by one (we use the length as the counter). + let next_thread_id = self.genmc_to_miri.len(); + let genmc_tid = next_thread_id.try_into().unwrap(); + // If there is already an entry, we override it. + // This could happen if Miri were to reuse `ThreadId`s, but we assume that if this happens, the previous thread with that id doesn't exist anymore. + self.miri_to_genmc.insert(thread_id, genmc_tid); + self.genmc_to_miri.push(thread_id); + + genmc_tid + } + + #[must_use] + /// Try to get the GenMC thread ID corresponding to a given Miri `ThreadId`. + /// Panics if there is no mapping for the given `ThreadId`. + pub fn get_genmc_tid(&self, thread_id: ThreadId) -> i32 { + *self.miri_to_genmc.get(&thread_id).unwrap() + } + + #[must_use] + /// Get the Miri `ThreadId` corresponding to a given GenMC thread id. + /// Panics if the given thread id isn't valid. + pub fn get_miri_tid(&self, genmc_tid: i32) -> ThreadId { + let index: usize = genmc_tid.try_into().unwrap(); + self.genmc_to_miri + .get(index) + .copied() + .expect("A thread id returned from GenMC should exist.") + } +} diff --git a/src/tools/miri/src/concurrency/init_once.rs b/src/tools/miri/src/concurrency/init_once.rs index 165215f9270c..3fb3d890419f 100644 --- a/src/tools/miri/src/concurrency/init_once.rs +++ b/src/tools/miri/src/concurrency/init_once.rs @@ -142,7 +142,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// Synchronize with the previous completion of an InitOnce. /// Must only be called after checking that it is complete. #[inline] - fn init_once_observe_completed(&mut self, init_once_ref: &InitOnceRef) { + fn init_once_observe_completed(&mut self, init_once_ref: &InitOnceRef) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let init_once = init_once_ref.0.borrow(); @@ -152,6 +152,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "observing the completion of incomplete init once" ); - this.acquire_clock(&init_once.clock); + this.acquire_clock(&init_once.clock) } } diff --git a/src/tools/miri/src/concurrency/mod.rs b/src/tools/miri/src/concurrency/mod.rs index 435615efd9fa..9dae858592f1 100644 --- a/src/tools/miri/src/concurrency/mod.rs +++ b/src/tools/miri/src/concurrency/mod.rs @@ -22,5 +22,5 @@ pub mod weak_memory; mod genmc; pub use self::data_race_handler::{AllocDataRaceHandler, GlobalDataRaceHandler}; -pub use self::genmc::{GenmcConfig, GenmcCtx}; +pub use self::genmc::{ExitType, GenmcConfig, GenmcCtx, run_genmc_mode}; pub use self::vector_clock::VClock; diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index fe1ef86ccd31..00c5e337b1e9 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -209,6 +209,11 @@ impl<'tcx> Thread<'tcx> { self.thread_name.as_deref() } + /// Return whether this thread is enabled or not. + pub fn is_enabled(&self) -> bool { + self.state.is_enabled() + } + /// Get the name of the current thread for display purposes; will include thread ID if not set. fn thread_display_name(&self, id: ThreadId) -> String { if let Some(ref thread_name) = self.thread_name { @@ -404,6 +409,7 @@ pub struct ThreadManager<'tcx> { /// A mapping from a thread-local static to the thread specific allocation. thread_local_allocs: FxHashMap<(DefId, ThreadId), StrictPointer>, /// A flag that indicates that we should change the active thread. + /// Completely ignored in GenMC mode. yield_active_thread: bool, /// A flag that indicates that we should do round robin scheduling of threads else randomized scheduling is used. fixed_scheduling: bool, @@ -676,13 +682,6 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { #[inline] fn run_on_stack_empty(&mut self) -> InterpResult<'tcx, Poll<()>> { let this = self.eval_context_mut(); - // Inform GenMC that a thread has finished all user code. GenMC needs to know this for scheduling. - // FIXME(GenMC): Thread-local destructors *are* user code, so this is odd. Also now that we - // support pre-main constructors, it can get called there as well. - if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { - let thread_id = this.active_thread(); - genmc_ctx.handle_thread_stack_empty(thread_id); - } let mut callback = this .active_thread_mut() .on_stack_empty @@ -703,19 +702,19 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { /// If GenMC mode is active, the scheduling is instead handled by GenMC. fn schedule(&mut self) -> InterpResult<'tcx, SchedulingAction> { let this = self.eval_context_mut(); - // In GenMC mode, we let GenMC do the scheduling + + // In GenMC mode, we let GenMC do the scheduling. if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { let next_thread_id = genmc_ctx.schedule_thread(this)?; let thread_manager = &mut this.machine.threads; thread_manager.active_thread = next_thread_id; - thread_manager.yield_active_thread = false; assert!(thread_manager.threads[thread_manager.active_thread].state.is_enabled()); return interp_ok(SchedulingAction::ExecuteStep); } - // We are not in GenMC mode, so we control the schedule + // We are not in GenMC mode, so we control the scheduling. let thread_manager = &mut this.machine.threads; let clock = &this.machine.monotonic_clock; let rng = this.machine.rng.get_mut(); @@ -863,7 +862,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { GlobalDataRaceHandler::Vclocks(data_race) => data_race.thread_created(&this.machine.threads, new_thread_id, current_span), GlobalDataRaceHandler::Genmc(genmc_ctx) => - genmc_ctx.handle_thread_create(&this.machine.threads, new_thread_id)?, + genmc_ctx.handle_thread_create( + &this.machine.threads, + start_routine, + &func_arg, + new_thread_id, + )?, } // Write the current thread-id, switch to the next thread later // to treat this write operation as occurring on the current thread. @@ -916,13 +920,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let thread = this.active_thread_mut(); assert!(thread.stack.is_empty(), "only threads with an empty stack can be terminated"); thread.state = ThreadState::Terminated; - match &mut this.machine.data_race { - GlobalDataRaceHandler::None => {} - GlobalDataRaceHandler::Vclocks(data_race) => - data_race.thread_terminated(&this.machine.threads), - GlobalDataRaceHandler::Genmc(genmc_ctx) => - genmc_ctx.handle_thread_finish(&this.machine.threads)?, - } + // Deallocate TLS. let gone_thread = this.active_thread(); { @@ -953,6 +951,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } } + + match &mut this.machine.data_race { + GlobalDataRaceHandler::None => {} + GlobalDataRaceHandler::Vclocks(data_race) => + data_race.thread_terminated(&this.machine.threads), + GlobalDataRaceHandler::Genmc(genmc_ctx) => { + // Inform GenMC that the thread finished. + // This needs to happen once all accesses to the thread are done, including freeing any TLS statics. + genmc_ctx.handle_thread_finish(&this.machine.threads) + } + } + // Unblock joining threads. let unblock_reason = BlockReason::Join(gone_thread); let threads = &this.machine.threads.threads; @@ -978,6 +988,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { callback: DynUnblockCallback<'tcx>, ) { let this = self.eval_context_mut(); + if timeout.is_some() && this.machine.data_race.as_genmc_ref().is_some() { + panic!("Unimplemented: Timeouts not yet supported in GenMC mode."); + } let timeout = timeout.map(|(clock, anchor, duration)| { let anchor = match clock { TimeoutClock::RealTime => { @@ -1076,6 +1089,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "{:?} blocked on {:?} when trying to join", thread_mgr.active_thread, joined_thread_id ); + if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { + genmc_ctx.handle_thread_join(thread_mgr.active_thread, joined_thread_id)?; + } + // The joined thread is still running, we need to wait for it. // Once we get unblocked, perform the appropriate synchronization and write the return value. let dest = return_dest.clone(); diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 9ecbd31c5b9f..34f560962e66 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -31,8 +31,9 @@ pub enum TerminationInfo { }, Int2PtrWithStrictProvenance, Deadlock, - /// In GenMC mode, an execution can get stuck in certain cases. This is not an error. - GenmcStuckExecution, + /// In GenMC mode, executions can get blocked, which stops the current execution without running any cleanup. + /// No leak checks should be performed if this happens, since they would give false positives. + GenmcBlockedExecution, MultipleSymbolDefinitions { link_name: Symbol, first: SpanData, @@ -77,7 +78,8 @@ impl fmt::Display for TerminationInfo { StackedBorrowsUb { msg, .. } => write!(f, "{msg}"), TreeBorrowsUb { title, .. } => write!(f, "{title}"), Deadlock => write!(f, "the evaluated program deadlocked"), - GenmcStuckExecution => write!(f, "GenMC determined that the execution got stuck"), + GenmcBlockedExecution => + write!(f, "GenMC determined that the execution got blocked (this is not an error)"), MultipleSymbolDefinitions { link_name, .. } => write!(f, "multiple definitions of symbol `{link_name}`"), SymbolShimClashing { link_name, .. } => @@ -243,11 +245,12 @@ pub fn report_error<'tcx>( labels.push(format!("this thread got stuck here")); None } - GenmcStuckExecution => { - // This case should only happen in GenMC mode. We treat it like a normal program exit. + GenmcBlockedExecution => { + // This case should only happen in GenMC mode. assert!(ecx.machine.data_race.as_genmc_ref().is_some()); - tracing::info!("GenMC: found stuck execution"); - return Some((0, true)); + // The program got blocked by GenMC without finishing the execution. + // No cleanup code was executed, so we don't do any leak checks. + return Some((0, false)); } MultipleSymbolDefinitions { .. } | SymbolShimClashing { .. } => None, }; diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index 4c531a8d1f52..e6c2163105ad 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -303,8 +303,21 @@ impl<'tcx> MainThreadState<'tcx> { // to be like a global `static`, so that all memory reached by it is considered to "not leak". this.terminate_active_thread(TlsAllocAction::Leak)?; - // Stop interpreter loop. - throw_machine_stop!(TerminationInfo::Exit { code: exit_code, leak_check: true }); + // In GenMC mode, we do not immediately stop execution on main thread exit. + if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { + // If there's no error, execution will continue (on another thread). + genmc_ctx.handle_exit( + ThreadId::MAIN_THREAD, + exit_code, + crate::concurrency::ExitType::MainThreadFinish, + )?; + } else { + // Stop interpreter loop. + throw_machine_stop!(TerminationInfo::Exit { + code: exit_code, + leak_check: true + }); + } } } interp_ok(Poll::Pending) @@ -505,10 +518,6 @@ pub fn eval_entry<'tcx>( // Copy setting before we move `config`. let ignore_leaks = config.ignore_leaks; - if let Some(genmc_ctx) = &genmc_ctx { - genmc_ctx.handle_execution_start(); - } - let mut ecx = match create_ecx(tcx, entry_id, entry_type, config, genmc_ctx).report_err() { Ok(v) => v, Err(err) => { @@ -532,15 +541,6 @@ pub fn eval_entry<'tcx>( // Show diagnostic, if any. let (return_code, leak_check) = report_error(&ecx, err)?; - // We inform GenMC that the execution is complete. - if let Some(genmc_ctx) = ecx.machine.data_race.as_genmc_ref() - && let Err(error) = genmc_ctx.handle_execution_end(&ecx) - { - // FIXME(GenMC): Improve error reporting. - tcx.dcx().err(format!("GenMC returned an error: \"{error}\"")); - return None; - } - // If we get here there was no fatal error. // Possibly check for memory leaks. diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 507d4f7b4289..a9279bbefd8f 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -133,7 +133,7 @@ pub use crate::concurrency::thread::{ BlockReason, DynUnblockCallback, EvalContextExt as _, StackEmptyCallback, ThreadId, ThreadManager, TimeoutAnchor, TimeoutClock, UnblockKind, }; -pub use crate::concurrency::{GenmcConfig, GenmcCtx}; +pub use crate::concurrency::{GenmcConfig, GenmcCtx, run_genmc_mode}; pub use crate::data_structures::dedup_range_map::DedupRangeMap; pub use crate::data_structures::mono_hash_map::MonoHashMap; pub use crate::diagnostics::{ diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 9482769a2fc7..4bb6dd2aafe0 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -1427,9 +1427,8 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { } match &machine.data_race { GlobalDataRaceHandler::None => {} - GlobalDataRaceHandler::Genmc(genmc_ctx) => { - genmc_ctx.memory_store(machine, ptr.addr(), range.size)?; - } + GlobalDataRaceHandler::Genmc(genmc_ctx) => + genmc_ctx.memory_store(machine, ptr.addr(), range.size)?, GlobalDataRaceHandler::Vclocks(_global_state) => { let AllocDataRaceHandler::Vclocks(data_race, weak_memory) = &mut alloc_extra.data_race @@ -1465,7 +1464,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { match &machine.data_race { GlobalDataRaceHandler::None => {} GlobalDataRaceHandler::Genmc(genmc_ctx) => - genmc_ctx.handle_dealloc(machine, ptr.addr(), size, align, kind)?, + genmc_ctx.handle_dealloc(machine, alloc_id, ptr.addr(), kind)?, GlobalDataRaceHandler::Vclocks(_global_state) => { let data_race = alloc_extra.data_race.as_vclocks_mut().unwrap(); data_race.write( diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 21545b680299..c6bc29cb8cb4 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -443,13 +443,22 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { "exit" => { let [code] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let code = this.read_scalar(code)?.to_i32()?; + if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { + // If there is no error, execution should continue (on a different thread). + genmc_ctx.handle_exit( + this.machine.threads.active_thread(), + code, + crate::concurrency::ExitType::ExitCalled, + )?; + todo!(); // FIXME(genmc): Add a way to return here that is allowed to not do progress (can't use existing EmulateItemResult variants). + } throw_machine_stop!(TerminationInfo::Exit { code, leak_check: false }); } "abort" => { let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; throw_machine_stop!(TerminationInfo::Abort( "the program aborted execution".to_owned() - )) + )); } // Standard C allocation diff --git a/src/tools/miri/src/shims/unix/linux_like/epoll.rs b/src/tools/miri/src/shims/unix/linux_like/epoll.rs index d460abc783dd..aac880feda40 100644 --- a/src/tools/miri/src/shims/unix/linux_like/epoll.rs +++ b/src/tools/miri/src/shims/unix/linux_like/epoll.rs @@ -639,7 +639,7 @@ fn return_ready_list<'tcx>( &des.1, )?; // Synchronize waking thread with the event of interest. - ecx.acquire_clock(&epoll_event_instance.clock); + ecx.acquire_clock(&epoll_event_instance.clock)?; num_of_events = num_of_events.strict_add(1); } else { diff --git a/src/tools/miri/src/shims/unix/linux_like/eventfd.rs b/src/tools/miri/src/shims/unix/linux_like/eventfd.rs index 2d35ef064db8..ad8ea6410264 100644 --- a/src/tools/miri/src/shims/unix/linux_like/eventfd.rs +++ b/src/tools/miri/src/shims/unix/linux_like/eventfd.rs @@ -299,7 +299,7 @@ fn eventfd_read<'tcx>( ); } else { // Synchronize with all prior `write` calls to this FD. - ecx.acquire_clock(&eventfd.clock.borrow()); + ecx.acquire_clock(&eventfd.clock.borrow())?; // Return old counter value into user-space buffer. ecx.write_int(counter, &buf_place)?; diff --git a/src/tools/miri/src/shims/unix/unnamed_socket.rs b/src/tools/miri/src/shims/unix/unnamed_socket.rs index 817ddd7954df..52073b58e70d 100644 --- a/src/tools/miri/src/shims/unix/unnamed_socket.rs +++ b/src/tools/miri/src/shims/unix/unnamed_socket.rs @@ -337,7 +337,7 @@ fn anonsocket_read<'tcx>( // Synchronize with all previous writes to this buffer. // FIXME: this over-synchronizes; a more precise approach would be to // only sync with the writes whose data we will read. - ecx.acquire_clock(&readbuf.clock); + ecx.acquire_clock(&readbuf.clock)?; // Do full read / partial read based on the space available. // Conveniently, `read` exists on `VecDeque` and has exactly the desired behavior. diff --git a/src/tools/miri/src/shims/windows/sync.rs b/src/tools/miri/src/shims/windows/sync.rs index 9165e76b63d1..a893999ef8e5 100644 --- a/src/tools/miri/src/shims/windows/sync.rs +++ b/src/tools/miri/src/shims/windows/sync.rs @@ -60,7 +60,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { true } InitOnceStatus::Complete => { - this.init_once_observe_completed(init_once_ref); + this.init_once_observe_completed(init_once_ref)?; this.write_scalar(this.eval_windows("c", "FALSE"), pending_place)?; this.write_scalar(this.eval_windows("c", "TRUE"), dest)?; true diff --git a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rel_rlx.stderr b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rel_rlx.stderr new file mode 100644 index 000000000000..121ded2a181c --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rel_rlx.stderr @@ -0,0 +1,19 @@ +error: Undefined Behavior: Non-atomic race + --> tests/genmc/fail/data_race/weak_orderings.rs:LL:CC + | +LL | X = 2; + | ^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE on thread `unnamed-ID`: + = note: inside closure at tests/genmc/fail/data_race/weak_orderings.rs:LL:CC + = note: inside ` as std::ops::FnOnce<()>>::call_once` at RUSTLIB/alloc/src/boxed.rs:LL:CC +note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/fail/data_race/weak_orderings.rs:LL:CC}>` + --> tests/genmc/fail/data_race/../../../utils/genmc.rs:LL:CC + | +LL | f(); + | ^^^ + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_acq.stderr b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_acq.stderr new file mode 100644 index 000000000000..121ded2a181c --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_acq.stderr @@ -0,0 +1,19 @@ +error: Undefined Behavior: Non-atomic race + --> tests/genmc/fail/data_race/weak_orderings.rs:LL:CC + | +LL | X = 2; + | ^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE on thread `unnamed-ID`: + = note: inside closure at tests/genmc/fail/data_race/weak_orderings.rs:LL:CC + = note: inside ` as std::ops::FnOnce<()>>::call_once` at RUSTLIB/alloc/src/boxed.rs:LL:CC +note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/fail/data_race/weak_orderings.rs:LL:CC}>` + --> tests/genmc/fail/data_race/../../../utils/genmc.rs:LL:CC + | +LL | f(); + | ^^^ + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_rlx.stderr b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_rlx.stderr new file mode 100644 index 000000000000..121ded2a181c --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_rlx.stderr @@ -0,0 +1,19 @@ +error: Undefined Behavior: Non-atomic race + --> tests/genmc/fail/data_race/weak_orderings.rs:LL:CC + | +LL | X = 2; + | ^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE on thread `unnamed-ID`: + = note: inside closure at tests/genmc/fail/data_race/weak_orderings.rs:LL:CC + = note: inside ` as std::ops::FnOnce<()>>::call_once` at RUSTLIB/alloc/src/boxed.rs:LL:CC +note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/fail/data_race/weak_orderings.rs:LL:CC}>` + --> tests/genmc/fail/data_race/../../../utils/genmc.rs:LL:CC + | +LL | f(); + | ^^^ + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rs b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rs new file mode 100644 index 000000000000..1568a302f85a --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rs @@ -0,0 +1,40 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@revisions: rlx_rlx rlx_acq rel_rlx + +// Translated from GenMC's test `wrong/racy/MP+rel+rlx`, `MP+rlx+acq` and `MP+rlx+rlx`. +// Test if Miri with GenMC can detect the data race on `X`. +// Relaxed orderings on an atomic store-load pair should not synchronize the non-atomic write to X, leading to a data race. + +// FIXME(genmc): once Miri-GenMC error reporting is improved, ensure that it correctly points to the two spans involved in the data race. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::{self, *}; + +use genmc::spawn_pthread_closure; + +static mut X: u64 = 0; +static Y: AtomicUsize = AtomicUsize::new(0); + +const STORE_ORD: Ordering = if cfg!(rel_rlx) { Release } else { Relaxed }; +const LOAD_ORD: Ordering = if cfg!(rlx_acq) { Acquire } else { Relaxed }; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + spawn_pthread_closure(|| { + X = 1; + Y.store(1, STORE_ORD); + }); + spawn_pthread_closure(|| { + if Y.load(LOAD_ORD) != 0 { + X = 2; //~ ERROR: Undefined Behavior: Non-atomic race + } + }); + } + 0 +} diff --git a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr new file mode 100644 index 000000000000..e2b114df652f --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: entering unreachable code + --> tests/genmc/fail/simple/2w2w_weak.rs:LL:CC + | +LL | std::hint::unreachable_unchecked(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `miri_start` at tests/genmc/fail/simple/2w2w_weak.rs:LL:CC + +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/genmc/fail/simple/2w2w_weak.release4.stderr b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.release4.stderr new file mode 100644 index 000000000000..e2b114df652f --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.release4.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: entering unreachable code + --> tests/genmc/fail/simple/2w2w_weak.rs:LL:CC + | +LL | std::hint::unreachable_unchecked(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `miri_start` at tests/genmc/fail/simple/2w2w_weak.rs:LL:CC + +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/genmc/fail/simple/2w2w_weak.rs b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.rs new file mode 100644 index 000000000000..dea2b5f952e7 --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.rs @@ -0,0 +1,73 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@revisions: sc3_rel1 release4 relaxed4 + +// The pass tests "2w2w_3sc_1rel.rs", "2w2w_4rel" and "2w2w_4sc" and the fail test "2w2w_weak.rs" are related. +// +// This test has multiple variants using different memory orderings. +// When using any combination of orderings except using all 4 `SeqCst`, the memory model allows the program to result in (X, Y) == (1, 1). +// The "pass" variants only check that we get the expected number of executions (3 for all SC, 4 otherwise), +// and a valid outcome every execution, but do not check that we get all allowed results. +// This "fail" variant ensures we can explore the execution resulting in (1, 1), an incorrect unsafe assumption that the result (1, 1) is impossible. +// +// Miri without GenMC is unable to produce this program execution and thus detect the incorrect assumption, even with `-Zmiri-many-seeds`. +// +// To get good coverage, we test the combination with the strongest orderings allowing this result (3 `SeqCst`, 1 `Release`), +// the weakest orderings (4 `Relaxed`), and one in between (4 `Release`). + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::{self, *}; + +use crate::genmc::{join_pthreads, spawn_pthread_closure}; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +// Strongest orderings allowing result (1, 1). +#[cfg(seqcst_rel)] +const STORE_ORD_3: Ordering = SeqCst; +#[cfg(seqcst_rel)] +const STORE_ORD_1: Ordering = Release; + +// 4 * `Release`. +#[cfg(acqrel)] +const STORE_ORD_3: Ordering = Release; +#[cfg(acqrel)] +const STORE_ORD_1: Ordering = Release; + +// Weakest orderings (4 * `Relaxed`). +#[cfg(not(any(acqrel, seqcst_rel)))] +const STORE_ORD_3: Ordering = Relaxed; +#[cfg(not(any(acqrel, seqcst_rel)))] +const STORE_ORD_1: Ordering = Relaxed; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + let ids = [ + spawn_pthread_closure(|| { + X.store(1, STORE_ORD_3); + Y.store(2, STORE_ORD_3); + }), + spawn_pthread_closure(|| { + Y.store(1, STORE_ORD_1); + X.store(2, STORE_ORD_3); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // We mark the result (1, 1) as unreachable, which is incorrect. + let result = (X.load(Relaxed), Y.load(Relaxed)); + if result == (1, 1) { + // FIXME(genmc): Use `std::process::abort()` once backtraces for that are improved (https://github.com/rust-lang/rust/pull/146118) + std::hint::unreachable_unchecked(); //~ ERROR: entering unreachable code + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr new file mode 100644 index 000000000000..e2b114df652f --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: entering unreachable code + --> tests/genmc/fail/simple/2w2w_weak.rs:LL:CC + | +LL | std::hint::unreachable_unchecked(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `miri_start` at tests/genmc/fail/simple/2w2w_weak.rs:LL:CC + +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/genmc/pass/litmus/2cowr.rs b/src/tools/miri/tests/genmc/pass/litmus/2cowr.rs new file mode 100644 index 000000000000..d3fdb470ed36 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/2cowr.rs @@ -0,0 +1,51 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's test "2CoWR". + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + Y.store(0, Relaxed); + + unsafe { + let mut a = 1234; + let mut b = 1234; + let ids = [ + spawn_pthread_closure(|| { + a = Y.load(Acquire); + }), + spawn_pthread_closure(|| { + b = X.load(Acquire); + }), + spawn_pthread_closure(|| { + X.store(1, Release); + }), + spawn_pthread_closure(|| { + Y.store(1, Release); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values: + if !matches!((a, b), (0, 0) | (0, 1) | (1, 0) | (1, 1)) { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/2cowr.stderr b/src/tools/miri/tests/genmc/pass/litmus/2cowr.stderr new file mode 100644 index 000000000000..0667962f99c8 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/2cowr.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 4 diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release1.stderr b/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release1.stderr new file mode 100644 index 000000000000..0667962f99c8 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release1.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 4 diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release2.stderr b/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release2.stderr new file mode 100644 index 000000000000..0667962f99c8 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release2.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 4 diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.rs b/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.rs new file mode 100644 index 000000000000..22fe9524c37f --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.rs @@ -0,0 +1,54 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@revisions: release1 release2 + +// Translated from GenMC's test "2+2W+3sc+rel1" and "2+2W+3sc+rel2" (two variants that swap which store is `Release`). +// +// The pass tests "2w2w_3sc_1rel.rs", "2w2w_4rel" and "2w2w_4sc" and the fail test "2w2w_weak.rs" are related. +// Check "2w2w_weak.rs" for a more detailed description. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + let ids = [ + spawn_pthread_closure(|| { + X.store(1, SeqCst); + Y.store(2, SeqCst); + }), + // Variant 1: `Release` goes first. + #[cfg(release1)] + spawn_pthread_closure(|| { + Y.store(1, Release); + X.store(2, SeqCst); + }), + // Variant 2: `Release` goes second. + #[cfg(not(release1))] + spawn_pthread_closure(|| { + Y.store(1, SeqCst); + X.store(2, Release); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values: + let result = (X.load(Relaxed), Y.load(Relaxed)); + if !matches!(result, (1, 2) | (1, 1) | (2, 2) | (2, 1)) { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.rs b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.rs new file mode 100644 index 000000000000..bbe9995dea0b --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.rs @@ -0,0 +1,45 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's test "2+2W". +// +// The pass tests "2w2w_3sc_1rel.rs", "2w2w_4rel" and "2w2w_4sc" and the fail test "2w2w_weak.rs" are related. +// Check "2w2w_weak.rs" for a more detailed description. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + let ids = [ + spawn_pthread_closure(|| { + Y.store(1, Release); + X.store(2, Release); + }), + spawn_pthread_closure(|| { + X.store(1, Release); + Y.store(2, Release); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values: + let result = (X.load(Relaxed), Y.load(Relaxed)); + if !matches!(result, (1, 2) | (1, 1) | (2, 2) | (2, 1)) { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.stderr b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.stderr new file mode 100644 index 000000000000..0667962f99c8 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 4 diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_4sc.rs b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4sc.rs new file mode 100644 index 000000000000..c5711ba04fce --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4sc.rs @@ -0,0 +1,45 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's test "2+2W+4c". +// +// The pass tests "2w2w_3sc_1rel.rs", "2w2w_4rel" and "2w2w_4sc" and the fail test "2w2w_weak.rs" are related. +// Check "2w2w_weak.rs" for a more detailed description. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + let ids = [ + spawn_pthread_closure(|| { + X.store(1, SeqCst); + Y.store(2, SeqCst); + }), + spawn_pthread_closure(|| { + Y.store(1, SeqCst); + X.store(2, SeqCst); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values: + let result = (X.load(Relaxed), Y.load(Relaxed)); + if !matches!(result, (2, 1) | (2, 2) | (1, 2)) { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_4sc.stderr b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4sc.stderr new file mode 100644 index 000000000000..115b1986ce55 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4sc.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 3 diff --git a/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.rs b/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.rs new file mode 100644 index 000000000000..79e7c7feb7aa --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.rs @@ -0,0 +1,64 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "IRIW-acq-sc" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + Y.store(0, Relaxed); + + unsafe { + let mut a = 1234; + let mut b = 1234; + let mut c = 1234; + let ids = [ + spawn_pthread_closure(|| { + X.store(1, SeqCst); + }), + spawn_pthread_closure(|| { + a = X.load(Acquire); + Y.load(SeqCst); + }), + spawn_pthread_closure(|| { + b = Y.load(Acquire); + c = X.load(SeqCst); + }), + spawn_pthread_closure(|| { + Y.store(1, SeqCst); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values: + if !matches!( + (a, b, c), + (0, 0, 0) + | (0, 0, 1) + | (0, 1, 0) + | (0, 1, 1) + | (1, 0, 0) + | (1, 0, 1) + | (1, 1, 0) + | (1, 1, 1) + ) { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.stderr b/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.stderr new file mode 100644 index 000000000000..c760b4460511 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 16 diff --git a/src/tools/miri/tests/genmc/pass/litmus/LB.rs b/src/tools/miri/tests/genmc/pass/litmus/LB.rs new file mode 100644 index 000000000000..1cee3230b127 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/LB.rs @@ -0,0 +1,47 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "litmus/LB" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + Y.store(0, Relaxed); + + unsafe { + let mut a = 1234; + let mut b = 1234; + let ids = [ + spawn_pthread_closure(|| { + a = Y.load(Acquire); + X.store(2, Release); + }), + spawn_pthread_closure(|| { + b = X.load(Acquire); + Y.store(1, Release); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values: + if !matches!((a, b), (0, 0) | (0, 2) | (1, 0)) { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/LB.stderr b/src/tools/miri/tests/genmc/pass/litmus/LB.stderr new file mode 100644 index 000000000000..115b1986ce55 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/LB.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 3 diff --git a/src/tools/miri/tests/genmc/pass/litmus/MP.rs b/src/tools/miri/tests/genmc/pass/litmus/MP.rs new file mode 100644 index 000000000000..e245cdd15eef --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/MP.rs @@ -0,0 +1,47 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "litmus/MP" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + Y.store(0, Relaxed); + + unsafe { + let mut a = 1234; + let mut b = 1234; + let ids = [ + spawn_pthread_closure(|| { + X.store(1, Release); + Y.store(1, Release); + }), + spawn_pthread_closure(|| { + a = Y.load(Acquire); + b = X.load(Acquire); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values: + if !matches!((a, b), (0, 0) | (0, 1) | (1, 1)) { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/MP.stderr b/src/tools/miri/tests/genmc/pass/litmus/MP.stderr new file mode 100644 index 000000000000..115b1986ce55 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/MP.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 3 diff --git a/src/tools/miri/tests/genmc/pass/litmus/SB.rs b/src/tools/miri/tests/genmc/pass/litmus/SB.rs new file mode 100644 index 000000000000..e592fe05c4e4 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/SB.rs @@ -0,0 +1,47 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "litmus/SB" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + Y.store(0, Relaxed); + + unsafe { + let mut a = 1234; + let mut b = 1234; + let ids = [ + spawn_pthread_closure(|| { + X.store(1, Release); + a = Y.load(Acquire); + }), + spawn_pthread_closure(|| { + Y.store(1, Release); + b = X.load(Acquire); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values: + if !matches!((a, b), (0, 0) | (0, 1) | (1, 0) | (1, 1)) { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/SB.stderr b/src/tools/miri/tests/genmc/pass/litmus/SB.stderr new file mode 100644 index 000000000000..0667962f99c8 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/SB.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 4 diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr.rs b/src/tools/miri/tests/genmc/pass/litmus/corr.rs new file mode 100644 index 000000000000..d6c95100fc24 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/corr.rs @@ -0,0 +1,45 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "CoRR" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + + unsafe { + let mut a = 1234; + let mut b = 1234; + let ids = [ + spawn_pthread_closure(|| { + X.store(1, Release); + X.store(2, Release); + }), + spawn_pthread_closure(|| { + a = X.load(Acquire); + b = X.load(Acquire); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values: + if !matches!((a, b), (0, 0) | (0, 1) | (0, 2) | (1, 1) | (1, 2) | (2, 2)) { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr.stderr b/src/tools/miri/tests/genmc/pass/litmus/corr.stderr new file mode 100644 index 000000000000..be75e68fde77 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/corr.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 6 diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr0.rs b/src/tools/miri/tests/genmc/pass/litmus/corr0.rs new file mode 100644 index 000000000000..f722131fda84 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/corr0.rs @@ -0,0 +1,46 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "CoRR0" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + + unsafe { + let mut a = 1234; + let mut b = 1234; + let ids = [ + spawn_pthread_closure(|| { + X.store(1, Release); + }), + spawn_pthread_closure(|| { + a = X.load(Acquire); + }), + spawn_pthread_closure(|| { + b = X.load(Acquire); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values: + if !matches!((a, b), (0, 0) | (0, 1) | (1, 0) | (1, 1)) { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr0.stderr b/src/tools/miri/tests/genmc/pass/litmus/corr0.stderr new file mode 100644 index 000000000000..0667962f99c8 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/corr0.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 4 diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr1.rs b/src/tools/miri/tests/genmc/pass/litmus/corr1.rs new file mode 100644 index 000000000000..a4e8249bac30 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/corr1.rs @@ -0,0 +1,58 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "CoRR1" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + + unsafe { + let mut a = 1234; + let mut b = 1234; + let mut c = 1234; + let ids = [ + spawn_pthread_closure(|| { + X.store(1, Release); + }), + spawn_pthread_closure(|| { + X.store(2, Release); + }), + spawn_pthread_closure(|| { + a = X.load(Acquire); + b = X.load(Acquire); + }), + spawn_pthread_closure(|| { + c = X.load(Acquire); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values (only 0, 1, 2 are allowed): + if !(matches!(a, 0..=2) && matches!(b, 0..=2) && matches!(c, 0..=2)) { + std::process::abort(); + } + // The 36 possible program executions can have 21 different results for (a, b, c). + // Of the 27 = 3*3*3 total results for (a, b, c), + // those where `a != 0` and `b == 0` are not allowed by the memory model. + // Once the load for `a` reads either 1 or 2, the load for `b` must see that store too, so it cannot read 0. + if a != 0 && b == 0 { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr1.stderr b/src/tools/miri/tests/genmc/pass/litmus/corr1.stderr new file mode 100644 index 000000000000..f6d07e9c77b2 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/corr1.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 36 diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr2.rs b/src/tools/miri/tests/genmc/pass/litmus/corr2.rs new file mode 100644 index 000000000000..2f490d363779 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/corr2.rs @@ -0,0 +1,70 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "CoRR2" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + + unsafe { + let mut a = 1234; + let mut b = 1234; + let mut c = 1234; + let mut d = 1234; + let ids = [ + spawn_pthread_closure(|| { + X.store(1, Release); + }), + spawn_pthread_closure(|| { + X.store(2, Release); + }), + spawn_pthread_closure(|| { + a = X.load(Acquire); + b = X.load(Acquire); + }), + spawn_pthread_closure(|| { + c = X.load(Acquire); + d = X.load(Acquire); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values (only 0, 1, 2 are allowed): + if !(matches!(a, 0..=2) && matches!(b, 0..=2) && matches!(c, 0..=2) && matches!(d, 0..=2)) { + std::process::abort(); + } + + // The 72 possible program executions can have 47 different results for (a, b, c, d). + // Of the 81 = 3*3*3*3 total results for (a, b, c, d), + // those where `a != 0` and `b == 0` are not allowed by the memory model. + // Once the load for `a` reads either 1 or 2, the load for `b` must see that store too, so it cannot read 0. + // The same applies to `c, d` in the other thread. + // + // Additionally, if one thread reads `1, 2` or `2, 1`, the other thread cannot see the opposite order. + if a != 0 && b == 0 { + std::process::abort(); + } else if c != 0 && d == 0 { + std::process::abort(); + } else if (a, b) == (1, 2) && (c, d) == (2, 1) { + std::process::abort(); + } else if (a, b) == (2, 1) && (c, d) == (1, 2) { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr2.stderr b/src/tools/miri/tests/genmc/pass/litmus/corr2.stderr new file mode 100644 index 000000000000..78a90b63feab --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/corr2.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 72 diff --git a/src/tools/miri/tests/genmc/pass/litmus/corw.rs b/src/tools/miri/tests/genmc/pass/litmus/corw.rs new file mode 100644 index 000000000000..7acc20822a4f --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/corw.rs @@ -0,0 +1,43 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "CoRW" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + + unsafe { + let mut a = 1234; + let ids = [ + spawn_pthread_closure(|| { + a = X.load(Acquire); + X.store(1, Release); + }), + spawn_pthread_closure(|| { + X.store(2, Release); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values (the load cannot read `1`): + if !matches!(a, 0 | 2) { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/corw.stderr b/src/tools/miri/tests/genmc/pass/litmus/corw.stderr new file mode 100644 index 000000000000..115b1986ce55 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/corw.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 3 diff --git a/src/tools/miri/tests/genmc/pass/litmus/cowr.rs b/src/tools/miri/tests/genmc/pass/litmus/cowr.rs new file mode 100644 index 000000000000..1c51f23a09c6 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/cowr.rs @@ -0,0 +1,40 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "CoWR" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + let mut a = 1234; + let ids = [ + spawn_pthread_closure(|| { + X.store(1, Release); + a = X.load(Acquire); + }), + spawn_pthread_closure(|| { + X.store(2, Release); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values (the load cannot read `0`): + if !matches!(a, 1 | 2) { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/cowr.stderr b/src/tools/miri/tests/genmc/pass/litmus/cowr.stderr new file mode 100644 index 000000000000..115b1986ce55 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/cowr.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 3 diff --git a/src/tools/miri/tests/genmc/pass/litmus/default.rs b/src/tools/miri/tests/genmc/pass/litmus/default.rs new file mode 100644 index 000000000000..55fb1ac34acb --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/default.rs @@ -0,0 +1,47 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "litmus/default" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + + unsafe { + let mut a = 1234; + let mut b = 1234; + let ids = [ + spawn_pthread_closure(|| { + a = X.load(Acquire); + b = X.load(Acquire); + }), + spawn_pthread_closure(|| { + X.store(1, Release); + }), + spawn_pthread_closure(|| { + X.store(2, Release); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values: + if !matches!((a, b), (0, 0) | (0, 1) | (0, 2) | (1, 1) | (1, 2) | (2, 1) | (2, 2)) { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/default.stderr b/src/tools/miri/tests/genmc/pass/litmus/default.stderr new file mode 100644 index 000000000000..e0313930282e --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/default.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 12 diff --git a/src/tools/miri/tests/genmc/pass/litmus/detour.join.stderr b/src/tools/miri/tests/genmc/pass/litmus/detour.join.stderr new file mode 100644 index 000000000000..15017249dc3a --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/detour.join.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 9 diff --git a/src/tools/miri/tests/genmc/pass/litmus/detour.no_join.stderr b/src/tools/miri/tests/genmc/pass/litmus/detour.no_join.stderr new file mode 100644 index 000000000000..15017249dc3a --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/detour.no_join.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 9 diff --git a/src/tools/miri/tests/genmc/pass/litmus/detour.rs b/src/tools/miri/tests/genmc/pass/litmus/detour.rs new file mode 100644 index 000000000000..7136c029bbb5 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/detour.rs @@ -0,0 +1,68 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@revisions: join no_join + +// Translated from GenMC's "litmus/detour" test. + +// This test has two revisitions to test whether we get the same result +// independent of whether we join the spawned threads or not. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicI64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicI64 = AtomicI64::new(0); +static Y: AtomicI64 = AtomicI64::new(0); +static Z: AtomicI64 = AtomicI64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + Y.store(0, Relaxed); + Z.store(0, Relaxed); + + unsafe { + // Make these static so we can exit the main thread while the other threads still run. + // If these are `let mut` like the other tests, this will cause a use-after-free bug. + static mut A: i64 = 1234; + static mut B: i64 = 1234; + static mut C: i64 = 1234; + let ids = [ + spawn_pthread_closure(|| { + X.store(1, Relaxed); + }), + spawn_pthread_closure(|| { + A = Z.load(Relaxed); + X.store(A.wrapping_sub(1), Relaxed); + B = X.load(Relaxed); + Y.store(B, Relaxed); + }), + spawn_pthread_closure(|| { + C = Y.load(Relaxed); + Z.store(C, Relaxed); + }), + ]; + + // The `no_join` revision doesn't join any of the running threads to test that + // we still explore the same number of executions in that case. + if cfg!(no_join) { + return 0; + } + + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values: + if !matches!((A, B, C), (0, 1, 0) | (0, -1, 0) | (0, 1, 1) | (0, -1, -1)) { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.rs b/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.rs new file mode 100644 index 000000000000..9a08d517b7c6 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.rs @@ -0,0 +1,54 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "fr+w+w+w+reads" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + + unsafe { + let mut result = [1234; 4]; + let ids = [ + spawn_pthread_closure(|| { + X.store(1, Relaxed); + }), + spawn_pthread_closure(|| { + X.store(2, Relaxed); + }), + spawn_pthread_closure(|| { + X.store(3, Relaxed); + }), + spawn_pthread_closure(|| { + result[0] = X.load(Relaxed); + result[1] = X.load(Relaxed); + result[2] = X.load(Relaxed); + result[3] = X.load(Relaxed); + }), + ]; + + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values: + for val in result { + if !matches!(val, 0..=3) { + std::process::abort(); + } + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.stderr b/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.stderr new file mode 100644 index 000000000000..3b6ba238f534 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 210 diff --git a/src/tools/miri/tests/genmc/pass/test_cxx_build.rs b/src/tools/miri/tests/genmc/pass/test_cxx_build.rs deleted file mode 100644 index f621bd9114fa..000000000000 --- a/src/tools/miri/tests/genmc/pass/test_cxx_build.rs +++ /dev/null @@ -1,8 +0,0 @@ -//@compile-flags: -Zmiri-genmc - -#![no_main] - -#[unsafe(no_mangle)] -fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { - 0 -} diff --git a/src/tools/miri/tests/genmc/pass/test_cxx_build.stderr b/src/tools/miri/tests/genmc/pass/test_cxx_build.stderr deleted file mode 100644 index 4b7aa824bd12..000000000000 --- a/src/tools/miri/tests/genmc/pass/test_cxx_build.stderr +++ /dev/null @@ -1,5 +0,0 @@ -warning: borrow tracking has been disabled, it is not (yet) supported in GenMC mode. -C++: GenMC handle created! -Miri: GenMC handle creation successful! -C++: GenMC handle destroyed! -Miri: Dropping GenMC handle successful! diff --git a/src/tools/miri/tests/utils/genmc.rs b/src/tools/miri/tests/utils/genmc.rs new file mode 100644 index 000000000000..fb6334bd0608 --- /dev/null +++ b/src/tools/miri/tests/utils/genmc.rs @@ -0,0 +1,57 @@ +#![allow(unused)] + +use std::ffi::c_void; + +use libc::{self, pthread_attr_t, pthread_t}; + +/// Spawn a thread using `pthread_create`, abort the process on any errors. +pub unsafe fn spawn_pthread( + f: extern "C" fn(*mut c_void) -> *mut c_void, + value: *mut c_void, +) -> pthread_t { + let mut thread_id: pthread_t = 0; + + let attr: *const pthread_attr_t = std::ptr::null(); + + if unsafe { libc::pthread_create(&raw mut thread_id, attr, f, value) } != 0 { + std::process::abort(); + } + thread_id +} + +/// Unsafe because we do *not* check that `F` is `Send + 'static`. +/// That makes it much easier to write tests... +pub unsafe fn spawn_pthread_closure(f: F) -> pthread_t { + let mut thread_id: pthread_t = 0; + let attr: *const pthread_attr_t = std::ptr::null(); + let f = Box::new(f); + extern "C" fn thread_func(f: *mut c_void) -> *mut c_void { + let f = unsafe { Box::from_raw(f as *mut F) }; + f(); + std::ptr::null_mut() + } + if unsafe { + libc::pthread_create( + &raw mut thread_id, + attr, + thread_func::, + Box::into_raw(f) as *mut c_void, + ) + } != 0 + { + std::process::abort(); + } + thread_id +} + +// Join the given pthread, abort the process on any errors. +pub unsafe fn join_pthread(thread_id: pthread_t) { + if unsafe { libc::pthread_join(thread_id, std::ptr::null_mut()) } != 0 { + std::process::abort(); + } +} + +// Join the `N` given pthreads, abort the process on any errors. +pub unsafe fn join_pthreads(thread_ids: [pthread_t; N]) { + let _ = thread_ids.map(|id| join_pthread(id)); +} From 82f5cdf33e15bf08548ec8b9f13bb4cad8ac5ae4 Mon Sep 17 00:00:00 2001 From: joboet Date: Sat, 30 Aug 2025 00:21:38 +0200 Subject: [PATCH 177/682] std: improve the `dlsym!` macro and add a test for it * Ensure nul-termination of the symbol name at compile-time * Use an acquire load instead of a relaxed load and acquire fence * Properly use `unsafe` and add safety comments * Add tests --- library/std/src/lib.rs | 1 + library/std/src/sys/pal/unix/weak.rs | 120 ++++++++++++--------- library/std/src/sys/pal/unix/weak/tests.rs | 32 ++++++ 3 files changed, 102 insertions(+), 51 deletions(-) create mode 100644 library/std/src/sys/pal/unix/weak/tests.rs diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 30a1b1088178..69f640a68551 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -350,6 +350,7 @@ #![feature(float_gamma)] #![feature(float_minimum_maximum)] #![feature(fmt_internals)] +#![feature(fn_ptr_trait)] #![feature(generic_atomic)] #![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] diff --git a/library/std/src/sys/pal/unix/weak.rs b/library/std/src/sys/pal/unix/weak.rs index c8cf75b876c2..ad649fdbab6c 100644 --- a/library/std/src/sys/pal/unix/weak.rs +++ b/library/std/src/sys/pal/unix/weak.rs @@ -22,11 +22,14 @@ #![allow(dead_code, unused_macros)] #![forbid(unsafe_op_in_unsafe_fn)] -use crate::ffi::CStr; -use crate::marker::PhantomData; -use crate::sync::atomic::{self, Atomic, AtomicPtr, Ordering}; +use crate::ffi::{CStr, c_char, c_void}; +use crate::marker::{FnPtr, PhantomData}; +use crate::sync::atomic::{Atomic, AtomicPtr, Ordering}; use crate::{mem, ptr}; +#[cfg(test)] +mod tests; + // We can use true weak linkage on ELF targets. #[cfg(all(unix, not(target_vendor = "apple")))] pub(crate) macro weak { @@ -64,7 +67,7 @@ impl ExternWeak { pub(crate) macro dlsym { (fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty;) => ( - dlsym!( + dlsym!( #[link_name = stringify!($name)] fn $name($($param : $t),*) -> $ret; ); @@ -73,21 +76,39 @@ pub(crate) macro dlsym { #[link_name = $sym:expr] fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty; ) => ( - static DLSYM: DlsymWeak $ret> = - DlsymWeak::new(concat!($sym, '\0')); + static DLSYM: DlsymWeak $ret> = { + let Ok(name) = CStr::from_bytes_with_nul(concat!($sym, '\0').as_bytes()) else { + panic!("symbol name may not contain NUL") + }; + + // SAFETY: Whoever calls the function pointer returned by `get()` + // is responsible for ensuring that the signature is correct. Just + // like with extern blocks, this is syntactically enforced by making + // the function pointer be unsafe. + unsafe { DlsymWeak::new(name) } + }; + let $name = &DLSYM; ) } + pub(crate) struct DlsymWeak { - name: &'static str, + /// A pointer to the nul-terminated name of the symbol. + // Use a pointer instead of `&'static CStr` to save space. + name: *const c_char, func: Atomic<*mut libc::c_void>, _marker: PhantomData, } -impl DlsymWeak { - pub(crate) const fn new(name: &'static str) -> Self { +impl DlsymWeak { + /// # Safety + /// + /// If the signature of `F` does not match the signature of the symbol (if + /// it exists), calling the function pointer returned by `get()` is + /// undefined behaviour. + pub(crate) const unsafe fn new(name: &'static CStr) -> Self { DlsymWeak { - name, + name: name.as_ptr(), func: AtomicPtr::new(ptr::without_provenance_mut(1)), _marker: PhantomData, } @@ -95,62 +116,59 @@ impl DlsymWeak { #[inline] pub(crate) fn get(&self) -> Option { - unsafe { - // Relaxed is fine here because we fence before reading through the - // pointer (see the comment below). - match self.func.load(Ordering::Relaxed) { - func if func.addr() == 1 => self.initialize(), - func if func.is_null() => None, - func => { - let func = mem::transmute_copy::<*mut libc::c_void, F>(&func); - // The caller is presumably going to read through this value - // (by calling the function we've dlsymed). This means we'd - // need to have loaded it with at least C11's consume - // ordering in order to be guaranteed that the data we read - // from the pointer isn't from before the pointer was - // stored. Rust has no equivalent to memory_order_consume, - // so we use an acquire fence (sorry, ARM). - // - // Now, in practice this likely isn't needed even on CPUs - // where relaxed and consume mean different things. The - // symbols we're loading are probably present (or not) at - // init, and even if they aren't the runtime dynamic loader - // is extremely likely have sufficient barriers internally - // (possibly implicitly, for example the ones provided by - // invoking `mprotect`). - // - // That said, none of that's *guaranteed*, and so we fence. - atomic::fence(Ordering::Acquire); - Some(func) - } - } + // The caller is presumably going to read through this value + // (by calling the function we've dlsymed). This means we'd + // need to have loaded it with at least C11's consume + // ordering in order to be guaranteed that the data we read + // from the pointer isn't from before the pointer was + // stored. Rust has no equivalent to memory_order_consume, + // so we use an acquire load (sorry, ARM). + // + // Now, in practice this likely isn't needed even on CPUs + // where relaxed and consume mean different things. The + // symbols we're loading are probably present (or not) at + // init, and even if they aren't the runtime dynamic loader + // is extremely likely have sufficient barriers internally + // (possibly implicitly, for example the ones provided by + // invoking `mprotect`). + // + // That said, none of that's *guaranteed*, so we use acquire. + match self.func.load(Ordering::Acquire) { + func if func.addr() == 1 => self.initialize(), + func if func.is_null() => None, + // SAFETY: + // `func` is not null and `F` implements `FnPtr`, thus this + // transmutation is well-defined. It is the responsibility of the + // creator of this `DlsymWeak` to ensure that calling the resulting + // function pointer does not result in undefined behaviour (though + // the `dlsym!` macro delegates this responsibility to the caller + // of the function by using `unsafe` function pointers). + // FIXME: use `transmute` once it stops complaining about generics. + func => Some(unsafe { mem::transmute_copy::<*mut c_void, F>(&func) }), } } // Cold because it should only happen during first-time initialization. #[cold] - unsafe fn initialize(&self) -> Option { - assert_eq!(size_of::(), size_of::<*mut libc::c_void>()); - - let val = unsafe { fetch(self.name) }; - // This synchronizes with the acquire fence in `get`. + fn initialize(&self) -> Option { + // SAFETY: `self.name` was created from a `&'static CStr` and is + // therefore a valid C string pointer. + let val = unsafe { libc::dlsym(libc::RTLD_DEFAULT, self.name) }; + // This synchronizes with the acquire load in `get`. self.func.store(val, Ordering::Release); if val.is_null() { None } else { + // SAFETY: see the comment in `get`. + // FIXME: use `transmute` once it stops complaining about generics. Some(unsafe { mem::transmute_copy::<*mut libc::c_void, F>(&val) }) } } } -unsafe fn fetch(name: &str) -> *mut libc::c_void { - let name = match CStr::from_bytes_with_nul(name.as_bytes()) { - Ok(cstr) => cstr, - Err(..) => return ptr::null_mut(), - }; - unsafe { libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr()) } -} +unsafe impl Send for DlsymWeak {} +unsafe impl Sync for DlsymWeak {} #[cfg(not(any(target_os = "linux", target_os = "android")))] pub(crate) macro syscall { diff --git a/library/std/src/sys/pal/unix/weak/tests.rs b/library/std/src/sys/pal/unix/weak/tests.rs new file mode 100644 index 000000000000..d807ba64e357 --- /dev/null +++ b/library/std/src/sys/pal/unix/weak/tests.rs @@ -0,0 +1,32 @@ +use super::*; + +#[test] +fn dlsym_existing() { + const TEST_STRING: &'static CStr = c"Ferris!"; + + // Try to find a symbol that definitely exists. + dlsym! { + fn strlen(cs: *const c_char) -> usize; + } + + dlsym! { + #[link_name = "strlen"] + fn custom_name(cs: *const c_char) -> usize; + } + + let strlen = strlen.get().unwrap(); + assert_eq!(unsafe { strlen(TEST_STRING.as_ptr()) }, TEST_STRING.count_bytes()); + + let custom_name = custom_name.get().unwrap(); + assert_eq!(unsafe { custom_name(TEST_STRING.as_ptr()) }, TEST_STRING.count_bytes()); +} + +#[test] +fn dlsym_missing() { + // Try to find a symbol that definitely does not exist. + dlsym! { + fn test_symbol_that_does_not_exist() -> i32; + } + + assert!(test_symbol_that_does_not_exist.get().is_none()); +} From 48c15bd58c5f0c0105894a2b660845037937cfbd Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 3 Sep 2025 17:26:02 +0200 Subject: [PATCH 178/682] test valid cases of c-variadic function definitions --- tests/ui/c-variadic/valid.rs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tests/ui/c-variadic/valid.rs diff --git a/tests/ui/c-variadic/valid.rs b/tests/ui/c-variadic/valid.rs new file mode 100644 index 000000000000..5a0b32026dc7 --- /dev/null +++ b/tests/ui/c-variadic/valid.rs @@ -0,0 +1,29 @@ +//@ run-pass +#![feature(c_variadic)] + +// In rust (and C23 and above) `...` can be the only argument. +unsafe extern "C" fn only_dot_dot_dot(mut ap: ...) -> i32 { + unsafe { ap.arg() } +} + +unsafe extern "C-unwind" fn abi_c_unwind(mut ap: ...) -> i32 { + unsafe { ap.arg() } +} + +#[allow(improper_ctypes_definitions)] +unsafe extern "C" fn mix_int_float(mut ap: ...) -> (i64, f64, *const i32, f64) { + (ap.arg(), ap.arg(), ap.arg(), ap.arg()) +} + +fn main() { + unsafe { + assert_eq!(only_dot_dot_dot(32), 32); + assert_eq!(abi_c_unwind(32), 32); + + // Passing more arguments than expected is allowed. + assert_eq!(only_dot_dot_dot(32, 1i64, core::ptr::null::(), 3.14f64), 32); + + let ptr = &14i32 as *const i32; + assert_eq!(mix_int_float(12i64, 13.0f64, ptr, 15.0f64), (12, 13.0, ptr, 15.0)); + } +} From 8d11719e6c980232b9e9657a356d3c3c1a35c6da Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 3 Sep 2025 16:56:13 +0200 Subject: [PATCH 179/682] simplify `check_c_variadic_type` --- .../rustc_ast_passes/src/ast_validation.rs | 42 ++++----- compiler/rustc_ast_passes/src/errors.rs | 4 +- tests/ui/c-variadic/issue-86053-1.stderr | 4 +- tests/ui/c-variadic/no-closure.rs | 17 ++++ tests/ui/c-variadic/no-closure.stderr | 35 +++++++ .../variadic-ffi-semantic-restrictions.rs | 12 +-- .../variadic-ffi-semantic-restrictions.stderr | 91 +++++++------------ 7 files changed, 111 insertions(+), 94 deletions(-) create mode 100644 tests/ui/c-variadic/no-closure.rs create mode 100644 tests/ui/c-variadic/no-closure.stderr diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 6133cc3548b5..538918a890d5 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -665,46 +665,42 @@ impl<'a> AstValidator<'a> { /// - Non-const /// - Either foreign, or free and `unsafe extern "C"` semantically fn check_c_variadic_type(&self, fk: FnKind<'a>) { - let variadic_spans: Vec<_> = fk - .decl() - .inputs - .iter() - .filter(|arg| matches!(arg.ty.kind, TyKind::CVarArgs)) - .map(|arg| arg.span) - .collect(); + // `...` is already rejected when it is not the final parameter. + let variadic_param = match fk.decl().inputs.last() { + Some(param) if matches!(param.ty.kind, TyKind::CVarArgs) => param, + _ => return, + }; - if variadic_spans.is_empty() { - return; - } + let FnKind::Fn(fn_ctxt, _, Fn { sig, .. }) = fk else { + // Unreachable because the parser already rejects `...` in closures. + unreachable!("C variable argument list cannot be used in closures") + }; - if let Some(header) = fk.header() - && let Const::Yes(const_span) = header.constness - { - let mut spans = variadic_spans.clone(); - spans.push(const_span); + // C-variadics are not yet implemented in const evaluation. + if let Const::Yes(const_span) = sig.header.constness { self.dcx().emit_err(errors::ConstAndCVariadic { - spans, + spans: vec![const_span, variadic_param.span], const_span, - variadic_spans: variadic_spans.clone(), + variadic_span: variadic_param.span, }); } - match (fk.ctxt(), fk.header()) { - (Some(FnCtxt::Foreign), _) => return, - (Some(FnCtxt::Free), Some(header)) => match header.ext { + match fn_ctxt { + FnCtxt::Foreign => return, + FnCtxt::Free => match sig.header.ext { Extern::Explicit(StrLit { symbol_unescaped: sym::C, .. }, _) | Extern::Explicit(StrLit { symbol_unescaped: sym::C_dash_unwind, .. }, _) | Extern::Implicit(_) - if matches!(header.safety, Safety::Unsafe(_)) => + if matches!(sig.header.safety, Safety::Unsafe(_)) => { return; } _ => {} }, - _ => {} + FnCtxt::Assoc(_) => {} }; - self.dcx().emit_err(errors::BadCVariadic { span: variadic_spans }); + self.dcx().emit_err(errors::BadCVariadic { span: variadic_param.span }); } fn check_item_named(&self, ident: Ident, kind: &str) { diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index ae8f056cb4e6..476ed27a10e3 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -322,7 +322,7 @@ pub(crate) struct ExternItemAscii { #[diag(ast_passes_bad_c_variadic)] pub(crate) struct BadCVariadic { #[primary_span] - pub span: Vec, + pub span: Span, } #[derive(Diagnostic)] @@ -656,7 +656,7 @@ pub(crate) struct ConstAndCVariadic { #[label(ast_passes_const)] pub const_span: Span, #[label(ast_passes_variadic)] - pub variadic_spans: Vec, + pub variadic_span: Span, } #[derive(Diagnostic)] diff --git a/tests/ui/c-variadic/issue-86053-1.stderr b/tests/ui/c-variadic/issue-86053-1.stderr index 675dfd335c42..b58016b5a816 100644 --- a/tests/ui/c-variadic/issue-86053-1.stderr +++ b/tests/ui/c-variadic/issue-86053-1.stderr @@ -47,10 +47,10 @@ LL | self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize | ^^^ error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/issue-86053-1.rs:11:12 + --> $DIR/issue-86053-1.rs:11:36 | LL | self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize ) { - | ^^^ ^^^ + | ^^^ error[E0412]: cannot find type `F` in this scope --> $DIR/issue-86053-1.rs:11:48 diff --git a/tests/ui/c-variadic/no-closure.rs b/tests/ui/c-variadic/no-closure.rs new file mode 100644 index 000000000000..c0b77786e8b4 --- /dev/null +++ b/tests/ui/c-variadic/no-closure.rs @@ -0,0 +1,17 @@ +#![feature(c_variadic)] +#![crate_type = "lib"] + +// Check that `...` in closures is rejected. + +const F: extern "C" fn(...) = |_: ...| {}; +//~^ ERROR C-variadic type `...` may not be nested inside another type + +fn foo() { + let f = |...| {}; + //~^ ERROR: `..` patterns are not allowed here + //~| ERROR: unexpected `...` + + let f = |_: ...| {}; + //~^ ERROR C-variadic type `...` may not be nested inside another type + f(1i64) +} diff --git a/tests/ui/c-variadic/no-closure.stderr b/tests/ui/c-variadic/no-closure.stderr new file mode 100644 index 000000000000..77bd106bb951 --- /dev/null +++ b/tests/ui/c-variadic/no-closure.stderr @@ -0,0 +1,35 @@ +error[E0743]: C-variadic type `...` may not be nested inside another type + --> $DIR/no-closure.rs:6:35 + | +LL | const F: extern "C" fn(...) = |_: ...| {}; + | ^^^ + +error: unexpected `...` + --> $DIR/no-closure.rs:10:14 + | +LL | let f = |...| {}; + | ^^^ not a valid pattern + | +help: for a rest pattern, use `..` instead of `...` + | +LL - let f = |...| {}; +LL + let f = |..| {}; + | + +error[E0743]: C-variadic type `...` may not be nested inside another type + --> $DIR/no-closure.rs:14:17 + | +LL | let f = |_: ...| {}; + | ^^^ + +error: `..` patterns are not allowed here + --> $DIR/no-closure.rs:10:14 + | +LL | let f = |...| {}; + | ^^^ + | + = note: only allowed in tuple, tuple struct, and slice patterns + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0743`. diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs index e7a0248cffab..243924e6c53e 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs @@ -16,8 +16,7 @@ extern "C" fn f2_2(...) {} //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention extern "C" fn f2_3(..., x: isize) {} -//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention -//~| ERROR `...` must be the last argument of a C-variadic function +//~^ ERROR `...` must be the last argument of a C-variadic function extern "C" fn f3_1(x: isize, ...) {} //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention @@ -26,8 +25,7 @@ extern "C" fn f3_2(...) {} //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention extern "C" fn f3_3(..., x: isize) {} -//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention -//~| ERROR `...` must be the last argument of a C-variadic function +//~^ ERROR `...` must be the last argument of a C-variadic function const unsafe extern "C" fn f4_1(x: isize, ...) {} //~^ ERROR functions cannot be both `const` and C-variadic @@ -77,9 +75,7 @@ trait T { fn t_f4(...); //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention fn t_f5(..., x: isize) {} - //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - //~| ERROR `...` must be the last argument of a C-variadic function + //~^ ERROR `...` must be the last argument of a C-variadic function fn t_f6(..., x: isize); - //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - //~| ERROR `...` must be the last argument of a C-variadic function + //~^ ERROR `...` must be the last argument of a C-variadic function } diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr index 5379045967aa..5c55cc38b568 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr @@ -29,118 +29,103 @@ LL | extern "C" fn f2_3(..., x: isize) {} | ^^^ error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:18:20 - | -LL | extern "C" fn f2_3(..., x: isize) {} - | ^^^ - -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:22:30 + --> $DIR/variadic-ffi-semantic-restrictions.rs:21:30 | LL | extern "C" fn f3_1(x: isize, ...) {} | ^^^ error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:25:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:24:20 | LL | extern "C" fn f3_2(...) {} | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:28:20 - | -LL | extern "C" fn f3_3(..., x: isize) {} - | ^^^ - -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:28:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:27:20 | LL | extern "C" fn f3_3(..., x: isize) {} | ^^^ error: functions cannot be both `const` and C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:32:1 + --> $DIR/variadic-ffi-semantic-restrictions.rs:30:1 | LL | const unsafe extern "C" fn f4_1(x: isize, ...) {} | ^^^^^ `const` because of this ^^^ C-variadic because of this error: functions cannot be both `const` and C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:36:1 + --> $DIR/variadic-ffi-semantic-restrictions.rs:34:1 | LL | const extern "C" fn f4_2(x: isize, ...) {} | ^^^^^ `const` because of this ^^^ C-variadic because of this error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:36:36 + --> $DIR/variadic-ffi-semantic-restrictions.rs:34:36 | LL | const extern "C" fn f4_2(x: isize, ...) {} | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:41:26 + --> $DIR/variadic-ffi-semantic-restrictions.rs:39:26 | LL | const extern "C" fn f4_3(..., x: isize, ...) {} | ^^^ error: functions cannot be both `const` and C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:41:1 + --> $DIR/variadic-ffi-semantic-restrictions.rs:39:1 | LL | const extern "C" fn f4_3(..., x: isize, ...) {} - | ^^^^^ ^^^ ^^^ C-variadic because of this - | | | - | | C-variadic because of this - | `const` because of this + | ^^^^^ `const` because of this ^^^ C-variadic because of this error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:41:26 + --> $DIR/variadic-ffi-semantic-restrictions.rs:39:41 | LL | const extern "C" fn f4_3(..., x: isize, ...) {} - | ^^^ ^^^ + | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:47:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:45:13 | LL | fn e_f2(..., x: isize); | ^^^ error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:54:23 + --> $DIR/variadic-ffi-semantic-restrictions.rs:52:23 | LL | fn i_f1(x: isize, ...) {} | ^^^ error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:56:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:54:13 | LL | fn i_f2(...) {} | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:58:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:56:13 | LL | fn i_f3(..., x: isize, ...) {} | ^^^ error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:58:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:56:28 | LL | fn i_f3(..., x: isize, ...) {} - | ^^^ ^^^ + | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:61:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:59:13 | LL | fn i_f4(..., x: isize, ...) {} | ^^^ error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:61:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:59:28 | LL | fn i_f4(..., x: isize, ...) {} - | ^^^ ^^^ + | ^^^ error: functions cannot be both `const` and C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:64:5 + --> $DIR/variadic-ffi-semantic-restrictions.rs:62:5 | LL | const fn i_f5(x: isize, ...) {} | ^^^^^ ^^^ C-variadic because of this @@ -148,61 +133,49 @@ LL | const fn i_f5(x: isize, ...) {} | `const` because of this error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:64:29 + --> $DIR/variadic-ffi-semantic-restrictions.rs:62:29 | LL | const fn i_f5(x: isize, ...) {} | ^^^ error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:71:23 + --> $DIR/variadic-ffi-semantic-restrictions.rs:69:23 | LL | fn t_f1(x: isize, ...) {} | ^^^ error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:73:23 + --> $DIR/variadic-ffi-semantic-restrictions.rs:71:23 | LL | fn t_f2(x: isize, ...); | ^^^ error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:75:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:73:13 | LL | fn t_f3(...) {} | ^^^ error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:77:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:75:13 | LL | fn t_f4(...); | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:79:13 - | -LL | fn t_f5(..., x: isize) {} - | ^^^ - -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:79:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:77:13 | LL | fn t_f5(..., x: isize) {} | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:82:13 - | -LL | fn t_f6(..., x: isize); - | ^^^ - -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:82:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:79:13 | LL | fn t_f6(..., x: isize); | ^^^ error[E0493]: destructor of `VaListImpl<'_>` cannot be evaluated at compile-time - --> $DIR/variadic-ffi-semantic-restrictions.rs:32:43 + --> $DIR/variadic-ffi-semantic-restrictions.rs:30:43 | LL | const unsafe extern "C" fn f4_1(x: isize, ...) {} | ^^^ - value is dropped here @@ -210,7 +183,7 @@ LL | const unsafe extern "C" fn f4_1(x: isize, ...) {} | the destructor for this type cannot be evaluated in constant functions error[E0493]: destructor of `VaListImpl<'_>` cannot be evaluated at compile-time - --> $DIR/variadic-ffi-semantic-restrictions.rs:36:36 + --> $DIR/variadic-ffi-semantic-restrictions.rs:34:36 | LL | const extern "C" fn f4_2(x: isize, ...) {} | ^^^ - value is dropped here @@ -218,13 +191,13 @@ LL | const extern "C" fn f4_2(x: isize, ...) {} | the destructor for this type cannot be evaluated in constant functions error[E0493]: destructor of `VaListImpl<'_>` cannot be evaluated at compile-time - --> $DIR/variadic-ffi-semantic-restrictions.rs:64:29 + --> $DIR/variadic-ffi-semantic-restrictions.rs:62:29 | LL | const fn i_f5(x: isize, ...) {} | ^^^ - value is dropped here | | | the destructor for this type cannot be evaluated in constant functions -error: aborting due to 36 previous errors +error: aborting due to 32 previous errors For more information about this error, try `rustc --explain E0493`. From 891523d0de6792c132e83c4833b678543ecf5d19 Mon Sep 17 00:00:00 2001 From: yanglsh Date: Thu, 28 Aug 2025 07:17:06 +0800 Subject: [PATCH 180/682] fix: `read_zero_byte_vec` suggests wrongly inside `let` stmt --- clippy_lints/src/read_zero_byte_vec.rs | 27 ++++++++--- tests/ui/read_zero_byte_vec.rs | 27 +++++++++++ tests/ui/read_zero_byte_vec.stderr | 65 ++++++++++++++++++++++++-- 3 files changed, 109 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/read_zero_byte_vec.rs b/clippy_lints/src/read_zero_byte_vec.rs index acd840401c6b..b8d4e7c4651d 100644 --- a/clippy_lints/src/read_zero_byte_vec.rs +++ b/clippy_lints/src/read_zero_byte_vec.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::higher::{VecInitKind, get_vec_init_kind}; -use clippy_utils::source::snippet; +use clippy_utils::source::{indent_of, snippet}; use clippy_utils::{get_enclosing_block, sym}; use rustc_errors::Applicability; @@ -83,10 +83,12 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { expr.span, "reading zero byte data to `Vec`", |diag| { + let span = first_stmt_containing_expr(cx, expr).map_or(expr.span, |stmt| stmt.span); + let indent = indent_of(cx, span).unwrap_or(0); diag.span_suggestion( - expr.span, + span.shrink_to_lo(), "try", - format!("{}.resize({len}, 0); {}", ident, snippet(cx, expr.span, "..")), + format!("{ident}.resize({len}, 0);\n{}", " ".repeat(indent)), applicability, ); }, @@ -100,14 +102,15 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { expr.span, "reading zero byte data to `Vec`", |diag| { + let span = first_stmt_containing_expr(cx, expr).map_or(expr.span, |stmt| stmt.span); + let indent = indent_of(cx, span).unwrap_or(0); diag.span_suggestion( - expr.span, + span.shrink_to_lo(), "try", format!( - "{}.resize({}, 0); {}", - ident, + "{ident}.resize({}, 0);\n{}", snippet(cx, e.span, ".."), - snippet(cx, expr.span, "..") + " ".repeat(indent) ), applicability, ); @@ -130,6 +133,16 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { } } +fn first_stmt_containing_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx hir::Stmt<'tcx>> { + cx.tcx.hir_parent_iter(expr.hir_id).find_map(|(_, node)| { + if let hir::Node::Stmt(stmt) = node { + Some(stmt) + } else { + None + } + }) +} + struct ReadVecVisitor<'tcx> { local_id: HirId, read_zero_expr: Option<&'tcx Expr<'tcx>>, diff --git a/tests/ui/read_zero_byte_vec.rs b/tests/ui/read_zero_byte_vec.rs index 938d61b68607..720276cb5548 100644 --- a/tests/ui/read_zero_byte_vec.rs +++ b/tests/ui/read_zero_byte_vec.rs @@ -120,3 +120,30 @@ fn allow_works(mut f: F) { } fn main() {} + +fn issue15575() -> usize { + use std::io::Read; + use std::net::TcpListener; + + let listener = TcpListener::bind("127.0.0.1:9010").unwrap(); + let mut stream_and_addr = listener.accept().unwrap(); + let mut buf = Vec::with_capacity(32); + let num_bytes_received = stream_and_addr.0.read(&mut buf).unwrap(); + //~^ read_zero_byte_vec + + let cap = 1000; + let mut buf = Vec::with_capacity(cap); + let num_bytes_received = stream_and_addr.0.read(&mut buf).unwrap(); + //~^ read_zero_byte_vec + + let cap = 1000; + let mut buf = Vec::with_capacity(cap); + let num_bytes_received = { stream_and_addr.0.read(&mut buf) }.unwrap(); + //~^ read_zero_byte_vec + + use std::fs::File; + let mut f = File::open("foo.txt").unwrap(); + let mut data = Vec::with_capacity(100); + f.read(&mut data).unwrap() + //~^ read_zero_byte_vec +} diff --git a/tests/ui/read_zero_byte_vec.stderr b/tests/ui/read_zero_byte_vec.stderr index 8f255bc87ab8..8dd74592e4c1 100644 --- a/tests/ui/read_zero_byte_vec.stderr +++ b/tests/ui/read_zero_byte_vec.stderr @@ -2,16 +2,27 @@ error: reading zero byte data to `Vec` --> tests/ui/read_zero_byte_vec.rs:22:5 | LL | f.read_exact(&mut data).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `data.resize(20, 0); f.read_exact(&mut data)` + | ^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::read-zero-byte-vec` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::read_zero_byte_vec)]` +help: try + | +LL ~ data.resize(20, 0); +LL ~ f.read_exact(&mut data).unwrap(); + | error: reading zero byte data to `Vec` --> tests/ui/read_zero_byte_vec.rs:28:5 | LL | f.read_exact(&mut data2)?; - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `data2.resize(cap, 0); f.read_exact(&mut data2)` + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ data2.resize(cap, 0); +LL ~ f.read_exact(&mut data2)?; + | error: reading zero byte data to `Vec` --> tests/ui/read_zero_byte_vec.rs:33:5 @@ -67,5 +78,53 @@ error: reading zero byte data to `Vec` LL | r.read_exact(&mut data2).await.unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 11 previous errors +error: reading zero byte data to `Vec` + --> tests/ui/read_zero_byte_vec.rs:131:30 + | +LL | let num_bytes_received = stream_and_addr.0.read(&mut buf).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ buf.resize(32, 0); +LL ~ let num_bytes_received = stream_and_addr.0.read(&mut buf).unwrap(); + | + +error: reading zero byte data to `Vec` + --> tests/ui/read_zero_byte_vec.rs:136:30 + | +LL | let num_bytes_received = stream_and_addr.0.read(&mut buf).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ buf.resize(cap, 0); +LL ~ let num_bytes_received = stream_and_addr.0.read(&mut buf).unwrap(); + | + +error: reading zero byte data to `Vec` + --> tests/ui/read_zero_byte_vec.rs:141:32 + | +LL | let num_bytes_received = { stream_and_addr.0.read(&mut buf) }.unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ buf.resize(cap, 0); +LL ~ let num_bytes_received = { stream_and_addr.0.read(&mut buf) }.unwrap(); + | + +error: reading zero byte data to `Vec` + --> tests/ui/read_zero_byte_vec.rs:147:5 + | +LL | f.read(&mut data).unwrap() + | ^^^^^^^^^^^^^^^^^ + | +help: try + | +LL ~ data.resize(100, 0); +LL ~ f.read(&mut data).unwrap() + | + +error: aborting due to 15 previous errors From 4fec3aa2a4e087c981a083dca59e7434cac52625 Mon Sep 17 00:00:00 2001 From: Daniel Paoliello Date: Wed, 3 Sep 2025 16:59:03 -0700 Subject: [PATCH 181/682] In the rustc_llvm build script, don't consider arm64* to be 32-bit --- compiler/rustc_llvm/build.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs index d01f79dcade0..804f46374d57 100644 --- a/compiler/rustc_llvm/build.rs +++ b/compiler/rustc_llvm/build.rs @@ -254,7 +254,10 @@ fn main() { println!("cargo:rustc-link-lib=kstat"); } - if (target.starts_with("arm") && !target.contains("freebsd") && !target.contains("ohos")) + if (target.starts_with("arm") + && !target.starts_with("arm64") + && !target.contains("freebsd") + && !target.contains("ohos")) || target.starts_with("mips-") || target.starts_with("mipsel-") || target.starts_with("powerpc-") From d9619ed1c54f7c20b74913982cf0715002380e10 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Thu, 4 Sep 2025 04:52:52 +0000 Subject: [PATCH 182/682] Prepare for merging from rust-lang/rust This updates the rust-version file to 9385c64c95d971329e62917adc4349c8ccdbafe0. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index d44488399f8b..7420b6200967 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -51ff895062ba60a7cba53f57af928c3fb7b0f2f4 +9385c64c95d971329e62917adc4349c8ccdbafe0 From 169013d9eaae1d0b3d4d07058017698837ee09ff Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 4 Sep 2025 12:10:39 +0300 Subject: [PATCH 183/682] Fix typo in config To make it backwards-compatible. --- src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index c2252185a3aa..84f158cdea69 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -2832,7 +2832,7 @@ enum ReborrowHintsDef { #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "snake_case")] enum AdjustmentHintsDef { - #[serde(alias = "Reborrow")] + #[serde(alias = "reborrow")] Borrows, #[serde(with = "true_or_always")] #[serde(untagged)] From d5cb7e7c7c0f77c7a7b064584ae44e74acc155ca Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 4 Sep 2025 12:18:22 +0300 Subject: [PATCH 184/682] Add a regression test for a fixed new trait solver bug Not sure what exactly fixed it, but why not. --- .../hir-ty/src/tests/regression/new_solver.rs | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs index 20190fbc0456..e4ee52f45eb7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -1,6 +1,6 @@ use expect_test::expect; -use super::check_infer; +use crate::tests::{check_infer, check_no_mismatches}; #[test] fn opaque_generics() { @@ -50,3 +50,24 @@ fn main() { "#]], ); } + +#[test] +fn regression_20487() { + check_no_mismatches( + r#" +//- minicore: coerce_unsized, dispatch_from_dyn +trait Foo { + fn bar(&self) -> u32 { + 0xCAFE + } +} + +fn debug(_: &dyn Foo) {} + +impl Foo for i32 {} + +fn main() { + debug(&1); +}"#, + ); +} From 0976d6ccac3d71befb58ceefd3109864f0eb210d Mon Sep 17 00:00:00 2001 From: dianne Date: Sun, 24 Aug 2025 22:48:00 -0700 Subject: [PATCH 185/682] don't extend non-extended `super let` initializers' block tail temps --- .../rustc_hir_analysis/src/check/region.rs | 24 +++++++++++++++---- .../format-args-temporary-scopes.e2024.stderr | 14 ++++++++++- .../borrowck/format-args-temporary-scopes.rs | 1 + .../ui/drop/super-let-tail-expr-drop-order.rs | 14 +++++------ 4 files changed, 40 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index f5770b7312dd..58521697b387 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -467,8 +467,12 @@ fn resolve_local<'tcx>( // A, but the inner rvalues `a()` and `b()` have an extended lifetime // due to rule C. - if let_kind == LetKind::Super { - if let Some(scope) = visitor.extended_super_lets.remove(&pat.unwrap().hir_id.local_id) { + let extend_initializer = match let_kind { + LetKind::Regular => true, + LetKind::Super + if let Some(scope) = + visitor.extended_super_lets.remove(&pat.unwrap().hir_id.local_id) => + { // This expression was lifetime-extended by a parent let binding. E.g. // // let a = { @@ -481,7 +485,10 @@ fn resolve_local<'tcx>( // Processing of `let a` will have already decided to extend the lifetime of this // `super let` to its own var_scope. We use that scope. visitor.cx.var_parent = scope; - } else { + // Extend temporaries to live in the same scope as the parent `let`'s bindings. + true + } + LetKind::Super => { // This `super let` is not subject to lifetime extension from a parent let binding. E.g. // // identity({ super let x = temp(); &x }).method(); @@ -497,10 +504,17 @@ fn resolve_local<'tcx>( } visitor.cx.var_parent = parent; } + // Don't lifetime-extend child `super let`s or block tail expressions' temporaries in + // the initializer when this `super let` is not itself extended by a parent `let` + // (#145784). Block tail expressions are temporary drop scopes in Editions 2024 and + // later, their temps shouldn't outlive the block in e.g. `f(pin!({ &temp() }))`. + false } - } + }; - if let Some(expr) = init { + if let Some(expr) = init + && extend_initializer + { record_rvalue_scope_if_borrow_expr(visitor, expr, visitor.cx.var_parent); if let Some(pat) = pat { diff --git a/tests/ui/borrowck/format-args-temporary-scopes.e2024.stderr b/tests/ui/borrowck/format-args-temporary-scopes.e2024.stderr index 923c929a68d6..506fc6e0965f 100644 --- a/tests/ui/borrowck/format-args-temporary-scopes.e2024.stderr +++ b/tests/ui/borrowck/format-args-temporary-scopes.e2024.stderr @@ -10,6 +10,18 @@ LL | println!("{:?}", { &temp() }); | = note: consider using a `let` binding to create a longer lived value -error: aborting due to 1 previous error +error[E0716]: temporary value dropped while borrowed + --> $DIR/format-args-temporary-scopes.rs:19:29 + | +LL | println!("{:?}{:?}", { &temp() }, ()); + | ---^^^^^--- + | | | | + | | | temporary value is freed at the end of this statement + | | creates a temporary value which is freed while still in use + | borrow later used here + | + = note: consider using a `let` binding to create a longer lived value + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0716`. diff --git a/tests/ui/borrowck/format-args-temporary-scopes.rs b/tests/ui/borrowck/format-args-temporary-scopes.rs index e6223d7c5518..2641058accb3 100644 --- a/tests/ui/borrowck/format-args-temporary-scopes.rs +++ b/tests/ui/borrowck/format-args-temporary-scopes.rs @@ -17,4 +17,5 @@ fn main() { // arguments when provided with two or more arguments. This caused the result of `temp()` to // outlive the result of the block, making this compile. println!("{:?}{:?}", { &temp() }, ()); + //[e2024]~^ ERROR: temporary value dropped while borrowed [E0716] } diff --git a/tests/ui/drop/super-let-tail-expr-drop-order.rs b/tests/ui/drop/super-let-tail-expr-drop-order.rs index 5aa17bba21f1..8ec9296ed4f1 100644 --- a/tests/ui/drop/super-let-tail-expr-drop-order.rs +++ b/tests/ui/drop/super-let-tail-expr-drop-order.rs @@ -45,10 +45,10 @@ fn main() { #[cfg(e2024)] ( pin!(( - pin!({ &o.log(3) as *const LogDrop<'_> }), - drop(o.log(1)), + pin!({ &o.log(1) as *const LogDrop<'_> }), + drop(o.log(2)), )), - drop(o.log(2)), + drop(o.log(3)), ); }); @@ -69,12 +69,12 @@ fn main() { ( { super let _ = { - super let _ = { &o.log(4) as *const LogDrop<'_> }; - drop(o.log(1)) + super let _ = { &o.log(1) as *const LogDrop<'_> }; + drop(o.log(2)) }; - drop(o.log(2)) + drop(o.log(3)) }, - drop(o.log(3)), + drop(o.log(4)), ); }); From d5b5a4a0e11d92a963578d15a9f693e1538caf3f Mon Sep 17 00:00:00 2001 From: dianne Date: Mon, 25 Aug 2025 04:52:52 -0700 Subject: [PATCH 186/682] additional tests Co-authored-by: Travis Cross --- .../ui/drop/super-let-tail-expr-drop-order.rs | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/tests/ui/drop/super-let-tail-expr-drop-order.rs b/tests/ui/drop/super-let-tail-expr-drop-order.rs index 8ec9296ed4f1..5b2ecfbb3200 100644 --- a/tests/ui/drop/super-let-tail-expr-drop-order.rs +++ b/tests/ui/drop/super-let-tail-expr-drop-order.rs @@ -22,10 +22,13 @@ //@ [e2024] edition: 2024 #![feature(super_let)] +#![allow(unused_braces)] use std::cell::RefCell; use std::pin::pin; +fn f(_: LogDrop<'_>, x: T) -> T { x } + fn main() { // Test block arguments to `pin!` in non-extending expressions. // In Rust 2021, block tail expressions aren't temporary drop scopes, so their temporaries @@ -90,6 +93,73 @@ fn main() { let _ = { super let _ = { &o.log(2) as *const LogDrop<'_> }; }; drop(o.log(1)); }); + + // We have extending borrow expressions within an extending block + // expression (within an extending borrow expression) within a + // non-extending expresion within the initializer expression. + #[cfg(e2021)] + { + // These two should be the same. + assert_drop_order(1..=3, |e| { + let _v = f(e.log(1), &{ &raw const *&e.log(2) }); + drop(e.log(3)); + }); + assert_drop_order(1..=3, |e| { + let _v = f(e.log(1), { + super let v = &{ &raw const *&e.log(2) }; + v + }); + drop(e.log(3)); + }); + } + #[cfg(e2024)] + { + // These two should be the same. + assert_drop_order(1..=3, |e| { + let _v = f(e.log(2), &{ &raw const *&e.log(1) }); + drop(e.log(3)); + }); + assert_drop_order(1..=3, |e| { + let _v = f(e.log(2), { + super let v = &{ &raw const *&e.log(1) }; + v + }); + drop(e.log(3)); + }); + } + + // We have extending borrow expressions within a non-extending + // expression within the initializer expression. + // + // These two should be the same. + assert_drop_order(1..=3, |e| { + let _v = f(e.log(1), &&raw const *&e.log(2)); + drop(e.log(3)); + }); + assert_drop_order(1..=3, |e| { + let _v = f(e.log(1), { + super let v = &&raw const *&e.log(2); + v + }); + drop(e.log(3)); + }); + + // We have extending borrow expressions within an extending block + // expression (within an extending borrow expression) within the + // initializer expression. + // + // These two should be the same. + assert_drop_order(1..=2, |e| { + let _v = &{ &raw const *&e.log(2) }; + drop(e.log(1)); + }); + assert_drop_order(1..=2, |e| { + let _v = { + super let v = &{ &raw const *&e.log(2) }; + v + }; + drop(e.log(1)); + }); } // # Test scaffolding... From 7ff5a3d277939c9a3648f2dc270ff2e9166bcf70 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 4 Sep 2025 22:36:31 +0300 Subject: [PATCH 187/682] Upgrade rustc crates The main changes are (there are some other small changes): - Using a specific type for trait IDs in the new solver, allowing us to simplify a lot of code. - Add `BoundConst` similar to `BoundTy` and `BoundRegion` (previously consts used `BoundVar` directly), due to a new trait requirement. --- src/tools/rust-analyzer/Cargo.lock | 77 ++- src/tools/rust-analyzer/Cargo.toml | 16 +- .../crates/hir-ty/src/display.rs | 12 +- .../crates/hir-ty/src/dyn_compatibility.rs | 72 +-- .../crates/hir-ty/src/lower_nextsolver.rs | 64 +-- .../hir-ty/src/lower_nextsolver/path.rs | 5 +- .../crates/hir-ty/src/method_resolution.rs | 6 +- .../crates/hir-ty/src/next_solver/consts.rs | 29 +- .../crates/hir-ty/src/next_solver/def_id.rs | 57 ++ .../crates/hir-ty/src/next_solver/fold.rs | 8 +- .../crates/hir-ty/src/next_solver/fulfill.rs | 2 +- .../hir-ty/src/next_solver/generic_arg.rs | 2 - .../infer/canonical/instantiate.rs | 3 +- .../hir-ty/src/next_solver/infer/mod.rs | 6 +- .../next_solver/infer/relate/higher_ranked.rs | 8 +- .../crates/hir-ty/src/next_solver/interner.rs | 513 +++++++----------- .../crates/hir-ty/src/next_solver/ir_print.rs | 10 +- .../crates/hir-ty/src/next_solver/mapping.rs | 32 +- .../hir-ty/src/next_solver/predicate.rs | 8 +- .../crates/hir-ty/src/next_solver/solver.rs | 8 +- .../crates/hir-ty/src/next_solver/ty.rs | 27 + .../crates/hir-ty/src/next_solver/util.rs | 28 +- .../rust-analyzer/crates/hir-ty/src/utils.rs | 5 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 29 +- 24 files changed, 447 insertions(+), 580 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index b008aa1c50f8..344e6d101fe3 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1360,7 +1360,7 @@ dependencies = [ "expect-test", "intern", "parser", - "ra-ap-rustc_lexer 0.123.0", + "ra-ap-rustc_lexer", "rustc-hash 2.1.1", "smallvec", "span", @@ -1596,8 +1596,8 @@ dependencies = [ "drop_bomb", "edition", "expect-test", - "ra-ap-rustc_lexer 0.123.0", - "rustc-literal-escaper", + "ra-ap-rustc_lexer", + "rustc-literal-escaper 0.0.4", "stdx", "tracing", ] @@ -1717,7 +1717,7 @@ dependencies = [ "object", "paths", "proc-macro-test", - "ra-ap-rustc_lexer 0.123.0", + "ra-ap-rustc_lexer", "span", "syntax-bridge", "temp-dir", @@ -1863,9 +1863,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_abi" -version = "0.123.0" +version = "0.126.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f18c877575c259d127072e9bfc41d985202262fb4d6bfdae3d1252147c2562c2" +checksum = "0c6789d94fb3e6e30d62f55e99a321ba63484a8bb3b4ead338687c9ddc282d28" dependencies = [ "bitflags 2.9.1", "ra-ap-rustc_hashes", @@ -1875,24 +1875,24 @@ dependencies = [ [[package]] name = "ra-ap-rustc_ast_ir" -version = "0.123.0" +version = "0.126.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc17e8ce797f2a8d03b838fbf166749b876164432ce81e37d283bf69e3cf80" +checksum = "aaab80bda0f05e9842e3afb7779b0bad0a4b54e0f7ba6deb5705dcf86482811d" [[package]] name = "ra-ap-rustc_hashes" -version = "0.123.0" +version = "0.126.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2439ed1df3472443133b66949f81080dff88089b42f825761455463709ee1cad" +checksum = "64bd405e538102b5f699241794b2eefee39d5414c0e4bc72435e91430c51f905" dependencies = [ "rustc-stable-hash", ] [[package]] name = "ra-ap-rustc_index" -version = "0.123.0" +version = "0.126.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a24fe0be21be1f8ebc21dcb40129214fb4cefb0f2753f3d46b6dbe656a1a45" +checksum = "521621e271aa03b8433dad5981838278d6cfd7d2d8c9f4eb6d427f1d671f90fc" dependencies = [ "ra-ap-rustc_index_macros", "smallvec", @@ -1900,9 +1900,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index_macros" -version = "0.123.0" +version = "0.126.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "844a27ddcad0116facae2df8e741fd788662cf93dc13029cd864f2b8013b81f9" +checksum = "245e30f2e1fef258913cc548b36f575549c8af31cbc4649929d21deda96ceeb7" dependencies = [ "proc-macro2", "quote", @@ -1911,20 +1911,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_lexer" -version = "0.121.0" +version = "0.126.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22944e31fb91e9b3e75bcbc91e37d958b8c0825a6160927f2856831d2ce83b36" -dependencies = [ - "memchr", - "unicode-properties", - "unicode-xid", -] - -[[package]] -name = "ra-ap-rustc_lexer" -version = "0.123.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b734cfcb577d09877799a22742f1bd398be6c00bc428d9de56d48d11ece5771" +checksum = "a82681f924500e888c860e60ed99e9bf702a219a69374f59116c4261525a2157" dependencies = [ "memchr", "unicode-properties", @@ -1933,9 +1922,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_next_trait_solver" -version = "0.123.0" +version = "0.126.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14f7dfbdf1d045ff4e385e1efdfc3799379895e9c3f3b9b379a0bef4cb238441" +checksum = "0c9ce51f2431fbdc7fabd2d957522b6e27f41f68ec2af74b52a6f4116352ce1a" dependencies = [ "derive-where", "ra-ap-rustc_index", @@ -1946,19 +1935,19 @@ dependencies = [ [[package]] name = "ra-ap-rustc_parse_format" -version = "0.121.0" +version = "0.126.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81057891bc2063ad9e353f29462fbc47a0f5072560af34428ae9313aaa5e9d97" +checksum = "adc85ef3fdb6c084bde84857d8948dc66b752129dc8417a8614ce490e99a143f" dependencies = [ - "ra-ap-rustc_lexer 0.121.0", - "rustc-literal-escaper", + "ra-ap-rustc_lexer", + "rustc-literal-escaper 0.0.5", ] [[package]] name = "ra-ap-rustc_pattern_analysis" -version = "0.123.0" +version = "0.126.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b0ee1f059b9dea0818c6c7267478926eee95ba4c7dcf89c8db32fa165d3904" +checksum = "3cd81eccf33d9528905d4e5abaa254b3129a6405d6c5f123fed9b73a3d217f35" dependencies = [ "ra-ap-rustc_index", "rustc-hash 2.1.1", @@ -1969,9 +1958,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_type_ir" -version = "0.123.0" +version = "0.126.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bc59fb10a922c38a24cb8a1494f799b0af30bd041acbea689378d3bf330534b" +checksum = "11cb0da02853698d9c89e1d1c01657b9969752befd56365e8899d4310e52b373" dependencies = [ "bitflags 2.9.1", "derive-where", @@ -1988,9 +1977,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_type_ir_macros" -version = "0.123.0" +version = "0.126.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58878914b6dac7499baeecc8dbb4b9d9dda88030e4ab90cd3b4e87523fbedafe" +checksum = "ffc93adeb52c483ede13bee6680466458218243ab479c04fb71bb53925a6e0ff" dependencies = [ "proc-macro2", "quote", @@ -2140,6 +2129,12 @@ version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab03008eb631b703dd16978282ae36c73282e7922fe101a4bd072a40ecea7b8b" +[[package]] +name = "rustc-literal-escaper" +version = "0.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4ee29da77c5a54f42697493cd4c9b9f31b74df666a6c04dfc4fde77abe0438b" + [[package]] name = "rustc-stable-hash" version = "0.1.2" @@ -2445,7 +2440,7 @@ dependencies = [ "rayon", "rowan", "rustc-hash 2.1.1", - "rustc-literal-escaper", + "rustc-literal-escaper 0.0.4", "rustc_apfloat", "smol_str", "stdx", @@ -2773,7 +2768,7 @@ version = "0.0.0" dependencies = [ "arrayvec", "intern", - "ra-ap-rustc_lexer 0.123.0", + "ra-ap-rustc_lexer", "stdx", "text-size", ] diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 05ee190480c9..f325027ee587 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -89,14 +89,14 @@ vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" } edition = { path = "./crates/edition", version = "0.0.0" } -ra-ap-rustc_lexer = { version = "0.123", default-features = false } -ra-ap-rustc_parse_format = { version = "0.121", default-features = false } -ra-ap-rustc_index = { version = "0.123", default-features = false } -ra-ap-rustc_abi = { version = "0.123", default-features = false } -ra-ap-rustc_pattern_analysis = { version = "0.123", default-features = false } -ra-ap-rustc_ast_ir = { version = "0.123", default-features = false } -ra-ap-rustc_type_ir = { version = "0.123", default-features = false } -ra-ap-rustc_next_trait_solver = { version = "0.123", default-features = false } +ra-ap-rustc_lexer = { version = "0.126", default-features = false } +ra-ap-rustc_parse_format = { version = "0.126", default-features = false } +ra-ap-rustc_index = { version = "0.126", default-features = false } +ra-ap-rustc_abi = { version = "0.126", default-features = false } +ra-ap-rustc_pattern_analysis = { version = "0.126", default-features = false } +ra-ap-rustc_ast_ir = { version = "0.126", default-features = false } +ra-ap-rustc_type_ir = { version = "0.126", default-features = false } +ra-ap-rustc_next_trait_solver = { version = "0.126", default-features = false } # local crates that aren't published to crates.io. These should not have versions. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 5b8093f6b724..fcb79e9ffb57 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -737,7 +737,7 @@ impl<'db> HirDisplay for crate::next_solver::Const<'db> { match self.kind() { rustc_type_ir::ConstKind::Placeholder(_) => write!(f, ""), rustc_type_ir::ConstKind::Bound(db, bound_const) => { - write!(f, "?{}.{}", db.as_u32(), bound_const.as_u32()) + write!(f, "?{}.{}", db.as_u32(), bound_const.var.as_u32()) } rustc_type_ir::ConstKind::Infer(..) => write!(f, "#c#"), rustc_type_ir::ConstKind::Param(param) => { @@ -1208,10 +1208,7 @@ impl<'db> HirDisplay for crate::next_solver::Ty<'db> { let contains_impl_fn_ns = |bounds: &[BoundExistentialPredicate<'_>]| { bounds.iter().any(|bound| match bound.skip_binder() { rustc_type_ir::ExistentialPredicate::Trait(trait_ref) => { - let trait_ = match trait_ref.def_id { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; + let trait_ = trait_ref.def_id.0; fn_traits(db, trait_).any(|it| it == trait_) } _ => false, @@ -2217,10 +2214,7 @@ impl HirDisplay for TraitRef { impl<'db> HirDisplay for crate::next_solver::TraitRef<'db> { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { - let trait_ = match self.def_id { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; + let trait_ = self.def_id.0; f.start_location_link(trait_.into()); write!(f, "{}", f.db.trait_signature(trait_).name.display(f.db, f.edition()))?; f.end_location_link(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs index 23280b1f3b42..d4b3751cf566 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs @@ -2,12 +2,11 @@ use std::ops::ControlFlow; -use hir_def::hir::generics::LocalTypeOrConstParamId; use hir_def::{ AssocItemId, ConstId, CrateRootModuleId, FunctionId, GenericDefId, HasModule, TraitId, - TypeAliasId, lang_item::LangItem, signatures::TraitFlags, + TypeAliasId, TypeOrConstParamId, TypeParamId, hir::generics::LocalTypeOrConstParamId, + lang_item::LangItem, signatures::TraitFlags, }; -use hir_def::{TypeOrConstParamId, TypeParamId}; use intern::Symbol; use rustc_hash::FxHashSet; use rustc_type_ir::{ @@ -22,7 +21,7 @@ use crate::{ db::{HirDatabase, InternedOpaqueTyId}, lower_nextsolver::associated_ty_item_bounds, next_solver::{ - Clause, Clauses, DbInterner, GenericArgs, ParamEnv, SolverDefId, TraitPredicate, + Clause, Clauses, DbInterner, GenericArgs, ParamEnv, SolverDefId, TraitPredicate, TraitRef, TypingMode, infer::DbInternerInferExt, mk_param, }, traits::next_trait_solve_in_ctxt, @@ -56,16 +55,12 @@ pub fn dyn_compatibility( trait_: TraitId, ) -> Option { let interner = DbInterner::new_with(db, Some(trait_.krate(db)), None); - for super_trait in elaborate::supertrait_def_ids(interner, SolverDefId::TraitId(trait_)) { - let super_trait = match super_trait { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; - if let Some(v) = db.dyn_compatibility_of_trait(super_trait) { - return if super_trait == trait_ { + for super_trait in elaborate::supertrait_def_ids(interner, trait_.into()) { + if let Some(v) = db.dyn_compatibility_of_trait(super_trait.0) { + return if super_trait.0 == trait_ { Some(v) } else { - Some(DynCompatibilityViolation::HasNonCompatibleSuperTrait(super_trait)) + Some(DynCompatibilityViolation::HasNonCompatibleSuperTrait(super_trait.0)) }; } } @@ -82,13 +77,8 @@ where F: FnMut(DynCompatibilityViolation) -> ControlFlow<()>, { let interner = DbInterner::new_with(db, Some(trait_.krate(db)), None); - for super_trait in elaborate::supertrait_def_ids(interner, SolverDefId::TraitId(trait_)).skip(1) - { - let super_trait = match super_trait { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; - if db.dyn_compatibility_of_trait(super_trait).is_some() { + for super_trait in elaborate::supertrait_def_ids(interner, trait_.into()).skip(1) { + if db.dyn_compatibility_of_trait(super_trait.0).is_some() { cb(DynCompatibilityViolation::HasNonCompatibleSuperTrait(trait_))?; } } @@ -151,7 +141,7 @@ pub fn generics_require_sized_self(db: &dyn HirDatabase, def: GenericDefId) -> b elaborate::elaborate(interner, predicates.iter().copied()).any(|pred| { match pred.kind().skip_binder() { ClauseKind::Trait(trait_pred) => { - if SolverDefId::TraitId(sized) == trait_pred.def_id() + if sized == trait_pred.def_id().0 && let rustc_type_ir::TyKind::Param(param_ty) = trait_pred.trait_ref.self_ty().kind() && param_ty.index == 0 @@ -257,15 +247,9 @@ fn contains_illegal_self_type_reference<'db, T: rustc_type_ir::TypeVisitable id, - _ => unreachable!(), - }) - .collect(), + elaborate::supertrait_def_ids(interner, self.trait_.into()) + .map(|super_trait| super_trait.0) + .collect(), ) } if self.super_traits.as_ref().is_some_and(|s| s.contains(&trait_)) { @@ -390,8 +374,7 @@ where trait_ref: pred_trait_ref, polarity: PredicatePolarity::Positive, }) = pred - && let SolverDefId::TraitId(trait_id) = pred_trait_ref.def_id - && let trait_data = db.trait_signature(trait_id) + && let trait_data = db.trait_signature(pred_trait_ref.def_id.0) && trait_data.flags.contains(TraitFlags::AUTO) && let rustc_type_ir::TyKind::Param(crate::next_solver::ParamTy { index: 0, .. }) = pred_trait_ref.self_ty().kind() @@ -464,25 +447,17 @@ fn receiver_is_dispatchable<'db>( let generic_predicates = &*db.generic_predicates_ns(func.into()); // Self: Unsize - let unsize_predicate = crate::next_solver::TraitRef::new( - interner, - SolverDefId::TraitId(unsize_did), - [self_param_ty, unsized_self_ty], - ); + let unsize_predicate = + TraitRef::new(interner, unsize_did.into(), [self_param_ty, unsized_self_ty]); // U: Trait - let trait_def_id = SolverDefId::TraitId(trait_); - let args = GenericArgs::for_item(interner, trait_def_id, |name, index, kind, _| { + let args = GenericArgs::for_item(interner, trait_.into(), |name, index, kind, _| { if index == 0 { unsized_self_ty.into() } else { mk_param(interner, index, name, kind) } }); - let trait_predicate = - crate::next_solver::TraitRef::new_from_args(interner, trait_def_id, args); + let trait_predicate = TraitRef::new_from_args(interner, trait_.into(), args); - let meta_sized_predicate = crate::next_solver::TraitRef::new( - interner, - SolverDefId::TraitId(meta_sized_did), - [unsized_self_ty], - ); + let meta_sized_predicate = + TraitRef::new(interner, meta_sized_did.into(), [unsized_self_ty]); ParamEnv { clauses: Clauses::new_from_iter( @@ -497,11 +472,8 @@ fn receiver_is_dispatchable<'db>( }; // Receiver: DispatchFromDyn U]> - let predicate = crate::next_solver::TraitRef::new( - interner, - SolverDefId::TraitId(dispatch_from_dyn_did), - [receiver_ty, unsized_receiver_ty], - ); + let predicate = + TraitRef::new(interner, dispatch_from_dyn_did.into(), [receiver_ty, unsized_receiver_ty]); let goal = crate::next_solver::Goal::new(interner, param_env, predicate); let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs index 4578922ce321..c6a8fa81edff 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver.rs @@ -590,11 +590,9 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { .resolve_trait(ctx.ty_ctx().db, ctx.ty_ctx().resolver.krate()); let pointee_sized = LangItem::PointeeSized .resolve_trait(ctx.ty_ctx().db, ctx.ty_ctx().resolver.krate()); - if meta_sized.is_some_and(|it| SolverDefId::TraitId(it) == trait_ref.def_id) { + if meta_sized.is_some_and(|it| it == trait_ref.def_id.0) { // Ignore this bound - } else if pointee_sized - .is_some_and(|it| SolverDefId::TraitId(it) == trait_ref.def_id) - { + } else if pointee_sized.is_some_and(|it| it == trait_ref.def_id.0) { // Regard this as `?Sized` bound ctx.ty_ctx().unsized_types.insert(self_ty); } else { @@ -618,13 +616,9 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { // Don't lower associated type bindings as the only possible relaxed trait bound // `?Sized` has no of them. // If we got another trait here ignore the bound completely. - let trait_id = - self.lower_trait_ref_from_path(path, self_ty).map(|(trait_ref, _)| { - match trait_ref.def_id { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - } - }); + let trait_id = self + .lower_trait_ref_from_path(path, self_ty) + .map(|(trait_ref, _)| trait_ref.def_id.0); if trait_id == sized_trait { self.unsized_types.insert(self_ty); } @@ -668,12 +662,8 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { .map_bound(|c| match c { rustc_type_ir::ClauseKind::Trait(t) => { let id = t.def_id(); - let id = match id { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; let is_auto = - db.trait_signature(id).flags.contains(TraitFlags::AUTO); + db.trait_signature(id.0).flags.contains(TraitFlags::AUTO); if is_auto { Some(ExistentialPredicate::AutoTrait(t.def_id())) } else { @@ -733,17 +723,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { ( ExistentialPredicate::AutoTrait(lhs_id), ExistentialPredicate::AutoTrait(rhs_id), - ) => { - let lhs_id = match lhs_id { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; - let rhs_id = match rhs_id { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; - lhs_id.cmp(&rhs_id) - } + ) => lhs_id.0.cmp(&rhs_id.0), (ExistentialPredicate::Trait(_), _) => Ordering::Less, (_, ExistentialPredicate::Trait(_)) => Ordering::Greater, (ExistentialPredicate::AutoTrait(_), _) => Ordering::Less, @@ -1195,11 +1175,7 @@ pub(crate) fn generic_predicates_for_param_query<'db>( }; rustc_type_ir::elaborate::supertrait_def_ids(interner, tr.into()).any(|tr| { - let tr = match tr { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; - tr.trait_items(db).items.iter().any(|(name, item)| { + tr.0.trait_items(db).items.iter().any(|(name, item)| { matches!(item, AssocItemId::TypeAliasId(_)) && name == assoc_name }) }) @@ -1629,11 +1605,7 @@ pub(crate) fn associated_ty_item_bounds<'db>( .map_bound(|c| match c { rustc_type_ir::ClauseKind::Trait(t) => { let id = t.def_id(); - let id = match id { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; - let is_auto = db.trait_signature(id).flags.contains(TraitFlags::AUTO); + let is_auto = db.trait_signature(id.0).flags.contains(TraitFlags::AUTO); if is_auto { Some(ExistentialPredicate::AutoTrait(t.def_id())) } else { @@ -1677,7 +1649,7 @@ pub(crate) fn associated_ty_item_bounds<'db>( let sized_trait = LangItem::Sized.resolve_trait(db, resolver.krate()); let sized_clause = Binder::dummy(ExistentialPredicate::Trait(ExistentialTraitRef::new( interner, - SolverDefId::TraitId(trait_), + trait_.into(), [] as [crate::next_solver::GenericArg<'_>; 0], ))); bounds.push(sized_clause); @@ -1694,10 +1666,7 @@ pub(crate) fn associated_type_by_name_including_super_traits<'db>( ) -> Option<(TraitRef<'db>, TypeAliasId)> { let interner = DbInterner::new_with(db, None, None); rustc_type_ir::elaborate::supertraits(interner, Binder::dummy(trait_ref)).find_map(|t| { - let trait_id = match t.as_ref().skip_binder().def_id { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; + let trait_id = t.as_ref().skip_binder().def_id.0; let assoc_type = trait_id.trait_items(db).associated_type_by_name(name)?; Some((t.skip_binder(), assoc_type)) }) @@ -1727,10 +1696,7 @@ fn named_associated_type_shorthand_candidates<'db, R>( ) -> Option { let db = interner.db; let mut search = |t: TraitRef<'db>| -> Option { - let trait_id = match t.def_id { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; + let trait_id = t.def_id.0; let mut checked_traits = FxHashSet::default(); let mut check_trait = |trait_id: TraitId| { let name = &db.trait_signature(trait_id).name; @@ -1773,11 +1739,7 @@ fn named_associated_type_shorthand_candidates<'db, R>( rustc_type_ir::ClauseKind::Trait(pred) => pred.def_id(), _ => continue, }; - let trait_id = match trait_id { - SolverDefId::TraitId(trait_id) => trait_id, - _ => continue, - }; - stack.push(trait_id); + stack.push(trait_id.0); } tracing::debug!(?stack); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs index ccdb5a0bdb4a..7d6734303c48 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower_nextsolver/path.rs @@ -208,10 +208,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { tracing::debug!(?trait_ref); self.skip_resolved_segment(); let segment = self.current_or_prev_segment; - let trait_id = match trait_ref.def_id { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; + let trait_id = trait_ref.def_id.0; let found = trait_id.trait_items(self.ctx.db).associated_type_by_name(segment.name); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index 8bd71df7c191..f0be352fdd15 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -168,11 +168,7 @@ impl TyFingerprint { _ => None, }) .next()?; - let trait_id = match trait_ref { - SolverDefId::TraitId(id) => id, - _ => panic!("Bad GenericDefId in trait ref"), - }; - TyFingerprint::Dyn(trait_id) + TyFingerprint::Dyn(trait_ref.0) } TyKind::Ref(_, _, mutability) => match mutability { rustc_ast_ir::Mutability::Mut => TyFingerprint::Ref(Mutability::Mut), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs index cfafc65d1844..23789b06e828 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs @@ -81,7 +81,7 @@ impl<'db> std::fmt::Debug for InternedWrapperNoDebug; +pub type PlaceholderConst = Placeholder; #[derive(Copy, Clone, Hash, Eq, PartialEq)] pub struct ParamConst { @@ -304,7 +304,7 @@ impl<'db> rustc_type_ir::inherent::Const> for Const<'db> { fn new_bound( interner: DbInterner<'db>, debruijn: rustc_type_ir::DebruijnIndex, - var: BoundVar, + var: BoundConst, ) -> Self { Const::new(interner, ConstKind::Bound(debruijn, var)) } @@ -314,7 +314,7 @@ impl<'db> rustc_type_ir::inherent::Const> for Const<'db> { debruijn: rustc_type_ir::DebruijnIndex, var: rustc_type_ir::BoundVar, ) -> Self { - Const::new(interner, ConstKind::Bound(debruijn, var)) + Const::new(interner, ConstKind::Bound(debruijn, BoundConst { var })) } fn new_unevaluated( @@ -340,26 +340,41 @@ impl<'db> rustc_type_ir::inherent::Const> for Const<'db> { } } +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub struct BoundConst { + pub var: BoundVar, +} + +impl<'db> rustc_type_ir::inherent::BoundVarLike> for BoundConst { + fn var(self) -> BoundVar { + self.var + } + + fn assert_eq(self, var: BoundVarKind) { + var.expect_const() + } +} + impl<'db> PlaceholderLike> for PlaceholderConst { - type Bound = rustc_type_ir::BoundVar; + type Bound = BoundConst; fn universe(self) -> rustc_type_ir::UniverseIndex { self.universe } fn var(self) -> rustc_type_ir::BoundVar { - self.bound + self.bound.var } fn with_updated_universe(self, ui: rustc_type_ir::UniverseIndex) -> Self { Placeholder { universe: ui, bound: self.bound } } - fn new(ui: rustc_type_ir::UniverseIndex, var: rustc_type_ir::BoundVar) -> Self { + fn new(ui: rustc_type_ir::UniverseIndex, var: BoundConst) -> Self { Placeholder { universe: ui, bound: var } } fn new_anon(ui: rustc_type_ir::UniverseIndex, var: rustc_type_ir::BoundVar) -> Self { - Placeholder { universe: ui, bound: var } + Placeholder { universe: ui, bound: BoundConst { var } } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs index c9632ddcd4bb..8bbc6e337039 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs @@ -88,3 +88,60 @@ impl<'db> inherent::DefId> for SolverDefId { true } } + +macro_rules! declare_id_wrapper { + ($name:ident, $wraps:ident) => { + #[derive(Clone, Copy, PartialEq, Eq, Hash)] + pub struct $name(pub $wraps); + + impl std::fmt::Debug for $name { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(&self.0, f) + } + } + + impl From<$name> for $wraps { + #[inline] + fn from(value: $name) -> $wraps { + value.0 + } + } + + impl From<$wraps> for $name { + #[inline] + fn from(value: $wraps) -> $name { + Self(value) + } + } + + impl From<$name> for SolverDefId { + #[inline] + fn from(value: $name) -> SolverDefId { + value.0.into() + } + } + + impl TryFrom for $name { + type Error = (); + + #[inline] + fn try_from(value: SolverDefId) -> Result { + match value { + SolverDefId::$wraps(it) => Ok(Self(it)), + _ => Err(()), + } + } + } + + impl<'db> inherent::DefId> for $name { + fn as_local(self) -> Option { + Some(self.into()) + } + fn is_local(self) -> bool { + true + } + } + }; +} + +declare_id_wrapper!(TraitIdWrapper, TraitId); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs index 3cc1e64b6add..405a57d9e898 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fold.rs @@ -6,6 +6,8 @@ use rustc_type_ir::{ inherent::{IntoKind, Region as _}, }; +use crate::next_solver::BoundConst; + use super::{ Binder, BoundRegion, BoundTy, Const, ConstKind, DbInterner, Predicate, Region, Ty, TyKind, }; @@ -18,7 +20,7 @@ use super::{ pub trait BoundVarReplacerDelegate<'db> { fn replace_region(&mut self, br: BoundRegion) -> Region<'db>; fn replace_ty(&mut self, bt: BoundTy) -> Ty<'db>; - fn replace_const(&mut self, bv: BoundVar) -> Const<'db>; + fn replace_const(&mut self, bv: BoundConst) -> Const<'db>; } /// A simple delegate taking 3 mutable functions. The used functions must @@ -27,7 +29,7 @@ pub trait BoundVarReplacerDelegate<'db> { pub struct FnMutDelegate<'db, 'a> { pub regions: &'a mut (dyn FnMut(BoundRegion) -> Region<'db> + 'a), pub types: &'a mut (dyn FnMut(BoundTy) -> Ty<'db> + 'a), - pub consts: &'a mut (dyn FnMut(BoundVar) -> Const<'db> + 'a), + pub consts: &'a mut (dyn FnMut(BoundConst) -> Const<'db> + 'a), } impl<'db, 'a> BoundVarReplacerDelegate<'db> for FnMutDelegate<'db, 'a> { @@ -37,7 +39,7 @@ impl<'db, 'a> BoundVarReplacerDelegate<'db> for FnMutDelegate<'db, 'a> { fn replace_ty(&mut self, bt: BoundTy) -> Ty<'db> { (self.types)(bt) } - fn replace_const(&mut self, bv: BoundVar) -> Const<'db> { + fn replace_const(&mut self, bv: BoundConst) -> Const<'db> { (self.consts)(bv) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs index 007a674ad399..4258f4c7ac68 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs @@ -167,7 +167,7 @@ impl<'db> FulfillmentCtxt<'db> { } let result = delegate.evaluate_root_goal(goal, Span::dummy(), stalled_on); - let GoalEvaluation { certainty, has_changed, stalled_on } = match result { + let GoalEvaluation { goal: _, certainty, has_changed, stalled_on } = match result { Ok(result) => result, Err(NoSolution) => { errors.push(NextSolverError::TrueError(obligation)); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs index d284eb9c6b41..4e124d07d2b3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/generic_arg.rs @@ -384,7 +384,6 @@ impl<'db> rustc_type_ir::inherent::GenericArgs> for GenericArgs< signature_parts_ty: signature_parts_ty.expect_ty(), tupled_upvars_ty: tupled_upvars_ty.expect_ty(), coroutine_captures_by_ref_ty: coroutine_captures_by_ref_ty.expect_ty(), - coroutine_witness_ty: coroutine_witness_ty.expect_ty(), }, _ => panic!("GenericArgs were likely not for a CoroutineClosure."), } @@ -400,7 +399,6 @@ impl<'db> rustc_type_ir::inherent::GenericArgs> for GenericArgs< resume_ty: resume_ty.expect_ty(), yield_ty: yield_ty.expect_ty(), return_ty: return_ty.expect_ty(), - witness: Ty::new_unit(interner), tupled_upvars_ty: Ty::new_unit(interner), } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs index 0448f03463e4..6c7a87ef5249 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs @@ -6,6 +6,7 @@ //! //! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html +use crate::next_solver::BoundConst; use crate::next_solver::{ AliasTy, Binder, BoundRegion, BoundTy, Canonical, CanonicalVarValues, Const, DbInterner, Goal, ParamEnv, Predicate, PredicateKind, Region, Ty, TyKind, @@ -95,7 +96,7 @@ where GenericArgKind::Type(ty) => ty, r => panic!("{bound_ty:?} is a type but value is {r:?}"), }, - consts: &mut |bound_ct: BoundVar| match var_values[bound_ct].kind() { + consts: &mut |bound_ct: BoundConst| match var_values[bound_ct.var].kind() { GenericArgKind::Const(ct) => ct, c => panic!("{bound_ct:?} is a const but value is {c:?}"), }, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs index 58832aef8952..585719144ef4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs @@ -37,7 +37,7 @@ use unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey}; use crate::next_solver::fold::BoundVarReplacerDelegate; use crate::next_solver::infer::opaque_types::table::OpaqueTypeStorageEntries; -use crate::next_solver::{BoundRegion, BoundTy, BoundVarKind}; +use crate::next_solver::{BoundConst, BoundRegion, BoundTy, BoundVarKind}; use super::generics::GenericParamDef; use super::{ @@ -864,8 +864,8 @@ impl<'db> InferCtxt<'db> { fn replace_ty(&mut self, bt: BoundTy) -> Ty<'db> { self.args[bt.var.index()].expect_ty() } - fn replace_const(&mut self, bv: BoundVar) -> Const<'db> { - self.args[bv.index()].expect_const() + fn replace_const(&mut self, bv: BoundConst) -> Const<'db> { + self.args[bv.var.index()].expect_const() } } let delegate = ToFreshVars { args }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/higher_ranked.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/higher_ranked.rs index bb80c5157109..62028e0e7039 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/higher_ranked.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/relate/higher_ranked.rs @@ -10,8 +10,8 @@ use crate::next_solver::fold::FnMutDelegate; use crate::next_solver::infer::InferCtxt; use crate::next_solver::infer::snapshot::CombinedSnapshot; use crate::next_solver::{ - Binder, BoundRegion, BoundTy, Const, DbInterner, PlaceholderConst, PlaceholderRegion, - PlaceholderTy, Region, Ty, + Binder, BoundConst, BoundRegion, BoundTy, Const, DbInterner, PlaceholderConst, + PlaceholderRegion, PlaceholderTy, Region, Ty, }; impl<'db> InferCtxt<'db> { @@ -50,10 +50,10 @@ impl<'db> InferCtxt<'db> { PlaceholderTy { universe: next_universe, bound: bound_ty }, ) }, - consts: &mut |bound_var: BoundVar| { + consts: &mut |bound: BoundConst| { Const::new_placeholder( self.interner, - PlaceholderConst { universe: next_universe, bound: bound_var }, + PlaceholderConst { universe: next_universe, bound }, ) }, }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 6e1a5e964557..5709beaefc29 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -19,7 +19,7 @@ use rustc_type_ir::error::TypeError; use rustc_type_ir::inherent::{ AdtDef as _, GenericArgs as _, GenericsOf, IntoKind, SliceLike as _, Span as _, }; -use rustc_type_ir::lang_items::TraitSolverLangItem; +use rustc_type_ir::lang_items::{SolverLangItem, SolverTraitLangItem}; use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::{ AliasTerm, AliasTermKind, AliasTy, AliasTyKind, EarlyBinder, FlagComputation, Flags, @@ -44,9 +44,11 @@ use rustc_type_ir::{ use crate::lower_nextsolver::{self, TyLoweringContext}; use crate::method_resolution::{ALL_FLOAT_FPS, ALL_INT_FPS, TyFingerprint}; +use crate::next_solver::infer::InferCtxt; use crate::next_solver::util::{ContainsTypeErrors, explicit_item_bounds, for_trait_impls}; use crate::next_solver::{ - CanonicalVarKind, FxIndexMap, InternedWrapperNoDebug, RegionAssumptions, SolverDefIds, + BoundConst, CanonicalVarKind, FxIndexMap, InternedWrapperNoDebug, RegionAssumptions, + SolverContext, SolverDefIds, TraitIdWrapper, }; use crate::{ConstScalar, FnAbi, Interner, db::HirDatabase}; @@ -858,10 +860,35 @@ impl<'db> rustc_type_ir::relate::Relate> for Pattern<'db> { interned_vec_db!(PatList, Pattern); +macro_rules! as_lang_item { + ( + $solver_enum:ident, $var:ident; + + ignore = { + $( $ignore:ident ),* $(,)? + } + + $( $variant:ident ),* $(,)? + ) => {{ + // Ensure exhaustiveness. + if let Some(it) = None::<$solver_enum> { + match it { + $( $solver_enum::$variant => {} )* + $( $solver_enum::$ignore => {} )* + } + } + match $var { + $( LangItem::$variant => Some($solver_enum::$variant), )* + _ => None + } + }}; +} + impl<'db> rustc_type_ir::Interner for DbInterner<'db> { type DefId = SolverDefId; type LocalDefId = SolverDefId; type LocalDefIds = SolverDefIds; + type TraitId = TraitIdWrapper; type Span = Span; type GenericArgs = GenericArgs<'db>; @@ -923,7 +950,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { type Const = Const<'db>; type PlaceholderConst = PlaceholderConst; type ParamConst = ParamConst; - type BoundConst = rustc_type_ir::BoundVar; + type BoundConst = BoundConst; type ValueConst = ValueConst<'db>; type ValTree = Valtree<'db>; type ExprConst = ExprConst; @@ -1117,7 +1144,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { ); let alias_args = GenericArgs::new_from_iter(self, args.iter().skip(trait_generics.own_params.len())); - (TraitRef::new_from_args(self, trait_def_id, trait_args), alias_args) + (TraitRef::new_from_args(self, trait_def_id.try_into().unwrap(), trait_args), alias_args) } fn check_args_compatible(self, def_id: Self::DefId, args: Self::GenericArgs) -> bool { @@ -1305,11 +1332,11 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { #[tracing::instrument(skip(self), ret)] fn explicit_super_predicates_of( self, - def_id: Self::DefId, + def_id: Self::TraitId, ) -> EarlyBinder> { let predicates: Vec<(Clause<'db>, Span)> = self .db() - .generic_predicates_ns(def_id.try_into().unwrap()) + .generic_predicates_ns(def_id.0.into()) .iter() .cloned() .map(|p| (p, Span::dummy())) @@ -1369,65 +1396,19 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { false } - fn require_lang_item( - self, - lang_item: rustc_type_ir::lang_items::TraitSolverLangItem, - ) -> Self::DefId { + fn require_lang_item(self, lang_item: SolverLangItem) -> Self::DefId { let lang_item = match lang_item { - rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFn => LangItem::AsyncFn, - rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFnKindHelper => unimplemented!(), - rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFnKindUpvars => unimplemented!(), - rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFnMut => LangItem::AsyncFnMut, - rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFnOnce => LangItem::AsyncFnOnce, - rustc_type_ir::lang_items::TraitSolverLangItem::AsyncFnOnceOutput => { - LangItem::AsyncFnOnceOutput - } - rustc_type_ir::lang_items::TraitSolverLangItem::AsyncIterator => unimplemented!(), - rustc_type_ir::lang_items::TraitSolverLangItem::CallOnceFuture => { - LangItem::CallOnceFuture - } - rustc_type_ir::lang_items::TraitSolverLangItem::CallRefFuture => { - LangItem::CallRefFuture - } - rustc_type_ir::lang_items::TraitSolverLangItem::Clone => LangItem::Clone, - rustc_type_ir::lang_items::TraitSolverLangItem::Copy => LangItem::Copy, - rustc_type_ir::lang_items::TraitSolverLangItem::Coroutine => LangItem::Coroutine, - rustc_type_ir::lang_items::TraitSolverLangItem::CoroutineReturn => { - LangItem::CoroutineReturn - } - rustc_type_ir::lang_items::TraitSolverLangItem::CoroutineYield => { - LangItem::CoroutineYield - } - rustc_type_ir::lang_items::TraitSolverLangItem::Destruct => LangItem::Destruct, - rustc_type_ir::lang_items::TraitSolverLangItem::DiscriminantKind => { - LangItem::DiscriminantKind - } - rustc_type_ir::lang_items::TraitSolverLangItem::Drop => LangItem::Drop, - rustc_type_ir::lang_items::TraitSolverLangItem::DynMetadata => LangItem::DynMetadata, - rustc_type_ir::lang_items::TraitSolverLangItem::Fn => LangItem::Fn, - rustc_type_ir::lang_items::TraitSolverLangItem::FnMut => LangItem::FnMut, - rustc_type_ir::lang_items::TraitSolverLangItem::FnOnce => LangItem::FnOnce, - rustc_type_ir::lang_items::TraitSolverLangItem::FnPtrTrait => LangItem::FnPtrTrait, - rustc_type_ir::lang_items::TraitSolverLangItem::FusedIterator => unimplemented!(), - rustc_type_ir::lang_items::TraitSolverLangItem::Future => LangItem::Future, - rustc_type_ir::lang_items::TraitSolverLangItem::FutureOutput => LangItem::FutureOutput, - rustc_type_ir::lang_items::TraitSolverLangItem::Iterator => LangItem::Iterator, - rustc_type_ir::lang_items::TraitSolverLangItem::Metadata => LangItem::Metadata, - rustc_type_ir::lang_items::TraitSolverLangItem::Option => LangItem::Option, - rustc_type_ir::lang_items::TraitSolverLangItem::PointeeTrait => LangItem::PointeeTrait, - rustc_type_ir::lang_items::TraitSolverLangItem::Poll => LangItem::Poll, - rustc_type_ir::lang_items::TraitSolverLangItem::Sized => LangItem::Sized, - rustc_type_ir::lang_items::TraitSolverLangItem::MetaSized => LangItem::MetaSized, - rustc_type_ir::lang_items::TraitSolverLangItem::PointeeSized => LangItem::PointeeSized, - rustc_type_ir::lang_items::TraitSolverLangItem::TransmuteTrait => { - LangItem::TransmuteTrait - } - rustc_type_ir::lang_items::TraitSolverLangItem::Tuple => LangItem::Tuple, - rustc_type_ir::lang_items::TraitSolverLangItem::Unpin => LangItem::Unpin, - rustc_type_ir::lang_items::TraitSolverLangItem::Unsize => LangItem::Unsize, - rustc_type_ir::lang_items::TraitSolverLangItem::BikeshedGuaranteedNoDrop => { - unimplemented!() - } + SolverLangItem::AsyncFnKindUpvars => unimplemented!(), + SolverLangItem::AsyncFnOnceOutput => LangItem::AsyncFnOnceOutput, + SolverLangItem::CallOnceFuture => LangItem::CallOnceFuture, + SolverLangItem::CallRefFuture => LangItem::CallRefFuture, + SolverLangItem::CoroutineReturn => LangItem::CoroutineReturn, + SolverLangItem::CoroutineYield => LangItem::CoroutineYield, + SolverLangItem::DynMetadata => LangItem::DynMetadata, + SolverLangItem::FutureOutput => LangItem::FutureOutput, + SolverLangItem::Metadata => LangItem::Metadata, + SolverLangItem::Option => LangItem::Option, + SolverLangItem::Poll => LangItem::Poll, }; let target = hir_def::lang_item::lang_item( self.db(), @@ -1448,216 +1429,131 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { } } - #[allow(clippy::match_like_matches_macro)] - fn is_lang_item( - self, - def_id: Self::DefId, - lang_item: rustc_type_ir::lang_items::TraitSolverLangItem, - ) -> bool { - use rustc_type_ir::lang_items::TraitSolverLangItem::*; - - // FIXME: derive PartialEq on TraitSolverLangItem - self.as_lang_item(def_id).map_or(false, |l| match (l, lang_item) { - (AsyncFn, AsyncFn) => true, - (AsyncFnKindHelper, AsyncFnKindHelper) => true, - (AsyncFnKindUpvars, AsyncFnKindUpvars) => true, - (AsyncFnMut, AsyncFnMut) => true, - (AsyncFnOnce, AsyncFnOnce) => true, - (AsyncFnOnceOutput, AsyncFnOnceOutput) => true, - (AsyncIterator, AsyncIterator) => true, - (CallOnceFuture, CallOnceFuture) => true, - (CallRefFuture, CallRefFuture) => true, - (Clone, Clone) => true, - (Copy, Copy) => true, - (Coroutine, Coroutine) => true, - (CoroutineReturn, CoroutineReturn) => true, - (CoroutineYield, CoroutineYield) => true, - (Destruct, Destruct) => true, - (DiscriminantKind, DiscriminantKind) => true, - (Drop, Drop) => true, - (DynMetadata, DynMetadata) => true, - (Fn, Fn) => true, - (FnMut, FnMut) => true, - (FnOnce, FnOnce) => true, - (FnPtrTrait, FnPtrTrait) => true, - (FusedIterator, FusedIterator) => true, - (Future, Future) => true, - (FutureOutput, FutureOutput) => true, - (Iterator, Iterator) => true, - (Metadata, Metadata) => true, - (Option, Option) => true, - (PointeeTrait, PointeeTrait) => true, - (Poll, Poll) => true, - (Sized, Sized) => true, - (TransmuteTrait, TransmuteTrait) => true, - (Tuple, Tuple) => true, - (Unpin, Unpin) => true, - (Unsize, Unsize) => true, - _ => false, - }) + fn require_trait_lang_item(self, lang_item: SolverTraitLangItem) -> TraitIdWrapper { + let lang_item = match lang_item { + SolverTraitLangItem::AsyncFn => LangItem::AsyncFn, + SolverTraitLangItem::AsyncFnKindHelper => unimplemented!(), + SolverTraitLangItem::AsyncFnMut => LangItem::AsyncFnMut, + SolverTraitLangItem::AsyncFnOnce => LangItem::AsyncFnOnce, + SolverTraitLangItem::AsyncFnOnceOutput => LangItem::AsyncFnOnceOutput, + SolverTraitLangItem::AsyncIterator => unimplemented!(), + SolverTraitLangItem::Clone => LangItem::Clone, + SolverTraitLangItem::Copy => LangItem::Copy, + SolverTraitLangItem::Coroutine => LangItem::Coroutine, + SolverTraitLangItem::Destruct => LangItem::Destruct, + SolverTraitLangItem::DiscriminantKind => LangItem::DiscriminantKind, + SolverTraitLangItem::Drop => LangItem::Drop, + SolverTraitLangItem::Fn => LangItem::Fn, + SolverTraitLangItem::FnMut => LangItem::FnMut, + SolverTraitLangItem::FnOnce => LangItem::FnOnce, + SolverTraitLangItem::FnPtrTrait => LangItem::FnPtrTrait, + SolverTraitLangItem::FusedIterator => unimplemented!(), + SolverTraitLangItem::Future => LangItem::Future, + SolverTraitLangItem::Iterator => LangItem::Iterator, + SolverTraitLangItem::PointeeTrait => LangItem::PointeeTrait, + SolverTraitLangItem::Sized => LangItem::Sized, + SolverTraitLangItem::MetaSized => LangItem::MetaSized, + SolverTraitLangItem::PointeeSized => LangItem::PointeeSized, + SolverTraitLangItem::TransmuteTrait => LangItem::TransmuteTrait, + SolverTraitLangItem::Tuple => LangItem::Tuple, + SolverTraitLangItem::Unpin => LangItem::Unpin, + SolverTraitLangItem::Unsize => LangItem::Unsize, + SolverTraitLangItem::BikeshedGuaranteedNoDrop => { + unimplemented!() + } + }; + lang_item + .resolve_trait(self.db(), self.krate.expect("Must have self.krate")) + .unwrap_or_else(|| panic!("Lang item {lang_item:?} required but not found.")) + .into() } - fn as_lang_item( - self, - def_id: Self::DefId, - ) -> Option { - use rustc_type_ir::lang_items::TraitSolverLangItem; + #[allow(clippy::match_like_matches_macro)] + fn is_lang_item(self, def_id: Self::DefId, lang_item: SolverLangItem) -> bool { + use SolverLangItem::*; + // FIXME: derive PartialEq on SolverLangItem + self.as_lang_item(def_id) + .map_or(false, |l| std::mem::discriminant(&l) == std::mem::discriminant(&lang_item)) + } + + #[allow(clippy::match_like_matches_macro)] + fn is_trait_lang_item(self, def_id: Self::TraitId, lang_item: SolverTraitLangItem) -> bool { + use SolverTraitLangItem::*; + + // FIXME: derive PartialEq on SolverTraitLangItem + self.as_trait_lang_item(def_id) + .map_or(false, |l| std::mem::discriminant(&l) == std::mem::discriminant(&lang_item)) + } + + fn as_lang_item(self, def_id: Self::DefId) -> Option { let def_id: AttrDefId = match def_id { SolverDefId::TraitId(id) => id.into(), SolverDefId::TypeAliasId(id) => id.into(), + SolverDefId::AdtId(id) => id.into(), _ => panic!("Unexpected SolverDefId in as_lang_item"), }; let lang_item = self.db().lang_attr(def_id)?; - Some(match lang_item { - LangItem::Sized => TraitSolverLangItem::Sized, - LangItem::MetaSized => TraitSolverLangItem::MetaSized, - LangItem::PointeeSized => TraitSolverLangItem::PointeeSized, - LangItem::Unsize => TraitSolverLangItem::Unsize, - LangItem::StructuralPeq => return None, - LangItem::StructuralTeq => return None, - LangItem::Copy => TraitSolverLangItem::Copy, - LangItem::Clone => TraitSolverLangItem::Clone, - LangItem::Sync => return None, - LangItem::DiscriminantKind => TraitSolverLangItem::DiscriminantKind, - LangItem::Discriminant => return None, - LangItem::PointeeTrait => TraitSolverLangItem::PointeeTrait, - LangItem::Metadata => TraitSolverLangItem::Metadata, - LangItem::DynMetadata => TraitSolverLangItem::DynMetadata, - LangItem::Freeze => return None, - LangItem::FnPtrTrait => TraitSolverLangItem::FnPtrTrait, - LangItem::FnPtrAddr => return None, - LangItem::Drop => TraitSolverLangItem::Drop, - LangItem::Destruct => TraitSolverLangItem::Destruct, - LangItem::CoerceUnsized => return None, - LangItem::DispatchFromDyn => return None, - LangItem::TransmuteOpts => return None, - LangItem::TransmuteTrait => TraitSolverLangItem::TransmuteTrait, - LangItem::Add => return None, - LangItem::Sub => return None, - LangItem::Mul => return None, - LangItem::Div => return None, - LangItem::Rem => return None, - LangItem::Neg => return None, - LangItem::Not => return None, - LangItem::BitXor => return None, - LangItem::BitAnd => return None, - LangItem::BitOr => return None, - LangItem::Shl => return None, - LangItem::Shr => return None, - LangItem::AddAssign => return None, - LangItem::SubAssign => return None, - LangItem::MulAssign => return None, - LangItem::DivAssign => return None, - LangItem::RemAssign => return None, - LangItem::BitXorAssign => return None, - LangItem::BitAndAssign => return None, - LangItem::BitOrAssign => return None, - LangItem::ShlAssign => return None, - LangItem::ShrAssign => return None, - LangItem::Index => return None, - LangItem::IndexMut => return None, - LangItem::UnsafeCell => return None, - LangItem::VaList => return None, - LangItem::Deref => return None, - LangItem::DerefMut => return None, - LangItem::DerefTarget => return None, - LangItem::Receiver => return None, - LangItem::Fn => TraitSolverLangItem::Fn, - LangItem::FnMut => TraitSolverLangItem::FnMut, - LangItem::FnOnce => TraitSolverLangItem::FnOnce, - LangItem::FnOnceOutput => return None, - LangItem::Future => TraitSolverLangItem::Future, - LangItem::CoroutineState => return None, - LangItem::Coroutine => TraitSolverLangItem::Coroutine, - LangItem::CoroutineReturn => TraitSolverLangItem::CoroutineReturn, - LangItem::CoroutineYield => TraitSolverLangItem::CoroutineYield, - LangItem::Unpin => TraitSolverLangItem::Unpin, - LangItem::Pin => return None, - LangItem::PartialEq => return None, - LangItem::PartialOrd => return None, - LangItem::CVoid => return None, - LangItem::Panic => return None, - LangItem::PanicNounwind => return None, - LangItem::PanicFmt => return None, - LangItem::PanicDisplay => return None, - LangItem::ConstPanicFmt => return None, - LangItem::PanicBoundsCheck => return None, - LangItem::PanicMisalignedPointerDereference => return None, - LangItem::PanicInfo => return None, - LangItem::PanicLocation => return None, - LangItem::PanicImpl => return None, - LangItem::PanicCannotUnwind => return None, - LangItem::BeginPanic => return None, - LangItem::FormatAlignment => return None, - LangItem::FormatArgument => return None, - LangItem::FormatArguments => return None, - LangItem::FormatCount => return None, - LangItem::FormatPlaceholder => return None, - LangItem::FormatUnsafeArg => return None, - LangItem::ExchangeMalloc => return None, - LangItem::BoxFree => return None, - LangItem::DropInPlace => return None, - LangItem::AllocLayout => return None, - LangItem::Start => return None, - LangItem::EhPersonality => return None, - LangItem::EhCatchTypeinfo => return None, - LangItem::OwnedBox => return None, - LangItem::PhantomData => return None, - LangItem::ManuallyDrop => return None, - LangItem::MaybeUninit => return None, - LangItem::AlignOffset => return None, - LangItem::Termination => return None, - LangItem::Try => return None, - LangItem::Tuple => TraitSolverLangItem::Tuple, - LangItem::SliceLen => return None, - LangItem::TryTraitFromResidual => return None, - LangItem::TryTraitFromOutput => return None, - LangItem::TryTraitBranch => return None, - LangItem::TryTraitFromYeet => return None, - LangItem::PointerLike => return None, - LangItem::ConstParamTy => return None, - LangItem::Poll => TraitSolverLangItem::Poll, - LangItem::PollReady => return None, - LangItem::PollPending => return None, - LangItem::ResumeTy => return None, - LangItem::GetContext => return None, - LangItem::Context => return None, - LangItem::FuturePoll => return None, - LangItem::FutureOutput => TraitSolverLangItem::FutureOutput, - LangItem::Option => TraitSolverLangItem::Option, - LangItem::OptionSome => return None, - LangItem::OptionNone => return None, - LangItem::ResultOk => return None, - LangItem::ResultErr => return None, - LangItem::ControlFlowContinue => return None, - LangItem::ControlFlowBreak => return None, - LangItem::IntoFutureIntoFuture => return None, - LangItem::IntoIterIntoIter => return None, - LangItem::IteratorNext => return None, - LangItem::Iterator => TraitSolverLangItem::Iterator, - LangItem::PinNewUnchecked => return None, - LangItem::RangeFrom => return None, - LangItem::RangeFull => return None, - LangItem::RangeInclusiveStruct => return None, - LangItem::RangeInclusiveNew => return None, - LangItem::Range => return None, - LangItem::RangeToInclusive => return None, - LangItem::RangeTo => return None, - LangItem::String => return None, - LangItem::CStr => return None, - LangItem::AsyncFn => TraitSolverLangItem::AsyncFn, - LangItem::AsyncFnMut => TraitSolverLangItem::AsyncFnMut, - LangItem::AsyncFnOnce => TraitSolverLangItem::AsyncFnOnce, - LangItem::AsyncFnOnceOutput => TraitSolverLangItem::AsyncFnOnceOutput, - LangItem::CallRefFuture => TraitSolverLangItem::CallRefFuture, - LangItem::CallOnceFuture => TraitSolverLangItem::CallOnceFuture, - LangItem::Ordering => return None, - LangItem::PanicNullPointerDereference => return None, - LangItem::ReceiverTarget => return None, - LangItem::UnsafePinned => return None, - LangItem::AsyncFnOnceOutput => TraitSolverLangItem::AsyncFnOnceOutput, - }) + as_lang_item!( + SolverLangItem, lang_item; + + ignore = { + AsyncFnKindUpvars, + } + + Metadata, + DynMetadata, + CoroutineReturn, + CoroutineYield, + Poll, + FutureOutput, + Option, + AsyncFnOnceOutput, + CallRefFuture, + CallOnceFuture, + AsyncFnOnceOutput, + ) + } + + fn as_trait_lang_item(self, def_id: Self::TraitId) -> Option { + let def_id: AttrDefId = def_id.0.into(); + let lang_item = self.db().lang_attr(def_id)?; + as_lang_item!( + SolverTraitLangItem, lang_item; + + ignore = { + AsyncFnKindHelper, + AsyncIterator, + BikeshedGuaranteedNoDrop, + FusedIterator, + } + + Sized, + MetaSized, + PointeeSized, + Unsize, + Copy, + Clone, + DiscriminantKind, + PointeeTrait, + FnPtrTrait, + Drop, + Destruct, + TransmuteTrait, + Fn, + FnMut, + FnOnce, + Future, + Coroutine, + Unpin, + Tuple, + Iterator, + AsyncFn, + AsyncFnMut, + AsyncFnOnce, + AsyncFnOnceOutput, + AsyncFnOnceOutput, + ) } fn associated_type_def_ids(self, def_id: Self::DefId) -> impl IntoIterator { @@ -1670,15 +1566,11 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { fn for_each_relevant_impl( self, - trait_def_id: Self::DefId, + trait_: Self::TraitId, self_ty: Self::Ty, mut f: impl FnMut(Self::DefId), ) { - let trait_ = match trait_def_id { - SolverDefId::TraitId(id) => id, - _ => panic!("for_each_relevant_impl called for non-trait"), - }; - + let trait_ = trait_.0; let self_ty_fp = TyFingerprint::for_trait_impl_ns(&self_ty); let fps: &[TyFingerprint] = match self_ty.kind() { TyKind::Infer(InferTy::IntVar(..)) => &ALL_INT_FPS, @@ -1775,42 +1667,26 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { } } - fn trait_is_auto(self, trait_def_id: Self::DefId) -> bool { - let trait_ = match trait_def_id { - SolverDefId::TraitId(id) => id, - _ => panic!("Unexpected SolverDefId in trait_is_auto"), - }; - let trait_data = self.db().trait_signature(trait_); + fn trait_is_auto(self, trait_: Self::TraitId) -> bool { + let trait_data = self.db().trait_signature(trait_.0); trait_data.flags.contains(TraitFlags::AUTO) } - fn trait_is_alias(self, trait_def_id: Self::DefId) -> bool { - let trait_ = match trait_def_id { - SolverDefId::TraitId(id) => id, - _ => panic!("Unexpected SolverDefId in trait_is_alias"), - }; - let trait_data = self.db().trait_signature(trait_); + fn trait_is_alias(self, trait_: Self::TraitId) -> bool { + let trait_data = self.db().trait_signature(trait_.0); trait_data.flags.contains(TraitFlags::ALIAS) } - fn trait_is_dyn_compatible(self, trait_def_id: Self::DefId) -> bool { - let trait_ = match trait_def_id { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; - crate::dyn_compatibility::dyn_compatibility(self.db(), trait_).is_none() + fn trait_is_dyn_compatible(self, trait_: Self::TraitId) -> bool { + crate::dyn_compatibility::dyn_compatibility(self.db(), trait_.0).is_none() } - fn trait_is_fundamental(self, def_id: Self::DefId) -> bool { - let trait_ = match def_id { - SolverDefId::TraitId(id) => id, - _ => panic!("Unexpected SolverDefId in trait_is_fundamental"), - }; - let trait_data = self.db().trait_signature(trait_); + fn trait_is_fundamental(self, trait_: Self::TraitId) -> bool { + let trait_data = self.db().trait_signature(trait_.0); trait_data.flags.contains(TraitFlags::FUNDAMENTAL) } - fn trait_may_be_implemented_via_object(self, trait_def_id: Self::DefId) -> bool { + fn trait_may_be_implemented_via_object(self, trait_def_id: Self::TraitId) -> bool { // FIXME(next-solver): should check the `TraitFlags` for // the `#[rustc_do_not_implement_via_object]` flag true @@ -1920,12 +1796,12 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { (*entry.or_insert_with(|| BoundVarKind::Ty(BoundTyKind::Anon))).expect_ty(); Ty::new_bound(self.interner, DebruijnIndex::ZERO, BoundTy { var, kind }) } - fn replace_const(&mut self, bv: BoundVar) -> Const<'db> { - let entry = self.map.entry(bv); + fn replace_const(&mut self, bv: BoundConst) -> Const<'db> { + let entry = self.map.entry(bv.var); let index = entry.index(); let var = BoundVar::from_usize(index); let () = (*entry.or_insert_with(|| BoundVarKind::Const)).expect_const(); - Const::new_bound(self.interner, DebruijnIndex::ZERO, var) + Const::new_bound(self.interner, DebruijnIndex::ZERO, BoundConst { var }) } } @@ -2008,24 +1884,16 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { unimplemented!() } - fn is_default_trait(self, def_id: Self::DefId) -> bool { - self.as_lang_item(def_id).map_or(false, |l| matches!(l, TraitSolverLangItem::Sized)) + fn is_default_trait(self, def_id: Self::TraitId) -> bool { + self.as_trait_lang_item(def_id).map_or(false, |l| matches!(l, SolverTraitLangItem::Sized)) } - fn trait_is_coinductive(self, trait_def_id: Self::DefId) -> bool { - let id = match trait_def_id { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; - self.db().trait_signature(id).flags.contains(TraitFlags::COINDUCTIVE) + fn trait_is_coinductive(self, trait_: Self::TraitId) -> bool { + self.db().trait_signature(trait_.0).flags.contains(TraitFlags::COINDUCTIVE) } - fn trait_is_unsafe(self, trait_def_id: Self::DefId) -> bool { - let id = match trait_def_id { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; - self.db().trait_signature(id).flags.contains(TraitFlags::UNSAFE) + fn trait_is_unsafe(self, trait_: Self::TraitId) -> bool { + self.db().trait_signature(trait_.0).flags.contains(TraitFlags::UNSAFE) } fn impl_self_is_guaranteed_unsized(self, def_id: Self::DefId) -> bool { @@ -2047,6 +1915,20 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { // FIXME(next-solver) unimplemented!() } + + type Probe = rustc_type_ir::solve::inspect::Probe>; + fn mk_probe(self, probe: rustc_type_ir::solve::inspect::Probe) -> Self::Probe { + probe + } + fn evaluate_root_goal_for_proof_tree_raw( + self, + canonical_goal: rustc_type_ir::solve::CanonicalInput, + ) -> (rustc_type_ir::solve::QueryResult, Self::Probe) { + rustc_next_trait_solver::solve::evaluate_root_goal_for_proof_tree_raw_provider::< + SolverContext<'db>, + Self, + >(self, canonical_goal) + } } impl<'db> DbInterner<'db> { @@ -2072,7 +1954,9 @@ impl<'db> DbInterner<'db> { BoundTy { var: shift_bv(t.var), kind: t.kind }, ) }, - consts: &mut |c| Const::new_bound(self, DebruijnIndex::ZERO, shift_bv(c)), + consts: &mut |c| { + Const::new_bound(self, DebruijnIndex::ZERO, BoundConst { var: shift_bv(c.var) }) + }, }, ) } @@ -2135,6 +2019,7 @@ macro_rules! TrivialTypeTraversalImpls { TrivialTypeTraversalImpls! { SolverDefId, + TraitIdWrapper, Pattern<'db>, Safety, FnAbi, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs index 4d32b2713235..407e15756439 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ir_print.rs @@ -58,10 +58,7 @@ impl<'db> IrPrint> for DbInterner<'db> { fn print_debug(t: &ty::TraitRef, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { salsa::with_attached_database(|db| { - let trait_ = match t.def_id { - SolverDefId::TraitId(id) => id, - _ => panic!("Expected trait."), - }; + let trait_ = t.def_id.0; let self_ty = &t.args.as_slice()[0]; let trait_args = &t.args.as_slice()[1..]; if trait_args.is_empty() { @@ -122,10 +119,7 @@ impl<'db> IrPrint> for DbInterner<'db> { fmt: &mut std::fmt::Formatter<'_>, ) -> std::fmt::Result { salsa::with_attached_database(|db| { - let trait_ = match t.def_id { - SolverDefId::TraitId(id) => id, - _ => panic!("Expected trait."), - }; + let trait_ = t.def_id.0; fmt.write_str(&format!( "ExistentialTraitRef({:?}[{:?}])", db.as_view::().trait_signature(trait_).name.as_str(), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs index 6755d065e10d..dc54ee7d5878 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/mapping.rs @@ -22,6 +22,7 @@ use rustc_type_ir::{ use salsa::plumbing::FromId; use salsa::{Id, plumbing::AsId}; +use crate::next_solver::BoundConst; use crate::{ ConcreteConst, ConstScalar, ImplTraitId, Interner, MemoryMap, db::{ @@ -125,12 +126,12 @@ impl<'db> rustc_type_ir::TypeFolder> for BinderToEarlyBinder<'db fn fold_const(&mut self, c: Const<'db>) -> Const<'db> { match c.kind() { rustc_type_ir::ConstKind::Bound(debruijn, var) if self.debruijn == debruijn => { - let GenericParamId::ConstParamId(id) = self.params[var.as_usize()] else { + let GenericParamId::ConstParamId(id) = self.params[var.var.as_usize()] else { unreachable!() }; Const::new( self.cx(), - rustc_type_ir::ConstKind::Param(ParamConst { index: var.as_u32(), id }), + rustc_type_ir::ConstKind::Param(ParamConst { index: var.var.as_u32(), id }), ) } _ => c.super_fold_with(self), @@ -286,11 +287,8 @@ impl<'db> ChalkToNextSolver<'db, Ty<'db>> for chalk_ir::Ty { .flags .contains(TraitFlags::AUTO) { - ExistentialPredicate::AutoTrait(SolverDefId::TraitId( - trait_id, - )) + ExistentialPredicate::AutoTrait(trait_id.into()) } else { - let def_id = SolverDefId::TraitId(trait_id); let args = GenericArgs::new_from_iter( interner, trait_ref @@ -301,7 +299,7 @@ impl<'db> ChalkToNextSolver<'db, Ty<'db>> for chalk_ir::Ty { .map(|a| a.to_nextsolver(interner)), ); let trait_ref = ExistentialTraitRef::new_from_args( - interner, def_id, args, + interner, trait_id.into(), args, ); ExistentialPredicate::Trait(trait_ref) } @@ -473,7 +471,7 @@ impl<'db> ChalkToNextSolver<'db, Const<'db>> for chalk_ir::Const { match &data.value { chalk_ir::ConstValue::BoundVar(bound_var) => rustc_type_ir::ConstKind::Bound( bound_var.debruijn.to_nextsolver(interner), - rustc_type_ir::BoundVar::from_usize(bound_var.index), + BoundConst { var: rustc_type_ir::BoundVar::from_usize(bound_var.index) }, ), chalk_ir::ConstValue::InferenceVar(inference_var) => { rustc_type_ir::ConstKind::Infer(rustc_type_ir::InferConst::Var( @@ -871,11 +869,7 @@ impl<'db> ChalkToNextSolver<'db, PredicateKind<'db>> for chalk_ir::DomainGoal ChalkToNextSolver<'db, TraitRef<'db>> for chalk_ir::TraitRef { fn to_nextsolver(&self, interner: DbInterner<'db>) -> TraitRef<'db> { let args = self.substitution.to_nextsolver(interner); - TraitRef::new_from_args( - interner, - SolverDefId::TraitId(from_chalk_trait_id(self.trait_id)), - args, - ) + TraitRef::new_from_args(interner, from_chalk_trait_id(self.trait_id).into(), args) } } @@ -1207,20 +1201,14 @@ pub(crate) fn convert_ty_for_result<'db>(interner: DbInterner<'db>, ty: Ty<'db>) trait_ref.def_id, [self_ty.into()].into_iter().chain(trait_ref.args.iter()), ); - let trait_id = match trait_ref.def_id { - SolverDefId::TraitId(id) => to_chalk_trait_id(id), - _ => unreachable!(), - }; + let trait_id = to_chalk_trait_id(trait_ref.def_id.0); let substitution = convert_args_for_result(interner, trait_ref.args.as_slice()); let trait_ref = chalk_ir::TraitRef { trait_id, substitution }; chalk_ir::WhereClause::Implemented(trait_ref) } rustc_type_ir::ExistentialPredicate::AutoTrait(trait_) => { - let trait_id = match trait_ { - SolverDefId::TraitId(id) => to_chalk_trait_id(id), - _ => unreachable!(), - }; + let trait_id = to_chalk_trait_id(trait_.0); let substitution = chalk_ir::Substitution::from1( Interner, convert_ty_for_result(interner, self_ty), @@ -1354,7 +1342,7 @@ pub fn convert_const_for_result<'db>( rustc_type_ir::ConstKind::Bound(debruijn_index, var) => { chalk_ir::ConstValue::BoundVar(chalk_ir::BoundVar::new( chalk_ir::DebruijnIndex::new(debruijn_index.as_u32()), - var.index(), + var.var.index(), )) } rustc_type_ir::ConstKind::Placeholder(placeholder_const) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs index 50261bb5e381..c86d3a4aadb2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs @@ -15,6 +15,8 @@ use rustc_type_ir::{ }; use smallvec::{SmallVec, smallvec}; +use crate::next_solver::TraitIdWrapper; + use super::{Binder, BoundVarKinds, DbInterner, Region, Ty, interned_vec_db}; pub type BoundExistentialPredicate<'db> = Binder<'db, ExistentialPredicate<'db>>; @@ -72,7 +74,7 @@ interned_vec_db!(BoundExistentialPredicates, BoundExistentialPredicate); impl<'db> rustc_type_ir::inherent::BoundExistentialPredicates> for BoundExistentialPredicates<'db> { - fn principal_def_id(self) -> Option< as rustc_type_ir::Interner>::DefId> { + fn principal_def_id(self) -> Option { self.principal().map(|trait_ref| trait_ref.skip_binder().def_id) } @@ -89,9 +91,7 @@ impl<'db> rustc_type_ir::inherent::BoundExistentialPredicates> .transpose() } - fn auto_traits( - self, - ) -> impl IntoIterator as rustc_type_ir::Interner>::DefId> { + fn auto_traits(self) -> impl IntoIterator { self.iter().filter_map(|predicate| match predicate.skip_binder() { ExistentialPredicate::AutoTrait(did) => Some(did), _ => None, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs index 45888ac4d235..1d0ee0e488dc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/solver.rs @@ -2,10 +2,10 @@ use hir_def::{AssocItemId, GeneralConstId, TypeAliasId}; use rustc_next_trait_solver::delegate::SolverDelegate; +use rustc_type_ir::lang_items::SolverTraitLangItem; use rustc_type_ir::{ InferCtxtLike, Interner, PredicatePolarity, TypeFlags, TypeVisitableExt, UniverseIndex, inherent::{IntoKind, SliceLike, Span as _, Term as _, Ty as _}, - lang_items::TraitSolverLangItem, solve::{Certainty, NoSolution}, }; @@ -225,14 +225,14 @@ impl<'db> SolverDelegate for SolverContext<'db> { } if trait_pred.polarity() == PredicatePolarity::Positive { - match self.0.cx().as_lang_item(trait_pred.def_id()) { - Some(TraitSolverLangItem::Sized) | Some(TraitSolverLangItem::MetaSized) => { + match self.0.cx().as_trait_lang_item(trait_pred.def_id()) { + Some(SolverTraitLangItem::Sized) | Some(SolverTraitLangItem::MetaSized) => { let predicate = self.resolve_vars_if_possible(goal.predicate); if sizedness_fast_path(self.cx(), predicate, goal.param_env) { return Some(Certainty::Yes); } } - Some(TraitSolverLangItem::Copy | TraitSolverLangItem::Clone) => { + Some(SolverTraitLangItem::Copy | SolverTraitLangItem::Clone) => { let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty().skip_binder()); // Unlike `Sized` traits, which always prefer the built-in impl, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index 92f1076e75ec..4794e2d60450 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -4,6 +4,7 @@ use hir_def::{GenericDefId, TypeOrConstParamId, TypeParamId}; use intern::{Interned, Symbol, sym}; use rustc_abi::{Float, Integer, Size}; use rustc_ast_ir::{Mutability, try_visit, visit::VisitorResult}; +use rustc_type_ir::Interner; use rustc_type_ir::{ BoundVar, ClosureKind, FlagComputation, Flags, FloatTy, FloatVid, InferTy, IntTy, IntVid, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, UintTy, @@ -19,6 +20,7 @@ use rustc_type_ir::{ use salsa::plumbing::{AsId, FromId}; use smallvec::SmallVec; +use crate::next_solver::GenericArg; use crate::{ db::HirDatabase, interner::InternedWrapperNoDebug, @@ -645,6 +647,31 @@ impl<'db> rustc_type_ir::inherent::Ty> for Ty<'db> { Ty::new(interner, TyKind::CoroutineWitness(def_id, args)) } + fn new_coroutine_witness_for_coroutine( + interner: DbInterner<'db>, + def_id: as rustc_type_ir::Interner>::DefId, + coroutine_args: as rustc_type_ir::Interner>::GenericArgs, + ) -> Self { + // HACK: Coroutine witness types are lifetime erased, so they + // never reference any lifetime args from the coroutine. We erase + // the regions here since we may get into situations where a + // coroutine is recursively contained within itself, leading to + // witness types that differ by region args. This means that + // cycle detection in fulfillment will not kick in, which leads + // to unnecessary overflows in async code. See the issue: + // . + let coroutine_args = interner.mk_args_from_iter(coroutine_args.iter().map(|arg| { + match arg { + GenericArg::Ty(_) | GenericArg::Const(_) => arg, + GenericArg::Lifetime(_) => { + crate::next_solver::Region::new(interner, rustc_type_ir::RegionKind::ReErased) + .into() + } + } + })); + Ty::new_coroutine_witness(interner, def_id, coroutine_args) + } + fn new_ptr(interner: DbInterner<'db>, ty: Self, mutbl: rustc_ast_ir::Mutability) -> Self { Ty::new(interner, TyKind::RawPtr(ty, mutbl)) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs index 1db02e9eb611..50b96a160ed1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs @@ -14,7 +14,7 @@ use rustc_type_ir::inherent::{ AdtDef, Const as _, GenericArg as _, GenericArgs as _, ParamEnv as _, Region as _, SliceLike, Ty as _, }; -use rustc_type_ir::lang_items::TraitSolverLangItem; +use rustc_type_ir::lang_items::SolverTraitLangItem; use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::{ BoundVar, Canonical, DebruijnIndex, GenericArgKind, INNERMOST, Interner, PredicatePolarity, @@ -29,8 +29,8 @@ use rustc_type_ir::{InferCtxtLike, TypeFoldable}; use crate::lower_nextsolver::{LifetimeElisionKind, TyLoweringContext}; use crate::next_solver::infer::InferCtxt; use crate::next_solver::{ - CanonicalVarKind, FxIndexMap, ParamEnv, Placeholder, PlaceholderConst, PlaceholderRegion, - TypingMode, + BoundConst, CanonicalVarKind, FxIndexMap, ParamEnv, Placeholder, PlaceholderConst, + PlaceholderRegion, TypingMode, }; use crate::{ db::HirDatabase, @@ -511,7 +511,7 @@ pub fn apply_args_to_binder<'db, T: TypeFoldable>>( ) -> T { let types = &mut |ty: BoundTy| args.as_slice()[ty.var.index()].expect_ty(); let regions = &mut |region: BoundRegion| args.as_slice()[region.var.index()].expect_region(); - let consts = &mut |const_: BoundVar| args.as_slice()[const_.index()].expect_const(); + let consts = &mut |const_: BoundConst| args.as_slice()[const_.var.index()].expect_const(); let mut instantiate = BoundVarReplacer::new(interner, FnMutDelegate { types, regions, consts }); b.skip_binder().fold_with(&mut instantiate) } @@ -654,7 +654,10 @@ impl<'db> TypeFolder> for MiniCanonicalizer<'_, 'db> { ConstKind::Infer(infer) => { let len = self.vars.len(); let var = *self.vars.entry(c.into()).or_insert(len); - Const::new(self.cx(), ConstKind::Bound(self.db, BoundVar::from_usize(var))) + Const::new( + self.cx(), + ConstKind::Bound(self.db, BoundConst { var: BoundVar::from_usize(var) }), + ) } _ => c.super_fold_with(self), } @@ -852,7 +855,7 @@ pub struct PlaceholderReplacer<'a, 'db> { infcx: &'a InferCtxt<'db>, mapped_regions: FxIndexMap, mapped_types: FxIndexMap, BoundTy>, - mapped_consts: FxIndexMap, + mapped_consts: FxIndexMap, universe_indices: &'a [Option], current_index: DebruijnIndex, } @@ -862,7 +865,7 @@ impl<'a, 'db> PlaceholderReplacer<'a, 'db> { infcx: &'a InferCtxt<'db>, mapped_regions: FxIndexMap, mapped_types: FxIndexMap, BoundTy>, - mapped_consts: FxIndexMap, + mapped_consts: FxIndexMap, universe_indices: &'a [Option], value: T, ) -> T { @@ -1026,9 +1029,9 @@ pub fn sizedness_fast_path<'db>( if let PredicateKind::Clause(ClauseKind::Trait(trait_pred)) = predicate.kind().skip_binder() && trait_pred.polarity == PredicatePolarity::Positive { - let sizedness = match tcx.as_lang_item(trait_pred.def_id()) { - Some(TraitSolverLangItem::Sized) => SizedTraitKind::Sized, - Some(TraitSolverLangItem::MetaSized) => SizedTraitKind::MetaSized, + let sizedness = match tcx.as_trait_lang_item(trait_pred.def_id()) { + Some(SolverTraitLangItem::Sized) => SizedTraitKind::Sized, + Some(SolverTraitLangItem::MetaSized) => SizedTraitKind::MetaSized, _ => return false, }; @@ -1051,7 +1054,10 @@ pub fn sizedness_fast_path<'db>( && clause_pred.self_ty() == trait_pred.self_ty() && (clause_pred.def_id() == trait_pred.def_id() || (sizedness == SizedTraitKind::MetaSized - && tcx.is_lang_item(clause_pred.def_id(), TraitSolverLangItem::Sized))) + && tcx.is_trait_lang_item( + clause_pred.def_id(), + SolverTraitLangItem::Sized, + ))) { return true; } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs index 092d4e3a8d9e..881b1c1a9db2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs @@ -212,10 +212,7 @@ fn direct_super_trait_refs(db: &dyn HirDatabase, trait_ref: &TraitRef, cb: impl rustc_type_ir::ClauseKind::Trait(t) => { let t = rustc_type_ir::EarlyBinder::bind(t).instantiate(interner, trait_ref_args); - let trait_id = match t.def_id() { - crate::next_solver::SolverDefId::TraitId(id) => to_chalk_trait_id(id), - _ => unreachable!(), - }; + let trait_id = to_chalk_trait_id(t.def_id().0); let substitution = convert_args_for_result(interner, t.trait_ref.args.as_slice()); diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 46d3a2bcd67c..6610375451dd 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -86,8 +86,7 @@ use hir_ty::{ method_resolution, mir::{MutBorrowKind, interpret_mir}, next_solver::{ - ClauseKind, DbInterner, GenericArgs, SolverDefId, infer::InferCtxt, - mapping::ChalkToNextSolver, + ClauseKind, DbInterner, GenericArgs, infer::InferCtxt, mapping::ChalkToNextSolver, }, primitive::UintTy, traits::FnTrait, @@ -4251,13 +4250,7 @@ impl TypeParam { db.generic_predicates_for_param_ns(self.id.parent(), self.id.into(), None) .iter() .filter_map(|pred| match &pred.kind().skip_binder() { - ClauseKind::Trait(trait_ref) => { - let trait_ = match trait_ref.def_id() { - SolverDefId::TraitId(t) => t, - _ => unreachable!(), - }; - Some(Trait::from(trait_)) - } + ClauseKind::Trait(trait_ref) => Some(Trait::from(trait_ref.def_id().0)), _ => None, }) .collect() @@ -4515,11 +4508,7 @@ impl Impl { pub fn trait_(self, db: &dyn HirDatabase) -> Option { let trait_ref = db.impl_trait_ns(self.id)?; let id = trait_ref.skip_binder().def_id; - let id = match id { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; - Some(Trait { id }) + Some(Trait { id: id.0 }) } pub fn trait_ref(self, db: &dyn HirDatabase) -> Option> { @@ -4609,11 +4598,7 @@ impl<'db> TraitRef<'db> { } pub fn trait_(&self) -> Trait { - let id = match self.trait_ref.def_id { - SolverDefId::TraitId(id) => id, - _ => unreachable!(), - }; - Trait { id } + Trait { id: self.trait_ref.def_id.0 } } pub fn self_ty(&self) -> TypeNs<'_> { @@ -5984,11 +5969,7 @@ impl<'db> TypeNs<'db> { infcx.interner, [self.ty].into_iter().chain(args.iter().map(|t| t.ty)).map(|t| t.into()), ); - let trait_ref = hir_ty::next_solver::TraitRef::new( - infcx.interner, - SolverDefId::TraitId(trait_.id), - args, - ); + let trait_ref = hir_ty::next_solver::TraitRef::new(infcx.interner, trait_.id.into(), args); let pred_kind = rustc_type_ir::Binder::dummy(rustc_type_ir::PredicateKind::Clause( rustc_type_ir::ClauseKind::Trait(rustc_type_ir::TraitPredicate { From 9b0611ff7aeb28bd3fcd3117256a1d9d9c2f1921 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 4 Sep 2025 18:27:27 -0400 Subject: [PATCH 188/682] Merge commit 'e9b70454e4c9584be3b22ddabd26b741aeb06c10' into clippy-subtree-update --- .cargo/config.toml | 7 + CHANGELOG.md | 6 + book/src/lint_configuration.md | 14 +- clippy.toml | 6 +- clippy_config/src/conf.rs | 6 +- clippy_lints/Cargo.toml | 4 + .../src/assertions_on_result_states.rs | 27 +- clippy_lints/src/async_yields_async.rs | 60 +++-- clippy_lints/src/bool_comparison.rs | 179 +++++++++++++ clippy_lints/src/casts/cast_ptr_alignment.rs | 30 +-- .../src/casts/cast_slice_from_raw_parts.rs | 43 +++- clippy_lints/src/casts/mod.rs | 8 +- clippy_lints/src/casts/ptr_as_ptr.rs | 37 +-- clippy_lints/src/casts/ptr_cast_constness.rs | 34 +-- clippy_lints/src/cognitive_complexity.rs | 4 +- clippy_lints/src/declared_lints.rs | 2 +- clippy_lints/src/derivable_impls.rs | 80 +++++- clippy_lints/src/entry.rs | 67 ++--- clippy_lints/src/eta_reduction.rs | 21 +- clippy_lints/src/float_literal.rs | 45 +++- clippy_lints/src/functions/mod.rs | 2 +- .../src/functions/too_many_arguments.rs | 37 +-- clippy_lints/src/if_then_some_else_none.rs | 32 +-- clippy_lints/src/instant_subtraction.rs | 5 +- clippy_lints/src/large_include_file.rs | 4 +- clippy_lints/src/len_zero.rs | 22 +- clippy_lints/src/lib.rs | 7 +- clippy_lints/src/loops/infinite_loop.rs | 9 +- clippy_lints/src/loops/missing_spin_loop.rs | 6 +- clippy_lints/src/loops/needless_range_loop.rs | 33 ++- .../src/loops/unused_enumerate_index.rs | 5 +- clippy_lints/src/manual_is_ascii_check.rs | 26 +- clippy_lints/src/manual_retain.rs | 10 +- clippy_lints/src/manual_strip.rs | 10 +- clippy_lints/src/map_unit_fn.rs | 21 +- clippy_lints/src/matches/collapsible_match.rs | 49 +++- clippy_lints/src/matches/mod.rs | 11 +- .../matches/significant_drop_in_scrutinee.rs | 9 +- clippy_lints/src/matches/try_err.rs | 40 +-- clippy_lints/src/mem_replace.rs | 7 +- clippy_lints/src/methods/clone_on_ref_ptr.rs | 7 +- clippy_lints/src/methods/filter_map.rs | 14 +- .../src/methods/iter_out_of_bounds.rs | 45 ++-- clippy_lints/src/methods/map_flatten.rs | 8 +- clippy_lints/src/methods/map_identity.rs | 96 +++++-- .../src/methods/option_as_ref_deref.rs | 28 +- clippy_lints/src/methods/or_then_unwrap.rs | 2 +- .../src/methods/read_line_without_trim.rs | 4 +- .../src/methods/single_char_add_str.rs | 8 +- .../src/methods/suspicious_to_owned.rs | 5 +- .../src/methods/unnecessary_min_or_max.rs | 3 +- .../src/methods/unused_enumerate_index.rs | 7 +- clippy_lints/src/missing_inline.rs | 38 ++- clippy_lints/src/mut_reference.rs | 13 +- clippy_lints/src/needless_bool.rs | 228 +---------------- clippy_lints/src/needless_for_each.rs | 36 ++- clippy_lints/src/non_canonical_impls.rs | 9 +- .../src/non_octal_unix_permissions.rs | 12 +- .../src/operators/assign_op_pattern.rs | 27 +- clippy_lints/src/operators/cmp_owned.rs | 12 +- .../operators/float_equality_without_abs.rs | 5 +- clippy_lints/src/panic_unimplemented.rs | 6 +- clippy_lints/src/redundant_clone.rs | 20 +- clippy_lints/src/semicolon_block.rs | 5 + clippy_lints/src/std_instead_of_core.rs | 2 + clippy_lints/src/strings.rs | 7 +- .../missing_transmute_annotations.rs | 55 +++- clippy_lints/src/transmute/mod.rs | 2 +- .../transmute/transmute_int_to_non_zero.rs | 53 ++-- clippy_lints/src/types/rc_buffer.rs | 5 +- .../src/types/redundant_allocation.rs | 13 +- clippy_lints/src/unit_types/unit_cmp.rs | 9 +- .../src/unnecessary_owned_empty_strings.rs | 5 +- clippy_lints/src/unnecessary_wraps.rs | 10 +- clippy_lints/src/unnested_or_patterns.rs | 6 +- clippy_lints/src/unused_io_amount.rs | 4 +- clippy_lints/src/unused_unit.rs | 58 ++--- clippy_lints/src/unwrap_in_result.rs | 202 +++++++++++---- clippy_lints/src/useless_conversion.rs | 9 +- clippy_lints/src/vec.rs | 240 +++++++++++------- clippy_lints/src/write.rs | 13 +- clippy_lints/src/zombie_processes.rs | 20 +- clippy_utils/README.md | 2 +- clippy_utils/src/ast_utils/mod.rs | 2 +- clippy_utils/src/higher.rs | 27 +- clippy_utils/src/hir_utils.rs | 2 +- clippy_utils/src/lib.rs | 55 +++- clippy_utils/src/ty/mod.rs | 9 +- clippy_utils/src/usage.rs | 8 +- rust-toolchain.toml | 2 +- src/driver.rs | 3 +- tests/config-consistency.rs | 30 +++ tests/no-profile-in-cargo-toml.rs | 34 +++ tests/ui-internal/disallow_span_lint.stderr | 4 +- tests/ui-toml/excessive_precision/clippy.toml | 1 + .../excessive_precision.fixed | 38 +++ .../excessive_precision.rs | 38 +++ .../excessive_precision.stderr | 38 +++ .../toml_unknown_key/conf_unknown_key.stderr | 3 + tests/ui/assertions_on_result_states.fixed | 11 +- tests/ui/assertions_on_result_states.rs | 11 +- tests/ui/assertions_on_result_states.stderr | 10 +- tests/ui/assign_ops.fixed | 39 ++- tests/ui/assign_ops.rs | 41 ++- tests/ui/assign_ops.stderr | 50 +++- tests/ui/async_yields_async.fixed | 39 +++ tests/ui/async_yields_async.rs | 39 +++ tests/ui/async_yields_async.stderr | 47 +++- tests/ui/bool_comparison.fixed | 167 +++++------- tests/ui/bool_comparison.rs | 167 +++++------- tests/ui/bool_comparison.stderr | 154 ++++++----- tests/ui/cast_raw_slice_pointer_cast.fixed | 39 ++- tests/ui/cast_raw_slice_pointer_cast.rs | 25 +- tests/ui/cast_raw_slice_pointer_cast.stderr | 66 +++-- .../cast_raw_slice_pointer_cast_no_std.fixed | 55 ++++ .../ui/cast_raw_slice_pointer_cast_no_std.rs | 55 ++++ .../cast_raw_slice_pointer_cast_no_std.stderr | 83 ++++++ tests/ui/collapsible_match.rs | 41 +++ tests/ui/collapsible_match.stderr | 71 +++++- tests/ui/collapsible_match2.stderr | 2 + tests/ui/derivable_impls.fixed | 38 +++ tests/ui/derivable_impls.rs | 41 +++ tests/ui/derivable_impls.stderr | 47 +++- tests/ui/derivable_impls_derive_const.fixed | 25 ++ tests/ui/derivable_impls_derive_const.rs | 32 +++ tests/ui/derivable_impls_derive_const.stderr | 46 ++++ tests/ui/doc/doc-fixable.fixed | 1 + tests/ui/doc/doc-fixable.rs | 1 + tests/ui/doc/doc-fixable.stderr | 44 ++-- tests/ui/entry_unfixable.stderr | 5 + tests/ui/eta.fixed | 45 ++++ tests/ui/eta.rs | 45 ++++ tests/ui/eta.stderr | 28 +- tests/ui/excessive_precision.fixed | 45 +++- tests/ui/excessive_precision.rs | 45 +++- tests/ui/excessive_precision.stderr | 85 +++++-- tests/ui/float_equality_without_abs.rs | 14 +- tests/ui/float_equality_without_abs.stderr | 18 +- tests/ui/if_then_some_else_none.fixed | 41 +++ tests/ui/if_then_some_else_none.rs | 51 ++++ tests/ui/if_then_some_else_none.stderr | 25 +- tests/ui/infinite_loops.rs | 15 ++ tests/ui/infinite_loops.stderr | 12 +- tests/ui/manual_is_ascii_check.fixed | 4 + tests/ui/manual_is_ascii_check.rs | 4 + tests/ui/manual_is_ascii_check.stderr | 30 ++- tests/ui/map_identity.fixed | 20 +- tests/ui/map_identity.rs | 16 +- tests/ui/map_identity.stderr | 66 +++-- tests/ui/missing_inline.rs | 7 + ...missing_transmute_annotations_unfixable.rs | 29 +++ ...ing_transmute_annotations_unfixable.stderr | 36 +++ tests/ui/mut_reference.fixed | 170 +++++++++++++ tests/ui/mut_reference.rs | 152 +++++++++-- tests/ui/mut_reference.stderr | 76 +++++- tests/ui/needless_bool/fixable.stderr | 8 +- tests/ui/needless_for_each_unfixable.rs | 19 +- tests/ui/needless_for_each_unfixable.stderr | 38 ++- tests/ui/needless_range_loop.rs | 32 +++ tests/ui/needless_range_loop.stderr | 26 +- tests/ui/nonminimal_bool.rs | 2 - tests/ui/nonminimal_bool.stderr | 54 ++-- tests/ui/option_map_unit_fn_fixable.fixed | 9 + tests/ui/option_map_unit_fn_fixable.rs | 9 + tests/ui/option_map_unit_fn_fixable.stderr | 18 +- tests/ui/or_then_unwrap.fixed | 6 + tests/ui/or_then_unwrap.rs | 6 + tests/ui/or_then_unwrap.stderr | 10 +- tests/ui/panicking_macros.rs | 15 ++ tests/ui/panicking_macros.stderr | 46 +++- tests/ui/print_literal.fixed | 13 + tests/ui/print_literal.rs | 14 + tests/ui/print_literal.stderr | 38 ++- tests/ui/ptr_as_ptr.fixed | 6 + tests/ui/ptr_as_ptr.rs | 6 + tests/ui/ptr_as_ptr.stderr | 8 +- tests/ui/ptr_cast_constness.fixed | 6 + tests/ui/ptr_cast_constness.rs | 6 + tests/ui/ptr_cast_constness.stderr | 8 +- tests/ui/semicolon_inside_block.fixed | 5 + tests/ui/semicolon_inside_block.rs | 5 + ...micolon_inside_block_stmt_expr_attrs.fixed | 11 + .../semicolon_inside_block_stmt_expr_attrs.rs | 11 + ...icolon_inside_block_stmt_expr_attrs.stderr | 16 ++ tests/ui/std_instead_of_core.fixed | 7 + tests/ui/std_instead_of_core.rs | 7 + tests/ui/unit_cmp.rs | 17 ++ tests/ui/unit_cmp.stderr | 20 +- tests/ui/unnecessary_clone.stderr | 12 +- tests/ui/unnested_or_patterns.fixed | 25 +- tests/ui/unnested_or_patterns.rs | 25 +- tests/ui/unnested_or_patterns.stderr | 54 ++-- tests/ui/unused_unit.edition2021.fixed | 27 +- tests/ui/unused_unit.edition2021.stderr | 6 +- tests/ui/unused_unit.edition2024.fixed | 29 ++- tests/ui/unused_unit.rs | 29 ++- tests/ui/unwrap_in_result.rs | 70 ++++- tests/ui/unwrap_in_result.stderr | 132 +++++++--- tests/ui/vec.fixed | 9 + tests/ui/vec.rs | 9 + tests/ui/vec.stderr | 8 +- triagebot.toml | 7 + 202 files changed, 4541 insertions(+), 1646 deletions(-) create mode 100644 clippy_lints/src/bool_comparison.rs create mode 100644 tests/config-consistency.rs create mode 100644 tests/no-profile-in-cargo-toml.rs create mode 100644 tests/ui-toml/excessive_precision/clippy.toml create mode 100644 tests/ui-toml/excessive_precision/excessive_precision.fixed create mode 100644 tests/ui-toml/excessive_precision/excessive_precision.rs create mode 100644 tests/ui-toml/excessive_precision/excessive_precision.stderr create mode 100644 tests/ui/cast_raw_slice_pointer_cast_no_std.fixed create mode 100644 tests/ui/cast_raw_slice_pointer_cast_no_std.rs create mode 100644 tests/ui/cast_raw_slice_pointer_cast_no_std.stderr create mode 100644 tests/ui/derivable_impls_derive_const.fixed create mode 100644 tests/ui/derivable_impls_derive_const.rs create mode 100644 tests/ui/derivable_impls_derive_const.stderr create mode 100644 tests/ui/missing_transmute_annotations_unfixable.rs create mode 100644 tests/ui/missing_transmute_annotations_unfixable.stderr create mode 100644 tests/ui/mut_reference.fixed create mode 100644 tests/ui/semicolon_inside_block_stmt_expr_attrs.fixed create mode 100644 tests/ui/semicolon_inside_block_stmt_expr_attrs.rs create mode 100644 tests/ui/semicolon_inside_block_stmt_expr_attrs.stderr diff --git a/.cargo/config.toml b/.cargo/config.toml index d9c635df5dc7..a09bf95e87bd 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -23,3 +23,10 @@ split-debuginfo = "unpacked" rustflags = ["--remap-path-prefix", "=clippy_dev"] [profile.dev.package.lintcheck] rustflags = ["--remap-path-prefix", "=lintcheck"] + +# quine-mc_cluskey makes up a significant part of the runtime in dogfood +# due to the number of conditions in the clippy_lints crate +# and enabling optimizations for that specific dependency helps a bit +# without increasing total build times. +[profile.dev.package.quine-mc_cluskey] +opt-level = 3 diff --git a/CHANGELOG.md b/CHANGELOG.md index bc60b1c57f7d..eb2a76a81836 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,8 @@ Current stable, released 2025-08-07 * Removed superseded lints: `transmute_float_to_int`, `transmute_int_to_char`, `transmute_int_to_float`, `transmute_num_to_bytes` (now in rustc) [#14703](https://github.com/rust-lang/rust-clippy/pull/14703) +* Move [`uninlined_format_args`] to `pedantic` (from `style`, now allow-by-default) + [#15287](https://github.com/rust-lang/rust-clippy/pull/15287) ### Enhancements @@ -74,6 +76,9 @@ Current stable, released 2025-08-07 [#14719](https://github.com/rust-lang/rust-clippy/pull/14719) * [`unnecessary_to_owned`] fixed FP when map key is a reference [#14834](https://github.com/rust-lang/rust-clippy/pull/14834) +* [`swap_with_temporary`]: fix false positive leading to different semantics + being suggested, and use the right number of dereferences in suggestion + [#15172](https://github.com/rust-lang/rust-clippy/pull/15172) ### ICE Fixes @@ -6707,6 +6712,7 @@ Released 2018-09-13 [`check-inconsistent-struct-field-initializers`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-inconsistent-struct-field-initializers [`check-private-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-private-items [`cognitive-complexity-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cognitive-complexity-threshold +[`const-literal-digits-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#const-literal-digits-threshold [`disallowed-macros`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-macros [`disallowed-methods`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-methods [`disallowed-names`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-names diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 05590ff7b1c9..c2d080cd96a1 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -485,6 +485,16 @@ The maximum cognitive complexity a function can have * [`cognitive_complexity`](https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity) +## `const-literal-digits-threshold` +The minimum digits a const float literal must have to supress the `excessive_precicion` lint + +**Default Value:** `30` + +--- +**Affected lints:** +* [`excessive_precision`](https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision) + + ## `disallowed-macros` The list of disallowed macros, written as fully qualified paths. @@ -555,7 +565,7 @@ default configuration of Clippy. By default, any configuration will replace the * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`. * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list. -**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "MHz", "GHz", "THz", "AccessKit", "CoAP", "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "PowerPC", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "OpenType", "WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "NixOS", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` +**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "MHz", "GHz", "THz", "AccessKit", "CoAP", "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "InfiniBand", "RoCE", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "PowerPC", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "OpenType", "WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "NixOS", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` --- **Affected lints:** @@ -873,7 +883,6 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`needless_borrow`](https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow) * [`non_std_lazy_statics`](https://rust-lang.github.io/rust-clippy/master/index.html#non_std_lazy_statics) * [`option_as_ref_deref`](https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref) -* [`option_map_unwrap_or`](https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or) * [`ptr_as_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr) * [`question_mark`](https://rust-lang.github.io/rust-clippy/master/index.html#question_mark) * [`redundant_field_names`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names) @@ -881,7 +890,6 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`repeat_vec_with_capacity`](https://rust-lang.github.io/rust-clippy/master/index.html#repeat_vec_with_capacity) * [`same_item_push`](https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push) * [`seek_from_current`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current) -* [`seek_rewind`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_rewind) * [`to_digit_is_some`](https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some) * [`transmute_ptr_to_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref) * [`tuple_array_conversions`](https://rust-lang.github.io/rust-clippy/master/index.html#tuple_array_conversions) diff --git a/clippy.toml b/clippy.toml index 77573105d86a..d9bcfd17606e 100644 --- a/clippy.toml +++ b/clippy.toml @@ -6,12 +6,12 @@ lint-commented-code = true [[disallowed-methods]] path = "rustc_lint::context::LintContext::lint" -reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead" +reason = "this function does not add a link to our documentation; please use the `clippy_utils::diagnostics::span_lint*` functions instead" [[disallowed-methods]] path = "rustc_lint::context::LintContext::span_lint" -reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead" +reason = "this function does not add a link to our documentation; please use the `clippy_utils::diagnostics::span_lint*` functions instead" [[disallowed-methods]] path = "rustc_middle::ty::context::TyCtxt::node_span_lint" -reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead" +reason = "this function does not add a link to our documentation; please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead" diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 2ad3f2efcdd7..2f28f6175ad8 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -33,6 +33,7 @@ const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[ "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", + "InfiniBand", "RoCE", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "PowerPC", "WebAssembly", "NaN", "NaNs", @@ -569,6 +570,9 @@ define_Conf! { /// The maximum cognitive complexity a function can have #[lints(cognitive_complexity)] cognitive_complexity_threshold: u64 = 25, + /// The minimum digits a const float literal must have to supress the `excessive_precicion` lint + #[lints(excessive_precision)] + const_literal_digits_threshold: usize = 30, /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. /// /// Use the Cognitive Complexity lint instead. @@ -775,7 +779,6 @@ define_Conf! { needless_borrow, non_std_lazy_statics, option_as_ref_deref, - option_map_unwrap_or, ptr_as_ptr, question_mark, redundant_field_names, @@ -783,7 +786,6 @@ define_Conf! { repeat_vec_with_capacity, same_item_push, seek_from_current, - seek_rewind, to_digit_is_some, transmute_ptr_to_ref, tuple_array_conversions, diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index b9e6de79cc05..70184ee2ca59 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -27,6 +27,10 @@ url = "2.2" [dev-dependencies] walkdir = "2.3" +[lints.rust.unexpected_cfgs] +level = "warn" +check-cfg = ['cfg(bootstrap)'] + [package.metadata.rust-analyzer] # This crate uses #[feature(rustc_private)] rustc_private = true diff --git a/clippy_lints/src/assertions_on_result_states.rs b/clippy_lints/src/assertions_on_result_states.rs index 6f2a6a36a38b..08253b0c4995 100644 --- a/clippy_lints/src/assertions_on_result_states.rs +++ b/clippy_lints/src/assertions_on_result_states.rs @@ -3,10 +3,10 @@ use clippy_utils::macros::{PanicExpn, find_assert_args, root_macro_call_first_no use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{has_debug_impl, is_copy, is_type_diagnostic_item}; use clippy_utils::usage::local_used_after_expr; -use clippy_utils::{is_expr_final_block_expr, path_res, sym}; +use clippy_utils::{path_res, sym}; use rustc_errors::Applicability; use rustc_hir::def::Res; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{Expr, ExprKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::declare_lint_pass; @@ -77,17 +77,20 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates { _ => return, }; span_lint_and_then(cx, ASSERTIONS_ON_RESULT_STATES, macro_call.span, message, |diag| { - let semicolon = if is_expr_final_block_expr(cx.tcx, e) { ";" } else { "" }; let mut app = Applicability::MachineApplicable; - diag.span_suggestion( - macro_call.span, - "replace with", - format!( - "{}.{replacement}(){semicolon}", - snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0 - ), - app, - ); + let recv = snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0; + + // `assert!` doesn't return anything, but `Result::unwrap(_err)` does, so we might need to add a + // semicolon to the suggestion to avoid leaking the type + let sugg = match cx.tcx.parent_hir_node(e.hir_id) { + // trailing expr of a block + Node::Block(..) => format!("{recv}.{replacement}();"), + // already has a trailing semicolon + Node::Stmt(..) => format!("{recv}.{replacement}()"), + // this is the last-resort option, because it's rather verbose + _ => format!("{{ {recv}.{replacement}(); }}"), + }; + diag.span_suggestion(macro_call.span, "replace with", sugg, app); }); } } diff --git a/clippy_lints/src/async_yields_async.rs b/clippy_lints/src/async_yields_async.rs index 013819b0da8a..1a10db291cde 100644 --- a/clippy_lints/src/async_yields_async.rs +++ b/clippy_lints/src/async_yields_async.rs @@ -1,8 +1,12 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::source::snippet; +use clippy_utils::is_expr_async_block; +use clippy_utils::source::walk_span_to_context; +use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use rustc_errors::Applicability; -use rustc_hir::{Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, QPath}; +use rustc_hir::{ + Block, Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, QPath, +}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -87,31 +91,37 @@ impl<'tcx> LateLintPass<'tcx> for AsyncYieldsAsync { let expr_ty = typeck_results.expr_ty(body_expr); if implements_trait(cx, expr_ty, future_trait_def_id, &[]) { - let return_expr_span = match &body_expr.kind { - // XXXkhuey there has to be a better way. - ExprKind::Block(block, _) => block.expr.map(|e| e.span), - ExprKind::Path(QPath::Resolved(_, path)) => Some(path.span), - _ => None, + let (return_expr, return_expr_span) = match &body_expr.kind { + ExprKind::Block(Block { expr: Some(e), .. }, _) => (*e, e.span), + ExprKind::Path(QPath::Resolved(_, path)) => (body_expr, path.span), + _ => return, }; - if let Some(return_expr_span) = return_expr_span { - span_lint_hir_and_then( - cx, - ASYNC_YIELDS_ASYNC, - body_expr.hir_id, - return_expr_span, - "an async construct yields a type which is itself awaitable", - |db| { - db.span_label(body_expr.span, "outer async construct"); - db.span_label(return_expr_span, "awaitable value not awaited"); - db.span_suggestion( - return_expr_span, - "consider awaiting this value", - format!("{}.await", snippet(cx, return_expr_span, "..")), - Applicability::MaybeIncorrect, - ); - }, - ); + + let return_expr_span = walk_span_to_context(return_expr_span, expr.span.ctxt()).unwrap_or(return_expr_span); + let mut applicability = Applicability::MaybeIncorrect; + let mut return_expr_snip = + Sugg::hir_with_context(cx, return_expr, expr.span.ctxt(), "..", &mut applicability); + if !is_expr_async_block(return_expr) { + return_expr_snip = return_expr_snip.maybe_paren(); } + + span_lint_hir_and_then( + cx, + ASYNC_YIELDS_ASYNC, + body_expr.hir_id, + return_expr_span, + "an async construct yields a type which is itself awaitable", + |db| { + db.span_label(body_expr.span, "outer async construct"); + db.span_label(return_expr_span, "awaitable value not awaited"); + db.span_suggestion( + return_expr_span, + "consider awaiting this value", + format!("{return_expr_snip}.await"), + applicability, + ); + }, + ); } } } diff --git a/clippy_lints/src/bool_comparison.rs b/clippy_lints/src/bool_comparison.rs new file mode 100644 index 000000000000..722095909a6f --- /dev/null +++ b/clippy_lints/src/bool_comparison.rs @@ -0,0 +1,179 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::sugg::Sugg; +use clippy_utils::{is_expn_of, peel_blocks, sym}; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; +use rustc_span::Span; +use rustc_span::source_map::Spanned; + +declare_clippy_lint! { + /// ### What it does + /// Checks for expressions of the form `x == true`, + /// `x != true` and order comparisons such as `x < true` (or vice versa) and + /// suggest using the variable directly. + /// + /// ### Why is this bad? + /// Unnecessary code. + /// + /// ### Example + /// ```rust,ignore + /// if x == true {} + /// if y == false {} + /// ``` + /// use `x` directly: + /// ```rust,ignore + /// if x {} + /// if !y {} + /// ``` + #[clippy::version = "pre 1.29.0"] + pub BOOL_COMPARISON, + complexity, + "comparing a variable to a boolean, e.g., `if x == true` or `if x != true`" +} + +declare_lint_pass!(BoolComparison => [BOOL_COMPARISON]); + +impl<'tcx> LateLintPass<'tcx> for BoolComparison { + fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { + if e.span.from_expansion() { + return; + } + + if let ExprKind::Binary(Spanned { node, .. }, left_side, right_side) = e.kind + && is_expn_of(left_side.span, sym::cfg).is_none() + && is_expn_of(right_side.span, sym::cfg).is_none() + && cx.typeck_results().expr_ty(left_side).is_bool() + && cx.typeck_results().expr_ty(right_side).is_bool() + { + let ignore_case = None::<(fn(_) -> _, &str)>; + let ignore_no_literal = None::<(fn(_, _) -> _, &str)>; + match node { + BinOpKind::Eq => { + let true_case = Some((|h| h, "equality checks against true are unnecessary")); + let false_case = Some(( + |h: Sugg<'tcx>| !h, + "equality checks against false can be replaced by a negation", + )); + check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal); + }, + BinOpKind::Ne => { + let true_case = Some(( + |h: Sugg<'tcx>| !h, + "inequality checks against true can be replaced by a negation", + )); + let false_case = Some((|h| h, "inequality checks against false are unnecessary")); + check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal); + }, + BinOpKind::Lt => check_comparison( + cx, + e, + ignore_case, + Some((|h| h, "greater than checks against false are unnecessary")), + Some(( + |h: Sugg<'tcx>| !h, + "less than comparison against true can be replaced by a negation", + )), + ignore_case, + Some(( + |l: Sugg<'tcx>, r: Sugg<'tcx>| (!l).bit_and(&r), + "order comparisons between booleans can be simplified", + )), + ), + BinOpKind::Gt => check_comparison( + cx, + e, + Some(( + |h: Sugg<'tcx>| !h, + "less than comparison against true can be replaced by a negation", + )), + ignore_case, + ignore_case, + Some((|h| h, "greater than checks against false are unnecessary")), + Some(( + |l: Sugg<'tcx>, r: Sugg<'tcx>| l.bit_and(&(!r)), + "order comparisons between booleans can be simplified", + )), + ), + _ => (), + } + } + } +} + +fn check_comparison<'a, 'tcx>( + cx: &LateContext<'tcx>, + e: &'tcx Expr<'_>, + left_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>, + left_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>, + right_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>, + right_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>, + no_literal: Option<(impl FnOnce(Sugg<'a>, Sugg<'a>) -> Sugg<'a>, &'static str)>, +) { + if let ExprKind::Binary(_, left_side, right_side) = e.kind { + let mut applicability = Applicability::MachineApplicable; + // Eliminate parentheses in `e` by using the lo pos of lhs and hi pos of rhs, + // calling `source_callsite` make sure macros are handled correctly, see issue #9907 + let binop_span = left_side.span.source_callsite().to(right_side.span.source_callsite()); + + match (fetch_bool_expr(left_side), fetch_bool_expr(right_side)) { + (Some(true), None) => left_true.map_or((), |(h, m)| { + suggest_bool_comparison(cx, binop_span, right_side, applicability, m, h); + }), + (None, Some(true)) => right_true.map_or((), |(h, m)| { + suggest_bool_comparison(cx, binop_span, left_side, applicability, m, h); + }), + (Some(false), None) => left_false.map_or((), |(h, m)| { + suggest_bool_comparison(cx, binop_span, right_side, applicability, m, h); + }), + (None, Some(false)) => right_false.map_or((), |(h, m)| { + suggest_bool_comparison(cx, binop_span, left_side, applicability, m, h); + }), + (None, None) => no_literal.map_or((), |(h, m)| { + let left_side = Sugg::hir_with_context(cx, left_side, binop_span.ctxt(), "..", &mut applicability); + let right_side = Sugg::hir_with_context(cx, right_side, binop_span.ctxt(), "..", &mut applicability); + span_lint_and_sugg( + cx, + BOOL_COMPARISON, + binop_span, + m, + "try", + h(left_side, right_side).into_string(), + applicability, + ); + }), + _ => (), + } + } +} + +fn suggest_bool_comparison<'a, 'tcx>( + cx: &LateContext<'tcx>, + span: Span, + expr: &Expr<'_>, + mut app: Applicability, + message: &'static str, + conv_hint: impl FnOnce(Sugg<'a>) -> Sugg<'a>, +) { + let hint = Sugg::hir_with_context(cx, expr, span.ctxt(), "..", &mut app); + span_lint_and_sugg( + cx, + BOOL_COMPARISON, + span, + message, + "try", + conv_hint(hint).into_string(), + app, + ); +} + +fn fetch_bool_expr(expr: &Expr<'_>) -> Option { + if let ExprKind::Lit(lit_ptr) = peel_blocks(expr).kind + && let LitKind::Bool(value) = lit_ptr.node + { + return Some(value); + } + None +} diff --git a/clippy_lints/src/casts/cast_ptr_alignment.rs b/clippy_lints/src/casts/cast_ptr_alignment.rs index a1543cabd2f9..d78da9396faf 100644 --- a/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -8,17 +8,12 @@ use rustc_middle::ty::{self, Ty}; use super::CAST_PTR_ALIGNMENT; -pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { - if let ExprKind::Cast(cast_expr, cast_to) = expr.kind { - if is_hir_ty_cfg_dependant(cx, cast_to) { - return; - } - let (cast_from, cast_to) = ( - cx.typeck_results().expr_ty(cast_expr), - cx.typeck_results().expr_ty(expr), - ); - lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); - } else if let ExprKind::MethodCall(method_path, self_arg, [], _) = &expr.kind +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_from: Ty<'tcx>, cast_to: Ty<'tcx>) { + lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); +} + +pub(super) fn check_cast_method(cx: &LateContext<'_>, expr: &Expr<'_>) { + if let ExprKind::MethodCall(method_path, self_arg, [], _) = &expr.kind && method_path.ident.name == sym::cast && let Some(generic_args) = method_path.args && let [GenericArg::Type(cast_to)] = generic_args.args @@ -74,14 +69,13 @@ fn is_used_as_unaligned(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { ExprKind::Call(func, [arg, ..]) if arg.hir_id == e.hir_id => { if let ExprKind::Path(path) = &func.kind && let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id() + && let Some(name) = cx.tcx.get_diagnostic_name(def_id) && matches!( - cx.tcx.get_diagnostic_name(def_id), - Some( - sym::ptr_write_unaligned - | sym::ptr_read_unaligned - | sym::intrinsics_unaligned_volatile_load - | sym::intrinsics_unaligned_volatile_store - ) + name, + sym::ptr_write_unaligned + | sym::ptr_read_unaligned + | sym::intrinsics_unaligned_volatile_load + | sym::intrinsics_unaligned_volatile_store ) { true diff --git a/clippy_lints/src/casts/cast_slice_from_raw_parts.rs b/clippy_lints/src/casts/cast_slice_from_raw_parts.rs index 46b0c88d3fed..bce7b4c69cc6 100644 --- a/clippy_lints/src/casts/cast_slice_from_raw_parts.rs +++ b/clippy_lints/src/casts/cast_slice_from_raw_parts.rs @@ -1,10 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_context; +use clippy_utils::{get_parent_expr, is_no_std_crate}; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::{self, Ty}; use rustc_span::sym; @@ -42,13 +44,52 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, let mut applicability = Applicability::MachineApplicable; let ptr = snippet_with_context(cx, ptr_arg.span, ctxt, "ptr", &mut applicability).0; let len = snippet_with_context(cx, len_arg.span, ctxt, "len", &mut applicability).0; + let krate = if is_no_std_crate(cx) { "core" } else { "std" }; span_lint_and_sugg( cx, CAST_SLICE_FROM_RAW_PARTS, span, format!("casting the result of `{func}` to {cast_to}"), "replace with", - format!("core::ptr::slice_{func}({ptr}, {len})"), + format!("{krate}::ptr::slice_{func}({ptr}, {len})"), + applicability, + ); + } +} + +/// Checks for implicit cast from slice reference to raw slice pointer. +pub(super) fn check_implicit_cast(cx: &LateContext<'_>, expr: &Expr<'_>) { + if let ExprKind::Call(fun, [ptr_arg, len_arg]) = expr.peel_blocks().kind + && let ExprKind::Path(ref qpath) = fun.kind + && let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id() + && let Some(rpk) = raw_parts_kind(cx, fun_def_id) + && !matches!(get_parent_expr(cx, expr).map(|e| e.kind), Some(ExprKind::Cast(..))) + && let [deref, borrow] = cx.typeck_results().expr_adjustments(expr) + && matches!(deref.kind, Adjust::Deref(..)) + && let Adjustment { + kind: Adjust::Borrow(AutoBorrow::RawPtr(..)), + target, + } = borrow + && let ty::RawPtr(pointee_ty, _) = target.kind() + && pointee_ty.is_slice() + && !expr.span.from_expansion() + { + let func = match rpk { + RawPartsKind::Immutable => "from_raw_parts", + RawPartsKind::Mutable => "from_raw_parts_mut", + }; + let mut applicability = Applicability::MachineApplicable; + let ctxt = expr.span.ctxt(); + let ptr = snippet_with_context(cx, ptr_arg.span, ctxt, "ptr", &mut applicability).0; + let len = snippet_with_context(cx, len_arg.span, ctxt, "len", &mut applicability).0; + let krate = if is_no_std_crate(cx) { "core" } else { "std" }; + span_lint_and_sugg( + cx, + CAST_SLICE_FROM_RAW_PARTS, + expr.span, + format!("implicitly casting the result of `{func}` to `{target}`"), + "replace_with", + format!("{krate}::ptr::slice_{func}({ptr}, {len})"), applicability, ); } diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index e25df9dd249a..d2e62ee56e43 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -873,7 +873,9 @@ impl<'tcx> LateLintPass<'tcx> for Casts { } char_lit_as_u8::check(cx, expr, cast_from_expr, cast_to); cast_slice_from_raw_parts::check(cx, expr, cast_from_expr, cast_to, self.msrv); + cast_ptr_alignment::check(cx, expr, cast_from, cast_to); ptr_cast_constness::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv); + ptr_as_ptr::check(cx, expr, cast_from_expr, cast_from, cast_to_hir, cast_to, self.msrv); as_ptr_cast_mut::check(cx, expr, cast_from_expr, cast_to); fn_to_numeric_cast_any::check(cx, expr, cast_from_expr, cast_from, cast_to); confusing_method_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to); @@ -911,8 +913,10 @@ impl<'tcx> LateLintPass<'tcx> for Casts { if self.msrv.meets(cx, msrvs::RAW_REF_OP) { borrow_as_ptr::check_implicit_cast(cx, expr); } - cast_ptr_alignment::check(cx, expr); - ptr_as_ptr::check(cx, expr, self.msrv); + if self.msrv.meets(cx, msrvs::PTR_SLICE_RAW_PARTS) { + cast_slice_from_raw_parts::check_implicit_cast(cx, expr); + } + cast_ptr_alignment::check_cast_method(cx, expr); cast_slice_different_sizes::check(cx, expr, self.msrv); ptr_cast_constness::check_null_ptr_cast_method(cx, expr); } diff --git a/clippy_lints/src/casts/ptr_as_ptr.rs b/clippy_lints/src/casts/ptr_as_ptr.rs index 890754090989..d012380cb762 100644 --- a/clippy_lints/src/casts/ptr_as_ptr.rs +++ b/clippy_lints/src/casts/ptr_as_ptr.rs @@ -4,9 +4,9 @@ use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, Mutability, QPath, TyKind}; +use rustc_hir::{self as hir, Expr, ExprKind, QPath, TyKind}; use rustc_lint::LateContext; -use rustc_middle::ty; +use rustc_middle::ty::{self, Ty}; use rustc_span::{Span, sym}; use super::PTR_AS_PTR; @@ -26,13 +26,18 @@ impl OmitFollowedCastReason<'_> { } } -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Msrv) { - if let ExprKind::Cast(cast_expr, cast_to_hir_ty) = expr.kind - && let (cast_from, cast_to) = (cx.typeck_results().expr_ty(cast_expr), cx.typeck_results().expr_ty(expr)) - && let ty::RawPtr(_, from_mutbl) = cast_from.kind() +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &Expr<'tcx>, + cast_from_expr: &Expr<'_>, + cast_from: Ty<'_>, + cast_to_hir: &hir::Ty<'_>, + cast_to: Ty<'tcx>, + msrv: Msrv, +) { + if let ty::RawPtr(_, from_mutbl) = cast_from.kind() && let ty::RawPtr(to_pointee_ty, to_mutbl) = cast_to.kind() - && matches!((from_mutbl, to_mutbl), - (Mutability::Not, Mutability::Not) | (Mutability::Mut, Mutability::Mut)) + && from_mutbl == to_mutbl // The `U` in `pointer::cast` have to be `Sized` // as explained here: https://github.com/rust-lang/rust/issues/60602. && to_pointee_ty.is_sized(cx.tcx, cx.typing_env()) @@ -40,7 +45,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Msrv) && !is_from_proc_macro(cx, expr) { let mut app = Applicability::MachineApplicable; - let turbofish = match &cast_to_hir_ty.kind { + let turbofish = match &cast_to_hir.kind { TyKind::Infer(()) => String::new(), TyKind::Ptr(mut_ty) => { if matches!(mut_ty.ty.kind, TyKind::Infer(())) { @@ -58,16 +63,14 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Msrv) // following `cast` does not compile because it fails to infer what type is expected // as type argument to `std::ptr::ptr_null` or `std::ptr::ptr_null_mut`, so // we omit following `cast`: - let omit_cast = if let ExprKind::Call(func, []) = cast_expr.kind + let omit_cast = if let ExprKind::Call(func, []) = cast_from_expr.kind && let ExprKind::Path(ref qpath @ QPath::Resolved(None, path)) = func.kind && let Some(method_defid) = path.res.opt_def_id() { - if cx.tcx.is_diagnostic_item(sym::ptr_null, method_defid) { - OmitFollowedCastReason::Null(qpath) - } else if cx.tcx.is_diagnostic_item(sym::ptr_null_mut, method_defid) { - OmitFollowedCastReason::NullMut(qpath) - } else { - OmitFollowedCastReason::None + match cx.tcx.get_diagnostic_name(method_defid) { + Some(sym::ptr_null) => OmitFollowedCastReason::Null(qpath), + Some(sym::ptr_null_mut) => OmitFollowedCastReason::NullMut(qpath), + _ => OmitFollowedCastReason::None, } } else { OmitFollowedCastReason::None @@ -78,7 +81,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Msrv) let method = snippet_with_applicability(cx, qpath_span_without_turbofish(method), "..", &mut app); ("try call directly", format!("{method}{turbofish}()")) } else { - let cast_expr_sugg = Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app); + let cast_expr_sugg = Sugg::hir_with_context(cx, cast_from_expr, expr.span.ctxt(), "_", &mut app); ( "try `pointer::cast`, a safer alternative", diff --git a/clippy_lints/src/casts/ptr_cast_constness.rs b/clippy_lints/src/casts/ptr_cast_constness.rs index c0c0a47f8551..c0f13f5e5782 100644 --- a/clippy_lints/src/casts/ptr_cast_constness.rs +++ b/clippy_lints/src/casts/ptr_cast_constness.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::{std_or_core, sym}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, Mutability, QPath}; +use rustc_hir::{self as hir, Expr, ExprKind, QPath}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; @@ -12,26 +13,23 @@ use super::PTR_CAST_CONSTNESS; pub(super) fn check<'tcx>( cx: &LateContext<'_>, expr: &Expr<'_>, - cast_expr: &Expr<'_>, + cast_from_expr: &Expr<'_>, cast_from: Ty<'tcx>, cast_to: Ty<'tcx>, msrv: Msrv, ) { if let ty::RawPtr(from_ty, from_mutbl) = cast_from.kind() && let ty::RawPtr(to_ty, to_mutbl) = cast_to.kind() - && matches!( - (from_mutbl, to_mutbl), - (Mutability::Not, Mutability::Mut) | (Mutability::Mut, Mutability::Not) - ) + && from_mutbl != to_mutbl && from_ty == to_ty && !from_ty.has_erased_regions() { - if let ExprKind::Call(func, []) = cast_expr.kind + if let ExprKind::Call(func, []) = cast_from_expr.kind && let ExprKind::Path(QPath::Resolved(None, path)) = func.kind && let Some(defid) = path.res.opt_def_id() && let Some(prefix) = std_or_core(cx) && let mut app = Applicability::MachineApplicable - && let sugg = format!("{}", Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app)) + && let sugg = snippet_with_applicability(cx, cast_from_expr.span, "_", &mut app) && let Some((_, after_lt)) = sugg.split_once("::<") && let Some((source, target, target_func)) = match cx.tcx.get_diagnostic_name(defid) { Some(sym::ptr_null) => Some(("const", "mutable", "null_mut")), @@ -53,11 +51,17 @@ pub(super) fn check<'tcx>( if msrv.meets(cx, msrvs::POINTER_CAST_CONSTNESS) { let mut app = Applicability::MachineApplicable; - let sugg = Sugg::hir_with_context(cx, cast_expr, expr.span.ctxt(), "_", &mut app); - let constness = match *to_mutbl { - Mutability::Not => "const", - Mutability::Mut => "mut", + let sugg = if let ExprKind::Cast(nested_from, nested_hir_ty) = cast_from_expr.kind + && let hir::TyKind::Ptr(ptr_ty) = nested_hir_ty.kind + && let hir::TyKind::Infer(()) = ptr_ty.ty.kind + { + // `(foo as *const _).cast_mut()` fails method name resolution + // avoid this by `as`-ing the full type + Sugg::hir_with_context(cx, nested_from, expr.span.ctxt(), "_", &mut app).as_ty(cast_from) + } else { + Sugg::hir_with_context(cx, cast_from_expr, expr.span.ctxt(), "_", &mut app) }; + let constness = to_mutbl.ptr_str(); span_lint_and_sugg( cx, @@ -73,8 +77,8 @@ pub(super) fn check<'tcx>( } pub(super) fn check_null_ptr_cast_method(cx: &LateContext<'_>, expr: &Expr<'_>) { - if let ExprKind::MethodCall(method, cast_expr, [], _) = expr.kind - && let ExprKind::Call(func, []) = cast_expr.kind + if let ExprKind::MethodCall(method, cast_from_expr, [], _) = expr.kind + && let ExprKind::Call(func, []) = cast_from_expr.kind && let ExprKind::Path(QPath::Resolved(None, path)) = func.kind && let Some(defid) = path.res.opt_def_id() && let method = match (cx.tcx.get_diagnostic_name(defid), method.ident.name) { @@ -84,7 +88,7 @@ pub(super) fn check_null_ptr_cast_method(cx: &LateContext<'_>, expr: &Expr<'_>) } && let Some(prefix) = std_or_core(cx) && let mut app = Applicability::MachineApplicable - && let sugg = format!("{}", Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app)) + && let sugg = snippet_with_applicability(cx, cast_from_expr.span, "_", &mut app) && let Some((_, after_lt)) = sugg.split_once("::<") { span_lint_and_sugg( diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 8f95c63a854f..7646aa48b772 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -23,8 +23,8 @@ declare_clippy_lint! { /// /// ### Known problems /// The true Cognitive Complexity of a method is not something we can - /// calculate using modern technology. This lint has been left in the - /// `nursery` so as to not mislead users into using this lint as a + /// calculate using modern technology. This lint has been left in + /// `restriction` so as to not mislead users into using this lint as a /// measurement tool. /// /// For more detailed information, see [rust-clippy#3793](https://github.com/rust-lang/rust-clippy/issues/3793) diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index e67e8d9070f2..d0c7443a4a4b 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -35,6 +35,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::await_holding_invalid::AWAIT_HOLDING_REFCELL_REF_INFO, crate::blocks_in_conditions::BLOCKS_IN_CONDITIONS_INFO, crate::bool_assert_comparison::BOOL_ASSERT_COMPARISON_INFO, + crate::bool_comparison::BOOL_COMPARISON_INFO, crate::bool_to_int_with_if::BOOL_TO_INT_WITH_IF_INFO, crate::booleans::NONMINIMAL_BOOL_INFO, crate::booleans::OVERLY_COMPLEX_BOOL_EXPR_INFO, @@ -538,7 +539,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::mutex_atomic::MUTEX_ATOMIC_INFO, crate::mutex_atomic::MUTEX_INTEGER_INFO, crate::needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE_INFO, - crate::needless_bool::BOOL_COMPARISON_INFO, crate::needless_bool::NEEDLESS_BOOL_INFO, crate::needless_bool::NEEDLESS_BOOL_ASSIGN_INFO, crate::needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE_INFO, diff --git a/clippy_lints/src/derivable_impls.rs b/clippy_lints/src/derivable_impls.rs index 7580d6cab66d..06c2393e0a39 100644 --- a/clippy_lints/src/derivable_impls.rs +++ b/clippy_lints/src/derivable_impls.rs @@ -10,7 +10,7 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, PointerCoercion}; -use rustc_middle::ty::{self, AdtDef, GenericArgsRef, Ty, TypeckResults}; +use rustc_middle::ty::{self, AdtDef, GenericArgsRef, Ty, TypeckResults, VariantDef}; use rustc_session::impl_lint_pass; use rustc_span::sym; @@ -85,6 +85,13 @@ fn contains_trait_object(ty: Ty<'_>) -> bool { } } +fn determine_derive_macro(cx: &LateContext<'_>, is_const: bool) -> Option<&'static str> { + (!is_const) + .then_some("derive") + .or_else(|| cx.tcx.features().enabled(sym::derive_const).then_some("derive_const")) +} + +#[expect(clippy::too_many_arguments)] fn check_struct<'tcx>( cx: &LateContext<'tcx>, item: &'tcx Item<'_>, @@ -93,6 +100,7 @@ fn check_struct<'tcx>( adt_def: AdtDef<'_>, ty_args: GenericArgsRef<'_>, typeck_results: &'tcx TypeckResults<'tcx>, + is_const: bool, ) { if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind && let Some(PathSegment { args, .. }) = p.segments.last() @@ -125,14 +133,18 @@ fn check_struct<'tcx>( ExprKind::Tup(fields) => fields.iter().all(is_default_without_adjusts), ExprKind::Call(callee, args) if is_path_self(callee) => args.iter().all(is_default_without_adjusts), ExprKind::Struct(_, fields, _) => fields.iter().all(|ef| is_default_without_adjusts(ef.expr)), - _ => false, + _ => return, }; - if should_emit { + if should_emit && let Some(derive_snippet) = determine_derive_macro(cx, is_const) { let struct_span = cx.tcx.def_span(adt_def.did()); + let indent_enum = indent_of(cx, struct_span).unwrap_or(0); let suggestions = vec![ (item.span, String::new()), // Remove the manual implementation - (struct_span.shrink_to_lo(), "#[derive(Default)]\n".to_string()), // Add the derive attribute + ( + struct_span.shrink_to_lo(), + format!("#[{derive_snippet}(Default)]\n{}", " ".repeat(indent_enum)), + ), // Add the derive attribute ]; span_lint_and_then(cx, DERIVABLE_IMPLS, item.span, "this `impl` can be derived", |diag| { @@ -145,11 +157,41 @@ fn check_struct<'tcx>( } } -fn check_enum<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>, func_expr: &Expr<'_>, adt_def: AdtDef<'_>) { - if let ExprKind::Path(QPath::Resolved(None, p)) = &peel_blocks(func_expr).kind - && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res - && let variant_id = cx.tcx.parent(id) - && let Some(variant_def) = adt_def.variants().iter().find(|v| v.def_id == variant_id) +fn extract_enum_variant<'tcx>( + cx: &LateContext<'tcx>, + func_expr: &'tcx Expr<'tcx>, + adt_def: AdtDef<'tcx>, +) -> Option<&'tcx VariantDef> { + match &peel_blocks(func_expr).kind { + ExprKind::Path(QPath::Resolved(None, p)) + if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res + && let variant_id = cx.tcx.parent(id) + && let Some(variant_def) = adt_def.variants().iter().find(|v| v.def_id == variant_id) => + { + Some(variant_def) + }, + ExprKind::Path(QPath::TypeRelative(ty, segment)) + if let TyKind::Path(QPath::Resolved(None, p)) = &ty.kind + && let Res::SelfTyAlias { + is_trait_impl: true, .. + } = p.res + && let variant_ident = segment.ident + && let Some(variant_def) = adt_def.variants().iter().find(|v| v.ident(cx.tcx) == variant_ident) => + { + Some(variant_def) + }, + _ => None, + } +} + +fn check_enum<'tcx>( + cx: &LateContext<'tcx>, + item: &'tcx Item<'tcx>, + func_expr: &'tcx Expr<'tcx>, + adt_def: AdtDef<'tcx>, + is_const: bool, +) { + if let Some(variant_def) = extract_enum_variant(cx, func_expr, adt_def) && variant_def.fields.is_empty() && !variant_def.is_field_list_non_exhaustive() { @@ -158,11 +200,15 @@ fn check_enum<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>, func_expr: &Ex let variant_span = cx.tcx.def_span(variant_def.def_id); let indent_variant = indent_of(cx, variant_span).unwrap_or(0); + let Some(derive_snippet) = determine_derive_macro(cx, is_const) else { + return; + }; + let suggestions = vec![ (item.span, String::new()), // Remove the manual implementation ( enum_span.shrink_to_lo(), - format!("#[derive(Default)]\n{}", " ".repeat(indent_enum)), + format!("#[{derive_snippet}(Default)]\n{}", " ".repeat(indent_enum)), ), // Add the derive attribute ( variant_span.shrink_to_lo(), @@ -201,10 +247,20 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls { && !attrs.iter().any(|attr| attr.doc_str().is_some()) && cx.tcx.hir_attrs(impl_item_hir).is_empty() { + let is_const = of_trait.constness == hir::Constness::Const; if adt_def.is_struct() { - check_struct(cx, item, self_ty, func_expr, adt_def, args, cx.tcx.typeck_body(*b)); + check_struct( + cx, + item, + self_ty, + func_expr, + adt_def, + args, + cx.tcx.typeck_body(*b), + is_const, + ); } else if adt_def.is_enum() && self.msrv.meets(cx, msrvs::DEFAULT_ENUM_ATTRIBUTE) { - check_enum(cx, item, func_expr, adt_def); + check_enum(cx, item, func_expr, adt_def, is_const); } } } diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index 182cb4e46d2b..a5ec6777b434 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context}; use clippy_utils::ty::is_copy; use clippy_utils::visitors::for_each_expr; @@ -194,7 +194,17 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass { if let Some(sugg) = sugg { span_lint_and_sugg(cx, MAP_ENTRY, expr.span, lint_msg, "try", sugg, app); } else { - span_lint(cx, MAP_ENTRY, expr.span, lint_msg); + span_lint_and_help( + cx, + MAP_ENTRY, + expr.span, + lint_msg, + None, + format!( + "consider using the `Entry` API: https://doc.rust-lang.org/std/collections/struct.{}.html#entry-api", + map_ty.name() + ), + ); } } } @@ -254,35 +264,28 @@ fn try_parse_contains<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Optio _ => None, }); - match expr.kind { - ExprKind::MethodCall( - _, + if let ExprKind::MethodCall(_, map, [arg], _) = expr.kind + && let Expr { + kind: ExprKind::AddrOf(_, _, key), + span: key_span, + .. + } = arg + && key_span.eq_ctxt(expr.span) + { + let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?; + let expr = ContainsExpr { + negated, map, - [ - Expr { - kind: ExprKind::AddrOf(_, _, key), - span: key_span, - .. - }, - ], - _, - ) if key_span.eq_ctxt(expr.span) => { - let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?; - let expr = ContainsExpr { - negated, - map, - key, - call_ctxt: expr.span.ctxt(), - }; - if cx.tcx.is_diagnostic_item(sym::btreemap_contains_key, id) { - Some((MapType::BTree, expr)) - } else if cx.tcx.is_diagnostic_item(sym::hashmap_contains_key, id) { - Some((MapType::Hash, expr)) - } else { - None - } - }, - _ => None, + key, + call_ctxt: expr.span.ctxt(), + }; + match cx.tcx.get_diagnostic_name(id) { + Some(sym::btreemap_contains_key) => Some((MapType::BTree, expr)), + Some(sym::hashmap_contains_key) => Some((MapType::Hash, expr)), + _ => None, + } + } else { + None } } @@ -311,7 +314,9 @@ struct InsertExpr<'tcx> { fn try_parse_insert<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option> { if let ExprKind::MethodCall(_, map, [key, value], _) = expr.kind { let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?; - if cx.tcx.is_diagnostic_item(sym::btreemap_insert, id) || cx.tcx.is_diagnostic_item(sym::hashmap_insert, id) { + if let Some(insert) = cx.tcx.get_diagnostic_name(id) + && matches!(insert, sym::btreemap_insert | sym::hashmap_insert) + { Some(InsertExpr { map, key, value }) } else { None diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 0eefc2f61096..2da1c2bad117 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -12,6 +12,7 @@ use rustc_hir::attrs::AttributeKind; use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, GenericArgs, Param, PatKind, QPath, Safety, TyKind, find_attr}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::{ self, Binder, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, Ty, TypeVisitableExt, TypeckResults, }; @@ -148,10 +149,9 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx { return; } - let callee_ty_adjusted = typeck - .expr_adjustments(callee) - .last() - .map_or(callee_ty, |a| a.target.peel_refs()); + + let callee_ty_adjustments = typeck.expr_adjustments(callee); + let callee_ty_adjusted = callee_ty_adjustments.last().map_or(callee_ty, |a| a.target); let sig = match callee_ty_adjusted.kind() { ty::FnDef(def, _) => { @@ -230,7 +230,20 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx }, _ => (), } + } else if let n_refs = + callee_ty_adjustments + .iter() + .rev() + .fold(0, |acc, adjustment| match adjustment.kind { + Adjust::Deref(Some(_)) => acc + 1, + Adjust::Deref(_) if acc > 0 => acc + 1, + _ => acc, + }) + && n_refs > 0 + { + snippet = format!("{}{snippet}", "*".repeat(n_refs)); } + let replace_with = match callee_ty_adjusted.kind() { ty::FnDef(def, _) => cx.tcx.def_descr(*def), _ => "function", diff --git a/clippy_lints/src/float_literal.rs b/clippy_lints/src/float_literal.rs index ccaf38aee4d8..6178addfff12 100644 --- a/clippy_lints/src/float_literal.rs +++ b/clippy_lints/src/float_literal.rs @@ -1,11 +1,12 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::numeric_literal; +use clippy_utils::{ExprUseNode, expr_use_ctxt, numeric_literal}; use rustc_ast::ast::{LitFloatType, LitKind}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, FloatTy}; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; use std::fmt; declare_clippy_lint! { @@ -13,6 +14,8 @@ declare_clippy_lint! { /// Checks for float literals with a precision greater /// than that supported by the underlying type. /// + /// The lint is suppressed for literals with over `const_literal_digits_threshold` digits. + /// /// ### Why is this bad? /// Rust will truncate the literal silently. /// @@ -58,7 +61,21 @@ declare_clippy_lint! { "lossy whole number float literals" } -declare_lint_pass!(FloatLiteral => [EXCESSIVE_PRECISION, LOSSY_FLOAT_LITERAL]); +pub struct FloatLiteral { + const_literal_digits_threshold: usize, +} + +impl_lint_pass!(FloatLiteral => [ + EXCESSIVE_PRECISION, LOSSY_FLOAT_LITERAL +]); + +impl FloatLiteral { + pub fn new(conf: &'static Conf) -> Self { + Self { + const_literal_digits_threshold: conf.const_literal_digits_threshold, + } + } +} impl<'tcx> LateLintPass<'tcx> for FloatLiteral { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { @@ -126,13 +143,25 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral { }, ); } - } else if digits > max as usize && count_digits(&float_str) < count_digits(sym_str) { + } else if digits > max as usize && count_digits(&float_str) < digits { + if digits >= self.const_literal_digits_threshold + && matches!(expr_use_ctxt(cx, expr).use_node(cx), ExprUseNode::ConstStatic(_)) + { + // If a big enough number of digits is specified and it's a constant + // we assume the user is definining a constant, and excessive precision is ok + return; + } span_lint_and_then( cx, EXCESSIVE_PRECISION, expr.span, "float has excessive precision", |diag| { + if digits >= self.const_literal_digits_threshold + && let Some(let_stmt) = maybe_let_stmt(cx, expr) + { + diag.span_note(let_stmt.span, "consider making it a `const` item"); + } diag.span_suggestion_verbose( expr.span, "consider changing the type or truncating it to", @@ -196,3 +225,11 @@ impl FloatFormat { } } } + +fn maybe_let_stmt<'a>(cx: &LateContext<'a>, expr: &hir::Expr<'_>) -> Option<&'a hir::LetStmt<'a>> { + let parent = cx.tcx.parent_hir_node(expr.hir_id); + match parent { + hir::Node::LetStmt(let_stmt) => Some(let_stmt), + _ => None, + } +} diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index ca5ea9018149..5a40af421942 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -535,7 +535,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions { def_id: LocalDefId, ) { let hir_id = cx.tcx.local_def_id_to_hir_id(def_id); - too_many_arguments::check_fn(cx, kind, decl, span, hir_id, self.too_many_arguments_threshold); + too_many_arguments::check_fn(cx, kind, decl, hir_id, def_id, self.too_many_arguments_threshold); too_many_lines::check_fn(cx, kind, body, span, def_id, self.too_many_lines_threshold); not_unsafe_ptr_arg_deref::check_fn(cx, kind, decl, body, def_id); misnamed_getters::check_fn(cx, kind, decl, body, span); diff --git a/clippy_lints/src/functions/too_many_arguments.rs b/clippy_lints/src/functions/too_many_arguments.rs index 48d050aa36aa..6c3c3d354ecc 100644 --- a/clippy_lints/src/functions/too_many_arguments.rs +++ b/clippy_lints/src/functions/too_many_arguments.rs @@ -1,5 +1,7 @@ use rustc_abi::ExternAbi; -use rustc_hir::{self as hir, intravisit}; +use rustc_hir as hir; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::intravisit::FnKind; use rustc_lint::LateContext; use rustc_span::Span; @@ -10,39 +12,18 @@ use super::TOO_MANY_ARGUMENTS; pub(super) fn check_fn( cx: &LateContext<'_>, - kind: intravisit::FnKind<'_>, + kind: FnKind<'_>, decl: &hir::FnDecl<'_>, - span: Span, hir_id: hir::HirId, + def_id: LocalDefId, too_many_arguments_threshold: u64, ) { // don't warn for implementations, it's not their fault - if !is_trait_impl_item(cx, hir_id) { + if !is_trait_impl_item(cx, hir_id) // don't lint extern functions decls, it's not their fault either - match kind { - intravisit::FnKind::Method( - _, - &hir::FnSig { - header: hir::FnHeader { - abi: ExternAbi::Rust, .. - }, - .. - }, - ) - | intravisit::FnKind::ItemFn( - _, - _, - hir::FnHeader { - abi: ExternAbi::Rust, .. - }, - ) => check_arg_number( - cx, - decl, - span.with_hi(decl.output.span().hi()), - too_many_arguments_threshold, - ), - _ => {}, - } + && kind.header().is_some_and(|header| header.abi == ExternAbi::Rust) + { + check_arg_number(cx, decl, cx.tcx.def_span(def_id), too_many_arguments_threshold); } } diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index 7158f9419c1c..b50d91f10146 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -2,16 +2,16 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::snippet_with_context; +use clippy_utils::source::{snippet_with_applicability, snippet_with_context, walk_span_to_context}; use clippy_utils::sugg::Sugg; use clippy_utils::{ contains_return, expr_adjustment_requires_coercion, higher, is_else_clause, is_in_const_context, is_res_lang_ctor, - path_res, peel_blocks, + path_res, peel_blocks, sym, }; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; declare_clippy_lint! { @@ -71,21 +71,21 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { && let ExprKind::Block(then_block, _) = then.kind && let Some(then_expr) = then_block.expr && let ExprKind::Call(then_call, [then_arg]) = then_expr.kind - && let ctxt = expr.span.ctxt() - && then_expr.span.ctxt() == ctxt + && !expr.span.from_expansion() + && !then_expr.span.from_expansion() && is_res_lang_ctor(cx, path_res(cx, then_call), OptionSome) && is_res_lang_ctor(cx, path_res(cx, peel_blocks(els)), OptionNone) && !is_else_clause(cx.tcx, expr) && !is_in_const_context(cx) - && !expr.span.in_external_macro(cx.sess().source_map()) && self.msrv.meets(cx, msrvs::BOOL_THEN) && !contains_return(then_block.stmts) { let method_name = if switch_to_eager_eval(cx, expr) && self.msrv.meets(cx, msrvs::BOOL_THEN_SOME) { - "then_some" + sym::then_some } else { - "then" + sym::then }; + let ctxt = expr.span.ctxt(); span_lint_and_then( cx, @@ -98,16 +98,18 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { } let mut app = Applicability::MachineApplicable; - let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app) + let cond_snip = Sugg::hir_with_context(cx, cond, ctxt, "[condition]", &mut app) .maybe_paren() .to_string(); let arg_snip = snippet_with_context(cx, then_arg.span, ctxt, "[body]", &mut app).0; - let method_body = if let Some(first_stmt) = then_block.stmts.first() { - let (block_snippet, _) = - snippet_with_context(cx, first_stmt.span.until(then_arg.span), ctxt, "..", &mut app); - let closure = if method_name == "then" { "|| " } else { "" }; - format!("{closure} {{ {block_snippet}; {arg_snip} }}") - } else if method_name == "then" { + let method_body = if let Some(first_stmt) = then_block.stmts.first() + && let Some(first_stmt_span) = walk_span_to_context(first_stmt.span, ctxt) + { + let block_snippet = + snippet_with_applicability(cx, first_stmt_span.until(then_expr.span), "..", &mut app); + let closure = if method_name == sym::then { "|| " } else { "" }; + format!("{closure} {{ {} {arg_snip} }}", block_snippet.trim_end()) + } else if method_name == sym::then { (std::borrow::Cow::Borrowed("|| ") + arg_snip).into_owned() } else { arg_snip.into_owned() diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs index 91f65d0b79ca..13117f60abd5 100644 --- a/clippy_lints/src/instant_subtraction.rs +++ b/clippy_lints/src/instant_subtraction.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_context; use clippy_utils::sugg::Sugg; -use clippy_utils::ty; +use clippy_utils::{is_path_diagnostic_item, ty}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -107,8 +107,7 @@ impl LateLintPass<'_> for InstantSubtraction { fn is_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool { if let ExprKind::Call(fn_expr, []) = expr_block.kind - && let Some(fn_id) = clippy_utils::path_def_id(cx, fn_expr) - && cx.tcx.is_diagnostic_item(sym::instant_now, fn_id) + && is_path_diagnostic_item(cx, fn_expr, sym::instant_now) { true } else { diff --git a/clippy_lints/src/large_include_file.rs b/clippy_lints/src/large_include_file.rs index 8707612fbdd0..48ce1afc6e69 100644 --- a/clippy_lints/src/large_include_file.rs +++ b/clippy_lints/src/large_include_file.rs @@ -64,8 +64,8 @@ impl LateLintPass<'_> for LargeIncludeFile { } && len as u64 > self.max_file_size && let Some(macro_call) = root_macro_call_first_node(cx, expr) - && (cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id) - || cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id)) + && let Some(macro_name) = cx.tcx.get_diagnostic_name(macro_call.def_id) + && matches!(macro_name, sym::include_bytes_macro | sym::include_str_macro) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then( diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 57deb011f2b0..f44a5fdf715e 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -355,12 +355,15 @@ fn parse_len_output<'tcx>(cx: &LateContext<'tcx>, sig: FnSig<'tcx>) -> Option Some(LenOutput::Option(def_id)), + Some(sym::Result) => Some(LenOutput::Result(def_id)), + _ => None, } + && is_first_generic_integral(segment) + { + return Some(res); } return None; @@ -368,11 +371,10 @@ fn parse_len_output<'tcx>(cx: &LateContext<'tcx>, sig: FnSig<'tcx>) -> Option Some(LenOutput::Integral), - ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) => { - subs.type_at(0).is_integral().then(|| LenOutput::Option(adt.did())) - }, - ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) => { - subs.type_at(0).is_integral().then(|| LenOutput::Result(adt.did())) + ty::Adt(adt, subs) if subs.type_at(0).is_integral() => match cx.tcx.get_diagnostic_name(adt.did()) { + Some(sym::Option) => Some(LenOutput::Option(adt.did())), + Some(sym::Result) => Some(LenOutput::Result(adt.did())), + _ => None, }, _ => None, } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c8594cf35e23..a89cf3fdc1ee 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -84,6 +84,7 @@ mod attrs; mod await_holding_invalid; mod blocks_in_conditions; mod bool_assert_comparison; +mod bool_comparison; mod bool_to_int_with_if; mod booleans; mod borrow_deref_ref; @@ -474,10 +475,10 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(move |_| Box::new(types::Types::new(conf))); store.register_late_pass(move |_| Box::new(booleans::NonminimalBool::new(conf))); store.register_late_pass(|_| Box::new(enum_clike::UnportableVariant)); - store.register_late_pass(|_| Box::new(float_literal::FloatLiteral)); + store.register_late_pass(move |_| Box::new(float_literal::FloatLiteral::new(conf))); store.register_late_pass(|_| Box::new(ptr::Ptr)); store.register_late_pass(|_| Box::new(needless_bool::NeedlessBool)); - store.register_late_pass(|_| Box::new(needless_bool::BoolComparison)); + store.register_late_pass(|_| Box::new(bool_comparison::BoolComparison)); store.register_late_pass(|_| Box::new(needless_for_each::NeedlessForEach)); store.register_late_pass(|_| Box::new(misc::LintPass)); store.register_late_pass(|_| Box::new(eta_reduction::EtaReduction)); @@ -655,7 +656,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(conf))); store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(pattern_type_mismatch::PatternTypeMismatch)); - store.register_late_pass(|_| Box::new(unwrap_in_result::UnwrapInResult)); + store.register_late_pass(|_| Box::::default()); store.register_late_pass(|_| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned)); store.register_late_pass(|_| Box::new(async_yields_async::AsyncYieldsAsync)); let attrs = attr_storage.clone(); diff --git a/clippy_lints/src/loops/infinite_loop.rs b/clippy_lints/src/loops/infinite_loop.rs index a71e6963f8ca..74c0b1780189 100644 --- a/clippy_lints/src/loops/infinite_loop.rs +++ b/clippy_lints/src/loops/infinite_loop.rs @@ -4,7 +4,8 @@ use hir::intravisit::{Visitor, walk_expr}; use rustc_ast::Label; use rustc_errors::Applicability; use rustc_hir::{ - self as hir, Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, Expr, ExprKind, FnRetTy, FnSig, Node, TyKind, + self as hir, Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, FnRetTy, + FnSig, Node, TyKind, }; use rustc_lint::{LateContext, LintContext}; use rustc_span::sym; @@ -73,7 +74,11 @@ fn is_inside_unawaited_async_block(cx: &LateContext<'_>, expr: &Expr<'_>) -> boo if let Node::Expr(Expr { kind: ExprKind::Closure(Closure { - kind: ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)), + kind: + ClosureKind::Coroutine(CoroutineKind::Desugared( + CoroutineDesugaring::Async, + CoroutineSource::Block | CoroutineSource::Closure, + )), .. }), .. diff --git a/clippy_lints/src/loops/missing_spin_loop.rs b/clippy_lints/src/loops/missing_spin_loop.rs index a9944d64ce2d..8a2d0036203a 100644 --- a/clippy_lints/src/loops/missing_spin_loop.rs +++ b/clippy_lints/src/loops/missing_spin_loop.rs @@ -1,10 +1,10 @@ use super::MISSING_SPIN_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::std_or_core; +use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind}; use rustc_lint::LateContext; -use rustc_middle::ty; use rustc_span::sym; fn unpack_cond<'tcx>(cond: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { @@ -39,8 +39,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, body: &' ) = body.kind && let ExprKind::MethodCall(method, callee, ..) = unpack_cond(cond).kind && [sym::load, sym::compare_exchange, sym::compare_exchange_weak].contains(&method.ident.name) - && let ty::Adt(def, _args) = cx.typeck_results().expr_ty(callee).kind() - && cx.tcx.is_diagnostic_item(sym::AtomicBool, def.did()) + && let callee_ty = cx.typeck_results().expr_ty(callee) + && is_type_diagnostic_item(cx, callee_ty, sym::AtomicBool) && let Some(std_or_core) = std_or_core(cx) { span_lint_and_sugg( diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 7bb684d65bb4..11edb929d70b 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::ty::has_iter_method; use clippy_utils::visitors::is_local_used; -use clippy_utils::{SpanlessEq, contains_name, higher, is_integer_const, sugg}; +use clippy_utils::{SpanlessEq, contains_name, higher, is_integer_const, peel_hir_expr_while, sugg}; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_errors::Applicability; @@ -253,12 +253,38 @@ struct VarVisitor<'a, 'tcx> { impl<'tcx> VarVisitor<'_, 'tcx> { fn check(&mut self, idx: &'tcx Expr<'_>, seqexpr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) -> bool { - let index_used_directly = matches!(idx.kind, ExprKind::Path(_)); + let mut used_cnt = 0; + // It is `true` if all indices are direct + let mut index_used_directly = true; + + // Handle initial index + if is_local_used(self.cx, idx, self.var) { + used_cnt += 1; + index_used_directly &= matches!(idx.kind, ExprKind::Path(_)); + } + // Handle nested indices + let seqexpr = peel_hir_expr_while(seqexpr, |e| { + if let ExprKind::Index(e, idx, _) = e.kind { + if is_local_used(self.cx, idx, self.var) { + used_cnt += 1; + index_used_directly &= matches!(idx.kind, ExprKind::Path(_)); + } + Some(e) + } else { + None + } + }); + + match used_cnt { + 0 => return true, + n if n > 1 => self.nonindex = true, // Optimize code like `a[i][i]` + _ => {}, + } + if let ExprKind::Path(ref seqpath) = seqexpr.kind // the indexed container is referenced by a name && let QPath::Resolved(None, seqvar) = *seqpath && seqvar.segments.len() == 1 - && is_local_used(self.cx, idx, self.var) { if self.prefer_mutable { self.indexed_mut.insert(seqvar.segments[0].ident.name); @@ -312,7 +338,6 @@ impl<'tcx> VarVisitor<'_, 'tcx> { impl<'tcx> Visitor<'tcx> for VarVisitor<'_, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { if let ExprKind::MethodCall(meth, args_0, [args_1, ..], _) = &expr.kind - // a range index op && let Some(trait_id) = self .cx .typeck_results() diff --git a/clippy_lints/src/loops/unused_enumerate_index.rs b/clippy_lints/src/loops/unused_enumerate_index.rs index 51e21aa9734e..13b93d2c0097 100644 --- a/clippy_lints/src/loops/unused_enumerate_index.rs +++ b/clippy_lints/src/loops/unused_enumerate_index.rs @@ -1,12 +1,12 @@ use super::UNUSED_ENUMERATE_INDEX; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; +use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{pat_is_wild, sugg}; use rustc_errors::Applicability; use rustc_hir::def::DefKind; use rustc_hir::{Expr, ExprKind, Pat, PatKind}; use rustc_lint::LateContext; -use rustc_middle::ty; use rustc_span::sym; /// Checks for the `UNUSED_ENUMERATE_INDEX` lint. @@ -17,8 +17,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>, arg: &Expr<'_ && let ExprKind::MethodCall(_method, self_arg, [], _) = arg.kind && let ty = cx.typeck_results().expr_ty(arg) && pat_is_wild(cx, &index.kind, body) - && let ty::Adt(base, _) = *ty.kind() - && cx.tcx.is_diagnostic_item(sym::Enumerate, base.did()) + && is_type_diagnostic_item(cx, ty, sym::Enumerate) && let Some((DefKind::AssocFn, call_id)) = cx.typeck_results().type_dependent_def(arg.hir_id) && cx.tcx.is_diagnostic_item(sym::enumerate_method, call_id) { diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs index ac8c88f02057..2eebb2430fd9 100644 --- a/clippy_lints/src/manual_is_ascii_check.rs +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -97,11 +97,12 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { return; } - if let Some(macro_call) = matching_root_macro_call(cx, expr.span, sym::matches_macro) { - if let ExprKind::Match(recv, [arm, ..], _) = expr.kind { - let range = check_pat(&arm.pat.kind); - check_is_ascii(cx, macro_call.span, recv, &range, None); - } + let (arg, span, range) = if let Some(macro_call) = matching_root_macro_call(cx, expr.span, sym::matches_macro) + && let ExprKind::Match(recv, [arm, ..], _) = expr.kind + { + let recv = peel_ref_operators(cx, recv); + let range = check_pat(&arm.pat.kind); + (recv, macro_call.span, range) } else if let ExprKind::MethodCall(path, receiver, [arg], ..) = expr.kind && path.ident.name == sym::contains && let Some(higher::Range { @@ -112,10 +113,14 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { && !matches!(cx.typeck_results().expr_ty(arg).peel_refs().kind(), ty::Param(_)) { let arg = peel_ref_operators(cx, arg); - let ty_sugg = get_ty_sugg(cx, arg); let range = check_expr_range(start, end); - check_is_ascii(cx, expr.span, arg, &range, ty_sugg); - } + (arg, expr.span, range) + } else { + return; + }; + + let ty_sugg = get_ty_sugg(cx, arg); + check_is_ascii(cx, span, arg, &range, ty_sugg); } } @@ -146,9 +151,8 @@ fn check_is_ascii( CharRange::HexDigit => "is_ascii_hexdigit", CharRange::Otherwise | CharRange::LowerHexLetter | CharRange::UpperHexLetter => return, }; - let default_snip = ".."; let mut app = Applicability::MachineApplicable; - let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), default_snip, &mut app).maybe_paren(); + let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), "_", &mut app).maybe_paren(); let mut suggestion = vec![(span, format!("{recv}.{sugg}()"))]; if let Some((ty_span, ty)) = ty_sugg { suggestion.push((ty_span, format!("{recv}: {ty}"))); @@ -182,7 +186,7 @@ fn check_pat(pat_kind: &PatKind<'_>) -> CharRange { CharRange::Otherwise } }, - PatKind::Range(Some(start), Some(end), kind) if *kind == RangeEnd::Included => check_range(start, end), + PatKind::Range(Some(start), Some(end), RangeEnd::Included) => check_range(start, end), _ => CharRange::Otherwise, } } diff --git a/clippy_lints/src/manual_retain.rs b/clippy_lints/src/manual_retain.rs index 98e8b1f5cf92..7fb88763e640 100644 --- a/clippy_lints/src/manual_retain.rs +++ b/clippy_lints/src/manual_retain.rs @@ -123,8 +123,8 @@ fn check_iter( ) { if let hir::ExprKind::MethodCall(_, filter_expr, [], _) = &target_expr.kind && let Some(copied_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id) - && (cx.tcx.is_diagnostic_item(sym::iter_copied, copied_def_id) - || cx.tcx.is_diagnostic_item(sym::iter_cloned, copied_def_id)) + && let Some(copied_name) = cx.tcx.get_diagnostic_name(copied_def_id) + && matches!(copied_name, sym::iter_copied | sym::iter_cloned) && let hir::ExprKind::MethodCall(_, iter_expr, [_], _) = &filter_expr.kind && let Some(filter_def_id) = cx.typeck_results().type_dependent_def_id(filter_expr.hir_id) && cx.tcx.is_diagnostic_item(sym::iter_filter, filter_def_id) @@ -243,9 +243,9 @@ fn make_sugg( } fn match_acceptable_sym(cx: &LateContext<'_>, collect_def_id: DefId) -> bool { - ACCEPTABLE_METHODS - .iter() - .any(|&method| cx.tcx.is_diagnostic_item(method, collect_def_id)) + cx.tcx + .get_diagnostic_name(collect_def_id) + .is_some_and(|collect_name| ACCEPTABLE_METHODS.contains(&collect_name)) } fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: Msrv) -> bool { diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 6bf43a1c6d47..07cce4046ca4 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -75,12 +75,10 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { && let ExprKind::Path(target_path) = &target_arg.kind && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(cond.hir_id) { - let strip_kind = if cx.tcx.is_diagnostic_item(sym::str_starts_with, method_def_id) { - StripKind::Prefix - } else if cx.tcx.is_diagnostic_item(sym::str_ends_with, method_def_id) { - StripKind::Suffix - } else { - return; + let strip_kind = match cx.tcx.get_diagnostic_name(method_def_id) { + Some(sym::str_starts_with) => StripKind::Prefix, + Some(sym::str_ends_with) => StripKind::Suffix, + _ => return, }; let target_res = cx.qpath_res(target_path, target_arg.hir_id); if target_res == Res::Err { diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index af6a1b07a492..39e5289c62ae 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -116,8 +116,10 @@ fn is_unit_expression(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { /// The expression inside a closure may or may not have surrounding braces and /// semicolons, which causes problems when generating a suggestion. Given an /// expression that evaluates to '()' or '!', recursively remove useless braces -/// and semi-colons until is suitable for including in the suggestion template -fn reduce_unit_expression(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { +/// and semi-colons until is suitable for including in the suggestion template. +/// The `bool` is `true` when the resulting `span` needs to be enclosed in an +/// `unsafe` block. +fn reduce_unit_expression(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<(Span, bool)> { if !is_unit_expression(cx, expr) { return None; } @@ -125,22 +127,24 @@ fn reduce_unit_expression(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option< match expr.kind { hir::ExprKind::Call(_, _) | hir::ExprKind::MethodCall(..) => { // Calls can't be reduced any more - Some(expr.span) + Some((expr.span, false)) }, hir::ExprKind::Block(block, _) => { + let is_unsafe = matches!(block.rules, hir::BlockCheckMode::UnsafeBlock(_)); match (block.stmts, block.expr.as_ref()) { ([], Some(inner_expr)) => { // If block only contains an expression, // reduce `{ X }` to `X` reduce_unit_expression(cx, inner_expr) + .map(|(span, inner_is_unsafe)| (span, inner_is_unsafe || is_unsafe)) }, ([inner_stmt], None) => { // If block only contains statements, // reduce `{ X; }` to `X` or `X;` match inner_stmt.kind { - hir::StmtKind::Let(local) => Some(local.span), - hir::StmtKind::Expr(e) => Some(e.span), - hir::StmtKind::Semi(..) => Some(inner_stmt.span), + hir::StmtKind::Let(local) => Some((local.span, is_unsafe)), + hir::StmtKind::Expr(e) => Some((e.span, is_unsafe)), + hir::StmtKind::Semi(..) => Some((inner_stmt.span, is_unsafe)), hir::StmtKind::Item(..) => None, } }, @@ -228,10 +232,11 @@ fn lint_map_unit_fn( let msg = suggestion_msg("closure", map_type); span_lint_and_then(cx, lint, expr.span, msg, |diag| { - if let Some(reduced_expr_span) = reduce_unit_expression(cx, closure_expr) { + if let Some((reduced_expr_span, is_unsafe)) = reduce_unit_expression(cx, closure_expr) { let mut applicability = Applicability::MachineApplicable; + let (prefix_is_unsafe, suffix_is_unsafe) = if is_unsafe { ("unsafe { ", " }") } else { ("", "") }; let suggestion = format!( - "if let {0}({1}) = {2} {{ {3} }}", + "if let {0}({1}) = {2} {{ {prefix_is_unsafe}{3}{suffix_is_unsafe} }}", variant, snippet_with_applicability(cx, binding.pat.span, "_", &mut applicability), snippet_with_applicability(cx, var_arg.span, "_", &mut applicability), diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index 5b50efad3e44..aaf559fc4439 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -4,20 +4,22 @@ use clippy_utils::msrvs::Msrv; use clippy_utils::source::snippet; use clippy_utils::visitors::is_local_used; use clippy_utils::{ - SpanlessEq, is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, + SpanlessEq, get_ref_operators, is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, + peel_ref_operators, }; +use rustc_ast::BorrowKind; use rustc_errors::MultiSpan; use rustc_hir::LangItem::OptionNone; -use rustc_hir::{Arm, Expr, HirId, Pat, PatExpr, PatExprKind, PatKind}; +use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatExpr, PatExprKind, PatKind}; use rustc_lint::LateContext; use rustc_span::Span; use super::{COLLAPSIBLE_MATCH, pat_contains_disallowed_or}; -pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], msrv: Msrv) { +pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>], msrv: Msrv) { if let Some(els_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) { for arm in arms { - check_arm(cx, true, arm.pat, arm.body, arm.guard, Some(els_arm.body), msrv); + check_arm(cx, true, arm.pat, expr, arm.body, arm.guard, Some(els_arm.body), msrv); } } } @@ -27,15 +29,18 @@ pub(super) fn check_if_let<'tcx>( pat: &'tcx Pat<'_>, body: &'tcx Expr<'_>, else_expr: Option<&'tcx Expr<'_>>, + let_expr: &'tcx Expr<'_>, msrv: Msrv, ) { - check_arm(cx, false, pat, body, None, else_expr, msrv); + check_arm(cx, false, pat, let_expr, body, None, else_expr, msrv); } +#[allow(clippy::too_many_arguments)] fn check_arm<'tcx>( cx: &LateContext<'tcx>, outer_is_match: bool, outer_pat: &'tcx Pat<'tcx>, + outer_cond: &'tcx Expr<'tcx>, outer_then_body: &'tcx Expr<'tcx>, outer_guard: Option<&'tcx Expr<'tcx>>, outer_else_body: Option<&'tcx Expr<'tcx>>, @@ -82,6 +87,9 @@ fn check_arm<'tcx>( }, IfLetOrMatch::Match(_, arms, ..) => !arms.iter().any(|arm| is_local_used(cx, arm, binding_id)), } + // Check if the inner expression contains any borrows/dereferences + && let ref_types = get_ref_operators(cx, inner_scrutinee) + && let Some(method) = build_ref_method_chain(ref_types) { let msg = format!( "this `{}` can be collapsed into the outer `{}`", @@ -103,6 +111,10 @@ fn check_arm<'tcx>( let mut help_span = MultiSpan::from_spans(vec![binding_span, inner_then_pat.span]); help_span.push_span_label(binding_span, "replace this binding"); help_span.push_span_label(inner_then_pat.span, format!("with this pattern{replace_msg}")); + if !method.is_empty() { + let outer_cond_msg = format!("use: `{}{}`", snippet(cx, outer_cond.span, ".."), method); + help_span.push_span_label(outer_cond.span, outer_cond_msg); + } diag.span_help( help_span, "the outer pattern can be modified to include the inner pattern", @@ -148,3 +160,30 @@ fn find_pat_binding_and_is_innermost_parent_pat_struct(pat: &Pat<'_>, hir_id: Hi }); (span, is_innermost_parent_pat_struct) } + +/// Builds a chain of reference-manipulation method calls (e.g., `.as_ref()`, `.as_mut()`, +/// `.copied()`) based on reference operators +fn build_ref_method_chain(expr: Vec<&Expr<'_>>) -> Option { + let mut req_method_calls = String::new(); + + for ref_operator in expr { + match ref_operator.kind { + ExprKind::AddrOf(BorrowKind::Raw, _, _) => { + return None; + }, + ExprKind::AddrOf(_, m, _) if m.is_mut() => { + req_method_calls.push_str(".as_mut()"); + }, + ExprKind::AddrOf(_, _, _) => { + req_method_calls.push_str(".as_ref()"); + }, + // Deref operator is the only operator that this function should have received + ExprKind::Unary(_, _) => { + req_method_calls.push_str(".copied()"); + }, + _ => (), + } + } + + Some(req_method_calls) +} diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index c128fc40b733..6f49c5524118 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -1073,7 +1073,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { significant_drop_in_scrutinee::check_match(cx, expr, ex, arms, source); } - collapsible_match::check_match(cx, arms, self.msrv); + collapsible_match::check_match(cx, ex, arms, self.msrv); if !from_expansion { // These don't depend on a relationship between multiple arms match_wild_err_arm::check(cx, ex, arms); @@ -1137,7 +1137,14 @@ impl<'tcx> LateLintPass<'tcx> for Matches { match_ref_pats::check(cx, ex, arms.iter().map(|el| el.pat), expr); } } else if let Some(if_let) = higher::IfLet::hir(cx, expr) { - collapsible_match::check_if_let(cx, if_let.let_pat, if_let.if_then, if_let.if_else, self.msrv); + collapsible_match::check_if_let( + cx, + if_let.let_pat, + if_let.if_then, + if_let.if_else, + if_let.let_expr, + self.msrv, + ); significant_drop_in_scrutinee::check_if_let(cx, expr, if_let.let_expr, if_let.if_then, if_let.if_else); if !from_expansion { if let Some(else_expr) = if_let.if_else { diff --git a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 027dd7ce0534..81fecc87256c 100644 --- a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -226,11 +226,12 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> { } } -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Default)] enum SigDropHolder { /// No values with significant drop present in this expression. /// /// Expressions that we've emitted lints do not count. + #[default] None, /// Some field in this expression references to values with significant drop. /// @@ -244,12 +245,6 @@ enum SigDropHolder { Moved, } -impl Default for SigDropHolder { - fn default() -> Self { - Self::None - } -} - struct SigDropHelper<'a, 'tcx> { cx: &'a LateContext<'tcx>, parent_expr: Option<&'tcx Expr<'tcx>>, diff --git a/clippy_lints/src/matches/try_err.rs b/clippy_lints/src/matches/try_err.rs index ff7769af1df4..af90cb5e6733 100644 --- a/clippy_lints/src/matches/try_err.rs +++ b/clippy_lints/src/matches/try_err.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::option_arg_ty; use clippy_utils::{get_parent_expr, is_res_lang_ctor, path_res}; use rustc_errors::Applicability; use rustc_hir::LangItem::ResultErr; @@ -28,25 +28,15 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine && is_res_lang_ctor(cx, path_res(cx, err_fun), ResultErr) && let Some(return_ty) = find_return_type(cx, &expr.kind) { - let prefix; - let suffix; - let err_ty; - - if let Some(ty) = result_error_type(cx, return_ty) { - prefix = "Err("; - suffix = ")"; - err_ty = ty; + let (prefix, suffix, err_ty) = if let Some(ty) = result_error_type(cx, return_ty) { + ("Err(", ")", ty) } else if let Some(ty) = poll_result_error_type(cx, return_ty) { - prefix = "Poll::Ready(Err("; - suffix = "))"; - err_ty = ty; + ("Poll::Ready(Err(", "))", ty) } else if let Some(ty) = poll_option_result_error_type(cx, return_ty) { - prefix = "Poll::Ready(Some(Err("; - suffix = ")))"; - err_ty = ty; + ("Poll::Ready(Some(Err(", ")))", ty) } else { return; - } + }; span_lint_and_then( cx, @@ -88,8 +78,8 @@ fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> O /// Extracts the error type from Result. fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { - if let ty::Adt(_, subst) = ty.kind() - && is_type_diagnostic_item(cx, ty, sym::Result) + if let ty::Adt(def, subst) = ty.kind() + && cx.tcx.is_diagnostic_item(sym::Result, def.did()) { Some(subst.type_at(1)) } else { @@ -101,11 +91,9 @@ fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { if let ty::Adt(def, subst) = ty.kind() && cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did()) - && let ready_ty = subst.type_at(0) - && let ty::Adt(ready_def, ready_subst) = ready_ty.kind() - && cx.tcx.is_diagnostic_item(sym::Result, ready_def.did()) { - Some(ready_subst.type_at(1)) + let ready_ty = subst.type_at(0); + result_error_type(cx, ready_ty) } else { None } @@ -116,13 +104,9 @@ fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> if let ty::Adt(def, subst) = ty.kind() && cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did()) && let ready_ty = subst.type_at(0) - && let ty::Adt(ready_def, ready_subst) = ready_ty.kind() - && cx.tcx.is_diagnostic_item(sym::Option, ready_def.did()) - && let some_ty = ready_subst.type_at(0) - && let ty::Adt(some_def, some_subst) = some_ty.kind() - && cx.tcx.is_diagnostic_item(sym::Result, some_def.did()) + && let Some(some_ty) = option_arg_ty(cx, ready_ty) { - Some(some_subst.type_at(1)) + result_error_type(cx, some_ty) } else { None } diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index 28efd2038b38..e39916f733d5 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -215,7 +215,8 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<' && let ExprKind::Path(ref repl_func_qpath) = repl_func.kind && let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id() { - if cx.tcx.is_diagnostic_item(sym::mem_uninitialized, repl_def_id) { + let repl_name = cx.tcx.get_diagnostic_name(repl_def_id); + if repl_name == Some(sym::mem_uninitialized) { let Some(top_crate) = std_or_core(cx) else { return }; let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( @@ -230,9 +231,7 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<' ), applicability, ); - } else if cx.tcx.is_diagnostic_item(sym::mem_zeroed, repl_def_id) - && !cx.typeck_results().expr_ty(src).is_primitive() - { + } else if repl_name == Some(sym::mem_zeroed) && !cx.typeck_results().expr_ty(src).is_primitive() { span_lint_and_help( cx, MEM_REPLACE_WITH_UNINIT, diff --git a/clippy_lints/src/methods/clone_on_ref_ptr.rs b/clippy_lints/src/methods/clone_on_ref_ptr.rs index 96e2de0dc1cb..65583c6a9811 100644 --- a/clippy_lints/src/methods/clone_on_ref_ptr.rs +++ b/clippy_lints/src/methods/clone_on_ref_ptr.rs @@ -24,9 +24,10 @@ pub(super) fn check( && let Some(name) = cx.tcx.get_diagnostic_name(adt.did()) { let caller_type = match name { - sym::Rc => "Rc", - sym::Arc => "Arc", - sym::RcWeak | sym::ArcWeak => "Weak", + sym::Rc => "std::rc::Rc", + sym::Arc => "std::sync::Arc", + sym::RcWeak => "std::rc::Weak", + sym::ArcWeak => "std::sync::Weak", _ => return, }; span_lint_and_then( diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index 5b8457bdd164..2da0f8341b17 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -233,18 +233,16 @@ impl<'tcx> OffendingFilterExpr<'tcx> { // the latter only calls `effect` once let side_effect_expr_span = receiver.can_have_side_effects().then_some(receiver.span); - if cx.tcx.is_diagnostic_item(sym::Option, recv_ty.did()) && path.ident.name == sym::is_some { - Some(Self::IsSome { + match (cx.tcx.get_diagnostic_name(recv_ty.did()), path.ident.name) { + (Some(sym::Option), sym::is_some) => Some(Self::IsSome { receiver, side_effect_expr_span, - }) - } else if cx.tcx.is_diagnostic_item(sym::Result, recv_ty.did()) && path.ident.name == sym::is_ok { - Some(Self::IsOk { + }), + (Some(sym::Result), sym::is_ok) => Some(Self::IsOk { receiver, side_effect_expr_span, - }) - } else { - None + }), + _ => None, } } else if matching_root_macro_call(cx, expr.span, sym::matches_macro).is_some() // we know for a fact that the wildcard pattern is the second arm diff --git a/clippy_lints/src/methods/iter_out_of_bounds.rs b/clippy_lints/src/methods/iter_out_of_bounds.rs index 9a62b719a8fb..fa8f9d640ee6 100644 --- a/clippy_lints/src/methods/iter_out_of_bounds.rs +++ b/clippy_lints/src/methods/iter_out_of_bounds.rs @@ -24,32 +24,29 @@ fn get_iterator_length<'tcx>(cx: &LateContext<'tcx>, iter: &'tcx Expr<'tcx>) -> let ty::Adt(adt, substs) = cx.typeck_results().expr_ty(iter).kind() else { return None; }; - let did = adt.did(); - if cx.tcx.is_diagnostic_item(sym::ArrayIntoIter, did) { - // For array::IntoIter, the length is the second generic - // parameter. - substs.const_at(1).try_to_target_usize(cx.tcx).map(u128::from) - } else if cx.tcx.is_diagnostic_item(sym::SliceIter, did) - && let ExprKind::MethodCall(_, recv, ..) = iter.kind - { - if let ty::Array(_, len) = cx.typeck_results().expr_ty(recv).peel_refs().kind() { - // For slice::Iter<'_, T>, the receiver might be an array literal: [1,2,3].iter().skip(..) - len.try_to_target_usize(cx.tcx).map(u128::from) - } else if let Some(args) = VecArgs::hir(cx, expr_or_init(cx, recv)) { - match args { - VecArgs::Vec(vec) => vec.len().try_into().ok(), - VecArgs::Repeat(_, len) => expr_as_u128(cx, len), + match cx.tcx.get_diagnostic_name(adt.did()) { + Some(sym::ArrayIntoIter) => { + // For array::IntoIter, the length is the second generic + // parameter. + substs.const_at(1).try_to_target_usize(cx.tcx).map(u128::from) + }, + Some(sym::SliceIter) if let ExprKind::MethodCall(_, recv, ..) = iter.kind => { + if let ty::Array(_, len) = cx.typeck_results().expr_ty(recv).peel_refs().kind() { + // For slice::Iter<'_, T>, the receiver might be an array literal: [1,2,3].iter().skip(..) + len.try_to_target_usize(cx.tcx).map(u128::from) + } else if let Some(args) = VecArgs::hir(cx, expr_or_init(cx, recv)) { + match args { + VecArgs::Vec(vec) => vec.len().try_into().ok(), + VecArgs::Repeat(_, len) => expr_as_u128(cx, len), + } + } else { + None } - } else { - None - } - } else if cx.tcx.is_diagnostic_item(sym::IterEmpty, did) { - Some(0) - } else if cx.tcx.is_diagnostic_item(sym::IterOnce, did) { - Some(1) - } else { - None + }, + Some(sym::IterEmpty) => Some(0), + Some(sym::IterOnce) => Some(1), + _ => None, } } diff --git a/clippy_lints/src/methods/map_flatten.rs b/clippy_lints/src/methods/map_flatten.rs index f7bb8c1d696d..750f933330a2 100644 --- a/clippy_lints/src/methods/map_flatten.rs +++ b/clippy_lints/src/methods/map_flatten.rs @@ -50,10 +50,10 @@ fn try_get_caller_ty_name_and_method_name( } } else { if let ty::Adt(adt, _) = cx.typeck_results().expr_ty(caller_expr).kind() { - if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) { - return Some(("Option", "and_then")); - } else if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) { - return Some(("Result", "and_then")); + match cx.tcx.get_diagnostic_name(adt.did()) { + Some(sym::Option) => return Some(("Option", "and_then")), + Some(sym::Result) => return Some(("Result", "and_then")), + _ => {}, } } None diff --git a/clippy_lints/src/methods/map_identity.rs b/clippy_lints/src/methods/map_identity.rs index 98def66ca149..a98cfff8bfbd 100644 --- a/clippy_lints/src/methods/map_identity.rs +++ b/clippy_lints/src/methods/map_identity.rs @@ -1,14 +1,16 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_expr_untyped_identity_function, is_trait_method, path_to_local}; -use rustc_ast::BindingMode; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; +use clippy_utils::{is_expr_untyped_identity_function, is_mutable, is_trait_method, path_to_local_with_projections}; use rustc_errors::Applicability; -use rustc_hir::{self as hir, Node, PatKind}; -use rustc_lint::LateContext; +use rustc_hir::{self as hir, ExprKind, Node, PatKind}; +use rustc_lint::{LateContext, LintContext}; use rustc_span::{Span, Symbol, sym}; use super::MAP_IDENTITY; +const MSG: &str = "unnecessary map of the identity function"; + pub(super) fn check( cx: &LateContext<'_>, expr: &hir::Expr<'_>, @@ -23,26 +25,70 @@ pub(super) fn check( || is_type_diagnostic_item(cx, caller_ty, sym::Result) || is_type_diagnostic_item(cx, caller_ty, sym::Option)) && is_expr_untyped_identity_function(cx, map_arg) - && let Some(sugg_span) = expr.span.trim_start(caller.span) + && let Some(call_span) = expr.span.trim_start(caller.span) { - // If the result of `.map(identity)` is used as a mutable reference, - // the caller must not be an immutable binding. - if cx.typeck_results().expr_ty_adjusted(expr).is_mutable_ptr() - && let Some(hir_id) = path_to_local(caller) - && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) - && !matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..)) - { - return; - } + let main_sugg = (call_span, String::new()); + let mut app = if is_copy(cx, caller_ty) { + // there is technically a behavioral change here for `Copy` iterators, where + // `iter.map(|x| x).next()` would mutate a temporary copy of the iterator and + // changing it to `iter.next()` mutates iter directly + Applicability::Unspecified + } else { + Applicability::MachineApplicable + }; - span_lint_and_sugg( - cx, - MAP_IDENTITY, - sugg_span, - "unnecessary map of the identity function", - format!("remove the call to `{name}`"), - String::new(), - Applicability::MachineApplicable, - ); + let needs_to_be_mutable = cx.typeck_results().expr_ty_adjusted(expr).is_mutable_ptr(); + if needs_to_be_mutable && !is_mutable(cx, caller) { + if let Some(hir_id) = path_to_local_with_projections(caller) + && let Node::Pat(pat) = cx.tcx.hir_node(hir_id) + && let PatKind::Binding(_, _, ident, _) = pat.kind + { + // We can reach the binding -- suggest making it mutable + let suggs = vec![main_sugg, (ident.span.shrink_to_lo(), String::from("mut "))]; + + let ident = snippet_with_applicability(cx.sess(), ident.span, "_", &mut app); + + span_lint_and_then(cx, MAP_IDENTITY, call_span, MSG, |diag| { + diag.multipart_suggestion( + format!("remove the call to `{name}`, and make `{ident}` mutable"), + suggs, + app, + ); + }); + } else { + // If we can't make the binding mutable, prevent the suggestion from being automatically applied, + // and add a complementary help message. + app = Applicability::Unspecified; + + let method_requiring_mut = if let Node::Expr(expr) = cx.tcx.parent_hir_node(expr.hir_id) + && let ExprKind::MethodCall(method, ..) = expr.kind + { + Some(method.ident) + } else { + None + }; + + span_lint_and_then(cx, MAP_IDENTITY, call_span, MSG, |diag| { + diag.span_suggestion(main_sugg.0, format!("remove the call to `{name}`"), main_sugg.1, app); + + let note = if let Some(method_requiring_mut) = method_requiring_mut { + format!("this must be made mutable to use `{method_requiring_mut}`") + } else { + "this must be made mutable".to_string() + }; + diag.span_note(caller.span, note); + }); + } + } else { + span_lint_and_sugg( + cx, + MAP_IDENTITY, + main_sugg.0, + MSG, + format!("remove the call to `{name}`"), + main_sugg.1, + app, + ); + } } } diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index 63ee922acfa0..906ead16fd0d 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -38,17 +38,13 @@ pub(super) fn check( ]; let is_deref = match map_arg.kind { - hir::ExprKind::Path(ref expr_qpath) => { - cx.qpath_res(expr_qpath, map_arg.hir_id) - .opt_def_id() - .is_some_and(|fun_def_id| { - cx.tcx.is_diagnostic_item(sym::deref_method, fun_def_id) - || cx.tcx.is_diagnostic_item(sym::deref_mut_method, fun_def_id) - || deref_aliases - .iter() - .any(|&sym| cx.tcx.is_diagnostic_item(sym, fun_def_id)) - }) - }, + hir::ExprKind::Path(ref expr_qpath) => cx + .qpath_res(expr_qpath, map_arg.hir_id) + .opt_def_id() + .and_then(|fun_def_id| cx.tcx.get_diagnostic_name(fun_def_id)) + .is_some_and(|fun_name| { + matches!(fun_name, sym::deref_method | sym::deref_mut_method) || deref_aliases.contains(&fun_name) + }), hir::ExprKind::Closure(&hir::Closure { body, .. }) => { let closure_body = cx.tcx.hir_body(body); let closure_expr = peel_blocks(closure_body.value); @@ -63,13 +59,11 @@ pub(super) fn check( .map(|x| &x.kind) .collect::>() && let [ty::adjustment::Adjust::Deref(None), ty::adjustment::Adjust::Borrow(_)] = *adj + && let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap() + && let Some(method_name) = cx.tcx.get_diagnostic_name(method_did) { - let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap(); - cx.tcx.is_diagnostic_item(sym::deref_method, method_did) - || cx.tcx.is_diagnostic_item(sym::deref_mut_method, method_did) - || deref_aliases - .iter() - .any(|&sym| cx.tcx.is_diagnostic_item(sym, method_did)) + matches!(method_name, sym::deref_method | sym::deref_mut_method) + || deref_aliases.contains(&method_name) } else { false } diff --git a/clippy_lints/src/methods/or_then_unwrap.rs b/clippy_lints/src/methods/or_then_unwrap.rs index 3e64e15dc860..1a760ea733d7 100644 --- a/clippy_lints/src/methods/or_then_unwrap.rs +++ b/clippy_lints/src/methods/or_then_unwrap.rs @@ -62,7 +62,7 @@ fn get_content_if_ctor_matches(cx: &LateContext<'_>, expr: &Expr<'_>, item: Lang if let ExprKind::Call(some_expr, [arg]) = expr.kind && is_res_lang_ctor(cx, path_res(cx, some_expr), item) { - Some(arg.span) + Some(arg.span.source_callsite()) } else { None } diff --git a/clippy_lints/src/methods/read_line_without_trim.rs b/clippy_lints/src/methods/read_line_without_trim.rs index 407f2e80aff2..6738bbf0a12b 100644 --- a/clippy_lints/src/methods/read_line_without_trim.rs +++ b/clippy_lints/src/methods/read_line_without_trim.rs @@ -31,8 +31,8 @@ fn parse_fails_on_trailing_newline(ty: Ty<'_>) -> bool { } pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) { - if let Some(recv_adt) = cx.typeck_results().expr_ty(recv).ty_adt_def() - && cx.tcx.is_diagnostic_item(sym::Stdin, recv_adt.did()) + let recv_ty = cx.typeck_results().expr_ty(recv); + if is_type_diagnostic_item(cx, recv_ty, sym::Stdin) && let ExprKind::Path(QPath::Resolved(_, path)) = arg.peel_borrows().kind && let Res::Local(local_id) = path.res { diff --git a/clippy_lints/src/methods/single_char_add_str.rs b/clippy_lints/src/methods/single_char_add_str.rs index ccdf5529d537..ef3d7acdc01e 100644 --- a/clippy_lints/src/methods/single_char_add_str.rs +++ b/clippy_lints/src/methods/single_char_add_str.rs @@ -5,10 +5,10 @@ use rustc_span::sym; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { - if cx.tcx.is_diagnostic_item(sym::string_push_str, fn_def_id) { - single_char_push_string::check(cx, expr, receiver, args); - } else if cx.tcx.is_diagnostic_item(sym::string_insert_str, fn_def_id) { - single_char_insert_string::check(cx, expr, receiver, args); + match cx.tcx.get_diagnostic_name(fn_def_id) { + Some(sym::string_push_str) => single_char_push_string::check(cx, expr, receiver, args), + Some(sym::string_insert_str) => single_char_insert_string::check(cx, expr, receiver, args), + _ => {}, } } } diff --git a/clippy_lints/src/methods/suspicious_to_owned.rs b/clippy_lints/src/methods/suspicious_to_owned.rs index ce7aefed01f4..ffc237e3c24c 100644 --- a/clippy_lints/src/methods/suspicious_to_owned.rs +++ b/clippy_lints/src/methods/suspicious_to_owned.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_diag_trait_item; use clippy_utils::source::snippet_with_context; +use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty::print::with_forced_trimmed_paths; -use rustc_middle::ty::{self}; use rustc_span::sym; use super::SUSPICIOUS_TO_OWNED; @@ -14,8 +14,7 @@ pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) - if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && is_diag_trait_item(cx, method_def_id, sym::ToOwned) && let input_type = cx.typeck_results().expr_ty(expr) - && let ty::Adt(adt, _) = cx.typeck_results().expr_ty(expr).kind() - && cx.tcx.is_diagnostic_item(sym::Cow, adt.did()) + && is_type_diagnostic_item(cx, input_type, sym::Cow) { let mut app = Applicability::MaybeIncorrect; let recv_snip = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut app).0; diff --git a/clippy_lints/src/methods/unnecessary_min_or_max.rs b/clippy_lints/src/methods/unnecessary_min_or_max.rs index 413881d5ec99..b87d81b71026 100644 --- a/clippy_lints/src/methods/unnecessary_min_or_max.rs +++ b/clippy_lints/src/methods/unnecessary_min_or_max.rs @@ -22,7 +22,8 @@ pub(super) fn check<'tcx>( let typeck_results = cx.typeck_results(); let ecx = ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), typeck_results); if let Some(id) = typeck_results.type_dependent_def_id(expr.hir_id) - && (cx.tcx.is_diagnostic_item(sym::cmp_ord_min, id) || cx.tcx.is_diagnostic_item(sym::cmp_ord_max, id)) + && let Some(fn_name) = cx.tcx.get_diagnostic_name(id) + && matches!(fn_name, sym::cmp_ord_min | sym::cmp_ord_max) { if let Some((left, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(recv) && let Some((right, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(arg) diff --git a/clippy_lints/src/methods/unused_enumerate_index.rs b/clippy_lints/src/methods/unused_enumerate_index.rs index af466fe091c2..af4ade3cc0f7 100644 --- a/clippy_lints/src/methods/unused_enumerate_index.rs +++ b/clippy_lints/src/methods/unused_enumerate_index.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::{SpanRangeExt, snippet}; +use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{expr_or_init, is_trait_method, pat_is_wild}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, FnDecl, PatKind, TyKind}; use rustc_lint::LateContext; -use rustc_middle::ty::AdtDef; use rustc_span::{Span, sym}; use crate::loops::UNUSED_ENUMERATE_INDEX; @@ -39,9 +39,8 @@ use crate::loops::UNUSED_ENUMERATE_INDEX; /// * `closure_arg`: The argument to the map function call containing the closure/function to apply pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, closure_arg: &Expr<'_>) { let recv_ty = cx.typeck_results().expr_ty(recv); - if let Some(recv_ty_defid) = recv_ty.ty_adt_def().map(AdtDef::did) - // If we call a method on a `std::iter::Enumerate` instance - && cx.tcx.is_diagnostic_item(sym::Enumerate, recv_ty_defid) + // If we call a method on a `std::iter::Enumerate` instance + if is_type_diagnostic_item(cx, recv_ty, sym::Enumerate) // If we are calling a method of the `Iterator` trait && is_trait_method(cx, call_expr, sym::Iterator) // And the map argument is a closure diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index d02952eb4870..28555a610900 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::{span_lint, span_lint_hir}; use rustc_hir::attrs::AttributeKind; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, Attribute, find_attr}; @@ -64,14 +64,20 @@ declare_clippy_lint! { "detects missing `#[inline]` attribute for public callables (functions, trait methods, methods...)" } -fn check_missing_inline_attrs(cx: &LateContext<'_>, attrs: &[Attribute], sp: Span, desc: &'static str) { +fn check_missing_inline_attrs( + cx: &LateContext<'_>, + attrs: &[Attribute], + sp: Span, + desc: &'static str, + hir_id: Option, +) { if !find_attr!(attrs, AttributeKind::Inline(..)) { - span_lint( - cx, - MISSING_INLINE_IN_PUBLIC_ITEMS, - sp, - format!("missing `#[inline]` for {desc}"), - ); + let msg = format!("missing `#[inline]` for {desc}"); + if let Some(hir_id) = hir_id { + span_lint_hir(cx, MISSING_INLINE_IN_PUBLIC_ITEMS, hir_id, sp, msg); + } else { + span_lint(cx, MISSING_INLINE_IN_PUBLIC_ITEMS, sp, msg); + } } } @@ -103,17 +109,9 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { let desc = "a function"; let attrs = cx.tcx.hir_attrs(it.hir_id()); - check_missing_inline_attrs(cx, attrs, it.span, desc); + check_missing_inline_attrs(cx, attrs, it.span, desc, None); }, - hir::ItemKind::Trait( - ref _constness, - ref _is_auto, - ref _unsafe, - _ident, - _generics, - _bounds, - trait_items, - ) => { + hir::ItemKind::Trait(.., trait_items) => { // note: we need to check if the trait is exported so we can't use // `LateLintPass::check_trait_item` here. for &tit in trait_items { @@ -127,7 +125,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { let desc = "a default trait method"; let item = cx.tcx.hir_trait_item(tit); let attrs = cx.tcx.hir_attrs(item.hir_id()); - check_missing_inline_attrs(cx, attrs, item.span, desc); + check_missing_inline_attrs(cx, attrs, item.span, desc, Some(tit.hir_id())); } }, } @@ -182,7 +180,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { } let attrs = cx.tcx.hir_attrs(impl_item.hir_id()); - check_missing_inline_attrs(cx, attrs, impl_item.span, desc); + check_missing_inline_attrs(cx, attrs, impl_item.span, desc, None); } } diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/mut_reference.rs index 31f51b457540..ec93ef97cfaf 100644 --- a/clippy_lints/src/mut_reference.rs +++ b/clippy_lints/src/mut_reference.rs @@ -1,4 +1,6 @@ -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::sugg::Sugg; +use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; @@ -83,13 +85,18 @@ fn check_arguments<'tcx>( let parameters = type_definition.fn_sig(cx.tcx).skip_binder().inputs(); for (argument, parameter) in iter::zip(arguments, parameters) { if let ty::Ref(_, _, Mutability::Not) | ty::RawPtr(_, Mutability::Not) = parameter.kind() - && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _) = argument.kind + && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, arg) = argument.kind { - span_lint( + let mut applicability = Applicability::MachineApplicable; + let sugg = Sugg::hir_with_applicability(cx, arg, "_", &mut applicability).addr(); + span_lint_and_sugg( cx, UNNECESSARY_MUT_PASSED, argument.span, format!("the {fn_kind} `{name}` doesn't need a mutable reference"), + "remove this `mut`", + sugg.to_string(), + applicability, ); } } diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs index 6ae26156bc44..854e927aa2f7 100644 --- a/clippy_lints/src/needless_bool.rs +++ b/clippy_lints/src/needless_bool.rs @@ -2,16 +2,14 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::{ - SpanlessEq, get_parent_expr, higher, is_block_like, is_else_clause, is_expn_of, is_parent_stmt, - is_receiver_of_method_call, peel_blocks, peel_blocks_with_stmt, span_contains_comment, sym, + SpanlessEq, get_parent_expr, higher, is_block_like, is_else_clause, is_parent_stmt, is_receiver_of_method_call, + peel_blocks, peel_blocks_with_stmt, span_contains_comment, }; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::Span; -use rustc_span::source_map::Spanned; declare_clippy_lint! { /// ### What it does @@ -50,31 +48,6 @@ declare_clippy_lint! { "if-statements with plain booleans in the then- and else-clause, e.g., `if p { true } else { false }`" } -declare_clippy_lint! { - /// ### What it does - /// Checks for expressions of the form `x == true`, - /// `x != true` and order comparisons such as `x < true` (or vice versa) and - /// suggest using the variable directly. - /// - /// ### Why is this bad? - /// Unnecessary code. - /// - /// ### Example - /// ```rust,ignore - /// if x == true {} - /// if y == false {} - /// ``` - /// use `x` directly: - /// ```rust,ignore - /// if x {} - /// if !y {} - /// ``` - #[clippy::version = "pre 1.29.0"] - pub BOOL_COMPARISON, - complexity, - "comparing a variable to a boolean, e.g., `if x == true` or `if x != true`" -} - declare_clippy_lint! { /// ### What it does /// Checks for expressions of the form `if c { x = true } else { x = false }` @@ -224,201 +197,6 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool { } } -declare_lint_pass!(BoolComparison => [BOOL_COMPARISON]); - -impl<'tcx> LateLintPass<'tcx> for BoolComparison { - fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if e.span.from_expansion() { - return; - } - - if let ExprKind::Binary(Spanned { node, .. }, ..) = e.kind { - let ignore_case = None::<(fn(_) -> _, &str)>; - let ignore_no_literal = None::<(fn(_, _) -> _, &str)>; - match node { - BinOpKind::Eq => { - let true_case = Some((|h| h, "equality checks against true are unnecessary")); - let false_case = Some(( - |h: Sugg<'tcx>| !h, - "equality checks against false can be replaced by a negation", - )); - check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal); - }, - BinOpKind::Ne => { - let true_case = Some(( - |h: Sugg<'tcx>| !h, - "inequality checks against true can be replaced by a negation", - )); - let false_case = Some((|h| h, "inequality checks against false are unnecessary")); - check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal); - }, - BinOpKind::Lt => check_comparison( - cx, - e, - ignore_case, - Some((|h| h, "greater than checks against false are unnecessary")), - Some(( - |h: Sugg<'tcx>| !h, - "less than comparison against true can be replaced by a negation", - )), - ignore_case, - Some(( - |l: Sugg<'tcx>, r: Sugg<'tcx>| (!l).bit_and(&r), - "order comparisons between booleans can be simplified", - )), - ), - BinOpKind::Gt => check_comparison( - cx, - e, - Some(( - |h: Sugg<'tcx>| !h, - "less than comparison against true can be replaced by a negation", - )), - ignore_case, - ignore_case, - Some((|h| h, "greater than checks against false are unnecessary")), - Some(( - |l: Sugg<'tcx>, r: Sugg<'tcx>| l.bit_and(&(!r)), - "order comparisons between booleans can be simplified", - )), - ), - _ => (), - } - } - } -} - -struct ExpressionInfoWithSpan { - one_side_is_unary_not: bool, - left_span: Span, - right_span: Span, -} - -fn is_unary_not(e: &Expr<'_>) -> (bool, Span) { - if let ExprKind::Unary(UnOp::Not, operand) = e.kind { - return (true, operand.span); - } - (false, e.span) -} - -fn one_side_is_unary_not<'tcx>(left_side: &'tcx Expr<'_>, right_side: &'tcx Expr<'_>) -> ExpressionInfoWithSpan { - let left = is_unary_not(left_side); - let right = is_unary_not(right_side); - - ExpressionInfoWithSpan { - one_side_is_unary_not: left.0 != right.0, - left_span: left.1, - right_span: right.1, - } -} - -fn check_comparison<'a, 'tcx>( - cx: &LateContext<'tcx>, - e: &'tcx Expr<'_>, - left_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>, - left_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>, - right_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>, - right_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>, - no_literal: Option<(impl FnOnce(Sugg<'a>, Sugg<'a>) -> Sugg<'a>, &'static str)>, -) { - if let ExprKind::Binary(op, left_side, right_side) = e.kind { - let (l_ty, r_ty) = ( - cx.typeck_results().expr_ty(left_side), - cx.typeck_results().expr_ty(right_side), - ); - if is_expn_of(left_side.span, sym::cfg).is_some() || is_expn_of(right_side.span, sym::cfg).is_some() { - return; - } - if l_ty.is_bool() && r_ty.is_bool() { - let mut applicability = Applicability::MachineApplicable; - // Eliminate parentheses in `e` by using the lo pos of lhs and hi pos of rhs, - // calling `source_callsite` make sure macros are handled correctly, see issue #9907 - let binop_span = left_side - .span - .source_callsite() - .with_hi(right_side.span.source_callsite().hi()); - - if op.node == BinOpKind::Eq { - let expression_info = one_side_is_unary_not(left_side, right_side); - if expression_info.one_side_is_unary_not { - span_lint_and_sugg( - cx, - BOOL_COMPARISON, - binop_span, - "this comparison might be written more concisely", - "try simplifying it as shown", - format!( - "{} != {}", - snippet_with_applicability( - cx, - expression_info.left_span.source_callsite(), - "..", - &mut applicability - ), - snippet_with_applicability( - cx, - expression_info.right_span.source_callsite(), - "..", - &mut applicability - ) - ), - applicability, - ); - } - } - - match (fetch_bool_expr(left_side), fetch_bool_expr(right_side)) { - (Some(true), None) => left_true.map_or((), |(h, m)| { - suggest_bool_comparison(cx, binop_span, right_side, applicability, m, h); - }), - (None, Some(true)) => right_true.map_or((), |(h, m)| { - suggest_bool_comparison(cx, binop_span, left_side, applicability, m, h); - }), - (Some(false), None) => left_false.map_or((), |(h, m)| { - suggest_bool_comparison(cx, binop_span, right_side, applicability, m, h); - }), - (None, Some(false)) => right_false.map_or((), |(h, m)| { - suggest_bool_comparison(cx, binop_span, left_side, applicability, m, h); - }), - (None, None) => no_literal.map_or((), |(h, m)| { - let left_side = Sugg::hir_with_applicability(cx, left_side, "..", &mut applicability); - let right_side = Sugg::hir_with_applicability(cx, right_side, "..", &mut applicability); - span_lint_and_sugg( - cx, - BOOL_COMPARISON, - binop_span, - m, - "try simplifying it as shown", - h(left_side, right_side).into_string(), - applicability, - ); - }), - _ => (), - } - } - } -} - -fn suggest_bool_comparison<'a, 'tcx>( - cx: &LateContext<'tcx>, - span: Span, - expr: &Expr<'_>, - mut app: Applicability, - message: &'static str, - conv_hint: impl FnOnce(Sugg<'a>) -> Sugg<'a>, -) { - let hint = Sugg::hir_with_context(cx, expr, span.ctxt(), "..", &mut app); - span_lint_and_sugg( - cx, - BOOL_COMPARISON, - span, - message, - "try simplifying it as shown", - conv_hint(hint).into_string(), - app, - ); -} - enum Expression { Bool(bool), RetBool(bool), diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs index a67545e419ce..3a6ccc2bca99 100644 --- a/clippy_lints/src/needless_for_each.rs +++ b/clippy_lints/src/needless_for_each.rs @@ -1,6 +1,6 @@ use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_expr}; -use rustc_hir::{Block, BlockCheckMode, Closure, Expr, ExprKind, Stmt, StmtKind}; +use rustc_hir::{Block, BlockCheckMode, Closure, Expr, ExprKind, Stmt, StmtKind, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::Span; @@ -70,12 +70,24 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { && has_iter_method(cx, cx.typeck_results().expr_ty(iter_recv)).is_some() // Skip the lint if the body is not block because this is simpler than `for` loop. // e.g. `v.iter().for_each(f)` is simpler and clearer than using `for` loop. - && let ExprKind::Closure(&Closure { body, .. }) = for_each_arg.kind + && let ExprKind::Closure(&Closure { body, fn_decl, .. }) = for_each_arg.kind && let body = cx.tcx.hir_body(body) // Skip the lint if the body is not safe, so as not to suggest `for … in … unsafe {}` // and suggesting `for … in … { unsafe { } }` is a little ugly. && !matches!(body.value.kind, ExprKind::Block(Block { rules: BlockCheckMode::UnsafeBlock(_), .. }, ..)) { + let mut applicability = Applicability::MachineApplicable; + + // If any closure parameter has an explicit type specified, applying the lint would necessarily + // remove that specification, possibly breaking type inference + if fn_decl + .inputs + .iter() + .any(|input| matches!(input.kind, TyKind::Infer(..))) + { + applicability = Applicability::MaybeIncorrect; + } + let mut ret_collector = RetCollector::default(); ret_collector.visit_expr(body.value); @@ -84,18 +96,16 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { return; } - let (mut applicability, ret_suggs) = if ret_collector.spans.is_empty() { - (Applicability::MachineApplicable, None) + let ret_suggs = if ret_collector.spans.is_empty() { + None } else { - ( - Applicability::MaybeIncorrect, - Some( - ret_collector - .spans - .into_iter() - .map(|span| (span, "continue".to_string())) - .collect(), - ), + applicability = Applicability::MaybeIncorrect; + Some( + ret_collector + .spans + .into_iter() + .map(|span| (span, "continue".to_string())) + .collect(), ) }; diff --git a/clippy_lints/src/non_canonical_impls.rs b/clippy_lints/src/non_canonical_impls.rs index 04b092769666..ba67dc62abbd 100644 --- a/clippy_lints/src/non_canonical_impls.rs +++ b/clippy_lints/src/non_canonical_impls.rs @@ -134,7 +134,8 @@ impl LateLintPass<'_> for NonCanonicalImpls { return; } - if cx.tcx.is_diagnostic_item(sym::Clone, trait_impl.def_id) + let trait_name = cx.tcx.get_diagnostic_name(trait_impl.def_id); + if trait_name == Some(sym::Clone) && let Some(copy_def_id) = cx.tcx.get_diagnostic_item(sym::Copy) && implements_trait(cx, trait_impl.self_ty(), copy_def_id, &[]) { @@ -170,12 +171,8 @@ impl LateLintPass<'_> for NonCanonicalImpls { String::new(), Applicability::MaybeIncorrect, ); - - return; } - } - - if cx.tcx.is_diagnostic_item(sym::PartialOrd, trait_impl.def_id) + } else if trait_name == Some(sym::PartialOrd) && impl_item.ident.name == sym::partial_cmp && let Some(ord_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) && implements_trait(cx, trait_impl.self_ty(), ord_def_id, &[]) diff --git a/clippy_lints/src/non_octal_unix_permissions.rs b/clippy_lints/src/non_octal_unix_permissions.rs index 23a1622f30ff..cb934466bd89 100644 --- a/clippy_lints/src/non_octal_unix_permissions.rs +++ b/clippy_lints/src/non_octal_unix_permissions.rs @@ -43,13 +43,11 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { match &expr.kind { ExprKind::MethodCall(path, func, [param], _) => { if let Some(adt) = cx.typeck_results().expr_ty(func).peel_refs().ty_adt_def() - && ((path.ident.name == sym::mode - && matches!( - cx.tcx.get_diagnostic_name(adt.did()), - Some(sym::FsOpenOptions | sym::DirBuilder) - )) - || (path.ident.name == sym::set_mode - && cx.tcx.is_diagnostic_item(sym::FsPermissions, adt.did()))) + && matches!( + (cx.tcx.get_diagnostic_name(adt.did()), path.ident.name), + (Some(sym::FsOpenOptions | sym::DirBuilder), sym::mode) + | (Some(sym::FsPermissions), sym::set_mode) + ) && let ExprKind::Lit(_) = param.kind && param.span.eq_ctxt(expr.span) && param diff --git a/clippy_lints/src/operators/assign_op_pattern.rs b/clippy_lints/src/operators/assign_op_pattern.rs index 7317c62df7fe..2d303e40bd1c 100644 --- a/clippy_lints/src/operators/assign_op_pattern.rs +++ b/clippy_lints/src/operators/assign_op_pattern.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::Msrv; +use clippy_utils::qualify_min_const_fn::is_stable_const_fn; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::implements_trait; use clippy_utils::visitors::for_each_expr_without_closures; @@ -20,7 +21,7 @@ pub(super) fn check<'tcx>( expr: &'tcx hir::Expr<'_>, assignee: &'tcx hir::Expr<'_>, e: &'tcx hir::Expr<'_>, - _msrv: Msrv, + msrv: Msrv, ) { if let hir::ExprKind::Binary(op, l, r) = &e.kind { let lint = |assignee: &hir::Expr<'_>, rhs: &hir::Expr<'_>| { @@ -43,10 +44,28 @@ pub(super) fn check<'tcx>( } } - // Skip if the trait is not stable in const contexts - // FIXME: reintroduce a better check after this is merged back into Clippy + // Skip if the trait or the implementation is not stable in const contexts if is_in_const_context(cx) { - return; + if cx + .tcx + .associated_item_def_ids(trait_id) + .first() + .is_none_or(|binop_id| !is_stable_const_fn(cx, *binop_id, msrv)) + { + return; + } + + let impls = cx.tcx.non_blanket_impls_for_ty(trait_id, rty).collect::>(); + if impls.is_empty() + || impls.into_iter().any(|impl_id| { + cx.tcx + .associated_item_def_ids(impl_id) + .first() + .is_none_or(|fn_id| !is_stable_const_fn(cx, *fn_id, msrv)) + }) + { + return; + } } span_lint_and_then( diff --git a/clippy_lints/src/operators/cmp_owned.rs b/clippy_lints/src/operators/cmp_owned.rs index 22ec4fe60fb0..604f8f5da0b8 100644 --- a/clippy_lints/src/operators/cmp_owned.rs +++ b/clippy_lints/src/operators/cmp_owned.rs @@ -47,14 +47,10 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) (arg, arg.span) }, ExprKind::Call(path, [arg]) - if path_def_id(cx, path).is_some_and(|did| { - if cx.tcx.is_diagnostic_item(sym::from_str_method, did) { - true - } else if cx.tcx.is_diagnostic_item(sym::from_fn, did) { - !is_copy(cx, typeck.expr_ty(expr)) - } else { - false - } + if path_def_id(cx, path).is_some_and(|did| match cx.tcx.get_diagnostic_name(did) { + Some(sym::from_str_method) => true, + Some(sym::from_fn) => !is_copy(cx, typeck.expr_ty(expr)), + _ => false, }) => { (arg, arg.span) diff --git a/clippy_lints/src/operators/float_equality_without_abs.rs b/clippy_lints/src/operators/float_equality_without_abs.rs index 047a5a0159cb..17fa8017c978 100644 --- a/clippy_lints/src/operators/float_equality_without_abs.rs +++ b/clippy_lints/src/operators/float_equality_without_abs.rs @@ -34,10 +34,11 @@ pub(crate) fn check<'tcx>( val_r, ) = lhs.kind - // right hand side matches either f32::EPSILON or f64::EPSILON + // right hand side matches _::EPSILON && let ExprKind::Path(ref epsilon_path) = rhs.kind && let Res::Def(DefKind::AssocConst, def_id) = cx.qpath_res(epsilon_path, rhs.hir_id) - && ([sym::f32_epsilon, sym::f64_epsilon].into_iter().any(|sym| cx.tcx.is_diagnostic_item(sym, def_id))) + && let Some(sym) = cx.tcx.get_diagnostic_name(def_id) + && matches!(sym, sym::f16_epsilon | sym::f32_epsilon | sym::f64_epsilon | sym::f128_epsilon) // values of the subtractions on the left hand side are of the type float && let t_val_l = cx.typeck_results().expr_ty(val_l) diff --git a/clippy_lints/src/panic_unimplemented.rs b/clippy_lints/src/panic_unimplemented.rs index 449d3da76394..43db0085f2e3 100644 --- a/clippy_lints/src/panic_unimplemented.rs +++ b/clippy_lints/src/panic_unimplemented.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_in_test; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; +use clippy_utils::{is_in_test, is_inside_always_const_context}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; @@ -99,7 +99,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let Some(macro_call) = root_macro_call_first_node(cx, expr) { if is_panic(cx, macro_call.def_id) { - if cx.tcx.hir_is_inside_const_context(expr.hir_id) + if is_inside_always_const_context(cx.tcx, expr.hir_id) || self.allow_panic_in_tests && is_in_test(cx.tcx, expr.hir_id) { return; @@ -140,7 +140,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { && let Res::Def(DefKind::Fn, def_id) = expr_path.res && cx.tcx.is_diagnostic_item(sym::panic_any, def_id) { - if cx.tcx.hir_is_inside_const_context(expr.hir_id) + if is_inside_always_const_context(cx.tcx, expr.hir_id) || self.allow_panic_in_tests && is_in_test(cx.tcx, expr.hir_id) { return; diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index e57b8cc2d84e..1d58cdd26d88 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::fn_has_unsatisfiable_preds; use clippy_utils::mir::{LocalUsage, PossibleBorrowerMap, visit_local_usage}; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, is_type_lang_item, walk_ptrs_ty_depth}; +use clippy_utils::ty::{has_drop, is_copy, is_type_lang_item, walk_ptrs_ty_depth}; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, LangItem, def_id}; @@ -96,14 +96,13 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { let (fn_def_id, arg, arg_ty, clone_ret) = unwrap_or_continue!(is_call_with_ref_arg(cx, mir, &terminator.kind)); - let from_borrow = cx.tcx.lang_items().get(LangItem::CloneFn) == Some(fn_def_id) - || cx.tcx.is_diagnostic_item(sym::to_owned_method, fn_def_id) - || (cx.tcx.is_diagnostic_item(sym::to_string_method, fn_def_id) - && is_type_lang_item(cx, arg_ty, LangItem::String)); + let fn_name = cx.tcx.get_diagnostic_name(fn_def_id); - let from_deref = !from_borrow - && (cx.tcx.is_diagnostic_item(sym::path_to_pathbuf, fn_def_id) - || cx.tcx.is_diagnostic_item(sym::os_str_to_os_string, fn_def_id)); + let from_borrow = cx.tcx.lang_items().get(LangItem::CloneFn) == Some(fn_def_id) + || fn_name == Some(sym::to_owned_method) + || (fn_name == Some(sym::to_string_method) && is_type_lang_item(cx, arg_ty, LangItem::String)); + + let from_deref = !from_borrow && matches!(fn_name, Some(sym::path_to_pathbuf | sym::os_str_to_os_string)); if !from_borrow && !from_deref { continue; @@ -148,8 +147,9 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { is_call_with_ref_arg(cx, mir, &pred_terminator.kind) && res == cloned && cx.tcx.is_diagnostic_item(sym::deref_method, pred_fn_def_id) - && (is_type_diagnostic_item(cx, pred_arg_ty, sym::PathBuf) - || is_type_diagnostic_item(cx, pred_arg_ty, sym::OsString)) + && let ty::Adt(pred_arg_def, _) = pred_arg_ty.kind() + && let Some(pred_arg_name) = cx.tcx.get_diagnostic_name(pred_arg_def.did()) + && matches!(pred_arg_name, sym::PathBuf | sym::OsString) { (pred_arg, res) } else { diff --git a/clippy_lints/src/semicolon_block.rs b/clippy_lints/src/semicolon_block.rs index db91c57b1816..1dea8f17c34b 100644 --- a/clippy_lints/src/semicolon_block.rs +++ b/clippy_lints/src/semicolon_block.rs @@ -155,6 +155,11 @@ impl LateLintPass<'_> for SemicolonBlock { kind: ExprKind::Block(block, _), .. }) if !block.span.from_expansion() => { + let attrs = cx.tcx.hir_attrs(stmt.hir_id); + if !attrs.is_empty() && !cx.tcx.features().stmt_expr_attributes() { + return; + } + if let Some(tail) = block.expr { self.semicolon_inside_block(cx, block, tail, stmt.span); } diff --git a/clippy_lints/src/std_instead_of_core.rs b/clippy_lints/src/std_instead_of_core.rs index e9534bc63a69..8c4a50041e67 100644 --- a/clippy_lints/src/std_instead_of_core.rs +++ b/clippy_lints/src/std_instead_of_core.rs @@ -126,6 +126,8 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports { && !is_from_proc_macro(cx, &first_segment.ident) && !matches!(def_kind, DefKind::Macro(_)) && let Some(last_segment) = path.segments.last() + && let Res::Def(DefKind::Mod, crate_def_id) = first_segment.res + && crate_def_id.is_crate_root() { let (lint, used_mod, replace_with) = match first_segment.ident.name { sym::std => match cx.tcx.crate_name(def_id.krate) { diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 490e6c974ae1..57d5900b045e 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -457,7 +457,8 @@ impl<'tcx> LateLintPass<'tcx> for TrimSplitWhitespace { } fn is_one_of_trim_diagnostic_items(cx: &LateContext<'_>, trim_def_id: DefId) -> bool { - cx.tcx.is_diagnostic_item(sym::str_trim, trim_def_id) - || cx.tcx.is_diagnostic_item(sym::str_trim_start, trim_def_id) - || cx.tcx.is_diagnostic_item(sym::str_trim_end, trim_def_id) + matches!( + cx.tcx.get_diagnostic_name(trim_def_id), + Some(sym::str_trim | sym::str_trim_start | sym::str_trim_end) + ) } diff --git a/clippy_lints/src/transmute/missing_transmute_annotations.rs b/clippy_lints/src/transmute/missing_transmute_annotations.rs index 08f36a2ed5d2..543f3c45e146 100644 --- a/clippy_lints/src/transmute/missing_transmute_annotations.rs +++ b/clippy_lints/src/transmute/missing_transmute_annotations.rs @@ -1,8 +1,12 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use std::borrow::Cow; + +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::{HasSession, SpanRangeExt as _}; use rustc_errors::Applicability; -use rustc_hir::{GenericArg, HirId, LetStmt, Node, Path, TyKind}; +use rustc_hir::{Expr, GenericArg, HirId, LetStmt, Node, Path, TyKind}; use rustc_lint::LateContext; -use rustc_middle::ty::Ty; +use rustc_middle::ty::{self, Ty}; +use rustc_span::Span; use crate::transmute::MISSING_TRANSMUTE_ANNOTATIONS; @@ -38,6 +42,7 @@ fn is_function_block(cx: &LateContext<'_>, expr_hir_id: HirId) -> bool { pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, path: &Path<'tcx>, + arg: &Expr<'tcx>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>, expr_hir_id: HirId, @@ -68,14 +73,48 @@ pub(super) fn check<'tcx>( } else if is_function_block(cx, expr_hir_id) { return false; } - span_lint_and_sugg( + let span = last.ident.span.with_hi(path.span.hi()); + span_lint_and_then( cx, MISSING_TRANSMUTE_ANNOTATIONS, - last.ident.span.with_hi(path.span.hi()), + span, "transmute used without annotations", - "consider adding missing annotations", - format!("{}::<{from_ty}, {to_ty}>", last.ident), - Applicability::MaybeIncorrect, + |diag| { + let from_ty_no_name = ty_cannot_be_named(from_ty); + let to_ty_no_name = ty_cannot_be_named(to_ty); + if from_ty_no_name || to_ty_no_name { + let to_name = match (from_ty_no_name, to_ty_no_name) { + (true, false) => maybe_name_by_expr(cx, arg.span, "the origin type"), + (false, true) => "the destination type".into(), + _ => "the source and destination types".into(), + }; + diag.help(format!( + "consider giving {to_name} a name, and adding missing type annotations" + )); + } else { + diag.span_suggestion( + span, + "consider adding missing annotations", + format!("{}::<{from_ty}, {to_ty}>", last.ident), + Applicability::MaybeIncorrect, + ); + } + }, ); true } + +fn ty_cannot_be_named(ty: Ty<'_>) -> bool { + matches!( + ty.kind(), + ty::Alias(ty::AliasTyKind::Opaque | ty::AliasTyKind::Inherent, _) + ) +} + +fn maybe_name_by_expr<'a>(sess: &impl HasSession, span: Span, default: &'a str) -> Cow<'a, str> { + span.with_source_text(sess, |name| { + (name.len() + 9 < default.len()).then_some(format!("`{name}`'s type").into()) + }) + .flatten() + .unwrap_or(default.into()) +} diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index 1c7bb4314ddf..5fda388259a6 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -520,7 +520,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { | transmuting_null::check(cx, e, arg, to_ty) | transmute_null_to_fn::check(cx, e, arg, to_ty) | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv) - | missing_transmute_annotations::check(cx, path, from_ty, to_ty, e.hir_id) + | missing_transmute_annotations::check(cx, path, arg, from_ty, to_ty, e.hir_id) | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg, self.msrv) | transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg) diff --git a/clippy_lints/src/transmute/transmute_int_to_non_zero.rs b/clippy_lints/src/transmute/transmute_int_to_non_zero.rs index f27aaa2fa77a..2257aa1b73c8 100644 --- a/clippy_lints/src/transmute/transmute_int_to_non_zero.rs +++ b/clippy_lints/src/transmute/transmute_int_to_non_zero.rs @@ -1,6 +1,6 @@ use super::TRANSMUTE_INT_TO_NON_ZERO; -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::sugg; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::sugg::Sugg; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -16,35 +16,24 @@ pub(super) fn check<'tcx>( to_ty: Ty<'tcx>, arg: &'tcx Expr<'_>, ) -> bool { - let tcx = cx.tcx; - - let (ty::Int(_) | ty::Uint(_), ty::Adt(adt, substs)) = (&from_ty.kind(), to_ty.kind()) else { - return false; - }; - - if !tcx.is_diagnostic_item(sym::NonZero, adt.did()) { - return false; + if let ty::Int(_) | ty::Uint(_) = from_ty.kind() + && let ty::Adt(adt, substs) = to_ty.kind() + && cx.tcx.is_diagnostic_item(sym::NonZero, adt.did()) + && let int_ty = substs.type_at(0) + && from_ty == int_ty + { + let arg = Sugg::hir(cx, arg, ".."); + span_lint_and_sugg( + cx, + TRANSMUTE_INT_TO_NON_ZERO, + e.span, + format!("transmute from a `{from_ty}` to a `{}<{int_ty}>`", sym::NonZero), + "consider using", + format!("{}::{}({arg})", sym::NonZero, sym::new_unchecked), + Applicability::Unspecified, + ); + true + } else { + false } - - let int_ty = substs.type_at(0); - if from_ty != int_ty { - return false; - } - - span_lint_and_then( - cx, - TRANSMUTE_INT_TO_NON_ZERO, - e.span, - format!("transmute from a `{from_ty}` to a `{}<{int_ty}>`", sym::NonZero), - |diag| { - let arg = sugg::Sugg::hir(cx, arg, ".."); - diag.span_suggestion( - e.span, - "consider using", - format!("{}::{}({arg})", sym::NonZero, sym::new_unchecked), - Applicability::Unspecified, - ); - }, - ); - true } diff --git a/clippy_lints/src/types/rc_buffer.rs b/clippy_lints/src/types/rc_buffer.rs index d691f1878b11..c4fd0fbf87a9 100644 --- a/clippy_lints/src/types/rc_buffer.rs +++ b/clippy_lints/src/types/rc_buffer.rs @@ -11,7 +11,8 @@ use super::RC_BUFFER; pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { let app = Applicability::Unspecified; - if cx.tcx.is_diagnostic_item(sym::Rc, def_id) { + let name = cx.tcx.get_diagnostic_name(def_id); + if name == Some(sym::Rc) { if let Some(alternate) = match_buffer_type(cx, qpath) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then( @@ -56,7 +57,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ ); return true; } - } else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) { + } else if name == Some(sym::Arc) { if let Some(alternate) = match_buffer_type(cx, qpath) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then( diff --git a/clippy_lints/src/types/redundant_allocation.rs b/clippy_lints/src/types/redundant_allocation.rs index de3456a8ba5f..0ba51daf027d 100644 --- a/clippy_lints/src/types/redundant_allocation.rs +++ b/clippy_lints/src/types/redundant_allocation.rs @@ -13,14 +13,11 @@ use super::{REDUNDANT_ALLOCATION, utils}; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>, qpath: &QPath<'tcx>, def_id: DefId) -> bool { let mut applicability = Applicability::MaybeIncorrect; - let outer_sym = if Some(def_id) == cx.tcx.lang_items().owned_box() { - "Box" - } else if cx.tcx.is_diagnostic_item(sym::Rc, def_id) { - "Rc" - } else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) { - "Arc" - } else { - return false; + let outer_sym = match cx.tcx.get_diagnostic_name(def_id) { + _ if Some(def_id) == cx.tcx.lang_items().owned_box() => "Box", + Some(sym::Rc) => "Rc", + Some(sym::Arc) => "Arc", + _ => return false, }; if let Some(span) = utils::match_borrows_parameter(cx, qpath) { diff --git a/clippy_lints/src/unit_types/unit_cmp.rs b/clippy_lints/src/unit_types/unit_cmp.rs index 48b532968cb5..38716519e23b 100644 --- a/clippy_lints/src/unit_types/unit_cmp.rs +++ b/clippy_lints/src/unit_types/unit_cmp.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; -use clippy_utils::sym; +use clippy_utils::{is_unit_expr, sym}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; @@ -16,10 +16,13 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { sym::assert_ne_macro | sym::debug_assert_ne_macro => "fail", _ => return, }; - let Some((left, _, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { + let Some((lhs, rhs, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return; }; - if !cx.typeck_results().expr_ty(left).is_unit() { + if is_unit_expr(lhs) || is_unit_expr(rhs) { + return; + } + if !cx.typeck_results().expr_ty(lhs).is_unit() { return; } span_lint( diff --git a/clippy_lints/src/unnecessary_owned_empty_strings.rs b/clippy_lints/src/unnecessary_owned_empty_strings.rs index 7d996775a58c..28f4884fa311 100644 --- a/clippy_lints/src/unnecessary_owned_empty_strings.rs +++ b/clippy_lints/src/unnecessary_owned_empty_strings.rs @@ -41,7 +41,8 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings { && let ty::Ref(_, inner_str, _) = cx.typeck_results().expr_ty_adjusted(expr).kind() && inner_str.is_str() { - if cx.tcx.is_diagnostic_item(sym::string_new, fun_def_id) { + let fun_name = cx.tcx.get_diagnostic_name(fun_def_id); + if fun_name == Some(sym::string_new) { span_lint_and_sugg( cx, UNNECESSARY_OWNED_EMPTY_STRINGS, @@ -51,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings { "\"\"".to_owned(), Applicability::MachineApplicable, ); - } else if cx.tcx.is_diagnostic_item(sym::from_fn, fun_def_id) + } else if fun_name == Some(sym::from_fn) && let [arg] = args && let ExprKind::Lit(spanned) = &arg.kind && let LitKind::Str(symbol, _) = spanned.node diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 54a7efc090ad..849c0b438a5a 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -107,12 +107,10 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps { // Get the wrapper and inner types, if can't, abort. let (return_type_label, lang_item, inner_type) = if let ty::Adt(adt_def, subst) = return_ty(cx, hir_id.expect_owner()).kind() { - if cx.tcx.is_diagnostic_item(sym::Option, adt_def.did()) { - ("Option", OptionSome, subst.type_at(0)) - } else if cx.tcx.is_diagnostic_item(sym::Result, adt_def.did()) { - ("Result", ResultOk, subst.type_at(0)) - } else { - return; + match cx.tcx.get_diagnostic_name(adt_def.did()) { + Some(sym::Option) => ("Option", OptionSome, subst.type_at(0)), + Some(sym::Result) => ("Result", ResultOk, subst.type_at(0)), + _ => return, } } else { return; diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index 8b278d98a30e..f3410c98973f 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -326,7 +326,11 @@ fn extend_with_struct_pat( if idx_1 == idx { // In the case of `k`, we merely require identical field names // so that we will transform into `ident_k: p1_k | p2_k`. - let pos = fps2.iter().position(|fp2| eq_id(fp1.ident, fp2.ident)); + let pos = fps2.iter().position(|fp2| { + // Avoid `Foo { bar } | Foo { bar }` => `Foo { bar | bar }` + !(fp1.is_shorthand && fp2.is_shorthand) + && eq_id(fp1.ident, fp2.ident) + }); pos_in_2.set(pos); pos.is_some() } else { diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index f3cd3f1bb286..af3ad4566c46 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -89,7 +89,9 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { { // We don't want to lint inside io::Read or io::Write implementations, as the author has more // information about their trait implementation than our lint, see https://github.com/rust-lang/rust-clippy/issues/4836 - if cx.tcx.is_diagnostic_item(sym::IoRead, trait_id) || cx.tcx.is_diagnostic_item(sym::IoWrite, trait_id) { + if let Some(trait_name) = cx.tcx.get_diagnostic_name(trait_id) + && matches!(trait_name, sym::IoRead | sym::IoWrite) + { return; } diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs index 3811f0fe6b55..d503bd3379b0 100644 --- a/clippy_lints/src/unused_unit.rs +++ b/clippy_lints/src/unused_unit.rs @@ -6,13 +6,13 @@ use rustc_errors::Applicability; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::FnKind; use rustc_hir::{ - AssocItemConstraintKind, Body, Expr, ExprKind, FnDecl, FnRetTy, GenericArgsParentheses, Node, PolyTraitRef, Term, - Ty, TyKind, + AssocItemConstraintKind, Body, Expr, ExprKind, FnDecl, FnRetTy, GenericArgsParentheses, PolyTraitRef, Term, Ty, + TyKind, }; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::edition::Edition; -use rustc_span::{BytePos, Span, sym}; +use rustc_span::{BytePos, Pos as _, Span, sym}; declare_clippy_lint! { /// ### What it does @@ -49,19 +49,22 @@ impl<'tcx> LateLintPass<'tcx> for UnusedUnit { decl: &'tcx FnDecl<'tcx>, body: &'tcx Body<'tcx>, span: Span, - def_id: LocalDefId, + _def_id: LocalDefId, ) { if let FnRetTy::Return(hir_ty) = decl.output && is_unit_ty(hir_ty) && !hir_ty.span.from_expansion() && get_def(span) == get_def(hir_ty.span) { - // implicit types in closure signatures are forbidden when `for<...>` is present - if let FnKind::Closure = kind - && let Node::Expr(expr) = cx.tcx.hir_node_by_def_id(def_id) - && let ExprKind::Closure(closure) = expr.kind - && !closure.bound_generic_params.is_empty() - { + // The explicit `-> ()` in the closure signature might be necessary for multiple reasons: + // - Implicit types in closure signatures are forbidden when `for<...>` is present + // - If the closure body ends with a function call, and that function's return type is generic, the + // `-> ()` could be required for it to be inferred + // + // There could be more reasons to have it, and, in general, we shouldn't discourage the users from + // writing more type annotations than strictly necessary, because it can help readability and + // maintainability + if let FnKind::Closure = kind { return; } @@ -97,16 +100,13 @@ impl<'tcx> LateLintPass<'tcx> for UnusedUnit { } fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly: &'tcx PolyTraitRef<'tcx>) { - let segments = &poly.trait_ref.path.segments; - - if segments.len() == 1 - && matches!(segments[0].ident.name, sym::Fn | sym::FnMut | sym::FnOnce) - && let Some(args) = segments[0].args + if let [segment] = &poly.trait_ref.path.segments + && matches!(segment.ident.name, sym::Fn | sym::FnMut | sym::FnOnce) + && let Some(args) = segment.args && args.parenthesized == GenericArgsParentheses::ParenSugar - && let constraints = &args.constraints - && constraints.len() == 1 - && constraints[0].ident.name == sym::Output - && let AssocItemConstraintKind::Equality { term: Term::Ty(hir_ty) } = constraints[0].kind + && let [constraint] = &args.constraints + && constraint.ident.name == sym::Output + && let AssocItemConstraintKind::Equality { term: Term::Ty(hir_ty) } = constraint.kind && args.span_ext.hi() != poly.span.hi() && !hir_ty.span.from_expansion() && args.span_ext.hi() != hir_ty.span.hi() @@ -160,17 +160,15 @@ fn get_def(span: Span) -> Option { fn lint_unneeded_unit_return(cx: &LateContext<'_>, ty_span: Span, span: Span) { let (ret_span, appl) = - span.with_hi(ty_span.hi()) - .get_source_text(cx) - .map_or((ty_span, Applicability::MaybeIncorrect), |src| { - position_before_rarrow(&src).map_or((ty_span, Applicability::MaybeIncorrect), |rpos| { - ( - #[expect(clippy::cast_possible_truncation)] - ty_span.with_lo(BytePos(span.lo().0 + rpos as u32)), - Applicability::MachineApplicable, - ) - }) - }); + if let Some(Some(rpos)) = span.with_hi(ty_span.hi()).with_source_text(cx, position_before_rarrow) { + ( + ty_span.with_lo(span.lo() + BytePos::from_usize(rpos)), + Applicability::MachineApplicable, + ) + } else { + (ty_span, Applicability::MaybeIncorrect) + }; + span_lint_and_sugg( cx, UNUSED_UNIT, diff --git a/clippy_lints/src/unwrap_in_result.rs b/clippy_lints/src/unwrap_in_result.rs index 7bec212a23ca..f26647fa3485 100644 --- a/clippy_lints/src/unwrap_in_result.rs +++ b/clippy_lints/src/unwrap_in_result.rs @@ -1,13 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::visitors::for_each_expr; -use clippy_utils::{method_chain_args, return_ty}; -use core::ops::ControlFlow; -use rustc_hir as hir; -use rustc_hir::ImplItemKind; +use clippy_utils::{return_ty, sym}; +use rustc_hir::{ + Body, BodyOwnerKind, Expr, ExprKind, FnSig, ImplItem, ImplItemKind, Item, ItemKind, OwnerId, PathSegment, QPath, +}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; -use rustc_span::{Span, sym}; +use rustc_middle::ty::Ty; +use rustc_session::impl_lint_pass; +use rustc_span::{Ident, Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -57,62 +56,165 @@ declare_clippy_lint! { "functions of type `Result<..>` or `Option`<...> that contain `expect()` or `unwrap()`" } -declare_lint_pass!(UnwrapInResult=> [UNWRAP_IN_RESULT]); +impl_lint_pass!(UnwrapInResult=> [UNWRAP_IN_RESULT]); -impl<'tcx> LateLintPass<'tcx> for UnwrapInResult { - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { - if let ImplItemKind::Fn(ref _signature, _) = impl_item.kind - // first check if it's a method or function - // checking if its return type is `result` or `option` - && (is_type_diagnostic_item(cx, return_ty(cx, impl_item.owner_id), sym::Result) - || is_type_diagnostic_item(cx, return_ty(cx, impl_item.owner_id), sym::Option)) - { - lint_impl_body(cx, impl_item.span, impl_item); +#[derive(Clone, Copy, Eq, PartialEq)] +enum OptionOrResult { + Option, + Result, +} + +impl OptionOrResult { + fn with_article(self) -> &'static str { + match self { + Self::Option => "an `Option`", + Self::Result => "a `Result`", } } } -fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tcx hir::ImplItem<'_>) { - if let ImplItemKind::Fn(_, body_id) = impl_item.kind { - let body = cx.tcx.hir_body(body_id); - let typeck = cx.tcx.typeck(impl_item.owner_id.def_id); - let mut result = Vec::new(); - let _: Option = for_each_expr(cx, body.value, |e| { - // check for `expect` - if let Some(arglists) = method_chain_args(e, &[sym::expect]) { - let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs(); - if is_type_diagnostic_item(cx, receiver_ty, sym::Option) - || is_type_diagnostic_item(cx, receiver_ty, sym::Result) - { - result.push(e.span); - } - } +struct OptionOrResultFn { + kind: OptionOrResult, + return_ty_span: Option, +} - // check for `unwrap` - if let Some(arglists) = method_chain_args(e, &[sym::unwrap]) { - let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs(); - if is_type_diagnostic_item(cx, receiver_ty, sym::Option) - || is_type_diagnostic_item(cx, receiver_ty, sym::Result) - { - result.push(e.span); - } - } +#[derive(Default)] +pub struct UnwrapInResult { + fn_stack: Vec>, + current_fn: Option, +} - ControlFlow::Continue(()) +impl UnwrapInResult { + fn enter_item(&mut self, cx: &LateContext<'_>, fn_def_id: OwnerId, sig: &FnSig<'_>) { + self.fn_stack.push(self.current_fn.take()); + self.current_fn = is_option_or_result(cx, return_ty(cx, fn_def_id)).map(|kind| OptionOrResultFn { + kind, + return_ty_span: Some(sig.decl.output.span()), }); + } - // if we've found one, lint - if !result.is_empty() { + fn leave_item(&mut self) { + self.current_fn = self.fn_stack.pop().unwrap(); + } +} + +impl<'tcx> LateLintPass<'tcx> for UnwrapInResult { + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { + if let ImplItemKind::Fn(sig, _) = &impl_item.kind { + self.enter_item(cx, impl_item.owner_id, sig); + } + } + + fn check_impl_item_post(&mut self, _: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'tcx>) { + if let ImplItemKind::Fn(..) = impl_item.kind { + self.leave_item(); + } + } + + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if let ItemKind::Fn { + has_body: true, sig, .. + } = &item.kind + { + self.enter_item(cx, item.owner_id, sig); + } + } + + fn check_item_post(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if let ItemKind::Fn { has_body: true, .. } = item.kind { + self.leave_item(); + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { + if expr.span.from_expansion() { + return; + } + + if let Some(OptionOrResultFn { + kind, + ref mut return_ty_span, + }) = self.current_fn + && let Some((oor, fn_name)) = is_unwrap_or_expect_call(cx, expr) + && oor == kind + { span_lint_and_then( cx, UNWRAP_IN_RESULT, - impl_span, - "used unwrap or expect in a function that returns result or option", - move |diag| { - diag.help("unwrap and expect should not be used in a function that returns result or option"); - diag.span_note(result, "potential non-recoverable error(s)"); + expr.span, + format!("`{fn_name}` used in a function that returns {}", kind.with_article()), + |diag| { + // Issue the note and help only once per function + if let Some(span) = return_ty_span.take() { + diag.span_note(span, "in this function signature"); + let complement = if kind == OptionOrResult::Result { + " or calling the `.map_err()` method" + } else { + "" + }; + diag.help(format!("consider using the `?` operator{complement}")); + } }, ); } } + + fn check_body(&mut self, cx: &LateContext<'tcx>, body: &Body<'tcx>) { + let body_def_id = cx.tcx.hir_body_owner_def_id(body.id()); + if !matches!(cx.tcx.hir_body_owner_kind(body_def_id), BodyOwnerKind::Fn) { + // When entering a body which is not a function, mask the potential surrounding + // function to not apply the lint. + self.fn_stack.push(self.current_fn.take()); + } + } + + fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &Body<'tcx>) { + let body_def_id = cx.tcx.hir_body_owner_def_id(body.id()); + if !matches!(cx.tcx.hir_body_owner_kind(body_def_id), BodyOwnerKind::Fn) { + // Unmask the potential surrounding function. + self.current_fn = self.fn_stack.pop().unwrap(); + } + } +} + +fn is_option_or_result(cx: &LateContext<'_>, ty: Ty<'_>) -> Option { + match ty.ty_adt_def().and_then(|def| cx.tcx.get_diagnostic_name(def.did())) { + Some(sym::Option) => Some(OptionOrResult::Option), + Some(sym::Result) => Some(OptionOrResult::Result), + _ => None, + } +} + +fn is_unwrap_or_expect_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<(OptionOrResult, Symbol)> { + if let ExprKind::Call(func, _) = expr.kind + && let ExprKind::Path(QPath::TypeRelative( + hir_ty, + PathSegment { + ident: + Ident { + name: name @ (sym::unwrap | sym::expect), + .. + }, + .. + }, + )) = func.kind + { + is_option_or_result(cx, cx.typeck_results().node_type(hir_ty.hir_id)).map(|oor| (oor, *name)) + } else if let ExprKind::MethodCall( + PathSegment { + ident: Ident { + name: name @ (sym::unwrap | sym::expect), + .. + }, + .. + }, + recv, + _, + _, + ) = expr.kind + { + is_option_or_result(cx, cx.typeck_results().expr_ty_adjusted(recv)).map(|oor| (oor, *name)) + } else { + None + } } diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index e5b20c0e0a13..70ae982a4458 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -386,12 +386,13 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { ExprKind::Call(path, [arg]) => { if let ExprKind::Path(ref qpath) = path.kind - && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() && !is_ty_alias(qpath) + && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() + && let Some(name) = cx.tcx.get_diagnostic_name(def_id) { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(arg); - if cx.tcx.is_diagnostic_item(sym::try_from_fn, def_id) + if name == sym::try_from_fn && is_type_diagnostic_item(cx, a, sym::Result) && let ty::Adt(_, args) = a.kind() && let Some(a_type) = args.types().next() @@ -406,9 +407,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { None, hint, ); - } - - if cx.tcx.is_diagnostic_item(sym::from_fn, def_id) && same_type_and_consts(a, b) { + } else if name == sym::from_fn && same_type_and_consts(a, b) { let mut app = Applicability::MachineApplicable; let sugg = Sugg::hir_with_context(cx, arg, e.span.ctxt(), "", &mut app).maybe_paren(); let sugg_msg = format!("consider removing `{}()`", snippet(cx, path.span, "From::from")); diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index 7b6a25123e85..52b30ddce12b 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -1,4 +1,6 @@ use std::collections::BTreeMap; +use std::collections::btree_map::Entry; +use std::mem; use std::ops::ControlFlow; use clippy_config::Conf; @@ -20,15 +22,36 @@ use rustc_span::{DesugaringKind, Span}; pub struct UselessVec { too_large_for_stack: u64, msrv: Msrv, - span_to_lint_map: BTreeMap>, + /// Maps from a `vec![]` source callsite invocation span to the "state" (i.e., whether we can + /// emit a warning there or not). + /// + /// The purpose of this is to buffer lints up until `check_crate_post` so that we can cancel a + /// lint while visiting, because a `vec![]` invocation span can appear multiple times when + /// it is passed as a macro argument, once in a context that doesn't require a `Vec<_>` and + /// another time that does. Consider: + /// ``` + /// macro_rules! m { + /// ($v:expr) => { + /// let a = $v; + /// $v.push(3); + /// } + /// } + /// m!(vec![1, 2]); + /// ``` + /// The macro invocation expands to two `vec![1, 2]` invocations. If we eagerly suggest changing + /// the first `vec![1, 2]` (which is shared with the other expn) to an array which indeed would + /// work, we get a false positive warning on the `$v.push(3)` which really requires `$v` to + /// be a vector. + span_to_state: BTreeMap, allow_in_test: bool, } + impl UselessVec { pub fn new(conf: &'static Conf) -> Self { Self { too_large_for_stack: conf.too_large_for_stack, msrv: conf.msrv, - span_to_lint_map: BTreeMap::new(), + span_to_state: BTreeMap::new(), allow_in_test: conf.allow_useless_vec_in_tests, } } @@ -62,17 +85,28 @@ declare_clippy_lint! { impl_lint_pass!(UselessVec => [USELESS_VEC]); -impl<'tcx> LateLintPass<'tcx> for UselessVec { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let Some(vec_args) = higher::VecArgs::hir(cx, expr.peel_borrows()) else { - return; - }; - if self.allow_in_test && is_in_test(cx.tcx, expr.hir_id) { - return; - } - // the parent callsite of this `vec!` expression, or span to the borrowed one such as `&vec!` - let callsite = expr.span.parent_callsite().unwrap_or(expr.span); +/// The "state" of a `vec![]` invocation, indicating whether it can or cannot be changed. +enum VecState { + Change { + suggest_ty: SuggestedType, + vec_snippet: String, + expr_hir_id: HirId, + }, + NoChange, +} +enum VecToArray { + /// Expression does not need to be a `Vec<_>` and its type can be changed to an array (or + /// slice). + Possible, + /// Expression must be a `Vec<_>`. Type cannot change. + Impossible, +} + +impl UselessVec { + /// Checks if the surrounding environment requires this expression to actually be of type + /// `Vec<_>`, or if it can be changed to `&[]`/`[]` without causing type errors. + fn expr_usage_requires_vec(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) -> VecToArray { match cx.tcx.parent_hir_node(expr.hir_id) { // search for `let foo = vec![_]` expressions where all uses of `foo` // adjust to slices or call a method that exist on slices (e.g. len) @@ -100,107 +134,123 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { .is_continue(); if only_slice_uses { - self.check_vec_macro(cx, &vec_args, callsite, expr.hir_id, SuggestedType::Array); + VecToArray::Possible } else { - self.span_to_lint_map.insert(callsite, None); + VecToArray::Impossible } }, // if the local pattern has a specified type, do not lint. Node::LetStmt(LetStmt { ty: Some(_), .. }) if higher::VecArgs::hir(cx, expr).is_some() => { - self.span_to_lint_map.insert(callsite, None); + VecToArray::Impossible }, // search for `for _ in vec![...]` Node::Expr(Expr { span, .. }) if span.is_desugaring(DesugaringKind::ForLoop) && self.msrv.meets(cx, msrvs::ARRAY_INTO_ITERATOR) => { - let suggest_slice = suggest_type(expr); - self.check_vec_macro(cx, &vec_args, callsite, expr.hir_id, suggest_slice); + VecToArray::Possible }, // search for `&vec![_]` or `vec![_]` expressions where the adjusted type is `&[_]` _ => { - let suggest_slice = suggest_type(expr); - if adjusts_to_slice(cx, expr) { - self.check_vec_macro(cx, &vec_args, callsite, expr.hir_id, suggest_slice); + VecToArray::Possible } else { - self.span_to_lint_map.insert(callsite, None); + VecToArray::Impossible } }, } } - - fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { - for (span, lint_opt) in &self.span_to_lint_map { - if let Some((hir_id, suggest_slice, snippet, applicability)) = lint_opt { - let help_msg = format!("you can use {} directly", suggest_slice.desc()); - span_lint_hir_and_then(cx, USELESS_VEC, *hir_id, *span, "useless use of `vec!`", |diag| { - // If the `vec!` macro contains comment, better not make the suggestion machine - // applicable as it would remove them. - let applicability = if *applicability != Applicability::Unspecified - && let source_map = cx.tcx.sess.source_map() - && span_contains_comment(source_map, *span) - { - Applicability::Unspecified - } else { - *applicability - }; - diag.span_suggestion(*span, help_msg, snippet, applicability); - }); - } - } - } } -impl UselessVec { - fn check_vec_macro<'tcx>( - &mut self, - cx: &LateContext<'tcx>, - vec_args: &higher::VecArgs<'tcx>, - span: Span, - hir_id: HirId, - suggest_slice: SuggestedType, - ) { - if span.from_expansion() { - return; +impl<'tcx> LateLintPass<'tcx> for UselessVec { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let Some(vec_args) = higher::VecArgs::hir(cx, expr.peel_borrows()) + // The `vec![]` or `&vec![]` invocation span. + && let vec_span = expr.span.parent_callsite().unwrap_or(expr.span) + && !vec_span.from_expansion() + { + if self.allow_in_test && is_in_test(cx.tcx, expr.hir_id) { + return; + } + + match self.expr_usage_requires_vec(cx, expr) { + VecToArray::Possible => { + let suggest_ty = suggest_type(expr); + + // Size and `Copy` checks don't depend on the enclosing usage of the expression + // and don't need to be inserted into the state map. + let vec_snippet = match vec_args { + higher::VecArgs::Repeat(expr, len) => { + if is_copy(cx, cx.typeck_results().expr_ty(expr)) + && let Some(Constant::Int(length)) = ConstEvalCtxt::new(cx).eval(len) + && let Ok(length) = u64::try_from(length) + && size_of(cx, expr) + .checked_mul(length) + .is_some_and(|size| size <= self.too_large_for_stack) + { + suggest_ty.snippet( + cx, + Some(expr.span.source_callsite()), + Some(len.span.source_callsite()), + ) + } else { + return; + } + }, + higher::VecArgs::Vec(args) => { + if let Ok(length) = u64::try_from(args.len()) + && size_of(cx, expr) + .checked_mul(length) + .is_some_and(|size| size <= self.too_large_for_stack) + { + suggest_ty.snippet( + cx, + args.first().zip(args.last()).map(|(first, last)| { + first.span.source_callsite().to(last.span.source_callsite()) + }), + None, + ) + } else { + return; + } + }, + }; + + if let Entry::Vacant(entry) = self.span_to_state.entry(vec_span) { + entry.insert(VecState::Change { + suggest_ty, + vec_snippet, + expr_hir_id: expr.hir_id, + }); + } + }, + VecToArray::Impossible => { + self.span_to_state.insert(vec_span, VecState::NoChange); + }, + } } + } - let snippet = match *vec_args { - higher::VecArgs::Repeat(elem, len) => { - if let Some(Constant::Int(len_constant)) = ConstEvalCtxt::new(cx).eval(len) { - // vec![ty; N] works when ty is Clone, [ty; N] requires it to be Copy also - if !is_copy(cx, cx.typeck_results().expr_ty(elem)) { - return; - } - - #[expect(clippy::cast_possible_truncation)] - if len_constant as u64 * size_of(cx, elem) > self.too_large_for_stack { - return; - } - - suggest_slice.snippet(cx, Some(elem.span), Some(len.span)) - } else { - return; - } - }, - higher::VecArgs::Vec(args) => { - let args_span = if let Some(last) = args.iter().last() { - if args.len() as u64 * size_of(cx, last) > self.too_large_for_stack { - return; - } - Some(args[0].span.source_callsite().to(last.span.source_callsite())) - } else { - None - }; - suggest_slice.snippet(cx, args_span, None) - }, - }; - - self.span_to_lint_map.entry(span).or_insert(Some(( - hir_id, - suggest_slice, - snippet, - Applicability::MachineApplicable, - ))); + fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { + for (span, state) in mem::take(&mut self.span_to_state) { + if let VecState::Change { + suggest_ty, + vec_snippet, + expr_hir_id, + } = state + { + span_lint_hir_and_then(cx, USELESS_VEC, expr_hir_id, span, "useless use of `vec!`", |diag| { + let help_msg = format!("you can use {} directly", suggest_ty.desc()); + // If the `vec!` macro contains comment, better not make the suggestion machine applicable as it + // would remove them. + let applicability = if span_contains_comment(cx.tcx.sess.source_map(), span) { + Applicability::Unspecified + } else { + Applicability::MachineApplicable + }; + diag.span_suggestion(span, help_msg, vec_snippet, applicability); + }); + } + } } } @@ -221,11 +271,17 @@ impl SuggestedType { } fn snippet(self, cx: &LateContext<'_>, args_span: Option, len_span: Option) -> String { + // Invariant of the lint as implemented: all spans are from the root context (and as a result, + // always trivially crate-local). + assert!(args_span.is_none_or(|s| !s.from_expansion())); + assert!(len_span.is_none_or(|s| !s.from_expansion())); + let maybe_args = args_span - .and_then(|sp| sp.get_source_text(cx)) + .map(|sp| sp.get_source_text(cx).expect("spans are always crate-local")) .map_or(String::new(), |x| x.to_owned()); let maybe_len = len_span - .and_then(|sp| sp.get_source_text(cx).map(|s| format!("; {s}"))) + .map(|sp| sp.get_source_text(cx).expect("spans are always crate-local")) + .map(|st| format!("; {st}")) .unwrap_or_default(); match self { diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index a15e4e42e71d..c55c5ec2f51a 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -537,7 +537,7 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) { sug_span = Some(sug_span.unwrap_or(arg.expr.span).to(arg.expr.span)); - if let Some((_, index)) = positional_arg_piece_span(piece) { + if let Some((_, index)) = format_arg_piece_span(piece) { replaced_position.push(index); } @@ -569,16 +569,11 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) { } } -/// Extract Span and its index from the given `piece`, if it's positional argument. -fn positional_arg_piece_span(piece: &FormatArgsPiece) -> Option<(Span, usize)> { +/// Extract Span and its index from the given `piece` +fn format_arg_piece_span(piece: &FormatArgsPiece) -> Option<(Span, usize)> { match piece { FormatArgsPiece::Placeholder(FormatPlaceholder { - argument: - FormatArgPosition { - index: Ok(index), - kind: FormatArgPositionKind::Number, - .. - }, + argument: FormatArgPosition { index: Ok(index), .. }, span: Some(span), .. }) => Some((*span, *index)), diff --git a/clippy_lints/src/zombie_processes.rs b/clippy_lints/src/zombie_processes.rs index 6ab94a522109..a934d2094e00 100644 --- a/clippy_lints/src/zombie_processes.rs +++ b/clippy_lints/src/zombie_processes.rs @@ -1,5 +1,6 @@ use ControlFlow::{Break, Continue}; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{fn_def_id, get_enclosing_block, path_to_local_id}; use rustc_ast::Mutability; use rustc_ast::visit::visit_opt; @@ -58,8 +59,8 @@ declare_lint_pass!(ZombieProcesses => [ZOMBIE_PROCESSES]); impl<'tcx> LateLintPass<'tcx> for ZombieProcesses { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let ExprKind::Call(..) | ExprKind::MethodCall(..) = expr.kind - && let Some(child_adt) = cx.typeck_results().expr_ty(expr).ty_adt_def() - && cx.tcx.is_diagnostic_item(sym::Child, child_adt.did()) + && let child_ty = cx.typeck_results().expr_ty(expr) + && is_type_diagnostic_item(cx, child_ty, sym::Child) { match cx.tcx.parent_hir_node(expr.hir_id) { Node::LetStmt(local) @@ -177,8 +178,8 @@ impl<'tcx> Visitor<'tcx> for WaitFinder<'_, 'tcx> { Node::Expr(expr) if let ExprKind::AddrOf(_, Mutability::Not, _) = expr.kind => {}, Node::Expr(expr) if let Some(fn_did) = fn_def_id(self.cx, expr) - && (self.cx.tcx.is_diagnostic_item(sym::child_id, fn_did) - || self.cx.tcx.is_diagnostic_item(sym::child_kill, fn_did)) => {}, + && let Some(fn_name) = self.cx.tcx.get_diagnostic_name(fn_did) + && matches!(fn_name, sym::child_id | sym::child_kill) => {}, // Conservatively assume that all other kinds of nodes call `.wait()` somehow. _ => return Break(MaybeWait(ex.span)), @@ -351,9 +352,14 @@ fn check<'tcx>(cx: &LateContext<'tcx>, spawn_expr: &'tcx Expr<'tcx>, cause: Caus /// Checks if the given expression exits the process. fn is_exit_expression(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - fn_def_id(cx, expr).is_some_and(|fn_did| { - cx.tcx.is_diagnostic_item(sym::process_exit, fn_did) || cx.tcx.is_diagnostic_item(sym::process_abort, fn_did) - }) + if let Some(fn_did) = fn_def_id(cx, expr) + && let Some(fn_name) = cx.tcx.get_diagnostic_name(fn_did) + && matches!(fn_name, sym::process_exit | sym::process_abort) + { + true + } else { + false + } } #[derive(Debug)] diff --git a/clippy_utils/README.md b/clippy_utils/README.md index 2dfe28953d0c..e01f563c49e7 100644 --- a/clippy_utils/README.md +++ b/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2025-08-22 +nightly-2025-09-04 ``` diff --git a/clippy_utils/src/ast_utils/mod.rs b/clippy_utils/src/ast_utils/mod.rs index 40c00568a3bd..ad69e6eb184e 100644 --- a/clippy_utils/src/ast_utils/mod.rs +++ b/clippy_utils/src/ast_utils/mod.rs @@ -21,7 +21,7 @@ pub fn is_useless_with_eq_exprs(kind: BinOpKind) -> bool { } /// Checks if each element in the first slice is contained within the latter as per `eq_fn`. -pub fn unordered_over(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool { +pub fn unordered_over(left: &[X], right: &[Y], mut eq_fn: impl FnMut(&X, &Y) -> bool) -> bool { left.len() == right.len() && left.iter().all(|l| right.iter().any(|r| eq_fn(l, r))) } diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 4e0b00df9508..bda28a663fb0 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -287,23 +287,22 @@ impl<'a> VecArgs<'a> { && let ExprKind::Path(ref qpath) = fun.kind && is_expn_of(fun.span, sym::vec).is_some() && let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id() + && let Some(name) = cx.tcx.get_diagnostic_name(fun_def_id) { - return if cx.tcx.is_diagnostic_item(sym::vec_from_elem, fun_def_id) && args.len() == 2 { - // `vec![elem; size]` case - Some(VecArgs::Repeat(&args[0], &args[1])) - } else if cx.tcx.is_diagnostic_item(sym::slice_into_vec, fun_def_id) && args.len() == 1 { - // `vec![a, b, c]` case - if let ExprKind::Call(_, [arg]) = &args[0].kind - && let ExprKind::Array(args) = arg.kind + return match (name, args) { + (sym::vec_from_elem, [elem, size]) => { + // `vec![elem; size]` case + Some(VecArgs::Repeat(elem, size)) + }, + (sym::slice_into_vec, [slice]) + if let ExprKind::Call(_, [arg]) = slice.kind + && let ExprKind::Array(args) = arg.kind => { + // `vec![a, b, c]` case Some(VecArgs::Vec(args)) - } else { - None - } - } else if cx.tcx.is_diagnostic_item(sym::vec_new, fun_def_id) && args.is_empty() { - Some(VecArgs::Vec(&[])) - } else { - None + }, + (sym::vec_new, []) => Some(VecArgs::Vec(&[])), + _ => None, }; } diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index 8160443f4132..b79e15cd7170 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -757,7 +757,7 @@ pub fn both_some_and(l: Option, r: Option, mut pred: impl FnMut(X, Y } /// Checks if two slices are equal as per `eq_fn`. -pub fn over(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool { +pub fn over(left: &[X], right: &[Y], mut eq_fn: impl FnMut(&X, &Y) -> bool) -> bool { left.len() == right.len() && left.iter().zip(right).all(|(x, y)| eq_fn(x, y)) } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 011c9b2f931a..14b64eb4d54e 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -102,9 +102,9 @@ use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr}; use rustc_hir::{ self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, CoroutineDesugaring, - CoroutineKind, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs, HirId, Impl, - ImplItem, ImplItemKind, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, - Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem, + CoroutineKind, CoroutineSource, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs, + HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, + OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp, def, find_attr, }; use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize}; @@ -126,6 +126,7 @@ use rustc_span::{InnerSpan, Span}; use source::{SpanRangeExt, walk_span_to_context}; use visitors::{Visitable, for_each_unconsumed_temporary}; +use crate::ast_utils::unordered_over; use crate::consts::{ConstEvalCtxt, Constant, mir_to_const}; use crate::higher::Range; use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; @@ -308,6 +309,7 @@ pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) -> cx.tcx.lang_items().get(item) == Some(did) } +/// Checks if `expr` is an empty block or an empty tuple. pub fn is_unit_expr(expr: &Expr<'_>) -> bool { matches!( expr.kind, @@ -640,9 +642,8 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath< && let Some(impl_did) = cx.tcx.impl_of_assoc(def_id) && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def() { - return std_types_symbols.iter().any(|&symbol| { - cx.tcx.is_diagnostic_item(symbol, adt.did()) || Some(adt.did()) == cx.tcx.lang_items().string() - }); + return Some(adt.did()) == cx.tcx.lang_items().string() + || (cx.tcx.get_diagnostic_name(adt.did())).is_some_and(|adt_name| std_types_symbols.contains(&adt_name)); } false } @@ -1992,7 +1993,7 @@ pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr< (PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup)) if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() => { - zip(pats, tup).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr, by_hir)) + over(pats, tup, |pat, expr| is_expr_identity_of_pat(cx, pat, expr, by_hir)) }, (PatKind::Slice(before, None, after), ExprKind::Array(arr)) if before.len() + after.len() == arr.len() => { zip(before.iter().chain(after), arr).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr, by_hir)) @@ -2004,7 +2005,7 @@ pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr< if let ExprKind::Path(ident) = &ident.kind && qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id) // check fields - && zip(field_pats, fields).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr,by_hir)) + && over(field_pats, fields, |pat, expr| is_expr_identity_of_pat(cx, pat, expr,by_hir)) { true } else { @@ -2017,10 +2018,8 @@ pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr< // check ident qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id) // check fields - && field_pats.iter().all(|field_pat| { - fields.iter().any(|field| { - field_pat.ident == field.ident && is_expr_identity_of_pat(cx, field_pat.pat, field.expr, by_hir) - }) + && unordered_over(field_pats, fields, |field_pat, field| { + field_pat.ident == field.ident && is_expr_identity_of_pat(cx, field_pat.pat, field.expr, by_hir) }) }, _ => false, @@ -2405,6 +2404,24 @@ pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir> expr } +/// Returns a `Vec` of `Expr`s containing `AddrOf` operators (`&`) or deref operators (`*`) of a +/// given expression. +pub fn get_ref_operators<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>) -> Vec<&'hir Expr<'hir>> { + let mut operators = Vec::new(); + peel_hir_expr_while(expr, |expr| match expr.kind { + ExprKind::AddrOf(_, _, e) => { + operators.push(expr); + Some(e) + }, + ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => { + operators.push(expr); + Some(e) + }, + _ => None, + }); + operators +} + pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind && let Res::Def(_, def_id) = path.res @@ -3633,3 +3650,17 @@ pub fn expr_adjustment_requires_coercion(cx: &LateContext<'_>, expr: &Expr<'_>) ) }) } + +/// Checks if the expression is an async block (i.e., `async { ... }`). +pub fn is_expr_async_block(expr: &Expr<'_>) -> bool { + matches!( + expr.kind, + ExprKind::Closure(Closure { + kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared( + CoroutineDesugaring::Async, + CoroutineSource::Block + )), + .. + }) + ) +} diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index fafc1d07e51e..8e302f9d2ad1 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -387,10 +387,7 @@ pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool { /// Checks if the type is a reference equals to a diagnostic item pub fn is_type_ref_to_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool { match ty.kind() { - ty::Ref(_, ref_ty, _) => match ref_ty.kind() { - ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did()), - _ => false, - }, + ty::Ref(_, ref_ty, _) => is_type_diagnostic_item(cx, *ref_ty, diag_item), _ => false, } } @@ -1378,9 +1375,7 @@ pub fn has_non_owning_mutable_access<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<' /// Check if `ty` is slice-like, i.e., `&[T]`, `[T; N]`, or `Vec`. pub fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - ty.is_slice() - || ty.is_array() - || matches!(ty.kind(), ty::Adt(adt_def, _) if cx.tcx.is_diagnostic_item(sym::Vec, adt_def.did())) + ty.is_slice() || ty.is_array() || is_type_diagnostic_item(cx, ty, sym::Vec) } pub fn get_field_idx_by_name(ty: Ty<'_>, name: Symbol) -> Option { diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index 76d43feee12e..6eccbcdb1228 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -144,11 +144,9 @@ impl<'tcx> Visitor<'tcx> for BindingUsageFinder<'_, 'tcx> { /// Checks if the given expression is a macro call to `todo!()` or `unimplemented!()`. pub fn is_todo_unimplemented_macro(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - root_macro_call_first_node(cx, expr).is_some_and(|macro_call| { - [sym::todo_macro, sym::unimplemented_macro] - .iter() - .any(|&sym| cx.tcx.is_diagnostic_item(sym, macro_call.def_id)) - }) + root_macro_call_first_node(cx, expr) + .and_then(|macro_call| cx.tcx.get_diagnostic_name(macro_call.def_id)) + .is_some_and(|macro_name| matches!(macro_name, sym::todo_macro | sym::unimplemented_macro)) } /// Checks if the given expression is a stub, i.e., a `todo!()` or `unimplemented!()` expression, diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 5497e77e8ad1..ec2f24a0a6d8 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-08-22" +channel = "nightly-2025-09-04" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/driver.rs b/src/driver.rs index c4076cbaa77b..6bddcbfd94ce 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -330,7 +330,8 @@ pub fn main() { // Do not run Clippy for Cargo's info queries so that invalid CLIPPY_ARGS are not cached // https://github.com/rust-lang/cargo/issues/14385 - let info_query = has_arg(&orig_args, "-vV") || has_arg(&orig_args, "--print"); + let info_query = has_arg(&orig_args, "-vV") + || arg_value(&orig_args, "--print", |val| val != "crate-root-lint-levels").is_some(); let clippy_enabled = !cap_lints_allow && relevant_package && !info_query; if clippy_enabled { diff --git a/tests/config-consistency.rs b/tests/config-consistency.rs new file mode 100644 index 000000000000..9e7ca26c7d40 --- /dev/null +++ b/tests/config-consistency.rs @@ -0,0 +1,30 @@ +#![feature(rustc_private)] + +// This test checks that all lints defined in `clippy_config::conf` in `#[lints]` +// attributes exist as Clippy lints. +// +// This test is a no-op if run as part of the compiler test suite +// and will always succeed. + +use std::collections::HashSet; + +#[test] +fn config_consistency() { + if option_env!("RUSTC_TEST_SUITE").is_some() { + return; + } + + let lint_names: HashSet = clippy_lints::declared_lints::LINTS + .iter() + .map(|lint_info| lint_info.lint.name.strip_prefix("clippy::").unwrap().to_lowercase()) + .collect(); + for conf in clippy_config::get_configuration_metadata() { + for lint in conf.lints { + assert!( + lint_names.contains(*lint), + "Configuration option {} references lint `{lint}` which does not exist", + conf.name + ); + } + } +} diff --git a/tests/no-profile-in-cargo-toml.rs b/tests/no-profile-in-cargo-toml.rs new file mode 100644 index 000000000000..2ad9bfb75dee --- /dev/null +++ b/tests/no-profile-in-cargo-toml.rs @@ -0,0 +1,34 @@ +// Check that we do not have `profile.*` sections in our `Cargo.toml` files, +// as this causes warnings when run from the compiler repository which includes +// Clippy in a workspace. +// +// Those sections can be put into `.cargo/config.toml` which will be read +// when commands are issued from the top-level Clippy directory, outside of +// a workspace. + +use std::fs::File; +use std::io::{self, BufRead as _}; +use walkdir::WalkDir; + +#[test] +fn no_profile_in_cargo_toml() { + // This check could parse `Cargo.toml` using a TOML deserializer, but in practice + // profile sections would be added at the beginning of a line as `[profile.*]`, so + // keep it fast and simple. + for entry in WalkDir::new(".") + .into_iter() + .filter_map(Result::ok) + .filter(|e| e.file_name().to_str() == Some("Cargo.toml")) + { + for line in io::BufReader::new(File::open(entry.path()).unwrap()) + .lines() + .map(Result::unwrap) + { + if line.starts_with("[profile.") { + eprintln!("Profile section `{line}` found in file `{}`.", entry.path().display()); + eprintln!("Use `.cargo/config.toml` for profiles specific to the standalone Clippy repository."); + panic!("Profile section found in `Cargo.toml`"); + } + } + } +} diff --git a/tests/ui-internal/disallow_span_lint.stderr b/tests/ui-internal/disallow_span_lint.stderr index f03a745963e0..e9d53c64dd9b 100644 --- a/tests/ui-internal/disallow_span_lint.stderr +++ b/tests/ui-internal/disallow_span_lint.stderr @@ -4,7 +4,7 @@ error: use of a disallowed method `rustc_lint::context::LintContext::span_lint` LL | cx.span_lint(lint, span, |lint| { | ^^^^^^^^^ | - = note: this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead + = note: this function does not add a link to our documentation; please use the `clippy_utils::diagnostics::span_lint*` functions instead note: the lint level is defined here --> tests/ui-internal/disallow_span_lint.rs:2:9 | @@ -17,7 +17,7 @@ error: use of a disallowed method `rustc_middle::ty::context::TyCtxt::node_span_ LL | tcx.node_span_lint(lint, hir_id, span, |lint| { | ^^^^^^^^^^^^^^ | - = note: this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead + = note: this function does not add a link to our documentation; please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead error: aborting due to 2 previous errors diff --git a/tests/ui-toml/excessive_precision/clippy.toml b/tests/ui-toml/excessive_precision/clippy.toml new file mode 100644 index 000000000000..c7fc230dcb31 --- /dev/null +++ b/tests/ui-toml/excessive_precision/clippy.toml @@ -0,0 +1 @@ +const-literal-digits-threshold = 20 diff --git a/tests/ui-toml/excessive_precision/excessive_precision.fixed b/tests/ui-toml/excessive_precision/excessive_precision.fixed new file mode 100644 index 000000000000..577bbff2957f --- /dev/null +++ b/tests/ui-toml/excessive_precision/excessive_precision.fixed @@ -0,0 +1,38 @@ +#![warn(clippy::excessive_precision)] +#![allow( + dead_code, + overflowing_literals, + unused_variables, + clippy::print_literal, + clippy::useless_vec +)] + +fn main() { + // Overly specified constants + let _: f32 = 1.012_345_7; + //~^ excessive_precision + let _: f64 = 1.012_345_678_901_234_6; + //~^ excessive_precision + const _: f32 = 1.012345678901234567890; + const _: f64 = 1.012345678901234567890; + + static STATIC1: f32 = 1.012345678901234567890; + static STATIC2: f64 = 1.012345678901234567890; + + static mut STATIC_MUT1: f32 = 1.012345678901234567890; + static mut STATIC_MUT2: f64 = 1.012345678901234567890; +} + +trait ExcessivelyPreciseTrait { + // Overly specified constants + const GOOD1: f32 = 1.012345678901234567890; + const GOOD2: f64 = 1.012345678901234567890; +} + +struct ExcessivelyPreciseStruct; + +impl ExcessivelyPreciseStruct { + // Overly specified constants + const GOOD1: f32 = 1.012345678901234567890; + const GOOD2: f64 = 1.012345678901234567890; +} diff --git a/tests/ui-toml/excessive_precision/excessive_precision.rs b/tests/ui-toml/excessive_precision/excessive_precision.rs new file mode 100644 index 000000000000..121448ed540d --- /dev/null +++ b/tests/ui-toml/excessive_precision/excessive_precision.rs @@ -0,0 +1,38 @@ +#![warn(clippy::excessive_precision)] +#![allow( + dead_code, + overflowing_literals, + unused_variables, + clippy::print_literal, + clippy::useless_vec +)] + +fn main() { + // Overly specified constants + let _: f32 = 1.012345678901234567890; + //~^ excessive_precision + let _: f64 = 1.012345678901234567890; + //~^ excessive_precision + const _: f32 = 1.012345678901234567890; + const _: f64 = 1.012345678901234567890; + + static STATIC1: f32 = 1.012345678901234567890; + static STATIC2: f64 = 1.012345678901234567890; + + static mut STATIC_MUT1: f32 = 1.012345678901234567890; + static mut STATIC_MUT2: f64 = 1.012345678901234567890; +} + +trait ExcessivelyPreciseTrait { + // Overly specified constants + const GOOD1: f32 = 1.012345678901234567890; + const GOOD2: f64 = 1.012345678901234567890; +} + +struct ExcessivelyPreciseStruct; + +impl ExcessivelyPreciseStruct { + // Overly specified constants + const GOOD1: f32 = 1.012345678901234567890; + const GOOD2: f64 = 1.012345678901234567890; +} diff --git a/tests/ui-toml/excessive_precision/excessive_precision.stderr b/tests/ui-toml/excessive_precision/excessive_precision.stderr new file mode 100644 index 000000000000..65d33eddef17 --- /dev/null +++ b/tests/ui-toml/excessive_precision/excessive_precision.stderr @@ -0,0 +1,38 @@ +error: float has excessive precision + --> tests/ui-toml/excessive_precision/excessive_precision.rs:12:18 + | +LL | let _: f32 = 1.012345678901234567890; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +note: consider making it a `const` item + --> tests/ui-toml/excessive_precision/excessive_precision.rs:12:5 + | +LL | let _: f32 = 1.012345678901234567890; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `-D clippy::excessive-precision` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::excessive_precision)]` +help: consider changing the type or truncating it to + | +LL - let _: f32 = 1.012345678901234567890; +LL + let _: f32 = 1.012_345_7; + | + +error: float has excessive precision + --> tests/ui-toml/excessive_precision/excessive_precision.rs:14:18 + | +LL | let _: f64 = 1.012345678901234567890; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +note: consider making it a `const` item + --> tests/ui-toml/excessive_precision/excessive_precision.rs:14:5 + | +LL | let _: f64 = 1.012345678901234567890; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider changing the type or truncating it to + | +LL - let _: f64 = 1.012345678901234567890; +LL + let _: f64 = 1.012_345_678_901_234_6; + | + +error: aborting due to 2 previous errors + diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 6ee77ebd8ece..20aeb4bb8498 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -35,6 +35,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect check-inconsistent-struct-field-initializers check-private-items cognitive-complexity-threshold + const-literal-digits-threshold disallowed-macros disallowed-methods disallowed-names @@ -129,6 +130,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect check-inconsistent-struct-field-initializers check-private-items cognitive-complexity-threshold + const-literal-digits-threshold disallowed-macros disallowed-methods disallowed-names @@ -223,6 +225,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni check-inconsistent-struct-field-initializers check-private-items cognitive-complexity-threshold + const-literal-digits-threshold disallowed-macros disallowed-methods disallowed-names diff --git a/tests/ui/assertions_on_result_states.fixed b/tests/ui/assertions_on_result_states.fixed index 09c1a8d0ed1d..b2b7318c113f 100644 --- a/tests/ui/assertions_on_result_states.fixed +++ b/tests/ui/assertions_on_result_states.fixed @@ -82,9 +82,18 @@ fn main() { assert!(r.is_err()); } -#[allow(dead_code)] fn issue9450() { let res: Result = Ok(1); res.unwrap_err(); //~^ assertions_on_result_states } + +fn issue9916(res: Result) { + let a = 0; + match a { + 0 => {}, + 1 => { res.unwrap(); }, + //~^ assertions_on_result_states + _ => todo!(), + } +} diff --git a/tests/ui/assertions_on_result_states.rs b/tests/ui/assertions_on_result_states.rs index c63c2502b537..33f1485326b3 100644 --- a/tests/ui/assertions_on_result_states.rs +++ b/tests/ui/assertions_on_result_states.rs @@ -82,9 +82,18 @@ fn main() { assert!(r.is_err()); } -#[allow(dead_code)] fn issue9450() { let res: Result = Ok(1); assert!(res.is_err()) //~^ assertions_on_result_states } + +fn issue9916(res: Result) { + let a = 0; + match a { + 0 => {}, + 1 => assert!(res.is_ok()), + //~^ assertions_on_result_states + _ => todo!(), + } +} diff --git a/tests/ui/assertions_on_result_states.stderr b/tests/ui/assertions_on_result_states.stderr index 3bf811588c69..826f049555ca 100644 --- a/tests/ui/assertions_on_result_states.stderr +++ b/tests/ui/assertions_on_result_states.stderr @@ -38,10 +38,16 @@ LL | assert!(r.is_err()); | ^^^^^^^^^^^^^^^^^^^ help: replace with: `r.unwrap_err()` error: called `assert!` with `Result::is_err` - --> tests/ui/assertions_on_result_states.rs:88:5 + --> tests/ui/assertions_on_result_states.rs:87:5 | LL | assert!(res.is_err()) | ^^^^^^^^^^^^^^^^^^^^^ help: replace with: `res.unwrap_err();` -error: aborting due to 7 previous errors +error: called `assert!` with `Result::is_ok` + --> tests/ui/assertions_on_result_states.rs:95:14 + | +LL | 1 => assert!(res.is_ok()), + | ^^^^^^^^^^^^^^^^^^^^ help: replace with: `{ res.unwrap(); }` + +error: aborting due to 8 previous errors diff --git a/tests/ui/assign_ops.fixed b/tests/ui/assign_ops.fixed index 3754b9dfe744..2046d089d6a6 100644 --- a/tests/ui/assign_ops.fixed +++ b/tests/ui/assign_ops.fixed @@ -1,8 +1,9 @@ #![allow(clippy::useless_vec)] #![warn(clippy::assign_op_pattern)] -#![feature(const_trait_impl, const_ops)] +#![feature(const_ops)] +#![feature(const_trait_impl)] -use core::num::Wrapping; +use std::num::Wrapping; use std::ops::{Mul, MulAssign}; fn main() { @@ -82,6 +83,18 @@ mod issue14871 { pub trait Number: Copy + Add + AddAssign { const ZERO: Self; const ONE: Self; + + fn non_constant(value: usize) -> Self { + let mut res = Self::ZERO; + let mut count = 0; + while count < value { + res += Self::ONE; + //~^ assign_op_pattern + count += 1; + //~^ assign_op_pattern + } + res + } } #[rustfmt::skip] // rustfmt doesn't understand the order of pub const on traits (yet) @@ -91,7 +104,7 @@ mod issue14871 { impl const NumberConstants for T where - T: Number + [const] core::ops::Add, + T: Number + [const] std::ops::Add, { fn constant(value: usize) -> Self { let mut res = Self::ZERO; @@ -99,8 +112,28 @@ mod issue14871 { while count < value { res = res + Self::ONE; count += 1; + //~^ assign_op_pattern } res } } + + pub struct S; + + impl const std::ops::Add for S { + type Output = S; + fn add(self, _rhs: S) -> S { + S + } + } + + impl const std::ops::AddAssign for S { + fn add_assign(&mut self, rhs: S) {} + } + + const fn do_add() { + let mut s = S; + s += S; + //~^ assign_op_pattern + } } diff --git a/tests/ui/assign_ops.rs b/tests/ui/assign_ops.rs index 0b878d4f490b..f83a40f55473 100644 --- a/tests/ui/assign_ops.rs +++ b/tests/ui/assign_ops.rs @@ -1,8 +1,9 @@ #![allow(clippy::useless_vec)] #![warn(clippy::assign_op_pattern)] -#![feature(const_trait_impl, const_ops)] +#![feature(const_ops)] +#![feature(const_trait_impl)] -use core::num::Wrapping; +use std::num::Wrapping; use std::ops::{Mul, MulAssign}; fn main() { @@ -82,6 +83,18 @@ mod issue14871 { pub trait Number: Copy + Add + AddAssign { const ZERO: Self; const ONE: Self; + + fn non_constant(value: usize) -> Self { + let mut res = Self::ZERO; + let mut count = 0; + while count < value { + res = res + Self::ONE; + //~^ assign_op_pattern + count = count + 1; + //~^ assign_op_pattern + } + res + } } #[rustfmt::skip] // rustfmt doesn't understand the order of pub const on traits (yet) @@ -91,16 +104,36 @@ mod issue14871 { impl const NumberConstants for T where - T: Number + [const] core::ops::Add, + T: Number + [const] std::ops::Add, { fn constant(value: usize) -> Self { let mut res = Self::ZERO; let mut count = 0; while count < value { res = res + Self::ONE; - count += 1; + count = count + 1; + //~^ assign_op_pattern } res } } + + pub struct S; + + impl const std::ops::Add for S { + type Output = S; + fn add(self, _rhs: S) -> S { + S + } + } + + impl const std::ops::AddAssign for S { + fn add_assign(&mut self, rhs: S) {} + } + + const fn do_add() { + let mut s = S; + s = s + S; + //~^ assign_op_pattern + } } diff --git a/tests/ui/assign_ops.stderr b/tests/ui/assign_ops.stderr index c5e698b3ee11..a4fca04893c4 100644 --- a/tests/ui/assign_ops.stderr +++ b/tests/ui/assign_ops.stderr @@ -1,5 +1,5 @@ error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:10:5 + --> tests/ui/assign_ops.rs:11:5 | LL | a = a + 1; | ^^^^^^^^^ help: replace it with: `a += 1` @@ -8,70 +8,94 @@ LL | a = a + 1; = help: to override `-D warnings` add `#[allow(clippy::assign_op_pattern)]` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:12:5 + --> tests/ui/assign_ops.rs:13:5 | LL | a = 1 + a; | ^^^^^^^^^ help: replace it with: `a += 1` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:14:5 + --> tests/ui/assign_ops.rs:15:5 | LL | a = a - 1; | ^^^^^^^^^ help: replace it with: `a -= 1` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:16:5 + --> tests/ui/assign_ops.rs:17:5 | LL | a = a * 99; | ^^^^^^^^^^ help: replace it with: `a *= 99` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:18:5 + --> tests/ui/assign_ops.rs:19:5 | LL | a = 42 * a; | ^^^^^^^^^^ help: replace it with: `a *= 42` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:20:5 + --> tests/ui/assign_ops.rs:21:5 | LL | a = a / 2; | ^^^^^^^^^ help: replace it with: `a /= 2` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:22:5 + --> tests/ui/assign_ops.rs:23:5 | LL | a = a % 5; | ^^^^^^^^^ help: replace it with: `a %= 5` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:24:5 + --> tests/ui/assign_ops.rs:25:5 | LL | a = a & 1; | ^^^^^^^^^ help: replace it with: `a &= 1` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:31:5 + --> tests/ui/assign_ops.rs:32:5 | LL | s = s + "bla"; | ^^^^^^^^^^^^^ help: replace it with: `s += "bla"` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:36:5 + --> tests/ui/assign_ops.rs:37:5 | LL | a = a + Wrapping(1u32); | ^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a += Wrapping(1u32)` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:39:5 + --> tests/ui/assign_ops.rs:40:5 | LL | v[0] = v[0] + v[1]; | ^^^^^^^^^^^^^^^^^^ help: replace it with: `v[0] += v[1]` error: manual implementation of an assign operation - --> tests/ui/assign_ops.rs:52:5 + --> tests/ui/assign_ops.rs:53:5 | LL | buf = buf + cows.clone(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `buf += cows.clone()` -error: aborting due to 12 previous errors +error: manual implementation of an assign operation + --> tests/ui/assign_ops.rs:91:17 + | +LL | res = res + Self::ONE; + | ^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `res += Self::ONE` + +error: manual implementation of an assign operation + --> tests/ui/assign_ops.rs:93:17 + | +LL | count = count + 1; + | ^^^^^^^^^^^^^^^^^ help: replace it with: `count += 1` + +error: manual implementation of an assign operation + --> tests/ui/assign_ops.rs:114:17 + | +LL | count = count + 1; + | ^^^^^^^^^^^^^^^^^ help: replace it with: `count += 1` + +error: manual implementation of an assign operation + --> tests/ui/assign_ops.rs:136:9 + | +LL | s = s + S; + | ^^^^^^^^^ help: replace it with: `s += S` + +error: aborting due to 16 previous errors diff --git a/tests/ui/async_yields_async.fixed b/tests/ui/async_yields_async.fixed index 93c573d30863..bd1b31f74ee9 100644 --- a/tests/ui/async_yields_async.fixed +++ b/tests/ui/async_yields_async.fixed @@ -80,3 +80,42 @@ fn check_expect_suppression() { } }; } + +#[allow(clippy::let_underscore_future)] +fn issue15552() { + async fn bar(i: i32) {} + + macro_rules! call_bar { + () => { + async { bar(5).await } + }; + ($e:expr) => { + bar($e) + }; + } + let x = async { call_bar!(5).await }; + //~^ async_yields_async + let y = async { call_bar!().await }; + //~^ async_yields_async + //~| async_yields_async + + use std::future::{Future, Ready}; + use std::ops::Add; + use std::pin::Pin; + use std::task::{Context, Poll}; + struct CustomFutureType; + impl Add for CustomFutureType { + type Output = Self; + fn add(self, other: Self) -> Self { + self + } + } + impl Future for CustomFutureType { + type Output = (); + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + Poll::Ready(()) + } + } + let _ = async { (CustomFutureType + CustomFutureType).await }; + //~^ async_yields_async +} diff --git a/tests/ui/async_yields_async.rs b/tests/ui/async_yields_async.rs index 166d522e1c04..605d2734157c 100644 --- a/tests/ui/async_yields_async.rs +++ b/tests/ui/async_yields_async.rs @@ -80,3 +80,42 @@ fn check_expect_suppression() { } }; } + +#[allow(clippy::let_underscore_future)] +fn issue15552() { + async fn bar(i: i32) {} + + macro_rules! call_bar { + () => { + async { bar(5) } + }; + ($e:expr) => { + bar($e) + }; + } + let x = async { call_bar!(5) }; + //~^ async_yields_async + let y = async { call_bar!() }; + //~^ async_yields_async + //~| async_yields_async + + use std::future::{Future, Ready}; + use std::ops::Add; + use std::pin::Pin; + use std::task::{Context, Poll}; + struct CustomFutureType; + impl Add for CustomFutureType { + type Output = Self; + fn add(self, other: Self) -> Self { + self + } + } + impl Future for CustomFutureType { + type Output = (); + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + Poll::Ready(()) + } + } + let _ = async { CustomFutureType + CustomFutureType }; + //~^ async_yields_async +} diff --git a/tests/ui/async_yields_async.stderr b/tests/ui/async_yields_async.stderr index 17a35076fa3f..3041cf657893 100644 --- a/tests/ui/async_yields_async.stderr +++ b/tests/ui/async_yields_async.stderr @@ -89,5 +89,50 @@ LL | | CustomFutureType LL | | }; | |_____- outer async construct -error: aborting due to 6 previous errors +error: an async construct yields a type which is itself awaitable + --> tests/ui/async_yields_async.rs:96:21 + | +LL | let x = async { call_bar!(5) }; + | --^^^^^^^^^^^^-- + | | | + | | awaitable value not awaited + | | help: consider awaiting this value: `call_bar!(5).await` + | outer async construct + +error: an async construct yields a type which is itself awaitable + --> tests/ui/async_yields_async.rs:98:21 + | +LL | let y = async { call_bar!() }; + | --^^^^^^^^^^^-- + | | | + | | awaitable value not awaited + | | help: consider awaiting this value: `call_bar!().await` + | outer async construct + +error: an async construct yields a type which is itself awaitable + --> tests/ui/async_yields_async.rs:90:21 + | +LL | async { bar(5) } + | --^^^^^^-- + | | | + | | awaitable value not awaited + | | help: consider awaiting this value: `bar(5).await` + | outer async construct +... +LL | let y = async { call_bar!() }; + | ----------- in this macro invocation + | + = note: this error originates in the macro `call_bar` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: an async construct yields a type which is itself awaitable + --> tests/ui/async_yields_async.rs:119:21 + | +LL | let _ = async { CustomFutureType + CustomFutureType }; + | --^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-- + | | | + | | awaitable value not awaited + | | help: consider awaiting this value: `(CustomFutureType + CustomFutureType).await` + | outer async construct + +error: aborting due to 10 previous errors diff --git a/tests/ui/bool_comparison.fixed b/tests/ui/bool_comparison.fixed index 166abbe549c3..b0b60104c0b9 100644 --- a/tests/ui/bool_comparison.fixed +++ b/tests/ui/bool_comparison.fixed @@ -1,97 +1,41 @@ #![allow(non_local_definitions, clippy::needless_if)] #![warn(clippy::bool_comparison)] -#![allow(clippy::non_canonical_partial_ord_impl, clippy::nonminimal_bool)] +#![allow(clippy::non_canonical_partial_ord_impl)] fn main() { let x = true; - if x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if !x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if !x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if !x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if !x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if !x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if !x { - //~^ bool_comparison - "yes" - } else { - "no" - }; + let _ = if x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if !x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if !x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if !x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if !x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if !x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if !x { "yes" } else { "no" }; + //~^ bool_comparison + let y = true; - if !x & y { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x & !y { - //~^ bool_comparison - "yes" - } else { - "no" - }; + let _ = if !x & y { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x & !y { "yes" } else { "no" }; + //~^ bool_comparison } -#[allow(dead_code)] fn issue3703() { struct Foo; impl PartialEq for Foo { @@ -127,26 +71,6 @@ fn issue3703() { if false < Foo {} } -#[allow(dead_code)] -fn issue4983() { - let a = true; - let b = false; - - if a != b {}; - //~^ bool_comparison - if a != b {}; - //~^ bool_comparison - if a == b {}; - if !a == !b {}; - - if b != a {}; - //~^ bool_comparison - if b != a {}; - //~^ bool_comparison - if b == a {}; - if !b == !a {}; -} - macro_rules! m { ($func:ident) => { $func() @@ -157,7 +81,6 @@ fn func() -> bool { true } -#[allow(dead_code)] fn issue3973() { // ok, don't lint on `cfg` invocation if false == cfg!(feature = "debugging") {} @@ -196,6 +119,34 @@ fn issue9907() { //~^ bool_comparison // This is not part of the issue, but an unexpected found when fixing the issue, // the provided span was inside of macro rather than the macro callsite. - let _ = ((1 < 2) != m!(func)) as usize; + let _ = ((1 < 2) & !m!(func)) as usize; //~^ bool_comparison } + +#[allow(clippy::nonminimal_bool)] +fn issue15367() { + let a = true; + let b = false; + + // these cases are handled by `nonminimal_bool`, so don't double-lint + if a == !b {}; + if !a == b {}; + if b == !a {}; + if !b == a {}; +} + +fn issue15497() { + fn func() -> bool { + true + } + + fn foo(x: bool) -> bool { + x & !m!(func) + //~^ bool_comparison + } + + fn bar(x: bool) -> bool { + !x & m!(func) + //~^ bool_comparison + } +} diff --git a/tests/ui/bool_comparison.rs b/tests/ui/bool_comparison.rs index 7265c2b4c5a6..1b1108d6ce50 100644 --- a/tests/ui/bool_comparison.rs +++ b/tests/ui/bool_comparison.rs @@ -1,97 +1,41 @@ #![allow(non_local_definitions, clippy::needless_if)] #![warn(clippy::bool_comparison)] -#![allow(clippy::non_canonical_partial_ord_impl, clippy::nonminimal_bool)] +#![allow(clippy::non_canonical_partial_ord_impl)] fn main() { let x = true; - if x == true { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x == false { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if true == x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if false == x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x != true { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x != false { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if true != x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if false != x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x < true { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if false < x { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x > false { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if true > x { - //~^ bool_comparison - "yes" - } else { - "no" - }; + let _ = if x == true { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x == false { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if true == x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if false == x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x != true { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x != false { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if true != x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if false != x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x < true { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if false < x { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x > false { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if true > x { "yes" } else { "no" }; + //~^ bool_comparison + let y = true; - if x < y { - //~^ bool_comparison - "yes" - } else { - "no" - }; - if x > y { - //~^ bool_comparison - "yes" - } else { - "no" - }; + let _ = if x < y { "yes" } else { "no" }; + //~^ bool_comparison + let _ = if x > y { "yes" } else { "no" }; + //~^ bool_comparison } -#[allow(dead_code)] fn issue3703() { struct Foo; impl PartialEq for Foo { @@ -127,26 +71,6 @@ fn issue3703() { if false < Foo {} } -#[allow(dead_code)] -fn issue4983() { - let a = true; - let b = false; - - if a == !b {}; - //~^ bool_comparison - if !a == b {}; - //~^ bool_comparison - if a == b {}; - if !a == !b {}; - - if b == !a {}; - //~^ bool_comparison - if !b == a {}; - //~^ bool_comparison - if b == a {}; - if !b == !a {}; -} - macro_rules! m { ($func:ident) => { $func() @@ -157,7 +81,6 @@ fn func() -> bool { true } -#[allow(dead_code)] fn issue3973() { // ok, don't lint on `cfg` invocation if false == cfg!(feature = "debugging") {} @@ -196,6 +119,34 @@ fn issue9907() { //~^ bool_comparison // This is not part of the issue, but an unexpected found when fixing the issue, // the provided span was inside of macro rather than the macro callsite. - let _ = ((1 < 2) == !m!(func)) as usize; + let _ = ((1 < 2) > m!(func)) as usize; //~^ bool_comparison } + +#[allow(clippy::nonminimal_bool)] +fn issue15367() { + let a = true; + let b = false; + + // these cases are handled by `nonminimal_bool`, so don't double-lint + if a == !b {}; + if !a == b {}; + if b == !a {}; + if !b == a {}; +} + +fn issue15497() { + fn func() -> bool { + true + } + + fn foo(x: bool) -> bool { + x > m!(func) + //~^ bool_comparison + } + + fn bar(x: bool) -> bool { + x < m!(func) + //~^ bool_comparison + } +} diff --git a/tests/ui/bool_comparison.stderr b/tests/ui/bool_comparison.stderr index ddbb9ff78edb..98881a2d20fd 100644 --- a/tests/ui/bool_comparison.stderr +++ b/tests/ui/bool_comparison.stderr @@ -1,155 +1,143 @@ error: equality checks against true are unnecessary - --> tests/ui/bool_comparison.rs:7:8 + --> tests/ui/bool_comparison.rs:7:16 | -LL | if x == true { - | ^^^^^^^^^ help: try simplifying it as shown: `x` +LL | let _ = if x == true { "yes" } else { "no" }; + | ^^^^^^^^^ help: try: `x` | = note: `-D clippy::bool-comparison` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::bool_comparison)]` error: equality checks against false can be replaced by a negation - --> tests/ui/bool_comparison.rs:13:8 + --> tests/ui/bool_comparison.rs:9:16 | -LL | if x == false { - | ^^^^^^^^^^ help: try simplifying it as shown: `!x` +LL | let _ = if x == false { "yes" } else { "no" }; + | ^^^^^^^^^^ help: try: `!x` error: equality checks against true are unnecessary - --> tests/ui/bool_comparison.rs:19:8 + --> tests/ui/bool_comparison.rs:11:16 | -LL | if true == x { - | ^^^^^^^^^ help: try simplifying it as shown: `x` +LL | let _ = if true == x { "yes" } else { "no" }; + | ^^^^^^^^^ help: try: `x` error: equality checks against false can be replaced by a negation - --> tests/ui/bool_comparison.rs:25:8 + --> tests/ui/bool_comparison.rs:13:16 | -LL | if false == x { - | ^^^^^^^^^^ help: try simplifying it as shown: `!x` +LL | let _ = if false == x { "yes" } else { "no" }; + | ^^^^^^^^^^ help: try: `!x` error: inequality checks against true can be replaced by a negation - --> tests/ui/bool_comparison.rs:31:8 + --> tests/ui/bool_comparison.rs:15:16 | -LL | if x != true { - | ^^^^^^^^^ help: try simplifying it as shown: `!x` +LL | let _ = if x != true { "yes" } else { "no" }; + | ^^^^^^^^^ help: try: `!x` error: inequality checks against false are unnecessary - --> tests/ui/bool_comparison.rs:37:8 + --> tests/ui/bool_comparison.rs:17:16 | -LL | if x != false { - | ^^^^^^^^^^ help: try simplifying it as shown: `x` +LL | let _ = if x != false { "yes" } else { "no" }; + | ^^^^^^^^^^ help: try: `x` error: inequality checks against true can be replaced by a negation - --> tests/ui/bool_comparison.rs:43:8 + --> tests/ui/bool_comparison.rs:19:16 | -LL | if true != x { - | ^^^^^^^^^ help: try simplifying it as shown: `!x` +LL | let _ = if true != x { "yes" } else { "no" }; + | ^^^^^^^^^ help: try: `!x` error: inequality checks against false are unnecessary - --> tests/ui/bool_comparison.rs:49:8 + --> tests/ui/bool_comparison.rs:21:16 | -LL | if false != x { - | ^^^^^^^^^^ help: try simplifying it as shown: `x` +LL | let _ = if false != x { "yes" } else { "no" }; + | ^^^^^^^^^^ help: try: `x` error: less than comparison against true can be replaced by a negation - --> tests/ui/bool_comparison.rs:55:8 + --> tests/ui/bool_comparison.rs:23:16 | -LL | if x < true { - | ^^^^^^^^ help: try simplifying it as shown: `!x` +LL | let _ = if x < true { "yes" } else { "no" }; + | ^^^^^^^^ help: try: `!x` error: greater than checks against false are unnecessary - --> tests/ui/bool_comparison.rs:61:8 + --> tests/ui/bool_comparison.rs:25:16 | -LL | if false < x { - | ^^^^^^^^^ help: try simplifying it as shown: `x` +LL | let _ = if false < x { "yes" } else { "no" }; + | ^^^^^^^^^ help: try: `x` error: greater than checks against false are unnecessary - --> tests/ui/bool_comparison.rs:67:8 + --> tests/ui/bool_comparison.rs:27:16 | -LL | if x > false { - | ^^^^^^^^^ help: try simplifying it as shown: `x` +LL | let _ = if x > false { "yes" } else { "no" }; + | ^^^^^^^^^ help: try: `x` error: less than comparison against true can be replaced by a negation - --> tests/ui/bool_comparison.rs:73:8 + --> tests/ui/bool_comparison.rs:29:16 | -LL | if true > x { - | ^^^^^^^^ help: try simplifying it as shown: `!x` +LL | let _ = if true > x { "yes" } else { "no" }; + | ^^^^^^^^ help: try: `!x` error: order comparisons between booleans can be simplified - --> tests/ui/bool_comparison.rs:80:8 + --> tests/ui/bool_comparison.rs:33:16 | -LL | if x < y { - | ^^^^^ help: try simplifying it as shown: `!x & y` +LL | let _ = if x < y { "yes" } else { "no" }; + | ^^^^^ help: try: `!x & y` error: order comparisons between booleans can be simplified - --> tests/ui/bool_comparison.rs:86:8 + --> tests/ui/bool_comparison.rs:35:16 | -LL | if x > y { - | ^^^^^ help: try simplifying it as shown: `x & !y` - -error: this comparison might be written more concisely - --> tests/ui/bool_comparison.rs:135:8 - | -LL | if a == !b {}; - | ^^^^^^^ help: try simplifying it as shown: `a != b` - -error: this comparison might be written more concisely - --> tests/ui/bool_comparison.rs:137:8 - | -LL | if !a == b {}; - | ^^^^^^^ help: try simplifying it as shown: `a != b` - -error: this comparison might be written more concisely - --> tests/ui/bool_comparison.rs:142:8 - | -LL | if b == !a {}; - | ^^^^^^^ help: try simplifying it as shown: `b != a` - -error: this comparison might be written more concisely - --> tests/ui/bool_comparison.rs:144:8 - | -LL | if !b == a {}; - | ^^^^^^^ help: try simplifying it as shown: `b != a` +LL | let _ = if x > y { "yes" } else { "no" }; + | ^^^^^ help: try: `x & !y` error: equality checks against false can be replaced by a negation - --> tests/ui/bool_comparison.rs:169:8 + --> tests/ui/bool_comparison.rs:92:8 | LL | if false == m!(func) {} - | ^^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `!m!(func)` + | ^^^^^^^^^^^^^^^^^ help: try: `!m!(func)` error: equality checks against false can be replaced by a negation - --> tests/ui/bool_comparison.rs:171:8 + --> tests/ui/bool_comparison.rs:94:8 | LL | if m!(func) == false {} - | ^^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `!m!(func)` + | ^^^^^^^^^^^^^^^^^ help: try: `!m!(func)` error: equality checks against true are unnecessary - --> tests/ui/bool_comparison.rs:173:8 + --> tests/ui/bool_comparison.rs:96:8 | LL | if true == m!(func) {} - | ^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `m!(func)` + | ^^^^^^^^^^^^^^^^ help: try: `m!(func)` error: equality checks against true are unnecessary - --> tests/ui/bool_comparison.rs:175:8 + --> tests/ui/bool_comparison.rs:98:8 | LL | if m!(func) == true {} - | ^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `m!(func)` + | ^^^^^^^^^^^^^^^^ help: try: `m!(func)` error: equality checks against false can be replaced by a negation - --> tests/ui/bool_comparison.rs:193:14 + --> tests/ui/bool_comparison.rs:116:14 | LL | let _ = ((1 < 2) == false) as usize; - | ^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `1 >= 2` + | ^^^^^^^^^^^^^^^^ help: try: `1 >= 2` error: equality checks against false can be replaced by a negation - --> tests/ui/bool_comparison.rs:195:14 + --> tests/ui/bool_comparison.rs:118:14 | LL | let _ = (false == m!(func)) as usize; - | ^^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `!m!(func)` + | ^^^^^^^^^^^^^^^^^ help: try: `!m!(func)` -error: this comparison might be written more concisely - --> tests/ui/bool_comparison.rs:199:14 +error: order comparisons between booleans can be simplified + --> tests/ui/bool_comparison.rs:122:14 | -LL | let _ = ((1 < 2) == !m!(func)) as usize; - | ^^^^^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `(1 < 2) != m!(func)` +LL | let _ = ((1 < 2) > m!(func)) as usize; + | ^^^^^^^^^^^^^^^^^^ help: try: `(1 < 2) & !m!(func)` -error: aborting due to 25 previous errors +error: order comparisons between booleans can be simplified + --> tests/ui/bool_comparison.rs:144:9 + | +LL | x > m!(func) + | ^^^^^^^^^^^^ help: try: `x & !m!(func)` + +error: order comparisons between booleans can be simplified + --> tests/ui/bool_comparison.rs:149:9 + | +LL | x < m!(func) + | ^^^^^^^^^^^^ help: try: `!x & m!(func)` + +error: aborting due to 23 previous errors diff --git a/tests/ui/cast_raw_slice_pointer_cast.fixed b/tests/ui/cast_raw_slice_pointer_cast.fixed index bddcb0ebf64e..3c1cf8845957 100644 --- a/tests/ui/cast_raw_slice_pointer_cast.fixed +++ b/tests/ui/cast_raw_slice_pointer_cast.fixed @@ -1,30 +1,53 @@ #![warn(clippy::cast_slice_from_raw_parts)] -#[allow(unused_imports, unused_unsafe)] +const fn require_raw_slice_ptr(_: *const [T]) {} + fn main() { let mut vec = vec![0u8; 1]; let ptr: *const u8 = vec.as_ptr(); let mptr = vec.as_mut_ptr(); - let _: *const [u8] = unsafe { core::ptr::slice_from_raw_parts(ptr, 1) }; + let _: *const [u8] = unsafe { std::ptr::slice_from_raw_parts(ptr, 1) }; //~^ cast_slice_from_raw_parts - let _: *const [u8] = unsafe { core::ptr::slice_from_raw_parts_mut(mptr, 1) }; + let _: *const [u8] = unsafe { std::ptr::slice_from_raw_parts_mut(mptr, 1) }; //~^ cast_slice_from_raw_parts - let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1); + let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, 1); //~^ cast_slice_from_raw_parts { use core::slice; - let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1); + let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, 1); //~^ cast_slice_from_raw_parts use slice as one; - let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1); + let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, 1); //~^ cast_slice_from_raw_parts } { use std::slice; - let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1); + let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, 1); //~^ cast_slice_from_raw_parts use slice as one; - let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1); + let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, 1); //~^ cast_slice_from_raw_parts } + + // implicit cast + { + let _: *const [u8] = unsafe { std::ptr::slice_from_raw_parts(ptr, 1) }; + //~^ cast_slice_from_raw_parts + let _: *mut [u8] = unsafe { std::ptr::slice_from_raw_parts_mut(mptr, 1) }; + //~^ cast_slice_from_raw_parts + require_raw_slice_ptr(unsafe { std::ptr::slice_from_raw_parts(ptr, 1) }); + //~^ cast_slice_from_raw_parts + } + + // implicit cast in const context + const { + const PTR: *const u8 = std::ptr::null(); + const MPTR: *mut u8 = std::ptr::null_mut(); + let _: *const [u8] = unsafe { std::ptr::slice_from_raw_parts(PTR, 1) }; + //~^ cast_slice_from_raw_parts + let _: *mut [u8] = unsafe { std::ptr::slice_from_raw_parts_mut(MPTR, 1) }; + //~^ cast_slice_from_raw_parts + require_raw_slice_ptr(unsafe { std::ptr::slice_from_raw_parts(PTR, 1) }); + //~^ cast_slice_from_raw_parts + }; } diff --git a/tests/ui/cast_raw_slice_pointer_cast.rs b/tests/ui/cast_raw_slice_pointer_cast.rs index 0a1eb276d5e9..8f57b1f96192 100644 --- a/tests/ui/cast_raw_slice_pointer_cast.rs +++ b/tests/ui/cast_raw_slice_pointer_cast.rs @@ -1,6 +1,7 @@ #![warn(clippy::cast_slice_from_raw_parts)] -#[allow(unused_imports, unused_unsafe)] +const fn require_raw_slice_ptr(_: *const [T]) {} + fn main() { let mut vec = vec![0u8; 1]; let ptr: *const u8 = vec.as_ptr(); @@ -27,4 +28,26 @@ fn main() { let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8]; //~^ cast_slice_from_raw_parts } + + // implicit cast + { + let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) }; + //~^ cast_slice_from_raw_parts + let _: *mut [u8] = unsafe { std::slice::from_raw_parts_mut(mptr, 1) }; + //~^ cast_slice_from_raw_parts + require_raw_slice_ptr(unsafe { std::slice::from_raw_parts(ptr, 1) }); + //~^ cast_slice_from_raw_parts + } + + // implicit cast in const context + const { + const PTR: *const u8 = std::ptr::null(); + const MPTR: *mut u8 = std::ptr::null_mut(); + let _: *const [u8] = unsafe { std::slice::from_raw_parts(PTR, 1) }; + //~^ cast_slice_from_raw_parts + let _: *mut [u8] = unsafe { std::slice::from_raw_parts_mut(MPTR, 1) }; + //~^ cast_slice_from_raw_parts + require_raw_slice_ptr(unsafe { std::slice::from_raw_parts(PTR, 1) }); + //~^ cast_slice_from_raw_parts + }; } diff --git a/tests/ui/cast_raw_slice_pointer_cast.stderr b/tests/ui/cast_raw_slice_pointer_cast.stderr index 60794a988db7..328dbafbafe7 100644 --- a/tests/ui/cast_raw_slice_pointer_cast.stderr +++ b/tests/ui/cast_raw_slice_pointer_cast.stderr @@ -1,47 +1,83 @@ error: casting the result of `from_raw_parts` to *const [u8] - --> tests/ui/cast_raw_slice_pointer_cast.rs:8:35 + --> tests/ui/cast_raw_slice_pointer_cast.rs:9:35 | LL | let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) as *const [u8] }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::ptr::slice_from_raw_parts(ptr, 1)` | = note: `-D clippy::cast-slice-from-raw-parts` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::cast_slice_from_raw_parts)]` error: casting the result of `from_raw_parts_mut` to *mut [u8] - --> tests/ui/cast_raw_slice_pointer_cast.rs:10:35 + --> tests/ui/cast_raw_slice_pointer_cast.rs:11:35 | LL | let _: *const [u8] = unsafe { std::slice::from_raw_parts_mut(mptr, 1) as *mut [u8] }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts_mut(mptr, 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::ptr::slice_from_raw_parts_mut(mptr, 1)` error: casting the result of `from_raw_parts` to *const [u8] - --> tests/ui/cast_raw_slice_pointer_cast.rs:12:26 + --> tests/ui/cast_raw_slice_pointer_cast.rs:13:26 | LL | let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) } as *const [u8]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::ptr::slice_from_raw_parts(ptr, 1)` error: casting the result of `from_raw_parts` to *const [u8] - --> tests/ui/cast_raw_slice_pointer_cast.rs:16:30 + --> tests/ui/cast_raw_slice_pointer_cast.rs:17:30 | LL | let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::ptr::slice_from_raw_parts(ptr, 1)` error: casting the result of `from_raw_parts` to *const [u8] - --> tests/ui/cast_raw_slice_pointer_cast.rs:19:30 + --> tests/ui/cast_raw_slice_pointer_cast.rs:20:30 | LL | let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::ptr::slice_from_raw_parts(ptr, 1)` error: casting the result of `from_raw_parts` to *const [u8] - --> tests/ui/cast_raw_slice_pointer_cast.rs:24:30 + --> tests/ui/cast_raw_slice_pointer_cast.rs:25:30 | LL | let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::ptr::slice_from_raw_parts(ptr, 1)` error: casting the result of `from_raw_parts` to *const [u8] - --> tests/ui/cast_raw_slice_pointer_cast.rs:27:30 + --> tests/ui/cast_raw_slice_pointer_cast.rs:28:30 | LL | let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::ptr::slice_from_raw_parts(ptr, 1)` -error: aborting due to 7 previous errors +error: implicitly casting the result of `from_raw_parts` to `*const [u8]` + --> tests/ui/cast_raw_slice_pointer_cast.rs:34:39 + | +LL | let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `std::ptr::slice_from_raw_parts(ptr, 1)` + +error: implicitly casting the result of `from_raw_parts_mut` to `*mut [u8]` + --> tests/ui/cast_raw_slice_pointer_cast.rs:36:37 + | +LL | let _: *mut [u8] = unsafe { std::slice::from_raw_parts_mut(mptr, 1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `std::ptr::slice_from_raw_parts_mut(mptr, 1)` + +error: implicitly casting the result of `from_raw_parts` to `*const [u8]` + --> tests/ui/cast_raw_slice_pointer_cast.rs:38:40 + | +LL | require_raw_slice_ptr(unsafe { std::slice::from_raw_parts(ptr, 1) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `std::ptr::slice_from_raw_parts(ptr, 1)` + +error: implicitly casting the result of `from_raw_parts` to `*const [u8]` + --> tests/ui/cast_raw_slice_pointer_cast.rs:46:39 + | +LL | let _: *const [u8] = unsafe { std::slice::from_raw_parts(PTR, 1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `std::ptr::slice_from_raw_parts(PTR, 1)` + +error: implicitly casting the result of `from_raw_parts_mut` to `*mut [u8]` + --> tests/ui/cast_raw_slice_pointer_cast.rs:48:37 + | +LL | let _: *mut [u8] = unsafe { std::slice::from_raw_parts_mut(MPTR, 1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `std::ptr::slice_from_raw_parts_mut(MPTR, 1)` + +error: implicitly casting the result of `from_raw_parts` to `*const [u8]` + --> tests/ui/cast_raw_slice_pointer_cast.rs:50:40 + | +LL | require_raw_slice_ptr(unsafe { std::slice::from_raw_parts(PTR, 1) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `std::ptr::slice_from_raw_parts(PTR, 1)` + +error: aborting due to 13 previous errors diff --git a/tests/ui/cast_raw_slice_pointer_cast_no_std.fixed b/tests/ui/cast_raw_slice_pointer_cast_no_std.fixed new file mode 100644 index 000000000000..f71fb8d863c6 --- /dev/null +++ b/tests/ui/cast_raw_slice_pointer_cast_no_std.fixed @@ -0,0 +1,55 @@ +#![warn(clippy::cast_slice_from_raw_parts)] +#![no_std] +#![crate_type = "lib"] + +const fn require_raw_slice_ptr(_: *const [T]) {} + +fn main() { + let mut arr = [0u8; 1]; + let ptr: *const u8 = arr.as_ptr(); + let mptr = arr.as_mut_ptr(); + let _: *const [u8] = unsafe { core::ptr::slice_from_raw_parts(ptr, 1) }; + //~^ cast_slice_from_raw_parts + let _: *const [u8] = unsafe { core::ptr::slice_from_raw_parts_mut(mptr, 1) }; + //~^ cast_slice_from_raw_parts + let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1); + //~^ cast_slice_from_raw_parts + { + use core::slice; + let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1); + //~^ cast_slice_from_raw_parts + use slice as one; + let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1); + //~^ cast_slice_from_raw_parts + } + { + use core::slice; + let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1); + //~^ cast_slice_from_raw_parts + use slice as one; + let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1); + //~^ cast_slice_from_raw_parts + } + + // implicit cast + { + let _: *const [u8] = unsafe { core::ptr::slice_from_raw_parts(ptr, 1) }; + //~^ cast_slice_from_raw_parts + let _: *mut [u8] = unsafe { core::ptr::slice_from_raw_parts_mut(mptr, 1) }; + //~^ cast_slice_from_raw_parts + require_raw_slice_ptr(unsafe { core::ptr::slice_from_raw_parts(ptr, 1) }); + //~^ cast_slice_from_raw_parts + } + + // implicit cast in const context + const { + const PTR: *const u8 = core::ptr::null(); + const MPTR: *mut u8 = core::ptr::null_mut(); + let _: *const [u8] = unsafe { core::ptr::slice_from_raw_parts(PTR, 1) }; + //~^ cast_slice_from_raw_parts + let _: *mut [u8] = unsafe { core::ptr::slice_from_raw_parts_mut(MPTR, 1) }; + //~^ cast_slice_from_raw_parts + require_raw_slice_ptr(unsafe { core::ptr::slice_from_raw_parts(PTR, 1) }); + //~^ cast_slice_from_raw_parts + }; +} diff --git a/tests/ui/cast_raw_slice_pointer_cast_no_std.rs b/tests/ui/cast_raw_slice_pointer_cast_no_std.rs new file mode 100644 index 000000000000..743e44c97dc1 --- /dev/null +++ b/tests/ui/cast_raw_slice_pointer_cast_no_std.rs @@ -0,0 +1,55 @@ +#![warn(clippy::cast_slice_from_raw_parts)] +#![no_std] +#![crate_type = "lib"] + +const fn require_raw_slice_ptr(_: *const [T]) {} + +fn main() { + let mut arr = [0u8; 1]; + let ptr: *const u8 = arr.as_ptr(); + let mptr = arr.as_mut_ptr(); + let _: *const [u8] = unsafe { core::slice::from_raw_parts(ptr, 1) as *const [u8] }; + //~^ cast_slice_from_raw_parts + let _: *const [u8] = unsafe { core::slice::from_raw_parts_mut(mptr, 1) as *mut [u8] }; + //~^ cast_slice_from_raw_parts + let _: *const [u8] = unsafe { core::slice::from_raw_parts(ptr, 1) } as *const [u8]; + //~^ cast_slice_from_raw_parts + { + use core::slice; + let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8]; + //~^ cast_slice_from_raw_parts + use slice as one; + let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8]; + //~^ cast_slice_from_raw_parts + } + { + use core::slice; + let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8]; + //~^ cast_slice_from_raw_parts + use slice as one; + let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8]; + //~^ cast_slice_from_raw_parts + } + + // implicit cast + { + let _: *const [u8] = unsafe { core::slice::from_raw_parts(ptr, 1) }; + //~^ cast_slice_from_raw_parts + let _: *mut [u8] = unsafe { core::slice::from_raw_parts_mut(mptr, 1) }; + //~^ cast_slice_from_raw_parts + require_raw_slice_ptr(unsafe { core::slice::from_raw_parts(ptr, 1) }); + //~^ cast_slice_from_raw_parts + } + + // implicit cast in const context + const { + const PTR: *const u8 = core::ptr::null(); + const MPTR: *mut u8 = core::ptr::null_mut(); + let _: *const [u8] = unsafe { core::slice::from_raw_parts(PTR, 1) }; + //~^ cast_slice_from_raw_parts + let _: *mut [u8] = unsafe { core::slice::from_raw_parts_mut(MPTR, 1) }; + //~^ cast_slice_from_raw_parts + require_raw_slice_ptr(unsafe { core::slice::from_raw_parts(PTR, 1) }); + //~^ cast_slice_from_raw_parts + }; +} diff --git a/tests/ui/cast_raw_slice_pointer_cast_no_std.stderr b/tests/ui/cast_raw_slice_pointer_cast_no_std.stderr new file mode 100644 index 000000000000..5488fbcfa1ce --- /dev/null +++ b/tests/ui/cast_raw_slice_pointer_cast_no_std.stderr @@ -0,0 +1,83 @@ +error: casting the result of `from_raw_parts` to *const [u8] + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:11:35 + | +LL | let _: *const [u8] = unsafe { core::slice::from_raw_parts(ptr, 1) as *const [u8] }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + | + = note: `-D clippy::cast-slice-from-raw-parts` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::cast_slice_from_raw_parts)]` + +error: casting the result of `from_raw_parts_mut` to *mut [u8] + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:13:35 + | +LL | let _: *const [u8] = unsafe { core::slice::from_raw_parts_mut(mptr, 1) as *mut [u8] }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts_mut(mptr, 1)` + +error: casting the result of `from_raw_parts` to *const [u8] + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:15:26 + | +LL | let _: *const [u8] = unsafe { core::slice::from_raw_parts(ptr, 1) } as *const [u8]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + +error: casting the result of `from_raw_parts` to *const [u8] + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:19:30 + | +LL | let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + +error: casting the result of `from_raw_parts` to *const [u8] + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:22:30 + | +LL | let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + +error: casting the result of `from_raw_parts` to *const [u8] + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:27:30 + | +LL | let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + +error: casting the result of `from_raw_parts` to *const [u8] + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:30:30 + | +LL | let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)` + +error: implicitly casting the result of `from_raw_parts` to `*const [u8]` + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:36:39 + | +LL | let _: *const [u8] = unsafe { core::slice::from_raw_parts(ptr, 1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `core::ptr::slice_from_raw_parts(ptr, 1)` + +error: implicitly casting the result of `from_raw_parts_mut` to `*mut [u8]` + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:38:37 + | +LL | let _: *mut [u8] = unsafe { core::slice::from_raw_parts_mut(mptr, 1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `core::ptr::slice_from_raw_parts_mut(mptr, 1)` + +error: implicitly casting the result of `from_raw_parts` to `*const [u8]` + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:40:40 + | +LL | require_raw_slice_ptr(unsafe { core::slice::from_raw_parts(ptr, 1) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `core::ptr::slice_from_raw_parts(ptr, 1)` + +error: implicitly casting the result of `from_raw_parts` to `*const [u8]` + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:48:39 + | +LL | let _: *const [u8] = unsafe { core::slice::from_raw_parts(PTR, 1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `core::ptr::slice_from_raw_parts(PTR, 1)` + +error: implicitly casting the result of `from_raw_parts_mut` to `*mut [u8]` + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:50:37 + | +LL | let _: *mut [u8] = unsafe { core::slice::from_raw_parts_mut(MPTR, 1) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `core::ptr::slice_from_raw_parts_mut(MPTR, 1)` + +error: implicitly casting the result of `from_raw_parts` to `*const [u8]` + --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:52:40 + | +LL | require_raw_slice_ptr(unsafe { core::slice::from_raw_parts(PTR, 1) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `core::ptr::slice_from_raw_parts(PTR, 1)` + +error: aborting due to 13 previous errors + diff --git a/tests/ui/collapsible_match.rs b/tests/ui/collapsible_match.rs index 71b82040ff62..8931a3aa09c6 100644 --- a/tests/ui/collapsible_match.rs +++ b/tests/ui/collapsible_match.rs @@ -316,6 +316,47 @@ fn lint_emitted_at_right_node(opt: Option>) { }; } +pub fn issue_14155() { + let mut arr = ["a", "b", "c"]; + if let Some(last) = arr.last() { + match *last { + //~^ collapsible_match + "a" | "b" => { + unimplemented!() + }, + _ => (), + } + } + + if let Some(last) = arr.last() { + match &last { + //~^ collapsible_match + &&"a" | &&"b" => { + unimplemented!() + }, + _ => (), + } + } + + if let Some(mut last) = arr.last_mut() { + match &mut last { + //~^ collapsible_match + &mut &mut "a" | &mut &mut "b" => { + unimplemented!() + }, + _ => (), + } + } + + const NULL_PTR: *const &'static str = std::ptr::null(); + if let Some(last) = arr.last() { + match &raw const *last { + NULL_PTR => unimplemented!(), + _ => (), + } + } +} + fn make() -> T { unimplemented!() } diff --git a/tests/ui/collapsible_match.stderr b/tests/ui/collapsible_match.stderr index c290d84ec297..14b1c1b187e4 100644 --- a/tests/ui/collapsible_match.stderr +++ b/tests/ui/collapsible_match.stderr @@ -250,5 +250,74 @@ LL | if let Issue9647::A { a: Some(a), .. } = x { LL | if let Some(u) = a { | ^^^^^^^ with this pattern -error: aborting due to 13 previous errors +error: this `match` can be collapsed into the outer `if let` + --> tests/ui/collapsible_match.rs:322:9 + | +LL | / match *last { +LL | | +LL | | "a" | "b" => { +LL | | unimplemented!() +LL | | }, +LL | | _ => (), +LL | | } + | |_________^ + | +help: the outer pattern can be modified to include the inner pattern + --> tests/ui/collapsible_match.rs:321:17 + | +LL | if let Some(last) = arr.last() { + | ^^^^ ---------- use: `arr.last().copied()` + | | + | replace this binding +... +LL | "a" | "b" => { + | ^^^^^^^^^ with this pattern + +error: this `match` can be collapsed into the outer `if let` + --> tests/ui/collapsible_match.rs:332:9 + | +LL | / match &last { +LL | | +LL | | &&"a" | &&"b" => { +LL | | unimplemented!() +LL | | }, +LL | | _ => (), +LL | | } + | |_________^ + | +help: the outer pattern can be modified to include the inner pattern + --> tests/ui/collapsible_match.rs:331:17 + | +LL | if let Some(last) = arr.last() { + | ^^^^ ---------- use: `arr.last().as_ref()` + | | + | replace this binding +... +LL | &&"a" | &&"b" => { + | ^^^^^^^^^^^^^ with this pattern + +error: this `match` can be collapsed into the outer `if let` + --> tests/ui/collapsible_match.rs:342:9 + | +LL | / match &mut last { +LL | | +LL | | &mut &mut "a" | &mut &mut "b" => { +LL | | unimplemented!() +LL | | }, +LL | | _ => (), +LL | | } + | |_________^ + | +help: the outer pattern can be modified to include the inner pattern + --> tests/ui/collapsible_match.rs:341:17 + | +LL | if let Some(mut last) = arr.last_mut() { + | ^^^^^^^^ -------------- use: `arr.last_mut().as_mut()` + | | + | replace this binding +... +LL | &mut &mut "a" | &mut &mut "b" => { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ with this pattern + +error: aborting due to 16 previous errors diff --git a/tests/ui/collapsible_match2.stderr b/tests/ui/collapsible_match2.stderr index 7b2730637528..259706709990 100644 --- a/tests/ui/collapsible_match2.stderr +++ b/tests/ui/collapsible_match2.stderr @@ -77,6 +77,8 @@ LL | | }, help: the outer pattern can be modified to include the inner pattern --> tests/ui/collapsible_match2.rs:54:14 | +LL | match Some(&[1]) { + | ---------- use: `Some(&[1]).copied()` LL | Some(s) => match *s { | ^ replace this binding LL | diff --git a/tests/ui/derivable_impls.fixed b/tests/ui/derivable_impls.fixed index 65bfded38835..9f9e4e253c3c 100644 --- a/tests/ui/derivable_impls.fixed +++ b/tests/ui/derivable_impls.fixed @@ -1,4 +1,6 @@ #![allow(dead_code)] +#![feature(const_trait_impl)] +#![feature(const_default)] use std::collections::HashMap; @@ -326,4 +328,40 @@ mod issue11368 { } } +mod issue15493 { + #[derive(Copy, Clone)] + #[repr(transparent)] + struct Foo(u64); + + impl const Default for Foo { + fn default() -> Self { + Self(0) + } + } + + #[derive(Copy, Clone)] + enum Bar { + A, + B, + } + + impl const Default for Bar { + fn default() -> Self { + Bar::A + } + } +} + +mod issue15536 { + #[derive(Copy, Clone)] + #[derive(Default)] + enum Bar { + #[default] + A, + B, + } + + +} + fn main() {} diff --git a/tests/ui/derivable_impls.rs b/tests/ui/derivable_impls.rs index 4826c5497b4a..74a793b9a70e 100644 --- a/tests/ui/derivable_impls.rs +++ b/tests/ui/derivable_impls.rs @@ -1,4 +1,6 @@ #![allow(dead_code)] +#![feature(const_trait_impl)] +#![feature(const_default)] use std::collections::HashMap; @@ -396,4 +398,43 @@ mod issue11368 { } } +mod issue15493 { + #[derive(Copy, Clone)] + #[repr(transparent)] + struct Foo(u64); + + impl const Default for Foo { + fn default() -> Self { + Self(0) + } + } + + #[derive(Copy, Clone)] + enum Bar { + A, + B, + } + + impl const Default for Bar { + fn default() -> Self { + Bar::A + } + } +} + +mod issue15536 { + #[derive(Copy, Clone)] + enum Bar { + A, + B, + } + + impl Default for Bar { + //~^ derivable_impls + fn default() -> Self { + Self::A + } + } +} + fn main() {} diff --git a/tests/ui/derivable_impls.stderr b/tests/ui/derivable_impls.stderr index 0f73ad55a85e..cd46414cb4a8 100644 --- a/tests/ui/derivable_impls.stderr +++ b/tests/ui/derivable_impls.stderr @@ -1,5 +1,5 @@ error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:20:1 + --> tests/ui/derivable_impls.rs:22:1 | LL | / impl std::default::Default for FooDefault<'_> { LL | | @@ -18,7 +18,7 @@ LL ~ struct FooDefault<'a> { | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:42:1 + --> tests/ui/derivable_impls.rs:44:1 | LL | / impl std::default::Default for TupleDefault { LL | | @@ -35,7 +35,7 @@ LL ~ struct TupleDefault(bool, i32, u64); | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:95:1 + --> tests/ui/derivable_impls.rs:97:1 | LL | / impl Default for StrDefault<'_> { LL | | @@ -52,7 +52,7 @@ LL ~ struct StrDefault<'a>(&'a str); | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:122:1 + --> tests/ui/derivable_impls.rs:124:1 | LL | / impl Default for Y { LL | | @@ -69,7 +69,7 @@ LL ~ struct Y(u32); | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:162:1 + --> tests/ui/derivable_impls.rs:164:1 | LL | / impl Default for WithoutSelfCurly { LL | | @@ -86,7 +86,7 @@ LL ~ struct WithoutSelfCurly { | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:171:1 + --> tests/ui/derivable_impls.rs:173:1 | LL | / impl Default for WithoutSelfParan { LL | | @@ -103,7 +103,7 @@ LL ~ struct WithoutSelfParan(bool); | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:194:1 + --> tests/ui/derivable_impls.rs:196:1 | LL | / impl Default for DirectDefaultDefaultCall { LL | | @@ -119,7 +119,7 @@ LL ~ pub struct DirectDefaultDefaultCall { | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:206:1 + --> tests/ui/derivable_impls.rs:208:1 | LL | / impl Default for EquivalentToDefaultDefaultCallVec { LL | | @@ -135,7 +135,7 @@ LL ~ pub struct EquivalentToDefaultDefaultCallVec { | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:234:1 + --> tests/ui/derivable_impls.rs:236:1 | LL | / impl Default for EquivalentToDefaultDefaultCallLocal { LL | | @@ -151,7 +151,7 @@ LL ~ pub struct EquivalentToDefaultDefaultCallLocal { | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:274:1 + --> tests/ui/derivable_impls.rs:276:1 | LL | / impl Default for RepeatDefault1 { LL | | @@ -168,7 +168,7 @@ LL ~ pub struct RepeatDefault1 { | error: this `impl` can be derived - --> tests/ui/derivable_impls.rs:309:1 + --> tests/ui/derivable_impls.rs:311:1 | LL | / impl Default for SimpleEnum { LL | | @@ -187,5 +187,28 @@ LL ~ #[default] LL ~ Bar, | -error: aborting due to 11 previous errors +error: this `impl` can be derived + --> tests/ui/derivable_impls.rs:432:5 + | +LL | / impl Default for Bar { +LL | | +LL | | fn default() -> Self { +LL | | Self::A +LL | | } +LL | | } + | |_____^ + | +help: replace the manual implementation with a derive attribute and mark the default variant + | +LL ~ #[derive(Default)] +LL ~ enum Bar { +LL ~ #[default] +LL ~ A, +LL | B, +LL | } +LL | +LL ~ + | + +error: aborting due to 12 previous errors diff --git a/tests/ui/derivable_impls_derive_const.fixed b/tests/ui/derivable_impls_derive_const.fixed new file mode 100644 index 000000000000..f0d8d2d24092 --- /dev/null +++ b/tests/ui/derivable_impls_derive_const.fixed @@ -0,0 +1,25 @@ +#![allow(dead_code)] +#![feature(const_trait_impl)] +#![feature(const_default)] +#![feature(derive_const)] + +mod issue15493 { + #[derive(Copy, Clone)] + #[repr(transparent)] + #[derive_const(Default)] + struct Foo(u64); + + + + #[derive(Copy, Clone)] + #[derive_const(Default)] + enum Bar { + #[default] + A, + B, + } + + +} + +fn main() {} diff --git a/tests/ui/derivable_impls_derive_const.rs b/tests/ui/derivable_impls_derive_const.rs new file mode 100644 index 000000000000..7d70db1c097d --- /dev/null +++ b/tests/ui/derivable_impls_derive_const.rs @@ -0,0 +1,32 @@ +#![allow(dead_code)] +#![feature(const_trait_impl)] +#![feature(const_default)] +#![feature(derive_const)] + +mod issue15493 { + #[derive(Copy, Clone)] + #[repr(transparent)] + struct Foo(u64); + + impl const Default for Foo { + //~^ derivable_impls + fn default() -> Self { + Self(0) + } + } + + #[derive(Copy, Clone)] + enum Bar { + A, + B, + } + + impl const Default for Bar { + //~^ derivable_impls + fn default() -> Self { + Bar::A + } + } +} + +fn main() {} diff --git a/tests/ui/derivable_impls_derive_const.stderr b/tests/ui/derivable_impls_derive_const.stderr new file mode 100644 index 000000000000..196bac185dda --- /dev/null +++ b/tests/ui/derivable_impls_derive_const.stderr @@ -0,0 +1,46 @@ +error: this `impl` can be derived + --> tests/ui/derivable_impls_derive_const.rs:11:5 + | +LL | / impl const Default for Foo { +LL | | +LL | | fn default() -> Self { +LL | | Self(0) +LL | | } +LL | | } + | |_____^ + | + = note: `-D clippy::derivable-impls` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::derivable_impls)]` +help: replace the manual implementation with a derive attribute + | +LL ~ #[derive_const(Default)] +LL ~ struct Foo(u64); +LL | +LL ~ + | + +error: this `impl` can be derived + --> tests/ui/derivable_impls_derive_const.rs:24:5 + | +LL | / impl const Default for Bar { +LL | | +LL | | fn default() -> Self { +LL | | Bar::A +LL | | } +LL | | } + | |_____^ + | +help: replace the manual implementation with a derive attribute and mark the default variant + | +LL ~ #[derive_const(Default)] +LL ~ enum Bar { +LL ~ #[default] +LL ~ A, +LL | B, +LL | } +LL | +LL ~ + | + +error: aborting due to 2 previous errors + diff --git a/tests/ui/doc/doc-fixable.fixed b/tests/ui/doc/doc-fixable.fixed index 423a73734daa..46695dc929ab 100644 --- a/tests/ui/doc/doc-fixable.fixed +++ b/tests/ui/doc/doc-fixable.fixed @@ -73,6 +73,7 @@ fn test_units() { /// GPLv2 GPLv3 /// GitHub GitLab /// IPv4 IPv6 +/// InfiniBand RoCE /// ClojureScript CoffeeScript JavaScript PostScript PureScript TypeScript /// PowerPC WebAssembly /// NaN NaNs diff --git a/tests/ui/doc/doc-fixable.rs b/tests/ui/doc/doc-fixable.rs index 8deffb4210e4..4082fa5b56f4 100644 --- a/tests/ui/doc/doc-fixable.rs +++ b/tests/ui/doc/doc-fixable.rs @@ -73,6 +73,7 @@ fn test_units() { /// GPLv2 GPLv3 /// GitHub GitLab /// IPv4 IPv6 +/// InfiniBand RoCE /// ClojureScript CoffeeScript JavaScript PostScript PureScript TypeScript /// PowerPC WebAssembly /// NaN NaNs diff --git a/tests/ui/doc/doc-fixable.stderr b/tests/ui/doc/doc-fixable.stderr index 98c26e6bec2e..2a94a8f31658 100644 --- a/tests/ui/doc/doc-fixable.stderr +++ b/tests/ui/doc/doc-fixable.stderr @@ -145,7 +145,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:90:5 + --> tests/ui/doc/doc-fixable.rs:91:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -157,7 +157,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:108:5 + --> tests/ui/doc/doc-fixable.rs:109:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -169,7 +169,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:117:8 + --> tests/ui/doc/doc-fixable.rs:118:8 | LL | /// ## CamelCaseThing | ^^^^^^^^^^^^^^ @@ -181,7 +181,7 @@ LL + /// ## `CamelCaseThing` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:121:7 + --> tests/ui/doc/doc-fixable.rs:122:7 | LL | /// # CamelCaseThing | ^^^^^^^^^^^^^^ @@ -193,7 +193,7 @@ LL + /// # `CamelCaseThing` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:124:22 + --> tests/ui/doc/doc-fixable.rs:125:22 | LL | /// Not a title #897 CamelCaseThing | ^^^^^^^^^^^^^^ @@ -205,7 +205,7 @@ LL + /// Not a title #897 `CamelCaseThing` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:126:5 + --> tests/ui/doc/doc-fixable.rs:127:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -217,7 +217,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:134:5 + --> tests/ui/doc/doc-fixable.rs:135:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -229,7 +229,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:148:5 + --> tests/ui/doc/doc-fixable.rs:149:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -241,7 +241,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:160:43 + --> tests/ui/doc/doc-fixable.rs:161:43 | LL | /** E.g., serialization of an empty list: FooBar | ^^^^^^ @@ -253,7 +253,7 @@ LL + /** E.g., serialization of an empty list: `FooBar` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:165:5 + --> tests/ui/doc/doc-fixable.rs:166:5 | LL | And BarQuz too. | ^^^^^^ @@ -265,7 +265,7 @@ LL + And `BarQuz` too. | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:166:1 + --> tests/ui/doc/doc-fixable.rs:167:1 | LL | be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -277,7 +277,7 @@ LL + `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:174:43 + --> tests/ui/doc/doc-fixable.rs:175:43 | LL | /** E.g., serialization of an empty list: FooBar | ^^^^^^ @@ -289,7 +289,7 @@ LL + /** E.g., serialization of an empty list: `FooBar` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:179:5 + --> tests/ui/doc/doc-fixable.rs:180:5 | LL | And BarQuz too. | ^^^^^^ @@ -301,7 +301,7 @@ LL + And `BarQuz` too. | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:180:1 + --> tests/ui/doc/doc-fixable.rs:181:1 | LL | be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -313,7 +313,7 @@ LL + `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:194:5 + --> tests/ui/doc/doc-fixable.rs:195:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -325,7 +325,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:214:22 + --> tests/ui/doc/doc-fixable.rs:215:22 | LL | /// An iterator over mycrate::Collection's values. | ^^^^^^^^^^^^^^^^^^^ @@ -337,7 +337,7 @@ LL + /// An iterator over `mycrate::Collection`'s values. | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:239:34 + --> tests/ui/doc/doc-fixable.rs:240:34 | LL | /// Foo \[bar\] \[baz\] \[qux\]. DocMarkdownLint | ^^^^^^^^^^^^^^^ @@ -349,7 +349,7 @@ LL + /// Foo \[bar\] \[baz\] \[qux\]. `DocMarkdownLint` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:263:22 + --> tests/ui/doc/doc-fixable.rs:264:22 | LL | /// There is no try (do() or do_not()). | ^^^^ @@ -361,7 +361,7 @@ LL + /// There is no try (`do()` or do_not()). | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:263:30 + --> tests/ui/doc/doc-fixable.rs:264:30 | LL | /// There is no try (do() or do_not()). | ^^^^^^^^ @@ -373,7 +373,7 @@ LL + /// There is no try (do() or `do_not()`). | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:268:5 + --> tests/ui/doc/doc-fixable.rs:269:5 | LL | /// ABes | ^^^^ @@ -385,7 +385,7 @@ LL + /// `ABes` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:275:9 + --> tests/ui/doc/doc-fixable.rs:276:9 | LL | /// foo() | ^^^^^ @@ -397,7 +397,7 @@ LL + /// `foo()` | error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> tests/ui/doc/doc-fixable.rs:280:5 + --> tests/ui/doc/doc-fixable.rs:281:5 | LL | /// https://github.com/rust-lang/rust-clippy/pull/12836 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `` diff --git a/tests/ui/entry_unfixable.stderr b/tests/ui/entry_unfixable.stderr index 0197d2ab4cf9..f92f472512f6 100644 --- a/tests/ui/entry_unfixable.stderr +++ b/tests/ui/entry_unfixable.stderr @@ -10,6 +10,7 @@ LL | | false LL | | } | |_____________^ | + = help: consider using the `Entry` API: https://doc.rust-lang.org/std/collections/struct.HashMap.html#entry-api = note: `-D clippy::map-entry` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::map_entry)]` @@ -24,6 +25,8 @@ LL | | } else { LL | | hm.insert(key, true); LL | | } | |_____^ + | + = help: consider using the `Entry` API: https://doc.rust-lang.org/std/collections/struct.HashMap.html#entry-api error: usage of `contains_key` followed by `insert` on a `HashMap` --> tests/ui/entry_unfixable.rs:80:13 @@ -36,6 +39,8 @@ LL | | let interner = INTERNER.lock().unwrap(); LL | | return Err(interner.resolve(name).unwrap().to_owned()); LL | | } | |_____________^ + | + = help: consider using the `Entry` API: https://doc.rust-lang.org/std/collections/struct.HashMap.html#entry-api error: aborting due to 3 previous errors diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed index 3d2b41b8fb81..6944a979c05e 100644 --- a/tests/ui/eta.fixed +++ b/tests/ui/eta.fixed @@ -565,6 +565,51 @@ fn issue_14789() { ); } +fn issue_15072() { + use std::ops::Deref; + + struct Foo; + impl Deref for Foo { + type Target = fn() -> &'static str; + + fn deref(&self) -> &Self::Target { + fn hello() -> &'static str { + "Hello, world!" + } + &(hello as fn() -> &'static str) + } + } + + fn accepts_fn(f: impl Fn() -> &'static str) { + println!("{}", f()); + } + + fn some_fn() -> &'static str { + todo!() + } + + let f = &Foo; + accepts_fn(**f); + //~^ redundant_closure + + let g = &some_fn; + accepts_fn(g); + //~^ redundant_closure + + struct Bar(Foo); + impl Deref for Bar { + type Target = Foo; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + let b = &Bar(Foo); + accepts_fn(***b); + //~^ redundant_closure +} + fn issue8817() { fn f(_: u32) -> u32 { todo!() diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs index 79d1103410d9..5bcc1cb26fd7 100644 --- a/tests/ui/eta.rs +++ b/tests/ui/eta.rs @@ -565,6 +565,51 @@ fn issue_14789() { ); } +fn issue_15072() { + use std::ops::Deref; + + struct Foo; + impl Deref for Foo { + type Target = fn() -> &'static str; + + fn deref(&self) -> &Self::Target { + fn hello() -> &'static str { + "Hello, world!" + } + &(hello as fn() -> &'static str) + } + } + + fn accepts_fn(f: impl Fn() -> &'static str) { + println!("{}", f()); + } + + fn some_fn() -> &'static str { + todo!() + } + + let f = &Foo; + accepts_fn(|| f()); + //~^ redundant_closure + + let g = &some_fn; + accepts_fn(|| g()); + //~^ redundant_closure + + struct Bar(Foo); + impl Deref for Bar { + type Target = Foo; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + let b = &Bar(Foo); + accepts_fn(|| b()); + //~^ redundant_closure +} + fn issue8817() { fn f(_: u32) -> u32 { todo!() diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index aa32ed1a38ef..0b401cdea987 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -215,28 +215,46 @@ LL | let _field = bind.or_else(|| get_default()).unwrap(); | ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `get_default` error: redundant closure - --> tests/ui/eta.rs:588:14 + --> tests/ui/eta.rs:592:16 + | +LL | accepts_fn(|| f()); + | ^^^^^^ help: replace the closure with the function itself: `**f` + +error: redundant closure + --> tests/ui/eta.rs:596:16 + | +LL | accepts_fn(|| g()); + | ^^^^^^ help: replace the closure with the function itself: `g` + +error: redundant closure + --> tests/ui/eta.rs:609:16 + | +LL | accepts_fn(|| b()); + | ^^^^^^ help: replace the closure with the function itself: `***b` + +error: redundant closure + --> tests/ui/eta.rs:633:14 | LL | .map(|n| MyError::A(n)) | ^^^^^^^^^^^^^^^^^ help: replace the closure with the tuple variant itself: `MyError::A` error: redundant closure - --> tests/ui/eta.rs:585:14 + --> tests/ui/eta.rs:630:14 | LL | .map(|n| S(n)) | ^^^^^^^^ help: replace the closure with the tuple struct itself: `S` error: redundant closure - --> tests/ui/eta.rs:582:14 + --> tests/ui/eta.rs:627:14 | LL | .map(|n| g(n)) | ^^^^^^^^ help: replace the closure with the function itself: `g` error: redundant closure - --> tests/ui/eta.rs:579:14 + --> tests/ui/eta.rs:624:14 | LL | .map(|n| f(n)) | ^^^^^^^^ help: replace the closure with the function itself: `f` -error: aborting due to 39 previous errors +error: aborting due to 42 previous errors diff --git a/tests/ui/excessive_precision.fixed b/tests/ui/excessive_precision.fixed index 8a8c2e1939c8..8158d4b332ac 100644 --- a/tests/ui/excessive_precision.fixed +++ b/tests/ui/excessive_precision.fixed @@ -4,9 +4,16 @@ overflowing_literals, unused_variables, clippy::print_literal, - clippy::useless_vec + clippy::useless_vec, + clippy::approx_constant )] +macro_rules! make_pi { + ($i:ident : $t:ty) => { + const $i: $t = 3.14159265358979323846264338327950288419716939937510582097494459230781640628; + }; +} + fn main() { // Consts const GOOD32: f32 = 0.123_456; @@ -101,4 +108,40 @@ fn main() { const _: f64 = 3.0; //~^ excessive_precision const _: f64 = 3.0000000000000000; + + // Overly specified constants + let _: f32 = 1.012_345_7; + //~^ excessive_precision + let _: f64 = 1.012_345_678_901_234_6; + //~^ excessive_precision + const _: f32 = 1.01234567890123456789012345678901234567890; + const _: f64 = 1.01234567890123456789012345678901234567890; + + static STATIC1: f32 = 1.01234567890123456789012345678901234567890; + static STATIC2: f64 = 1.01234567890123456789012345678901234567890; + + static mut STATIC_MUT1: f32 = 1.01234567890123456789012345678901234567890; + static mut STATIC_MUT2: f64 = 1.01234567890123456789012345678901234567890; + + // From issue #13855 + let gamma = 0.577_215_664_901_532_9; + //~^ excessive_precision + const GAMMA: f64 = 0.5772156649015328606065120900824024310421; + + make_pi!(P32: f32); + make_pi!(P64: f64); +} + +trait ExcessivelyPreciseTrait { + // Overly specified constants + const GOOD1: f32 = 1.01234567890123456789012345678901234567890; + const GOOD2: f64 = 1.01234567890123456789012345678901234567890; +} + +struct ExcessivelyPreciseStruct; + +impl ExcessivelyPreciseStruct { + // Overly specified constants + const GOOD1: f32 = 1.01234567890123456789012345678901234567890; + const GOOD2: f64 = 1.01234567890123456789012345678901234567890; } diff --git a/tests/ui/excessive_precision.rs b/tests/ui/excessive_precision.rs index 5dcf55cb9273..7ee6247ee5ac 100644 --- a/tests/ui/excessive_precision.rs +++ b/tests/ui/excessive_precision.rs @@ -4,9 +4,16 @@ overflowing_literals, unused_variables, clippy::print_literal, - clippy::useless_vec + clippy::useless_vec, + clippy::approx_constant )] +macro_rules! make_pi { + ($i:ident : $t:ty) => { + const $i: $t = 3.14159265358979323846264338327950288419716939937510582097494459230781640628; + }; +} + fn main() { // Consts const GOOD32: f32 = 0.123_456; @@ -101,4 +108,40 @@ fn main() { const _: f64 = 3.0000000000000000e+00; //~^ excessive_precision const _: f64 = 3.0000000000000000; + + // Overly specified constants + let _: f32 = 1.01234567890123456789012345678901234567890; + //~^ excessive_precision + let _: f64 = 1.01234567890123456789012345678901234567890; + //~^ excessive_precision + const _: f32 = 1.01234567890123456789012345678901234567890; + const _: f64 = 1.01234567890123456789012345678901234567890; + + static STATIC1: f32 = 1.01234567890123456789012345678901234567890; + static STATIC2: f64 = 1.01234567890123456789012345678901234567890; + + static mut STATIC_MUT1: f32 = 1.01234567890123456789012345678901234567890; + static mut STATIC_MUT2: f64 = 1.01234567890123456789012345678901234567890; + + // From issue #13855 + let gamma = 0.5772156649015328606065120900824024310421; + //~^ excessive_precision + const GAMMA: f64 = 0.5772156649015328606065120900824024310421; + + make_pi!(P32: f32); + make_pi!(P64: f64); +} + +trait ExcessivelyPreciseTrait { + // Overly specified constants + const GOOD1: f32 = 1.01234567890123456789012345678901234567890; + const GOOD2: f64 = 1.01234567890123456789012345678901234567890; +} + +struct ExcessivelyPreciseStruct; + +impl ExcessivelyPreciseStruct { + // Overly specified constants + const GOOD1: f32 = 1.01234567890123456789012345678901234567890; + const GOOD2: f64 = 1.01234567890123456789012345678901234567890; } diff --git a/tests/ui/excessive_precision.stderr b/tests/ui/excessive_precision.stderr index f5eeadf0c8cb..40806d67487f 100644 --- a/tests/ui/excessive_precision.stderr +++ b/tests/ui/excessive_precision.stderr @@ -1,5 +1,5 @@ error: float has excessive precision - --> tests/ui/excessive_precision.rs:20:26 + --> tests/ui/excessive_precision.rs:27:26 | LL | const BAD32_1: f32 = 0.123_456_789_f32; | ^^^^^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL + const BAD32_1: f32 = 0.123_456_79_f32; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:22:26 + --> tests/ui/excessive_precision.rs:29:26 | LL | const BAD32_2: f32 = 0.123_456_789; | ^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL + const BAD32_2: f32 = 0.123_456_79; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:24:26 + --> tests/ui/excessive_precision.rs:31:26 | LL | const BAD32_3: f32 = 0.100_000_000_000_1; | ^^^^^^^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL + const BAD32_3: f32 = 0.1; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:26:29 + --> tests/ui/excessive_precision.rs:33:29 | LL | const BAD32_EDGE: f32 = 1.000_000_9; | ^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL + const BAD32_EDGE: f32 = 1.000_001; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:31:26 + --> tests/ui/excessive_precision.rs:38:26 | LL | const BAD64_3: f64 = 0.100_000_000_000_000_000_1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -61,7 +61,7 @@ LL + const BAD64_3: f64 = 0.1; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:35:22 + --> tests/ui/excessive_precision.rs:42:22 | LL | println!("{:?}", 8.888_888_888_888_888_888_888); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL + println!("{:?}", 8.888_888_888_888_89); | error: float has excessive precision - --> tests/ui/excessive_precision.rs:47:22 + --> tests/ui/excessive_precision.rs:54:22 | LL | let bad32: f32 = 1.123_456_789; | ^^^^^^^^^^^^^ @@ -85,7 +85,7 @@ LL + let bad32: f32 = 1.123_456_8; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:49:26 + --> tests/ui/excessive_precision.rs:56:26 | LL | let bad32_suf: f32 = 1.123_456_789_f32; | ^^^^^^^^^^^^^^^^^ @@ -97,7 +97,7 @@ LL + let bad32_suf: f32 = 1.123_456_8_f32; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:51:21 + --> tests/ui/excessive_precision.rs:58:21 | LL | let bad32_inf = 1.123_456_789_f32; | ^^^^^^^^^^^^^^^^^ @@ -109,7 +109,7 @@ LL + let bad32_inf = 1.123_456_8_f32; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:62:36 + --> tests/ui/excessive_precision.rs:69:36 | LL | let bad_vec32: Vec = vec![0.123_456_789]; | ^^^^^^^^^^^^^ @@ -121,7 +121,7 @@ LL + let bad_vec32: Vec = vec![0.123_456_79]; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:64:36 + --> tests/ui/excessive_precision.rs:71:36 | LL | let bad_vec64: Vec = vec![0.123_456_789_123_456_789]; | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -133,7 +133,7 @@ LL + let bad_vec64: Vec = vec![0.123_456_789_123_456_78]; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:69:24 + --> tests/ui/excessive_precision.rs:76:24 | LL | let bad_e32: f32 = 1.123_456_788_888e-10; | ^^^^^^^^^^^^^^^^^^^^^ @@ -145,7 +145,7 @@ LL + let bad_e32: f32 = 1.123_456_8e-10; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:73:27 + --> tests/ui/excessive_precision.rs:80:27 | LL | let bad_bige32: f32 = 1.123_456_788_888E-10; | ^^^^^^^^^^^^^^^^^^^^^ @@ -157,7 +157,7 @@ LL + let bad_bige32: f32 = 1.123_456_8E-10; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:86:13 + --> tests/ui/excessive_precision.rs:93:13 | LL | let _ = 2.225_073_858_507_201_1e-308_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -169,7 +169,7 @@ LL + let _ = 2.225_073_858_507_201e-308_f64; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:90:13 + --> tests/ui/excessive_precision.rs:97:13 | LL | let _ = 1.000_000_000_000_001e-324_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -181,7 +181,7 @@ LL + let _ = 0_f64; | error: float has excessive precision - --> tests/ui/excessive_precision.rs:101:20 + --> tests/ui/excessive_precision.rs:108:20 | LL | const _: f64 = 3.0000000000000000e+00; | ^^^^^^^^^^^^^^^^^^^^^^ @@ -192,5 +192,56 @@ LL - const _: f64 = 3.0000000000000000e+00; LL + const _: f64 = 3.0; | -error: aborting due to 16 previous errors +error: float has excessive precision + --> tests/ui/excessive_precision.rs:113:18 + | +LL | let _: f32 = 1.01234567890123456789012345678901234567890; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: consider making it a `const` item + --> tests/ui/excessive_precision.rs:113:5 + | +LL | let _: f32 = 1.01234567890123456789012345678901234567890; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider changing the type or truncating it to + | +LL - let _: f32 = 1.01234567890123456789012345678901234567890; +LL + let _: f32 = 1.012_345_7; + | + +error: float has excessive precision + --> tests/ui/excessive_precision.rs:115:18 + | +LL | let _: f64 = 1.01234567890123456789012345678901234567890; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: consider making it a `const` item + --> tests/ui/excessive_precision.rs:115:5 + | +LL | let _: f64 = 1.01234567890123456789012345678901234567890; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider changing the type or truncating it to + | +LL - let _: f64 = 1.01234567890123456789012345678901234567890; +LL + let _: f64 = 1.012_345_678_901_234_6; + | + +error: float has excessive precision + --> tests/ui/excessive_precision.rs:127:17 + | +LL | let gamma = 0.5772156649015328606065120900824024310421; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: consider making it a `const` item + --> tests/ui/excessive_precision.rs:127:5 + | +LL | let gamma = 0.5772156649015328606065120900824024310421; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider changing the type or truncating it to + | +LL - let gamma = 0.5772156649015328606065120900824024310421; +LL + let gamma = 0.577_215_664_901_532_9; + | + +error: aborting due to 19 previous errors diff --git a/tests/ui/float_equality_without_abs.rs b/tests/ui/float_equality_without_abs.rs index a1548db6710d..e1dd79026839 100644 --- a/tests/ui/float_equality_without_abs.rs +++ b/tests/ui/float_equality_without_abs.rs @@ -1,8 +1,8 @@ +#![feature(f128)] +#![feature(f16)] #![warn(clippy::float_equality_without_abs)] //@no-rustfix: suggestions cause type ambiguity -// FIXME(f16_f128): add tests for these types when abs is available - pub fn is_roughly_equal(a: f32, b: f32) -> bool { (a - b) < f32::EPSILON //~^ float_equality_without_abs @@ -44,10 +44,20 @@ pub fn main() { let _ = f32::EPSILON > 1.0 - 2.0; //~^ float_equality_without_abs + let _ = (a as f16 - b as f16) < f16::EPSILON; + //~^ float_equality_without_abs + + let _ = (a as f128 - b as f128) < f128::EPSILON; + //~^ float_equality_without_abs + // those are correct + let _ = (a as f16 - b as f16).abs() < f16::EPSILON; let _ = (a - b).abs() < f32::EPSILON; let _ = (a as f64 - b as f64).abs() < f64::EPSILON; + let _ = (a as f128 - b as f128).abs() < f128::EPSILON; + let _ = f16::EPSILON > (a as f16 - b as f16).abs(); let _ = f32::EPSILON > (a - b).abs(); let _ = f64::EPSILON > (a as f64 - b as f64).abs(); + let _ = f128::EPSILON > (a as f128 - b as f128).abs(); } diff --git a/tests/ui/float_equality_without_abs.stderr b/tests/ui/float_equality_without_abs.stderr index d4c89ce72ba9..55a150dead5a 100644 --- a/tests/ui/float_equality_without_abs.stderr +++ b/tests/ui/float_equality_without_abs.stderr @@ -89,5 +89,21 @@ LL | let _ = f32::EPSILON > 1.0 - 2.0; | | | help: add `.abs()`: `(1.0 - 2.0).abs()` -error: aborting due to 11 previous errors +error: float equality check without `.abs()` + --> tests/ui/float_equality_without_abs.rs:47:13 + | +LL | let _ = (a as f16 - b as f16) < f16::EPSILON; + | ---------------------^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(a as f16 - b as f16).abs()` + +error: float equality check without `.abs()` + --> tests/ui/float_equality_without_abs.rs:50:13 + | +LL | let _ = (a as f128 - b as f128) < f128::EPSILON; + | -----------------------^^^^^^^^^^^^^^^^ + | | + | help: add `.abs()`: `(a as f128 - b as f128).abs()` + +error: aborting due to 13 previous errors diff --git a/tests/ui/if_then_some_else_none.fixed b/tests/ui/if_then_some_else_none.fixed index d14a805b6667..0fd130609aee 100644 --- a/tests/ui/if_then_some_else_none.fixed +++ b/tests/ui/if_then_some_else_none.fixed @@ -165,3 +165,44 @@ mod issue15257 { do_something((i % 2 == 0).then_some(closure_fn)); } } + +fn issue15005() { + struct Counter { + count: u32, + } + + impl Counter { + fn new() -> Counter { + Counter { count: 0 } + } + } + + impl Iterator for Counter { + type Item = u32; + + fn next(&mut self) -> Option { + //~v if_then_some_else_none + (self.count < 5).then(|| { self.count += 1; self.count }) + } + } +} + +fn statements_from_macro() { + macro_rules! mac { + () => { + println!("foo"); + println!("bar"); + }; + } + //~v if_then_some_else_none + let _ = true.then(|| { mac!(); 42 }); +} + +fn dont_lint_inside_macros() { + macro_rules! mac { + ($cond:expr, $res:expr) => { + if $cond { Some($res) } else { None } + }; + } + let _: Option = mac!(true, 42); +} diff --git a/tests/ui/if_then_some_else_none.rs b/tests/ui/if_then_some_else_none.rs index bb0072f31573..640828aa9bf6 100644 --- a/tests/ui/if_then_some_else_none.rs +++ b/tests/ui/if_then_some_else_none.rs @@ -211,3 +211,54 @@ mod issue15257 { }); } } + +fn issue15005() { + struct Counter { + count: u32, + } + + impl Counter { + fn new() -> Counter { + Counter { count: 0 } + } + } + + impl Iterator for Counter { + type Item = u32; + + fn next(&mut self) -> Option { + //~v if_then_some_else_none + if self.count < 5 { + self.count += 1; + Some(self.count) + } else { + None + } + } + } +} + +fn statements_from_macro() { + macro_rules! mac { + () => { + println!("foo"); + println!("bar"); + }; + } + //~v if_then_some_else_none + let _ = if true { + mac!(); + Some(42) + } else { + None + }; +} + +fn dont_lint_inside_macros() { + macro_rules! mac { + ($cond:expr, $res:expr) => { + if $cond { Some($res) } else { None } + }; + } + let _: Option = mac!(true, 42); +} diff --git a/tests/ui/if_then_some_else_none.stderr b/tests/ui/if_then_some_else_none.stderr index c2e624a0a73b..58651a055942 100644 --- a/tests/ui/if_then_some_else_none.stderr +++ b/tests/ui/if_then_some_else_none.stderr @@ -116,5 +116,28 @@ LL | | None LL | | }); | |_________^ help: try: `(i % 2 == 0).then_some(closure_fn)` -error: aborting due to 11 previous errors +error: this could be simplified with `bool::then` + --> tests/ui/if_then_some_else_none.rs:231:13 + | +LL | / if self.count < 5 { +LL | | self.count += 1; +LL | | Some(self.count) +LL | | } else { +LL | | None +LL | | } + | |_____________^ help: try: `(self.count < 5).then(|| { self.count += 1; self.count })` + +error: this could be simplified with `bool::then` + --> tests/ui/if_then_some_else_none.rs:249:13 + | +LL | let _ = if true { + | _____________^ +LL | | mac!(); +LL | | Some(42) +LL | | } else { +LL | | None +LL | | }; + | |_____^ help: try: `true.then(|| { mac!(); 42 })` + +error: aborting due to 13 previous errors diff --git a/tests/ui/infinite_loops.rs b/tests/ui/infinite_loops.rs index 9b8c39331970..7d01a7fb61fc 100644 --- a/tests/ui/infinite_loops.rs +++ b/tests/ui/infinite_loops.rs @@ -521,4 +521,19 @@ mod tokio_spawn_test { } } +mod issue15541 { + async fn good() -> ! { + loop { + std::future::pending().await + } + } + + async fn bad() { + //~v infinite_loop + loop { + std::future::pending().await + } + } +} + fn main() {} diff --git a/tests/ui/infinite_loops.stderr b/tests/ui/infinite_loops.stderr index 4c6b6f725f13..319f1e5012b6 100644 --- a/tests/ui/infinite_loops.stderr +++ b/tests/ui/infinite_loops.stderr @@ -333,5 +333,15 @@ LL | | } | = help: if this is not intended, try adding a `break` or `return` condition in the loop -error: aborting due to 23 previous errors +error: infinite loop detected + --> tests/ui/infinite_loops.rs:533:9 + | +LL | / loop { +LL | | std::future::pending().await +LL | | } + | |_________^ + | + = help: if this is not intended, try adding a `break` or `return` condition in the loop + +error: aborting due to 24 previous errors diff --git a/tests/ui/manual_is_ascii_check.fixed b/tests/ui/manual_is_ascii_check.fixed index 7b0d19068346..406336dbd095 100644 --- a/tests/ui/manual_is_ascii_check.fixed +++ b/tests/ui/manual_is_ascii_check.fixed @@ -108,6 +108,8 @@ fn generics() { //~^ manual_is_ascii_check take_while(|c: char| c.is_ascii_uppercase()); //~^ manual_is_ascii_check + take_while(|c: char| c.is_ascii_uppercase()); + //~^ manual_is_ascii_check } fn adds_type_reference() { @@ -115,4 +117,6 @@ fn adds_type_reference() { //~^ manual_is_ascii_check let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c: &&mut char| c.is_ascii_digit()).collect(); //~^ manual_is_ascii_check + let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c: &&mut char| c.is_ascii_digit()).collect(); + //~^ manual_is_ascii_check } diff --git a/tests/ui/manual_is_ascii_check.rs b/tests/ui/manual_is_ascii_check.rs index e4f7fe9f5838..a624497e75e5 100644 --- a/tests/ui/manual_is_ascii_check.rs +++ b/tests/ui/manual_is_ascii_check.rs @@ -108,6 +108,8 @@ fn generics() { //~^ manual_is_ascii_check take_while(|c: char| ('A'..='Z').contains(&c)); //~^ manual_is_ascii_check + take_while(|c| matches!(c, 'A'..='Z')); + //~^ manual_is_ascii_check } fn adds_type_reference() { @@ -115,4 +117,6 @@ fn adds_type_reference() { //~^ manual_is_ascii_check let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| ('0'..='9').contains(c)).collect(); //~^ manual_is_ascii_check + let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| matches!(c, '0'..='9')).collect(); + //~^ manual_is_ascii_check } diff --git a/tests/ui/manual_is_ascii_check.stderr b/tests/ui/manual_is_ascii_check.stderr index 9fd7f457b420..cb2548ea7316 100644 --- a/tests/ui/manual_is_ascii_check.stderr +++ b/tests/ui/manual_is_ascii_check.stderr @@ -176,7 +176,19 @@ LL | take_while(|c: char| ('A'..='Z').contains(&c)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `c.is_ascii_uppercase()` error: manual check for common ascii range - --> tests/ui/manual_is_ascii_check.rs:114:63 + --> tests/ui/manual_is_ascii_check.rs:111:20 + | +LL | take_while(|c| matches!(c, 'A'..='Z')); + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL - take_while(|c| matches!(c, 'A'..='Z')); +LL + take_while(|c: char| c.is_ascii_uppercase()); + | + +error: manual check for common ascii range + --> tests/ui/manual_is_ascii_check.rs:116:63 | LL | let digits: Vec<&char> = ['1', 'A'].iter().take_while(|c| ('0'..='9').contains(c)).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -188,7 +200,7 @@ LL + let digits: Vec<&char> = ['1', 'A'].iter().take_while(|c: &&char| c.is_ | error: manual check for common ascii range - --> tests/ui/manual_is_ascii_check.rs:116:71 + --> tests/ui/manual_is_ascii_check.rs:118:71 | LL | let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| ('0'..='9').contains(c)).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -199,5 +211,17 @@ LL - let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| ('0'. LL + let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c: &&mut char| c.is_ascii_digit()).collect(); | -error: aborting due to 29 previous errors +error: manual check for common ascii range + --> tests/ui/manual_is_ascii_check.rs:120:71 + | +LL | let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| matches!(c, '0'..='9')).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL - let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| matches!(c, '0'..='9')).collect(); +LL + let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c: &&mut char| c.is_ascii_digit()).collect(); + | + +error: aborting due to 31 previous errors diff --git a/tests/ui/map_identity.fixed b/tests/ui/map_identity.fixed index 6c971ba63384..a10aae8cc121 100644 --- a/tests/ui/map_identity.fixed +++ b/tests/ui/map_identity.fixed @@ -1,3 +1,4 @@ +//@require-annotations-for-level: ERROR #![warn(clippy::map_identity)] #![allow(clippy::needless_return, clippy::disallowed_names)] @@ -72,20 +73,33 @@ fn issue11764() { } fn issue13904() { - // don't lint: `it.next()` would not be legal as `it` is immutable - let it = [1, 2, 3].into_iter(); - let _ = it.map(|x| x).next(); + // lint, but there's a catch: + // when we remove the `.map()`, `it.next()` would require `it` to be mutable + // therefore, include that in the suggestion as well + let mut it = [1, 2, 3].into_iter(); + let _ = it.next(); + //~^ map_identity + //~| HELP: remove the call to `map`, and make `it` mutable + + // lint + let mut index = [1, 2, 3].into_iter(); + let mut subindex = (index.by_ref().take(3), 42); + let _ = subindex.0.next(); + //~^ map_identity + //~| HELP: remove the call to `map`, and make `subindex` mutable // lint #[allow(unused_mut)] let mut it = [1, 2, 3].into_iter(); let _ = it.next(); //~^ map_identity + //~| HELP: remove the call to `map` // lint let it = [1, 2, 3].into_iter(); let _ = { it }.next(); //~^ map_identity + //~| HELP: remove the call to `map` } // same as `issue11764`, but for arrays diff --git a/tests/ui/map_identity.rs b/tests/ui/map_identity.rs index 59dcfcda3b6e..fc6e018f9248 100644 --- a/tests/ui/map_identity.rs +++ b/tests/ui/map_identity.rs @@ -1,3 +1,4 @@ +//@require-annotations-for-level: ERROR #![warn(clippy::map_identity)] #![allow(clippy::needless_return, clippy::disallowed_names)] @@ -78,20 +79,33 @@ fn issue11764() { } fn issue13904() { - // don't lint: `it.next()` would not be legal as `it` is immutable + // lint, but there's a catch: + // when we remove the `.map()`, `it.next()` would require `it` to be mutable + // therefore, include that in the suggestion as well let it = [1, 2, 3].into_iter(); let _ = it.map(|x| x).next(); + //~^ map_identity + //~| HELP: remove the call to `map`, and make `it` mutable + + // lint + let mut index = [1, 2, 3].into_iter(); + let subindex = (index.by_ref().take(3), 42); + let _ = subindex.0.map(|n| n).next(); + //~^ map_identity + //~| HELP: remove the call to `map`, and make `subindex` mutable // lint #[allow(unused_mut)] let mut it = [1, 2, 3].into_iter(); let _ = it.map(|x| x).next(); //~^ map_identity + //~| HELP: remove the call to `map` // lint let it = [1, 2, 3].into_iter(); let _ = { it }.map(|x| x).next(); //~^ map_identity + //~| HELP: remove the call to `map` } // same as `issue11764`, but for arrays diff --git a/tests/ui/map_identity.stderr b/tests/ui/map_identity.stderr index a50c0d6b87b5..8d19d62cdc64 100644 --- a/tests/ui/map_identity.stderr +++ b/tests/ui/map_identity.stderr @@ -1,5 +1,5 @@ error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:7:47 + --> tests/ui/map_identity.rs:8:47 | LL | let _: Vec<_> = x.iter().map(not_identity).map(|x| return x).collect(); | ^^^^^^^^^^^^^^^^^^ help: remove the call to `map` @@ -8,25 +8,25 @@ LL | let _: Vec<_> = x.iter().map(not_identity).map(|x| return x).collect(); = help: to override `-D warnings` add `#[allow(clippy::map_identity)]` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:9:57 + --> tests/ui/map_identity.rs:10:57 | LL | let _: Vec<_> = x.iter().map(std::convert::identity).map(|y| y).collect(); | ^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:9:29 + --> tests/ui/map_identity.rs:10:29 | LL | let _: Vec<_> = x.iter().map(std::convert::identity).map(|y| y).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:12:32 + --> tests/ui/map_identity.rs:13:32 | LL | let _: Option = Some(3).map(|x| x); | ^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:14:36 + --> tests/ui/map_identity.rs:15:36 | LL | let _: Result = Ok(-3).map(|x| { | ____________________________________^ @@ -36,19 +36,19 @@ LL | | }); | |______^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:25:36 + --> tests/ui/map_identity.rs:26:36 | LL | let _: Result = Ok(1).map_err(|a| a); | ^^^^^^^^^^^^^^^ help: remove the call to `map_err` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:36:22 + --> tests/ui/map_identity.rs:37:22 | LL | let _ = x.clone().map(|(x, y)| (x, y)); | ^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:38:22 + --> tests/ui/map_identity.rs:39:22 | LL | let _ = x.clone().map(|(x, y)| { | ______________________^ @@ -58,76 +58,100 @@ LL | | }); | |______^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:42:22 + --> tests/ui/map_identity.rs:43:22 | LL | let _ = x.clone().map(|(x, y)| return (x, y)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:46:22 + --> tests/ui/map_identity.rs:47:22 | LL | let _ = y.clone().map(|(x, y, (z, (w,)))| (x, y, (z, (w,)))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:76:30 + --> tests/ui/map_identity.rs:77:30 | LL | let _ = x.iter().copied().map(|(x, y)| (x, y)); | ^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:88:15 + --> tests/ui/map_identity.rs:86:15 + | +LL | let _ = it.map(|x| x).next(); + | ^^^^^^^^^^^ + | +help: remove the call to `map`, and make `it` mutable + | +LL ~ let mut it = [1, 2, 3].into_iter(); +LL ~ let _ = it.next(); + | + +error: unnecessary map of the identity function + --> tests/ui/map_identity.rs:93:23 + | +LL | let _ = subindex.0.map(|n| n).next(); + | ^^^^^^^^^^^ + | +help: remove the call to `map`, and make `subindex` mutable + | +LL ~ let mut subindex = (index.by_ref().take(3), 42); +LL ~ let _ = subindex.0.next(); + | + +error: unnecessary map of the identity function + --> tests/ui/map_identity.rs:100:15 | LL | let _ = it.map(|x| x).next(); | ^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:93:19 + --> tests/ui/map_identity.rs:106:19 | LL | let _ = { it }.map(|x| x).next(); | ^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:105:30 + --> tests/ui/map_identity.rs:119:30 | LL | let _ = x.iter().copied().map(|[x, y]| [x, y]); | ^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:131:26 + --> tests/ui/map_identity.rs:145:26 | LL | let _ = x.into_iter().map(|Foo { foo, bar }| Foo { foo, bar }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:135:26 + --> tests/ui/map_identity.rs:149:26 | LL | let _ = x.into_iter().map(|Foo { foo, bar }| foo::Foo { foo, bar }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:143:26 + --> tests/ui/map_identity.rs:157:26 | LL | let _ = x.into_iter().map(|Foo { foo, bar }| Foo { foo: foo, bar: bar }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:147:26 + --> tests/ui/map_identity.rs:161:26 | LL | let _ = x.into_iter().map(|Foo { foo, bar }| Foo { bar, foo }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:157:26 + --> tests/ui/map_identity.rs:171:26 | LL | let _ = x.into_iter().map(|Foo2(foo, bar)| Foo2(foo, bar)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` error: unnecessary map of the identity function - --> tests/ui/map_identity.rs:161:26 + --> tests/ui/map_identity.rs:175:26 | LL | let _ = x.into_iter().map(|Foo2(foo, bar)| foo::Foo2(foo, bar)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map` -error: aborting due to 20 previous errors +error: aborting due to 22 previous errors diff --git a/tests/ui/missing_inline.rs b/tests/ui/missing_inline.rs index 223c7447975a..8e937d609512 100644 --- a/tests/ui/missing_inline.rs +++ b/tests/ui/missing_inline.rs @@ -97,3 +97,10 @@ pub mod issue15301 { println!("Just called a Rust function from Rust!"); } } + +pub mod issue15491 { + pub trait Foo { + #[allow(clippy::missing_inline_in_public_items)] + fn foo(&self) {} + } +} diff --git a/tests/ui/missing_transmute_annotations_unfixable.rs b/tests/ui/missing_transmute_annotations_unfixable.rs new file mode 100644 index 000000000000..08ba3b791ee7 --- /dev/null +++ b/tests/ui/missing_transmute_annotations_unfixable.rs @@ -0,0 +1,29 @@ +//@no-rustfix + +fn issue14984() { + async fn e() {} + async fn x() -> u32 { + 0 + } + async fn y() -> f32 { + 0.0 + }; + let mut yy = unsafe { std::ptr::read(&y()) }; + yy = unsafe { std::mem::transmute(std::ptr::read(&x())) }; + //~^ missing_transmute_annotations + + let mut zz = 0u8; + zz = unsafe { std::mem::transmute(std::ptr::read(&x())) }; + //~^ missing_transmute_annotations + + yy = unsafe { std::mem::transmute(zz) }; + //~^ missing_transmute_annotations + + fn a() -> impl Sized { + 0u32 + } + + let mut b: f32 = 0.0; + b = unsafe { std::mem::transmute(a()) }; + //~^ missing_transmute_annotations +} diff --git a/tests/ui/missing_transmute_annotations_unfixable.stderr b/tests/ui/missing_transmute_annotations_unfixable.stderr new file mode 100644 index 000000000000..83efdce13f7e --- /dev/null +++ b/tests/ui/missing_transmute_annotations_unfixable.stderr @@ -0,0 +1,36 @@ +error: transmute used without annotations + --> tests/ui/missing_transmute_annotations_unfixable.rs:12:29 + | +LL | yy = unsafe { std::mem::transmute(std::ptr::read(&x())) }; + | ^^^^^^^^^ + | + = help: consider giving the source and destination types a name, and adding missing type annotations + = note: `-D clippy::missing-transmute-annotations` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::missing_transmute_annotations)]` + +error: transmute used without annotations + --> tests/ui/missing_transmute_annotations_unfixable.rs:16:29 + | +LL | zz = unsafe { std::mem::transmute(std::ptr::read(&x())) }; + | ^^^^^^^^^ + | + = help: consider giving the origin type a name, and adding missing type annotations + +error: transmute used without annotations + --> tests/ui/missing_transmute_annotations_unfixable.rs:19:29 + | +LL | yy = unsafe { std::mem::transmute(zz) }; + | ^^^^^^^^^ + | + = help: consider giving the destination type a name, and adding missing type annotations + +error: transmute used without annotations + --> tests/ui/missing_transmute_annotations_unfixable.rs:27:28 + | +LL | b = unsafe { std::mem::transmute(a()) }; + | ^^^^^^^^^ + | + = help: consider giving `a()`'s type a name, and adding missing type annotations + +error: aborting due to 4 previous errors + diff --git a/tests/ui/mut_reference.fixed b/tests/ui/mut_reference.fixed new file mode 100644 index 000000000000..03d854099e64 --- /dev/null +++ b/tests/ui/mut_reference.fixed @@ -0,0 +1,170 @@ +#![allow(clippy::mut_mut)] + +fn takes_ref(a: &i32) {} +fn takes_refmut(a: &mut i32) {} +fn takes_ref_ref(a: &&i32) {} +fn takes_refmut_ref(a: &mut &i32) {} +fn takes_ref_refmut(a: &&mut i32) {} +fn takes_refmut_refmut(a: &mut &mut i32) {} +fn takes_raw_const(a: *const i32) {} +fn takes_raw_mut(a: *mut i32) {} + +mod issue11268 { + macro_rules! x { + (1 $f:expr) => { + $f(&mut 1); + }; + (2 $f:expr) => { + $f(&mut &1) + }; + (3 $f:expr) => { + $f(&mut &mut 1) + }; + (4 $f:expr) => { + let mut a = 1; + $f(&raw mut a) + }; + } + + fn f() { + x!(1 super::takes_ref); + x!(1 super::takes_refmut); + x!(2 super::takes_refmut_ref); + x!(3 super::takes_ref_refmut); + x!(3 super::takes_refmut_refmut); + x!(4 super::takes_raw_const); + x!(4 super::takes_raw_mut); + } +} + +struct MyStruct; + +impl MyStruct { + fn takes_ref(&self, a: &i32) {} + fn takes_refmut(&self, a: &mut i32) {} + fn takes_ref_ref(&self, a: &&i32) {} + fn takes_refmut_ref(&self, a: &mut &i32) {} + fn takes_ref_refmut(&self, a: &&mut i32) {} + fn takes_refmut_refmut(&self, a: &mut &mut i32) {} + fn takes_raw_const(&self, a: *const i32) {} + fn takes_raw_mut(&self, a: *mut i32) {} +} + +#[warn(clippy::unnecessary_mut_passed)] +fn main() { + // Functions + takes_ref(&42); + //~^ unnecessary_mut_passed + takes_ref_ref(&&42); + //~^ unnecessary_mut_passed + takes_ref_refmut(&&mut 42); + //~^ unnecessary_mut_passed + takes_raw_const(&42); + //~^ unnecessary_mut_passed + + let as_ptr: fn(&i32) = takes_ref; + as_ptr(&42); + //~^ unnecessary_mut_passed + let as_ptr: fn(&&i32) = takes_ref_ref; + as_ptr(&&42); + //~^ unnecessary_mut_passed + let as_ptr: fn(&&mut i32) = takes_ref_refmut; + as_ptr(&&mut 42); + //~^ unnecessary_mut_passed + let as_ptr: fn(*const i32) = takes_raw_const; + as_ptr(&42); + //~^ unnecessary_mut_passed + + // Methods + let my_struct = MyStruct; + my_struct.takes_ref(&42); + //~^ unnecessary_mut_passed + my_struct.takes_ref_ref(&&42); + //~^ unnecessary_mut_passed + my_struct.takes_ref_refmut(&&mut 42); + //~^ unnecessary_mut_passed + my_struct.takes_raw_const(&42); + //~^ unnecessary_mut_passed + + // No error + + // Functions + takes_ref(&42); + let as_ptr: fn(&i32) = takes_ref; + as_ptr(&42); + + takes_refmut(&mut 42); + let as_ptr: fn(&mut i32) = takes_refmut; + as_ptr(&mut 42); + + takes_ref_ref(&&42); + let as_ptr: fn(&&i32) = takes_ref_ref; + as_ptr(&&42); + + takes_refmut_ref(&mut &42); + let as_ptr: fn(&mut &i32) = takes_refmut_ref; + as_ptr(&mut &42); + + takes_ref_refmut(&&mut 42); + let as_ptr: fn(&&mut i32) = takes_ref_refmut; + as_ptr(&&mut 42); + + takes_refmut_refmut(&mut &mut 42); + let as_ptr: fn(&mut &mut i32) = takes_refmut_refmut; + as_ptr(&mut &mut 42); + + takes_raw_const(&42); + let as_ptr: fn(*const i32) = takes_raw_const; + as_ptr(&42); + + takes_raw_mut(&mut 42); + let as_ptr: fn(*mut i32) = takes_raw_mut; + as_ptr(&mut 42); + + let a = &mut 42; + let b = &mut &42; + let c = &mut &mut 42; + takes_ref(a); + takes_ref_ref(b); + takes_ref_refmut(c); + takes_raw_const(a); + + // Methods + my_struct.takes_ref(&42); + my_struct.takes_refmut(&mut 42); + my_struct.takes_ref_ref(&&42); + my_struct.takes_refmut_ref(&mut &42); + my_struct.takes_ref_refmut(&&mut 42); + my_struct.takes_refmut_refmut(&mut &mut 42); + my_struct.takes_raw_const(&42); + my_struct.takes_raw_mut(&mut 42); + my_struct.takes_ref(a); + my_struct.takes_ref_ref(b); + my_struct.takes_ref_refmut(c); + my_struct.takes_raw_const(a); + my_struct.takes_raw_mut(a); +} + +// not supported currently +fn raw_ptrs(my_struct: MyStruct) { + let mut n = 42; + + takes_raw_const(&raw mut n); + + let as_ptr: fn(*const i32) = takes_raw_const; + as_ptr(&raw mut n); + + my_struct.takes_raw_const(&raw mut n); + + // No error + + takes_raw_const(&raw const n); + takes_raw_mut(&raw mut n); + + let a = &raw mut n; + takes_raw_const(a); + + my_struct.takes_raw_const(&raw const n); + my_struct.takes_raw_mut(&raw mut n); + my_struct.takes_raw_const(a); +} diff --git a/tests/ui/mut_reference.rs b/tests/ui/mut_reference.rs index f664c373cdc3..80e3f5069277 100644 --- a/tests/ui/mut_reference.rs +++ b/tests/ui/mut_reference.rs @@ -1,60 +1,170 @@ -#![allow(unused_variables, dead_code)] -//@no-rustfix -fn takes_an_immutable_reference(a: &i32) {} -fn takes_a_mutable_reference(a: &mut i32) {} +#![allow(clippy::mut_mut)] + +fn takes_ref(a: &i32) {} +fn takes_refmut(a: &mut i32) {} +fn takes_ref_ref(a: &&i32) {} +fn takes_refmut_ref(a: &mut &i32) {} +fn takes_ref_refmut(a: &&mut i32) {} +fn takes_refmut_refmut(a: &mut &mut i32) {} +fn takes_raw_const(a: *const i32) {} +fn takes_raw_mut(a: *mut i32) {} mod issue11268 { macro_rules! x { - ($f:expr) => { + (1 $f:expr) => { $f(&mut 1); }; + (2 $f:expr) => { + $f(&mut &1) + }; + (3 $f:expr) => { + $f(&mut &mut 1) + }; + (4 $f:expr) => { + let mut a = 1; + $f(&raw mut a) + }; } fn f() { - x!(super::takes_an_immutable_reference); - x!(super::takes_a_mutable_reference); + x!(1 super::takes_ref); + x!(1 super::takes_refmut); + x!(2 super::takes_refmut_ref); + x!(3 super::takes_ref_refmut); + x!(3 super::takes_refmut_refmut); + x!(4 super::takes_raw_const); + x!(4 super::takes_raw_mut); } } struct MyStruct; impl MyStruct { - fn takes_an_immutable_reference(&self, a: &i32) {} - - fn takes_a_mutable_reference(&self, a: &mut i32) {} + fn takes_ref(&self, a: &i32) {} + fn takes_refmut(&self, a: &mut i32) {} + fn takes_ref_ref(&self, a: &&i32) {} + fn takes_refmut_ref(&self, a: &mut &i32) {} + fn takes_ref_refmut(&self, a: &&mut i32) {} + fn takes_refmut_refmut(&self, a: &mut &mut i32) {} + fn takes_raw_const(&self, a: *const i32) {} + fn takes_raw_mut(&self, a: *mut i32) {} } #[warn(clippy::unnecessary_mut_passed)] fn main() { // Functions - takes_an_immutable_reference(&mut 42); + takes_ref(&mut 42); + //~^ unnecessary_mut_passed + takes_ref_ref(&mut &42); + //~^ unnecessary_mut_passed + takes_ref_refmut(&mut &mut 42); + //~^ unnecessary_mut_passed + takes_raw_const(&mut 42); //~^ unnecessary_mut_passed - let as_ptr: fn(&i32) = takes_an_immutable_reference; + let as_ptr: fn(&i32) = takes_ref; + as_ptr(&mut 42); + //~^ unnecessary_mut_passed + let as_ptr: fn(&&i32) = takes_ref_ref; + as_ptr(&mut &42); + //~^ unnecessary_mut_passed + let as_ptr: fn(&&mut i32) = takes_ref_refmut; + as_ptr(&mut &mut 42); + //~^ unnecessary_mut_passed + let as_ptr: fn(*const i32) = takes_raw_const; as_ptr(&mut 42); //~^ unnecessary_mut_passed // Methods let my_struct = MyStruct; - my_struct.takes_an_immutable_reference(&mut 42); + my_struct.takes_ref(&mut 42); + //~^ unnecessary_mut_passed + my_struct.takes_ref_ref(&mut &42); + //~^ unnecessary_mut_passed + my_struct.takes_ref_refmut(&mut &mut 42); + //~^ unnecessary_mut_passed + my_struct.takes_raw_const(&mut 42); //~^ unnecessary_mut_passed // No error // Functions - takes_an_immutable_reference(&42); - let as_ptr: fn(&i32) = takes_an_immutable_reference; + takes_ref(&42); + let as_ptr: fn(&i32) = takes_ref; as_ptr(&42); - takes_a_mutable_reference(&mut 42); - let as_ptr: fn(&mut i32) = takes_a_mutable_reference; + takes_refmut(&mut 42); + let as_ptr: fn(&mut i32) = takes_refmut; + as_ptr(&mut 42); + + takes_ref_ref(&&42); + let as_ptr: fn(&&i32) = takes_ref_ref; + as_ptr(&&42); + + takes_refmut_ref(&mut &42); + let as_ptr: fn(&mut &i32) = takes_refmut_ref; + as_ptr(&mut &42); + + takes_ref_refmut(&&mut 42); + let as_ptr: fn(&&mut i32) = takes_ref_refmut; + as_ptr(&&mut 42); + + takes_refmut_refmut(&mut &mut 42); + let as_ptr: fn(&mut &mut i32) = takes_refmut_refmut; + as_ptr(&mut &mut 42); + + takes_raw_const(&42); + let as_ptr: fn(*const i32) = takes_raw_const; + as_ptr(&42); + + takes_raw_mut(&mut 42); + let as_ptr: fn(*mut i32) = takes_raw_mut; as_ptr(&mut 42); let a = &mut 42; - takes_an_immutable_reference(a); + let b = &mut &42; + let c = &mut &mut 42; + takes_ref(a); + takes_ref_ref(b); + takes_ref_refmut(c); + takes_raw_const(a); // Methods - my_struct.takes_an_immutable_reference(&42); - my_struct.takes_a_mutable_reference(&mut 42); - my_struct.takes_an_immutable_reference(a); + my_struct.takes_ref(&42); + my_struct.takes_refmut(&mut 42); + my_struct.takes_ref_ref(&&42); + my_struct.takes_refmut_ref(&mut &42); + my_struct.takes_ref_refmut(&&mut 42); + my_struct.takes_refmut_refmut(&mut &mut 42); + my_struct.takes_raw_const(&42); + my_struct.takes_raw_mut(&mut 42); + my_struct.takes_ref(a); + my_struct.takes_ref_ref(b); + my_struct.takes_ref_refmut(c); + my_struct.takes_raw_const(a); + my_struct.takes_raw_mut(a); +} + +// not supported currently +fn raw_ptrs(my_struct: MyStruct) { + let mut n = 42; + + takes_raw_const(&raw mut n); + + let as_ptr: fn(*const i32) = takes_raw_const; + as_ptr(&raw mut n); + + my_struct.takes_raw_const(&raw mut n); + + // No error + + takes_raw_const(&raw const n); + takes_raw_mut(&raw mut n); + + let a = &raw mut n; + takes_raw_const(a); + + my_struct.takes_raw_const(&raw const n); + my_struct.takes_raw_mut(&raw mut n); + my_struct.takes_raw_const(a); } diff --git a/tests/ui/mut_reference.stderr b/tests/ui/mut_reference.stderr index 474221329c25..5ecfaa37416b 100644 --- a/tests/ui/mut_reference.stderr +++ b/tests/ui/mut_reference.stderr @@ -1,23 +1,77 @@ -error: the function `takes_an_immutable_reference` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:30:34 +error: the function `takes_ref` doesn't need a mutable reference + --> tests/ui/mut_reference.rs:56:15 | -LL | takes_an_immutable_reference(&mut 42); - | ^^^^^^^ +LL | takes_ref(&mut 42); + | ^^^^^^^ help: remove this `mut`: `&42` | = note: `-D clippy::unnecessary-mut-passed` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_mut_passed)]` +error: the function `takes_ref_ref` doesn't need a mutable reference + --> tests/ui/mut_reference.rs:58:19 + | +LL | takes_ref_ref(&mut &42); + | ^^^^^^^^ help: remove this `mut`: `&&42` + +error: the function `takes_ref_refmut` doesn't need a mutable reference + --> tests/ui/mut_reference.rs:60:22 + | +LL | takes_ref_refmut(&mut &mut 42); + | ^^^^^^^^^^^^ help: remove this `mut`: `&&mut 42` + +error: the function `takes_raw_const` doesn't need a mutable reference + --> tests/ui/mut_reference.rs:62:21 + | +LL | takes_raw_const(&mut 42); + | ^^^^^^^ help: remove this `mut`: `&42` + error: the function `as_ptr` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:34:12 + --> tests/ui/mut_reference.rs:66:12 | LL | as_ptr(&mut 42); - | ^^^^^^^ + | ^^^^^^^ help: remove this `mut`: `&42` -error: the method `takes_an_immutable_reference` doesn't need a mutable reference - --> tests/ui/mut_reference.rs:39:44 +error: the function `as_ptr` doesn't need a mutable reference + --> tests/ui/mut_reference.rs:69:12 | -LL | my_struct.takes_an_immutable_reference(&mut 42); - | ^^^^^^^ +LL | as_ptr(&mut &42); + | ^^^^^^^^ help: remove this `mut`: `&&42` -error: aborting due to 3 previous errors +error: the function `as_ptr` doesn't need a mutable reference + --> tests/ui/mut_reference.rs:72:12 + | +LL | as_ptr(&mut &mut 42); + | ^^^^^^^^^^^^ help: remove this `mut`: `&&mut 42` + +error: the function `as_ptr` doesn't need a mutable reference + --> tests/ui/mut_reference.rs:75:12 + | +LL | as_ptr(&mut 42); + | ^^^^^^^ help: remove this `mut`: `&42` + +error: the method `takes_ref` doesn't need a mutable reference + --> tests/ui/mut_reference.rs:80:25 + | +LL | my_struct.takes_ref(&mut 42); + | ^^^^^^^ help: remove this `mut`: `&42` + +error: the method `takes_ref_ref` doesn't need a mutable reference + --> tests/ui/mut_reference.rs:82:29 + | +LL | my_struct.takes_ref_ref(&mut &42); + | ^^^^^^^^ help: remove this `mut`: `&&42` + +error: the method `takes_ref_refmut` doesn't need a mutable reference + --> tests/ui/mut_reference.rs:84:32 + | +LL | my_struct.takes_ref_refmut(&mut &mut 42); + | ^^^^^^^^^^^^ help: remove this `mut`: `&&mut 42` + +error: the method `takes_raw_const` doesn't need a mutable reference + --> tests/ui/mut_reference.rs:86:31 + | +LL | my_struct.takes_raw_const(&mut 42); + | ^^^^^^^ help: remove this `mut`: `&42` + +error: aborting due to 12 previous errors diff --git a/tests/ui/needless_bool/fixable.stderr b/tests/ui/needless_bool/fixable.stderr index 3f117ee5a502..9404d07ba0e0 100644 --- a/tests/ui/needless_bool/fixable.stderr +++ b/tests/ui/needless_bool/fixable.stderr @@ -135,7 +135,7 @@ error: equality checks against true are unnecessary --> tests/ui/needless_bool/fixable.rs:157:8 | LL | if x == true {}; - | ^^^^^^^^^ help: try simplifying it as shown: `x` + | ^^^^^^^^^ help: try: `x` | = note: `-D clippy::bool-comparison` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::bool_comparison)]` @@ -144,19 +144,19 @@ error: equality checks against false can be replaced by a negation --> tests/ui/needless_bool/fixable.rs:162:8 | LL | if x == false {}; - | ^^^^^^^^^^ help: try simplifying it as shown: `!x` + | ^^^^^^^^^^ help: try: `!x` error: equality checks against true are unnecessary --> tests/ui/needless_bool/fixable.rs:173:8 | LL | if x == true {}; - | ^^^^^^^^^ help: try simplifying it as shown: `x` + | ^^^^^^^^^ help: try: `x` error: equality checks against false can be replaced by a negation --> tests/ui/needless_bool/fixable.rs:175:8 | LL | if x == false {}; - | ^^^^^^^^^^ help: try simplifying it as shown: `!x` + | ^^^^^^^^^^ help: try: `!x` error: this if-then-else expression returns a bool literal --> tests/ui/needless_bool/fixable.rs:185:12 diff --git a/tests/ui/needless_for_each_unfixable.rs b/tests/ui/needless_for_each_unfixable.rs index 159cbe49828f..24ce5786b910 100644 --- a/tests/ui/needless_for_each_unfixable.rs +++ b/tests/ui/needless_for_each_unfixable.rs @@ -1,6 +1,6 @@ //@no-rustfix: overlapping suggestions #![warn(clippy::needless_for_each)] -#![allow(clippy::needless_return, clippy::uninlined_format_args)] +#![allow(clippy::needless_return)] fn main() { let v: Vec = Vec::new(); @@ -11,7 +11,22 @@ fn main() { if *v == 10 { return; } else { - println!("{}", v); + println!("{v}"); } }); } + +fn issue9912() { + let mut i = 0; + // Changing this to a `for` loop would break type inference + [].iter().for_each(move |_: &i32| { + //~^ needless_for_each + i += 1; + }); + + // Changing this would actually be okay, but we still suggest `MaybeIncorrect`ly + [1i32].iter().for_each(move |_: &i32| { + //~^ needless_for_each + i += 1; + }); +} diff --git a/tests/ui/needless_for_each_unfixable.stderr b/tests/ui/needless_for_each_unfixable.stderr index 3a3a240c5a3a..6bc56db68a0e 100644 --- a/tests/ui/needless_for_each_unfixable.stderr +++ b/tests/ui/needless_for_each_unfixable.stderr @@ -19,7 +19,7 @@ LL + LL + if *v == 10 { LL + return; LL + } else { -LL + println!("{}", v); +LL + println!("{v}"); LL + } LL + } | @@ -29,5 +29,39 @@ LL - return; LL + continue; | -error: aborting due to 1 previous error +error: needless use of `for_each` + --> tests/ui/needless_for_each_unfixable.rs:22:5 + | +LL | / [].iter().for_each(move |_: &i32| { +LL | | +LL | | i += 1; +LL | | }); + | |_______^ + | +help: try + | +LL ~ for _ in [].iter() { +LL + +LL + i += 1; +LL + } + | + +error: needless use of `for_each` + --> tests/ui/needless_for_each_unfixable.rs:28:5 + | +LL | / [1i32].iter().for_each(move |_: &i32| { +LL | | +LL | | i += 1; +LL | | }); + | |_______^ + | +help: try + | +LL ~ for _ in [1i32].iter() { +LL + +LL + i += 1; +LL + } + | + +error: aborting due to 3 previous errors diff --git a/tests/ui/needless_range_loop.rs b/tests/ui/needless_range_loop.rs index 70cf9fa7369f..ea4591d8b71a 100644 --- a/tests/ui/needless_range_loop.rs +++ b/tests/ui/needless_range_loop.rs @@ -210,3 +210,35 @@ fn needless_loop() { black_box([1, 2, 3, 4, 5, 6, 7, 8][i]); } } + +fn issue_15068() { + let a = vec![vec![0u8; MAX_LEN]; MAX_LEN]; + let b = vec![0u8; MAX_LEN]; + + for i in 0..MAX_LEN { + // no error + let _ = a[0][i]; + let _ = b[i]; + } + + for i in 0..MAX_LEN { + // no error + let _ = a[i][0]; + let _ = b[i]; + } + + for i in 0..MAX_LEN { + // no error + let _ = a[i][b[i] as usize]; + } + + for i in 0..MAX_LEN { + //~^ needless_range_loop + let _ = a[i][i]; + } + + for i in 0..MAX_LEN { + //~^ needless_range_loop + let _ = a[0][i]; + } +} diff --git a/tests/ui/needless_range_loop.stderr b/tests/ui/needless_range_loop.stderr index 23c085f9d3b2..33a519d8a80d 100644 --- a/tests/ui/needless_range_loop.stderr +++ b/tests/ui/needless_range_loop.stderr @@ -168,5 +168,29 @@ LL - for i in 0..vec.len() { LL + for (i, ) in vec.iter_mut().enumerate() { | -error: aborting due to 14 previous errors +error: the loop variable `i` is used to index `a` + --> tests/ui/needless_range_loop.rs:235:14 + | +LL | for i in 0..MAX_LEN { + | ^^^^^^^^^^ + | +help: consider using an iterator and enumerate() + | +LL - for i in 0..MAX_LEN { +LL + for (i, ) in a.iter().enumerate().take(MAX_LEN) { + | + +error: the loop variable `i` is only used to index `a` + --> tests/ui/needless_range_loop.rs:240:14 + | +LL | for i in 0..MAX_LEN { + | ^^^^^^^^^^ + | +help: consider using an iterator + | +LL - for i in 0..MAX_LEN { +LL + for in a.iter().take(MAX_LEN) { + | + +error: aborting due to 16 previous errors diff --git a/tests/ui/nonminimal_bool.rs b/tests/ui/nonminimal_bool.rs index cacce9a7d1cb..f03f74dfafe2 100644 --- a/tests/ui/nonminimal_bool.rs +++ b/tests/ui/nonminimal_bool.rs @@ -182,14 +182,12 @@ fn issue_5794() { if !b == true {} //~^ nonminimal_bool //~| bool_comparison - //~| bool_comparison if !b != true {} //~^ nonminimal_bool //~| bool_comparison if true == !b {} //~^ nonminimal_bool //~| bool_comparison - //~| bool_comparison if true != !b {} //~^ nonminimal_bool //~| bool_comparison diff --git a/tests/ui/nonminimal_bool.stderr b/tests/ui/nonminimal_bool.stderr index c20412974b20..6a20b9216da5 100644 --- a/tests/ui/nonminimal_bool.stderr +++ b/tests/ui/nonminimal_bool.stderr @@ -154,98 +154,86 @@ error: this boolean expression can be simplified LL | if !b == true {} | ^^^^^^^^^^ help: try: `b != true` -error: this comparison might be written more concisely - --> tests/ui/nonminimal_bool.rs:182:8 - | -LL | if !b == true {} - | ^^^^^^^^^^ help: try simplifying it as shown: `b != true` - | - = note: `-D clippy::bool-comparison` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::bool_comparison)]` - error: equality checks against true are unnecessary --> tests/ui/nonminimal_bool.rs:182:8 | LL | if !b == true {} - | ^^^^^^^^^^ help: try simplifying it as shown: `!b` + | ^^^^^^^^^^ help: try: `!b` + | + = note: `-D clippy::bool-comparison` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::bool_comparison)]` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:186:8 + --> tests/ui/nonminimal_bool.rs:185:8 | LL | if !b != true {} | ^^^^^^^^^^ help: try: `b == true` error: inequality checks against true can be replaced by a negation - --> tests/ui/nonminimal_bool.rs:186:8 + --> tests/ui/nonminimal_bool.rs:185:8 | LL | if !b != true {} - | ^^^^^^^^^^ help: try simplifying it as shown: `b` + | ^^^^^^^^^^ help: try: `b` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:189:8 + --> tests/ui/nonminimal_bool.rs:188:8 | LL | if true == !b {} | ^^^^^^^^^^ help: try: `true != b` -error: this comparison might be written more concisely - --> tests/ui/nonminimal_bool.rs:189:8 - | -LL | if true == !b {} - | ^^^^^^^^^^ help: try simplifying it as shown: `true != b` - error: equality checks against true are unnecessary - --> tests/ui/nonminimal_bool.rs:189:8 + --> tests/ui/nonminimal_bool.rs:188:8 | LL | if true == !b {} - | ^^^^^^^^^^ help: try simplifying it as shown: `!b` + | ^^^^^^^^^^ help: try: `!b` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:193:8 + --> tests/ui/nonminimal_bool.rs:191:8 | LL | if true != !b {} | ^^^^^^^^^^ help: try: `true == b` error: inequality checks against true can be replaced by a negation - --> tests/ui/nonminimal_bool.rs:193:8 + --> tests/ui/nonminimal_bool.rs:191:8 | LL | if true != !b {} - | ^^^^^^^^^^ help: try simplifying it as shown: `b` + | ^^^^^^^^^^ help: try: `b` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:196:8 + --> tests/ui/nonminimal_bool.rs:194:8 | LL | if !b == !c {} | ^^^^^^^^ help: try: `b == c` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:198:8 + --> tests/ui/nonminimal_bool.rs:196:8 | LL | if !b != !c {} | ^^^^^^^^ help: try: `b != c` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:214:8 + --> tests/ui/nonminimal_bool.rs:212:8 | LL | if !(a < 2.0 && !b) { | ^^^^^^^^^^^^^^^^ help: try: `a >= 2.0 || b` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:233:12 + --> tests/ui/nonminimal_bool.rs:231:12 | LL | if !(matches!(ty, TyKind::Ref(_, _, _)) && !is_mutable(&expr)) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `!matches!(ty, TyKind::Ref(_, _, _)) || is_mutable(&expr)` error: this boolean expression can be simplified - --> tests/ui/nonminimal_bool.rs:253:8 + --> tests/ui/nonminimal_bool.rs:251:8 | LL | if !S != true {} | ^^^^^^^^^^ help: try: `S == true` error: inequality checks against true can be replaced by a negation - --> tests/ui/nonminimal_bool.rs:253:8 + --> tests/ui/nonminimal_bool.rs:251:8 | LL | if !S != true {} - | ^^^^^^^^^^ help: try simplifying it as shown: `!!S` + | ^^^^^^^^^^ help: try: `!!S` -error: aborting due to 33 previous errors +error: aborting due to 31 previous errors diff --git a/tests/ui/option_map_unit_fn_fixable.fixed b/tests/ui/option_map_unit_fn_fixable.fixed index 55c1b8f110ce..340be7c7e932 100644 --- a/tests/ui/option_map_unit_fn_fixable.fixed +++ b/tests/ui/option_map_unit_fn_fixable.fixed @@ -102,4 +102,13 @@ fn option_map_unit_fn() { //~^ option_map_unit_fn } +fn issue15568() { + unsafe fn f(_: u32) {} + let x = Some(3); + if let Some(x) = x { unsafe { f(x) } } + //~^ option_map_unit_fn + if let Some(x) = x { unsafe { f(x) } } + //~^ option_map_unit_fn +} + fn main() {} diff --git a/tests/ui/option_map_unit_fn_fixable.rs b/tests/ui/option_map_unit_fn_fixable.rs index 5ed47e4c60b5..d902c87379b7 100644 --- a/tests/ui/option_map_unit_fn_fixable.rs +++ b/tests/ui/option_map_unit_fn_fixable.rs @@ -102,4 +102,13 @@ fn option_map_unit_fn() { //~^ option_map_unit_fn } +fn issue15568() { + unsafe fn f(_: u32) {} + let x = Some(3); + x.map(|x| unsafe { f(x) }); + //~^ option_map_unit_fn + x.map(|x| unsafe { { f(x) } }); + //~^ option_map_unit_fn +} + fn main() {} diff --git a/tests/ui/option_map_unit_fn_fixable.stderr b/tests/ui/option_map_unit_fn_fixable.stderr index 3f7abae34eea..2405aa9a7ccf 100644 --- a/tests/ui/option_map_unit_fn_fixable.stderr +++ b/tests/ui/option_map_unit_fn_fixable.stderr @@ -153,5 +153,21 @@ LL | option().map(|value| println!("{:?}", value)); | | | help: try: `if let Some(value) = option() { println!("{:?}", value) }` -error: aborting due to 19 previous errors +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` + --> tests/ui/option_map_unit_fn_fixable.rs:108:5 + | +LL | x.map(|x| unsafe { f(x) }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try: `if let Some(x) = x { unsafe { f(x) } }` + +error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()` + --> tests/ui/option_map_unit_fn_fixable.rs:110:5 + | +LL | x.map(|x| unsafe { { f(x) } }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: try: `if let Some(x) = x { unsafe { f(x) } }` + +error: aborting due to 21 previous errors diff --git a/tests/ui/or_then_unwrap.fixed b/tests/ui/or_then_unwrap.fixed index ba9beef57afa..9660b82fe7d6 100644 --- a/tests/ui/or_then_unwrap.fixed +++ b/tests/ui/or_then_unwrap.fixed @@ -28,6 +28,12 @@ fn main() { // //~^^ or_then_unwrap + // Call with macro should preserve the macro call rather than expand it + let option: Option> = None; + let _ = option.unwrap_or(vec!["fallback"]); // should trigger lint + // + //~^^ or_then_unwrap + // as part of a method chain let option: Option<&str> = None; let _ = option.map(|v| v).unwrap_or("fallback").to_string().chars(); // should trigger lint diff --git a/tests/ui/or_then_unwrap.rs b/tests/ui/or_then_unwrap.rs index fac90249a243..c38733521164 100644 --- a/tests/ui/or_then_unwrap.rs +++ b/tests/ui/or_then_unwrap.rs @@ -28,6 +28,12 @@ fn main() { // //~^^ or_then_unwrap + // Call with macro should preserve the macro call rather than expand it + let option: Option> = None; + let _ = option.or(Some(vec!["fallback"])).unwrap(); // should trigger lint + // + //~^^ or_then_unwrap + // as part of a method chain let option: Option<&str> = None; let _ = option.map(|v| v).or(Some("fallback")).unwrap().to_string().chars(); // should trigger lint diff --git a/tests/ui/or_then_unwrap.stderr b/tests/ui/or_then_unwrap.stderr index 1160498c6053..3e66b15edbd6 100644 --- a/tests/ui/or_then_unwrap.stderr +++ b/tests/ui/or_then_unwrap.stderr @@ -14,10 +14,16 @@ LL | let _ = result.or::<&str>(Ok("fallback")).unwrap(); // should trigger l | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or("fallback")` error: found `.or(Some(…)).unwrap()` - --> tests/ui/or_then_unwrap.rs:33:31 + --> tests/ui/or_then_unwrap.rs:33:20 + | +LL | let _ = option.or(Some(vec!["fallback"])).unwrap(); // should trigger lint + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or(vec!["fallback"])` + +error: found `.or(Some(…)).unwrap()` + --> tests/ui/or_then_unwrap.rs:39:31 | LL | let _ = option.map(|v| v).or(Some("fallback")).unwrap().to_string().chars(); // should trigger lint | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or("fallback")` -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/panicking_macros.rs b/tests/ui/panicking_macros.rs index 65854d7eb4bf..b044be7d54ac 100644 --- a/tests/ui/panicking_macros.rs +++ b/tests/ui/panicking_macros.rs @@ -31,6 +31,20 @@ fn panic() { let b = a + 2; } +const fn panic_const() { + let a = 2; + panic!(); + //~^ panic + + panic!("message"); + //~^ panic + + panic!("{} {}", "panic with", "multiple arguments"); + //~^ panic + + let b = a + 2; +} + fn todo() { let a = 2; todo!(); @@ -114,6 +128,7 @@ fn debug_assert_msg() { fn main() { panic(); + panic_const(); todo(); unimplemented(); unreachable(); diff --git a/tests/ui/panicking_macros.stderr b/tests/ui/panicking_macros.stderr index 03e459e4ec6e..7b767ae0f65b 100644 --- a/tests/ui/panicking_macros.stderr +++ b/tests/ui/panicking_macros.stderr @@ -19,9 +19,27 @@ error: `panic` should not be present in production code LL | panic!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: `todo` should not be present in production code +error: `panic` should not be present in production code --> tests/ui/panicking_macros.rs:36:5 | +LL | panic!(); + | ^^^^^^^^ + +error: `panic` should not be present in production code + --> tests/ui/panicking_macros.rs:39:5 + | +LL | panic!("message"); + | ^^^^^^^^^^^^^^^^^ + +error: `panic` should not be present in production code + --> tests/ui/panicking_macros.rs:42:5 + | +LL | panic!("{} {}", "panic with", "multiple arguments"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `todo` should not be present in production code + --> tests/ui/panicking_macros.rs:50:5 + | LL | todo!(); | ^^^^^^^ | @@ -29,19 +47,19 @@ LL | todo!(); = help: to override `-D warnings` add `#[allow(clippy::todo)]` error: `todo` should not be present in production code - --> tests/ui/panicking_macros.rs:39:5 + --> tests/ui/panicking_macros.rs:53:5 | LL | todo!("message"); | ^^^^^^^^^^^^^^^^ error: `todo` should not be present in production code - --> tests/ui/panicking_macros.rs:42:5 + --> tests/ui/panicking_macros.rs:56:5 | LL | todo!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `unimplemented` should not be present in production code - --> tests/ui/panicking_macros.rs:50:5 + --> tests/ui/panicking_macros.rs:64:5 | LL | unimplemented!(); | ^^^^^^^^^^^^^^^^ @@ -50,19 +68,19 @@ LL | unimplemented!(); = help: to override `-D warnings` add `#[allow(clippy::unimplemented)]` error: `unimplemented` should not be present in production code - --> tests/ui/panicking_macros.rs:53:5 + --> tests/ui/panicking_macros.rs:67:5 | LL | unimplemented!("message"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: `unimplemented` should not be present in production code - --> tests/ui/panicking_macros.rs:56:5 + --> tests/ui/panicking_macros.rs:70:5 | LL | unimplemented!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: usage of the `unreachable!` macro - --> tests/ui/panicking_macros.rs:64:5 + --> tests/ui/panicking_macros.rs:78:5 | LL | unreachable!(); | ^^^^^^^^^^^^^^ @@ -71,40 +89,40 @@ LL | unreachable!(); = help: to override `-D warnings` add `#[allow(clippy::unreachable)]` error: usage of the `unreachable!` macro - --> tests/ui/panicking_macros.rs:67:5 + --> tests/ui/panicking_macros.rs:81:5 | LL | unreachable!("message"); | ^^^^^^^^^^^^^^^^^^^^^^^ error: usage of the `unreachable!` macro - --> tests/ui/panicking_macros.rs:70:5 + --> tests/ui/panicking_macros.rs:84:5 | LL | unreachable!("{} {}", "panic with", "multiple arguments"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `panic` should not be present in production code - --> tests/ui/panicking_macros.rs:78:5 + --> tests/ui/panicking_macros.rs:92:5 | LL | panic!(); | ^^^^^^^^ error: `todo` should not be present in production code - --> tests/ui/panicking_macros.rs:81:5 + --> tests/ui/panicking_macros.rs:95:5 | LL | todo!(); | ^^^^^^^ error: `unimplemented` should not be present in production code - --> tests/ui/panicking_macros.rs:84:5 + --> tests/ui/panicking_macros.rs:98:5 | LL | unimplemented!(); | ^^^^^^^^^^^^^^^^ error: usage of the `unreachable!` macro - --> tests/ui/panicking_macros.rs:87:5 + --> tests/ui/panicking_macros.rs:101:5 | LL | unreachable!(); | ^^^^^^^^^^^^^^ -error: aborting due to 16 previous errors +error: aborting due to 19 previous errors diff --git a/tests/ui/print_literal.fixed b/tests/ui/print_literal.fixed index ebfe19c700ee..26139f9b6710 100644 --- a/tests/ui/print_literal.fixed +++ b/tests/ui/print_literal.fixed @@ -105,3 +105,16 @@ fn issue_14930() { println!("Hello x is {0:2$.1$}", 0.01, 2, 3); //~^ print_literal } + +fn issue_15576() { + println!("Hello x is {1:.*}", 5, 0.01); + //~^ print_literal + + println!("Hello x is {:.p$}", 0.01, p = 5); + //~^ print_literal + + println!( + "Hello name: x is {1:.*} (which {1} with {0} places)", 5, 0.01 + ); + //~^^ print_literal +} diff --git a/tests/ui/print_literal.rs b/tests/ui/print_literal.rs index 8f3d9be06985..7c4cf028e84c 100644 --- a/tests/ui/print_literal.rs +++ b/tests/ui/print_literal.rs @@ -106,3 +106,17 @@ fn issue_14930() { println!("Hello {0} is {1:3$.2$}", "x", 0.01, 2, 3); //~^ print_literal } + +fn issue_15576() { + println!("Hello {} is {2:.*}", "x", 5, 0.01); + //~^ print_literal + + println!("Hello {} is {:.p$}", "x", 0.01, p = 5); + //~^ print_literal + + println!( + "Hello {}: {2} is {3:.*} (which {3} with {1} places)", + "name", 5, "x", 0.01 + ); + //~^^ print_literal +} diff --git a/tests/ui/print_literal.stderr b/tests/ui/print_literal.stderr index 1c378017d151..c136f52800f6 100644 --- a/tests/ui/print_literal.stderr +++ b/tests/ui/print_literal.stderr @@ -277,5 +277,41 @@ LL - println!("Hello {0} is {1:3$.2$}", "x", 0.01, 2, 3); LL + println!("Hello x is {0:2$.1$}", 0.01, 2, 3); | -error: aborting due to 22 previous errors +error: literal with an empty format string + --> tests/ui/print_literal.rs:111:36 + | +LL | println!("Hello {} is {2:.*}", "x", 5, 0.01); + | ^^^ + | +help: try + | +LL - println!("Hello {} is {2:.*}", "x", 5, 0.01); +LL + println!("Hello x is {1:.*}", 5, 0.01); + | + +error: literal with an empty format string + --> tests/ui/print_literal.rs:114:36 + | +LL | println!("Hello {} is {:.p$}", "x", 0.01, p = 5); + | ^^^ + | +help: try + | +LL - println!("Hello {} is {:.p$}", "x", 0.01, p = 5); +LL + println!("Hello x is {:.p$}", 0.01, p = 5); + | + +error: literal with an empty format string + --> tests/ui/print_literal.rs:119:9 + | +LL | "name", 5, "x", 0.01 + | ^^^^^^^^^^^^^^ + | +help: try + | +LL - "Hello {}: {2} is {3:.*} (which {3} with {1} places)", +LL + "Hello name: x is {1:.*} (which {1} with {0} places)", 5, 0.01 + | + +error: aborting due to 25 previous errors diff --git a/tests/ui/ptr_as_ptr.fixed b/tests/ui/ptr_as_ptr.fixed index 78e1ceb480a5..4457878b6faf 100644 --- a/tests/ui/ptr_as_ptr.fixed +++ b/tests/ui/ptr_as_ptr.fixed @@ -229,3 +229,9 @@ fn issue15283() { //~^ ptr_as_ptr } } + +fn issue15232() { + let mut b = Box::new(0i32); + let _ptr = std::ptr::addr_of_mut!(*b).cast::<()>(); + //~^ ptr_as_ptr +} diff --git a/tests/ui/ptr_as_ptr.rs b/tests/ui/ptr_as_ptr.rs index 70732cf0a6c1..9124fc912d2c 100644 --- a/tests/ui/ptr_as_ptr.rs +++ b/tests/ui/ptr_as_ptr.rs @@ -229,3 +229,9 @@ fn issue15283() { //~^ ptr_as_ptr } } + +fn issue15232() { + let mut b = Box::new(0i32); + let _ptr = std::ptr::addr_of_mut!(*b) as *mut (); + //~^ ptr_as_ptr +} diff --git a/tests/ui/ptr_as_ptr.stderr b/tests/ui/ptr_as_ptr.stderr index c0a2a4b6d204..af21c1e35f52 100644 --- a/tests/ui/ptr_as_ptr.stderr +++ b/tests/ui/ptr_as_ptr.stderr @@ -199,5 +199,11 @@ error: `as` casting between raw pointers without changing their constness LL | let _: fn() = std::mem::transmute(std::ptr::null::<()>() as *const u8); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null::()` -error: aborting due to 33 previous errors +error: `as` casting between raw pointers without changing their constness + --> tests/ui/ptr_as_ptr.rs:235:16 + | +LL | let _ptr = std::ptr::addr_of_mut!(*b) as *mut (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `std::ptr::addr_of_mut!(*b).cast::<()>()` + +error: aborting due to 34 previous errors diff --git a/tests/ui/ptr_cast_constness.fixed b/tests/ui/ptr_cast_constness.fixed index 79bfae1f7ebb..cf57de53d9f3 100644 --- a/tests/ui/ptr_cast_constness.fixed +++ b/tests/ui/ptr_cast_constness.fixed @@ -106,3 +106,9 @@ fn issue14621() { let _ = std::ptr::addr_of_mut!(local).cast_const(); //~^ ptr_cast_constness } + +fn issue11317() { + let r = &0_u32; + let _ptr: *mut u32 = (r as *const u32).cast_mut(); + //~^ ptr_cast_constness +} diff --git a/tests/ui/ptr_cast_constness.rs b/tests/ui/ptr_cast_constness.rs index f6590dabd5b8..ea53a0fa8c50 100644 --- a/tests/ui/ptr_cast_constness.rs +++ b/tests/ui/ptr_cast_constness.rs @@ -106,3 +106,9 @@ fn issue14621() { let _ = std::ptr::addr_of_mut!(local) as *const _; //~^ ptr_cast_constness } + +fn issue11317() { + let r = &0_u32; + let _ptr: *mut u32 = r as *const _ as *mut _; + //~^ ptr_cast_constness +} diff --git a/tests/ui/ptr_cast_constness.stderr b/tests/ui/ptr_cast_constness.stderr index 0b1644168ff5..4adb5cc5ad7f 100644 --- a/tests/ui/ptr_cast_constness.stderr +++ b/tests/ui/ptr_cast_constness.stderr @@ -89,5 +89,11 @@ error: `as` casting between raw pointers while changing only its constness LL | let _ = std::ptr::addr_of_mut!(local) as *const _; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_const`, a safer alternative: `std::ptr::addr_of_mut!(local).cast_const()` -error: aborting due to 14 previous errors +error: `as` casting between raw pointers while changing only its constness + --> tests/ui/ptr_cast_constness.rs:112:26 + | +LL | let _ptr: *mut u32 = r as *const _ as *mut _; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `(r as *const u32).cast_mut()` + +error: aborting due to 15 previous errors diff --git a/tests/ui/semicolon_inside_block.fixed b/tests/ui/semicolon_inside_block.fixed index 7eb53e733ad5..7308e78aae26 100644 --- a/tests/ui/semicolon_inside_block.fixed +++ b/tests/ui/semicolon_inside_block.fixed @@ -86,3 +86,8 @@ fn main() { unit_fn_block() } + +pub fn issue15388() { + #[rustfmt::skip] + {0; 0}; +} diff --git a/tests/ui/semicolon_inside_block.rs b/tests/ui/semicolon_inside_block.rs index 9fa5b117194d..467bf4d779f2 100644 --- a/tests/ui/semicolon_inside_block.rs +++ b/tests/ui/semicolon_inside_block.rs @@ -86,3 +86,8 @@ fn main() { unit_fn_block() } + +pub fn issue15388() { + #[rustfmt::skip] + {0; 0}; +} diff --git a/tests/ui/semicolon_inside_block_stmt_expr_attrs.fixed b/tests/ui/semicolon_inside_block_stmt_expr_attrs.fixed new file mode 100644 index 000000000000..5b93a91da003 --- /dev/null +++ b/tests/ui/semicolon_inside_block_stmt_expr_attrs.fixed @@ -0,0 +1,11 @@ +// Test when the feature `stmt_expr_attributes` is enabled + +#![feature(stmt_expr_attributes)] +#![allow(clippy::no_effect)] +#![warn(clippy::semicolon_inside_block)] + +pub fn issue15388() { + #[rustfmt::skip] + {0; 0;} + //~^ semicolon_inside_block +} diff --git a/tests/ui/semicolon_inside_block_stmt_expr_attrs.rs b/tests/ui/semicolon_inside_block_stmt_expr_attrs.rs new file mode 100644 index 000000000000..aa2c0cb50299 --- /dev/null +++ b/tests/ui/semicolon_inside_block_stmt_expr_attrs.rs @@ -0,0 +1,11 @@ +// Test when the feature `stmt_expr_attributes` is enabled + +#![feature(stmt_expr_attributes)] +#![allow(clippy::no_effect)] +#![warn(clippy::semicolon_inside_block)] + +pub fn issue15388() { + #[rustfmt::skip] + {0; 0}; + //~^ semicolon_inside_block +} diff --git a/tests/ui/semicolon_inside_block_stmt_expr_attrs.stderr b/tests/ui/semicolon_inside_block_stmt_expr_attrs.stderr new file mode 100644 index 000000000000..5bb91915a5fd --- /dev/null +++ b/tests/ui/semicolon_inside_block_stmt_expr_attrs.stderr @@ -0,0 +1,16 @@ +error: consider moving the `;` inside the block for consistent formatting + --> tests/ui/semicolon_inside_block_stmt_expr_attrs.rs:9:5 + | +LL | {0; 0}; + | ^^^^^^^ + | + = note: `-D clippy::semicolon-inside-block` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::semicolon_inside_block)]` +help: put the `;` here + | +LL - {0; 0}; +LL + {0; 0;} + | + +error: aborting due to 1 previous error + diff --git a/tests/ui/std_instead_of_core.fixed b/tests/ui/std_instead_of_core.fixed index 603ab0accb0b..c27cec558242 100644 --- a/tests/ui/std_instead_of_core.fixed +++ b/tests/ui/std_instead_of_core.fixed @@ -89,3 +89,10 @@ fn msrv_1_76(_: std::net::IpAddr) {} #[clippy::msrv = "1.77"] fn msrv_1_77(_: core::net::IpAddr) {} //~^ std_instead_of_core + +#[warn(clippy::alloc_instead_of_core)] +fn issue15579() { + use std::alloc; + + let layout = alloc::Layout::new::(); +} diff --git a/tests/ui/std_instead_of_core.rs b/tests/ui/std_instead_of_core.rs index b6d4abad9f8f..7d53f7fc3072 100644 --- a/tests/ui/std_instead_of_core.rs +++ b/tests/ui/std_instead_of_core.rs @@ -89,3 +89,10 @@ fn msrv_1_76(_: std::net::IpAddr) {} #[clippy::msrv = "1.77"] fn msrv_1_77(_: std::net::IpAddr) {} //~^ std_instead_of_core + +#[warn(clippy::alloc_instead_of_core)] +fn issue15579() { + use std::alloc; + + let layout = alloc::Layout::new::(); +} diff --git a/tests/ui/unit_cmp.rs b/tests/ui/unit_cmp.rs index 93f5b87c3d2a..0a0fe3a1990b 100644 --- a/tests/ui/unit_cmp.rs +++ b/tests/ui/unit_cmp.rs @@ -68,3 +68,20 @@ fn main() { } ); } + +fn issue15559() { + fn foo() {} + assert_eq!( + //~^ unit_cmp + { + 1; + }, + foo() + ); + assert_eq!(foo(), foo()); + //~^ unit_cmp + + // don't lint on explicitly written unit expr + assert_eq!(foo(), ()); + assert_ne!((), ContainsUnit(()).0); +} diff --git a/tests/ui/unit_cmp.stderr b/tests/ui/unit_cmp.stderr index 8f26749fd860..21aa9dce1510 100644 --- a/tests/ui/unit_cmp.stderr +++ b/tests/ui/unit_cmp.stderr @@ -71,5 +71,23 @@ LL | | true; LL | | ); | |_____^ -error: aborting due to 6 previous errors +error: `assert_eq` of unit values detected. This will always succeed + --> tests/ui/unit_cmp.rs:74:5 + | +LL | / assert_eq!( +LL | | +LL | | { +LL | | 1; +LL | | }, +LL | | foo() +LL | | ); + | |_____^ + +error: `assert_eq` of unit values detected. This will always succeed + --> tests/ui/unit_cmp.rs:81:5 + | +LL | assert_eq!(foo(), foo()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 8 previous errors diff --git a/tests/ui/unnecessary_clone.stderr b/tests/ui/unnecessary_clone.stderr index a4fdf09cd5dc..17518e123d12 100644 --- a/tests/ui/unnecessary_clone.stderr +++ b/tests/ui/unnecessary_clone.stderr @@ -2,7 +2,7 @@ error: using `.clone()` on a ref-counted pointer --> tests/ui/unnecessary_clone.rs:23:5 | LL | rc.clone(); - | ^^^^^^^^^^ help: try: `Rc::::clone(&rc)` + | ^^^^^^^^^^ help: try: `std::rc::Rc::::clone(&rc)` | = note: `-D clippy::clone-on-ref-ptr` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::clone_on_ref_ptr)]` @@ -11,25 +11,25 @@ error: using `.clone()` on a ref-counted pointer --> tests/ui/unnecessary_clone.rs:28:5 | LL | arc.clone(); - | ^^^^^^^^^^^ help: try: `Arc::::clone(&arc)` + | ^^^^^^^^^^^ help: try: `std::sync::Arc::::clone(&arc)` error: using `.clone()` on a ref-counted pointer --> tests/ui/unnecessary_clone.rs:33:5 | LL | rcweak.clone(); - | ^^^^^^^^^^^^^^ help: try: `Weak::::clone(&rcweak)` + | ^^^^^^^^^^^^^^ help: try: `std::rc::Weak::::clone(&rcweak)` error: using `.clone()` on a ref-counted pointer --> tests/ui/unnecessary_clone.rs:38:5 | LL | arc_weak.clone(); - | ^^^^^^^^^^^^^^^^ help: try: `Weak::::clone(&arc_weak)` + | ^^^^^^^^^^^^^^^^ help: try: `std::sync::Weak::::clone(&arc_weak)` error: using `.clone()` on a ref-counted pointer --> tests/ui/unnecessary_clone.rs:44:33 | LL | let _: Arc = x.clone(); - | ^^^^^^^^^ help: try: `Arc::::clone(&x)` + | ^^^^^^^^^ help: try: `std::sync::Arc::::clone(&x)` error: using `clone` on type `T` which implements the `Copy` trait --> tests/ui/unnecessary_clone.rs:49:5 @@ -56,7 +56,7 @@ error: using `.clone()` on a ref-counted pointer --> tests/ui/unnecessary_clone.rs:108:14 | LL | Some(try_opt!(Some(rc)).clone()) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Rc::::clone(&try_opt!(Some(rc)))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::rc::Rc::::clone(&try_opt!(Some(rc)))` error: aborting due to 9 previous errors diff --git a/tests/ui/unnested_or_patterns.fixed b/tests/ui/unnested_or_patterns.fixed index 2081772d06b3..339d4a95084a 100644 --- a/tests/ui/unnested_or_patterns.fixed +++ b/tests/ui/unnested_or_patterns.fixed @@ -9,6 +9,11 @@ )] #![allow(unreachable_patterns, irrefutable_let_patterns, unused)] +struct S { + x: u8, + y: u8, +} + fn main() { // Should be ignored by this lint, as nesting requires more characters. if let &0 | &2 = &0 {} @@ -45,10 +50,6 @@ fn main() { //~^ unnested_or_patterns if let TS(x, ..) | TS(x, 1 | 2) = TS(0, 0) {} //~^ unnested_or_patterns - struct S { - x: u8, - y: u8, - } if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {} //~^ unnested_or_patterns if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} @@ -77,3 +78,19 @@ mod issue9952 { fn or_in_param((x | x | x): i32) {} //~^ unnested_or_patterns } + +fn issue15219() { + struct Foo { + x: u8, + } + + // the original repro + if let Foo { x } | Foo { x } = (Foo { x: 0 }) {} + + // also works with more fields + if let S { x, y } | S { x, y } = (S { x: 0, y: 0 }) {} + + // `y` not triggering the lint doesn't stop the `x` from getting flagged + if let S { y, x: 0 | 1 } = (S { x: 0, y: 1 }) {} + //~^ unnested_or_patterns +} diff --git a/tests/ui/unnested_or_patterns.rs b/tests/ui/unnested_or_patterns.rs index 6bf8fce36616..f5c99183b0c5 100644 --- a/tests/ui/unnested_or_patterns.rs +++ b/tests/ui/unnested_or_patterns.rs @@ -9,6 +9,11 @@ )] #![allow(unreachable_patterns, irrefutable_let_patterns, unused)] +struct S { + x: u8, + y: u8, +} + fn main() { // Should be ignored by this lint, as nesting requires more characters. if let &0 | &2 = &0 {} @@ -45,10 +50,6 @@ fn main() { //~^ unnested_or_patterns if let TS(x, ..) | TS(x, 1) | TS(x, 2) = TS(0, 0) {} //~^ unnested_or_patterns - struct S { - x: u8, - y: u8, - } if let S { x: 0, y } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} //~^ unnested_or_patterns if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} @@ -77,3 +78,19 @@ mod issue9952 { fn or_in_param((x | (x | x)): i32) {} //~^ unnested_or_patterns } + +fn issue15219() { + struct Foo { + x: u8, + } + + // the original repro + if let Foo { x } | Foo { x } = (Foo { x: 0 }) {} + + // also works with more fields + if let S { x, y } | S { x, y } = (S { x: 0, y: 0 }) {} + + // `y` not triggering the lint doesn't stop the `x` from getting flagged + if let S { y, x: 0 } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} + //~^ unnested_or_patterns +} diff --git a/tests/ui/unnested_or_patterns.stderr b/tests/ui/unnested_or_patterns.stderr index c805dc992b1c..d2b617c322c4 100644 --- a/tests/ui/unnested_or_patterns.stderr +++ b/tests/ui/unnested_or_patterns.stderr @@ -1,5 +1,5 @@ error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:16:12 + --> tests/ui/unnested_or_patterns.rs:21:12 | LL | if let box 0 | box 2 = Box::new(0) {} | ^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL + if let box (0 | 2) = Box::new(0) {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:18:12 + --> tests/ui/unnested_or_patterns.rs:23:12 | LL | if let box ((0 | 1)) | box (2 | 3) | box 4 = Box::new(0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL + if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:21:12 + --> tests/ui/unnested_or_patterns.rs:26:12 | LL | if let Some(1) | C0 | Some(2) = None {} | ^^^^^^^^^^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL + if let Some(1 | 2) | C0 = None {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:23:12 + --> tests/ui/unnested_or_patterns.rs:28:12 | LL | if let &mut 0 | &mut 2 = &mut 0 {} | ^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL + if let &mut (0 | 2) = &mut 0 {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:25:12 + --> tests/ui/unnested_or_patterns.rs:30:12 | LL | if let x @ 0 | x @ 2 = 0 {} | ^^^^^^^^^^^^^ @@ -61,7 +61,7 @@ LL + if let x @ (0 | 2) = 0 {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:27:12 + --> tests/ui/unnested_or_patterns.rs:32:12 | LL | if let (0, 1) | (0, 2) | (0, 3) = (0, 0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL + if let (0, 1 | 2 | 3) = (0, 0) {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:29:12 + --> tests/ui/unnested_or_patterns.rs:34:12 | LL | if let (1, 0) | (2, 0) | (3, 0) = (0, 0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -85,7 +85,7 @@ LL + if let (1 | 2 | 3, 0) = (0, 0) {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:31:12 + --> tests/ui/unnested_or_patterns.rs:36:12 | LL | if let (x, ..) | (x, 1) | (x, 2) = (0, 1) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -97,7 +97,7 @@ LL + if let (x, ..) | (x, 1 | 2) = (0, 1) {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:33:12 + --> tests/ui/unnested_or_patterns.rs:38:12 | LL | if let [0] | [1] = [0] {} | ^^^^^^^^^ @@ -109,7 +109,7 @@ LL + if let [0 | 1] = [0] {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:35:12 + --> tests/ui/unnested_or_patterns.rs:40:12 | LL | if let [x, 0] | [x, 1] = [0, 1] {} | ^^^^^^^^^^^^^^^ @@ -121,7 +121,7 @@ LL + if let [x, 0 | 1] = [0, 1] {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:37:12 + --> tests/ui/unnested_or_patterns.rs:42:12 | LL | if let [x, 0] | [x, 1] | [x, 2] = [0, 1] {} | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -133,7 +133,7 @@ LL + if let [x, 0 | 1 | 2] = [0, 1] {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:39:12 + --> tests/ui/unnested_or_patterns.rs:44:12 | LL | if let [x, ..] | [x, 1] | [x, 2] = [0, 1] {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -145,7 +145,7 @@ LL + if let [x, ..] | [x, 1 | 2] = [0, 1] {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:42:12 + --> tests/ui/unnested_or_patterns.rs:47:12 | LL | if let TS(0, x) | TS(1, x) = TS(0, 0) {} | ^^^^^^^^^^^^^^^^^^^ @@ -157,7 +157,7 @@ LL + if let TS(0 | 1, x) = TS(0, 0) {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:44:12 + --> tests/ui/unnested_or_patterns.rs:49:12 | LL | if let TS(1, 0) | TS(2, 0) | TS(3, 0) = TS(0, 0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -169,7 +169,7 @@ LL + if let TS(1 | 2 | 3, 0) = TS(0, 0) {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:46:12 + --> tests/ui/unnested_or_patterns.rs:51:12 | LL | if let TS(x, ..) | TS(x, 1) | TS(x, 2) = TS(0, 0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -181,7 +181,7 @@ LL + if let TS(x, ..) | TS(x, 1 | 2) = TS(0, 0) {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:52:12 + --> tests/ui/unnested_or_patterns.rs:53:12 | LL | if let S { x: 0, y } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -193,7 +193,7 @@ LL + if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:64:12 + --> tests/ui/unnested_or_patterns.rs:65:12 | LL | if let [1] | [53] = [0] {} | ^^^^^^^^^^ @@ -205,7 +205,7 @@ LL + if let [1 | 53] = [0] {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:70:13 + --> tests/ui/unnested_or_patterns.rs:71:13 | LL | let (0 | (1 | _)) = 0; | ^^^^^^^^^^^^^ @@ -217,7 +217,7 @@ LL + let (0 | 1 | _) = 0; | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:73:16 + --> tests/ui/unnested_or_patterns.rs:74:16 | LL | if let (0 | (1 | _)) = 0 {} | ^^^^^^^^^^^^^ @@ -229,7 +229,7 @@ LL + if let (0 | 1 | _) = 0 {} | error: unnested or-patterns - --> tests/ui/unnested_or_patterns.rs:77:20 + --> tests/ui/unnested_or_patterns.rs:78:20 | LL | fn or_in_param((x | (x | x)): i32) {} | ^^^^^^^^^^^^^ @@ -240,5 +240,17 @@ LL - fn or_in_param((x | (x | x)): i32) {} LL + fn or_in_param((x | x | x): i32) {} | -error: aborting due to 20 previous errors +error: unnested or-patterns + --> tests/ui/unnested_or_patterns.rs:94:12 + | +LL | if let S { y, x: 0 } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: nest the patterns + | +LL - if let S { y, x: 0 } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} +LL + if let S { y, x: 0 | 1 } = (S { x: 0, y: 1 }) {} + | + +error: aborting due to 21 previous errors diff --git a/tests/ui/unused_unit.edition2021.fixed b/tests/ui/unused_unit.edition2021.fixed index def8ef86e3c5..8e12bd2c8c7b 100644 --- a/tests/ui/unused_unit.edition2021.fixed +++ b/tests/ui/unused_unit.edition2021.fixed @@ -127,14 +127,10 @@ mod issue14577 { trait Unit {} impl Unit for () {} - fn run(f: impl FnOnce() -> R) { - f(); - } - #[allow(dependency_on_unit_never_type_fallback)] fn bar() { - run(|| { todo!() }); //~[edition2021]^ unused_unit + panic!() } struct UnitStruct; @@ -150,3 +146,24 @@ mod pr14962 { type UnusedParensButNoUnit = Box; } + +mod issue15035 { + + trait Convert { + fn from(value: T) -> Self; + } + + impl Convert for () { + fn from(_value: u64) -> Self {} + } + + fn handle>(value: u64) -> T { + Convert::from(value) + } + + pub fn f() -> Option { + let result: Result = Err(42); + // the `-> ()` is required for the inference of `handle`'s return type + result.map_err(|err| -> () { handle(err) }).ok() + } +} diff --git a/tests/ui/unused_unit.edition2021.stderr b/tests/ui/unused_unit.edition2021.stderr index 13cc20d4d7ad..9ad3c2df915e 100644 --- a/tests/ui/unused_unit.edition2021.stderr +++ b/tests/ui/unused_unit.edition2021.stderr @@ -119,10 +119,10 @@ LL | fn test3()-> (){} | ^^^^^ help: remove the `-> ()` error: unneeded unit return type - --> tests/ui/unused_unit.rs:136:15 + --> tests/ui/unused_unit.rs:131:13 | -LL | run(|| -> () { todo!() }); - | ^^^^^^ help: remove the `-> ()` +LL | fn bar() -> () { + | ^^^^^^ help: remove the `-> ()` error: aborting due to 20 previous errors diff --git a/tests/ui/unused_unit.edition2024.fixed b/tests/ui/unused_unit.edition2024.fixed index f908b958b191..688d2fe9afa2 100644 --- a/tests/ui/unused_unit.edition2024.fixed +++ b/tests/ui/unused_unit.edition2024.fixed @@ -127,14 +127,10 @@ mod issue14577 { trait Unit {} impl Unit for () {} - fn run(f: impl FnOnce() -> R) { - f(); - } - #[allow(dependency_on_unit_never_type_fallback)] - fn bar() { - run(|| -> () { todo!() }); + fn bar() -> () { //~[edition2021]^ unused_unit + panic!() } struct UnitStruct; @@ -150,3 +146,24 @@ mod pr14962 { type UnusedParensButNoUnit = Box; } + +mod issue15035 { + + trait Convert { + fn from(value: T) -> Self; + } + + impl Convert for () { + fn from(_value: u64) -> Self {} + } + + fn handle>(value: u64) -> T { + Convert::from(value) + } + + pub fn f() -> Option { + let result: Result = Err(42); + // the `-> ()` is required for the inference of `handle`'s return type + result.map_err(|err| -> () { handle(err) }).ok() + } +} diff --git a/tests/ui/unused_unit.rs b/tests/ui/unused_unit.rs index 7298ec40cc28..31e980f2655c 100644 --- a/tests/ui/unused_unit.rs +++ b/tests/ui/unused_unit.rs @@ -127,14 +127,10 @@ mod issue14577 { trait Unit {} impl Unit for () {} - fn run(f: impl FnOnce() -> R) { - f(); - } - #[allow(dependency_on_unit_never_type_fallback)] - fn bar() { - run(|| -> () { todo!() }); + fn bar() -> () { //~[edition2021]^ unused_unit + panic!() } struct UnitStruct; @@ -150,3 +146,24 @@ mod pr14962 { type UnusedParensButNoUnit = Box; } + +mod issue15035 { + + trait Convert { + fn from(value: T) -> Self; + } + + impl Convert for () { + fn from(_value: u64) -> Self {} + } + + fn handle>(value: u64) -> T { + Convert::from(value) + } + + pub fn f() -> Option { + let result: Result = Err(42); + // the `-> ()` is required for the inference of `handle`'s return type + result.map_err(|err| -> () { handle(err) }).ok() + } +} diff --git a/tests/ui/unwrap_in_result.rs b/tests/ui/unwrap_in_result.rs index 4e872c67b423..70c28fe54f37 100644 --- a/tests/ui/unwrap_in_result.rs +++ b/tests/ui/unwrap_in_result.rs @@ -1,4 +1,5 @@ #![warn(clippy::unwrap_in_result)] +#![allow(clippy::ok_expect)] struct A; @@ -20,10 +21,9 @@ impl A { // should be detected fn bad_divisible_by_3(i_str: String) -> Result { - //~^ unwrap_in_result - // checks whether a string represents a number divisible by 3 let i = i_str.parse::().unwrap(); + //~^ unwrap_in_result if i % 3 == 0 { Ok(true) } else { @@ -32,9 +32,8 @@ impl A { } fn example_option_expect(i_str: String) -> Option { + let i = i_str.parse::().ok().expect("not a number"); //~^ unwrap_in_result - - let i = i_str.parse::().expect("not a number"); if i % 3 == 0 { return Some(true); } @@ -42,13 +41,66 @@ impl A { } fn in_closure(a: Option) -> Option { - //~^ unwrap_in_result + // No lint inside a closure let c = || a.unwrap(); - Some(c()) + + // But lint outside + let a = c().then_some(true); + let _ = a.unwrap(); + //~^ unwrap_in_result + + None + } + + const fn in_const_inside_fn() -> bool { + const A: bool = { + const fn inner(b: Option) -> Option { + Some(b.unwrap()) + //~^ unwrap_in_result + } + + // No lint inside `const` + inner(Some(true)).unwrap() + }; + A + } + + fn in_static_inside_fn() -> bool { + static A: bool = { + const fn inner(b: Option) -> Option { + Some(b.unwrap()) + //~^ unwrap_in_result + } + + // No lint inside `static` + inner(Some(true)).unwrap() + }; + A } } -fn main() { - A::bad_divisible_by_3("3".to_string()); - A::good_divisible_by_3("3".to_string()); +macro_rules! mac { + () => { + Option::unwrap(Some(3)) + }; +} + +fn type_relative_unwrap() -> Option<()> { + _ = Option::unwrap(Some(3)); + //~^ unwrap_in_result + + // Do not lint macro output + _ = mac!(); + + None +} + +fn main() -> Result<(), ()> { + A::bad_divisible_by_3("3".to_string()).unwrap(); + //~^ unwrap_in_result + A::good_divisible_by_3("3".to_string()).unwrap(); + //~^ unwrap_in_result + Result::unwrap(A::good_divisible_by_3("3".to_string())); + //~^ unwrap_in_result + Ok(()) } diff --git a/tests/ui/unwrap_in_result.stderr b/tests/ui/unwrap_in_result.stderr index 5e3eab813e07..804b44246dc7 100644 --- a/tests/ui/unwrap_in_result.stderr +++ b/tests/ui/unwrap_in_result.stderr @@ -1,55 +1,107 @@ -error: used unwrap or expect in a function that returns result or option - --> tests/ui/unwrap_in_result.rs:22:5 - | -LL | / fn bad_divisible_by_3(i_str: String) -> Result { -... | -LL | | } - | |_____^ - | - = help: unwrap and expect should not be used in a function that returns result or option -note: potential non-recoverable error(s) - --> tests/ui/unwrap_in_result.rs:26:17 +error: `unwrap` used in a function that returns a `Result` + --> tests/ui/unwrap_in_result.rs:25:17 | LL | let i = i_str.parse::().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: in this function signature + --> tests/ui/unwrap_in_result.rs:23:45 + | +LL | fn bad_divisible_by_3(i_str: String) -> Result { + | ^^^^^^^^^^^^^^^^^^^^ + = help: consider using the `?` operator or calling the `.map_err()` method = note: `-D clippy::unwrap-in-result` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unwrap_in_result)]` -error: used unwrap or expect in a function that returns result or option - --> tests/ui/unwrap_in_result.rs:34:5 +error: `expect` used in a function that returns an `Option` + --> tests/ui/unwrap_in_result.rs:35:17 | -LL | / fn example_option_expect(i_str: String) -> Option { -LL | | -LL | | -LL | | let i = i_str.parse::().expect("not a number"); -... | -LL | | None -LL | | } - | |_____^ +LL | let i = i_str.parse::().ok().expect("not a number"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: unwrap and expect should not be used in a function that returns result or option -note: potential non-recoverable error(s) - --> tests/ui/unwrap_in_result.rs:37:17 +note: in this function signature + --> tests/ui/unwrap_in_result.rs:34:48 | -LL | let i = i_str.parse::().expect("not a number"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn example_option_expect(i_str: String) -> Option { + | ^^^^^^^^^^^^ + = help: consider using the `?` operator -error: used unwrap or expect in a function that returns result or option - --> tests/ui/unwrap_in_result.rs:44:5 +error: `unwrap` used in a function that returns an `Option` + --> tests/ui/unwrap_in_result.rs:49:17 | -LL | / fn in_closure(a: Option) -> Option { -LL | | -LL | | let c = || a.unwrap(); -LL | | Some(c()) -LL | | } - | |_____^ +LL | let _ = a.unwrap(); + | ^^^^^^^^^^ | - = help: unwrap and expect should not be used in a function that returns result or option -note: potential non-recoverable error(s) - --> tests/ui/unwrap_in_result.rs:46:20 +note: in this function signature + --> tests/ui/unwrap_in_result.rs:43:39 | -LL | let c = || a.unwrap(); - | ^^^^^^^^^^ +LL | fn in_closure(a: Option) -> Option { + | ^^^^^^^^^^^^ + = help: consider using the `?` operator -error: aborting due to 3 previous errors +error: `unwrap` used in a function that returns an `Option` + --> tests/ui/unwrap_in_result.rs:58:22 + | +LL | Some(b.unwrap()) + | ^^^^^^^^^^ + | +note: in this function signature + --> tests/ui/unwrap_in_result.rs:57:48 + | +LL | const fn inner(b: Option) -> Option { + | ^^^^^^^^^^^^ + = help: consider using the `?` operator + +error: `unwrap` used in a function that returns an `Option` + --> tests/ui/unwrap_in_result.rs:71:22 + | +LL | Some(b.unwrap()) + | ^^^^^^^^^^ + | +note: in this function signature + --> tests/ui/unwrap_in_result.rs:70:48 + | +LL | const fn inner(b: Option) -> Option { + | ^^^^^^^^^^^^ + = help: consider using the `?` operator + +error: `unwrap` used in a function that returns an `Option` + --> tests/ui/unwrap_in_result.rs:89:9 + | +LL | _ = Option::unwrap(Some(3)); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +note: in this function signature + --> tests/ui/unwrap_in_result.rs:88:30 + | +LL | fn type_relative_unwrap() -> Option<()> { + | ^^^^^^^^^^ + = help: consider using the `?` operator + +error: `unwrap` used in a function that returns a `Result` + --> tests/ui/unwrap_in_result.rs:99:5 + | +LL | A::bad_divisible_by_3("3".to_string()).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: in this function signature + --> tests/ui/unwrap_in_result.rs:98:14 + | +LL | fn main() -> Result<(), ()> { + | ^^^^^^^^^^^^^^ + = help: consider using the `?` operator or calling the `.map_err()` method + +error: `unwrap` used in a function that returns a `Result` + --> tests/ui/unwrap_in_result.rs:101:5 + | +LL | A::good_divisible_by_3("3".to_string()).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `unwrap` used in a function that returns a `Result` + --> tests/ui/unwrap_in_result.rs:103:5 + | +LL | Result::unwrap(A::good_divisible_by_3("3".to_string())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 9 previous errors diff --git a/tests/ui/vec.fixed b/tests/ui/vec.fixed index f360a8afadf6..55742459c92c 100644 --- a/tests/ui/vec.fixed +++ b/tests/ui/vec.fixed @@ -242,3 +242,12 @@ fn issue_12101() { for a in &[1, 2] {} //~^ useless_vec } + +fn issue_14531() { + // The lint used to suggest using an array rather than a reference to a slice. + + fn requires_ref_slice(v: &[()]) {} + let v = &[]; + //~^ useless_vec + requires_ref_slice(v); +} diff --git a/tests/ui/vec.rs b/tests/ui/vec.rs index a779d33557cb..fbf7131323c3 100644 --- a/tests/ui/vec.rs +++ b/tests/ui/vec.rs @@ -242,3 +242,12 @@ fn issue_12101() { for a in &(vec![1, 2]) {} //~^ useless_vec } + +fn issue_14531() { + // The lint used to suggest using an array rather than a reference to a slice. + + fn requires_ref_slice(v: &[()]) {} + let v = &vec![]; + //~^ useless_vec + requires_ref_slice(v); +} diff --git a/tests/ui/vec.stderr b/tests/ui/vec.stderr index 806d6617200f..d16c8a8944a2 100644 --- a/tests/ui/vec.stderr +++ b/tests/ui/vec.stderr @@ -127,5 +127,11 @@ error: useless use of `vec!` LL | for a in &(vec![1, 2]) {} | ^^^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]` -error: aborting due to 21 previous errors +error: useless use of `vec!` + --> tests/ui/vec.rs:250:13 + | +LL | let v = &vec![]; + | ^^^^^^^ help: you can use a slice directly: `&[]` + +error: aborting due to 22 previous errors diff --git a/triagebot.toml b/triagebot.toml index 805baf2af6dd..7b19f8658c08 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -49,11 +49,18 @@ new_pr = true # These labels are set when there are unresolved concerns, removed otherwise labels = ["S-waiting-on-concerns"] +# Show differences when a PR is rebased +[range-diff] + +# Amend a review to include a link to what was changed since the review +[review-changes-since] + [assign] contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md" users_on_vacation = [ "matthiaskrgr", "Manishearth", + "flip1995", ] [assign.owners] From b2eb28da2342e60779a6d78d19c991c99e1b5c37 Mon Sep 17 00:00:00 2001 From: Scott Schafer Date: Wed, 27 Aug 2025 20:33:50 -0600 Subject: [PATCH 189/682] fix: Filter suggestion parts that match existing code --- tests/ui/bool_assert_comparison.stderr | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/tests/ui/bool_assert_comparison.stderr b/tests/ui/bool_assert_comparison.stderr index f823f08f31dc..72aa6303a202 100644 --- a/tests/ui/bool_assert_comparison.stderr +++ b/tests/ui/bool_assert_comparison.stderr @@ -272,10 +272,8 @@ LL | assert_eq!(a!(), true); | help: replace it with `assert!(..)` | -LL | true -... -LL | -LL ~ assert!(a!()); +LL - assert_eq!(a!(), true); +LL + assert!(a!()); | error: used `assert_eq!` with a literal bool @@ -286,10 +284,8 @@ LL | assert_eq!(true, b!()); | help: replace it with `assert!(..)` | -LL | true -... -LL | -LL ~ assert!(b!()); +LL - assert_eq!(true, b!()); +LL + assert!(b!()); | error: used `debug_assert_eq!` with a literal bool From 9a1eb85394e3612213e829ed871eb281079a2364 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Fri, 5 Sep 2025 04:53:05 +0000 Subject: [PATCH 190/682] Prepare for merging from rust-lang/rust This updates the rust-version file to b3cfb8faf84c5f3b7909479a9f9b6a3290d5f4d7. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 7420b6200967..a51d86c3d6f1 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -9385c64c95d971329e62917adc4349c8ccdbafe0 +b3cfb8faf84c5f3b7909479a9f9b6a3290d5f4d7 From 5cadd4541c96edf0caaf1847b3c13b1ced4d5121 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 5 Sep 2025 09:07:08 +0200 Subject: [PATCH 191/682] make use of Duration::from_nanos_u128 --- src/tools/miri/src/clock.rs | 9 +-------- src/tools/miri/src/lib.rs | 1 + 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/tools/miri/src/clock.rs b/src/tools/miri/src/clock.rs index 34465e9cac60..47608f873a48 100644 --- a/src/tools/miri/src/clock.rs +++ b/src/tools/miri/src/clock.rs @@ -46,14 +46,7 @@ impl Instant { InstantKind::Virtual { nanoseconds: earlier }, ) => { let duration = nanoseconds.saturating_sub(earlier); - // `Duration` does not provide a nice constructor from a `u128` of nanoseconds, - // so we have to implement this ourselves. - // It is possible for second to overflow because u64::MAX < (u128::MAX / 1e9). - // It will be saturated to u64::MAX seconds if the value after division exceeds u64::MAX. - let seconds = u64::try_from(duration / 1_000_000_000).unwrap_or(u64::MAX); - // It is impossible for nanosecond to overflow because u32::MAX > 1e9. - let nanosecond = u32::try_from(duration.wrapping_rem(1_000_000_000)).unwrap(); - Duration::new(seconds, nanosecond) + Duration::from_nanos_u128(duration) } _ => panic!("all `Instant` must be of the same kind"), } diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 0856411b8e83..a00d9525fab5 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -18,6 +18,7 @@ #![feature(derive_coerce_pointee)] #![feature(arbitrary_self_types)] #![feature(iter_advance_by)] +#![feature(duration_from_nanos_u128)] // Configure clippy and other lints #![allow( clippy::collapsible_else_if, From b9b235acb088aa88d96890c930e174ab56abbccd Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 5 Sep 2025 15:34:49 +0800 Subject: [PATCH 192/682] Fix indent for unresolved_field fixes Examples --- ```rust mod indent { struct Foo {} fn foo() { let foo = Foo{}; foo.bar$0; } } ``` **Before this PR**: ```rust mod indent { struct Foo { bar: () } fn foo() { let foo = Foo{}; foo.bar; } } ``` **After this PR**: ```rust mod indent { struct Foo { bar: () } fn foo() { let foo = Foo{}; foo.bar; } } ``` --- New field list add newline ```rust mod indent { struct Foo; fn foo() { Foo.bar$0; } } ``` **Before this PR**: ```rust mod indent { struct Foo{ bar: () } fn foo() { Foo.bar; } } ``` **After this PR**: ```rust mod indent { struct Foo { bar: (), } fn foo() { Foo.bar; } } ``` --- .../src/handlers/unresolved_field.rs | 65 +++++++++++++++---- 1 file changed, 52 insertions(+), 13 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs index 690158989679..358e0c43c2a9 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs @@ -1,5 +1,3 @@ -use std::iter; - use either::Either; use hir::{Adt, FileRange, HasSource, HirDisplay, InFile, Struct, Union, db::ExpandDatabase}; use ide_db::text_edit::TextEdit; @@ -194,17 +192,20 @@ fn add_field_to_struct_fix( Some(make::visibility_pub_crate()) }; // FIXME: Allow for choosing a visibility modifier see https://github.com/rust-lang/rust-analyzer/issues/11563 - let indent = IndentLevel::from_node(struct_syntax.value) + 1; + let indent = IndentLevel::from_node(struct_syntax.value); - let field = make::record_field(visibility, field_name, suggested_type).indent(indent); - let record_field_list = make::record_field_list(iter::once(field)); + let field = + make::record_field(visibility, field_name, suggested_type).indent(indent + 1); // A Unit Struct with no `;` is invalid syntax. We should not suggest this fix. let semi_colon = algo::skip_trivia_token(struct_syntax.value.last_token()?, Direction::Prev)?; if semi_colon.kind() != SyntaxKind::SEMICOLON { return None; } - src_change_builder.replace(semi_colon.text_range(), record_field_list.to_string()); + src_change_builder.replace( + semi_colon.text_range(), + format!(" {{\n{}{field},\n{indent}}}", indent + 1), + ); Some(Assist { id: AssistId::quick_fix("convert-unit-struct-to-record-struct"), @@ -230,7 +231,7 @@ fn record_field_layout( field_list: ast::RecordFieldList, struct_syntax: &SyntaxNode, ) -> Option<(TextSize, String)> { - let (offset, needs_comma, trailing_new_line, indent) = match field_list.fields().last() { + let (offset, needs_comma, indent) = match field_list.fields().last() { Some(record_field) => { let syntax = algo::skip_trivia_token(field_list.r_curly_token()?, Direction::Prev)?; @@ -239,19 +240,22 @@ fn record_field_layout( ( last_field_syntax.text_range().end(), syntax.kind() != SyntaxKind::COMMA, - false, last_field_indent, ) } // Empty Struct. Add a field right before the closing brace None => { let indent = IndentLevel::from_node(struct_syntax) + 1; - let offset = field_list.r_curly_token()?.text_range().start(); - (offset, false, true, indent) + let offset = field_list.l_curly_token()?.text_range().end(); + (offset, false, indent) } }; - let comma = if needs_comma { ",\n" } else { "" }; - let trailing_new_line = if trailing_new_line { "\n" } else { "" }; + let trailing_new_line = if !field_list.syntax().text().contains_char('\n') { + format!("\n{}", field_list.indent_level()) + } else { + String::new() + }; + let comma = if needs_comma { ",\n" } else { "\n" }; let record_field = make::record_field(visibility, name, suggested_type); Some((offset, format!("{comma}{indent}{record_field}{trailing_new_line}"))) @@ -377,18 +381,24 @@ fn foo() { fn unresolved_field_fix_on_unit() { check_fix( r#" + mod indent { struct Foo; fn foo() { Foo.bar$0; } + } "#, r#" - struct Foo{ bar: () } + mod indent { + struct Foo { + bar: (), + } fn foo() { Foo.bar; } + } "#, ); } @@ -396,6 +406,7 @@ fn foo() { fn unresolved_field_fix_on_empty() { check_fix( r#" + mod indent { struct Foo{ } @@ -403,8 +414,10 @@ fn foo() { let foo = Foo{}; foo.bar$0; } + } "#, r#" + mod indent { struct Foo{ bar: () } @@ -413,6 +426,32 @@ fn foo() { let foo = Foo{}; foo.bar; } + } + "#, + ); + + check_fix( + r#" + mod indent { + struct Foo {} + + fn foo() { + let foo = Foo{}; + foo.bar$0; + } + } + "#, + r#" + mod indent { + struct Foo { + bar: () + } + + fn foo() { + let foo = Foo{}; + foo.bar; + } + } "#, ); } From 1186db896e2f4013b1f15612b31ed18e2b653228 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 5 Sep 2025 10:28:47 +0200 Subject: [PATCH 193/682] atomics: unify handling min/max and the other RMWs --- src/tools/miri/src/concurrency/data_race.rs | 85 +++++-------------- src/tools/miri/src/concurrency/genmc/dummy.rs | 19 +---- src/tools/miri/src/concurrency/genmc/mod.rs | 29 +------ src/tools/miri/src/intrinsics/atomic.rs | 74 ++++++++++------ src/tools/miri/src/intrinsics/mod.rs | 3 + 5 files changed, 83 insertions(+), 127 deletions(-) diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs index 19d37a691d90..a7822d0d6fa3 100644 --- a/src/tools/miri/src/concurrency/data_race.rs +++ b/src/tools/miri/src/concurrency/data_race.rs @@ -56,6 +56,7 @@ use super::vector_clock::{VClock, VTimestamp, VectorIdx}; use super::weak_memory::EvalContextExt as _; use crate::concurrency::GlobalDataRaceHandler; use crate::diagnostics::RacingOp; +use crate::intrinsics::AtomicRmwOp; use crate::*; pub type AllocState = VClockAlloc; @@ -778,9 +779,8 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { &mut self, place: &MPlaceTy<'tcx>, rhs: &ImmTy<'tcx>, - op: mir::BinOp, - not: bool, - atomic: AtomicRwOrd, + atomic_op: AtomicRmwOp, + ord: AtomicRwOrd, ) -> InterpResult<'tcx, ImmTy<'tcx>> { let this = self.eval_context_mut(); this.atomic_access_check(place, AtomicAccessType::Rmw)?; @@ -793,8 +793,9 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { this, place.ptr().addr(), place.layout.size, - atomic, - (op, not), + place.layout.backend_repr.is_signed(), + ord, + atomic_op, rhs.to_scalar(), old.to_scalar(), )?; @@ -804,13 +805,26 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { return interp_ok(ImmTy::from_scalar(old_val, old.layout)); } - let val = this.binary_op(op, &old, rhs)?; - let val = if not { this.unary_op(mir::UnOp::Not, &val)? } else { val }; + let val = match atomic_op { + AtomicRmwOp::MirOp { op, neg } => { + let val = this.binary_op(op, &old, rhs)?; + if neg { this.unary_op(mir::UnOp::Not, &val)? } else { val } + } + AtomicRmwOp::Max => { + let lt = this.binary_op(mir::BinOp::Lt, &old, rhs)?.to_scalar().to_bool()?; + if lt { rhs } else { &old }.clone() + } + AtomicRmwOp::Min => { + let lt = this.binary_op(mir::BinOp::Lt, &old, rhs)?.to_scalar().to_bool()?; + if lt { &old } else { rhs }.clone() + } + }; + this.allow_data_races_mut(|this| this.write_immediate(*val, place))?; - this.validate_atomic_rmw(place, atomic)?; + this.validate_atomic_rmw(place, ord)?; - this.buffered_atomic_rmw(val.to_scalar(), place, atomic, old.to_scalar())?; + this.buffered_atomic_rmw(val.to_scalar(), place, ord, old.to_scalar())?; interp_ok(old) } @@ -852,59 +866,6 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { interp_ok(old) } - /// Perform an conditional atomic exchange with a memory place and a new - /// scalar value, the old value is returned. - fn atomic_min_max_scalar( - &mut self, - place: &MPlaceTy<'tcx>, - rhs: ImmTy<'tcx>, - min: bool, - atomic: AtomicRwOrd, - ) -> InterpResult<'tcx, ImmTy<'tcx>> { - let this = self.eval_context_mut(); - this.atomic_access_check(place, AtomicAccessType::Rmw)?; - - let old = this.allow_data_races_mut(|this| this.read_immediate(place))?; - - // Inform GenMC about the atomic min/max operation. - if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { - let (old_val, new_val) = genmc_ctx.atomic_min_max_op( - this, - place.ptr().addr(), - place.layout.size, - atomic, - min, - old.layout.backend_repr.is_signed(), - rhs.to_scalar(), - old.to_scalar(), - )?; - // The store might be the latest store in coherence order (determined by GenMC). - // If it is, we need to update the value in Miri's memory: - if let Some(new_val) = new_val { - this.allow_data_races_mut(|this| this.write_scalar(new_val, place))?; - } - return interp_ok(ImmTy::from_scalar(old_val, old.layout)); - } - - let lt = this.binary_op(mir::BinOp::Lt, &old, &rhs)?.to_scalar().to_bool()?; - - #[rustfmt::skip] // rustfmt makes this unreadable - let new_val = if min { - if lt { &old } else { &rhs } - } else { - if lt { &rhs } else { &old } - }; - - this.allow_data_races_mut(|this| this.write_immediate(**new_val, place))?; - - this.validate_atomic_rmw(place, atomic)?; - - this.buffered_atomic_rmw(new_val.to_scalar(), place, atomic, old.to_scalar())?; - - // Return the old value. - interp_ok(old) - } - /// Perform an atomic compare and exchange at a given memory location. /// On success an atomic RMW operation is performed and on failure /// only an atomic read occurs. If `can_fail_spuriously` is true, diff --git a/src/tools/miri/src/concurrency/genmc/dummy.rs b/src/tools/miri/src/concurrency/genmc/dummy.rs index 1960ef37cc9f..7c420961ec54 100644 --- a/src/tools/miri/src/concurrency/genmc/dummy.rs +++ b/src/tools/miri/src/concurrency/genmc/dummy.rs @@ -1,8 +1,8 @@ use rustc_abi::{Align, Size}; use rustc_const_eval::interpret::{AllocId, InterpCx, InterpResult}; -use rustc_middle::mir; pub use self::run::run_genmc_mode; +use crate::intrinsics::AtomicRmwOp; use crate::{ AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, MemoryKind, MiriMachine, Scalar, ThreadId, ThreadManager, VisitProvenance, VisitWith, @@ -94,22 +94,9 @@ impl GenmcCtx { _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, _address: Size, _size: Size, - _ordering: AtomicRwOrd, - (_rmw_op, _not): (mir::BinOp, bool), - _rhs_scalar: Scalar, - _old_value: Scalar, - ) -> InterpResult<'tcx, (Scalar, Option)> { - unreachable!() - } - - pub(crate) fn atomic_min_max_op<'tcx>( - &self, - _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - _address: Size, - _size: Size, - _ordering: AtomicRwOrd, - _min: bool, _is_signed: bool, + _ordering: AtomicRwOrd, + _atomic_op: AtomicRmwOp, _rhs_scalar: Scalar, _old_value: Scalar, ) -> InterpResult<'tcx, (Scalar, Option)> { diff --git a/src/tools/miri/src/concurrency/genmc/mod.rs b/src/tools/miri/src/concurrency/genmc/mod.rs index b5d20a439c01..26ce0b9c43ac 100644 --- a/src/tools/miri/src/concurrency/genmc/mod.rs +++ b/src/tools/miri/src/concurrency/genmc/mod.rs @@ -7,7 +7,7 @@ use genmc_sys::{ }; use rustc_abi::{Align, Size}; use rustc_const_eval::interpret::{AllocId, InterpCx, InterpResult, interp_ok}; -use rustc_middle::{mir, throw_machine_stop, throw_ub_format, throw_unsup_format}; +use rustc_middle::{throw_machine_stop, throw_ub_format, throw_unsup_format}; // FIXME(genmc,tracing): Implement some work-around for enabling debug/trace level logging (currently disabled statically in rustc). use tracing::{debug, info}; @@ -15,6 +15,7 @@ use self::global_allocations::{EvalContextExt as _, GlobalAllocationHandler}; use self::helper::{MAX_ACCESS_SIZE, genmc_scalar_to_scalar, scalar_to_genmc_scalar}; use self::thread_id_map::ThreadIdMap; use crate::concurrency::genmc::helper::split_access; +use crate::intrinsics::AtomicRmwOp; use crate::{ AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, MemoryKind, MiriConfig, MiriMachine, MiriMemoryKind, Scalar, TerminationInfo, ThreadId, ThreadManager, VisitProvenance, @@ -298,8 +299,9 @@ impl GenmcCtx { _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, _address: Size, _size: Size, + _is_signed: bool, _ordering: AtomicRwOrd, - (_rmw_op, _not): (mir::BinOp, bool), + _atomic_op: AtomicRmwOp, _rhs_scalar: Scalar, _old_value: Scalar, ) -> InterpResult<'tcx, (Scalar, Option)> { @@ -310,29 +312,6 @@ impl GenmcCtx { throw_unsup_format!("FIXME(genmc): Add support for atomic RMW.") } - /// Inform GenMC about an atomic `min` or `max` operation. - /// - /// Returns `(old_val, Option)`. `new_val` might not be the latest write in coherence order, which is indicated by `None`. - /// - /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized. - pub(crate) fn atomic_min_max_op<'tcx>( - &self, - _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - _address: Size, - _size: Size, - _ordering: AtomicRwOrd, - _min: bool, - _is_signed: bool, - _rhs_scalar: Scalar, - _old_value: Scalar, - ) -> InterpResult<'tcx, (Scalar, Option)> { - assert!( - !self.get_alloc_data_races(), - "atomic min/max operation with data race checking disabled." - ); - throw_unsup_format!("FIXME(genmc): Add support for atomic min/max.") - } - /// Returns `(old_val, Option)`. `new_val` might not be the latest write in coherence order, which is indicated by `None`. /// /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized. diff --git a/src/tools/miri/src/intrinsics/atomic.rs b/src/tools/miri/src/intrinsics/atomic.rs index e63412529275..c9f63640b854 100644 --- a/src/tools/miri/src/intrinsics/atomic.rs +++ b/src/tools/miri/src/intrinsics/atomic.rs @@ -5,10 +5,13 @@ use rustc_middle::{mir, ty}; use super::check_intrinsic_arg_count; use crate::*; -pub enum AtomicOp { - /// The `bool` indicates whether the result of the operation should be negated (`UnOp::Not`, - /// must be a boolean-typed operation). - MirOp(mir::BinOp, bool), +pub enum AtomicRmwOp { + MirOp { + op: mir::BinOp, + /// Indicates whether the result of the operation should be negated (`UnOp::Not`, must be a + /// boolean-typed operation). + neg: bool, + }, Max, Min, } @@ -106,55 +109,85 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "or" => { let ord = get_ord_at(2); - this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), rw_ord(ord))?; + this.atomic_rmw_op( + args, + dest, + AtomicRmwOp::MirOp { op: BinOp::BitOr, neg: false }, + rw_ord(ord), + )?; } "xor" => { let ord = get_ord_at(2); - this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), rw_ord(ord))?; + this.atomic_rmw_op( + args, + dest, + AtomicRmwOp::MirOp { op: BinOp::BitXor, neg: false }, + rw_ord(ord), + )?; } "and" => { let ord = get_ord_at(2); - this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), rw_ord(ord))?; + this.atomic_rmw_op( + args, + dest, + AtomicRmwOp::MirOp { op: BinOp::BitAnd, neg: false }, + rw_ord(ord), + )?; } "nand" => { let ord = get_ord_at(2); - this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), rw_ord(ord))?; + this.atomic_rmw_op( + args, + dest, + AtomicRmwOp::MirOp { op: BinOp::BitAnd, neg: true }, + rw_ord(ord), + )?; } "xadd" => { let ord = get_ord_at(2); - this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), rw_ord(ord))?; + this.atomic_rmw_op( + args, + dest, + AtomicRmwOp::MirOp { op: BinOp::Add, neg: false }, + rw_ord(ord), + )?; } "xsub" => { let ord = get_ord_at(2); - this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), rw_ord(ord))?; + this.atomic_rmw_op( + args, + dest, + AtomicRmwOp::MirOp { op: BinOp::Sub, neg: false }, + rw_ord(ord), + )?; } "min" => { let ord = get_ord_at(1); // Later we will use the type to indicate signed vs unsigned, // so make sure it matches the intrinsic name. assert!(matches!(args[1].layout.ty.kind(), ty::Int(_))); - this.atomic_rmw_op(args, dest, AtomicOp::Min, rw_ord(ord))?; + this.atomic_rmw_op(args, dest, AtomicRmwOp::Min, rw_ord(ord))?; } "umin" => { let ord = get_ord_at(1); // Later we will use the type to indicate signed vs unsigned, // so make sure it matches the intrinsic name. assert!(matches!(args[1].layout.ty.kind(), ty::Uint(_))); - this.atomic_rmw_op(args, dest, AtomicOp::Min, rw_ord(ord))?; + this.atomic_rmw_op(args, dest, AtomicRmwOp::Min, rw_ord(ord))?; } "max" => { let ord = get_ord_at(1); // Later we will use the type to indicate signed vs unsigned, // so make sure it matches the intrinsic name. assert!(matches!(args[1].layout.ty.kind(), ty::Int(_))); - this.atomic_rmw_op(args, dest, AtomicOp::Max, rw_ord(ord))?; + this.atomic_rmw_op(args, dest, AtomicRmwOp::Max, rw_ord(ord))?; } "umax" => { let ord = get_ord_at(1); // Later we will use the type to indicate signed vs unsigned, // so make sure it matches the intrinsic name. assert!(matches!(args[1].layout.ty.kind(), ty::Uint(_))); - this.atomic_rmw_op(args, dest, AtomicOp::Max, rw_ord(ord))?; + this.atomic_rmw_op(args, dest, AtomicRmwOp::Max, rw_ord(ord))?; } _ => return interp_ok(EmulateItemResult::NotSupported), @@ -222,8 +255,8 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { &mut self, args: &[OpTy<'tcx>], dest: &MPlaceTy<'tcx>, - atomic_op: AtomicOp, - atomic: AtomicRwOrd, + atomic_op: AtomicRmwOp, + ord: AtomicRwOrd, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); @@ -240,14 +273,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { ); } - let old = match atomic_op { - AtomicOp::Min => - this.atomic_min_max_scalar(&place, rhs, /* min */ true, atomic)?, - AtomicOp::Max => - this.atomic_min_max_scalar(&place, rhs, /* min */ false, atomic)?, - AtomicOp::MirOp(op, not) => - this.atomic_rmw_op_immediate(&place, &rhs, op, not, atomic)?, - }; + let old = this.atomic_rmw_op_immediate(&place, &rhs, atomic_op, ord)?; this.write_immediate(*old, dest)?; // old value is returned interp_ok(()) } diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index 5e46768b0e6c..f6f9af47d0a7 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -3,6 +3,9 @@ mod atomic; mod simd; +pub use self::atomic::AtomicRmwOp; + +#[rustfmt::skip] // prevent `use` reordering use std::ops::Neg; use rand::Rng; From 69c7652a01a721be007cb9f7ef56f1e483f39fc8 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 5 Sep 2025 13:31:01 +0200 Subject: [PATCH 194/682] atomic rmw intrinsics: RHS must be an integer --- src/tools/miri/src/intrinsics/atomic.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/src/intrinsics/atomic.rs b/src/tools/miri/src/intrinsics/atomic.rs index c9f63640b854..9bb0ab70de23 100644 --- a/src/tools/miri/src/intrinsics/atomic.rs +++ b/src/tools/miri/src/intrinsics/atomic.rs @@ -264,8 +264,9 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { let place = this.deref_pointer(place)?; let rhs = this.read_immediate(rhs)?; + // The LHS can be a pointer, the RHS must be an integer. if !(place.layout.ty.is_integral() || place.layout.ty.is_raw_ptr()) - || !(rhs.layout.ty.is_integral() || rhs.layout.ty.is_raw_ptr()) + || !rhs.layout.ty.is_integral() { span_bug!( this.cur_span(), From 095fa86a3ba30f4198c88ef300354391d3ab97e1 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Mon, 18 Aug 2025 16:42:21 -0400 Subject: [PATCH 195/682] compiler: Add Windows resources to rustc-main and rustc_driver Adds Windows resources with the rust version information to rustc-main.exe and rustc_driver.dll Sets the product description to "Rust Compiler" or "Rust Compiler (channel)" for non-stable channels --- Cargo.lock | 9 ++ compiler/rustc/Cargo.toml | 5 + compiler/rustc/build.rs | 16 ++- compiler/rustc_codegen_ssa/Cargo.toml | 2 +- compiler/rustc_driver/Cargo.toml | 5 + compiler/rustc_driver/build.rs | 21 ++++ compiler/rustc_llvm/Cargo.toml | 2 +- compiler/rustc_windows_rc/Cargo.toml | 11 ++ compiler/rustc_windows_rc/rustc.rc.in | 40 ++++++ compiler/rustc_windows_rc/src/lib.rs | 158 ++++++++++++++++++++++++ src/bootstrap/src/core/builder/tests.rs | 10 +- 11 files changed, 271 insertions(+), 8 deletions(-) create mode 100644 compiler/rustc_driver/build.rs create mode 100644 compiler/rustc_windows_rc/Cargo.toml create mode 100644 compiler/rustc_windows_rc/rustc.rc.in create mode 100644 compiler/rustc_windows_rc/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 8b9c243879ca..03bda287c493 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3248,6 +3248,7 @@ dependencies = [ "rustc_driver_impl", "rustc_public", "rustc_public_bridge", + "rustc_windows_rc", "tikv-jemalloc-sys", ] @@ -3623,6 +3624,7 @@ name = "rustc_driver" version = "0.0.0" dependencies = [ "rustc_driver_impl", + "rustc_windows_rc", ] [[package]] @@ -4718,6 +4720,13 @@ dependencies = [ "semver", ] +[[package]] +name = "rustc_windows_rc" +version = "0.0.0" +dependencies = [ + "cc", +] + [[package]] name = "rustdoc" version = "0.0.0" diff --git a/compiler/rustc/Cargo.toml b/compiler/rustc/Cargo.toml index 3ca752354466..e3214d1ab9ce 100644 --- a/compiler/rustc/Cargo.toml +++ b/compiler/rustc/Cargo.toml @@ -33,3 +33,8 @@ llvm = ['rustc_driver_impl/llvm'] max_level_info = ['rustc_driver_impl/max_level_info'] rustc_randomized_layouts = ['rustc_driver_impl/rustc_randomized_layouts'] # tidy-alphabetical-end + +[build-dependencies] +# tidy-alphabetical-start +rustc_windows_rc = { path = "../rustc_windows_rc" } +# tidy-alphabetical-end diff --git a/compiler/rustc/build.rs b/compiler/rustc/build.rs index 8b7d28d2b8aa..9b5def53e3cb 100644 --- a/compiler/rustc/build.rs +++ b/compiler/rustc/build.rs @@ -1,4 +1,6 @@ -use std::env; +use std::{env, path}; + +use rustc_windows_rc::{VersionInfoFileType, compile_windows_resource_file}; fn main() { let target_os = env::var("CARGO_CFG_TARGET_OS"); @@ -13,6 +15,18 @@ fn main() { // Add a manifest file to rustc.exe. fn set_windows_exe_options() { + set_windows_resource(); + set_windows_manifest(); +} + +fn set_windows_resource() { + let stem = path::PathBuf::from("rustc_main_resource"); + let file_description = "rustc"; + let res_file = compile_windows_resource_file(&stem, file_description, VersionInfoFileType::App); + println!("cargo:rustc-link-arg={}", res_file.display()); +} + +fn set_windows_manifest() { static WINDOWS_MANIFEST_FILE: &str = "Windows Manifest.xml"; let mut manifest = env::current_dir().unwrap(); diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 57e1fee2c0a6..afcfe82d68d2 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -9,7 +9,7 @@ ar_archive_writer = "0.4.2" bitflags.workspace = true bstr = "1.11.3" # `cc` updates often break things, so we pin it here. Cargo enforces "max 1 semver-compat version -# per crate", so if you change this, you need to also change it in `rustc_llvm`. +# per crate", so if you change this, you need to also change it in `rustc_llvm` and `rustc_windows_rc`. cc = "=1.2.16" itertools.workspace = true pathdiff = "0.2.0" diff --git a/compiler/rustc_driver/Cargo.toml b/compiler/rustc_driver/Cargo.toml index e3ee83512952..519097198276 100644 --- a/compiler/rustc_driver/Cargo.toml +++ b/compiler/rustc_driver/Cargo.toml @@ -10,3 +10,8 @@ crate-type = ["dylib"] # tidy-alphabetical-start rustc_driver_impl = { path = "../rustc_driver_impl" } # tidy-alphabetical-end + +[build-dependencies] +# tidy-alphabetical-start +rustc_windows_rc = { path = "../rustc_windows_rc" } +# tidy-alphabetical-end diff --git a/compiler/rustc_driver/build.rs b/compiler/rustc_driver/build.rs new file mode 100644 index 000000000000..ba44fe7a86ed --- /dev/null +++ b/compiler/rustc_driver/build.rs @@ -0,0 +1,21 @@ +use std::{env, path}; + +use rustc_windows_rc::{VersionInfoFileType, compile_windows_resource_file}; + +fn main() { + let target_os = env::var("CARGO_CFG_TARGET_OS"); + let target_env = env::var("CARGO_CFG_TARGET_ENV"); + if Ok("windows") == target_os.as_deref() && Ok("msvc") == target_env.as_deref() { + set_windows_dll_options(); + } else { + // Avoid rerunning the build script every time. + println!("cargo:rerun-if-changed=build.rs"); + } +} + +fn set_windows_dll_options() { + let stem = path::PathBuf::from("rustc_driver_resource"); + let file_description = "rustc_driver"; + let res_file = compile_windows_resource_file(&stem, file_description, VersionInfoFileType::Dll); + println!("cargo:rustc-link-arg={}", res_file.display()); +} diff --git a/compiler/rustc_llvm/Cargo.toml b/compiler/rustc_llvm/Cargo.toml index e74de453be21..6321104ee3ef 100644 --- a/compiler/rustc_llvm/Cargo.toml +++ b/compiler/rustc_llvm/Cargo.toml @@ -11,7 +11,7 @@ libc.workspace = true [build-dependencies] # tidy-alphabetical-start # `cc` updates often break things, so we pin it here. Cargo enforces "max 1 semver-compat version -# per crate", so if you change this, you need to also change it in `rustc_codegen_ssa`. +# per crate", so if you change this, you need to also change it in `rustc_codegen_ssa` and `rustc_windows_rc`. cc = "=1.2.16" # tidy-alphabetical-end diff --git a/compiler/rustc_windows_rc/Cargo.toml b/compiler/rustc_windows_rc/Cargo.toml new file mode 100644 index 000000000000..080acd35c38c --- /dev/null +++ b/compiler/rustc_windows_rc/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "rustc_windows_rc" +version = "0.0.0" +edition = "2024" + +[dependencies] +#tidy-alphabetical-start +# `cc` updates often break things, so we pin it here. Cargo enforces "max 1 semver-compat version +# per crate", so if you change this, you need to also change it in `rustc_llvm` and `rustc_codegen_ssa`. +cc = "=1.2.16" +#tidy-alphabetical-end diff --git a/compiler/rustc_windows_rc/rustc.rc.in b/compiler/rustc_windows_rc/rustc.rc.in new file mode 100644 index 000000000000..10a00b9bd4e3 --- /dev/null +++ b/compiler/rustc_windows_rc/rustc.rc.in @@ -0,0 +1,40 @@ +// A template for the rustc_driver and rustc Windows resource files. +// This file is processed by the build script to produce rustc.rc and rustc_driver.rc +// with the appropriate version information filled in. + +// All the strings are in UTF-8 +#pragma code_page(65001) + +#define RUSTC_FILEDESCRIPTION_STR "@RUSTC_FILEDESCRIPTION_STR@" +#define RUSTC_FILETYPE @RUSTC_FILETYPE@ +#define RUSTC_FILEVERSION_QUAD @RUSTC_FILEVERSION_QUAD@ +#define RUSTC_FILEVERSION_STR "@RUSTC_FILEVERSION_STR@" + +#define RUSTC_PRODUCTNAME_STR "@RUSTC_PRODUCTNAME_STR@" +#define RUSTC_PRODUCTVERSION_QUAD @RUSTC_PRODUCTVERSION_QUAD@ +#define RUSTC_PRODUCTVERSION_STR "@RUSTC_PRODUCTVERSION_STR@" + + +1 VERSIONINFO +FILEVERSION RUSTC_FILEVERSION_QUAD +PRODUCTVERSION RUSTC_PRODUCTVERSION_QUAD +FILEOS 0x00040004 +FILETYPE RUSTC_FILETYPE +FILESUBTYPE 0 +FILEFLAGSMASK 0x3f +FILEFLAGS 0x0 +{ + BLOCK "StringFileInfo" + { + BLOCK "000004b0" + { + VALUE "FileDescription", RUSTC_FILEDESCRIPTION_STR + VALUE "FileVersion", RUSTC_FILEVERSION_STR + VALUE "ProductVersion", RUSTC_PRODUCTVERSION_STR + VALUE "ProductName", RUSTC_PRODUCTNAME_STR + } + } + BLOCK "VarFileInfo" { + VALUE "Translation", 0x0, 0x04b0 + } +} diff --git a/compiler/rustc_windows_rc/src/lib.rs b/compiler/rustc_windows_rc/src/lib.rs new file mode 100644 index 000000000000..caa5e5ef2765 --- /dev/null +++ b/compiler/rustc_windows_rc/src/lib.rs @@ -0,0 +1,158 @@ +//! A build script dependency to create a Windows resource file for the compiler +//! +//! Uses values from the `CFG_VERSION` and `CFG_RELEASE` environment variables +//! to set the product and file version information in the Windows resource file. +use std::{env, ffi, fs, path, process}; + +use cc::windows_registry; + +/// The template for the Windows resource file. +const RESOURCE_TEMPLATE: &str = include_str!("../rustc.rc.in"); + +/// A subset of the possible values for the `FILETYPE` field in a Windows resource file +/// +/// See the `dwFileType` member of [VS_FIXEDFILEINFO](https://learn.microsoft.com/en-us/windows/win32/api/verrsrc/ns-verrsrc-vs_fixedfileinfo#members) +#[derive(Debug, Clone, Copy)] +#[repr(u32)] +pub enum VersionInfoFileType { + /// `VFT_APP` - The file is an application. + App = 0x00000001, + /// `VFT_DLL` - The file is a dynamic link library. + Dll = 0x00000002, +} + +/// Create and compile a Windows resource file with the product and file version information for the rust compiler. +/// +/// Returns the path to the compiled resource file +/// +/// Does not emit any cargo directives, the caller is responsible for that. +pub fn compile_windows_resource_file( + file_stem: &path::Path, + file_description: &str, + filetype: VersionInfoFileType, +) -> path::PathBuf { + let mut resources_dir = path::PathBuf::from(env::var_os("OUT_DIR").unwrap()); + resources_dir.push("resources"); + fs::create_dir_all(&resources_dir).unwrap(); + + let resource_compiler = + find_resource_compiler(&env::var("CARGO_CFG_TARGET_ARCH").unwrap()).expect("found rc.exe"); + + let rc_path = resources_dir.join(file_stem.with_extension("rc")); + + write_resource_script_file(&rc_path, file_description, filetype); + + let res_path = resources_dir.join(file_stem.with_extension("res")); + + let status = process::Command::new(resource_compiler) + .arg("/fo") + .arg(&res_path) + .arg(&rc_path) + .status() + .expect("can execute resource compiler"); + assert!(status.success(), "rc.exe failed with status {}", status); + assert!( + res_path.try_exists().unwrap_or(false), + "resource file {} was not created", + res_path.display() + ); + res_path +} + +/// Writes a Windows resource script file for the rust compiler with the product and file version information +/// into `rc_path` +fn write_resource_script_file( + rc_path: &path::Path, + file_description: &str, + filetype: VersionInfoFileType, +) { + let mut resource_script = RESOURCE_TEMPLATE.to_string(); + + // Set the string product and file version to the same thing as `rustc --version` + let descriptive_version = env::var("CFG_VERSION").unwrap_or("unknown".to_string()); + + // Set the product name to "Rust Compiler" or "Rust Compiler (nightly)" etc + let product_name = product_name(env::var("CFG_RELEASE_CHANNEL").unwrap()); + + // For the numeric version we need `major,minor,patch,build`. + // Extract them from `CFG_RELEASE` which is "major.minor.patch" and a "-dev", "-nightly" or similar suffix + let cfg_release = env::var("CFG_RELEASE").unwrap(); + // remove the suffix, if present and parse into [`ResourceVersion`] + let version = parse_version(cfg_release.split("-").next().unwrap_or("0.0.0")) + .expect("valid CFG_RELEASE version"); + + resource_script = resource_script + .replace("@RUSTC_FILEDESCRIPTION_STR@", file_description) + .replace("@RUSTC_FILETYPE@", &format!("{}", filetype as u32)) + .replace("@RUSTC_FILEVERSION_QUAD@", &version.to_quad_string()) + .replace("@RUSTC_FILEVERSION_STR@", &descriptive_version) + .replace("@RUSTC_PRODUCTNAME_STR@", &product_name) + .replace("@RUSTC_PRODUCTVERSION_QUAD@", &version.to_quad_string()) + .replace("@RUSTC_PRODUCTVERSION_STR@", &descriptive_version); + + fs::write(&rc_path, resource_script) + .unwrap_or_else(|_| panic!("failed to write resource file {}", rc_path.display())); +} + +fn product_name(channel: String) -> String { + format!( + "Rust Compiler{}", + if channel == "stable" { "".to_string() } else { format!(" ({})", channel) } + ) +} + +/// Windows resources store versions as four 16-bit integers. +struct ResourceVersion { + major: u16, + minor: u16, + patch: u16, + build: u16, +} + +impl ResourceVersion { + /// Format the version as a comma-separated string of four integers + /// as expected by Windows resource scripts for the `FILEVERSION` and `PRODUCTVERSION` fields. + fn to_quad_string(&self) -> String { + format!("{},{},{},{}", self.major, self.minor, self.patch, self.build) + } +} + +/// Parse a string in the format "major.minor.patch" into a [`ResourceVersion`]. +/// The build is set to 0. +/// Returns `None` if the version string is not in the expected format. +fn parse_version(version: &str) -> Option { + let mut parts = version.split('.'); + let major = parts.next()?.parse::().ok()?; + let minor = parts.next()?.parse::().ok()?; + let patch = parts.next()?.parse::().ok()?; + if parts.next().is_some() { + None + } else { + Some(ResourceVersion { major, minor, patch, build: 0 }) + } +} + +/// Find the Windows SDK resource compiler `rc.exe` for the given architecture or target triple. +/// Returns `None` if the tool could not be found. +fn find_resource_compiler(arch_or_target: &str) -> Option { + find_windows_sdk_tool(arch_or_target, "rc.exe") +} + +/// Find a Windows SDK tool for the given architecture or target triple. +/// Returns `None` if the tool could not be found. +fn find_windows_sdk_tool(arch_or_target: &str, tool_name: &str) -> Option { + // windows_registry::find_tool can only find MSVC tools, not Windows SDK tools, but + // cc does include the Windows SDK tools in the PATH environment of MSVC tools. + + let msvc_linker = windows_registry::find_tool(arch_or_target, "link.exe")?; + let path = &msvc_linker.env().iter().find(|(k, _)| k == "PATH")?.1; + find_tool_in_path(tool_name, path) +} + +/// Find a tool in the directories in a given PATH-like string. +fn find_tool_in_path>(tool_name: &str, path: P) -> Option { + env::split_paths(path.as_ref()).find_map(|p| { + let tool_path = p.join(tool_name); + if tool_path.try_exists().unwrap_or(false) { Some(tool_path) } else { None } + }) +} diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index b079117c5a7d..9d6382cb4e38 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -1777,7 +1777,7 @@ mod snapshot { insta::assert_snapshot!( ctx.config("check") .path("compiler") - .render_steps(), @"[check] rustc 0 -> rustc 1 (74 crates)"); + .render_steps(), @"[check] rustc 0 -> rustc 1 (75 crates)"); } #[test] @@ -1803,7 +1803,7 @@ mod snapshot { ctx.config("check") .path("compiler") .stage(1) - .render_steps(), @"[check] rustc 0 -> rustc 1 (74 crates)"); + .render_steps(), @"[check] rustc 0 -> rustc 1 (75 crates)"); } #[test] @@ -1817,7 +1817,7 @@ mod snapshot { [build] llvm [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 - [check] rustc 1 -> rustc 2 (74 crates) + [check] rustc 1 -> rustc 2 (75 crates) "); } @@ -1833,7 +1833,7 @@ mod snapshot { [build] rustc 0 -> rustc 1 [build] rustc 1 -> std 1 [check] rustc 1 -> std 1 - [check] rustc 1 -> rustc 2 (74 crates) + [check] rustc 1 -> rustc 2 (75 crates) [check] rustc 1 -> rustc 2 [check] rustc 1 -> Rustdoc 2 [check] rustc 1 -> rustc_codegen_cranelift 2 @@ -1929,7 +1929,7 @@ mod snapshot { ctx.config("check") .paths(&["library", "compiler"]) .args(&args) - .render_steps(), @"[check] rustc 0 -> rustc 1 (74 crates)"); + .render_steps(), @"[check] rustc 0 -> rustc 1 (75 crates)"); } #[test] From d4cb0b475935d29c18f94676c2d0e9793e95b8e3 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 5 Sep 2025 22:32:11 +0200 Subject: [PATCH 196/682] clean-up a bit - if-else reordering: having one fewer nesting levels makes it easier to understand imo --- .../src/transmute/transmute_ptr_to_ref.rs | 20 ++++++++----------- tests/ui/transmute_ptr_to_ref.fixed | 10 +++++----- tests/ui/transmute_ptr_to_ref.rs | 10 +++++----- tests/ui/transmute_ptr_to_ref.stderr | 4 ++-- 4 files changed, 20 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs index e58212fae15c..44fa2121341d 100644 --- a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs @@ -28,10 +28,9 @@ pub(super) fn check<'tcx>( format!("transmute from a pointer type (`{from_ty}`) to a reference type (`{to_ty}`)"), |diag| { let arg = sugg::Sugg::hir(cx, arg, ".."); - let (deref, cast) = if *mutbl == Mutability::Mut { - ("&mut *", "*mut") - } else { - ("&*", "*const") + let (deref, cast) = match mutbl { + Mutability::Mut => ("&mut *", "*mut"), + Mutability::Not => ("&*", "*const"), }; let mut app = Applicability::MachineApplicable; @@ -45,15 +44,12 @@ pub(super) fn check<'tcx>( sugg::make_unop(deref, arg.as_ty(format!("{cast} {ty_snip}"))).to_string() } } else if *from_ptr_ty == *to_ref_ty { - if from_ptr_ty.has_erased_regions() { - if msrv.meets(cx, msrvs::POINTER_CAST) { - format!("{deref}{}.cast::<{to_ref_ty}>()", arg.maybe_paren()) - } else { - sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {to_ref_ty}"))) - .to_string() - } - } else { + if !from_ptr_ty.has_erased_regions() { sugg::make_unop(deref, arg).to_string() + } else if msrv.meets(cx, msrvs::POINTER_CAST) { + format!("{deref}{}.cast::<{to_ref_ty}>()", arg.maybe_paren()) + } else { + sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {to_ref_ty}"))).to_string() } } else { sugg::make_unop(deref, arg.as_ty(format!("{cast} {to_ref_ty}"))).to_string() diff --git a/tests/ui/transmute_ptr_to_ref.fixed b/tests/ui/transmute_ptr_to_ref.fixed index 61e3ac2fe88e..68839fd04c56 100644 --- a/tests/ui/transmute_ptr_to_ref.fixed +++ b/tests/ui/transmute_ptr_to_ref.fixed @@ -5,7 +5,7 @@ clippy::missing_transmute_annotations )] -unsafe fn _ptr_to_ref(p: *const T, m: *mut T, o: *const U, om: *mut U) { +fn ptr_to_ref(p: *const T, m: *mut T, o: *const U, om: *mut U) { unsafe { let _: &T = &*p; //~^ transmute_ptr_to_ref @@ -37,7 +37,7 @@ unsafe fn _ptr_to_ref(p: *const T, m: *mut T, o: *const U, om: *mut U) { } } -fn _issue1231() { +fn issue1231() { struct Foo<'a, T> { bar: &'a T, } @@ -55,7 +55,7 @@ fn _issue1231() { //~^ transmute_ptr_to_ref } -unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 { +fn issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 { unsafe { match 0 { 0 => &*x.cast::<&u32>(), @@ -71,7 +71,7 @@ unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &' } #[clippy::msrv = "1.38"] -unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { +fn meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { unsafe { let a = 0u32; let a = &a as *const u32; @@ -89,7 +89,7 @@ unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { } #[clippy::msrv = "1.37"] -unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { +fn under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { unsafe { let a = 0u32; let a = &a as *const u32; diff --git a/tests/ui/transmute_ptr_to_ref.rs b/tests/ui/transmute_ptr_to_ref.rs index 48e2f527b554..b41b7cd21794 100644 --- a/tests/ui/transmute_ptr_to_ref.rs +++ b/tests/ui/transmute_ptr_to_ref.rs @@ -5,7 +5,7 @@ clippy::missing_transmute_annotations )] -unsafe fn _ptr_to_ref(p: *const T, m: *mut T, o: *const U, om: *mut U) { +fn ptr_to_ref(p: *const T, m: *mut T, o: *const U, om: *mut U) { unsafe { let _: &T = std::mem::transmute(p); //~^ transmute_ptr_to_ref @@ -37,7 +37,7 @@ unsafe fn _ptr_to_ref(p: *const T, m: *mut T, o: *const U, om: *mut U) { } } -fn _issue1231() { +fn issue1231() { struct Foo<'a, T> { bar: &'a T, } @@ -55,7 +55,7 @@ fn _issue1231() { //~^ transmute_ptr_to_ref } -unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 { +fn issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 { unsafe { match 0 { 0 => std::mem::transmute(x), @@ -71,7 +71,7 @@ unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &' } #[clippy::msrv = "1.38"] -unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { +fn meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { unsafe { let a = 0u32; let a = &a as *const u32; @@ -89,7 +89,7 @@ unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { } #[clippy::msrv = "1.37"] -unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { +fn under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { unsafe { let a = 0u32; let a = &a as *const u32; diff --git a/tests/ui/transmute_ptr_to_ref.stderr b/tests/ui/transmute_ptr_to_ref.stderr index 7685c345c861..520f804d264a 100644 --- a/tests/ui/transmute_ptr_to_ref.stderr +++ b/tests/ui/transmute_ptr_to_ref.stderr @@ -43,13 +43,13 @@ error: transmute from a pointer type (`*mut U`) to a reference type (`&T`) LL | let _: &T = std::mem::transmute(om); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(om as *const T)` -error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo<'_, u8>`) +error: transmute from a pointer type (`*const i32`) to a reference type (`&issue1231::Foo<'_, u8>`) --> tests/ui/transmute_ptr_to_ref.rs:46:32 | LL | let _: &Foo = unsafe { std::mem::transmute::<_, &Foo<_>>(raw) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*raw.cast::>()` -error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo<'_, &u8>`) +error: transmute from a pointer type (`*const i32`) to a reference type (`&issue1231::Foo<'_, &u8>`) --> tests/ui/transmute_ptr_to_ref.rs:49:33 | LL | let _: &Foo<&u8> = unsafe { std::mem::transmute::<_, &Foo<&_>>(raw) }; From ab91a63d403b0105cacd72809cd292a72984ed99 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Tue, 26 Aug 2025 21:08:03 -0400 Subject: [PATCH 197/682] Ignore intrinsic calls in cross-crate-inlining cost model --- .../rustc_mir_transform/src/cross_crate_inline.rs | 11 ++++++++++- tests/assembly-llvm/breakpoint.rs | 1 + tests/assembly-llvm/simd/reduce-fadd-unordered.rs | 1 + .../cross-crate-inlining/auxiliary/leaf.rs | 5 +++++ .../cross-crate-inlining/leaf-inlining.rs | 7 +++++++ tests/codegen-llvm/default-visibility.rs | 1 + 6 files changed, 25 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_mir_transform/src/cross_crate_inline.rs b/compiler/rustc_mir_transform/src/cross_crate_inline.rs index b186c2bd7758..df98c07f5495 100644 --- a/compiler/rustc_mir_transform/src/cross_crate_inline.rs +++ b/compiler/rustc_mir_transform/src/cross_crate_inline.rs @@ -135,7 +135,16 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { } } } - TerminatorKind::Call { unwind, .. } => { + TerminatorKind::Call { ref func, unwind, .. } => { + // We track calls because they make our function not a leaf (and in theory, the + // number of calls indicates how likely this function is to perturb other CGUs). + // But intrinsics don't have a body that gets assigned to a CGU, so they are + // ignored. + if let Some((fn_def_id, _)) = func.const_fn_def() + && self.tcx.has_attr(fn_def_id, sym::rustc_intrinsic) + { + return; + } self.calls += 1; if let UnwindAction::Cleanup(_) = unwind { self.landing_pads += 1; diff --git a/tests/assembly-llvm/breakpoint.rs b/tests/assembly-llvm/breakpoint.rs index e0cc2d1eebb7..d119b68e899b 100644 --- a/tests/assembly-llvm/breakpoint.rs +++ b/tests/assembly-llvm/breakpoint.rs @@ -9,6 +9,7 @@ // CHECK-LABEL: use_bp // aarch64: brk #0xf000 // x86_64: int3 +#[inline(never)] pub fn use_bp() { core::arch::breakpoint(); } diff --git a/tests/assembly-llvm/simd/reduce-fadd-unordered.rs b/tests/assembly-llvm/simd/reduce-fadd-unordered.rs index e872826f6ef7..fdd03639da0e 100644 --- a/tests/assembly-llvm/simd/reduce-fadd-unordered.rs +++ b/tests/assembly-llvm/simd/reduce-fadd-unordered.rs @@ -16,6 +16,7 @@ use std::simd::*; // It would emit about an extra fadd, depending on the architecture. // CHECK-LABEL: reduce_fadd_negative_zero +#[inline(never)] pub unsafe fn reduce_fadd_negative_zero(v: f32x4) -> f32 { // x86_64: addps // x86_64-NEXT: movshdup diff --git a/tests/codegen-llvm/cross-crate-inlining/auxiliary/leaf.rs b/tests/codegen-llvm/cross-crate-inlining/auxiliary/leaf.rs index d059a3d0a73b..7b5679c3f4de 100644 --- a/tests/codegen-llvm/cross-crate-inlining/auxiliary/leaf.rs +++ b/tests/codegen-llvm/cross-crate-inlining/auxiliary/leaf.rs @@ -18,3 +18,8 @@ pub fn stem_fn() -> String { fn inner() -> String { String::from("test") } + +// This function's optimized MIR contains a call, but it is to an intrinsic. +pub fn leaf_with_intrinsic(a: &[u64; 2], b: &[u64; 2]) -> bool { + a == b +} diff --git a/tests/codegen-llvm/cross-crate-inlining/leaf-inlining.rs b/tests/codegen-llvm/cross-crate-inlining/leaf-inlining.rs index 37132312ca94..5e7912791ad6 100644 --- a/tests/codegen-llvm/cross-crate-inlining/leaf-inlining.rs +++ b/tests/codegen-llvm/cross-crate-inlining/leaf-inlining.rs @@ -18,3 +18,10 @@ pub fn stem_outer() -> String { // CHECK: call {{.*}}stem_fn leaf::stem_fn() } + +// Check that we inline functions that call intrinsics +#[no_mangle] +pub fn leaf_with_intrinsic_outer(a: &[u64; 2], b: &[u64; 2]) -> bool { + // CHECK-NOT: call {{.*}}leaf_with_intrinsic + leaf::leaf_with_intrinsic(a, b) +} diff --git a/tests/codegen-llvm/default-visibility.rs b/tests/codegen-llvm/default-visibility.rs index 88ff9fee2544..28238e5ef129 100644 --- a/tests/codegen-llvm/default-visibility.rs +++ b/tests/codegen-llvm/default-visibility.rs @@ -32,6 +32,7 @@ pub static tested_symbol: [u8; 6] = *b"foobar"; // INTERPOSABLE: @{{.*}}default_visibility{{.*}}tested_symbol{{.*}} = constant // DEFAULT: @{{.*}}default_visibility{{.*}}tested_symbol{{.*}} = constant +#[inline(never)] pub fn do_memcmp(left: &[u8], right: &[u8]) -> i32 { left.cmp(right) as i32 } From 31b39150c79e77cbde2d0567120977a014c224f8 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 3 Sep 2025 07:47:32 -0500 Subject: [PATCH 198/682] Some hygiene doc improvements --- compiler/rustc_span/src/hygiene.rs | 30 ++++++++++++++++++++---------- compiler/rustc_span/src/lib.rs | 4 ++-- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 19494ffc37ea..633cbc3a4ae2 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -46,6 +46,8 @@ use crate::symbol::{Symbol, kw, sym}; use crate::{DUMMY_SP, HashStableContext, Span, SpanDecoder, SpanEncoder, with_session_globals}; /// A `SyntaxContext` represents a chain of pairs `(ExpnId, Transparency)` named "marks". +/// +/// See for more explanation. #[derive(Clone, Copy, PartialEq, Eq, Hash)] pub struct SyntaxContext(u32); @@ -61,7 +63,10 @@ pub type SyntaxContextKey = (SyntaxContext, ExpnId, Transparency); #[derive(Clone, Copy, Debug)] struct SyntaxContextData { + /// The last macro expansion in the chain. + /// (Here we say the most deeply nested macro expansion is the "outermost" expansion.) outer_expn: ExpnId, + /// Transparency of the last macro expansion outer_transparency: Transparency, parent: SyntaxContext, /// This context, but with all transparent and semi-opaque expansions filtered away. @@ -450,11 +455,13 @@ impl HygieneData { self.syntax_context_data[ctxt.0 as usize].opaque_and_semiopaque } + /// See [`SyntaxContextData::outer_expn`] #[inline] fn outer_expn(&self, ctxt: SyntaxContext) -> ExpnId { self.syntax_context_data[ctxt.0 as usize].outer_expn } + /// The last macro expansion and its Transparency #[inline] fn outer_mark(&self, ctxt: SyntaxContext) -> (ExpnId, Transparency) { let data = &self.syntax_context_data[ctxt.0 as usize]; @@ -900,6 +907,7 @@ impl SyntaxContext { HygieneData::with(|data| data.normalize_to_macro_rules(self)) } + /// See [`SyntaxContextData::outer_expn`] #[inline] pub fn outer_expn(self) -> ExpnId { HygieneData::with(|data| data.outer_expn(self)) @@ -912,6 +920,7 @@ impl SyntaxContext { HygieneData::with(|data| data.expn_data(data.outer_expn(self)).clone()) } + /// See [`HygieneData::outer_mark`] #[inline] fn outer_mark(self) -> (ExpnId, Transparency) { HygieneData::with(|data| data.outer_mark(self)) @@ -982,19 +991,20 @@ impl Span { #[derive(Clone, Debug, Encodable, Decodable, HashStable_Generic)] pub struct ExpnData { // --- The part unique to each expansion. - /// The kind of this expansion - macro or compiler desugaring. pub kind: ExpnKind, - /// The expansion that produced this expansion. + /// The expansion that contains the definition of the macro for this expansion. pub parent: ExpnId, - /// The location of the actual macro invocation or syntax sugar , e.g. - /// `let x = foo!();` or `if let Some(y) = x {}` + /// The span of the macro call which produced this expansion. /// - /// This may recursively refer to other macro invocations, e.g., if - /// `foo!()` invoked `bar!()` internally, and there was an - /// expression inside `bar!`; the call_site of the expression in - /// the expansion would point to the `bar!` invocation; that - /// call_site span would have its own ExpnData, with the call_site - /// pointing to the `foo!` invocation. + /// This span will typically have a different `ExpnData` and `call_site`. + /// This recursively traces back through any macro calls which expanded into further + /// macro calls, until the "source call-site" is reached at the root SyntaxContext. + /// For example, if `food!()` expands to `fruit!()` which then expands to `grape`, + /// then the call-site of `grape` is `fruit!()` and the call-site of `fruit!()` + /// is `food!()`. + /// + /// For a desugaring expansion, this is the span of the expression or node that was + /// desugared. pub call_site: Span, /// Used to force two `ExpnData`s to have different `Fingerprint`s. /// Due to macro expansion, it's possible to end up with two `ExpnId`s diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index ae6755f07642..e95b743b1ce2 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -710,8 +710,8 @@ impl Span { if !ctxt.is_root() { ctxt.outer_expn_data().call_site.source_callsite() } else { self } } - /// The `Span` for the tokens in the previous macro expansion from which `self` was generated, - /// if any. + /// Returns the call-site span of the last macro expansion which produced this `Span`. + /// (see [`ExpnData::call_site`]). Returns `None` if this is not an expansion. pub fn parent_callsite(self) -> Option { let ctxt = self.ctxt(); (!ctxt.is_root()).then(|| ctxt.outer_expn_data().call_site) From a5bb60b19c1113a751c7dac00ea8ae0ac20cd15f Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 6 Sep 2025 13:07:53 +0800 Subject: [PATCH 199/682] Add allow `else` keyword completion in LetStmt Example --- ```rust fn foo() { let _ = 2 el$0 } ``` -> ```rust fn foo() { let _ = 2 else { $0 }; } ``` --- .../ide-completion/src/completions/expr.rs | 6 +- .../ide-completion/src/completions/keyword.rs | 40 +++++ .../crates/ide-completion/src/context.rs | 1 + .../ide-completion/src/context/analysis.rs | 41 +++-- .../ide-completion/src/tests/expression.rs | 149 ++++++++++++++++++ 5 files changed, 220 insertions(+), 17 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs index 1972f166134a..2c7d9e97c44e 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs @@ -61,6 +61,7 @@ pub(crate) fn complete_expr_path( after_if_expr, in_condition, incomplete_let, + after_incomplete_let, in_value, ref ref_expr_parent, after_amp, @@ -385,8 +386,11 @@ pub(crate) fn complete_expr_path( add_keyword("let", "let $1 = $0;"); } - if after_if_expr { + if after_if_expr || after_incomplete_let { add_keyword("else", "else {\n $0\n}"); + } + + if after_if_expr { add_keyword("else if", "else if $1 {\n $0\n}"); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs index aea4e119f207..b3d770997ab0 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs @@ -247,6 +247,46 @@ fn main() { "#, ); + check_edit( + "else", + r#" +fn main() { + let x = if true { + () + } $0 + let y = 92; +} +"#, + r#" +fn main() { + let x = if true { + () + } else { + $0 +}; + let y = 92; +} +"#, + ); + + check_edit( + "else", + r#" +fn main() { + let x = 2 $0 + let y = 92; +} +"#, + r#" +fn main() { + let x = 2 else { + $0 +}; + let y = 92; +} +"#, + ); + check_edit( "loop", r#" diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index 265462d22016..007475688d20 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -147,6 +147,7 @@ pub(crate) struct PathExprCtx<'db> { /// Whether this expression is the direct condition of an if or while expression pub(crate) in_condition: bool, pub(crate) incomplete_let: bool, + pub(crate) after_incomplete_let: bool, pub(crate) in_value: bool, pub(crate) ref_expr_parent: Option, pub(crate) after_amp: bool, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 17978b4c1079..33b98a33cabf 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -947,25 +947,29 @@ fn classify_name_ref<'db>( None } }; - let after_if_expr = |node: SyntaxNode| { - let prev_expr = (|| { - let node = match node.parent().and_then(ast::ExprStmt::cast) { - Some(stmt) => stmt.syntax().clone(), - None => node, - }; - let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?; + let prev_expr = |node: SyntaxNode| { + let node = match node.parent().and_then(ast::ExprStmt::cast) { + Some(stmt) => stmt.syntax().clone(), + None => node, + }; + let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?; - match_ast! { - match prev_sibling { - ast::ExprStmt(stmt) => stmt.expr().filter(|_| stmt.semicolon_token().is_none()), - ast::LetStmt(stmt) => stmt.initializer().filter(|_| stmt.semicolon_token().is_none()), - ast::Expr(expr) => Some(expr), - _ => None, - } + match_ast! { + match prev_sibling { + ast::ExprStmt(stmt) => stmt.expr().filter(|_| stmt.semicolon_token().is_none()), + ast::LetStmt(stmt) => stmt.initializer().filter(|_| stmt.semicolon_token().is_none()), + ast::Expr(expr) => Some(expr), + _ => None, } - })(); + } + }; + let after_if_expr = |node: SyntaxNode| { + let prev_expr = prev_expr(node); matches!(prev_expr, Some(ast::Expr::IfExpr(_))) }; + let after_incomplete_let = |node: SyntaxNode| { + prev_expr(node).and_then(|it| it.syntax().parent()).and_then(ast::LetStmt::cast) + }; // We do not want to generate path completions when we are sandwiched between an item decl signature and its body. // ex. trait Foo $0 {} @@ -1265,10 +1269,14 @@ fn classify_name_ref<'db>( }; let is_func_update = func_update_record(it); let in_condition = is_in_condition(&expr); + let after_incomplete_let = after_incomplete_let(it.clone()).is_some(); + let incomplete_expr_stmt = + it.parent().and_then(ast::ExprStmt::cast).map(|it| it.semicolon_token().is_none()); let incomplete_let = it .parent() .and_then(ast::LetStmt::cast) - .is_some_and(|it| it.semicolon_token().is_none()); + .is_some_and(|it| it.semicolon_token().is_none()) + || after_incomplete_let && incomplete_expr_stmt.unwrap_or(true); let in_value = it.parent().and_then(Either::::cast).is_some(); let impl_ = fetch_immediate_impl(sema, original_file, expr.syntax()); @@ -1292,6 +1300,7 @@ fn classify_name_ref<'db>( self_param, in_value, incomplete_let, + after_incomplete_let, impl_, in_match_guard, }, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index 7a0d00444129..56fbd91a60fc 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -450,6 +450,155 @@ fn completes_in_let_initializer() { ) } +#[test] +fn completes_let_else() { + check( + r#"fn main() { let _ = 2 $0 }"#, + expect![[r#" + fn main() fn() + bt u32 u32 + kw async + kw const + kw crate:: + kw else + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); + + check( + r#"fn main() { let _ = 2 el$0 }"#, + expect![[r#" + fn main() fn() + bt u32 u32 + kw async + kw const + kw crate:: + kw else + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); + + check_edit( + "else", + r#" +fn main() { + let _ = 2 $0 +} +"#, + r#" +fn main() { + let _ = 2 else { + $0 +}; +} +"#, + ); + + check_edit( + "else", + r#" +fn main() { + let _ = 2 el$0 +} +"#, + r#" +fn main() { + let _ = 2 else { + $0 +}; +} +"#, + ); + + check_edit( + "else", + r#" +fn main() { + let _ = 2 $0; +} +"#, + r#" +fn main() { + let _ = 2 else { + $0 +}; +} +"#, + ); + + check_edit( + "else", + r#" +fn main() { + let _ = 2 el$0; +} +"#, + r#" +fn main() { + let _ = 2 else { + $0 +}; +} +"#, + ); +} + #[test] fn completes_after_ref_expr() { check( From 1f8667982014babf392f08d45bb6d7bb1bc27d94 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Fri, 5 Sep 2025 23:12:56 +0200 Subject: [PATCH 200/682] fix(transmute_ptr_to_ref): don't suggest `.cast` when to-type is DST --- .../src/transmute/transmute_ptr_to_ref.rs | 24 ++++++++- tests/ui/transmute_ptr_to_ref.fixed | 29 +++++++++++ tests/ui/transmute_ptr_to_ref.rs | 29 +++++++++++ tests/ui/transmute_ptr_to_ref.stderr | 50 ++++++++++++++++++- 4 files changed, 130 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs index 44fa2121341d..e67ab6a73d26 100644 --- a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs @@ -36,7 +36,19 @@ pub(super) fn check<'tcx>( let sugg = if let Some(ty) = get_explicit_type(path) { let ty_snip = snippet_with_applicability(cx, ty.span, "..", &mut app); - if msrv.meets(cx, msrvs::POINTER_CAST) { + if !to_ref_ty.is_sized(cx.tcx, cx.typing_env()) { + // We can't suggest `.cast()`, because that requires `to_ref_ty` to be Sized. + if from_ptr_ty.has_erased_regions() { + // We can't suggest `as *mut/const () as *mut/const to_ref_ty`, because the former is a + // thin pointer, whereas the latter is a wide pointer, due of its pointee, `to_ref_ty`, + // being !Sized. + // + // The only remaining option is be to skip `*mut/const ()`, but that might not be safe + // to do because of the erased regions in `from_ptr_ty`, so reduce the applicability. + app = Applicability::MaybeIncorrect; + } + sugg::make_unop(deref, arg.as_ty(format!("{cast} {ty_snip}"))).to_string() + } else if msrv.meets(cx, msrvs::POINTER_CAST) { format!("{deref}{}.cast::<{ty_snip}>()", arg.maybe_paren()) } else if from_ptr_ty.has_erased_regions() { sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {ty_snip}"))).to_string() @@ -46,6 +58,16 @@ pub(super) fn check<'tcx>( } else if *from_ptr_ty == *to_ref_ty { if !from_ptr_ty.has_erased_regions() { sugg::make_unop(deref, arg).to_string() + } else if !to_ref_ty.is_sized(cx.tcx, cx.typing_env()) { + // 1. We can't suggest `.cast()`, because that requires `to_ref_ty` to be Sized. + // 2. We can't suggest `as *mut/const () as *mut/const to_ref_ty`, because the former is a + // thin pointer, whereas the latter is a wide pointer, due of its pointee, `to_ref_ty`, + // being !Sized. + // + // The only remaining option is be to skip `*mut/const ()`, but that might not be safe to do + // because of the erased regions in `from_ptr_ty`, so reduce the applicability. + app = Applicability::MaybeIncorrect; + sugg::make_unop(deref, arg.as_ty(format!("{cast} {to_ref_ty}"))).to_string() } else if msrv.meets(cx, msrvs::POINTER_CAST) { format!("{deref}{}.cast::<{to_ref_ty}>()", arg.maybe_paren()) } else { diff --git a/tests/ui/transmute_ptr_to_ref.fixed b/tests/ui/transmute_ptr_to_ref.fixed index 68839fd04c56..c130575df960 100644 --- a/tests/ui/transmute_ptr_to_ref.fixed +++ b/tests/ui/transmute_ptr_to_ref.fixed @@ -106,4 +106,33 @@ fn under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { } } +// handle DSTs +fn issue13357(ptr: *const [i32], s_ptr: *const &str, a_s_ptr: *const [&str]) { + unsafe { + // different types, without erased regions + let _ = &*(ptr as *const [u32]); + //~^ transmute_ptr_to_ref + let _: &[u32] = &*(ptr as *const [u32]); + //~^ transmute_ptr_to_ref + + // different types, with erased regions + let _ = &*(a_s_ptr as *const [&[u8]]); + //~^ transmute_ptr_to_ref + let _: &[&[u8]] = &*(a_s_ptr as *const [&[u8]]); + //~^ transmute_ptr_to_ref + + // same type, without erased regions + let _ = &*(ptr as *const [i32]); + //~^ transmute_ptr_to_ref + let _: &[i32] = &*ptr; + //~^ transmute_ptr_to_ref + + // same type, with erased regions + let _ = &*(a_s_ptr as *const [&str]); + //~^ transmute_ptr_to_ref + let _: &[&str] = &*(a_s_ptr as *const [&str]); + //~^ transmute_ptr_to_ref + } +} + fn main() {} diff --git a/tests/ui/transmute_ptr_to_ref.rs b/tests/ui/transmute_ptr_to_ref.rs index b41b7cd21794..f79d54234a2c 100644 --- a/tests/ui/transmute_ptr_to_ref.rs +++ b/tests/ui/transmute_ptr_to_ref.rs @@ -106,4 +106,33 @@ fn under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { } } +// handle DSTs +fn issue13357(ptr: *const [i32], s_ptr: *const &str, a_s_ptr: *const [&str]) { + unsafe { + // different types, without erased regions + let _ = core::mem::transmute::<_, &[u32]>(ptr); + //~^ transmute_ptr_to_ref + let _: &[u32] = core::mem::transmute(ptr); + //~^ transmute_ptr_to_ref + + // different types, with erased regions + let _ = core::mem::transmute::<_, &[&[u8]]>(a_s_ptr); + //~^ transmute_ptr_to_ref + let _: &[&[u8]] = core::mem::transmute(a_s_ptr); + //~^ transmute_ptr_to_ref + + // same type, without erased regions + let _ = core::mem::transmute::<_, &[i32]>(ptr); + //~^ transmute_ptr_to_ref + let _: &[i32] = core::mem::transmute(ptr); + //~^ transmute_ptr_to_ref + + // same type, with erased regions + let _ = core::mem::transmute::<_, &[&str]>(a_s_ptr); + //~^ transmute_ptr_to_ref + let _: &[&str] = core::mem::transmute(a_s_ptr); + //~^ transmute_ptr_to_ref + } +} + fn main() {} diff --git a/tests/ui/transmute_ptr_to_ref.stderr b/tests/ui/transmute_ptr_to_ref.stderr index 520f804d264a..3f404d295fef 100644 --- a/tests/ui/transmute_ptr_to_ref.stderr +++ b/tests/ui/transmute_ptr_to_ref.stderr @@ -133,5 +133,53 @@ error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32 LL | _ => std::mem::transmute::<_, &&'b u32>(x), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &'b u32)` -error: aborting due to 22 previous errors +error: transmute from a pointer type (`*const [i32]`) to a reference type (`&[u32]`) + --> tests/ui/transmute_ptr_to_ref.rs:113:17 + | +LL | let _ = core::mem::transmute::<_, &[u32]>(ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(ptr as *const [u32])` + +error: transmute from a pointer type (`*const [i32]`) to a reference type (`&[u32]`) + --> tests/ui/transmute_ptr_to_ref.rs:115:25 + | +LL | let _: &[u32] = core::mem::transmute(ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(ptr as *const [u32])` + +error: transmute from a pointer type (`*const [&str]`) to a reference type (`&[&[u8]]`) + --> tests/ui/transmute_ptr_to_ref.rs:119:17 + | +LL | let _ = core::mem::transmute::<_, &[&[u8]]>(a_s_ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a_s_ptr as *const [&[u8]])` + +error: transmute from a pointer type (`*const [&str]`) to a reference type (`&[&[u8]]`) + --> tests/ui/transmute_ptr_to_ref.rs:121:27 + | +LL | let _: &[&[u8]] = core::mem::transmute(a_s_ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a_s_ptr as *const [&[u8]])` + +error: transmute from a pointer type (`*const [i32]`) to a reference type (`&[i32]`) + --> tests/ui/transmute_ptr_to_ref.rs:125:17 + | +LL | let _ = core::mem::transmute::<_, &[i32]>(ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(ptr as *const [i32])` + +error: transmute from a pointer type (`*const [i32]`) to a reference type (`&[i32]`) + --> tests/ui/transmute_ptr_to_ref.rs:127:25 + | +LL | let _: &[i32] = core::mem::transmute(ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*ptr` + +error: transmute from a pointer type (`*const [&str]`) to a reference type (`&[&str]`) + --> tests/ui/transmute_ptr_to_ref.rs:131:17 + | +LL | let _ = core::mem::transmute::<_, &[&str]>(a_s_ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a_s_ptr as *const [&str])` + +error: transmute from a pointer type (`*const [&str]`) to a reference type (`&[&str]`) + --> tests/ui/transmute_ptr_to_ref.rs:133:26 + | +LL | let _: &[&str] = core::mem::transmute(a_s_ptr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a_s_ptr as *const [&str])` + +error: aborting due to 30 previous errors From 4ae4f67e8c4c41b1c46d021be63ddc645d36154a Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 4 Sep 2025 11:24:36 +0200 Subject: [PATCH 201/682] clean-up a bit Notably, rename `same_type_and_consts` to `same_type_modulo_regions` to be more explicit about the function ignoring lifetimes --- clippy_lints/src/matches/needless_match.rs | 6 +- clippy_lints/src/operators/erasing_op.rs | 4 +- clippy_lints/src/use_self.rs | 77 ++++++++++------------ clippy_lints/src/useless_conversion.rs | 14 ++-- clippy_utils/src/ty/mod.rs | 32 +++++---- 5 files changed, 64 insertions(+), 69 deletions(-) diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs index b04db03f8d2e..3a2097c3df26 100644 --- a/clippy_lints/src/matches/needless_match.rs +++ b/clippy_lints/src/matches/needless_match.rs @@ -1,7 +1,7 @@ use super::NEEDLESS_MATCH; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts}; +use clippy_utils::ty::{is_type_diagnostic_item, same_type_modulo_regions}; use clippy_utils::{ SpanlessEq, eq_expr_value, get_parent_expr_for_hir, higher, is_else_clause, is_res_lang_ctor, over, path_res, peel_blocks_with_stmt, @@ -122,7 +122,7 @@ fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_> // Compare match_expr ty with local in `let local = match match_expr {..}` Node::LetStmt(local) => { let results = cx.typeck_results(); - return same_type_and_consts(results.node_type(local.hir_id), results.expr_ty(expr)); + return same_type_modulo_regions(results.node_type(local.hir_id), results.expr_ty(expr)); }, // compare match_expr ty with RetTy in `fn foo() -> RetTy` Node::Item(item) => { @@ -133,7 +133,7 @@ fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_> .instantiate_identity() .output() .skip_binder(); - return same_type_and_consts(output, cx.typeck_results().expr_ty(expr)); + return same_type_modulo_regions(output, cx.typeck_results().expr_ty(expr)); } }, // check the parent expr for this whole block `{ match match_expr {..} }` diff --git a/clippy_lints/src/operators/erasing_op.rs b/clippy_lints/src/operators/erasing_op.rs index e3fc8d8fea7d..8f5ee390f722 100644 --- a/clippy_lints/src/operators/erasing_op.rs +++ b/clippy_lints/src/operators/erasing_op.rs @@ -1,6 +1,6 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::ty::same_type_and_consts; +use clippy_utils::ty::same_type_modulo_regions; use rustc_hir::{BinOpKind, Expr}; use rustc_lint::LateContext; @@ -29,7 +29,7 @@ pub(super) fn check<'tcx>( fn different_types(tck: &TypeckResults<'_>, input: &Expr<'_>, output: &Expr<'_>) -> bool { let input_ty = tck.expr_ty(input).peel_refs(); let output_ty = tck.expr_ty(output).peel_refs(); - !same_type_and_consts(input_ty, output_ty) + !same_type_modulo_regions(input_ty, output_ty) } fn check_op<'tcx>( diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index aeda864b7eb5..2679eeba8785 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -2,20 +2,21 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::ty::{same_type_and_consts, ty_from_hir_ty}; +use clippy_utils::ty::{same_type_modulo_regions, ty_from_hir_ty}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_ty}; use rustc_hir::{ - self as hir, AmbigArg, Expr, ExprKind, FnRetTy, FnSig, GenericArgsParentheses, GenericParam, GenericParamKind, - HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatExpr, PatExprKind, PatKind, Path, QPath, Ty, TyKind, + self as hir, AmbigArg, Expr, ExprKind, FnRetTy, FnSig, GenericArgsParentheses, GenericParamKind, HirId, Impl, + ImplItemKind, Item, ItemKind, Pat, PatExpr, PatExprKind, PatKind, Path, QPath, Ty, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty as MiddleTy; use rustc_session::impl_lint_pass; use rustc_span::Span; +use std::iter; declare_clippy_lint! { /// ### What it does @@ -101,17 +102,11 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { let types_to_skip = generics .params .iter() - .filter_map(|param| match param { - GenericParam { - kind: - GenericParamKind::Const { - ty: Ty { hir_id, .. }, .. - }, - .. - } => Some(*hir_id), + .filter_map(|param| match param.kind { + GenericParamKind::Const { ty, .. } => Some(ty.hir_id), _ => None, }) - .chain(std::iter::once(self_ty.hir_id)) + .chain([self_ty.hir_id]) .collect(); StackItem::Check { impl_id: item.owner_id.def_id, @@ -210,11 +205,11 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { && !types_to_skip.contains(&hir_ty.hir_id) && let ty = ty_from_hir_ty(cx, hir_ty.as_unambig_ty()) && let impl_ty = cx.tcx.type_of(impl_id).instantiate_identity() - && same_type_and_consts(ty, impl_ty) + && same_type_modulo_regions(ty, impl_ty) // Ensure the type we encounter and the one from the impl have the same lifetime parameters. It may be that - // the lifetime parameters of `ty` are elided (`impl<'a> Foo<'a> { fn new() -> Self { Foo{..} } }`, in + // the lifetime parameters of `ty` are elided (`impl<'a> Foo<'a> { fn new() -> Self { Foo{..} } }`), in // which case we must still trigger the lint. - && (has_no_lifetime(ty) || same_lifetimes(ty, impl_ty)) + && (!has_lifetime(ty) || same_lifetimes(ty, impl_ty)) && self.msrv.meets(cx, msrvs::TYPE_ALIAS_ENUM_VARIANTS) { span_lint(cx, hir_ty.span); @@ -227,18 +222,16 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { && cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id).instantiate_identity() && self.msrv.meets(cx, msrvs::TYPE_ALIAS_ENUM_VARIANTS) { - } else { - return; - } - match expr.kind { - ExprKind::Struct(QPath::Resolved(_, path), ..) => check_path(cx, path), - ExprKind::Call(fun, _) => { - if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind { - check_path(cx, path); - } - }, - ExprKind::Path(QPath::Resolved(_, path)) => check_path(cx, path), - _ => (), + match expr.kind { + ExprKind::Struct(QPath::Resolved(_, path), ..) => check_path(cx, path), + ExprKind::Call(fun, _) => { + if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind { + check_path(cx, path); + } + }, + ExprKind::Path(QPath::Resolved(_, path)) => check_path(cx, path), + _ => (), + } } } @@ -308,36 +301,32 @@ fn lint_path_to_variant(cx: &LateContext<'_>, path: &Path<'_>) { } } -/// Returns `true` if types `a` and `b` have the same lifetime parameters, otherwise returns -/// `false`. +/// Checks whether types `a` and `b` have the same lifetime parameters. /// /// This function does not check that types `a` and `b` are the same types. fn same_lifetimes<'tcx>(a: MiddleTy<'tcx>, b: MiddleTy<'tcx>) -> bool { use rustc_middle::ty::{Adt, GenericArgKind}; - match (&a.kind(), &b.kind()) { - (&Adt(_, args_a), &Adt(_, args_b)) => { - args_a - .iter() - .zip(args_b.iter()) - .all(|(arg_a, arg_b)| match (arg_a.kind(), arg_b.kind()) { - // TODO: Handle inferred lifetimes - (GenericArgKind::Lifetime(inner_a), GenericArgKind::Lifetime(inner_b)) => inner_a == inner_b, - (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => same_lifetimes(type_a, type_b), - _ => true, - }) + match (a.kind(), b.kind()) { + (Adt(_, args_a), Adt(_, args_b)) => { + iter::zip(*args_a, *args_b).all(|(arg_a, arg_b)| match (arg_a.kind(), arg_b.kind()) { + // TODO: Handle inferred lifetimes + (GenericArgKind::Lifetime(inner_a), GenericArgKind::Lifetime(inner_b)) => inner_a == inner_b, + (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => same_lifetimes(type_a, type_b), + _ => true, + }) }, _ => a == b, } } -/// Returns `true` if `ty` has no lifetime parameter, otherwise returns `false`. -fn has_no_lifetime(ty: MiddleTy<'_>) -> bool { +/// Checks whether `ty` has lifetime parameters. +fn has_lifetime(ty: MiddleTy<'_>) -> bool { use rustc_middle::ty::{Adt, GenericArgKind}; match ty.kind() { - &Adt(_, args) => !args + Adt(_, args) => args .iter() // TODO: Handle inferred lifetimes .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(..))), - _ => true, + _ => false, } } diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 70ae982a4458..5274fdbd1dc2 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::{DiagExt as _, Sugg}; -use clippy_utils::ty::{get_type_diagnostic_name, is_copy, is_type_diagnostic_item, same_type_and_consts}; +use clippy_utils::ty::{get_type_diagnostic_name, is_copy, is_type_diagnostic_item, same_type_modulo_regions}; use clippy_utils::{ get_parent_expr, is_inherent_method_call, is_trait_item, is_trait_method, is_ty_alias, path_to_local, sym, }; @@ -184,7 +184,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { && (is_trait_item(cx, arg, sym::Into) || is_trait_item(cx, arg, sym::From)) && let ty::FnDef(_, args) = cx.typeck_results().expr_ty(arg).kind() && let &[from_ty, to_ty] = args.into_type_list(cx.tcx).as_slice() - && same_type_and_consts(from_ty, to_ty) + && same_type_modulo_regions(from_ty, to_ty) { span_lint_and_then( cx, @@ -207,7 +207,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { if is_trait_method(cx, e, sym::Into) && name.ident.name == sym::into { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(recv); - if same_type_and_consts(a, b) { + if same_type_modulo_regions(a, b) { let mut app = Applicability::MachineApplicable; let sugg = snippet_with_context(cx, recv.span, e.span.ctxt(), "", &mut app).0; span_lint_and_sugg( @@ -324,7 +324,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { // If the types are identical then .into_iter() can be removed, unless the type // implements Copy, in which case .into_iter() returns a copy of the receiver and // cannot be safely omitted. - if same_type_and_consts(a, b) && !is_copy(cx, b) { + if same_type_modulo_regions(a, b) && !is_copy(cx, b) { // Below we check if the parent method call meets the following conditions: // 1. First parameter is `&mut self` (requires mutable reference) // 2. Second parameter implements the `FnMut` trait (e.g., Iterator::any) @@ -371,7 +371,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { && is_type_diagnostic_item(cx, a, sym::Result) && let ty::Adt(_, args) = a.kind() && let Some(a_type) = args.types().next() - && same_type_and_consts(a_type, b) + && same_type_modulo_regions(a_type, b) { span_lint_and_help( cx, @@ -396,7 +396,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { && is_type_diagnostic_item(cx, a, sym::Result) && let ty::Adt(_, args) = a.kind() && let Some(a_type) = args.types().next() - && same_type_and_consts(a_type, b) + && same_type_modulo_regions(a_type, b) { let hint = format!("consider removing `{}()`", snippet(cx, path.span, "TryFrom::try_from")); span_lint_and_help( @@ -407,7 +407,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { None, hint, ); - } else if name == sym::from_fn && same_type_and_consts(a, b) { + } else if name == sym::from_fn && same_type_modulo_regions(a, b) { let mut app = Applicability::MachineApplicable; let sugg = Sugg::hir_with_context(cx, arg, e.span.ctxt(), "", &mut app).maybe_paren(); let sugg_msg = format!("consider removing `{}()`", snippet(cx, path.span, "From::from")); diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 8e302f9d2ad1..8fbaac58288f 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -513,25 +513,31 @@ pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) { inner(ty, 0) } -/// Returns `true` if types `a` and `b` are same types having same `Const` generic args, -/// otherwise returns `false` -pub fn same_type_and_consts<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { +/// Checks whether `a` and `b` are same types having same `Const` generic args, but ignores +/// lifetimes. +/// +/// For example, the function would return `true` for +/// - `u32` and `u32` +/// - `[u8; N]` and `[u8; M]`, if `N=M` +/// - `Option` and `Option`, if `same_type_modulo_regions(T, U)` holds +/// - `&'a str` and `&'b str` +/// +/// and `false` for: +/// - `Result` and `Result` +pub fn same_type_modulo_regions<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { match (&a.kind(), &b.kind()) { (&ty::Adt(did_a, args_a), &ty::Adt(did_b, args_b)) => { if did_a != did_b { return false; } - args_a - .iter() - .zip(args_b.iter()) - .all(|(arg_a, arg_b)| match (arg_a.kind(), arg_b.kind()) { - (GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => inner_a == inner_b, - (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => { - same_type_and_consts(type_a, type_b) - }, - _ => true, - }) + iter::zip(*args_a, *args_b).all(|(arg_a, arg_b)| match (arg_a.kind(), arg_b.kind()) { + (GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => inner_a == inner_b, + (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => { + same_type_modulo_regions(type_a, type_b) + }, + _ => true, + }) }, _ => a == b, } From 6812aa3403cc6b8c5a229fc36f0022b06c276e3c Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 4 Sep 2025 12:27:19 +0200 Subject: [PATCH 202/682] fix(use_self): descend into type's children when looking for lifetimes This unfortunately breaks another test case -- it had only worked because when we checked `!has_lifetime` on `S2>`, we only looked for generic lifetime params on `S2`, forgetting to descend into `S`. One thing that makes this a bit less sad is that this particular test case doesn't come from a reported issue, but rather was added as a miscellaneous check during a kind of unrelated PR. --- clippy_lints/src/use_self.rs | 8 +++++--- tests/ui/use_self.fixed | 18 ++++++++++++++++-- tests/ui/use_self.rs | 16 +++++++++++++++- tests/ui/use_self.stderr | 8 +------- 4 files changed, 37 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 2679eeba8785..e84641d4d1b9 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -323,10 +323,12 @@ fn same_lifetimes<'tcx>(a: MiddleTy<'tcx>, b: MiddleTy<'tcx>) -> bool { fn has_lifetime(ty: MiddleTy<'_>) -> bool { use rustc_middle::ty::{Adt, GenericArgKind}; match ty.kind() { - Adt(_, args) => args - .iter() + Adt(_, args) => args.iter().any(|arg| match arg.kind() { // TODO: Handle inferred lifetimes - .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(..))), + GenericArgKind::Lifetime(..) => true, + GenericArgKind::Type(ty) => has_lifetime(ty), + _ => false, + }), _ => false, } } diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index cccb6bffabb7..075e31d202b0 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -530,8 +530,8 @@ mod issue7206 { impl<'a> S2> { fn new_again() -> Self { - Self::new() - //~^ use_self + S2::new() + // FIXME: ^Broken by PR #15611 } } } @@ -755,3 +755,17 @@ mod crash_check_13128 { } } } + +mod issue_13277 { + trait Foo { + type Item<'foo>; + } + struct Bar<'b> { + content: &'b str, + } + impl<'b> Foo for Option> { + // when checking whether `Option>` has a lifetime, check not only the outer + // `Option`, but also the inner `Bar<'foo>` + type Item<'foo> = Option>; + } +} diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index 09288677aa71..6fbba0bbc550 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -531,7 +531,7 @@ mod issue7206 { impl<'a> S2> { fn new_again() -> Self { S2::new() - //~^ use_self + // FIXME: ^Broken by PR #15611 } } } @@ -755,3 +755,17 @@ mod crash_check_13128 { } } } + +mod issue_13277 { + trait Foo { + type Item<'foo>; + } + struct Bar<'b> { + content: &'b str, + } + impl<'b> Foo for Option> { + // when checking whether `Option>` has a lifetime, check not only the outer + // `Option`, but also the inner `Bar<'foo>` + type Item<'foo> = Option>; + } +} diff --git a/tests/ui/use_self.stderr b/tests/ui/use_self.stderr index 781327696ac1..5f65c53ea25c 100644 --- a/tests/ui/use_self.stderr +++ b/tests/ui/use_self.stderr @@ -169,12 +169,6 @@ error: unnecessary structure name repetition LL | A::new::(submod::B {}) | ^ help: use the applicable keyword: `Self` -error: unnecessary structure name repetition - --> tests/ui/use_self.rs:533:13 - | -LL | S2::new() - | ^^ help: use the applicable keyword: `Self` - error: unnecessary structure name repetition --> tests/ui/use_self.rs:571:17 | @@ -259,5 +253,5 @@ error: unnecessary structure name repetition LL | E::A => {}, | ^ help: use the applicable keyword: `Self` -error: aborting due to 43 previous errors +error: aborting due to 42 previous errors From e7c91ab13ad36e3dfefc4f4f92f4c6e5e445c8f6 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 4 Sep 2025 20:49:26 +0200 Subject: [PATCH 203/682] remove `has_lifetime` It's now redundant to `same_lifetimes`: if `ty` and `impl_ty` are the same type, and they both have no lifetimes, `same_lifetimes` will return `true` already --- clippy_lints/src/use_self.rs | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index e84641d4d1b9..8210320591c6 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -209,7 +209,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { // Ensure the type we encounter and the one from the impl have the same lifetime parameters. It may be that // the lifetime parameters of `ty` are elided (`impl<'a> Foo<'a> { fn new() -> Self { Foo{..} } }`), in // which case we must still trigger the lint. - && (!has_lifetime(ty) || same_lifetimes(ty, impl_ty)) + && same_lifetimes(ty, impl_ty) && self.msrv.meets(cx, msrvs::TYPE_ALIAS_ENUM_VARIANTS) { span_lint(cx, hir_ty.span); @@ -318,17 +318,3 @@ fn same_lifetimes<'tcx>(a: MiddleTy<'tcx>, b: MiddleTy<'tcx>) -> bool { _ => a == b, } } - -/// Checks whether `ty` has lifetime parameters. -fn has_lifetime(ty: MiddleTy<'_>) -> bool { - use rustc_middle::ty::{Adt, GenericArgKind}; - match ty.kind() { - Adt(_, args) => args.iter().any(|arg| match arg.kind() { - // TODO: Handle inferred lifetimes - GenericArgKind::Lifetime(..) => true, - GenericArgKind::Type(ty) => has_lifetime(ty), - _ => false, - }), - _ => false, - } -} From 4fd9a063855302cf3276c2f6b9349e41d75adb1c Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 6 Sep 2025 12:50:57 +0200 Subject: [PATCH 204/682] Move `derive.rs` to `derive/mod.rs` Will hopefully make the next commit's diff somewhat easier to understand --- clippy_lints/src/{derive.rs => derive/mod.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename clippy_lints/src/{derive.rs => derive/mod.rs} (100%) diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive/mod.rs similarity index 100% rename from clippy_lints/src/derive.rs rename to clippy_lints/src/derive/mod.rs From 8a11cf8d3a6366d4d5e2d52e94003018f7ef8181 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 6 Sep 2025 11:43:34 +0200 Subject: [PATCH 205/682] Split out each lint into a separate module Best viewed with `git diff --color-moved` --- .../src/derive/derive_ord_xor_partial_ord.rs | 50 +++ .../derive/derive_partial_eq_without_eq.rs | 87 +++++ .../src/derive/derived_hash_with_manual_eq.rs | 49 +++ .../src/derive/expl_impl_clone_on_copy.rs | 65 ++++ clippy_lints/src/derive/mod.rs | 336 +----------------- .../src/derive/unsafe_derive_deserialize.rs | 93 +++++ 6 files changed, 356 insertions(+), 324 deletions(-) create mode 100644 clippy_lints/src/derive/derive_ord_xor_partial_ord.rs create mode 100644 clippy_lints/src/derive/derive_partial_eq_without_eq.rs create mode 100644 clippy_lints/src/derive/derived_hash_with_manual_eq.rs create mode 100644 clippy_lints/src/derive/expl_impl_clone_on_copy.rs create mode 100644 clippy_lints/src/derive/unsafe_derive_deserialize.rs diff --git a/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs b/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs new file mode 100644 index 000000000000..cbbcb2f7a3ba --- /dev/null +++ b/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs @@ -0,0 +1,50 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty::Ty; +use rustc_span::{Span, sym}; + +use super::DERIVE_ORD_XOR_PARTIAL_ORD; + +/// Implementation of the `DERIVE_ORD_XOR_PARTIAL_ORD` lint. +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + span: Span, + trait_ref: &hir::TraitRef<'_>, + ty: Ty<'tcx>, + ord_is_automatically_derived: bool, +) { + if let Some(ord_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) + && let Some(partial_ord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait() + && let Some(def_id) = &trait_ref.trait_def_id() + && *def_id == ord_trait_def_id + { + // Look for the PartialOrd implementations for `ty` + cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| { + let partial_ord_is_automatically_derived = cx.tcx.is_automatically_derived(impl_id); + + if partial_ord_is_automatically_derived == ord_is_automatically_derived { + return; + } + + let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); + + // Only care about `impl PartialOrd for Foo` + // For `impl PartialOrd for A, input_types is [A, B] + if trait_ref.instantiate_identity().args.type_at(1) == ty { + let mess = if partial_ord_is_automatically_derived { + "you are implementing `Ord` explicitly but have derived `PartialOrd`" + } else { + "you are deriving `Ord` but have implemented `PartialOrd` explicitly" + }; + + span_lint_and_then(cx, DERIVE_ORD_XOR_PARTIAL_ORD, span, mess, |diag| { + if let Some(local_def_id) = impl_id.as_local() { + let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); + diag.span_note(cx.tcx.hir_span(hir_id), "`PartialOrd` implemented here"); + } + }); + } + }); + } +} diff --git a/clippy_lints/src/derive/derive_partial_eq_without_eq.rs b/clippy_lints/src/derive/derive_partial_eq_without_eq.rs new file mode 100644 index 000000000000..ed7881c461ff --- /dev/null +++ b/clippy_lints/src/derive/derive_partial_eq_without_eq.rs @@ -0,0 +1,87 @@ +use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::has_non_exhaustive_attr; +use clippy_utils::ty::implements_trait_with_env; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, ClauseKind, GenericParamDefKind, ParamEnv, TraitPredicate, Ty, TyCtxt, Upcast}; +use rustc_span::{Span, sym}; + +use super::DERIVE_PARTIAL_EQ_WITHOUT_EQ; + +/// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint. +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { + if let ty::Adt(adt, args) = ty.kind() + && cx.tcx.visibility(adt.did()).is_public() + && let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq) + && let Some(def_id) = trait_ref.trait_def_id() + && cx.tcx.is_diagnostic_item(sym::PartialEq, def_id) + && !has_non_exhaustive_attr(cx.tcx, *adt) + && !ty_implements_eq_trait(cx.tcx, ty, eq_trait_def_id) + && let typing_env = typing_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id) + && let Some(local_def_id) = adt.did().as_local() + // If all of our fields implement `Eq`, we can implement `Eq` too + && adt + .all_fields() + .map(|f| f.ty(cx.tcx, args)) + .all(|ty| implements_trait_with_env(cx.tcx, typing_env, ty, eq_trait_def_id, None, &[])) + { + span_lint_hir_and_then( + cx, + DERIVE_PARTIAL_EQ_WITHOUT_EQ, + cx.tcx.local_def_id_to_hir_id(local_def_id), + span.ctxt().outer_expn_data().call_site, + "you are deriving `PartialEq` and can implement `Eq`", + |diag| { + diag.span_suggestion( + span.ctxt().outer_expn_data().call_site, + "consider deriving `Eq` as well", + "PartialEq, Eq", + Applicability::MachineApplicable, + ); + }, + ); + } +} + +fn ty_implements_eq_trait<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, eq_trait_id: DefId) -> bool { + tcx.non_blanket_impls_for_ty(eq_trait_id, ty).next().is_some() +} + +/// Creates the `ParamEnv` used for the given type's derived `Eq` impl. +fn typing_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> ty::TypingEnv<'_> { + // Initial map from generic index to param def. + // Vec<(param_def, needs_eq)> + let mut params = tcx + .generics_of(did) + .own_params + .iter() + .map(|p| (p, matches!(p.kind, GenericParamDefKind::Type { .. }))) + .collect::>(); + + let ty_predicates = tcx.predicates_of(did).predicates; + for (p, _) in ty_predicates { + if let ClauseKind::Trait(p) = p.kind().skip_binder() + && p.trait_ref.def_id == eq_trait_id + && let ty::Param(self_ty) = p.trait_ref.self_ty().kind() + { + // Flag types which already have an `Eq` bound. + params[self_ty.index as usize].1 = false; + } + } + + let param_env = ParamEnv::new(tcx.mk_clauses_from_iter(ty_predicates.iter().map(|&(p, _)| p).chain( + params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| { + ClauseKind::Trait(TraitPredicate { + trait_ref: ty::TraitRef::new(tcx, eq_trait_id, [tcx.mk_param_from_def(param)]), + polarity: ty::PredicatePolarity::Positive, + }) + .upcast(tcx) + }), + ))); + ty::TypingEnv { + typing_mode: ty::TypingMode::non_body_analysis(), + param_env, + } +} diff --git a/clippy_lints/src/derive/derived_hash_with_manual_eq.rs b/clippy_lints/src/derive/derived_hash_with_manual_eq.rs new file mode 100644 index 000000000000..6f36a58025a2 --- /dev/null +++ b/clippy_lints/src/derive/derived_hash_with_manual_eq.rs @@ -0,0 +1,49 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use rustc_hir as hir; +use rustc_lint::LateContext; +use rustc_middle::ty::Ty; +use rustc_span::{Span, sym}; + +use super::DERIVED_HASH_WITH_MANUAL_EQ; + +/// Implementation of the `DERIVED_HASH_WITH_MANUAL_EQ` lint. +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + span: Span, + trait_ref: &hir::TraitRef<'_>, + ty: Ty<'tcx>, + hash_is_automatically_derived: bool, +) { + if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait() + && let Some(def_id) = trait_ref.trait_def_id() + && cx.tcx.is_diagnostic_item(sym::Hash, def_id) + { + // Look for the PartialEq implementations for `ty` + cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| { + let peq_is_automatically_derived = cx.tcx.is_automatically_derived(impl_id); + + if !hash_is_automatically_derived || peq_is_automatically_derived { + return; + } + + let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); + + // Only care about `impl PartialEq for Foo` + // For `impl PartialEq for A, input_types is [A, B] + if trait_ref.instantiate_identity().args.type_at(1) == ty { + span_lint_and_then( + cx, + DERIVED_HASH_WITH_MANUAL_EQ, + span, + "you are deriving `Hash` but have implemented `PartialEq` explicitly", + |diag| { + if let Some(local_def_id) = impl_id.as_local() { + let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); + diag.span_note(cx.tcx.hir_span(hir_id), "`PartialEq` implemented here"); + } + }, + ); + } + }); + } +} diff --git a/clippy_lints/src/derive/expl_impl_clone_on_copy.rs b/clippy_lints/src/derive/expl_impl_clone_on_copy.rs new file mode 100644 index 000000000000..6b97b4bd6b4d --- /dev/null +++ b/clippy_lints/src/derive/expl_impl_clone_on_copy.rs @@ -0,0 +1,65 @@ +use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::ty::{implements_trait, is_copy}; +use rustc_hir::{self as hir, Item}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, GenericArgKind, Ty}; + +use super::EXPL_IMPL_CLONE_ON_COPY; + +/// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint. +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { + let clone_id = match cx.tcx.lang_items().clone_trait() { + Some(id) if trait_ref.trait_def_id() == Some(id) => id, + _ => return, + }; + let Some(copy_id) = cx.tcx.lang_items().copy_trait() else { + return; + }; + let (ty_adt, ty_subs) = match *ty.kind() { + // Unions can't derive clone. + ty::Adt(adt, subs) if !adt.is_union() => (adt, subs), + _ => return, + }; + // If the current self type doesn't implement Copy (due to generic constraints), search to see if + // there's a Copy impl for any instance of the adt. + if !is_copy(cx, ty) { + if ty_subs.non_erasable_generics().next().is_some() { + let has_copy_impl = cx.tcx.local_trait_impls(copy_id).iter().any(|&id| { + matches!(cx.tcx.type_of(id).instantiate_identity().kind(), ty::Adt(adt, _) + if ty_adt.did() == adt.did()) + }); + if !has_copy_impl { + return; + } + } else { + return; + } + } + // Derive constrains all generic types to requiring Clone. Check if any type is not constrained for + // this impl. + if ty_subs.types().any(|ty| !implements_trait(cx, ty, clone_id, &[])) { + return; + } + // `#[repr(packed)]` structs with type/const parameters can't derive `Clone`. + // https://github.com/rust-lang/rust-clippy/issues/10188 + if ty_adt.repr().packed() + && ty_subs + .iter() + .any(|arg| matches!(arg.kind(), GenericArgKind::Type(_) | GenericArgKind::Const(_))) + { + return; + } + // The presence of `unsafe` fields prevents deriving `Clone` automatically + if ty_adt.all_fields().any(|f| f.safety.is_unsafe()) { + return; + } + + span_lint_and_note( + cx, + EXPL_IMPL_CLONE_ON_COPY, + item.span, + "you are implementing `Clone` explicitly on a `Copy` type", + Some(item.span), + "consider deriving `Clone` or removing `Copy`", + ); +} diff --git a/clippy_lints/src/derive/mod.rs b/clippy_lints/src/derive/mod.rs index c53a957f6a8b..1d63394ce37d 100644 --- a/clippy_lints/src/derive/mod.rs +++ b/clippy_lints/src/derive/mod.rs @@ -1,20 +1,12 @@ -use std::ops::ControlFlow; - -use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then, span_lint_hir_and_then}; -use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy}; -use clippy_utils::{has_non_exhaustive_attr, is_lint_allowed, paths}; -use rustc_errors::Applicability; -use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn, walk_item}; -use rustc_hir::{self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Impl, Item, ItemKind, UnsafeSource}; +use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::hir::nested_filter; -use rustc_middle::ty::{ - self, ClauseKind, GenericArgKind, GenericParamDefKind, ParamEnv, TraitPredicate, Ty, TyCtxt, Upcast, -}; use rustc_session::declare_lint_pass; -use rustc_span::def_id::LocalDefId; -use rustc_span::{Span, sym}; + +mod derive_ord_xor_partial_ord; +mod derive_partial_eq_without_eq; +mod derived_hash_with_manual_eq; +mod expl_impl_clone_on_copy; +mod unsafe_derive_deserialize; declare_clippy_lint! { /// ### What it does @@ -209,319 +201,15 @@ impl<'tcx> LateLintPass<'tcx> for Derive { let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); let is_automatically_derived = cx.tcx.is_automatically_derived(item.owner_id.to_def_id()); - check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived); - check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived); + derived_hash_with_manual_eq::check(cx, item.span, trait_ref, ty, is_automatically_derived); + derive_ord_xor_partial_ord::check(cx, item.span, trait_ref, ty, is_automatically_derived); if is_automatically_derived { - check_unsafe_derive_deserialize(cx, item, trait_ref, ty); - check_partial_eq_without_eq(cx, item.span, trait_ref, ty); + unsafe_derive_deserialize::check(cx, item, trait_ref, ty); + derive_partial_eq_without_eq::check(cx, item.span, trait_ref, ty); } else { - check_copy_clone(cx, item, trait_ref, ty); + expl_impl_clone_on_copy::check(cx, item, trait_ref, ty); } } } } - -/// Implementation of the `DERIVED_HASH_WITH_MANUAL_EQ` lint. -fn check_hash_peq<'tcx>( - cx: &LateContext<'tcx>, - span: Span, - trait_ref: &hir::TraitRef<'_>, - ty: Ty<'tcx>, - hash_is_automatically_derived: bool, -) { - if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait() - && let Some(def_id) = trait_ref.trait_def_id() - && cx.tcx.is_diagnostic_item(sym::Hash, def_id) - { - // Look for the PartialEq implementations for `ty` - cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| { - let peq_is_automatically_derived = cx.tcx.is_automatically_derived(impl_id); - - if !hash_is_automatically_derived || peq_is_automatically_derived { - return; - } - - let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); - - // Only care about `impl PartialEq for Foo` - // For `impl PartialEq for A, input_types is [A, B] - if trait_ref.instantiate_identity().args.type_at(1) == ty { - span_lint_and_then( - cx, - DERIVED_HASH_WITH_MANUAL_EQ, - span, - "you are deriving `Hash` but have implemented `PartialEq` explicitly", - |diag| { - if let Some(local_def_id) = impl_id.as_local() { - let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); - diag.span_note(cx.tcx.hir_span(hir_id), "`PartialEq` implemented here"); - } - }, - ); - } - }); - } -} - -/// Implementation of the `DERIVE_ORD_XOR_PARTIAL_ORD` lint. -fn check_ord_partial_ord<'tcx>( - cx: &LateContext<'tcx>, - span: Span, - trait_ref: &hir::TraitRef<'_>, - ty: Ty<'tcx>, - ord_is_automatically_derived: bool, -) { - if let Some(ord_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Ord) - && let Some(partial_ord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait() - && let Some(def_id) = &trait_ref.trait_def_id() - && *def_id == ord_trait_def_id - { - // Look for the PartialOrd implementations for `ty` - cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| { - let partial_ord_is_automatically_derived = cx.tcx.is_automatically_derived(impl_id); - - if partial_ord_is_automatically_derived == ord_is_automatically_derived { - return; - } - - let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation"); - - // Only care about `impl PartialOrd for Foo` - // For `impl PartialOrd for A, input_types is [A, B] - if trait_ref.instantiate_identity().args.type_at(1) == ty { - let mess = if partial_ord_is_automatically_derived { - "you are implementing `Ord` explicitly but have derived `PartialOrd`" - } else { - "you are deriving `Ord` but have implemented `PartialOrd` explicitly" - }; - - span_lint_and_then(cx, DERIVE_ORD_XOR_PARTIAL_ORD, span, mess, |diag| { - if let Some(local_def_id) = impl_id.as_local() { - let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id); - diag.span_note(cx.tcx.hir_span(hir_id), "`PartialOrd` implemented here"); - } - }); - } - }); - } -} - -/// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint. -fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { - let clone_id = match cx.tcx.lang_items().clone_trait() { - Some(id) if trait_ref.trait_def_id() == Some(id) => id, - _ => return, - }; - let Some(copy_id) = cx.tcx.lang_items().copy_trait() else { - return; - }; - let (ty_adt, ty_subs) = match *ty.kind() { - // Unions can't derive clone. - ty::Adt(adt, subs) if !adt.is_union() => (adt, subs), - _ => return, - }; - // If the current self type doesn't implement Copy (due to generic constraints), search to see if - // there's a Copy impl for any instance of the adt. - if !is_copy(cx, ty) { - if ty_subs.non_erasable_generics().next().is_some() { - let has_copy_impl = cx.tcx.local_trait_impls(copy_id).iter().any(|&id| { - matches!(cx.tcx.type_of(id).instantiate_identity().kind(), ty::Adt(adt, _) - if ty_adt.did() == adt.did()) - }); - if !has_copy_impl { - return; - } - } else { - return; - } - } - // Derive constrains all generic types to requiring Clone. Check if any type is not constrained for - // this impl. - if ty_subs.types().any(|ty| !implements_trait(cx, ty, clone_id, &[])) { - return; - } - // `#[repr(packed)]` structs with type/const parameters can't derive `Clone`. - // https://github.com/rust-lang/rust-clippy/issues/10188 - if ty_adt.repr().packed() - && ty_subs - .iter() - .any(|arg| matches!(arg.kind(), GenericArgKind::Type(_) | GenericArgKind::Const(_))) - { - return; - } - // The presence of `unsafe` fields prevents deriving `Clone` automatically - if ty_adt.all_fields().any(|f| f.safety.is_unsafe()) { - return; - } - - span_lint_and_note( - cx, - EXPL_IMPL_CLONE_ON_COPY, - item.span, - "you are implementing `Clone` explicitly on a `Copy` type", - Some(item.span), - "consider deriving `Clone` or removing `Copy`", - ); -} - -/// Implementation of the `UNSAFE_DERIVE_DESERIALIZE` lint. -fn check_unsafe_derive_deserialize<'tcx>( - cx: &LateContext<'tcx>, - item: &Item<'_>, - trait_ref: &hir::TraitRef<'_>, - ty: Ty<'tcx>, -) { - fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool { - let mut visitor = UnsafeVisitor { cx }; - walk_item(&mut visitor, item).is_break() - } - - if let Some(trait_def_id) = trait_ref.trait_def_id() - && paths::SERDE_DESERIALIZE.matches(cx, trait_def_id) - && let ty::Adt(def, _) = ty.kind() - && let Some(local_def_id) = def.did().as_local() - && let adt_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id) - && !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id) - && cx - .tcx - .inherent_impls(def.did()) - .iter() - .map(|imp_did| cx.tcx.hir_expect_item(imp_did.expect_local())) - .any(|imp| has_unsafe(cx, imp)) - { - span_lint_hir_and_then( - cx, - UNSAFE_DERIVE_DESERIALIZE, - adt_hir_id, - item.span, - "you are deriving `serde::Deserialize` on a type that has methods using `unsafe`", - |diag| { - diag.help( - "consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html", - ); - }, - ); - } -} - -struct UnsafeVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, -} - -impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> { - type Result = ControlFlow<()>; - type NestedFilter = nested_filter::All; - - fn visit_fn( - &mut self, - kind: FnKind<'tcx>, - decl: &'tcx FnDecl<'_>, - body_id: BodyId, - _: Span, - id: LocalDefId, - ) -> Self::Result { - if let Some(header) = kind.header() - && header.is_unsafe() - { - ControlFlow::Break(()) - } else { - walk_fn(self, kind, decl, body_id, id) - } - } - - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) -> Self::Result { - if let ExprKind::Block(block, _) = expr.kind - && block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) - && block - .span - .source_callee() - .and_then(|expr| expr.macro_def_id) - .is_none_or(|did| !self.cx.tcx.is_diagnostic_item(sym::pin_macro, did)) - { - return ControlFlow::Break(()); - } - - walk_expr(self, expr) - } - - fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { - self.cx.tcx - } -} - -/// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint. -fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { - if let ty::Adt(adt, args) = ty.kind() - && cx.tcx.visibility(adt.did()).is_public() - && let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq) - && let Some(def_id) = trait_ref.trait_def_id() - && cx.tcx.is_diagnostic_item(sym::PartialEq, def_id) - && !has_non_exhaustive_attr(cx.tcx, *adt) - && !ty_implements_eq_trait(cx.tcx, ty, eq_trait_def_id) - && let typing_env = typing_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id) - && let Some(local_def_id) = adt.did().as_local() - // If all of our fields implement `Eq`, we can implement `Eq` too - && adt - .all_fields() - .map(|f| f.ty(cx.tcx, args)) - .all(|ty| implements_trait_with_env(cx.tcx, typing_env, ty, eq_trait_def_id, None, &[])) - { - span_lint_hir_and_then( - cx, - DERIVE_PARTIAL_EQ_WITHOUT_EQ, - cx.tcx.local_def_id_to_hir_id(local_def_id), - span.ctxt().outer_expn_data().call_site, - "you are deriving `PartialEq` and can implement `Eq`", - |diag| { - diag.span_suggestion( - span.ctxt().outer_expn_data().call_site, - "consider deriving `Eq` as well", - "PartialEq, Eq", - Applicability::MachineApplicable, - ); - }, - ); - } -} - -fn ty_implements_eq_trait<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, eq_trait_id: DefId) -> bool { - tcx.non_blanket_impls_for_ty(eq_trait_id, ty).next().is_some() -} - -/// Creates the `ParamEnv` used for the given type's derived `Eq` impl. -fn typing_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> ty::TypingEnv<'_> { - // Initial map from generic index to param def. - // Vec<(param_def, needs_eq)> - let mut params = tcx - .generics_of(did) - .own_params - .iter() - .map(|p| (p, matches!(p.kind, GenericParamDefKind::Type { .. }))) - .collect::>(); - - let ty_predicates = tcx.predicates_of(did).predicates; - for (p, _) in ty_predicates { - if let ClauseKind::Trait(p) = p.kind().skip_binder() - && p.trait_ref.def_id == eq_trait_id - && let ty::Param(self_ty) = p.trait_ref.self_ty().kind() - { - // Flag types which already have an `Eq` bound. - params[self_ty.index as usize].1 = false; - } - } - - let param_env = ParamEnv::new(tcx.mk_clauses_from_iter(ty_predicates.iter().map(|&(p, _)| p).chain( - params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| { - ClauseKind::Trait(TraitPredicate { - trait_ref: ty::TraitRef::new(tcx, eq_trait_id, [tcx.mk_param_from_def(param)]), - polarity: ty::PredicatePolarity::Positive, - }) - .upcast(tcx) - }), - ))); - ty::TypingEnv { - typing_mode: ty::TypingMode::non_body_analysis(), - param_env, - } -} diff --git a/clippy_lints/src/derive/unsafe_derive_deserialize.rs b/clippy_lints/src/derive/unsafe_derive_deserialize.rs new file mode 100644 index 000000000000..c391e7b62289 --- /dev/null +++ b/clippy_lints/src/derive/unsafe_derive_deserialize.rs @@ -0,0 +1,93 @@ +use std::ops::ControlFlow; + +use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::{is_lint_allowed, paths}; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn, walk_item}; +use rustc_hir::{self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Item, UnsafeSource}; +use rustc_lint::LateContext; +use rustc_middle::hir::nested_filter; +use rustc_middle::ty::{self, Ty}; +use rustc_span::{Span, sym}; + +use super::UNSAFE_DERIVE_DESERIALIZE; + +/// Implementation of the `UNSAFE_DERIVE_DESERIALIZE` lint. +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { + fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool { + let mut visitor = UnsafeVisitor { cx }; + walk_item(&mut visitor, item).is_break() + } + + if let Some(trait_def_id) = trait_ref.trait_def_id() + && paths::SERDE_DESERIALIZE.matches(cx, trait_def_id) + && let ty::Adt(def, _) = ty.kind() + && let Some(local_def_id) = def.did().as_local() + && let adt_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id) + && !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id) + && cx + .tcx + .inherent_impls(def.did()) + .iter() + .map(|imp_did| cx.tcx.hir_expect_item(imp_did.expect_local())) + .any(|imp| has_unsafe(cx, imp)) + { + span_lint_hir_and_then( + cx, + UNSAFE_DERIVE_DESERIALIZE, + adt_hir_id, + item.span, + "you are deriving `serde::Deserialize` on a type that has methods using `unsafe`", + |diag| { + diag.help( + "consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html", + ); + }, + ); + } +} + +struct UnsafeVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, +} + +impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> { + type Result = ControlFlow<()>; + type NestedFilter = nested_filter::All; + + fn visit_fn( + &mut self, + kind: FnKind<'tcx>, + decl: &'tcx FnDecl<'_>, + body_id: BodyId, + _: Span, + id: LocalDefId, + ) -> Self::Result { + if let Some(header) = kind.header() + && header.is_unsafe() + { + ControlFlow::Break(()) + } else { + walk_fn(self, kind, decl, body_id, id) + } + } + + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) -> Self::Result { + if let ExprKind::Block(block, _) = expr.kind + && block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) + && block + .span + .source_callee() + .and_then(|expr| expr.macro_def_id) + .is_none_or(|did| !self.cx.tcx.is_diagnostic_item(sym::pin_macro, did)) + { + return ControlFlow::Break(()); + } + + walk_expr(self, expr) + } + + fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { + self.cx.tcx + } +} From 93101b5783b522469320b60926918f308ddae6b6 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 6 Sep 2025 14:32:36 +0200 Subject: [PATCH 206/682] s390x: use the new `u128::funnel_shl` --- library/stdarch/crates/core_arch/src/lib.rs | 3 ++- .../crates/core_arch/src/s390x/vector.rs | 17 +++-------------- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/lib.rs b/library/stdarch/crates/core_arch/src/lib.rs index 7d3cbd55ad85..26a9cb589918 100644 --- a/library/stdarch/crates/core_arch/src/lib.rs +++ b/library/stdarch/crates/core_arch/src/lib.rs @@ -33,7 +33,8 @@ x86_amx_intrinsics, f16, aarch64_unstable_target_feature, - bigint_helper_methods + bigint_helper_methods, + funnel_shifts )] #![cfg_attr(test, feature(test, abi_vectorcall, stdarch_internal))] #![deny(clippy::missing_inline_in_public_items)] diff --git a/library/stdarch/crates/core_arch/src/s390x/vector.rs b/library/stdarch/crates/core_arch/src/s390x/vector.rs index f6d14c45e0d2..f018344ead12 100644 --- a/library/stdarch/crates/core_arch/src/s390x/vector.rs +++ b/library/stdarch/crates/core_arch/src/s390x/vector.rs @@ -3513,17 +3513,6 @@ mod sealed { a.vec_srdb::<7>(b) } - unsafe fn funnel_shl_u128(a: u128, b: u128, c: u128) -> u128 { - #[repr(simd)] - struct Single([u128; 1]); - - transmute(simd_funnel_shl::( - transmute(a), - transmute(b), - transmute(c), - )) - } - macro_rules! impl_vec_sld { ($($ty:ident)*) => { $( @@ -3533,21 +3522,21 @@ mod sealed { #[target_feature(enable = "vector")] unsafe fn vec_sld(self, b: Self) -> Self { static_assert_uimm_bits!(C, 4); - transmute(funnel_shl_u128(transmute(self), transmute(b), const { C as u128 * 8 })) + transmute(u128::funnel_shl(transmute(self), transmute(b), C * 8)) } #[inline] #[target_feature(enable = "vector")] unsafe fn vec_sldw(self, b: Self) -> Self { static_assert_uimm_bits!(C, 2); - transmute(funnel_shl_u128(transmute(self), transmute(b), const { C as u128 * 4 * 8 })) + transmute(u128::funnel_shl(transmute(self), transmute(b), C * 4 * 8)) } #[inline] #[target_feature(enable = "vector-enhancements-2")] unsafe fn vec_sldb(self, b: Self) -> Self { static_assert_uimm_bits!(C, 3); - transmute(funnel_shl_u128(transmute(self), transmute(b), const { C as u128 })) + transmute(u128::funnel_shl(transmute(self), transmute(b), C)) } } From 24c34d738f00f0f38b88cead5ab869d156513609 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 25 Aug 2025 17:20:37 +0200 Subject: [PATCH 207/682] extend the let-chain removes one level of nesting --- clippy_lints/src/future_not_send.rs | 109 ++++++++++++++-------------- 1 file changed, 54 insertions(+), 55 deletions(-) diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index 3ccfa51ab70b..2788b89f1439 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -78,66 +78,65 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { if let ty::Alias(ty::Opaque, AliasTy { def_id, args, .. }) = *ret_ty.kind() && let Some(future_trait) = cx.tcx.lang_items().future_trait() && let Some(send_trait) = cx.tcx.get_diagnostic_item(sym::Send) - { - let preds = cx.tcx.explicit_item_self_bounds(def_id); - let is_future = preds.iter_instantiated_copied(cx.tcx, args).any(|(p, _)| { + && let preds = cx.tcx.explicit_item_self_bounds(def_id) + // If is a Future + && preds.iter_instantiated_copied(cx.tcx, args).any(|(p, _)| { p.as_trait_clause() .is_some_and(|trait_pred| trait_pred.skip_binder().trait_ref.def_id == future_trait) + }) + { + let span = decl.output.span(); + let infcx = cx.tcx.infer_ctxt().build(cx.typing_mode()); + let ocx = ObligationCtxt::new_with_diagnostics(&infcx); + let cause = traits::ObligationCause::misc(span, fn_def_id); + ocx.register_bound(cause, cx.param_env, ret_ty, send_trait); + let send_errors = ocx.select_all_or_error(); + + // Allow errors that try to prove `Send` for types that "mention" a generic parameter at the "top + // level". + // For example, allow errors that `T: Send` can't be proven, but reject `Rc: Send` errors, + // which is always unconditionally `!Send` for any possible type `T`. + // + // We also allow associated type projections if the self type is either itself a projection or a + // type parameter. + // This is to prevent emitting warnings for e.g. holding a `::Output` across await + // points, where `Fut` is a type parameter. + + let is_send = send_errors.iter().all(|err| { + err.obligation + .predicate + .as_trait_clause() + .map(Binder::skip_binder) + .is_some_and(|pred| { + pred.def_id() == send_trait + && pred.self_ty().has_param() + && TyParamAtTopLevelVisitor.visit_ty(pred.self_ty()) == ControlFlow::Break(true) + }) }); - if is_future { - let span = decl.output.span(); - let infcx = cx.tcx.infer_ctxt().build(cx.typing_mode()); - let ocx = ObligationCtxt::new_with_diagnostics(&infcx); - let cause = traits::ObligationCause::misc(span, fn_def_id); - ocx.register_bound(cause, cx.param_env, ret_ty, send_trait); - let send_errors = ocx.select_all_or_error(); - // Allow errors that try to prove `Send` for types that "mention" a generic parameter at the "top - // level". - // For example, allow errors that `T: Send` can't be proven, but reject `Rc: Send` errors, - // which is always unconditionally `!Send` for any possible type `T`. - // - // We also allow associated type projections if the self type is either itself a projection or a - // type parameter. - // This is to prevent emitting warnings for e.g. holding a `::Output` across await - // points, where `Fut` is a type parameter. - - let is_send = send_errors.iter().all(|err| { - err.obligation - .predicate - .as_trait_clause() - .map(Binder::skip_binder) - .is_some_and(|pred| { - pred.def_id() == send_trait - && pred.self_ty().has_param() - && TyParamAtTopLevelVisitor.visit_ty(pred.self_ty()) == ControlFlow::Break(true) - }) - }); - - if !is_send { - span_lint_and_then( - cx, - FUTURE_NOT_SEND, - span, - "future cannot be sent between threads safely", - |db| { - for FulfillmentError { obligation, .. } in send_errors { - infcx - .err_ctxt() - .maybe_note_obligation_cause_for_async_await(db, &obligation); - if let PredicateKind::Clause(ClauseKind::Trait(trait_pred)) = - obligation.predicate.kind().skip_binder() - { - db.note(format!( - "`{}` doesn't implement `{}`", - trait_pred.self_ty(), - trait_pred.trait_ref.print_only_trait_path(), - )); - } + if !is_send { + span_lint_and_then( + cx, + FUTURE_NOT_SEND, + span, + "future cannot be sent between threads safely", + |db| { + for FulfillmentError { obligation, .. } in send_errors { + infcx + .err_ctxt() + .maybe_note_obligation_cause_for_async_await(db, &obligation); + if let PredicateKind::Clause(ClauseKind::Trait(trait_pred)) = + obligation.predicate.kind().skip_binder() + { + db.note(format!( + "`{}` doesn't implement `{}`", + trait_pred.self_ty(), + trait_pred.trait_ref.print_only_trait_path(), + )); } - }, - ); - } + } + }, + ); } } } From fe86ce6bcc07ff199c804a6a2e0a66c8557c5364 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 7 Sep 2025 00:13:40 +0200 Subject: [PATCH 208/682] split `any` in two parts --- clippy_lints/src/future_not_send.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index 2788b89f1439..596047977a9b 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -80,10 +80,10 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { && let Some(send_trait) = cx.tcx.get_diagnostic_item(sym::Send) && let preds = cx.tcx.explicit_item_self_bounds(def_id) // If is a Future - && preds.iter_instantiated_copied(cx.tcx, args).any(|(p, _)| { - p.as_trait_clause() - .is_some_and(|trait_pred| trait_pred.skip_binder().trait_ref.def_id == future_trait) - }) + && preds + .iter_instantiated_copied(cx.tcx, args) + .filter_map(|(p, _)| p.as_trait_clause()) + .any(|trait_pred| trait_pred.skip_binder().trait_ref.def_id == future_trait) { let span = decl.output.span(); let infcx = cx.tcx.infer_ctxt().build(cx.typing_mode()); From 589515bc8a1d4939c4a738e9bc895dcfb217d2d8 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 7 Sep 2025 12:37:15 +0200 Subject: [PATCH 209/682] update Cargo.lock --- library/stdarch/Cargo.lock | 217 ++++++++++++++++++++++++++----------- 1 file changed, 152 insertions(+), 65 deletions(-) diff --git a/library/stdarch/Cargo.lock b/library/stdarch/Cargo.lock index 9df0791b8652..a10a456acce1 100644 --- a/library/stdarch/Cargo.lock +++ b/library/stdarch/Cargo.lock @@ -13,9 +13,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.19" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" dependencies = [ "anstyle", "anstyle-parse", @@ -43,29 +43,29 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys", + "windows-sys 0.60.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.9" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys", + "windows-sys 0.60.2", ] [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" [[package]] name = "assert-instr-macro" @@ -84,30 +84,31 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "bitflags" -version = "2.9.1" +version = "2.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" [[package]] name = "cc" -version = "1.2.31" +version = "1.2.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2" +checksum = "5252b3d2648e5eedbc1a6f501e3c795e07025c1e93bbf8bbdd6eef7f447a6d54" dependencies = [ + "find-msvc-tools", "shlex", ] [[package]] name = "cfg-if" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "clap" -version = "4.5.42" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed87a9d530bb41a67537289bafcac159cb3ee28460e0a4571123d2a778a6a882" +checksum = "7eac00902d9d136acd712710d71823fb8ac8004ca445a89e73a41d45aa712931" dependencies = [ "clap_builder", "clap_derive", @@ -115,9 +116,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.42" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64f4f3f3c77c94aff3c7e9aac9a2ca1974a5adf392a8bb751e827d6d127ab966" +checksum = "2ad9bbf750e73b5884fb8a211a9424a1906c1e156724260fdae972f31d70e1d6" dependencies = [ "anstream", "anstyle", @@ -127,9 +128,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.41" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" +checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" dependencies = [ "heck", "proc-macro2", @@ -258,6 +259,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "find-msvc-tools" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d" + [[package]] name = "fnv" version = "1.0.7" @@ -283,9 +290,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.15.4" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" [[package]] name = "heck" @@ -323,12 +330,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9" dependencies = [ "equivalent", - "hashbrown 0.15.4", + "hashbrown 0.15.5", ] [[package]] @@ -353,7 +360,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ "hermit-abi", "libc", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -379,9 +386,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "libc" -version = "0.2.174" +version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "linked-hash-map" @@ -391,9 +398,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "log" -version = "0.4.27" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "memchr" @@ -428,9 +435,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] @@ -497,9 +504,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" dependencies = [ "either", "rayon-core", @@ -507,9 +514,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -517,9 +524,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.1" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" dependencies = [ "aho-corasick", "memchr", @@ -529,9 +536,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" dependencies = [ "aho-corasick", "memchr", @@ -540,9 +547,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" [[package]] name = "rustc-demangle" @@ -593,9 +600,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.142" +version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" +checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" dependencies = [ "itoa", "memchr", @@ -715,9 +722,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.104" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", @@ -774,7 +781,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "161296c618fa2d63f6ed5fffd1112937e803cb9ec71b32b01a76321555660917" dependencies = [ "bitflags", - "indexmap 2.10.0", + "indexmap 2.11.0", "semver", ] @@ -791,20 +798,35 @@ dependencies = [ [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22" dependencies = [ - "windows-sys", + "windows-sys 0.60.2", ] +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.3", ] [[package]] @@ -813,14 +835,31 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", ] [[package]] @@ -829,48 +868,96 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "yaml-rust" version = "0.4.5" @@ -882,18 +969,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.26" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.26" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", From d70ef4f0a7468fe7fb2dfabf89d8ccbb956a45d9 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 7 Sep 2025 12:38:02 +0200 Subject: [PATCH 210/682] move `compare_outputs` implementation into `SupportedArchitectureTest` definition --- .../crates/intrinsic-test/src/arm/mod.rs | 35 +++++++------------ .../crates/intrinsic-test/src/common/mod.rs | 31 +++++++++++++--- .../stdarch/crates/intrinsic-test/src/main.rs | 21 ++++------- 3 files changed, 47 insertions(+), 40 deletions(-) diff --git a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs index 51f5ac428378..d8f7ae9b3061 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs @@ -10,7 +10,6 @@ use std::fs::{self, File}; use rayon::prelude::*; use crate::common::cli::ProcessedCli; -use crate::common::compare::compare_outputs; use crate::common::gen_c::{write_main_cpp, write_mod_cpp}; use crate::common::gen_rust::{ compile_rust_programs, write_bin_cargo_toml, write_lib_cargo_toml, write_lib_rs, write_main_rs, @@ -28,7 +27,17 @@ pub struct ArmArchitectureTest { } impl SupportedArchitectureTest for ArmArchitectureTest { - fn create(cli_options: ProcessedCli) -> Box { + type IntrinsicImpl = ArmIntrinsicType; + + fn cli_options(&self) -> &ProcessedCli { + &self.cli_options + } + + fn intrinsics(&self) -> &[Intrinsic] { + &self.intrinsics + } + + fn create(cli_options: ProcessedCli) -> Self { let a32 = cli_options.target.contains("v7"); let mut intrinsics = get_neon_intrinsics(&cli_options.filename, &cli_options.target) .expect("Error parsing input file"); @@ -50,10 +59,10 @@ impl SupportedArchitectureTest for ArmArchitectureTest { .collect::>(); intrinsics.dedup(); - Box::new(Self { + Self { intrinsics, cli_options, - }) + } } fn build_c_file(&self) -> bool { @@ -177,22 +186,4 @@ impl SupportedArchitectureTest for ArmArchitectureTest { compile_rust_programs(toolchain, target, linker) } - - fn compare_outputs(&self) -> bool { - if self.cli_options.toolchain.is_some() { - let intrinsics_name_list = self - .intrinsics - .iter() - .map(|i| i.name.clone()) - .collect::>(); - - compare_outputs( - &intrinsics_name_list, - &self.cli_options.runner, - &self.cli_options.target, - ) - } else { - true - } - } } diff --git a/library/stdarch/crates/intrinsic-test/src/common/mod.rs b/library/stdarch/crates/intrinsic-test/src/common/mod.rs index 5a57c8027db9..f1def1e9c0eb 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/mod.rs @@ -1,5 +1,7 @@ use cli::ProcessedCli; +use crate::common::{intrinsic::Intrinsic, intrinsic_helpers::IntrinsicTypeDefinition}; + pub mod argument; pub mod cli; pub mod compare; @@ -15,12 +17,33 @@ pub mod values; /// Architectures must support this trait /// to be successfully tested. pub trait SupportedArchitectureTest { - fn create(cli_options: ProcessedCli) -> Box - where - Self: Sized; + type IntrinsicImpl: IntrinsicTypeDefinition; + + fn cli_options(&self) -> &ProcessedCli; + fn intrinsics(&self) -> &[Intrinsic]; + + fn create(cli_options: ProcessedCli) -> Self; + fn build_c_file(&self) -> bool; fn build_rust_file(&self) -> bool; - fn compare_outputs(&self) -> bool; + + fn compare_outputs(&self) -> bool { + if self.cli_options().toolchain.is_some() { + let intrinsics_name_list = self + .intrinsics() + .iter() + .map(|i| i.name.clone()) + .collect::>(); + + compare::compare_outputs( + &intrinsics_name_list, + &self.cli_options().runner, + &self.cli_options().target, + ) + } else { + true + } + } } pub fn chunk_info(intrinsic_count: usize) -> (usize, usize) { diff --git a/library/stdarch/crates/intrinsic-test/src/main.rs b/library/stdarch/crates/intrinsic-test/src/main.rs index 538f317a2978..44d7aafd827f 100644 --- a/library/stdarch/crates/intrinsic-test/src/main.rs +++ b/library/stdarch/crates/intrinsic-test/src/main.rs @@ -13,23 +13,16 @@ fn main() { let args: Cli = clap::Parser::parse(); let processed_cli_options = ProcessedCli::new(args); - let test_environment_result: Option> = - match processed_cli_options.target.as_str() { - "aarch64-unknown-linux-gnu" - | "armv7-unknown-linux-gnueabihf" - | "aarch64_be-unknown-linux-gnu" => { - Some(ArmArchitectureTest::create(processed_cli_options)) - } + match processed_cli_options.target.as_str() { + "aarch64-unknown-linux-gnu" + | "armv7-unknown-linux-gnueabihf" + | "aarch64_be-unknown-linux-gnu" => run(ArmArchitectureTest::create(processed_cli_options)), - _ => None, - }; - - if test_environment_result.is_none() { - std::process::exit(0); + _ => std::process::exit(0), } +} - let test_environment = test_environment_result.unwrap(); - +fn run(test_environment: impl SupportedArchitectureTest) { info!("building C binaries"); if !test_environment.build_c_file() { std::process::exit(2); From 6ab097b2453c5247f8d78c7ed5dfd4184b9a1c1d Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 7 Sep 2025 12:42:48 +0200 Subject: [PATCH 211/682] move platform headers into `SupportedArchitectureTest` --- library/stdarch/crates/intrinsic-test/src/arm/mod.rs | 5 +++-- library/stdarch/crates/intrinsic-test/src/common/mod.rs | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs index d8f7ae9b3061..60e1ebe5f33b 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs @@ -65,9 +65,10 @@ impl SupportedArchitectureTest for ArmArchitectureTest { } } + const PLATFORM_HEADERS: &[&str] = &["arm_neon.h", "arm_acle.h", "arm_fp16.h"]; + fn build_c_file(&self) -> bool { let c_target = "aarch64"; - let platform_headers = &["arm_neon.h", "arm_acle.h", "arm_fp16.h"]; let (chunk_size, chunk_count) = chunk_info(self.intrinsics.len()); @@ -81,7 +82,7 @@ impl SupportedArchitectureTest for ArmArchitectureTest { .map(|(i, chunk)| { let c_filename = format!("c_programs/mod_{i}.cpp"); let mut file = File::create(&c_filename).unwrap(); - write_mod_cpp(&mut file, notice, c_target, platform_headers, chunk).unwrap(); + write_mod_cpp(&mut file, notice, c_target, Self::PLATFORM_HEADERS, chunk).unwrap(); // compile this cpp file into a .o file. // diff --git a/library/stdarch/crates/intrinsic-test/src/common/mod.rs b/library/stdarch/crates/intrinsic-test/src/common/mod.rs index f1def1e9c0eb..be08aaecaba1 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/mod.rs @@ -24,6 +24,8 @@ pub trait SupportedArchitectureTest { fn create(cli_options: ProcessedCli) -> Self; + const PLATFORM_HEADERS: &[&str]; + fn build_c_file(&self) -> bool; fn build_rust_file(&self) -> bool; From 916424f38d1b3d763cf19c0392799d7208482573 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 7 Sep 2025 13:16:40 +0200 Subject: [PATCH 212/682] move more constants into `SupportedArchitectureTest` --- .../crates/intrinsic-test/src/arm/config.rs | 13 ++--- .../crates/intrinsic-test/src/arm/mod.rs | 52 +++++++++++++------ .../crates/intrinsic-test/src/common/mod.rs | 16 ++++-- 3 files changed, 53 insertions(+), 28 deletions(-) diff --git a/library/stdarch/crates/intrinsic-test/src/arm/config.rs b/library/stdarch/crates/intrinsic-test/src/arm/config.rs index 9a7b37253d1c..beaca621378d 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/config.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/config.rs @@ -1,12 +1,7 @@ -pub fn build_notices(line_prefix: &str) -> String { - format!( - "\ -{line_prefix}This is a transient test file, not intended for distribution. Some aspects of the -{line_prefix}test are derived from a JSON specification, published under the same license as the -{line_prefix}`intrinsic-test` crate.\n -" - ) -} +pub const NOTICE: &str = "\ +// This is a transient test file, not intended for distribution. Some aspects of the +// test are derived from a JSON specification, published under the same license as the +// `intrinsic-test` crate.\n"; pub const POLY128_OSTREAM_DEF: &str = r#"std::ostream& operator<<(std::ostream& os, poly128_t value) { std::stringstream temp; diff --git a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs index 60e1ebe5f33b..82dc5001adf0 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs @@ -5,11 +5,12 @@ mod intrinsic; mod json_parser; mod types; -use std::fs::{self, File}; +use std::fs::File; use rayon::prelude::*; use crate::common::cli::ProcessedCli; +use crate::common::compile_c::CppCompilation; use crate::common::gen_c::{write_main_cpp, write_mod_cpp}; use crate::common::gen_rust::{ compile_rust_programs, write_bin_cargo_toml, write_lib_cargo_toml, write_lib_rs, write_main_rs, @@ -17,7 +18,6 @@ use crate::common::gen_rust::{ use crate::common::intrinsic::Intrinsic; use crate::common::intrinsic_helpers::TypeKind; use crate::common::{SupportedArchitectureTest, chunk_info}; -use config::{AARCH_CONFIGURATIONS, F16_FORMATTING_DEF, POLY128_OSTREAM_DEF, build_notices}; use intrinsic::ArmIntrinsicType; use json_parser::get_neon_intrinsics; @@ -65,24 +65,40 @@ impl SupportedArchitectureTest for ArmArchitectureTest { } } - const PLATFORM_HEADERS: &[&str] = &["arm_neon.h", "arm_acle.h", "arm_fp16.h"]; + const NOTICE: &str = config::NOTICE; + + const PLATFORM_C_HEADERS: &[&str] = &["arm_neon.h", "arm_acle.h", "arm_fp16.h"]; + const PLATFORM_C_DEFINITIONS: &str = config::POLY128_OSTREAM_DEF; + + const PLATFORM_RUST_DEFINITIONS: &str = config::F16_FORMATTING_DEF; + const PLATFORM_RUST_CFGS: &str = config::AARCH_CONFIGURATIONS; + + fn cpp_compilation(&self) -> Option { + compile::build_cpp_compilation(&self.cli_options) + } fn build_c_file(&self) -> bool { let c_target = "aarch64"; - let (chunk_size, chunk_count) = chunk_info(self.intrinsics.len()); + let (chunk_size, chunk_count) = chunk_info(self.intrinsics().len()); - let cpp_compiler_wrapped = compile::build_cpp_compilation(&self.cli_options); + let cpp_compiler_wrapped = self.cpp_compilation(); - let notice = &build_notices("// "); - fs::create_dir_all("c_programs").unwrap(); - self.intrinsics + std::fs::create_dir_all("c_programs").unwrap(); + self.intrinsics() .par_chunks(chunk_size) .enumerate() .map(|(i, chunk)| { let c_filename = format!("c_programs/mod_{i}.cpp"); let mut file = File::create(&c_filename).unwrap(); - write_mod_cpp(&mut file, notice, c_target, Self::PLATFORM_HEADERS, chunk).unwrap(); + write_mod_cpp( + &mut file, + Self::NOTICE, + c_target, + Self::PLATFORM_C_HEADERS, + chunk, + ) + .unwrap(); // compile this cpp file into a .o file. // @@ -103,8 +119,8 @@ impl SupportedArchitectureTest for ArmArchitectureTest { write_main_cpp( &mut file, c_target, - POLY128_OSTREAM_DEF, - self.intrinsics.iter().map(|i| i.name.as_str()), + Self::PLATFORM_C_DEFINITIONS, + self.intrinsics().iter().map(|i| i.name.as_str()), ) .unwrap(); @@ -149,7 +165,7 @@ impl SupportedArchitectureTest for ArmArchitectureTest { write_main_rs( &mut main_rs, chunk_count, - AARCH_CONFIGURATIONS, + Self::PLATFORM_RUST_CFGS, "", self.intrinsics.iter().map(|i| i.name.as_str()), ) @@ -159,7 +175,6 @@ impl SupportedArchitectureTest for ArmArchitectureTest { let toolchain = self.cli_options.toolchain.as_deref(); let linker = self.cli_options.linker.as_deref(); - let notice = &build_notices("// "); self.intrinsics .par_chunks(chunk_size) .enumerate() @@ -170,9 +185,14 @@ impl SupportedArchitectureTest for ArmArchitectureTest { trace!("generating `{rust_filename}`"); let mut file = File::create(rust_filename)?; - let cfg = AARCH_CONFIGURATIONS; - let definitions = F16_FORMATTING_DEF; - write_lib_rs(&mut file, architecture, notice, cfg, definitions, chunk)?; + write_lib_rs( + &mut file, + architecture, + Self::NOTICE, + Self::PLATFORM_RUST_CFGS, + Self::PLATFORM_RUST_DEFINITIONS, + chunk, + )?; let toml_filename = format!("rust_programs/mod_{i}/Cargo.toml"); trace!("generating `{toml_filename}`"); diff --git a/library/stdarch/crates/intrinsic-test/src/common/mod.rs b/library/stdarch/crates/intrinsic-test/src/common/mod.rs index be08aaecaba1..13e6c9fe9e28 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/mod.rs @@ -1,6 +1,8 @@ use cli::ProcessedCli; -use crate::common::{intrinsic::Intrinsic, intrinsic_helpers::IntrinsicTypeDefinition}; +use crate::common::{ + compile_c::CppCompilation, intrinsic::Intrinsic, intrinsic_helpers::IntrinsicTypeDefinition, +}; pub mod argument; pub mod cli; @@ -17,14 +19,22 @@ pub mod values; /// Architectures must support this trait /// to be successfully tested. pub trait SupportedArchitectureTest { - type IntrinsicImpl: IntrinsicTypeDefinition; + type IntrinsicImpl: IntrinsicTypeDefinition + Sync; fn cli_options(&self) -> &ProcessedCli; fn intrinsics(&self) -> &[Intrinsic]; fn create(cli_options: ProcessedCli) -> Self; - const PLATFORM_HEADERS: &[&str]; + const NOTICE: &str; + + const PLATFORM_C_HEADERS: &[&str]; + const PLATFORM_C_DEFINITIONS: &str; + + const PLATFORM_RUST_CFGS: &str; + const PLATFORM_RUST_DEFINITIONS: &str; + + fn cpp_compilation(&self) -> Option; fn build_c_file(&self) -> bool; fn build_rust_file(&self) -> bool; From 9d9ca01bfa541e9badd9f34c776b588cfbf0e3ce Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 7 Sep 2025 13:23:24 +0200 Subject: [PATCH 213/682] move `print_result_c` into the inner intrinsic type --- .../intrinsic-test/src/arm/intrinsic.rs | 52 +++++++++++-------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs b/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs index fd93eff76e09..7c31fd4d9dd0 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs @@ -41,18 +41,27 @@ impl IntrinsicDefinition for Intrinsic { /// rust debug output format for the return type. The generated line assumes /// there is an int i in scope which is the current pass number. fn print_result_c(&self, indentation: Indentation, additional: &str) -> String { - let lanes = if self.results().num_vectors() > 1 { - (0..self.results().num_vectors()) + self.results().print_result_c(indentation, additional) + } +} + +impl ArmIntrinsicType { + /// Generates a std::cout for the intrinsics results that will match the + /// rust debug output format for the return type. The generated line assumes + /// there is an int i in scope which is the current pass number. + fn print_result_c(&self, indentation: Indentation, additional: &str) -> String { + let lanes = if self.num_vectors() > 1 { + (0..self.num_vectors()) .map(|vector| { format!( r#""{ty}(" << {lanes} << ")""#, - ty = self.results().c_single_vector_type(), - lanes = (0..self.results().num_lanes()) + ty = self.c_single_vector_type(), + lanes = (0..self.num_lanes()) .map(move |idx| -> std::string::String { format!( "{cast}{lane_fn}(__return_value.val[{vector}], {lane})", - cast = self.results().c_promotion(), - lane_fn = self.results().get_lane_function(), + cast = self.c_promotion(), + lane_fn = self.get_lane_function(), lane = idx, vector = vector, ) @@ -63,13 +72,13 @@ impl IntrinsicDefinition for Intrinsic { }) .collect::>() .join(r#" << ", " << "#) - } else if self.results().num_lanes() > 1 { - (0..self.results().num_lanes()) + } else if self.num_lanes() > 1 { + (0..self.num_lanes()) .map(|idx| -> std::string::String { format!( "{cast}{lane_fn}(__return_value, {lane})", - cast = self.results().c_promotion(), - lane_fn = self.results().get_lane_function(), + cast = self.c_promotion(), + lane_fn = self.get_lane_function(), lane = idx ) }) @@ -78,28 +87,27 @@ impl IntrinsicDefinition for Intrinsic { } else { format!( "{promote}cast<{cast}>(__return_value)", - cast = match self.results.kind() { - TypeKind::Float if self.results().inner_size() == 16 => "float16_t".to_string(), - TypeKind::Float if self.results().inner_size() == 32 => "float".to_string(), - TypeKind::Float if self.results().inner_size() == 64 => "double".to_string(), - TypeKind::Int(Sign::Signed) => format!("int{}_t", self.results().inner_size()), - TypeKind::Int(Sign::Unsigned) => - format!("uint{}_t", self.results().inner_size()), - TypeKind::Poly => format!("poly{}_t", self.results().inner_size()), + cast = match self.kind() { + TypeKind::Float if self.inner_size() == 16 => "float16_t".to_string(), + TypeKind::Float if self.inner_size() == 32 => "float".to_string(), + TypeKind::Float if self.inner_size() == 64 => "double".to_string(), + TypeKind::Int(Sign::Signed) => format!("int{}_t", self.inner_size()), + TypeKind::Int(Sign::Unsigned) => format!("uint{}_t", self.inner_size()), + TypeKind::Poly => format!("poly{}_t", self.inner_size()), ty => todo!("print_result_c - Unknown type: {:#?}", ty), }, - promote = self.results().c_promotion(), + promote = self.c_promotion(), ) }; format!( r#"{indentation}std::cout << "Result {additional}-" << i+1 << ": {ty}" << std::fixed << std::setprecision(150) << {lanes} << "{close}" << std::endl;"#, - ty = if self.results().is_simd() { - format!("{}(", self.results().c_type()) + ty = if self.is_simd() { + format!("{}(", self.c_type()) } else { String::from("") }, - close = if self.results.is_simd() { ")" } else { "" }, + close = if self.is_simd() { ")" } else { "" }, ) } } From 2ba0a6e48974e3f358c18724ebf673787d72ceea Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 7 Sep 2025 13:26:50 +0200 Subject: [PATCH 214/682] move `print_result_c` into the trait --- .../intrinsic-test/src/arm/intrinsic.rs | 77 +------------------ .../crates/intrinsic-test/src/arm/types.rs | 66 ++++++++++++++++ .../crates/intrinsic-test/src/common/gen_c.rs | 4 +- .../intrinsic-test/src/common/intrinsic.rs | 6 -- .../src/common/intrinsic_helpers.rs | 5 ++ 5 files changed, 75 insertions(+), 83 deletions(-) diff --git a/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs b/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs index 7c31fd4d9dd0..1928a00f4f38 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs @@ -1,7 +1,6 @@ use crate::common::argument::ArgumentList; -use crate::common::indentation::Indentation; use crate::common::intrinsic::{Intrinsic, IntrinsicDefinition}; -use crate::common::intrinsic_helpers::{IntrinsicType, IntrinsicTypeDefinition, Sign, TypeKind}; +use crate::common::intrinsic_helpers::IntrinsicType; use std::ops::{Deref, DerefMut}; #[derive(Debug, Clone, PartialEq)] @@ -36,78 +35,4 @@ impl IntrinsicDefinition for Intrinsic { fn name(&self) -> String { self.name.clone() } - - /// Generates a std::cout for the intrinsics results that will match the - /// rust debug output format for the return type. The generated line assumes - /// there is an int i in scope which is the current pass number. - fn print_result_c(&self, indentation: Indentation, additional: &str) -> String { - self.results().print_result_c(indentation, additional) - } -} - -impl ArmIntrinsicType { - /// Generates a std::cout for the intrinsics results that will match the - /// rust debug output format for the return type. The generated line assumes - /// there is an int i in scope which is the current pass number. - fn print_result_c(&self, indentation: Indentation, additional: &str) -> String { - let lanes = if self.num_vectors() > 1 { - (0..self.num_vectors()) - .map(|vector| { - format!( - r#""{ty}(" << {lanes} << ")""#, - ty = self.c_single_vector_type(), - lanes = (0..self.num_lanes()) - .map(move |idx| -> std::string::String { - format!( - "{cast}{lane_fn}(__return_value.val[{vector}], {lane})", - cast = self.c_promotion(), - lane_fn = self.get_lane_function(), - lane = idx, - vector = vector, - ) - }) - .collect::>() - .join(r#" << ", " << "#) - ) - }) - .collect::>() - .join(r#" << ", " << "#) - } else if self.num_lanes() > 1 { - (0..self.num_lanes()) - .map(|idx| -> std::string::String { - format!( - "{cast}{lane_fn}(__return_value, {lane})", - cast = self.c_promotion(), - lane_fn = self.get_lane_function(), - lane = idx - ) - }) - .collect::>() - .join(r#" << ", " << "#) - } else { - format!( - "{promote}cast<{cast}>(__return_value)", - cast = match self.kind() { - TypeKind::Float if self.inner_size() == 16 => "float16_t".to_string(), - TypeKind::Float if self.inner_size() == 32 => "float".to_string(), - TypeKind::Float if self.inner_size() == 64 => "double".to_string(), - TypeKind::Int(Sign::Signed) => format!("int{}_t", self.inner_size()), - TypeKind::Int(Sign::Unsigned) => format!("uint{}_t", self.inner_size()), - TypeKind::Poly => format!("poly{}_t", self.inner_size()), - ty => todo!("print_result_c - Unknown type: {:#?}", ty), - }, - promote = self.c_promotion(), - ) - }; - - format!( - r#"{indentation}std::cout << "Result {additional}-" << i+1 << ": {ty}" << std::fixed << std::setprecision(150) << {lanes} << "{close}" << std::endl;"#, - ty = if self.is_simd() { - format!("{}(", self.c_type()) - } else { - String::from("") - }, - close = if self.is_simd() { ")" } else { "" }, - ) - } } diff --git a/library/stdarch/crates/intrinsic-test/src/arm/types.rs b/library/stdarch/crates/intrinsic-test/src/arm/types.rs index 32f8f106ce26..e86a2c5189f0 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/types.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/types.rs @@ -1,5 +1,6 @@ use super::intrinsic::ArmIntrinsicType; use crate::common::cli::Language; +use crate::common::indentation::Indentation; use crate::common::intrinsic_helpers::{IntrinsicType, IntrinsicTypeDefinition, Sign, TypeKind}; impl IntrinsicTypeDefinition for ArmIntrinsicType { @@ -98,6 +99,71 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType { todo!("get_lane_function IntrinsicType: {:#?}", self) } } + + /// Generates a std::cout for the intrinsics results that will match the + /// rust debug output format for the return type. The generated line assumes + /// there is an int i in scope which is the current pass number. + fn print_result_c(&self, indentation: Indentation, additional: &str) -> String { + let lanes = if self.num_vectors() > 1 { + (0..self.num_vectors()) + .map(|vector| { + format!( + r#""{ty}(" << {lanes} << ")""#, + ty = self.c_single_vector_type(), + lanes = (0..self.num_lanes()) + .map(move |idx| -> std::string::String { + format!( + "{cast}{lane_fn}(__return_value.val[{vector}], {lane})", + cast = self.c_promotion(), + lane_fn = self.get_lane_function(), + lane = idx, + vector = vector, + ) + }) + .collect::>() + .join(r#" << ", " << "#) + ) + }) + .collect::>() + .join(r#" << ", " << "#) + } else if self.num_lanes() > 1 { + (0..self.num_lanes()) + .map(|idx| -> std::string::String { + format!( + "{cast}{lane_fn}(__return_value, {lane})", + cast = self.c_promotion(), + lane_fn = self.get_lane_function(), + lane = idx + ) + }) + .collect::>() + .join(r#" << ", " << "#) + } else { + format!( + "{promote}cast<{cast}>(__return_value)", + cast = match self.kind() { + TypeKind::Float if self.inner_size() == 16 => "float16_t".to_string(), + TypeKind::Float if self.inner_size() == 32 => "float".to_string(), + TypeKind::Float if self.inner_size() == 64 => "double".to_string(), + TypeKind::Int(Sign::Signed) => format!("int{}_t", self.inner_size()), + TypeKind::Int(Sign::Unsigned) => format!("uint{}_t", self.inner_size()), + TypeKind::Poly => format!("poly{}_t", self.inner_size()), + ty => todo!("print_result_c - Unknown type: {:#?}", ty), + }, + promote = self.c_promotion(), + ) + }; + + format!( + r#"{indentation}std::cout << "Result {additional}-" << i+1 << ": {ty}" << std::fixed << std::setprecision(150) << {lanes} << "{close}" << std::endl;"#, + ty = if self.is_simd() { + format!("{}(", self.c_type()) + } else { + String::from("") + }, + close = if self.is_simd() { ")" } else { "" }, + ) + } } impl ArmIntrinsicType { diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs index 84755ce52505..bfb77be1d4a2 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs @@ -24,7 +24,9 @@ pub fn generate_c_test_loop( loaded_args = intrinsic.arguments().load_values_c(body_indentation), intrinsic_call = intrinsic.name(), args = intrinsic.arguments().as_call_param_c(), - print_result = intrinsic.print_result_c(body_indentation, additional) + print_result = intrinsic + .results() + .print_result_c(body_indentation, additional) ) } diff --git a/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs b/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs index bc46ccfbac40..810681337fd5 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs @@ -1,5 +1,4 @@ use super::argument::ArgumentList; -use super::indentation::Indentation; use super::intrinsic_helpers::{IntrinsicTypeDefinition, TypeKind}; /// An intrinsic @@ -27,11 +26,6 @@ where fn results(&self) -> T; fn name(&self) -> String; - - /// Generates a std::cout for the intrinsics results that will match the - /// rust debug output format for the return type. The generated line assumes - /// there is an int i in scope which is the current pass number. - fn print_result_c(&self, _indentation: Indentation, _additional: &str) -> String; } pub fn format_f16_return_value( diff --git a/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs b/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs index f5e84ca97af2..7bc1015a387c 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs @@ -325,4 +325,9 @@ pub trait IntrinsicTypeDefinition: Deref { /// can be directly defined in `impl` blocks fn c_single_vector_type(&self) -> String; + + /// Generates a std::cout for the intrinsics results that will match the + /// rust debug output format for the return type. The generated line assumes + /// there is an int i in scope which is the current pass number. + fn print_result_c(&self, indentation: Indentation, additional: &str) -> String; } From 1697f36225310391cb96a2c0616f4596d3ba490f Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 7 Sep 2025 13:32:19 +0200 Subject: [PATCH 215/682] remove `trait IntrinsicDefinition` --- .../intrinsic-test/src/arm/intrinsic.rs | 16 ------------- .../crates/intrinsic-test/src/common/gen_c.rs | 23 +++++++++--------- .../intrinsic-test/src/common/gen_rust.rs | 24 ++++++++++--------- .../intrinsic-test/src/common/intrinsic.rs | 19 +++------------ 4 files changed, 28 insertions(+), 54 deletions(-) diff --git a/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs b/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs index 1928a00f4f38..29343bee4c30 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs @@ -1,5 +1,3 @@ -use crate::common::argument::ArgumentList; -use crate::common::intrinsic::{Intrinsic, IntrinsicDefinition}; use crate::common::intrinsic_helpers::IntrinsicType; use std::ops::{Deref, DerefMut}; @@ -22,17 +20,3 @@ impl DerefMut for ArmIntrinsicType { &mut self.data } } - -impl IntrinsicDefinition for Intrinsic { - fn arguments(&self) -> ArgumentList { - self.arguments.clone() - } - - fn results(&self) -> ArmIntrinsicType { - self.results.clone() - } - - fn name(&self) -> String { - self.name.clone() - } -} diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs index bfb77be1d4a2..20a5f2c63f0e 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs @@ -1,6 +1,7 @@ +use crate::common::intrinsic::Intrinsic; + use super::argument::Argument; use super::indentation::Indentation; -use super::intrinsic::IntrinsicDefinition; use super::intrinsic_helpers::IntrinsicTypeDefinition; // The number of times each intrinsic will be called. @@ -8,7 +9,7 @@ const PASSES: u32 = 20; pub fn generate_c_test_loop( w: &mut impl std::io::Write, - intrinsic: &dyn IntrinsicDefinition, + intrinsic: &Intrinsic, indentation: Indentation, additional: &str, passes: u32, @@ -21,18 +22,18 @@ pub fn generate_c_test_loop( {body_indentation}auto __return_value = {intrinsic_call}({args});\n\ {print_result}\n\ {indentation}}}", - loaded_args = intrinsic.arguments().load_values_c(body_indentation), - intrinsic_call = intrinsic.name(), - args = intrinsic.arguments().as_call_param_c(), + loaded_args = intrinsic.arguments.load_values_c(body_indentation), + intrinsic_call = intrinsic.name, + args = intrinsic.arguments.as_call_param_c(), print_result = intrinsic - .results() + .results .print_result_c(body_indentation, additional) ) } pub fn generate_c_constraint_blocks<'a, T: IntrinsicTypeDefinition + 'a>( w: &mut impl std::io::Write, - intrinsic: &dyn IntrinsicDefinition, + intrinsic: &Intrinsic, indentation: Indentation, constraints: &mut (impl Iterator> + Clone), name: String, @@ -65,14 +66,14 @@ pub fn generate_c_constraint_blocks<'a, T: IntrinsicTypeDefinition + 'a>( // Compiles C test programs using specified compiler pub fn create_c_test_function( w: &mut impl std::io::Write, - intrinsic: &dyn IntrinsicDefinition, + intrinsic: &Intrinsic, ) -> std::io::Result<()> { let indentation = Indentation::default(); - writeln!(w, "int run_{}() {{", intrinsic.name())?; + writeln!(w, "int run_{}() {{", intrinsic.name)?; // Define the arrays of arguments. - let arguments = intrinsic.arguments(); + let arguments = &intrinsic.arguments; arguments.gen_arglists_c(w, indentation.nested(), PASSES)?; generate_c_constraint_blocks( @@ -94,7 +95,7 @@ pub fn write_mod_cpp( notice: &str, architecture: &str, platform_headers: &[&str], - intrinsics: &[impl IntrinsicDefinition], + intrinsics: &[Intrinsic], ) -> std::io::Result<()> { write!(w, "{notice}")?; diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs index 2a02b8fdff1d..240496f9b765 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs @@ -1,8 +1,10 @@ use itertools::Itertools; use std::process::Command; +use crate::common::intrinsic::Intrinsic; + use super::indentation::Indentation; -use super::intrinsic::{IntrinsicDefinition, format_f16_return_value}; +use super::intrinsic::format_f16_return_value; use super::intrinsic_helpers::IntrinsicTypeDefinition; // The number of times each intrinsic will be called. @@ -100,7 +102,7 @@ pub fn write_lib_rs( notice: &str, cfg: &str, definitions: &str, - intrinsics: &[impl IntrinsicDefinition], + intrinsics: &[Intrinsic], ) -> std::io::Result<()> { write!(w, "{notice}")?; @@ -189,16 +191,16 @@ pub fn compile_rust_programs(toolchain: Option<&str>, target: &str, linker: Opti pub fn generate_rust_test_loop( w: &mut impl std::io::Write, - intrinsic: &dyn IntrinsicDefinition, + intrinsic: &Intrinsic, indentation: Indentation, specializations: &[Vec], passes: u32, ) -> std::io::Result<()> { - let intrinsic_name = intrinsic.name(); + let intrinsic_name = &intrinsic.name; // Each function (and each specialization) has its own type. Erase that type with a cast. let mut coerce = String::from("unsafe fn("); - for _ in intrinsic.arguments().iter().filter(|a| !a.has_constraint()) { + for _ in intrinsic.arguments.iter().filter(|a| !a.has_constraint()) { coerce += "_, "; } coerce += ") -> _"; @@ -248,8 +250,8 @@ pub fn generate_rust_test_loop( }}\n\ }}\n\ }}", - loaded_args = intrinsic.arguments().load_values_rust(indentation3), - args = intrinsic.arguments().as_call_param_rust(), + loaded_args = intrinsic.arguments.load_values_rust(indentation3), + args = intrinsic.arguments.as_call_param_rust(), ) } @@ -277,15 +279,15 @@ fn generate_rust_specializations<'a>( // Top-level function to create complete test program pub fn create_rust_test_module( w: &mut impl std::io::Write, - intrinsic: &dyn IntrinsicDefinition, + intrinsic: &Intrinsic, ) -> std::io::Result<()> { - trace!("generating `{}`", intrinsic.name()); + trace!("generating `{}`", intrinsic.name); let indentation = Indentation::default(); - writeln!(w, "pub fn run_{}() {{", intrinsic.name())?; + writeln!(w, "pub fn run_{}() {{", intrinsic.name)?; // Define the arrays of arguments. - let arguments = intrinsic.arguments(); + let arguments = &intrinsic.arguments; arguments.gen_arglists_rust(w, indentation.nested(), PASSES)?; // Define any const generics as `const` items, then generate the actual test loop. diff --git a/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs b/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs index 810681337fd5..95276d19b72f 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs @@ -17,27 +17,14 @@ pub struct Intrinsic { pub arch_tags: Vec, } -pub trait IntrinsicDefinition -where - T: IntrinsicTypeDefinition, -{ - fn arguments(&self) -> ArgumentList; - - fn results(&self) -> T; - - fn name(&self) -> String; -} - -pub fn format_f16_return_value( - intrinsic: &dyn IntrinsicDefinition, -) -> String { +pub fn format_f16_return_value(intrinsic: &Intrinsic) -> String { // the `intrinsic-test` crate compares the output of C and Rust intrinsics. Currently, It uses // a string representation of the output value to compare. In C, f16 values are currently printed // as hexadecimal integers. Since https://github.com/rust-lang/rust/pull/127013, rust does print // them as decimal floating point values. To keep the intrinsics tests working, for now, format // vectors containing f16 values like C prints them. - let return_value = match intrinsic.results().kind() { - TypeKind::Float if intrinsic.results().inner_size() == 16 => "debug_f16(__return_value)", + let return_value = match intrinsic.results.kind() { + TypeKind::Float if intrinsic.results.inner_size() == 16 => "debug_f16(__return_value)", _ => "format_args!(\"{__return_value:.150?}\")", }; From ccec2027272c635aaa8713df0943c85df765570e Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 7 Sep 2025 13:35:30 +0200 Subject: [PATCH 216/682] move `build_c_file` and `build_rust_file` into `SupportedArchitectureTest` --- .../crates/intrinsic-test/src/arm/mod.rs | 141 +---------------- .../crates/intrinsic-test/src/common/mod.rs | 145 +++++++++++++++++- 2 files changed, 143 insertions(+), 143 deletions(-) diff --git a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs index 82dc5001adf0..bbf6a7a5a2d6 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs @@ -5,19 +5,11 @@ mod intrinsic; mod json_parser; mod types; -use std::fs::File; - -use rayon::prelude::*; - +use crate::common::SupportedArchitectureTest; use crate::common::cli::ProcessedCli; use crate::common::compile_c::CppCompilation; -use crate::common::gen_c::{write_main_cpp, write_mod_cpp}; -use crate::common::gen_rust::{ - compile_rust_programs, write_bin_cargo_toml, write_lib_cargo_toml, write_lib_rs, write_main_rs, -}; use crate::common::intrinsic::Intrinsic; use crate::common::intrinsic_helpers::TypeKind; -use crate::common::{SupportedArchitectureTest, chunk_info}; use intrinsic::ArmIntrinsicType; use json_parser::get_neon_intrinsics; @@ -76,135 +68,4 @@ impl SupportedArchitectureTest for ArmArchitectureTest { fn cpp_compilation(&self) -> Option { compile::build_cpp_compilation(&self.cli_options) } - - fn build_c_file(&self) -> bool { - let c_target = "aarch64"; - - let (chunk_size, chunk_count) = chunk_info(self.intrinsics().len()); - - let cpp_compiler_wrapped = self.cpp_compilation(); - - std::fs::create_dir_all("c_programs").unwrap(); - self.intrinsics() - .par_chunks(chunk_size) - .enumerate() - .map(|(i, chunk)| { - let c_filename = format!("c_programs/mod_{i}.cpp"); - let mut file = File::create(&c_filename).unwrap(); - write_mod_cpp( - &mut file, - Self::NOTICE, - c_target, - Self::PLATFORM_C_HEADERS, - chunk, - ) - .unwrap(); - - // compile this cpp file into a .o file. - // - // This is done because `cpp_compiler_wrapped` is None when - // the --generate-only flag is passed - if let Some(cpp_compiler) = cpp_compiler_wrapped.as_ref() { - let output = cpp_compiler - .compile_object_file(&format!("mod_{i}.cpp"), &format!("mod_{i}.o"))?; - assert!(output.status.success(), "{output:?}"); - } - - Ok(()) - }) - .collect::>() - .unwrap(); - - let mut file = File::create("c_programs/main.cpp").unwrap(); - write_main_cpp( - &mut file, - c_target, - Self::PLATFORM_C_DEFINITIONS, - self.intrinsics().iter().map(|i| i.name.as_str()), - ) - .unwrap(); - - // This is done because `cpp_compiler_wrapped` is None when - // the --generate-only flag is passed - if let Some(cpp_compiler) = cpp_compiler_wrapped.as_ref() { - // compile this cpp file into a .o file - info!("compiling main.cpp"); - let output = cpp_compiler - .compile_object_file("main.cpp", "intrinsic-test-programs.o") - .unwrap(); - assert!(output.status.success(), "{output:?}"); - - let object_files = (0..chunk_count) - .map(|i| format!("mod_{i}.o")) - .chain(["intrinsic-test-programs.o".to_owned()]); - - let output = cpp_compiler - .link_executable(object_files, "intrinsic-test-programs") - .unwrap(); - assert!(output.status.success(), "{output:?}"); - } - - true - } - - fn build_rust_file(&self) -> bool { - std::fs::create_dir_all("rust_programs/src").unwrap(); - - let architecture = if self.cli_options.target.contains("v7") { - "arm" - } else { - "aarch64" - }; - - let (chunk_size, chunk_count) = chunk_info(self.intrinsics.len()); - - let mut cargo = File::create("rust_programs/Cargo.toml").unwrap(); - write_bin_cargo_toml(&mut cargo, chunk_count).unwrap(); - - let mut main_rs = File::create("rust_programs/src/main.rs").unwrap(); - write_main_rs( - &mut main_rs, - chunk_count, - Self::PLATFORM_RUST_CFGS, - "", - self.intrinsics.iter().map(|i| i.name.as_str()), - ) - .unwrap(); - - let target = &self.cli_options.target; - let toolchain = self.cli_options.toolchain.as_deref(); - let linker = self.cli_options.linker.as_deref(); - - self.intrinsics - .par_chunks(chunk_size) - .enumerate() - .map(|(i, chunk)| { - std::fs::create_dir_all(format!("rust_programs/mod_{i}/src"))?; - - let rust_filename = format!("rust_programs/mod_{i}/src/lib.rs"); - trace!("generating `{rust_filename}`"); - let mut file = File::create(rust_filename)?; - - write_lib_rs( - &mut file, - architecture, - Self::NOTICE, - Self::PLATFORM_RUST_CFGS, - Self::PLATFORM_RUST_DEFINITIONS, - chunk, - )?; - - let toml_filename = format!("rust_programs/mod_{i}/Cargo.toml"); - trace!("generating `{toml_filename}`"); - let mut file = File::create(toml_filename).unwrap(); - - write_lib_cargo_toml(&mut file, &format!("mod_{i}"))?; - - Ok(()) - }) - .collect::>() - .unwrap(); - - compile_rust_programs(toolchain, target, linker) - } } diff --git a/library/stdarch/crates/intrinsic-test/src/common/mod.rs b/library/stdarch/crates/intrinsic-test/src/common/mod.rs index 13e6c9fe9e28..b6589ddaef8e 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/mod.rs @@ -1,7 +1,18 @@ +use std::fs::File; + +use rayon::prelude::*; + use cli::ProcessedCli; use crate::common::{ - compile_c::CppCompilation, intrinsic::Intrinsic, intrinsic_helpers::IntrinsicTypeDefinition, + compile_c::CppCompilation, + gen_c::{write_main_cpp, write_mod_cpp}, + gen_rust::{ + compile_rust_programs, write_bin_cargo_toml, write_lib_cargo_toml, write_lib_rs, + write_main_rs, + }, + intrinsic::Intrinsic, + intrinsic_helpers::IntrinsicTypeDefinition, }; pub mod argument; @@ -36,8 +47,136 @@ pub trait SupportedArchitectureTest { fn cpp_compilation(&self) -> Option; - fn build_c_file(&self) -> bool; - fn build_rust_file(&self) -> bool; + fn build_c_file(&self) -> bool { + let c_target = "aarch64"; + + let (chunk_size, chunk_count) = chunk_info(self.intrinsics().len()); + + let cpp_compiler_wrapped = self.cpp_compilation(); + + std::fs::create_dir_all("c_programs").unwrap(); + self.intrinsics() + .par_chunks(chunk_size) + .enumerate() + .map(|(i, chunk)| { + let c_filename = format!("c_programs/mod_{i}.cpp"); + let mut file = File::create(&c_filename).unwrap(); + write_mod_cpp( + &mut file, + Self::NOTICE, + c_target, + Self::PLATFORM_C_HEADERS, + chunk, + ) + .unwrap(); + + // compile this cpp file into a .o file. + // + // This is done because `cpp_compiler_wrapped` is None when + // the --generate-only flag is passed + if let Some(cpp_compiler) = cpp_compiler_wrapped.as_ref() { + let output = cpp_compiler + .compile_object_file(&format!("mod_{i}.cpp"), &format!("mod_{i}.o"))?; + assert!(output.status.success(), "{output:?}"); + } + + Ok(()) + }) + .collect::>() + .unwrap(); + + let mut file = File::create("c_programs/main.cpp").unwrap(); + write_main_cpp( + &mut file, + c_target, + Self::PLATFORM_C_DEFINITIONS, + self.intrinsics().iter().map(|i| i.name.as_str()), + ) + .unwrap(); + + // This is done because `cpp_compiler_wrapped` is None when + // the --generate-only flag is passed + if let Some(cpp_compiler) = cpp_compiler_wrapped.as_ref() { + // compile this cpp file into a .o file + info!("compiling main.cpp"); + let output = cpp_compiler + .compile_object_file("main.cpp", "intrinsic-test-programs.o") + .unwrap(); + assert!(output.status.success(), "{output:?}"); + + let object_files = (0..chunk_count) + .map(|i| format!("mod_{i}.o")) + .chain(["intrinsic-test-programs.o".to_owned()]); + + let output = cpp_compiler + .link_executable(object_files, "intrinsic-test-programs") + .unwrap(); + assert!(output.status.success(), "{output:?}"); + } + + true + } + + fn build_rust_file(&self) -> bool { + std::fs::create_dir_all("rust_programs/src").unwrap(); + + let architecture = if self.cli_options().target.contains("v7") { + "arm" + } else { + "aarch64" + }; + + let (chunk_size, chunk_count) = chunk_info(self.intrinsics().len()); + + let mut cargo = File::create("rust_programs/Cargo.toml").unwrap(); + write_bin_cargo_toml(&mut cargo, chunk_count).unwrap(); + + let mut main_rs = File::create("rust_programs/src/main.rs").unwrap(); + write_main_rs( + &mut main_rs, + chunk_count, + Self::PLATFORM_RUST_CFGS, + "", + self.intrinsics().iter().map(|i| i.name.as_str()), + ) + .unwrap(); + + let target = &self.cli_options().target; + let toolchain = self.cli_options().toolchain.as_deref(); + let linker = self.cli_options().linker.as_deref(); + + self.intrinsics() + .par_chunks(chunk_size) + .enumerate() + .map(|(i, chunk)| { + std::fs::create_dir_all(format!("rust_programs/mod_{i}/src"))?; + + let rust_filename = format!("rust_programs/mod_{i}/src/lib.rs"); + trace!("generating `{rust_filename}`"); + let mut file = File::create(rust_filename)?; + + write_lib_rs( + &mut file, + architecture, + Self::NOTICE, + Self::PLATFORM_RUST_CFGS, + Self::PLATFORM_RUST_DEFINITIONS, + chunk, + )?; + + let toml_filename = format!("rust_programs/mod_{i}/Cargo.toml"); + trace!("generating `{toml_filename}`"); + let mut file = File::create(toml_filename).unwrap(); + + write_lib_cargo_toml(&mut file, &format!("mod_{i}"))?; + + Ok(()) + }) + .collect::>() + .unwrap(); + + compile_rust_programs(toolchain, target, linker) + } fn compare_outputs(&self) -> bool { if self.cli_options().toolchain.is_some() { From 4b549a7330736bad6f7296241c6ce1cdbab4ef60 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 7 Sep 2025 14:10:47 +0200 Subject: [PATCH 217/682] move target-specific definitions into constants --- .../crates/intrinsic-test/src/arm/config.rs | 20 +++++++++++++-- .../crates/intrinsic-test/src/arm/mod.rs | 25 ++++++++++--------- .../crates/intrinsic-test/src/common/gen_c.rs | 13 +++------- .../intrinsic-test/src/common/gen_rust.rs | 3 --- .../crates/intrinsic-test/src/common/mod.rs | 13 ++-------- 5 files changed, 36 insertions(+), 38 deletions(-) diff --git a/library/stdarch/crates/intrinsic-test/src/arm/config.rs b/library/stdarch/crates/intrinsic-test/src/arm/config.rs index beaca621378d..72e997de154a 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/config.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/config.rs @@ -3,7 +3,15 @@ pub const NOTICE: &str = "\ // test are derived from a JSON specification, published under the same license as the // `intrinsic-test` crate.\n"; -pub const POLY128_OSTREAM_DEF: &str = r#"std::ostream& operator<<(std::ostream& os, poly128_t value) { +pub const POLY128_OSTREAM_DECL: &str = r#" +#ifdef __aarch64__ +std::ostream& operator<<(std::ostream& os, poly128_t value); +#endif +"#; + +pub const POLY128_OSTREAM_DEF: &str = r#" +#ifdef __aarch64__ +std::ostream& operator<<(std::ostream& os, poly128_t value) { std::stringstream temp; do { int n = value % 10; @@ -14,7 +22,9 @@ pub const POLY128_OSTREAM_DEF: &str = r#"std::ostream& operator<<(std::ostream& std::string res(tempstr.rbegin(), tempstr.rend()); os << res; return os; -}"#; +} +#endif +"#; // Format f16 values (and vectors containing them) in a way that is consistent with C. pub const F16_FORMATTING_DEF: &str = r#" @@ -113,4 +123,10 @@ pub const AARCH_CONFIGURATIONS: &str = r#" #![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_ftts))] #![feature(fmt_helpers_for_derive)] #![feature(stdarch_neon_f16)] + +#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec"))] +use core::arch::aarch64::*; + +#[cfg(target_arch = "arm")] +use core::arch::arm::*; "#; diff --git a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs index bbf6a7a5a2d6..08dc2d38702c 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs @@ -29,6 +29,19 @@ impl SupportedArchitectureTest for ArmArchitectureTest { &self.intrinsics } + const NOTICE: &str = config::NOTICE; + + const PLATFORM_C_HEADERS: &[&str] = &["arm_neon.h", "arm_acle.h", "arm_fp16.h"]; + const PLATFORM_C_DEFINITIONS: &str = config::POLY128_OSTREAM_DEF; + const PLATFORM_C_FORWARD_DECLARATIONS: &str = config::POLY128_OSTREAM_DECL; + + const PLATFORM_RUST_DEFINITIONS: &str = config::F16_FORMATTING_DEF; + const PLATFORM_RUST_CFGS: &str = config::AARCH_CONFIGURATIONS; + + fn cpp_compilation(&self) -> Option { + compile::build_cpp_compilation(&self.cli_options) + } + fn create(cli_options: ProcessedCli) -> Self { let a32 = cli_options.target.contains("v7"); let mut intrinsics = get_neon_intrinsics(&cli_options.filename, &cli_options.target) @@ -56,16 +69,4 @@ impl SupportedArchitectureTest for ArmArchitectureTest { cli_options, } } - - const NOTICE: &str = config::NOTICE; - - const PLATFORM_C_HEADERS: &[&str] = &["arm_neon.h", "arm_acle.h", "arm_fp16.h"]; - const PLATFORM_C_DEFINITIONS: &str = config::POLY128_OSTREAM_DEF; - - const PLATFORM_RUST_DEFINITIONS: &str = config::F16_FORMATTING_DEF; - const PLATFORM_RUST_CFGS: &str = config::AARCH_CONFIGURATIONS; - - fn cpp_compilation(&self) -> Option { - compile::build_cpp_compilation(&self.cli_options) - } } diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs index 20a5f2c63f0e..28902b3dfe98 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs @@ -93,8 +93,8 @@ pub fn create_c_test_function( pub fn write_mod_cpp( w: &mut impl std::io::Write, notice: &str, - architecture: &str, platform_headers: &[&str], + forward_declarations: &str, intrinsics: &[Intrinsic], ) -> std::io::Result<()> { write!(w, "{notice}")?; @@ -125,12 +125,7 @@ std::ostream& operator<<(std::ostream& os, float16_t value); "# )?; - writeln!(w, "#ifdef __{architecture}__")?; - writeln!( - w, - "std::ostream& operator<<(std::ostream& os, poly128_t value);" - )?; - writeln!(w, "#endif")?; + writeln!(w, "{}", forward_declarations)?; for intrinsic in intrinsics { create_c_test_function(w, intrinsic)?; @@ -141,7 +136,6 @@ std::ostream& operator<<(std::ostream& os, float16_t value); pub fn write_main_cpp<'a>( w: &mut impl std::io::Write, - architecture: &str, arch_specific_definitions: &str, intrinsics: impl Iterator + Clone, ) -> std::io::Result<()> { @@ -170,9 +164,8 @@ std::ostream& operator<<(std::ostream& os, float16_t value) {{ "# )?; - writeln!(w, "#ifdef __{architecture}__")?; + // NOTE: It's assumed that this value contains the required `ifdef`s. writeln!(w, "{arch_specific_definitions }")?; - writeln!(w, "#endif")?; for intrinsic in intrinsics.clone() { writeln!(w, "extern int run_{intrinsic}(void);")?; diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs index 240496f9b765..312cbee692a5 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs @@ -98,7 +98,6 @@ pub fn write_main_rs<'a>( pub fn write_lib_rs( w: &mut impl std::io::Write, - architecture: &str, notice: &str, cfg: &str, definitions: &str, @@ -117,8 +116,6 @@ pub fn write_lib_rs( writeln!(w, "{cfg}")?; - writeln!(w, "use core_arch::arch::{architecture}::*;")?; - writeln!(w, "{definitions}")?; for intrinsic in intrinsics { diff --git a/library/stdarch/crates/intrinsic-test/src/common/mod.rs b/library/stdarch/crates/intrinsic-test/src/common/mod.rs index b6589ddaef8e..666b3885c147 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/mod.rs @@ -41,6 +41,7 @@ pub trait SupportedArchitectureTest { const PLATFORM_C_HEADERS: &[&str]; const PLATFORM_C_DEFINITIONS: &str; + const PLATFORM_C_FORWARD_DECLARATIONS: &str; const PLATFORM_RUST_CFGS: &str; const PLATFORM_RUST_DEFINITIONS: &str; @@ -48,8 +49,6 @@ pub trait SupportedArchitectureTest { fn cpp_compilation(&self) -> Option; fn build_c_file(&self) -> bool { - let c_target = "aarch64"; - let (chunk_size, chunk_count) = chunk_info(self.intrinsics().len()); let cpp_compiler_wrapped = self.cpp_compilation(); @@ -64,8 +63,8 @@ pub trait SupportedArchitectureTest { write_mod_cpp( &mut file, Self::NOTICE, - c_target, Self::PLATFORM_C_HEADERS, + Self::PLATFORM_C_FORWARD_DECLARATIONS, chunk, ) .unwrap(); @@ -88,7 +87,6 @@ pub trait SupportedArchitectureTest { let mut file = File::create("c_programs/main.cpp").unwrap(); write_main_cpp( &mut file, - c_target, Self::PLATFORM_C_DEFINITIONS, self.intrinsics().iter().map(|i| i.name.as_str()), ) @@ -120,12 +118,6 @@ pub trait SupportedArchitectureTest { fn build_rust_file(&self) -> bool { std::fs::create_dir_all("rust_programs/src").unwrap(); - let architecture = if self.cli_options().target.contains("v7") { - "arm" - } else { - "aarch64" - }; - let (chunk_size, chunk_count) = chunk_info(self.intrinsics().len()); let mut cargo = File::create("rust_programs/Cargo.toml").unwrap(); @@ -157,7 +149,6 @@ pub trait SupportedArchitectureTest { write_lib_rs( &mut file, - architecture, Self::NOTICE, Self::PLATFORM_RUST_CFGS, Self::PLATFORM_RUST_DEFINITIONS, From a8c669461f0c71985c72dd5b05f70b8d4d149e3b Mon Sep 17 00:00:00 2001 From: Karl Meakin Date: Sat, 2 Aug 2025 23:32:10 +0100 Subject: [PATCH 218/682] optimization: Don't include ASCII characters in Unicode tables The ASCII subset of Unicode is fixed and will never change, so we don't need to generate tables for it with every new Unicode version. This saves a few bytes of static data and speeds up `char::is_control` and `char::is_grapheme_extended` on ASCII inputs. Since the table lookup functions exported from the `unicode` module will give nonsensical errors on ASCII input (and in fact will panic in debug mode), I had to add some private wrapper methods to `char` which check for ASCII-ness first. --- library/alloc/src/str.rs | 5 +- library/core/src/char/methods.rs | 38 +- library/core/src/unicode/unicode_data.rs | 519 ++++++++++-------- .../src/cascading_map.rs | 1 + src/tools/unicode-table-generator/src/main.rs | 1 + .../src/raw_emitter.rs | 1 + .../unicode-table-generator/src/skiplist.rs | 2 + 7 files changed, 320 insertions(+), 247 deletions(-) diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index 22cdd8ecde02..e772ac25a95c 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -418,9 +418,8 @@ impl str { } fn case_ignorable_then_cased>(iter: I) -> bool { - use core::unicode::{Case_Ignorable, Cased}; - match iter.skip_while(|&c| Case_Ignorable(c)).next() { - Some(c) => Cased(c), + match iter.skip_while(|&c| c.is_case_ignorable()).next() { + Some(c) => c.is_cased(), None => false, } } diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 3336d028e273..76f54db28707 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -969,7 +969,43 @@ impl char { #[must_use] #[inline] pub(crate) fn is_grapheme_extended(self) -> bool { - unicode::Grapheme_Extend(self) + !self.is_ascii() && unicode::Grapheme_Extend(self) + } + + /// Returns `true` if this `char` has the `Cased` property. + /// + /// `Cased` is described in Chapter 4 (Character Properties) of the [Unicode Standard] and + /// specified in the [Unicode Character Database][ucd] [`DerivedCoreProperties.txt`]. + /// + /// [Unicode Standard]: https://www.unicode.org/versions/latest/ + /// [ucd]: https://www.unicode.org/reports/tr44/ + /// [`DerivedCoreProperties.txt`]: https://www.unicode.org/Public/UCD/latest/ucd/DerivedCoreProperties.txt + #[must_use] + #[inline] + #[doc(hidden)] + #[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")] + pub fn is_cased(self) -> bool { + if self.is_ascii() { self.is_ascii_alphabetic() } else { unicode::Cased(self) } + } + + /// Returns `true` if this `char` has the `Case_Ignorable` property. + /// + /// `Case_Ignorable` is described in Chapter 4 (Character Properties) of the [Unicode Standard] and + /// specified in the [Unicode Character Database][ucd] [`DerivedCoreProperties.txt`]. + /// + /// [Unicode Standard]: https://www.unicode.org/versions/latest/ + /// [ucd]: https://www.unicode.org/reports/tr44/ + /// [`DerivedCoreProperties.txt`]: https://www.unicode.org/Public/UCD/latest/ucd/DerivedCoreProperties.txt + #[must_use] + #[inline] + #[doc(hidden)] + #[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")] + pub fn is_case_ignorable(self) -> bool { + if self.is_ascii() { + matches!(self, '\'' | '.' | ':' | '^' | '`') + } else { + unicode::Case_Ignorable(self) + } } /// Returns `true` if this `char` has one of the general categories for numbers. diff --git a/library/core/src/unicode/unicode_data.rs b/library/core/src/unicode/unicode_data.rs index 0bf2f948e85b..2f53de183f62 100644 --- a/library/core/src/unicode/unicode_data.rs +++ b/library/core/src/unicode/unicode_data.rs @@ -1,16 +1,15 @@ //! This file is generated by `./x run src/tools/unicode-table-generator`; do not edit manually! -// Alphabetic : 1727 bytes, 142759 codepoints in 757 ranges (U+000041 - U+0323B0) using skiplist -// Case_Ignorable : 1053 bytes, 2749 codepoints in 452 ranges (U+000027 - U+0E01F0) using skiplist -// Cased : 407 bytes, 4578 codepoints in 159 ranges (U+000041 - U+01F18A) using skiplist -// Cc : 9 bytes, 65 codepoints in 2 ranges (U+000000 - U+0000A0) using skiplist +// Alphabetic : 1723 bytes, 142707 codepoints in 755 ranges (U+0000AA - U+0323B0) using skiplist +// Case_Ignorable : 1043 bytes, 2744 codepoints in 447 ranges (U+0000A8 - U+0E01F0) using skiplist +// Cased : 403 bytes, 4526 codepoints in 157 ranges (U+0000AA - U+01F18A) using skiplist // Grapheme_Extend : 887 bytes, 2193 codepoints in 375 ranges (U+000300 - U+0E01F0) using skiplist -// Lowercase : 935 bytes, 2569 codepoints in 675 ranges (U+000061 - U+01E944) using bitset -// N : 457 bytes, 1911 codepoints in 144 ranges (U+000030 - U+01FBFA) using skiplist -// Uppercase : 799 bytes, 1978 codepoints in 656 ranges (U+000041 - U+01F18A) using bitset -// White_Space : 256 bytes, 25 codepoints in 10 ranges (U+000009 - U+003001) using cascading +// Lowercase : 933 bytes, 2543 codepoints in 674 ranges (U+0000AA - U+01E944) using bitset +// N : 455 bytes, 1901 codepoints in 143 ranges (U+0000B2 - U+01FBFA) using skiplist +// Uppercase : 797 bytes, 1952 codepoints in 655 ranges (U+0000C0 - U+01F18A) using bitset +// White_Space : 256 bytes, 19 codepoints in 8 ranges (U+000085 - U+003001) using cascading // to_lower : 11484 bytes // to_upper : 13432 bytes -// Total : 31446 bytes +// Total : 31413 bytes #[inline(always)] const fn bitset_search< @@ -148,93 +147,100 @@ pub mod alphabetic { use super::ShortOffsetRunHeader; static SHORT_OFFSET_RUNS: [ShortOffsetRunHeader; 53] = [ - ShortOffsetRunHeader::new(0, 706), ShortOffsetRunHeader::new(16, 4681), - ShortOffsetRunHeader::new(418, 5741), ShortOffsetRunHeader::new(456, 7958), - ShortOffsetRunHeader::new(556, 9398), ShortOffsetRunHeader::new(627, 11264), - ShortOffsetRunHeader::new(629, 12293), ShortOffsetRunHeader::new(667, 13312), - ShortOffsetRunHeader::new(691, 19904), ShortOffsetRunHeader::new(692, 42125), - ShortOffsetRunHeader::new(694, 42509), ShortOffsetRunHeader::new(698, 55204), - ShortOffsetRunHeader::new(788, 63744), ShortOffsetRunHeader::new(793, 64110), - ShortOffsetRunHeader::new(794, 64830), ShortOffsetRunHeader::new(816, 66176), - ShortOffsetRunHeader::new(857, 67383), ShortOffsetRunHeader::new(904, 73440), - ShortOffsetRunHeader::new(1221, 74650), ShortOffsetRunHeader::new(1232, 77712), - ShortOffsetRunHeader::new(1237, 78896), ShortOffsetRunHeader::new(1240, 82939), - ShortOffsetRunHeader::new(1244, 83527), ShortOffsetRunHeader::new(1246, 90368), - ShortOffsetRunHeader::new(1247, 92160), ShortOffsetRunHeader::new(1249, 92729), - ShortOffsetRunHeader::new(1250, 93504), ShortOffsetRunHeader::new(1265, 100344), - ShortOffsetRunHeader::new(1282, 101590), ShortOffsetRunHeader::new(1284, 110576), - ShortOffsetRunHeader::new(1287, 110883), ShortOffsetRunHeader::new(1294, 111356), - ShortOffsetRunHeader::new(1304, 113664), ShortOffsetRunHeader::new(1305, 119808), - ShortOffsetRunHeader::new(1315, 120486), ShortOffsetRunHeader::new(1352, 122624), - ShortOffsetRunHeader::new(1375, 123536), ShortOffsetRunHeader::new(1399, 124112), - ShortOffsetRunHeader::new(1403, 124896), ShortOffsetRunHeader::new(1409, 126464), - ShortOffsetRunHeader::new(1425, 127280), ShortOffsetRunHeader::new(1491, 131072), - ShortOffsetRunHeader::new(1497, 173792), ShortOffsetRunHeader::new(1498, 177978), - ShortOffsetRunHeader::new(1500, 183970), ShortOffsetRunHeader::new(1504, 191457), - ShortOffsetRunHeader::new(1506, 192094), ShortOffsetRunHeader::new(1508, 194560), - ShortOffsetRunHeader::new(1509, 195102), ShortOffsetRunHeader::new(1510, 196608), - ShortOffsetRunHeader::new(1511, 201547), ShortOffsetRunHeader::new(1512, 205744), - ShortOffsetRunHeader::new(1514, 1319856), + ShortOffsetRunHeader::new(0, 706), ShortOffsetRunHeader::new(12, 4681), + ShortOffsetRunHeader::new(414, 5741), ShortOffsetRunHeader::new(452, 7958), + ShortOffsetRunHeader::new(552, 9398), ShortOffsetRunHeader::new(623, 11264), + ShortOffsetRunHeader::new(625, 12293), ShortOffsetRunHeader::new(663, 13312), + ShortOffsetRunHeader::new(687, 19904), ShortOffsetRunHeader::new(688, 42125), + ShortOffsetRunHeader::new(690, 42509), ShortOffsetRunHeader::new(694, 55204), + ShortOffsetRunHeader::new(784, 63744), ShortOffsetRunHeader::new(789, 64110), + ShortOffsetRunHeader::new(790, 64830), ShortOffsetRunHeader::new(812, 66176), + ShortOffsetRunHeader::new(853, 67383), ShortOffsetRunHeader::new(900, 73440), + ShortOffsetRunHeader::new(1217, 74650), ShortOffsetRunHeader::new(1228, 77712), + ShortOffsetRunHeader::new(1233, 78896), ShortOffsetRunHeader::new(1236, 82939), + ShortOffsetRunHeader::new(1240, 83527), ShortOffsetRunHeader::new(1242, 90368), + ShortOffsetRunHeader::new(1243, 92160), ShortOffsetRunHeader::new(1245, 92729), + ShortOffsetRunHeader::new(1246, 93504), ShortOffsetRunHeader::new(1261, 100344), + ShortOffsetRunHeader::new(1278, 101590), ShortOffsetRunHeader::new(1280, 110576), + ShortOffsetRunHeader::new(1283, 110883), ShortOffsetRunHeader::new(1290, 111356), + ShortOffsetRunHeader::new(1300, 113664), ShortOffsetRunHeader::new(1301, 119808), + ShortOffsetRunHeader::new(1311, 120486), ShortOffsetRunHeader::new(1348, 122624), + ShortOffsetRunHeader::new(1371, 123536), ShortOffsetRunHeader::new(1395, 124112), + ShortOffsetRunHeader::new(1399, 124896), ShortOffsetRunHeader::new(1405, 126464), + ShortOffsetRunHeader::new(1421, 127280), ShortOffsetRunHeader::new(1487, 131072), + ShortOffsetRunHeader::new(1493, 173792), ShortOffsetRunHeader::new(1494, 177978), + ShortOffsetRunHeader::new(1496, 183970), ShortOffsetRunHeader::new(1500, 191457), + ShortOffsetRunHeader::new(1502, 192094), ShortOffsetRunHeader::new(1504, 194560), + ShortOffsetRunHeader::new(1505, 195102), ShortOffsetRunHeader::new(1506, 196608), + ShortOffsetRunHeader::new(1507, 201547), ShortOffsetRunHeader::new(1508, 205744), + ShortOffsetRunHeader::new(1510, 1319856), ]; - static OFFSETS: [u8; 1515] = [ - 65, 26, 6, 26, 47, 1, 10, 1, 4, 1, 5, 23, 1, 31, 1, 0, 4, 12, 14, 5, 7, 1, 1, 1, 86, 1, 29, - 18, 1, 2, 2, 4, 1, 1, 6, 1, 1, 3, 1, 1, 1, 20, 1, 83, 1, 139, 8, 166, 1, 38, 2, 1, 6, 41, - 39, 14, 1, 1, 1, 2, 1, 2, 1, 1, 8, 27, 4, 4, 29, 11, 5, 56, 1, 7, 14, 102, 1, 8, 4, 8, 4, 3, - 10, 3, 2, 1, 16, 48, 13, 101, 24, 33, 9, 2, 4, 1, 5, 24, 2, 19, 19, 25, 7, 11, 5, 24, 1, 6, - 8, 1, 8, 42, 10, 12, 3, 7, 6, 76, 1, 16, 1, 3, 4, 15, 13, 19, 1, 8, 2, 2, 2, 22, 1, 7, 1, 1, - 3, 4, 3, 8, 2, 2, 2, 2, 1, 1, 8, 1, 4, 2, 1, 5, 12, 2, 10, 1, 4, 3, 1, 6, 4, 2, 2, 22, 1, 7, - 1, 2, 1, 2, 1, 2, 4, 5, 4, 2, 2, 2, 4, 1, 7, 4, 1, 1, 17, 6, 11, 3, 1, 9, 1, 3, 1, 22, 1, 7, - 1, 2, 1, 5, 3, 9, 1, 3, 1, 2, 3, 1, 15, 4, 21, 4, 4, 3, 1, 8, 2, 2, 2, 22, 1, 7, 1, 2, 1, 5, - 3, 8, 2, 2, 2, 2, 9, 2, 4, 2, 1, 5, 13, 1, 16, 2, 1, 6, 3, 3, 1, 4, 3, 2, 1, 1, 1, 2, 3, 2, - 3, 3, 3, 12, 4, 5, 3, 3, 1, 3, 3, 1, 6, 1, 40, 13, 1, 3, 1, 23, 1, 16, 3, 8, 1, 3, 1, 3, 8, - 2, 1, 3, 2, 1, 2, 4, 28, 4, 1, 8, 1, 3, 1, 23, 1, 10, 1, 5, 3, 8, 1, 3, 1, 3, 8, 2, 6, 2, 1, - 4, 13, 3, 12, 13, 1, 3, 1, 41, 2, 8, 1, 3, 1, 3, 1, 1, 5, 4, 7, 5, 22, 6, 1, 3, 1, 18, 3, - 24, 1, 9, 1, 1, 2, 7, 8, 6, 1, 1, 1, 8, 18, 2, 13, 58, 5, 7, 6, 1, 51, 2, 1, 1, 1, 5, 1, 24, - 1, 1, 1, 19, 1, 3, 2, 5, 1, 1, 6, 1, 14, 4, 32, 1, 63, 8, 1, 36, 4, 19, 4, 16, 1, 36, 67, - 55, 1, 1, 2, 5, 16, 64, 10, 4, 2, 38, 1, 1, 5, 1, 2, 43, 1, 0, 1, 4, 2, 7, 1, 1, 1, 4, 2, - 41, 1, 4, 2, 33, 1, 4, 2, 7, 1, 1, 1, 4, 2, 15, 1, 57, 1, 4, 2, 67, 37, 16, 16, 86, 2, 6, 3, - 0, 2, 17, 1, 26, 5, 75, 3, 11, 7, 20, 11, 21, 12, 20, 12, 13, 1, 3, 1, 2, 12, 52, 2, 19, 14, - 1, 4, 1, 67, 89, 7, 43, 5, 70, 10, 31, 1, 12, 4, 9, 23, 30, 2, 5, 11, 44, 4, 26, 54, 28, 4, - 63, 2, 20, 50, 1, 23, 2, 11, 3, 49, 52, 1, 15, 1, 8, 51, 42, 2, 4, 10, 44, 1, 11, 14, 55, - 22, 3, 10, 36, 2, 11, 5, 43, 2, 3, 41, 4, 1, 6, 1, 2, 3, 1, 5, 192, 19, 34, 11, 0, 2, 6, 2, - 38, 2, 6, 2, 8, 1, 1, 1, 1, 1, 1, 1, 31, 2, 53, 1, 7, 1, 1, 3, 3, 1, 7, 3, 4, 2, 6, 4, 13, - 5, 3, 1, 7, 116, 1, 13, 1, 16, 13, 101, 1, 4, 1, 2, 10, 1, 1, 3, 5, 6, 1, 1, 1, 1, 1, 1, 4, - 1, 11, 2, 4, 5, 5, 4, 1, 17, 41, 0, 52, 0, 229, 6, 4, 3, 2, 12, 38, 1, 1, 5, 1, 2, 56, 7, 1, - 16, 23, 9, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 32, 47, 1, 0, 3, 25, 9, 7, 5, 2, - 5, 4, 86, 6, 3, 1, 90, 1, 4, 5, 43, 1, 94, 17, 32, 48, 16, 0, 0, 64, 0, 67, 46, 2, 0, 3, 16, - 10, 2, 20, 47, 5, 8, 3, 113, 39, 9, 2, 103, 2, 67, 2, 2, 1, 1, 1, 8, 21, 20, 1, 33, 24, 52, - 12, 68, 1, 1, 44, 6, 3, 1, 1, 3, 10, 33, 5, 35, 13, 29, 3, 51, 1, 12, 15, 1, 16, 16, 10, 5, - 1, 55, 9, 14, 18, 23, 3, 69, 1, 1, 1, 1, 24, 3, 2, 16, 2, 4, 11, 6, 2, 6, 2, 6, 9, 7, 1, 7, - 1, 43, 1, 14, 6, 123, 21, 0, 12, 23, 4, 49, 0, 0, 2, 106, 38, 7, 12, 5, 5, 12, 1, 13, 1, 5, - 1, 1, 1, 2, 1, 2, 1, 108, 33, 0, 18, 64, 2, 54, 40, 12, 116, 5, 1, 135, 36, 26, 6, 26, 11, - 89, 3, 6, 2, 6, 2, 6, 2, 3, 35, 12, 1, 26, 1, 19, 1, 2, 1, 15, 2, 14, 34, 123, 69, 53, 0, - 29, 3, 49, 47, 32, 13, 30, 5, 43, 5, 30, 2, 36, 4, 8, 1, 5, 42, 158, 18, 36, 4, 36, 4, 40, - 8, 52, 12, 11, 1, 15, 1, 7, 1, 2, 1, 11, 1, 15, 1, 7, 1, 2, 3, 52, 12, 0, 9, 22, 10, 8, 24, - 6, 1, 42, 1, 9, 69, 6, 2, 1, 1, 44, 1, 2, 3, 1, 2, 23, 10, 23, 9, 31, 65, 19, 1, 2, 10, 22, - 10, 26, 70, 56, 6, 2, 64, 4, 1, 2, 5, 8, 1, 3, 1, 29, 42, 29, 3, 29, 35, 8, 1, 28, 27, 54, - 10, 22, 10, 19, 13, 18, 110, 73, 55, 51, 13, 51, 13, 40, 34, 28, 3, 1, 5, 23, 250, 42, 1, 2, - 3, 2, 16, 3, 55, 1, 3, 29, 10, 1, 8, 22, 42, 18, 46, 21, 27, 23, 9, 70, 43, 5, 10, 57, 9, 1, - 13, 25, 23, 51, 17, 4, 8, 35, 3, 1, 9, 64, 1, 4, 9, 2, 10, 1, 1, 1, 35, 18, 1, 34, 2, 1, 6, - 4, 62, 7, 1, 1, 1, 4, 1, 15, 1, 10, 7, 57, 23, 4, 1, 8, 2, 2, 2, 22, 1, 7, 1, 2, 1, 5, 3, 8, - 2, 2, 2, 2, 3, 1, 6, 1, 5, 7, 28, 10, 1, 1, 2, 1, 1, 38, 1, 10, 1, 1, 2, 1, 1, 4, 1, 2, 3, - 1, 1, 1, 44, 66, 1, 3, 1, 4, 20, 3, 30, 66, 2, 2, 1, 1, 184, 54, 2, 7, 25, 6, 34, 63, 1, 1, - 3, 1, 59, 54, 2, 1, 71, 27, 2, 14, 21, 7, 185, 57, 103, 64, 31, 8, 2, 1, 2, 8, 1, 2, 1, 30, - 1, 2, 2, 2, 2, 4, 93, 8, 2, 46, 2, 6, 1, 1, 1, 2, 27, 51, 2, 10, 17, 72, 5, 1, 18, 73, 199, - 33, 31, 9, 1, 45, 1, 7, 1, 1, 49, 30, 2, 22, 1, 14, 73, 7, 1, 2, 1, 44, 3, 1, 1, 2, 1, 3, 1, - 1, 2, 2, 24, 6, 1, 2, 1, 37, 1, 2, 1, 4, 1, 1, 0, 23, 9, 17, 1, 41, 3, 3, 111, 1, 79, 0, - 102, 111, 17, 196, 0, 97, 15, 0, 17, 6, 25, 0, 5, 0, 0, 47, 0, 0, 7, 31, 17, 79, 17, 30, 18, - 48, 16, 4, 31, 21, 5, 19, 0, 45, 211, 64, 128, 75, 4, 57, 7, 17, 64, 2, 1, 1, 12, 2, 14, 0, - 8, 0, 41, 10, 0, 4, 1, 7, 1, 2, 1, 0, 15, 1, 29, 3, 2, 1, 14, 4, 8, 0, 0, 107, 5, 13, 3, 9, - 7, 10, 4, 1, 0, 85, 1, 71, 1, 2, 2, 1, 2, 2, 2, 4, 1, 12, 1, 1, 1, 7, 1, 65, 1, 4, 2, 8, 1, - 7, 1, 28, 1, 4, 1, 5, 1, 1, 3, 7, 1, 0, 2, 25, 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, 1, 31, 1, - 25, 1, 31, 1, 25, 1, 8, 0, 31, 6, 6, 213, 7, 1, 17, 2, 7, 1, 2, 1, 5, 5, 62, 33, 1, 112, 45, - 10, 7, 16, 1, 0, 30, 18, 44, 0, 28, 228, 30, 2, 1, 0, 7, 1, 4, 1, 2, 1, 15, 1, 197, 59, 68, - 3, 1, 3, 1, 0, 4, 1, 27, 1, 2, 1, 1, 2, 1, 1, 10, 1, 4, 1, 1, 1, 1, 6, 1, 4, 1, 1, 1, 1, 1, - 1, 3, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 4, 1, 7, 1, 4, 1, 4, 1, 1, 1, - 10, 1, 17, 5, 3, 1, 5, 1, 17, 0, 26, 6, 26, 6, 26, 0, 0, 32, 0, 6, 222, 2, 0, 14, 0, 15, 0, - 0, 0, 0, 0, 5, 0, 0, + static OFFSETS: [u8; 1511] = [ + 170, 1, 10, 1, 4, 1, 5, 23, 1, 31, 1, 0, 4, 12, 14, 5, 7, 1, 1, 1, 86, 1, 29, 18, 1, 2, 2, + 4, 1, 1, 6, 1, 1, 3, 1, 1, 1, 20, 1, 83, 1, 139, 8, 166, 1, 38, 2, 1, 6, 41, 39, 14, 1, 1, + 1, 2, 1, 2, 1, 1, 8, 27, 4, 4, 29, 11, 5, 56, 1, 7, 14, 102, 1, 8, 4, 8, 4, 3, 10, 3, 2, 1, + 16, 48, 13, 101, 24, 33, 9, 2, 4, 1, 5, 24, 2, 19, 19, 25, 7, 11, 5, 24, 1, 6, 8, 1, 8, 42, + 10, 12, 3, 7, 6, 76, 1, 16, 1, 3, 4, 15, 13, 19, 1, 8, 2, 2, 2, 22, 1, 7, 1, 1, 3, 4, 3, 8, + 2, 2, 2, 2, 1, 1, 8, 1, 4, 2, 1, 5, 12, 2, 10, 1, 4, 3, 1, 6, 4, 2, 2, 22, 1, 7, 1, 2, 1, 2, + 1, 2, 4, 5, 4, 2, 2, 2, 4, 1, 7, 4, 1, 1, 17, 6, 11, 3, 1, 9, 1, 3, 1, 22, 1, 7, 1, 2, 1, 5, + 3, 9, 1, 3, 1, 2, 3, 1, 15, 4, 21, 4, 4, 3, 1, 8, 2, 2, 2, 22, 1, 7, 1, 2, 1, 5, 3, 8, 2, 2, + 2, 2, 9, 2, 4, 2, 1, 5, 13, 1, 16, 2, 1, 6, 3, 3, 1, 4, 3, 2, 1, 1, 1, 2, 3, 2, 3, 3, 3, 12, + 4, 5, 3, 3, 1, 3, 3, 1, 6, 1, 40, 13, 1, 3, 1, 23, 1, 16, 3, 8, 1, 3, 1, 3, 8, 2, 1, 3, 2, + 1, 2, 4, 28, 4, 1, 8, 1, 3, 1, 23, 1, 10, 1, 5, 3, 8, 1, 3, 1, 3, 8, 2, 6, 2, 1, 4, 13, 3, + 12, 13, 1, 3, 1, 41, 2, 8, 1, 3, 1, 3, 1, 1, 5, 4, 7, 5, 22, 6, 1, 3, 1, 18, 3, 24, 1, 9, 1, + 1, 2, 7, 8, 6, 1, 1, 1, 8, 18, 2, 13, 58, 5, 7, 6, 1, 51, 2, 1, 1, 1, 5, 1, 24, 1, 1, 1, 19, + 1, 3, 2, 5, 1, 1, 6, 1, 14, 4, 32, 1, 63, 8, 1, 36, 4, 19, 4, 16, 1, 36, 67, 55, 1, 1, 2, 5, + 16, 64, 10, 4, 2, 38, 1, 1, 5, 1, 2, 43, 1, 0, 1, 4, 2, 7, 1, 1, 1, 4, 2, 41, 1, 4, 2, 33, + 1, 4, 2, 7, 1, 1, 1, 4, 2, 15, 1, 57, 1, 4, 2, 67, 37, 16, 16, 86, 2, 6, 3, 0, 2, 17, 1, 26, + 5, 75, 3, 11, 7, 20, 11, 21, 12, 20, 12, 13, 1, 3, 1, 2, 12, 52, 2, 19, 14, 1, 4, 1, 67, 89, + 7, 43, 5, 70, 10, 31, 1, 12, 4, 9, 23, 30, 2, 5, 11, 44, 4, 26, 54, 28, 4, 63, 2, 20, 50, 1, + 23, 2, 11, 3, 49, 52, 1, 15, 1, 8, 51, 42, 2, 4, 10, 44, 1, 11, 14, 55, 22, 3, 10, 36, 2, + 11, 5, 43, 2, 3, 41, 4, 1, 6, 1, 2, 3, 1, 5, 192, 19, 34, 11, 0, 2, 6, 2, 38, 2, 6, 2, 8, 1, + 1, 1, 1, 1, 1, 1, 31, 2, 53, 1, 7, 1, 1, 3, 3, 1, 7, 3, 4, 2, 6, 4, 13, 5, 3, 1, 7, 116, 1, + 13, 1, 16, 13, 101, 1, 4, 1, 2, 10, 1, 1, 3, 5, 6, 1, 1, 1, 1, 1, 1, 4, 1, 11, 2, 4, 5, 5, + 4, 1, 17, 41, 0, 52, 0, 229, 6, 4, 3, 2, 12, 38, 1, 1, 5, 1, 2, 56, 7, 1, 16, 23, 9, 7, 1, + 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 7, 1, 32, 47, 1, 0, 3, 25, 9, 7, 5, 2, 5, 4, 86, 6, 3, + 1, 90, 1, 4, 5, 43, 1, 94, 17, 32, 48, 16, 0, 0, 64, 0, 67, 46, 2, 0, 3, 16, 10, 2, 20, 47, + 5, 8, 3, 113, 39, 9, 2, 103, 2, 67, 2, 2, 1, 1, 1, 8, 21, 20, 1, 33, 24, 52, 12, 68, 1, 1, + 44, 6, 3, 1, 1, 3, 10, 33, 5, 35, 13, 29, 3, 51, 1, 12, 15, 1, 16, 16, 10, 5, 1, 55, 9, 14, + 18, 23, 3, 69, 1, 1, 1, 1, 24, 3, 2, 16, 2, 4, 11, 6, 2, 6, 2, 6, 9, 7, 1, 7, 1, 43, 1, 14, + 6, 123, 21, 0, 12, 23, 4, 49, 0, 0, 2, 106, 38, 7, 12, 5, 5, 12, 1, 13, 1, 5, 1, 1, 1, 2, 1, + 2, 1, 108, 33, 0, 18, 64, 2, 54, 40, 12, 116, 5, 1, 135, 36, 26, 6, 26, 11, 89, 3, 6, 2, 6, + 2, 6, 2, 3, 35, 12, 1, 26, 1, 19, 1, 2, 1, 15, 2, 14, 34, 123, 69, 53, 0, 29, 3, 49, 47, 32, + 13, 30, 5, 43, 5, 30, 2, 36, 4, 8, 1, 5, 42, 158, 18, 36, 4, 36, 4, 40, 8, 52, 12, 11, 1, + 15, 1, 7, 1, 2, 1, 11, 1, 15, 1, 7, 1, 2, 3, 52, 12, 0, 9, 22, 10, 8, 24, 6, 1, 42, 1, 9, + 69, 6, 2, 1, 1, 44, 1, 2, 3, 1, 2, 23, 10, 23, 9, 31, 65, 19, 1, 2, 10, 22, 10, 26, 70, 56, + 6, 2, 64, 4, 1, 2, 5, 8, 1, 3, 1, 29, 42, 29, 3, 29, 35, 8, 1, 28, 27, 54, 10, 22, 10, 19, + 13, 18, 110, 73, 55, 51, 13, 51, 13, 40, 34, 28, 3, 1, 5, 23, 250, 42, 1, 2, 3, 2, 16, 3, + 55, 1, 3, 29, 10, 1, 8, 22, 42, 18, 46, 21, 27, 23, 9, 70, 43, 5, 10, 57, 9, 1, 13, 25, 23, + 51, 17, 4, 8, 35, 3, 1, 9, 64, 1, 4, 9, 2, 10, 1, 1, 1, 35, 18, 1, 34, 2, 1, 6, 4, 62, 7, 1, + 1, 1, 4, 1, 15, 1, 10, 7, 57, 23, 4, 1, 8, 2, 2, 2, 22, 1, 7, 1, 2, 1, 5, 3, 8, 2, 2, 2, 2, + 3, 1, 6, 1, 5, 7, 28, 10, 1, 1, 2, 1, 1, 38, 1, 10, 1, 1, 2, 1, 1, 4, 1, 2, 3, 1, 1, 1, 44, + 66, 1, 3, 1, 4, 20, 3, 30, 66, 2, 2, 1, 1, 184, 54, 2, 7, 25, 6, 34, 63, 1, 1, 3, 1, 59, 54, + 2, 1, 71, 27, 2, 14, 21, 7, 185, 57, 103, 64, 31, 8, 2, 1, 2, 8, 1, 2, 1, 30, 1, 2, 2, 2, 2, + 4, 93, 8, 2, 46, 2, 6, 1, 1, 1, 2, 27, 51, 2, 10, 17, 72, 5, 1, 18, 73, 199, 33, 31, 9, 1, + 45, 1, 7, 1, 1, 49, 30, 2, 22, 1, 14, 73, 7, 1, 2, 1, 44, 3, 1, 1, 2, 1, 3, 1, 1, 2, 2, 24, + 6, 1, 2, 1, 37, 1, 2, 1, 4, 1, 1, 0, 23, 9, 17, 1, 41, 3, 3, 111, 1, 79, 0, 102, 111, 17, + 196, 0, 97, 15, 0, 17, 6, 25, 0, 5, 0, 0, 47, 0, 0, 7, 31, 17, 79, 17, 30, 18, 48, 16, 4, + 31, 21, 5, 19, 0, 45, 211, 64, 128, 75, 4, 57, 7, 17, 64, 2, 1, 1, 12, 2, 14, 0, 8, 0, 41, + 10, 0, 4, 1, 7, 1, 2, 1, 0, 15, 1, 29, 3, 2, 1, 14, 4, 8, 0, 0, 107, 5, 13, 3, 9, 7, 10, 4, + 1, 0, 85, 1, 71, 1, 2, 2, 1, 2, 2, 2, 4, 1, 12, 1, 1, 1, 7, 1, 65, 1, 4, 2, 8, 1, 7, 1, 28, + 1, 4, 1, 5, 1, 1, 3, 7, 1, 0, 2, 25, 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, 1, 31, + 1, 25, 1, 8, 0, 31, 6, 6, 213, 7, 1, 17, 2, 7, 1, 2, 1, 5, 5, 62, 33, 1, 112, 45, 10, 7, 16, + 1, 0, 30, 18, 44, 0, 28, 228, 30, 2, 1, 0, 7, 1, 4, 1, 2, 1, 15, 1, 197, 59, 68, 3, 1, 3, 1, + 0, 4, 1, 27, 1, 2, 1, 1, 2, 1, 1, 10, 1, 4, 1, 1, 1, 1, 6, 1, 4, 1, 1, 1, 1, 1, 1, 3, 1, 2, + 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 4, 1, 7, 1, 4, 1, 4, 1, 1, 1, 10, 1, 17, + 5, 3, 1, 5, 1, 17, 0, 26, 6, 26, 6, 26, 0, 0, 32, 0, 6, 222, 2, 0, 14, 0, 15, 0, 0, 0, 0, 0, + 5, 0, 0, ]; + #[inline] pub fn lookup(c: char) -> bool { + debug_assert!(!c.is_ascii()); + (c as u32) >= 0xaa && lookup_slow(c) + } + + #[inline(never)] + fn lookup_slow(c: char) -> bool { const { assert!(SHORT_OFFSET_RUNS.last().unwrap().0 > char::MAX as u32); let mut i = 0; @@ -254,62 +260,69 @@ pub mod case_ignorable { use super::ShortOffsetRunHeader; static SHORT_OFFSET_RUNS: [ShortOffsetRunHeader; 37] = [ - ShortOffsetRunHeader::new(0, 688), ShortOffsetRunHeader::new(21, 4957), - ShortOffsetRunHeader::new(273, 5906), ShortOffsetRunHeader::new(275, 8125), - ShortOffsetRunHeader::new(385, 11388), ShortOffsetRunHeader::new(419, 12293), - ShortOffsetRunHeader::new(431, 40981), ShortOffsetRunHeader::new(443, 42232), - ShortOffsetRunHeader::new(445, 42508), ShortOffsetRunHeader::new(447, 64286), - ShortOffsetRunHeader::new(543, 65024), ShortOffsetRunHeader::new(547, 66045), - ShortOffsetRunHeader::new(577, 67456), ShortOffsetRunHeader::new(583, 68097), - ShortOffsetRunHeader::new(589, 68900), ShortOffsetRunHeader::new(601, 69291), - ShortOffsetRunHeader::new(609, 71727), ShortOffsetRunHeader::new(733, 71995), - ShortOffsetRunHeader::new(737, 72752), ShortOffsetRunHeader::new(765, 73459), - ShortOffsetRunHeader::new(795, 78896), ShortOffsetRunHeader::new(807, 90398), - ShortOffsetRunHeader::new(811, 92912), ShortOffsetRunHeader::new(815, 93504), - ShortOffsetRunHeader::new(821, 94031), ShortOffsetRunHeader::new(825, 110576), - ShortOffsetRunHeader::new(833, 113821), ShortOffsetRunHeader::new(839, 118528), - ShortOffsetRunHeader::new(843, 119143), ShortOffsetRunHeader::new(847, 121344), - ShortOffsetRunHeader::new(857, 122880), ShortOffsetRunHeader::new(869, 123566), - ShortOffsetRunHeader::new(885, 124139), ShortOffsetRunHeader::new(889, 125136), - ShortOffsetRunHeader::new(893, 127995), ShortOffsetRunHeader::new(897, 917505), - ShortOffsetRunHeader::new(899, 2032112), + ShortOffsetRunHeader::new(0, 688), ShortOffsetRunHeader::new(11, 4957), + ShortOffsetRunHeader::new(263, 5906), ShortOffsetRunHeader::new(265, 8125), + ShortOffsetRunHeader::new(375, 11388), ShortOffsetRunHeader::new(409, 12293), + ShortOffsetRunHeader::new(421, 40981), ShortOffsetRunHeader::new(433, 42232), + ShortOffsetRunHeader::new(435, 42508), ShortOffsetRunHeader::new(437, 64286), + ShortOffsetRunHeader::new(533, 65024), ShortOffsetRunHeader::new(537, 66045), + ShortOffsetRunHeader::new(567, 67456), ShortOffsetRunHeader::new(573, 68097), + ShortOffsetRunHeader::new(579, 68900), ShortOffsetRunHeader::new(591, 69291), + ShortOffsetRunHeader::new(599, 71727), ShortOffsetRunHeader::new(723, 71995), + ShortOffsetRunHeader::new(727, 72752), ShortOffsetRunHeader::new(755, 73459), + ShortOffsetRunHeader::new(785, 78896), ShortOffsetRunHeader::new(797, 90398), + ShortOffsetRunHeader::new(801, 92912), ShortOffsetRunHeader::new(805, 93504), + ShortOffsetRunHeader::new(811, 94031), ShortOffsetRunHeader::new(815, 110576), + ShortOffsetRunHeader::new(823, 113821), ShortOffsetRunHeader::new(829, 118528), + ShortOffsetRunHeader::new(833, 119143), ShortOffsetRunHeader::new(837, 121344), + ShortOffsetRunHeader::new(847, 122880), ShortOffsetRunHeader::new(859, 123566), + ShortOffsetRunHeader::new(875, 124139), ShortOffsetRunHeader::new(879, 125136), + ShortOffsetRunHeader::new(883, 127995), ShortOffsetRunHeader::new(887, 917505), + ShortOffsetRunHeader::new(889, 2032112), ]; - static OFFSETS: [u8; 905] = [ - 39, 1, 6, 1, 11, 1, 35, 1, 1, 1, 71, 1, 4, 1, 1, 1, 4, 1, 2, 2, 0, 192, 4, 2, 4, 1, 9, 2, - 1, 1, 251, 7, 207, 1, 5, 1, 49, 45, 1, 1, 1, 2, 1, 2, 1, 1, 44, 1, 11, 6, 10, 11, 1, 1, 35, - 1, 10, 21, 16, 1, 101, 8, 1, 10, 1, 4, 33, 1, 1, 1, 30, 27, 91, 11, 58, 11, 4, 1, 2, 1, 24, - 24, 43, 3, 44, 1, 7, 2, 5, 9, 41, 58, 55, 1, 1, 1, 4, 8, 4, 1, 3, 7, 10, 2, 13, 1, 15, 1, - 58, 1, 4, 4, 8, 1, 20, 2, 26, 1, 2, 2, 57, 1, 4, 2, 4, 2, 2, 3, 3, 1, 30, 2, 3, 1, 11, 2, - 57, 1, 4, 5, 1, 2, 4, 1, 20, 2, 22, 6, 1, 1, 58, 1, 2, 1, 1, 4, 8, 1, 7, 2, 11, 2, 30, 1, - 61, 1, 12, 1, 50, 1, 3, 1, 55, 1, 1, 3, 5, 3, 1, 4, 7, 2, 11, 2, 29, 1, 58, 1, 2, 1, 6, 1, - 5, 2, 20, 2, 28, 2, 57, 2, 4, 4, 8, 1, 20, 2, 29, 1, 72, 1, 7, 3, 1, 1, 90, 1, 2, 7, 11, 9, - 98, 1, 2, 9, 9, 1, 1, 7, 73, 2, 27, 1, 1, 1, 1, 1, 55, 14, 1, 5, 1, 2, 5, 11, 1, 36, 9, 1, - 102, 4, 1, 6, 1, 2, 2, 2, 25, 2, 4, 3, 16, 4, 13, 1, 2, 2, 6, 1, 15, 1, 94, 1, 0, 3, 0, 3, - 29, 2, 30, 2, 30, 2, 64, 2, 1, 7, 8, 1, 2, 11, 3, 1, 5, 1, 45, 5, 51, 1, 65, 2, 34, 1, 118, - 3, 4, 2, 9, 1, 6, 3, 219, 2, 2, 1, 58, 1, 1, 7, 1, 1, 1, 1, 2, 8, 6, 10, 2, 1, 39, 1, 8, 31, - 49, 4, 48, 1, 1, 5, 1, 1, 5, 1, 40, 9, 12, 2, 32, 4, 2, 2, 1, 3, 56, 1, 1, 2, 3, 1, 1, 3, - 58, 8, 2, 2, 64, 6, 82, 3, 1, 13, 1, 7, 4, 1, 6, 1, 3, 2, 50, 63, 13, 1, 34, 101, 0, 1, 1, - 3, 11, 3, 13, 3, 13, 3, 13, 2, 12, 5, 8, 2, 10, 1, 2, 1, 2, 5, 49, 5, 1, 10, 1, 1, 13, 1, - 16, 13, 51, 33, 0, 2, 113, 3, 125, 1, 15, 1, 96, 32, 47, 1, 0, 1, 36, 4, 3, 5, 5, 1, 93, 6, - 93, 3, 0, 1, 0, 6, 0, 1, 98, 4, 1, 10, 1, 1, 28, 4, 80, 2, 14, 34, 78, 1, 23, 3, 103, 3, 3, - 2, 8, 1, 3, 1, 4, 1, 25, 2, 5, 1, 151, 2, 26, 18, 13, 1, 38, 8, 25, 11, 46, 3, 48, 1, 2, 4, - 2, 2, 17, 1, 21, 2, 66, 6, 2, 2, 2, 2, 12, 1, 8, 1, 35, 1, 11, 1, 51, 1, 1, 3, 2, 2, 5, 2, - 1, 1, 27, 1, 14, 2, 5, 2, 1, 1, 100, 5, 9, 3, 121, 1, 2, 1, 4, 1, 0, 1, 147, 17, 0, 16, 3, - 1, 12, 16, 34, 1, 2, 1, 169, 1, 7, 1, 6, 1, 11, 1, 35, 1, 1, 1, 47, 1, 45, 2, 67, 1, 21, 3, - 0, 1, 226, 1, 149, 5, 0, 6, 1, 42, 1, 9, 0, 3, 1, 2, 5, 4, 40, 3, 4, 1, 165, 2, 0, 4, 38, 1, - 26, 5, 1, 1, 0, 2, 79, 4, 70, 11, 49, 4, 123, 1, 54, 15, 41, 1, 2, 2, 10, 3, 49, 4, 2, 2, 2, - 1, 4, 1, 10, 1, 50, 3, 36, 5, 1, 8, 62, 1, 12, 2, 52, 9, 10, 4, 2, 1, 95, 3, 2, 1, 1, 2, 6, - 1, 2, 1, 157, 1, 3, 8, 21, 2, 57, 2, 3, 1, 37, 7, 3, 5, 70, 6, 13, 1, 1, 1, 1, 1, 14, 2, 85, - 8, 2, 3, 1, 1, 23, 1, 84, 6, 1, 1, 4, 2, 1, 2, 238, 4, 6, 2, 1, 2, 27, 2, 85, 8, 2, 1, 1, 2, - 106, 1, 1, 1, 2, 6, 1, 1, 101, 1, 1, 1, 2, 4, 1, 5, 0, 9, 1, 2, 0, 2, 1, 1, 4, 1, 144, 4, 2, - 2, 4, 1, 32, 10, 40, 6, 2, 4, 8, 1, 9, 6, 2, 3, 46, 13, 1, 2, 0, 7, 1, 6, 1, 1, 82, 22, 2, - 7, 1, 2, 1, 2, 122, 6, 3, 1, 1, 2, 1, 7, 1, 1, 72, 2, 3, 1, 1, 1, 0, 2, 11, 2, 52, 5, 5, 1, - 1, 1, 23, 1, 0, 17, 6, 15, 0, 12, 3, 3, 0, 5, 59, 7, 9, 4, 0, 3, 40, 2, 0, 1, 63, 17, 64, 2, - 1, 2, 0, 4, 1, 7, 1, 2, 0, 2, 1, 4, 0, 46, 2, 23, 0, 3, 9, 16, 2, 7, 30, 4, 148, 3, 0, 55, - 4, 50, 8, 1, 14, 1, 22, 5, 1, 15, 0, 7, 1, 17, 2, 7, 1, 2, 1, 5, 5, 62, 33, 1, 160, 14, 0, - 1, 61, 4, 0, 5, 254, 2, 0, 7, 109, 8, 0, 5, 0, 1, 30, 96, 128, 240, 0, + static OFFSETS: [u8; 895] = [ + 168, 1, 4, 1, 1, 1, 4, 1, 2, 2, 0, 192, 4, 2, 4, 1, 9, 2, 1, 1, 251, 7, 207, 1, 5, 1, 49, + 45, 1, 1, 1, 2, 1, 2, 1, 1, 44, 1, 11, 6, 10, 11, 1, 1, 35, 1, 10, 21, 16, 1, 101, 8, 1, 10, + 1, 4, 33, 1, 1, 1, 30, 27, 91, 11, 58, 11, 4, 1, 2, 1, 24, 24, 43, 3, 44, 1, 7, 2, 5, 9, 41, + 58, 55, 1, 1, 1, 4, 8, 4, 1, 3, 7, 10, 2, 13, 1, 15, 1, 58, 1, 4, 4, 8, 1, 20, 2, 26, 1, 2, + 2, 57, 1, 4, 2, 4, 2, 2, 3, 3, 1, 30, 2, 3, 1, 11, 2, 57, 1, 4, 5, 1, 2, 4, 1, 20, 2, 22, 6, + 1, 1, 58, 1, 2, 1, 1, 4, 8, 1, 7, 2, 11, 2, 30, 1, 61, 1, 12, 1, 50, 1, 3, 1, 55, 1, 1, 3, + 5, 3, 1, 4, 7, 2, 11, 2, 29, 1, 58, 1, 2, 1, 6, 1, 5, 2, 20, 2, 28, 2, 57, 2, 4, 4, 8, 1, + 20, 2, 29, 1, 72, 1, 7, 3, 1, 1, 90, 1, 2, 7, 11, 9, 98, 1, 2, 9, 9, 1, 1, 7, 73, 2, 27, 1, + 1, 1, 1, 1, 55, 14, 1, 5, 1, 2, 5, 11, 1, 36, 9, 1, 102, 4, 1, 6, 1, 2, 2, 2, 25, 2, 4, 3, + 16, 4, 13, 1, 2, 2, 6, 1, 15, 1, 94, 1, 0, 3, 0, 3, 29, 2, 30, 2, 30, 2, 64, 2, 1, 7, 8, 1, + 2, 11, 3, 1, 5, 1, 45, 5, 51, 1, 65, 2, 34, 1, 118, 3, 4, 2, 9, 1, 6, 3, 219, 2, 2, 1, 58, + 1, 1, 7, 1, 1, 1, 1, 2, 8, 6, 10, 2, 1, 39, 1, 8, 31, 49, 4, 48, 1, 1, 5, 1, 1, 5, 1, 40, 9, + 12, 2, 32, 4, 2, 2, 1, 3, 56, 1, 1, 2, 3, 1, 1, 3, 58, 8, 2, 2, 64, 6, 82, 3, 1, 13, 1, 7, + 4, 1, 6, 1, 3, 2, 50, 63, 13, 1, 34, 101, 0, 1, 1, 3, 11, 3, 13, 3, 13, 3, 13, 2, 12, 5, 8, + 2, 10, 1, 2, 1, 2, 5, 49, 5, 1, 10, 1, 1, 13, 1, 16, 13, 51, 33, 0, 2, 113, 3, 125, 1, 15, + 1, 96, 32, 47, 1, 0, 1, 36, 4, 3, 5, 5, 1, 93, 6, 93, 3, 0, 1, 0, 6, 0, 1, 98, 4, 1, 10, 1, + 1, 28, 4, 80, 2, 14, 34, 78, 1, 23, 3, 103, 3, 3, 2, 8, 1, 3, 1, 4, 1, 25, 2, 5, 1, 151, 2, + 26, 18, 13, 1, 38, 8, 25, 11, 46, 3, 48, 1, 2, 4, 2, 2, 17, 1, 21, 2, 66, 6, 2, 2, 2, 2, 12, + 1, 8, 1, 35, 1, 11, 1, 51, 1, 1, 3, 2, 2, 5, 2, 1, 1, 27, 1, 14, 2, 5, 2, 1, 1, 100, 5, 9, + 3, 121, 1, 2, 1, 4, 1, 0, 1, 147, 17, 0, 16, 3, 1, 12, 16, 34, 1, 2, 1, 169, 1, 7, 1, 6, 1, + 11, 1, 35, 1, 1, 1, 47, 1, 45, 2, 67, 1, 21, 3, 0, 1, 226, 1, 149, 5, 0, 6, 1, 42, 1, 9, 0, + 3, 1, 2, 5, 4, 40, 3, 4, 1, 165, 2, 0, 4, 38, 1, 26, 5, 1, 1, 0, 2, 79, 4, 70, 11, 49, 4, + 123, 1, 54, 15, 41, 1, 2, 2, 10, 3, 49, 4, 2, 2, 2, 1, 4, 1, 10, 1, 50, 3, 36, 5, 1, 8, 62, + 1, 12, 2, 52, 9, 10, 4, 2, 1, 95, 3, 2, 1, 1, 2, 6, 1, 2, 1, 157, 1, 3, 8, 21, 2, 57, 2, 3, + 1, 37, 7, 3, 5, 70, 6, 13, 1, 1, 1, 1, 1, 14, 2, 85, 8, 2, 3, 1, 1, 23, 1, 84, 6, 1, 1, 4, + 2, 1, 2, 238, 4, 6, 2, 1, 2, 27, 2, 85, 8, 2, 1, 1, 2, 106, 1, 1, 1, 2, 6, 1, 1, 101, 1, 1, + 1, 2, 4, 1, 5, 0, 9, 1, 2, 0, 2, 1, 1, 4, 1, 144, 4, 2, 2, 4, 1, 32, 10, 40, 6, 2, 4, 8, 1, + 9, 6, 2, 3, 46, 13, 1, 2, 0, 7, 1, 6, 1, 1, 82, 22, 2, 7, 1, 2, 1, 2, 122, 6, 3, 1, 1, 2, 1, + 7, 1, 1, 72, 2, 3, 1, 1, 1, 0, 2, 11, 2, 52, 5, 5, 1, 1, 1, 23, 1, 0, 17, 6, 15, 0, 12, 3, + 3, 0, 5, 59, 7, 9, 4, 0, 3, 40, 2, 0, 1, 63, 17, 64, 2, 1, 2, 0, 4, 1, 7, 1, 2, 0, 2, 1, 4, + 0, 46, 2, 23, 0, 3, 9, 16, 2, 7, 30, 4, 148, 3, 0, 55, 4, 50, 8, 1, 14, 1, 22, 5, 1, 15, 0, + 7, 1, 17, 2, 7, 1, 2, 1, 5, 5, 62, 33, 1, 160, 14, 0, 1, 61, 4, 0, 5, 254, 2, 0, 7, 109, 8, + 0, 5, 0, 1, 30, 96, 128, 240, 0, ]; + #[inline] pub fn lookup(c: char) -> bool { + debug_assert!(!c.is_ascii()); + (c as u32) >= 0xa8 && lookup_slow(c) + } + + #[inline(never)] + fn lookup_slow(c: char) -> bool { const { assert!(SHORT_OFFSET_RUNS.last().unwrap().0 > char::MAX as u32); let mut i = 0; @@ -329,33 +342,40 @@ pub mod cased { use super::ShortOffsetRunHeader; static SHORT_OFFSET_RUNS: [ShortOffsetRunHeader; 22] = [ - ShortOffsetRunHeader::new(0, 4256), ShortOffsetRunHeader::new(55, 5024), - ShortOffsetRunHeader::new(65, 7296), ShortOffsetRunHeader::new(69, 7958), - ShortOffsetRunHeader::new(78, 9398), ShortOffsetRunHeader::new(153, 11264), - ShortOffsetRunHeader::new(155, 42560), ShortOffsetRunHeader::new(167, 43824), - ShortOffsetRunHeader::new(187, 64256), ShortOffsetRunHeader::new(193, 65313), - ShortOffsetRunHeader::new(197, 66560), ShortOffsetRunHeader::new(201, 67456), - ShortOffsetRunHeader::new(223, 68736), ShortOffsetRunHeader::new(231, 71840), - ShortOffsetRunHeader::new(239, 93760), ShortOffsetRunHeader::new(241, 119808), - ShortOffsetRunHeader::new(243, 120486), ShortOffsetRunHeader::new(280, 122624), - ShortOffsetRunHeader::new(303, 122928), ShortOffsetRunHeader::new(309, 125184), - ShortOffsetRunHeader::new(311, 127280), ShortOffsetRunHeader::new(313, 1241482), + ShortOffsetRunHeader::new(0, 4256), ShortOffsetRunHeader::new(51, 5024), + ShortOffsetRunHeader::new(61, 7296), ShortOffsetRunHeader::new(65, 7958), + ShortOffsetRunHeader::new(74, 9398), ShortOffsetRunHeader::new(149, 11264), + ShortOffsetRunHeader::new(151, 42560), ShortOffsetRunHeader::new(163, 43824), + ShortOffsetRunHeader::new(183, 64256), ShortOffsetRunHeader::new(189, 65313), + ShortOffsetRunHeader::new(193, 66560), ShortOffsetRunHeader::new(197, 67456), + ShortOffsetRunHeader::new(219, 68736), ShortOffsetRunHeader::new(227, 71840), + ShortOffsetRunHeader::new(235, 93760), ShortOffsetRunHeader::new(237, 119808), + ShortOffsetRunHeader::new(239, 120486), ShortOffsetRunHeader::new(276, 122624), + ShortOffsetRunHeader::new(299, 122928), ShortOffsetRunHeader::new(305, 125184), + ShortOffsetRunHeader::new(307, 127280), ShortOffsetRunHeader::new(309, 1241482), ]; - static OFFSETS: [u8; 319] = [ - 65, 26, 6, 26, 47, 1, 10, 1, 4, 1, 5, 23, 1, 31, 1, 195, 1, 4, 4, 208, 1, 36, 7, 2, 30, 5, - 96, 1, 42, 4, 2, 2, 2, 4, 1, 1, 6, 1, 1, 3, 1, 1, 1, 20, 1, 83, 1, 139, 8, 166, 1, 38, 9, - 41, 0, 38, 1, 1, 5, 1, 2, 43, 1, 4, 0, 86, 2, 6, 0, 11, 5, 43, 2, 3, 64, 192, 64, 0, 2, 6, - 2, 38, 2, 6, 2, 8, 1, 1, 1, 1, 1, 1, 1, 31, 2, 53, 1, 7, 1, 1, 3, 3, 1, 7, 3, 4, 2, 6, 4, - 13, 5, 3, 1, 7, 116, 1, 13, 1, 16, 13, 101, 1, 4, 1, 2, 10, 1, 1, 3, 5, 6, 1, 1, 1, 1, 1, 1, - 4, 1, 6, 4, 1, 2, 4, 5, 5, 4, 1, 17, 32, 3, 2, 0, 52, 0, 229, 6, 4, 3, 2, 12, 38, 1, 1, 5, - 1, 0, 46, 18, 30, 132, 102, 3, 4, 1, 62, 2, 2, 1, 1, 1, 8, 21, 5, 1, 3, 0, 43, 1, 14, 6, 80, - 0, 7, 12, 5, 0, 26, 6, 26, 0, 80, 96, 36, 4, 36, 116, 11, 1, 15, 1, 7, 1, 2, 1, 11, 1, 15, - 1, 7, 1, 2, 0, 1, 2, 3, 1, 42, 1, 9, 0, 51, 13, 51, 93, 22, 10, 22, 0, 64, 0, 64, 0, 85, 1, - 71, 1, 2, 2, 1, 2, 2, 2, 4, 1, 12, 1, 1, 1, 7, 1, 65, 1, 4, 2, 8, 1, 7, 1, 28, 1, 4, 1, 5, - 1, 1, 3, 7, 1, 0, 2, 25, 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, 1, - 8, 0, 10, 1, 20, 6, 6, 0, 62, 0, 68, 0, 26, 6, 26, 6, 26, 0, + static OFFSETS: [u8; 315] = [ + 170, 1, 10, 1, 4, 1, 5, 23, 1, 31, 1, 195, 1, 4, 4, 208, 1, 36, 7, 2, 30, 5, 96, 1, 42, 4, + 2, 2, 2, 4, 1, 1, 6, 1, 1, 3, 1, 1, 1, 20, 1, 83, 1, 139, 8, 166, 1, 38, 9, 41, 0, 38, 1, 1, + 5, 1, 2, 43, 1, 4, 0, 86, 2, 6, 0, 11, 5, 43, 2, 3, 64, 192, 64, 0, 2, 6, 2, 38, 2, 6, 2, 8, + 1, 1, 1, 1, 1, 1, 1, 31, 2, 53, 1, 7, 1, 1, 3, 3, 1, 7, 3, 4, 2, 6, 4, 13, 5, 3, 1, 7, 116, + 1, 13, 1, 16, 13, 101, 1, 4, 1, 2, 10, 1, 1, 3, 5, 6, 1, 1, 1, 1, 1, 1, 4, 1, 6, 4, 1, 2, 4, + 5, 5, 4, 1, 17, 32, 3, 2, 0, 52, 0, 229, 6, 4, 3, 2, 12, 38, 1, 1, 5, 1, 0, 46, 18, 30, 132, + 102, 3, 4, 1, 62, 2, 2, 1, 1, 1, 8, 21, 5, 1, 3, 0, 43, 1, 14, 6, 80, 0, 7, 12, 5, 0, 26, 6, + 26, 0, 80, 96, 36, 4, 36, 116, 11, 1, 15, 1, 7, 1, 2, 1, 11, 1, 15, 1, 7, 1, 2, 0, 1, 2, 3, + 1, 42, 1, 9, 0, 51, 13, 51, 93, 22, 10, 22, 0, 64, 0, 64, 0, 85, 1, 71, 1, 2, 2, 1, 2, 2, 2, + 4, 1, 12, 1, 1, 1, 7, 1, 65, 1, 4, 2, 8, 1, 7, 1, 28, 1, 4, 1, 5, 1, 1, 3, 7, 1, 0, 2, 25, + 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, 1, 31, 1, 25, 1, 8, 0, 10, 1, 20, 6, 6, 0, + 62, 0, 68, 0, 26, 6, 26, 6, 26, 0, ]; + #[inline] pub fn lookup(c: char) -> bool { + debug_assert!(!c.is_ascii()); + (c as u32) >= 0xaa && lookup_slow(c) + } + + #[inline(never)] + fn lookup_slow(c: char) -> bool { const { assert!(SHORT_OFFSET_RUNS.last().unwrap().0 > char::MAX as u32); let mut i = 0; @@ -424,6 +444,7 @@ pub mod grapheme_extend { ]; #[inline] pub fn lookup(c: char) -> bool { + debug_assert!(!c.is_ascii()); (c as u32) >= 0x300 && lookup_slow(c) } @@ -446,7 +467,7 @@ pub mod grapheme_extend { #[rustfmt::skip] pub mod lowercase { static BITSET_CHUNKS_MAP: [u8; 123] = [ - 14, 17, 0, 0, 9, 0, 0, 12, 13, 10, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 12, 17, 0, 0, 9, 0, 0, 13, 14, 10, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 1, 0, 15, 0, 8, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, @@ -458,37 +479,37 @@ pub mod lowercase { [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 14, 56, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 43, 0, 52, 48, 50, 33], - [0, 0, 0, 0, 10, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 9, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 3, 0, 16, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27], [0, 0, 0, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 34, 17, 23, 53, 54, 49, 47, 7, 35, 42, 0, 28, 12, 31], [0, 0, 46, 0, 56, 56, 56, 0, 22, 22, 69, 22, 36, 25, 24, 37], [0, 5, 70, 0, 29, 15, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 66, 34, 17, 23, 53, 54, 49, 47, 8, 35, 42, 0, 28, 13, 31], - [11, 60, 0, 6, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 32, 0], + [10, 60, 0, 6, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 32, 0], [16, 26, 22, 38, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [16, 51, 2, 21, 68, 9, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [16, 51, 2, 21, 68, 8, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0], [16, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [65, 41, 55, 12, 77, 63, 18, 1, 7, 64, 76, 20, 73, 74, 4, 45], + [65, 41, 55, 11, 66, 63, 18, 13, 1, 64, 76, 20, 73, 74, 4, 45], ]; static BITSET_CANONICAL: [u64; 56] = [ 0b0000000000000000000000000000000000000000000000000000000000000000, - 0b1111111111111111110000000000000000000000000011111111111111111111, + 0b0000111111111111111111111111110000000000000000000000000011111111, 0b1010101010101010101010101010101010101010101010101010100000000010, 0b0000000000000111111111111111111111111111111111111111111111111111, 0b1111111111111111111111000000000000000000000000001111110111111111, 0b1000000000000010000000000000000000000000000000000000000000000000, 0b0000111111111111111111111111111111111111000000000000000000000000, - 0b0000111111111111111111111111110000000000000000000000000011111111, 0b1111111111111111111111111111111111111111111111111010101010000101, 0b1111111111111111111111111111111100000000000000000000000000000000, 0b1111111111111111111111111111110000000000000000000000000000000000, 0b1111111111111111111111110000000000000000000000000000000000000000, 0b1111111111111111111111000000000000000000000000001111111111101111, 0b1111111111111111111100000000000000000000000000010000000000000000, + 0b1111111111111111110000000000000000000000000011111111111111111111, 0b1111111111111111000000111111111111110111111111111111111111111111, 0b1111111111111111000000000000000000000000000000000100001111000000, 0b1111111111111111000000000000000000000000000000000000000000000000, @@ -532,13 +553,15 @@ pub mod lowercase { 0b1110011001010001001011010010101001001110001001000011000100101001, 0b1110101111000000000000000000000000001111111111111111111111111100, ]; - static BITSET_MAPPING: [(u8, u8); 22] = [ - (0, 64), (1, 188), (1, 186), (1, 183), (1, 176), (1, 109), (1, 124), (1, 126), (1, 66), - (1, 70), (1, 77), (2, 146), (2, 144), (2, 83), (3, 93), (3, 147), (3, 133), (4, 12), (4, 6), - (5, 187), (6, 78), (7, 132), + static BITSET_MAPPING: [(u8, u8); 21] = [ + (0, 64), (1, 184), (1, 182), (1, 179), (1, 172), (1, 161), (1, 146), (1, 144), (1, 140), + (1, 136), (1, 132), (2, 146), (2, 144), (2, 83), (3, 93), (3, 147), (3, 133), (4, 12), + (4, 6), (5, 187), (6, 78), ]; pub const fn lookup(c: char) -> bool { + debug_assert!(!c.is_ascii()); + (c as u32) >= 0xaa && super::bitset_search( c as u32, &BITSET_CHUNKS_MAP, @@ -554,43 +577,50 @@ pub mod n { use super::ShortOffsetRunHeader; static SHORT_OFFSET_RUNS: [ShortOffsetRunHeader; 42] = [ - ShortOffsetRunHeader::new(0, 1632), ShortOffsetRunHeader::new(9, 2406), - ShortOffsetRunHeader::new(15, 4160), ShortOffsetRunHeader::new(49, 4969), - ShortOffsetRunHeader::new(53, 5870), ShortOffsetRunHeader::new(55, 6470), - ShortOffsetRunHeader::new(63, 8304), ShortOffsetRunHeader::new(79, 9312), - ShortOffsetRunHeader::new(89, 10102), ShortOffsetRunHeader::new(93, 11517), - ShortOffsetRunHeader::new(95, 12295), ShortOffsetRunHeader::new(97, 12690), - ShortOffsetRunHeader::new(103, 42528), ShortOffsetRunHeader::new(115, 43056), - ShortOffsetRunHeader::new(119, 44016), ShortOffsetRunHeader::new(131, 65296), - ShortOffsetRunHeader::new(133, 65799), ShortOffsetRunHeader::new(135, 66273), - ShortOffsetRunHeader::new(141, 67672), ShortOffsetRunHeader::new(153, 68858), - ShortOffsetRunHeader::new(183, 69216), ShortOffsetRunHeader::new(189, 70736), - ShortOffsetRunHeader::new(209, 71248), ShortOffsetRunHeader::new(213, 71904), - ShortOffsetRunHeader::new(221, 72688), ShortOffsetRunHeader::new(225, 73552), - ShortOffsetRunHeader::new(233, 74752), ShortOffsetRunHeader::new(237, 90416), - ShortOffsetRunHeader::new(239, 92768), ShortOffsetRunHeader::new(241, 93552), - ShortOffsetRunHeader::new(249, 93824), ShortOffsetRunHeader::new(251, 118000), - ShortOffsetRunHeader::new(253, 119488), ShortOffsetRunHeader::new(255, 120782), - ShortOffsetRunHeader::new(261, 123200), ShortOffsetRunHeader::new(263, 123632), - ShortOffsetRunHeader::new(265, 124144), ShortOffsetRunHeader::new(267, 125127), - ShortOffsetRunHeader::new(271, 126065), ShortOffsetRunHeader::new(275, 127232), - ShortOffsetRunHeader::new(285, 130032), ShortOffsetRunHeader::new(287, 1244154), + ShortOffsetRunHeader::new(0, 1632), ShortOffsetRunHeader::new(7, 2406), + ShortOffsetRunHeader::new(13, 4160), ShortOffsetRunHeader::new(47, 4969), + ShortOffsetRunHeader::new(51, 5870), ShortOffsetRunHeader::new(53, 6470), + ShortOffsetRunHeader::new(61, 8304), ShortOffsetRunHeader::new(77, 9312), + ShortOffsetRunHeader::new(87, 10102), ShortOffsetRunHeader::new(91, 11517), + ShortOffsetRunHeader::new(93, 12295), ShortOffsetRunHeader::new(95, 12690), + ShortOffsetRunHeader::new(101, 42528), ShortOffsetRunHeader::new(113, 43056), + ShortOffsetRunHeader::new(117, 44016), ShortOffsetRunHeader::new(129, 65296), + ShortOffsetRunHeader::new(131, 65799), ShortOffsetRunHeader::new(133, 66273), + ShortOffsetRunHeader::new(139, 67672), ShortOffsetRunHeader::new(151, 68858), + ShortOffsetRunHeader::new(181, 69216), ShortOffsetRunHeader::new(187, 70736), + ShortOffsetRunHeader::new(207, 71248), ShortOffsetRunHeader::new(211, 71904), + ShortOffsetRunHeader::new(219, 72688), ShortOffsetRunHeader::new(223, 73552), + ShortOffsetRunHeader::new(231, 74752), ShortOffsetRunHeader::new(235, 90416), + ShortOffsetRunHeader::new(237, 92768), ShortOffsetRunHeader::new(239, 93552), + ShortOffsetRunHeader::new(247, 93824), ShortOffsetRunHeader::new(249, 118000), + ShortOffsetRunHeader::new(251, 119488), ShortOffsetRunHeader::new(253, 120782), + ShortOffsetRunHeader::new(259, 123200), ShortOffsetRunHeader::new(261, 123632), + ShortOffsetRunHeader::new(263, 124144), ShortOffsetRunHeader::new(265, 125127), + ShortOffsetRunHeader::new(269, 126065), ShortOffsetRunHeader::new(273, 127232), + ShortOffsetRunHeader::new(283, 130032), ShortOffsetRunHeader::new(285, 1244154), ]; - static OFFSETS: [u8; 289] = [ - 48, 10, 120, 2, 5, 1, 2, 3, 0, 10, 134, 10, 198, 10, 0, 10, 118, 10, 4, 6, 108, 10, 118, - 10, 118, 10, 2, 6, 110, 13, 115, 10, 8, 7, 103, 10, 104, 7, 7, 19, 109, 10, 96, 10, 118, 10, - 70, 20, 0, 10, 70, 10, 0, 20, 0, 3, 239, 10, 6, 10, 22, 10, 0, 10, 128, 11, 165, 10, 6, 10, - 182, 10, 86, 10, 134, 10, 6, 10, 0, 1, 3, 6, 6, 10, 198, 51, 2, 5, 0, 60, 78, 22, 0, 30, 0, - 1, 0, 1, 25, 9, 14, 3, 0, 4, 138, 10, 30, 8, 1, 15, 32, 10, 39, 15, 0, 10, 188, 10, 0, 6, - 154, 10, 38, 10, 198, 10, 22, 10, 86, 10, 0, 10, 0, 10, 0, 45, 12, 57, 17, 2, 0, 27, 36, 4, - 29, 1, 8, 1, 134, 5, 202, 10, 0, 8, 25, 7, 39, 9, 75, 5, 22, 6, 160, 2, 2, 16, 2, 46, 64, 9, - 52, 2, 30, 3, 75, 5, 104, 8, 24, 8, 41, 7, 0, 6, 48, 10, 6, 10, 0, 31, 158, 10, 42, 4, 112, - 7, 134, 30, 128, 10, 60, 10, 144, 10, 7, 20, 251, 10, 0, 10, 118, 10, 0, 10, 102, 10, 6, 20, - 76, 12, 0, 19, 93, 10, 0, 10, 86, 29, 227, 10, 70, 10, 0, 10, 102, 21, 0, 111, 0, 10, 0, 10, - 86, 10, 134, 10, 1, 7, 0, 10, 0, 23, 0, 10, 0, 20, 12, 20, 108, 25, 0, 50, 0, 10, 0, 10, 0, - 10, 247, 10, 0, 9, 128, 10, 0, 59, 1, 3, 1, 4, 76, 45, 1, 15, 0, 13, 0, 10, 0, + static OFFSETS: [u8; 287] = [ + 178, 2, 5, 1, 2, 3, 0, 10, 134, 10, 198, 10, 0, 10, 118, 10, 4, 6, 108, 10, 118, 10, 118, + 10, 2, 6, 110, 13, 115, 10, 8, 7, 103, 10, 104, 7, 7, 19, 109, 10, 96, 10, 118, 10, 70, 20, + 0, 10, 70, 10, 0, 20, 0, 3, 239, 10, 6, 10, 22, 10, 0, 10, 128, 11, 165, 10, 6, 10, 182, 10, + 86, 10, 134, 10, 6, 10, 0, 1, 3, 6, 6, 10, 198, 51, 2, 5, 0, 60, 78, 22, 0, 30, 0, 1, 0, 1, + 25, 9, 14, 3, 0, 4, 138, 10, 30, 8, 1, 15, 32, 10, 39, 15, 0, 10, 188, 10, 0, 6, 154, 10, + 38, 10, 198, 10, 22, 10, 86, 10, 0, 10, 0, 10, 0, 45, 12, 57, 17, 2, 0, 27, 36, 4, 29, 1, 8, + 1, 134, 5, 202, 10, 0, 8, 25, 7, 39, 9, 75, 5, 22, 6, 160, 2, 2, 16, 2, 46, 64, 9, 52, 2, + 30, 3, 75, 5, 104, 8, 24, 8, 41, 7, 0, 6, 48, 10, 6, 10, 0, 31, 158, 10, 42, 4, 112, 7, 134, + 30, 128, 10, 60, 10, 144, 10, 7, 20, 251, 10, 0, 10, 118, 10, 0, 10, 102, 10, 6, 20, 76, 12, + 0, 19, 93, 10, 0, 10, 86, 29, 227, 10, 70, 10, 0, 10, 102, 21, 0, 111, 0, 10, 0, 10, 86, 10, + 134, 10, 1, 7, 0, 10, 0, 23, 0, 10, 0, 20, 12, 20, 108, 25, 0, 50, 0, 10, 0, 10, 0, 10, 247, + 10, 0, 9, 128, 10, 0, 59, 1, 3, 1, 4, 76, 45, 1, 15, 0, 13, 0, 10, 0, ]; + #[inline] pub fn lookup(c: char) -> bool { + debug_assert!(!c.is_ascii()); + (c as u32) >= 0xb2 && lookup_slow(c) + } + + #[inline(never)] + fn lookup_slow(c: char) -> bool { const { assert!(SHORT_OFFSET_RUNS.last().unwrap().0 > char::MAX as u32); let mut i = 0; @@ -608,34 +638,34 @@ pub mod n { #[rustfmt::skip] pub mod uppercase { static BITSET_CHUNKS_MAP: [u8; 125] = [ - 12, 15, 6, 6, 0, 6, 6, 2, 4, 11, 6, 16, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 8, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 5, 6, 14, 6, 10, 6, 6, 1, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 13, 6, 6, - 6, 6, 9, 6, 3, + 3, 14, 6, 6, 0, 6, 6, 2, 5, 12, 6, 15, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 9, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 7, 6, 13, 6, 11, 6, 6, 1, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 8, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 16, 6, 6, + 6, 6, 10, 6, 4, ]; static BITSET_INDEX_CHUNKS: [[u8; 16]; 17] = [ - [44, 44, 5, 35, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 5, 1], + [44, 44, 5, 35, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 5, 0], [44, 44, 5, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], - [44, 44, 40, 44, 44, 44, 44, 44, 17, 17, 63, 17, 43, 29, 24, 23], + [44, 44, 40, 44, 44, 44, 44, 44, 17, 17, 62, 17, 43, 29, 24, 23], + [44, 44, 44, 32, 36, 21, 22, 15, 13, 34, 44, 44, 44, 11, 30, 39], [44, 44, 44, 44, 9, 8, 45, 44, 44, 44, 44, 44, 44, 44, 44, 44], - [44, 44, 44, 44, 37, 28, 67, 44, 44, 44, 44, 44, 44, 44, 44, 44], - [44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 0, 44, 44, 44], + [44, 44, 44, 44, 37, 28, 66, 44, 44, 44, 44, 44, 44, 44, 44, 44], [44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], - [44, 44, 44, 44, 44, 44, 44, 44, 44, 55, 44, 44, 44, 44, 44, 44], - [44, 44, 44, 44, 44, 44, 44, 44, 44, 62, 61, 44, 20, 14, 16, 4], - [44, 44, 44, 44, 56, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], - [44, 44, 59, 44, 44, 31, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], - [44, 44, 60, 46, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], - [44, 49, 44, 32, 36, 21, 22, 15, 13, 34, 44, 44, 44, 11, 30, 39], - [52, 54, 26, 50, 12, 7, 25, 51, 41, 53, 6, 3, 66, 65, 64, 68], - [57, 44, 9, 47, 44, 42, 33, 44, 44, 44, 44, 44, 44, 44, 44, 44], - [58, 19, 2, 18, 10, 48, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], - [58, 38, 17, 27, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], + [44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 57, 44, 44, 44], + [44, 44, 44, 44, 44, 44, 44, 44, 44, 49, 44, 44, 44, 44, 44, 44], + [44, 44, 44, 44, 44, 44, 44, 44, 44, 61, 60, 44, 20, 14, 16, 4], + [44, 44, 44, 44, 50, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], + [44, 44, 53, 44, 44, 31, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], + [44, 44, 54, 46, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], + [51, 44, 9, 47, 44, 42, 33, 44, 44, 44, 44, 44, 44, 44, 44, 44], + [52, 19, 2, 18, 10, 48, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], + [52, 38, 17, 27, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44], + [58, 1, 26, 55, 12, 7, 25, 56, 41, 59, 6, 3, 65, 64, 63, 67], ]; static BITSET_CANONICAL: [u64; 44] = [ - 0b0000011111111111111111111111111000000000000000000000000000000000, 0b0000000000111111111111111111111111111111111111111111111111111111, + 0b1111111111111111111111110000000000000000000000000011111111111111, 0b0101010101010101010101010101010101010101010101010101010000000001, 0b0000011111111111111111111111110000000000000000000000000000000001, 0b0000000000100000000000000000000000010101010000010001101011110101, @@ -679,13 +709,15 @@ pub mod uppercase { 0b1111011111111111000000000000000000000000000000000000000000000000, 0b1111111100000000111111110000000000111111000000001111111100000000, ]; - static BITSET_MAPPING: [(u8, u8); 25] = [ - (0, 187), (0, 177), (0, 171), (0, 167), (0, 164), (0, 32), (0, 47), (0, 51), (0, 121), - (0, 117), (0, 109), (1, 150), (1, 148), (1, 142), (1, 134), (1, 131), (1, 64), (2, 164), - (2, 146), (2, 20), (3, 146), (3, 140), (3, 134), (4, 178), (4, 171), + static BITSET_MAPPING: [(u8, u8); 24] = [ + (0, 182), (0, 74), (0, 166), (0, 162), (0, 159), (0, 150), (0, 148), (0, 142), (0, 134), + (0, 131), (0, 64), (1, 66), (1, 70), (1, 83), (1, 12), (1, 8), (2, 164), (2, 146), (2, 20), + (3, 146), (3, 140), (3, 134), (4, 178), (4, 171), ]; pub const fn lookup(c: char) -> bool { + debug_assert!(!c.is_ascii()); + (c as u32) >= 0xc0 && super::bitset_search( c as u32, &BITSET_CHUNKS_MAP, @@ -699,8 +731,8 @@ pub mod uppercase { #[rustfmt::skip] pub mod white_space { static WHITESPACE_MAP: [u8; 256] = [ - 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -711,6 +743,7 @@ pub mod white_space { ]; #[inline] pub const fn lookup(c: char) -> bool { + debug_assert!(!c.is_ascii()); match c as u32 >> 8 { 0 => WHITESPACE_MAP[c as usize & 0xff] & 1 != 0, 22 => c as u32 == 0x1680, diff --git a/src/tools/unicode-table-generator/src/cascading_map.rs b/src/tools/unicode-table-generator/src/cascading_map.rs index 78a7bba32087..56e6401908dc 100644 --- a/src/tools/unicode-table-generator/src/cascading_map.rs +++ b/src/tools/unicode-table-generator/src/cascading_map.rs @@ -64,6 +64,7 @@ impl RawEmitter { writeln!(&mut self.file, "#[inline]").unwrap(); writeln!(&mut self.file, "pub const fn lookup(c: char) -> bool {{").unwrap(); + writeln!(&mut self.file, " debug_assert!(!c.is_ascii());").unwrap(); writeln!(&mut self.file, " match c as u32 >> 8 {{").unwrap(); for arm in arms { writeln!(&mut self.file, " {arm},").unwrap(); diff --git a/src/tools/unicode-table-generator/src/main.rs b/src/tools/unicode-table-generator/src/main.rs index aa7d97f7f3d7..ded9205ffc4b 100644 --- a/src/tools/unicode-table-generator/src/main.rs +++ b/src/tools/unicode-table-generator/src/main.rs @@ -195,6 +195,7 @@ fn load_data() -> UnicodeData { .into_iter() .flatten() .flat_map(|cp| cp.scalar()) + .filter(|c| !c.is_ascii()) .map(u32::from) .collect::>(); (prop, ranges_from_set(&codepoints)) diff --git a/src/tools/unicode-table-generator/src/raw_emitter.rs b/src/tools/unicode-table-generator/src/raw_emitter.rs index 03ed9499e26c..297965615c1a 100644 --- a/src/tools/unicode-table-generator/src/raw_emitter.rs +++ b/src/tools/unicode-table-generator/src/raw_emitter.rs @@ -98,6 +98,7 @@ impl RawEmitter { self.blank_line(); writeln!(&mut self.file, "pub const fn lookup(c: char) -> bool {{").unwrap(); + writeln!(&mut self.file, " debug_assert!(!c.is_ascii());").unwrap(); if first_code_point > 0x7f { writeln!(&mut self.file, " (c as u32) >= {first_code_point:#04x} &&").unwrap(); } diff --git a/src/tools/unicode-table-generator/src/skiplist.rs b/src/tools/unicode-table-generator/src/skiplist.rs index 34c9802e122f..660a8f342f7a 100644 --- a/src/tools/unicode-table-generator/src/skiplist.rs +++ b/src/tools/unicode-table-generator/src/skiplist.rs @@ -99,6 +99,7 @@ impl RawEmitter { if first_code_point > 0x7f { writeln!(&mut self.file, "#[inline]").unwrap(); writeln!(&mut self.file, "pub fn lookup(c: char) -> bool {{").unwrap(); + writeln!(&mut self.file, " debug_assert!(!c.is_ascii());").unwrap(); writeln!(&mut self.file, " (c as u32) >= {first_code_point:#04x} && lookup_slow(c)") .unwrap(); writeln!(&mut self.file, "}}").unwrap(); @@ -107,6 +108,7 @@ impl RawEmitter { writeln!(&mut self.file, "fn lookup_slow(c: char) -> bool {{").unwrap(); } else { writeln!(&mut self.file, "pub fn lookup(c: char) -> bool {{").unwrap(); + writeln!(&mut self.file, " debug_assert!(!c.is_ascii());").unwrap(); } writeln!(&mut self.file, " const {{").unwrap(); writeln!( From f8180c3c429794b87968932166e8c8333f682956 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 7 Sep 2025 15:17:25 +0200 Subject: [PATCH 219/682] extract to a separate file --- clippy_lints/src/declared_lints.rs | 2 +- clippy_lints/src/lib.rs | 2 + clippy_lints/src/misc.rs | 118 +------------------------- clippy_lints/src/toplevel_ref_arg.rs | 120 +++++++++++++++++++++++++++ 4 files changed, 126 insertions(+), 116 deletions(-) create mode 100644 clippy_lints/src/toplevel_ref_arg.rs diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index d0c7443a4a4b..e610e655f158 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -503,7 +503,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::min_ident_chars::MIN_IDENT_CHARS_INFO, crate::minmax::MIN_MAX_INFO, crate::misc::SHORT_CIRCUIT_STATEMENT_INFO, - crate::misc::TOPLEVEL_REF_ARG_INFO, crate::misc::USED_UNDERSCORE_BINDING_INFO, crate::misc::USED_UNDERSCORE_ITEMS_INFO, crate::misc_early::BUILTIN_TYPE_SHADOW_INFO, @@ -706,6 +705,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::tests_outside_test_module::TESTS_OUTSIDE_TEST_MODULE_INFO, crate::to_digit_is_some::TO_DIGIT_IS_SOME_INFO, crate::to_string_trait_impl::TO_STRING_TRAIT_IMPL_INFO, + crate::toplevel_ref_arg::TOPLEVEL_REF_ARG_INFO, crate::trailing_empty_array::TRAILING_EMPTY_ARRAY_INFO, crate::trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS_INFO, crate::trait_bounds::TYPE_REPETITION_IN_BOUNDS_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index a89cf3fdc1ee..7590bec9e1e9 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -360,6 +360,7 @@ mod temporary_assignment; mod tests_outside_test_module; mod to_digit_is_some; mod to_string_trait_impl; +mod toplevel_ref_arg; mod trailing_empty_array; mod trait_bounds; mod transmute; @@ -831,5 +832,6 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(cloned_ref_to_slice_refs::ClonedRefToSliceRefs::new(conf))); store.register_late_pass(|_| Box::new(infallible_try_from::InfallibleTryFrom)); store.register_late_pass(|_| Box::new(coerce_container_to_any::CoerceContainerToAny)); + store.register_late_pass(|_| Box::new(toplevel_ref_arg::ToplevelRefArg)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 09ee6f7037c6..19e9910dfe9d 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -1,57 +1,11 @@ -use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir, span_lint_hir_and_then}; -use clippy_utils::source::{snippet, snippet_with_context}; +use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::sugg::Sugg; -use clippy_utils::{ - SpanlessEq, fulfill_or_allowed, get_parent_expr, in_automatically_derived, is_lint_allowed, iter_input_pats, - last_path_segment, -}; +use clippy_utils::{SpanlessEq, fulfill_or_allowed, get_parent_expr, in_automatically_derived, last_path_segment}; use rustc_errors::Applicability; use rustc_hir::def::Res; -use rustc_hir::intravisit::FnKind; -use rustc_hir::{ - BinOpKind, BindingMode, Body, ByRef, Expr, ExprKind, FnDecl, Mutability, PatKind, QPath, Stmt, StmtKind, -}; +use rustc_hir::{BinOpKind, Expr, ExprKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; -use rustc_span::Span; -use rustc_span::def_id::LocalDefId; - -use crate::ref_patterns::REF_PATTERNS; - -declare_clippy_lint! { - /// ### What it does - /// Checks for function arguments and let bindings denoted as - /// `ref`. - /// - /// ### Why is this bad? - /// The `ref` declaration makes the function take an owned - /// value, but turns the argument into a reference (which means that the value - /// is destroyed when exiting the function). This adds not much value: either - /// take a reference type, or take an owned value and create references in the - /// body. - /// - /// For let bindings, `let x = &foo;` is preferred over `let ref x = foo`. The - /// type of `x` is more obvious with the former. - /// - /// ### Known problems - /// If the argument is dereferenced within the function, - /// removing the `ref` will lead to errors. This can be fixed by removing the - /// dereferences, e.g., changing `*x` to `x` within the function. - /// - /// ### Example - /// ```no_run - /// fn foo(ref _x: u8) {} - /// ``` - /// - /// Use instead: - /// ```no_run - /// fn foo(_x: &u8) {} - /// ``` - #[clippy::version = "pre 1.29.0"] - pub TOPLEVEL_REF_ARG, - style, - "an entire binding declared as `ref`, in a function argument or a `let` statement" -} declare_clippy_lint! { /// ### What it does @@ -140,79 +94,13 @@ declare_clippy_lint! { } declare_lint_pass!(LintPass => [ - TOPLEVEL_REF_ARG, USED_UNDERSCORE_BINDING, USED_UNDERSCORE_ITEMS, SHORT_CIRCUIT_STATEMENT, ]); impl<'tcx> LateLintPass<'tcx> for LintPass { - fn check_fn( - &mut self, - cx: &LateContext<'tcx>, - k: FnKind<'tcx>, - decl: &'tcx FnDecl<'_>, - body: &'tcx Body<'_>, - _: Span, - _: LocalDefId, - ) { - if !matches!(k, FnKind::Closure) { - for arg in iter_input_pats(decl, body) { - if let PatKind::Binding(BindingMode(ByRef::Yes(_), _), ..) = arg.pat.kind - && is_lint_allowed(cx, REF_PATTERNS, arg.pat.hir_id) - && !arg.span.in_external_macro(cx.tcx.sess.source_map()) - { - span_lint_hir( - cx, - TOPLEVEL_REF_ARG, - arg.hir_id, - arg.pat.span, - "`ref` directly on a function parameter does not prevent taking ownership of the passed argument. \ - Consider using a reference type instead", - ); - } - } - } - } - fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - if let StmtKind::Let(local) = stmt.kind - && let PatKind::Binding(BindingMode(ByRef::Yes(mutabl), _), .., name, None) = local.pat.kind - && let Some(init) = local.init - // Do not emit if clippy::ref_patterns is not allowed to avoid having two lints for the same issue. - && is_lint_allowed(cx, REF_PATTERNS, local.pat.hir_id) - && !stmt.span.in_external_macro(cx.tcx.sess.source_map()) - { - let ctxt = local.span.ctxt(); - let mut app = Applicability::MachineApplicable; - let sugg_init = Sugg::hir_with_context(cx, init, ctxt, "..", &mut app); - let (mutopt, initref) = if mutabl == Mutability::Mut { - ("mut ", sugg_init.mut_addr()) - } else { - ("", sugg_init.addr()) - }; - let tyopt = if let Some(ty) = local.ty { - let ty_snip = snippet_with_context(cx, ty.span, ctxt, "_", &mut app).0; - format!(": &{mutopt}{ty_snip}") - } else { - String::new() - }; - span_lint_hir_and_then( - cx, - TOPLEVEL_REF_ARG, - init.hir_id, - local.pat.span, - "`ref` on an entire `let` pattern is discouraged, take a reference with `&` instead", - |diag| { - diag.span_suggestion( - stmt.span, - "try", - format!("let {name}{tyopt} = {initref};", name = snippet(cx, name.span, ".."),), - app, - ); - }, - ); - } if let StmtKind::Semi(expr) = stmt.kind && let ExprKind::Binary(binop, a, b) = &expr.kind && matches!(binop.node, BinOpKind::And | BinOpKind::Or) diff --git a/clippy_lints/src/toplevel_ref_arg.rs b/clippy_lints/src/toplevel_ref_arg.rs new file mode 100644 index 000000000000..04da5efce5da --- /dev/null +++ b/clippy_lints/src/toplevel_ref_arg.rs @@ -0,0 +1,120 @@ +use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; +use clippy_utils::source::{snippet, snippet_with_context}; +use clippy_utils::sugg::Sugg; +use clippy_utils::{is_lint_allowed, iter_input_pats}; +use rustc_errors::Applicability; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{BindingMode, Body, ByRef, FnDecl, Mutability, PatKind, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; +use rustc_span::Span; +use rustc_span::def_id::LocalDefId; + +use crate::ref_patterns::REF_PATTERNS; + +declare_clippy_lint! { + /// ### What it does + /// Checks for function arguments and let bindings denoted as + /// `ref`. + /// + /// ### Why is this bad? + /// The `ref` declaration makes the function take an owned + /// value, but turns the argument into a reference (which means that the value + /// is destroyed when exiting the function). This adds not much value: either + /// take a reference type, or take an owned value and create references in the + /// body. + /// + /// For let bindings, `let x = &foo;` is preferred over `let ref x = foo`. The + /// type of `x` is more obvious with the former. + /// + /// ### Known problems + /// If the argument is dereferenced within the function, + /// removing the `ref` will lead to errors. This can be fixed by removing the + /// dereferences, e.g., changing `*x` to `x` within the function. + /// + /// ### Example + /// ```no_run + /// fn foo(ref _x: u8) {} + /// ``` + /// + /// Use instead: + /// ```no_run + /// fn foo(_x: &u8) {} + /// ``` + #[clippy::version = "pre 1.29.0"] + pub TOPLEVEL_REF_ARG, + style, + "an entire binding declared as `ref`, in a function argument or a `let` statement" +} + +declare_lint_pass!(ToplevelRefArg => [TOPLEVEL_REF_ARG]); + +impl<'tcx> LateLintPass<'tcx> for ToplevelRefArg { + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + k: FnKind<'tcx>, + decl: &'tcx FnDecl<'_>, + body: &'tcx Body<'_>, + _: Span, + _: LocalDefId, + ) { + if !matches!(k, FnKind::Closure) { + for arg in iter_input_pats(decl, body) { + if let PatKind::Binding(BindingMode(ByRef::Yes(_), _), ..) = arg.pat.kind + && is_lint_allowed(cx, REF_PATTERNS, arg.pat.hir_id) + && !arg.span.in_external_macro(cx.tcx.sess.source_map()) + { + span_lint_hir( + cx, + TOPLEVEL_REF_ARG, + arg.hir_id, + arg.pat.span, + "`ref` directly on a function parameter does not prevent taking ownership of the passed argument. \ + Consider using a reference type instead", + ); + } + } + } + } + + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + if let StmtKind::Let(local) = stmt.kind + && let PatKind::Binding(BindingMode(ByRef::Yes(mutabl), _), .., name, None) = local.pat.kind + && let Some(init) = local.init + // Do not emit if clippy::ref_patterns is not allowed to avoid having two lints for the same issue. + && is_lint_allowed(cx, REF_PATTERNS, local.pat.hir_id) + && !stmt.span.in_external_macro(cx.tcx.sess.source_map()) + { + let ctxt = local.span.ctxt(); + let mut app = Applicability::MachineApplicable; + let sugg_init = Sugg::hir_with_context(cx, init, ctxt, "..", &mut app); + let (mutopt, initref) = if mutabl == Mutability::Mut { + ("mut ", sugg_init.mut_addr()) + } else { + ("", sugg_init.addr()) + }; + let tyopt = if let Some(ty) = local.ty { + let ty_snip = snippet_with_context(cx, ty.span, ctxt, "_", &mut app).0; + format!(": &{mutopt}{ty_snip}") + } else { + String::new() + }; + span_lint_hir_and_then( + cx, + TOPLEVEL_REF_ARG, + init.hir_id, + local.pat.span, + "`ref` on an entire `let` pattern is discouraged, take a reference with `&` instead", + |diag| { + diag.span_suggestion( + stmt.span, + "try", + format!("let {name}{tyopt} = {initref};", name = snippet(cx, name.span, ".."),), + app, + ); + }, + ); + } + } +} From 2c27b586183779d2437ac758055bb21011c6551c Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 7 Sep 2025 15:26:50 +0200 Subject: [PATCH 220/682] tiny clean-up --- clippy_lints/src/toplevel_ref_arg.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/toplevel_ref_arg.rs b/clippy_lints/src/toplevel_ref_arg.rs index 04da5efce5da..074b79263d37 100644 --- a/clippy_lints/src/toplevel_ref_arg.rs +++ b/clippy_lints/src/toplevel_ref_arg.rs @@ -89,10 +89,9 @@ impl<'tcx> LateLintPass<'tcx> for ToplevelRefArg { let ctxt = local.span.ctxt(); let mut app = Applicability::MachineApplicable; let sugg_init = Sugg::hir_with_context(cx, init, ctxt, "..", &mut app); - let (mutopt, initref) = if mutabl == Mutability::Mut { - ("mut ", sugg_init.mut_addr()) - } else { - ("", sugg_init.addr()) + let (mutopt, initref) = match mutabl { + Mutability::Mut => ("mut ", sugg_init.mut_addr()), + Mutability::Not => ("", sugg_init.addr()), }; let tyopt = if let Some(ty) = local.ty { let ty_snip = snippet_with_context(cx, ty.span, ctxt, "_", &mut app).0; From 91241a1d255bde9d1bc3399c1c1f779bb17f1cfa Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Wed, 2 Jul 2025 22:09:46 +0000 Subject: [PATCH 221/682] Ensure indirect is first projection in try_as_place. --- compiler/rustc_mir_transform/src/gvn.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 5a13394543b4..7f005accc655 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -1654,6 +1654,11 @@ impl<'tcx> VnState<'_, 'tcx> { let place = Place { local, projection: self.tcx.mk_place_elems(projection.as_slice()) }; return Some(place); + } else if projection.last() == Some(&PlaceElem::Deref) { + // `Deref` can only be the first projection in a place. + // If we are here, we failed to find a local, and we already have a `Deref`. + // Trying to add projections will only result in an ill-formed place. + return None; } else if let Value::Projection(pointer, proj) = *self.get(index) && (allow_complex_projection || proj.is_stable_offset()) && let Some(proj) = self.try_as_place_elem(self.ty(index), proj, loc) From 6da8094b1385b1647178a3ab9dab3c359ef1c38d Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 7 Sep 2025 21:49:55 +0800 Subject: [PATCH 222/682] Improve make::struct_ field_list whitespace Example --- **Before this PR**: ```rust struct Variant{ field: u32 } ``` **After this PR**: ```rust struct Variant { field: u32 } ``` --- .../extract_struct_from_enum_variant.rs | 44 +++++++++---------- .../src/handlers/json_is_not_rust.rs | 24 +++++----- .../crates/syntax/src/ast/make.rs | 7 ++- 3 files changed, 39 insertions(+), 36 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index c56d0b3de5d6..79a4c73c698d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -478,7 +478,7 @@ macro_rules! foo { }; } -struct TheVariant{ the_field: u8 } +struct TheVariant { the_field: u8 } enum TheEnum { TheVariant(TheVariant), @@ -502,7 +502,7 @@ enum Foo { } "#, r#" -struct Bar{ node: Box } +struct Bar { node: Box } enum Foo { Bar(Bar), @@ -519,7 +519,7 @@ enum Foo { } "#, r#" -struct Bar{ node: Box, a: Arc> } +struct Bar { node: Box, a: Arc> } enum Foo { Bar(Bar), @@ -560,7 +560,7 @@ enum A { One(One) }"#, check_assist( extract_struct_from_enum_variant, "enum A { $0One { foo: u32, bar: u32 } }", - r#"struct One{ foo: u32, bar: u32 } + r#"struct One { foo: u32, bar: u32 } enum A { One(One) }"#, ); @@ -571,7 +571,7 @@ enum A { One(One) }"#, check_assist( extract_struct_from_enum_variant, "enum A { $0One { foo: u32 } }", - r#"struct One{ foo: u32 } + r#"struct One { foo: u32 } enum A { One(One) }"#, ); @@ -582,7 +582,7 @@ enum A { One(One) }"#, check_assist( extract_struct_from_enum_variant, r"enum En { Var { a: T$0 } }", - r#"struct Var{ a: T } + r#"struct Var { a: T } enum En { Var(Var) }"#, ); @@ -599,7 +599,7 @@ enum Enum { Variant{ field: u32$0 } }"#, r#" #[derive(Debug)] #[derive(Clone)] -struct Variant{ field: u32 } +struct Variant { field: u32 } #[derive(Debug)] #[derive(Clone)] @@ -618,7 +618,7 @@ enum Enum { } }"#, r#" -struct Variant{ +struct Variant { field: u32 } @@ -642,7 +642,7 @@ mod indenting { }"#, r#" mod indenting { - struct Variant{ + struct Variant { field: u32 } @@ -668,7 +668,7 @@ enum A { } }"#, r#" -struct One{ +struct One { // leading comment /// doc comment #[an_attr] @@ -700,7 +700,7 @@ enum A { } }"#, r#" -struct One{ +struct One { // comment /// doc #[attr] @@ -747,7 +747,7 @@ enum A { /* comment */ // other /// comment -struct One{ +struct One { a: u32 } @@ -789,7 +789,7 @@ enum A { extract_struct_from_enum_variant, "enum A { $0One{ a: u32, pub(crate) b: u32, pub(super) c: u32, d: u32 } }", r#" -struct One{ a: u32, pub(crate) b: u32, pub(super) c: u32, d: u32 } +struct One { a: u32, pub(crate) b: u32, pub(super) c: u32, d: u32 } enum A { One(One) }"#, ); @@ -850,7 +850,7 @@ pub enum A { One(One) }"#, extract_struct_from_enum_variant, "pub(in something) enum A { $0One{ a: u32, b: u32 } }", r#" -pub(in something) struct One{ pub(in something) a: u32, pub(in something) b: u32 } +pub(in something) struct One { pub(in something) a: u32, pub(in something) b: u32 } pub(in something) enum A { One(One) }"#, ); @@ -862,7 +862,7 @@ pub(in something) enum A { One(One) }"#, extract_struct_from_enum_variant, "pub(crate) enum A { $0One{ a: u32, b: u32, c: u32 } }", r#" -pub(crate) struct One{ pub(crate) a: u32, pub(crate) b: u32, pub(crate) c: u32 } +pub(crate) struct One { pub(crate) a: u32, pub(crate) b: u32, pub(crate) c: u32 } pub(crate) enum A { One(One) }"#, ); @@ -933,7 +933,7 @@ fn f() { } "#, r#" -struct V{ i: i32, j: i32 } +struct V { i: i32, j: i32 } enum E { V(V) @@ -1027,7 +1027,7 @@ fn f() { "#, r#" //- /main.rs -struct V{ i: i32, j: i32 } +struct V { i: i32, j: i32 } enum E { V(V) @@ -1057,7 +1057,7 @@ fn foo() { } "#, r#" -struct One{ a: u32, b: u32 } +struct One { a: u32, b: u32 } enum A { One(One) } @@ -1114,7 +1114,7 @@ enum X<'a, 'b, 'x> { } "#, r#" -struct A<'a, 'x>{ a: &'a &'x mut () } +struct A<'a, 'x> { a: &'a &'x mut () } enum X<'a, 'b, 'x> { A(A<'a, 'x>), @@ -1136,7 +1136,7 @@ enum X<'b, T, V, const C: usize> { } "#, r#" -struct A<'b, T, const C: usize>{ a: T, b: X<'b>, c: [u8; C] } +struct A<'b, T, const C: usize> { a: T, b: X<'b>, c: [u8; C] } enum X<'b, T, V, const C: usize> { A(A<'b, T, C>), @@ -1158,7 +1158,7 @@ enum X<'a, 'b> { } "#, r#" -struct C{ c: () } +struct C { c: () } enum X<'a, 'b> { A { a: &'a () }, @@ -1180,7 +1180,7 @@ enum En { } "#, r#" -struct A{ a: T } +struct A { a: T } enum En { A(A), diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs index 742d614bc567..a300997723b4 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs @@ -233,7 +233,7 @@ mod tests { } #[derive(Serialize)] - struct Root1{ bar: f64, bay: i64, baz: (), r#box: bool, foo: String } + struct Root1 { bar: f64, bay: i64, baz: (), r#box: bool, foo: String } "#, ); @@ -252,9 +252,9 @@ mod tests { } "#, r#" - struct Value1{ } - struct Bar1{ kind: String, value: Value1 } - struct Root1{ bar: Bar1, foo: String } + struct Value1 { } + struct Bar1 { kind: String, value: Value1 } + struct Root1 { bar: Bar1, foo: String } "#, ); @@ -284,12 +284,12 @@ mod tests { } "#, r#" - struct Address1{ house: i64, street: String } - struct User1{ address: Address1, email: String } - struct AnotherUser1{ user: User1 } - struct Address2{ house: i64, street: String } - struct User2{ address: Address2, email: String } - struct Root1{ another_user: AnotherUser1, user: User2 } + struct Address1 { house: i64, street: String } + struct User1 { address: Address1, email: String } + struct AnotherUser1 { user: User1 } + struct Address2 { house: i64, street: String } + struct User2 { address: Address2, email: String } + struct Root1 { another_user: AnotherUser1, user: User2 } "#, ); @@ -326,9 +326,9 @@ mod tests { use serde::Deserialize; #[derive(Serialize, Deserialize)] - struct OfObject1{ x: i64, y: i64 } + struct OfObject1 { x: i64, y: i64 } #[derive(Serialize, Deserialize)] - struct Root1{ empty: Vec<_>, nested: Vec>>, of_object: Vec, of_string: Vec } + struct Root1 { empty: Vec<_>, nested: Vec>>, of_object: Vec, of_string: Vec } "#, ); diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs index 9897fd094157..051c5835571b 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs @@ -1244,14 +1244,17 @@ pub fn struct_( generic_param_list: Option, field_list: ast::FieldList, ) -> ast::Struct { - let semicolon = if matches!(field_list, ast::FieldList::TupleFieldList(_)) { ";" } else { "" }; + let (semicolon, ws) = + if matches!(field_list, ast::FieldList::TupleFieldList(_)) { (";", "") } else { ("", " ") }; let type_params = generic_param_list.map_or_else(String::new, |it| it.to_string()); let visibility = match visibility { None => String::new(), Some(it) => format!("{it} "), }; - ast_from_text(&format!("{visibility}struct {strukt_name}{type_params}{field_list}{semicolon}",)) + ast_from_text(&format!( + "{visibility}struct {strukt_name}{type_params}{ws}{field_list}{semicolon}" + )) } pub fn enum_( From 4b5d0e02ca2647bf1efb2ecd3303c21ead64d2ea Mon Sep 17 00:00:00 2001 From: Soroush Mirzaei Date: Sun, 7 Sep 2025 11:14:00 -0400 Subject: [PATCH 223/682] docs(std): add error docs for path canonicalize --- library/std/src/path.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 19663e4a9df6..70ba502d6842 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -3037,6 +3037,14 @@ impl Path { /// /// This is an alias to [`fs::canonicalize`]. /// + /// # Errors + /// + /// This method will return an error in the following situations, but is not + /// limited to just these cases: + /// + /// * `path` does not exist. + /// * A non-final component in path is not a directory. + /// /// # Examples /// /// ```no_run From 02602ef65125ac743cc0a61c5afe7c0c5acbad56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Bj=C3=B8rnager=20Jensen?= Date: Sun, 7 Sep 2025 18:01:39 +0200 Subject: [PATCH 224/682] Implement 'Sum' and 'Product' for 'f16' and 'f128'; --- library/core/src/iter/traits/accum.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/iter/traits/accum.rs b/library/core/src/iter/traits/accum.rs index 73122369b41d..3b805139ded1 100644 --- a/library/core/src/iter/traits/accum.rs +++ b/library/core/src/iter/traits/accum.rs @@ -203,7 +203,7 @@ macro_rules! float_sum_product { integer_sum_product! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize } saturating_integer_sum_product! { u8 u16 u32 u64 u128 usize } -float_sum_product! { f32 f64 } +float_sum_product! { f16 f32 f64 f128 } #[stable(feature = "iter_arith_traits_result", since = "1.16.0")] impl Sum> for Result From a2d66db9badf35d4ad31323b0f5e6132d56f9391 Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Sat, 6 Sep 2025 17:47:54 +0000 Subject: [PATCH 225/682] remove unsused div_rem method from bignum --- library/core/src/num/bignum.rs | 37 --------------------------- library/coretests/tests/num/bignum.rs | 19 -------------- 2 files changed, 56 deletions(-) diff --git a/library/core/src/num/bignum.rs b/library/core/src/num/bignum.rs index e33f58197bba..f21fe0b4438f 100644 --- a/library/core/src/num/bignum.rs +++ b/library/core/src/num/bignum.rs @@ -335,43 +335,6 @@ macro_rules! define_bignum { } (self, borrow) } - - /// Divide self by another bignum, overwriting `q` with the quotient and `r` with the - /// remainder. - pub fn div_rem(&self, d: &$name, q: &mut $name, r: &mut $name) { - // Stupid slow base-2 long division taken from - // https://en.wikipedia.org/wiki/Division_algorithm - // FIXME use a greater base ($ty) for the long division. - assert!(!d.is_zero()); - let digitbits = <$ty>::BITS as usize; - for digit in &mut q.base[..] { - *digit = 0; - } - for digit in &mut r.base[..] { - *digit = 0; - } - r.size = d.size; - q.size = 1; - let mut q_is_zero = true; - let end = self.bit_length(); - for i in (0..end).rev() { - r.mul_pow2(1); - r.base[0] |= self.get_bit(i) as $ty; - if &*r >= d { - r.sub(d); - // Set bit `i` of q to 1. - let digit_idx = i / digitbits; - let bit_idx = i % digitbits; - if q_is_zero { - q.size = digit_idx + 1; - q_is_zero = false; - } - q.base[digit_idx] |= 1 << bit_idx; - } - } - debug_assert!(q.base[q.size..].iter().all(|&d| d == 0)); - debug_assert!(r.base[r.size..].iter().all(|&d| d == 0)); - } } impl crate::cmp::PartialEq for $name { diff --git a/library/coretests/tests/num/bignum.rs b/library/coretests/tests/num/bignum.rs index f213fd5366cb..6dfa496e018b 100644 --- a/library/coretests/tests/num/bignum.rs +++ b/library/coretests/tests/num/bignum.rs @@ -167,25 +167,6 @@ fn test_div_rem_small() { ); } -#[test] -fn test_div_rem() { - fn div_rem(n: u64, d: u64) -> (Big, Big) { - let mut q = Big::from_small(42); - let mut r = Big::from_small(42); - Big::from_u64(n).div_rem(&Big::from_u64(d), &mut q, &mut r); - (q, r) - } - assert_eq!(div_rem(1, 1), (Big::from_small(1), Big::from_small(0))); - assert_eq!(div_rem(4, 3), (Big::from_small(1), Big::from_small(1))); - assert_eq!(div_rem(1, 7), (Big::from_small(0), Big::from_small(1))); - assert_eq!(div_rem(45, 9), (Big::from_small(5), Big::from_small(0))); - assert_eq!(div_rem(103, 9), (Big::from_small(11), Big::from_small(4))); - assert_eq!(div_rem(123456, 77), (Big::from_u64(1603), Big::from_small(25))); - assert_eq!(div_rem(0xffff, 1), (Big::from_u64(0xffff), Big::from_small(0))); - assert_eq!(div_rem(0xeeee, 0xffff), (Big::from_small(0), Big::from_u64(0xeeee))); - assert_eq!(div_rem(2_000_000, 2), (Big::from_u64(1_000_000), Big::from_u64(0))); -} - #[test] fn test_is_zero() { assert!(Big::from_small(0).is_zero()); From 380c906bebfb4e2c8f6672870952505e4a92c577 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 8 Sep 2025 06:07:20 +1000 Subject: [PATCH 226/682] Minor symbol comment fixes. - The empty symbol is no longer a keyword. - I don't think any of the special reserved identifiers are used for error recovery. --- compiler/rustc_span/src/symbol.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index e5108d8b7e92..02bf3595279c 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -21,18 +21,17 @@ mod tests; // The proc macro code for this is in `compiler/rustc_macros/src/symbols.rs`. symbols! { - // This list includes things that are definitely keywords (e.g. `if`), - // a few things that are definitely not keywords (e.g. the empty symbol, - // `{{root}}`) and things where there is disagreement between people and/or - // documents (such as the Rust Reference) about whether it is a keyword - // (e.g. `_`). + // This list includes things that are definitely keywords (e.g. `if`), a + // few things that are definitely not keywords (e.g. `{{root}}`) and things + // where there is disagreement between people and/or documents (such as the + // Rust Reference) about whether it is a keyword (e.g. `_`). // // If you modify this list, adjust any relevant `Symbol::{is,can_be}_*` // predicates and `used_keywords`. Also consider adding new keywords to the // `ui/parser/raw/raw-idents.rs` test. Keywords { - // Special reserved identifiers used internally for elided lifetimes, - // unnamed method parameters, crate root module, error recovery etc. + // Special reserved identifiers used internally for unnamed method + // parameters, crate root module, etc. // Matching predicates: `is_special`/`is_reserved` // // tidy-alphabetical-start From 1cb749aa3b421db4f918e8befca569166e8ba6ab Mon Sep 17 00:00:00 2001 From: cyrgani Date: Sun, 7 Sep 2025 21:26:24 +0200 Subject: [PATCH 227/682] support integer literals in `${concat()}` --- compiler/rustc_expand/src/mbe/transcribe.rs | 18 +++++++- .../concat-allowed-operations.rs | 18 ++++++++ .../concat-usage-errors.rs | 10 +++-- .../concat-usage-errors.stderr | 44 ++++++++++--------- 4 files changed, 64 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index e4e4866b64cd..ed8aa71d59dd 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -940,11 +940,27 @@ fn extract_symbol_from_pnr<'a>( { Ok(*symbol) } + ParseNtResult::Literal(expr) + if let ExprKind::Lit(lit @ Lit { kind: LitKind::Integer, symbol, suffix }) = + &expr.kind => + { + if lit.is_semantic_float() { + Err(dcx + .struct_err("floats are not supported as metavariables of `${concat(..)}`") + .with_span(span_err)) + } else if suffix.is_none() { + Ok(*symbol) + } else { + Err(dcx + .struct_err("integer metavariables of `${concat(..)}` must not be suffixed") + .with_span(span_err)) + } + } _ => Err(dcx .struct_err( "metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt`", ) - .with_note("currently only string literals are supported") + .with_note("currently only string and integer literals are supported") .with_span(span_err)), } } diff --git a/tests/ui/macros/metavar-expressions/concat-allowed-operations.rs b/tests/ui/macros/metavar-expressions/concat-allowed-operations.rs index 695a752fe17d..5ac50c943d03 100644 --- a/tests/ui/macros/metavar-expressions/concat-allowed-operations.rs +++ b/tests/ui/macros/metavar-expressions/concat-allowed-operations.rs @@ -92,6 +92,12 @@ macro_rules! combinations { }}; } +macro_rules! int_struct { + ($n: literal) => { + struct ${concat(E, $n)}; + } +} + fn main() { create_things!(behold); behold_separated_idents_in_a_fn(); @@ -112,4 +118,16 @@ fn main() { assert_eq!(VAR_123, 2); combinations!(_hello, "a", b, "b"); + + int_struct!(1_0); + int_struct!(2); + int_struct!(3___0); + int_struct!(7_); + int_struct!(08); + + let _ = E1_0; + let _ = E2; + let _ = E3___0; + let _ = E7_; + let _ = E08; } diff --git a/tests/ui/macros/metavar-expressions/concat-usage-errors.rs b/tests/ui/macros/metavar-expressions/concat-usage-errors.rs index 7d8756de9e20..277ad240b1bf 100644 --- a/tests/ui/macros/metavar-expressions/concat-usage-errors.rs +++ b/tests/ui/macros/metavar-expressions/concat-usage-errors.rs @@ -140,7 +140,9 @@ macro_rules! bad_literal_non_string { //~| ERROR metavariables of `${concat(..)}` must be of type //~| ERROR metavariables of `${concat(..)}` must be of type //~| ERROR metavariables of `${concat(..)}` must be of type - //~| ERROR metavariables of `${concat(..)}` must be of type + //~| ERROR floats are not supported as metavariables of `${concat(..)}` + //~| ERROR integer metavariables of `${concat(..)}` must not be suffixed + //~| ERROR integer metavariables of `${concat(..)}` must not be suffixed } } @@ -149,7 +151,6 @@ macro_rules! bad_tt_literal { const ${concat(_foo, $tt)}: () = (); //~^ ERROR metavariables of `${concat(..)}` must be of type //~| ERROR metavariables of `${concat(..)}` must be of type - //~| ERROR metavariables of `${concat(..)}` must be of type } } @@ -178,13 +179,14 @@ fn main() { bad_literal_string!("1.0"); bad_literal_string!("'1'"); - bad_literal_non_string!(1); bad_literal_non_string!(-1); bad_literal_non_string!(1.0); bad_literal_non_string!('1'); bad_literal_non_string!(false); + bad_literal_non_string!(4f64); + bad_literal_non_string!(5u8); + bad_literal_non_string!(6_u8); - bad_tt_literal!(1); bad_tt_literal!(1.0); bad_tt_literal!('1'); } diff --git a/tests/ui/macros/metavar-expressions/concat-usage-errors.stderr b/tests/ui/macros/metavar-expressions/concat-usage-errors.stderr index 8be3e792ec3f..c124b76cb781 100644 --- a/tests/ui/macros/metavar-expressions/concat-usage-errors.stderr +++ b/tests/ui/macros/metavar-expressions/concat-usage-errors.stderr @@ -130,7 +130,7 @@ error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `t LL | ${concat($ex, aaaa)} | ^^ | - = note: currently only string literals are supported + = note: currently only string and integer literals are supported error: variable `foo` is not recognized in meta-variable expression --> $DIR/concat-usage-errors.rs:37:30 @@ -276,7 +276,7 @@ error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `t LL | const ${concat(_foo, $literal)}: () = (); | ^^^^^^^ | - = note: currently only string literals are supported + = note: currently only string and integer literals are supported error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt` --> $DIR/concat-usage-errors.rs:138:31 @@ -284,7 +284,7 @@ error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `t LL | const ${concat(_foo, $literal)}: () = (); | ^^^^^^^ | - = note: currently only string literals are supported + = note: currently only string and integer literals are supported = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt` @@ -293,7 +293,7 @@ error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `t LL | const ${concat(_foo, $literal)}: () = (); | ^^^^^^^ | - = note: currently only string literals are supported + = note: currently only string and integer literals are supported = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt` @@ -302,43 +302,45 @@ error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `t LL | const ${concat(_foo, $literal)}: () = (); | ^^^^^^^ | - = note: currently only string literals are supported + = note: currently only string and integer literals are supported = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt` +error: floats are not supported as metavariables of `${concat(..)}` + --> $DIR/concat-usage-errors.rs:138:31 + | +LL | const ${concat(_foo, $literal)}: () = (); + | ^^^^^^^ + +error: integer metavariables of `${concat(..)}` must not be suffixed + --> $DIR/concat-usage-errors.rs:138:31 + | +LL | const ${concat(_foo, $literal)}: () = (); + | ^^^^^^^ + +error: integer metavariables of `${concat(..)}` must not be suffixed --> $DIR/concat-usage-errors.rs:138:31 | LL | const ${concat(_foo, $literal)}: () = (); | ^^^^^^^ | - = note: currently only string literals are supported = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt` - --> $DIR/concat-usage-errors.rs:149:31 + --> $DIR/concat-usage-errors.rs:151:31 | LL | const ${concat(_foo, $tt)}: () = (); | ^^ | - = note: currently only string literals are supported + = note: currently only string and integer literals are supported error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt` - --> $DIR/concat-usage-errors.rs:149:31 + --> $DIR/concat-usage-errors.rs:151:31 | LL | const ${concat(_foo, $tt)}: () = (); | ^^ | - = note: currently only string literals are supported + = note: currently only string and integer literals are supported = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt` - --> $DIR/concat-usage-errors.rs:149:31 - | -LL | const ${concat(_foo, $tt)}: () = (); - | ^^ - | - = note: currently only string literals are supported - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: aborting due to 43 previous errors +error: aborting due to 44 previous errors From 006c85d017e9eb404e3798c59520d6058d971cd8 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 31 Jul 2025 15:53:18 +1000 Subject: [PATCH 228/682] Make some matches non-exhaustive. Exhaustive match isn't necessary for these trivial cases, and some similar nearby methods are non-exhaustive. --- compiler/rustc_middle/src/ty/predicate.rs | 32 ++--------------------- 1 file changed, 2 insertions(+), 30 deletions(-) diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs index 59e00f85957e..69dcca54f469 100644 --- a/compiler/rustc_middle/src/ty/predicate.rs +++ b/compiler/rustc_middle/src/ty/predicate.rs @@ -638,21 +638,7 @@ impl<'tcx> Predicate<'tcx> { let predicate = self.kind(); match predicate.skip_binder() { PredicateKind::Clause(ClauseKind::Trait(t)) => Some(predicate.rebind(t)), - PredicateKind::Clause(ClauseKind::Projection(..)) - | PredicateKind::Clause(ClauseKind::HostEffect(..)) - | PredicateKind::Clause(ClauseKind::ConstArgHasType(..)) - | PredicateKind::Clause(ClauseKind::UnstableFeature(_)) - | PredicateKind::NormalizesTo(..) - | PredicateKind::AliasRelate(..) - | PredicateKind::Subtype(..) - | PredicateKind::Coerce(..) - | PredicateKind::Clause(ClauseKind::RegionOutlives(..)) - | PredicateKind::Clause(ClauseKind::WellFormed(..)) - | PredicateKind::DynCompatible(..) - | PredicateKind::Clause(ClauseKind::TypeOutlives(..)) - | PredicateKind::Clause(ClauseKind::ConstEvaluatable(..)) - | PredicateKind::ConstEquate(..) - | PredicateKind::Ambiguous => None, + _ => None, } } @@ -660,21 +646,7 @@ impl<'tcx> Predicate<'tcx> { let predicate = self.kind(); match predicate.skip_binder() { PredicateKind::Clause(ClauseKind::Projection(t)) => Some(predicate.rebind(t)), - PredicateKind::Clause(ClauseKind::Trait(..)) - | PredicateKind::Clause(ClauseKind::HostEffect(..)) - | PredicateKind::Clause(ClauseKind::ConstArgHasType(..)) - | PredicateKind::Clause(ClauseKind::UnstableFeature(_)) - | PredicateKind::NormalizesTo(..) - | PredicateKind::AliasRelate(..) - | PredicateKind::Subtype(..) - | PredicateKind::Coerce(..) - | PredicateKind::Clause(ClauseKind::RegionOutlives(..)) - | PredicateKind::Clause(ClauseKind::WellFormed(..)) - | PredicateKind::DynCompatible(..) - | PredicateKind::Clause(ClauseKind::TypeOutlives(..)) - | PredicateKind::Clause(ClauseKind::ConstEvaluatable(..)) - | PredicateKind::ConstEquate(..) - | PredicateKind::Ambiguous => None, + _ => None, } } From 1e679154da915f971216f8e925eda2a1b47a38af Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 31 Jul 2025 16:35:38 +1000 Subject: [PATCH 229/682] Move `rustc_middle::MaxUniverse` to `rustc_infer`. Because `rust_infer` is the only crate that uses it. --- .../src/infer/relate/generalize.rs | 43 ++++++++++++++++++- compiler/rustc_middle/src/ty/mod.rs | 1 - compiler/rustc_middle/src/ty/visit.rs | 39 ----------------- 3 files changed, 41 insertions(+), 42 deletions(-) diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index a000bb1123c1..a75fd8dfa18b 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -6,8 +6,8 @@ use rustc_hir::def_id::DefId; use rustc_middle::bug; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::{ - self, AliasRelationDirection, InferConst, MaxUniverse, Term, Ty, TyCtxt, TypeVisitable, - TypeVisitableExt, TypingMode, + self, AliasRelationDirection, InferConst, Term, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt, TypeVisitor, TypingMode, }; use rustc_span::Span; use tracing::{debug, instrument, warn}; @@ -290,6 +290,45 @@ impl<'tcx> InferCtxt<'tcx> { } } +/// Finds the max universe present +struct MaxUniverse { + max_universe: ty::UniverseIndex, +} + +impl MaxUniverse { + fn new() -> Self { + MaxUniverse { max_universe: ty::UniverseIndex::ROOT } + } + + fn max_universe(self) -> ty::UniverseIndex { + self.max_universe + } +} + +impl<'tcx> TypeVisitor> for MaxUniverse { + fn visit_ty(&mut self, t: Ty<'tcx>) { + if let ty::Placeholder(placeholder) = t.kind() { + self.max_universe = self.max_universe.max(placeholder.universe); + } + + t.super_visit_with(self) + } + + fn visit_const(&mut self, c: ty::Const<'tcx>) { + if let ty::ConstKind::Placeholder(placeholder) = c.kind() { + self.max_universe = self.max_universe.max(placeholder.universe); + } + + c.super_visit_with(self) + } + + fn visit_region(&mut self, r: ty::Region<'tcx>) { + if let ty::RePlaceholder(placeholder) = r.kind() { + self.max_universe = self.max_universe.max(placeholder.universe); + } + } +} + /// The "generalizer" is used when handling inference variables. /// /// The basic strategy for handling a constraint like `?A <: B` is to diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index e567ba05f61c..da17ec1f9f37 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -109,7 +109,6 @@ pub use self::typeck_results::{ CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, IsIdentity, Rust2024IncompatiblePatInfo, TypeckResults, UserType, UserTypeAnnotationIndex, UserTypeKind, }; -pub use self::visit::*; use crate::error::{OpaqueHiddenTypeMismatch, TypeMismatchReason}; use crate::metadata::ModChild; use crate::middle::privacy::EffectiveVisibilities; diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs index 3853a804a920..f0c47f257cc4 100644 --- a/compiler/rustc_middle/src/ty/visit.rs +++ b/compiler/rustc_middle/src/ty/visit.rs @@ -212,42 +212,3 @@ impl<'tcx> TypeVisitor> for LateBoundRegionsCollector { } } } - -/// Finds the max universe present -pub struct MaxUniverse { - max_universe: ty::UniverseIndex, -} - -impl MaxUniverse { - pub fn new() -> Self { - MaxUniverse { max_universe: ty::UniverseIndex::ROOT } - } - - pub fn max_universe(self) -> ty::UniverseIndex { - self.max_universe - } -} - -impl<'tcx> TypeVisitor> for MaxUniverse { - fn visit_ty(&mut self, t: Ty<'tcx>) { - if let ty::Placeholder(placeholder) = t.kind() { - self.max_universe = self.max_universe.max(placeholder.universe); - } - - t.super_visit_with(self) - } - - fn visit_const(&mut self, c: ty::consts::Const<'tcx>) { - if let ty::ConstKind::Placeholder(placeholder) = c.kind() { - self.max_universe = self.max_universe.max(placeholder.universe); - } - - c.super_visit_with(self) - } - - fn visit_region(&mut self, r: ty::Region<'tcx>) { - if let ty::RePlaceholder(placeholder) = r.kind() { - self.max_universe = self.max_universe.max(placeholder.universe); - } - } -} From a171ec3cf04be09b7889223caa1e9a7768d82de4 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 31 Jul 2025 16:46:21 +1000 Subject: [PATCH 230/682] Move `describe_as_module` from `rustc_middle::print` to `rustc:middle::query`. That way it doesn't need to be exported. --- compiler/rustc_middle/src/query/mod.rs | 11 ++++++++++- compiler/rustc_middle/src/ty/print/mod.rs | 12 +----------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 874cee54c7c6..8fba218cc71c 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -135,7 +135,7 @@ use crate::traits::{ }; use crate::ty::fast_reject::SimplifiedType; use crate::ty::layout::ValidityRequirement; -use crate::ty::print::{PrintTraitRefExt, describe_as_module}; +use crate::ty::print::PrintTraitRefExt; use crate::ty::util::AlwaysRequiresDrop; use crate::ty::{ self, CrateInherentImpls, GenericArg, GenericArgsRef, PseudoCanonicalInput, SizedTraitKind, Ty, @@ -2731,3 +2731,12 @@ rustc_queries! { rustc_with_all_queries! { define_callbacks! } rustc_feedable_queries! { define_feedable! } + +fn describe_as_module(def_id: impl Into, tcx: TyCtxt<'_>) -> String { + let def_id = def_id.into(); + if def_id.is_top_level_module() { + "top-level module".to_string() + } else { + format!("module `{}`", tcx.def_path_str(def_id)) + } +} diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index 9e6f277ef770..d636e8ef31ff 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -2,7 +2,7 @@ use hir::def::Namespace; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sso::SsoHashSet; use rustc_hir as hir; -use rustc_hir::def_id::{CrateNum, DefId, LocalDefId}; +use rustc_hir::def_id::{CrateNum, DefId}; use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; use tracing::{debug, instrument, trace}; @@ -396,16 +396,6 @@ impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for ty::Const<'tcx> { } } -// This is only used by query descriptions -pub fn describe_as_module(def_id: impl Into, tcx: TyCtxt<'_>) -> String { - let def_id = def_id.into(); - if def_id.is_top_level_module() { - "top-level module".to_string() - } else { - format!("module `{}`", tcx.def_path_str(def_id)) - } -} - impl rustc_type_ir::ir_print::IrPrint for TyCtxt<'_> where T: Copy + for<'a, 'tcx> Lift, Lifted: Print<'tcx, FmtPrinter<'a, 'tcx>>>, From 61af5da8dfc9f5fd6e2d36f8316f7bf5884f5388 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Fri, 14 Mar 2025 09:58:46 +0100 Subject: [PATCH 231/682] Implement more features for GenMC mode - Support for atomic fences. - Support for atomic read-modify-write (RMW). - Add tests using RMW and fences. - Add options: - to disable weak memory effects in GenMC mode. - to print GenMC execution graphs. - to print GenMC output message. - Fix GenMC full rebuild issue and run configure step when commit changes. - Do cleanup. Co-authored-by: Ralf Jung --- src/tools/miri/doc/genmc.md | 7 +- src/tools/miri/genmc-sys/build.rs | 37 +++- .../genmc-sys/cpp/include/MiriInterface.hpp | 29 +++ .../cpp/src/MiriInterface/EventHandling.cpp | 66 ++++++ .../genmc-sys/cpp/src/MiriInterface/Setup.cpp | 13 +- src/tools/miri/genmc-sys/src/lib.rs | 77 ++++++- src/tools/miri/src/bin/miri.rs | 23 +-- src/tools/miri/src/concurrency/data_race.rs | 12 +- .../miri/src/concurrency/genmc/config.rs | 60 +++++- src/tools/miri/src/concurrency/genmc/dummy.rs | 8 +- .../miri/src/concurrency/genmc/helper.rs | 54 ++++- src/tools/miri/src/concurrency/genmc/mod.rs | 191 ++++++++++++------ src/tools/miri/src/concurrency/genmc/run.rs | 13 +- .../miri/tests/genmc/fail/loom/buggy_inc.rs | 66 ++++++ .../tests/genmc/fail/loom/buggy_inc.stderr | 13 ++ .../fail/simple/2w2w_weak.relaxed4.stderr | 8 +- .../fail/simple/2w2w_weak.release4.stderr | 8 +- .../miri/tests/genmc/fail/simple/2w2w_weak.rs | 7 +- .../fail/simple/2w2w_weak.sc3_rel1.stderr | 8 +- .../miri/tests/genmc/pass/atomics/rmw_ops.rs | 91 +++++++++ .../tests/genmc/pass/atomics/rmw_ops.stderr | 2 + .../tests/genmc/pass/litmus/2w2w_2sc_scf.rs | 33 +++ .../genmc/pass/litmus/2w2w_2sc_scf.stderr | 2 + .../miri/tests/genmc/pass/litmus/2w2w_4rel.rs | 4 + .../genmc/pass/litmus/2w2w_4rel.sc.stderr | 2 + ...2w2w_4rel.stderr => 2w2w_4rel.weak.stderr} | 0 .../tests/genmc/pass/litmus/IRIW-acq-sc.rs | 2 +- .../miri/tests/genmc/pass/litmus/IRIWish.rs | 66 ++++++ .../tests/genmc/pass/litmus/IRIWish.stderr | 30 +++ .../miri/tests/genmc/pass/litmus/LB_incMPs.rs | 41 ++++ .../tests/genmc/pass/litmus/LB_incMPs.stderr | 2 + .../tests/genmc/pass/litmus/MPU_rels_acq.rs | 42 ++++ .../genmc/pass/litmus/MPU_rels_acq.stderr | 2 + .../miri/tests/genmc/pass/litmus/MP_incMPs.rs | 36 ++++ .../tests/genmc/pass/litmus/MP_incMPs.stderr | 2 + .../tests/genmc/pass/litmus/MP_rels_acqf.rs | 40 ++++ .../genmc/pass/litmus/MP_rels_acqf.stderr | 2 + .../tests/genmc/pass/litmus/SB_2sc_scf.rs | 32 +++ .../tests/genmc/pass/litmus/SB_2sc_scf.stderr | 2 + .../miri/tests/genmc/pass/litmus/Z6_U.rs | 65 ++++++ .../tests/genmc/pass/litmus/Z6_U.sc.stderr | 20 ++ .../tests/genmc/pass/litmus/Z6_U.weak.stderr | 24 +++ .../miri/tests/genmc/pass/litmus/Z6_acq.rs | 38 ++++ .../tests/genmc/pass/litmus/Z6_acq.stderr | 2 + .../miri/tests/genmc/pass/litmus/atomicpo.rs | 32 +++ .../tests/genmc/pass/litmus/atomicpo.stderr | 2 + .../tests/genmc/pass/litmus/cumul-release.rs | 58 ++++++ .../genmc/pass/litmus/cumul-release.stderr | 2 + .../tests/genmc/pass/litmus/fr_w_w_w_reads.rs | 1 - .../miri/tests/genmc/pass/litmus/inc2w.rs | 45 +++++ .../miri/tests/genmc/pass/litmus/inc2w.stderr | 2 + .../genmc/pass/litmus/inc_inc_RR_W_RR.rs | 63 ++++++ .../genmc/pass/litmus/inc_inc_RR_W_RR.stderr | 2 + .../miri/tests/genmc/pass/litmus/riwi.rs | 31 +++ .../miri/tests/genmc/pass/litmus/riwi.stderr | 2 + .../tests/genmc/pass/litmus/viktor-relseq.rs | 36 ++++ .../genmc/pass/litmus/viktor-relseq.stderr | 2 + src/tools/miri/tests/utils/genmc.rs | 7 + 58 files changed, 1431 insertions(+), 136 deletions(-) create mode 100644 src/tools/miri/tests/genmc/fail/loom/buggy_inc.rs create mode 100644 src/tools/miri/tests/genmc/fail/loom/buggy_inc.stderr create mode 100644 src/tools/miri/tests/genmc/pass/atomics/rmw_ops.rs create mode 100644 src/tools/miri/tests/genmc/pass/atomics/rmw_ops.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/2w2w_2sc_scf.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/2w2w_2sc_scf.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.sc.stderr rename src/tools/miri/tests/genmc/pass/litmus/{2w2w_4rel.stderr => 2w2w_4rel.weak.stderr} (100%) create mode 100644 src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/IRIWish.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/LB_incMPs.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/LB_incMPs.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/MP_incMPs.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/MP_incMPs.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/SB_2sc_scf.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/SB_2sc_scf.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/Z6_U.sc.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/Z6_U.weak.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/Z6_acq.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/Z6_acq.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/atomicpo.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/atomicpo.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/cumul-release.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/cumul-release.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/inc2w.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/inc2w.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/inc_inc_RR_W_RR.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/inc_inc_RR_W_RR.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/riwi.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/riwi.stderr create mode 100644 src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.rs create mode 100644 src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.stderr diff --git a/src/tools/miri/doc/genmc.md b/src/tools/miri/doc/genmc.md index fdbf2defe42d..8af697be34a6 100644 --- a/src/tools/miri/doc/genmc.md +++ b/src/tools/miri/doc/genmc.md @@ -24,6 +24,9 @@ Note that `cargo miri test` in GenMC mode is currently not supported. ### Supported Parameters - `-Zmiri-genmc`: Enable GenMC mode (not required if any other GenMC options are used). +- `-Zmiri-genmc-print-exec-graphs={none,explored,blocked,all}`: Make GenMC print the execution graph of the program after every explored, every blocked, or after every execution (default: None). +- `-Zmiri-genmc-print-exec-graphs`: Shorthand for suffix `=explored`. +- `-Zmiri-genmc-print-genmc-output`: Print the output that GenMC provides. NOTE: this output is quite verbose and the events in the printed execution graph are hard to map back to the Rust code location they originate from. - `-Zmiri-genmc-log=LOG_LEVEL`: Change the log level for GenMC. Default: `warning`. - `quiet`: Disable logging. - `error`: Print errors. @@ -34,7 +37,9 @@ Note that `cargo miri test` in GenMC mode is currently not supported. - `debug2`: Print the execution graph after every memory access. - `debug3`: Print reads-from values considered by GenMC. - +#### Regular Miri parameters useful for GenMC mode + +- `-Zmiri-disable-weak-memory-emulation`: Disable any weak memory effects (effectively upgrading all atomic orderings in the program to `SeqCst`). This option may reduce the number of explored program executions, but any bugs related to weak memory effects will be missed. This option can help determine if an error is caused by weak memory effects (i.e., if it disappears with this option enabled). diff --git a/src/tools/miri/genmc-sys/build.rs b/src/tools/miri/genmc-sys/build.rs index 8eb818a6b265..8d437c20a092 100644 --- a/src/tools/miri/genmc-sys/build.rs +++ b/src/tools/miri/genmc-sys/build.rs @@ -30,13 +30,23 @@ mod downloading { /// The GenMC commit we depend on. It must be available on the specified GenMC repository. pub(crate) const GENMC_COMMIT: &str = "af9cc9ccd5d412b16defc35dbf36571c63a19c76"; - pub(crate) fn download_genmc() -> PathBuf { + /// Ensure that a local GenMC repo is present and set to the correct commit. + /// Return the path of the GenMC repo and whether the checked out commit was changed. + pub(crate) fn download_genmc() -> (PathBuf, bool) { let Ok(genmc_download_path) = PathBuf::from_str(GENMC_DOWNLOAD_PATH); let commit_oid = Oid::from_str(GENMC_COMMIT).expect("Commit should be valid."); match Repository::open(&genmc_download_path) { Ok(repo) => { assert_repo_unmodified(&repo); + if let Ok(head) = repo.head() + && let Ok(head_commit) = head.peel_to_commit() + && head_commit.id() == commit_oid + { + // Fast path: The expected commit is already checked out. + return (genmc_download_path, false); + } + // Check if the local repository already contains the commit we need, download it otherwise. let commit = update_local_repo(&repo, commit_oid); checkout_commit(&repo, &commit); } @@ -51,7 +61,7 @@ mod downloading { } }; - genmc_download_path + (genmc_download_path, true) } fn get_remote(repo: &Repository) -> Remote<'_> { @@ -71,7 +81,8 @@ mod downloading { // Update remote URL. println!( - "cargo::warning=GenMC repository remote URL has changed from '{remote_url:?}' to '{GENMC_GITHUB_URL}'" + "cargo::warning=GenMC repository remote URL has changed from '{}' to '{GENMC_GITHUB_URL}'", + remote_url.unwrap_or_default() ); repo.remote_set_url("origin", GENMC_GITHUB_URL) .expect("cannot rename url of remote 'origin'"); @@ -175,7 +186,7 @@ fn link_to_llvm(config_file: &Path) -> (String, String) { } /// Build the GenMC model checker library and the Rust-C++ interop library with cxx.rs -fn compile_cpp_dependencies(genmc_path: &Path) { +fn compile_cpp_dependencies(genmc_path: &Path, always_configure: bool) { // Give each step a separate build directory to prevent interference. let out_dir = PathBuf::from(std::env::var("OUT_DIR").as_deref().unwrap()); let genmc_build_dir = out_dir.join("genmc"); @@ -184,15 +195,13 @@ fn compile_cpp_dependencies(genmc_path: &Path) { // Part 1: // Compile the GenMC library using cmake. - let cmakelists_path = genmc_path.join("CMakeLists.txt"); - // FIXME(genmc,cargo): Switch to using `CARGO_CFG_DEBUG_ASSERTIONS` once https://github.com/rust-lang/cargo/issues/15760 is completed. // Enable/disable additional debug checks, prints and options for GenMC, based on the Rust profile (debug/release) let enable_genmc_debug = matches!(std::env::var("PROFILE").as_deref().unwrap(), "debug"); - let mut config = cmake::Config::new(cmakelists_path); + let mut config = cmake::Config::new(genmc_path); config - .always_configure(false) // We don't need to reconfigure on subsequent compilation runs. + .always_configure(always_configure) // We force running the configure step when the GenMC commit changed. .out_dir(genmc_build_dir) .profile(GENMC_CMAKE_PROFILE) .define("GENMC_DEBUG", if enable_genmc_debug { "ON" } else { "OFF" }); @@ -251,7 +260,8 @@ fn compile_cpp_dependencies(genmc_path: &Path) { fn main() { // Select which path to use for the GenMC repo: - let genmc_path = if let Some(genmc_src_path) = option_env!("GENMC_SRC_PATH") { + let (genmc_path, always_configure) = if let Some(genmc_src_path) = option_env!("GENMC_SRC_PATH") + { let genmc_src_path = PathBuf::from_str(&genmc_src_path).expect("GENMC_SRC_PATH should contain a valid path"); assert!( @@ -261,13 +271,18 @@ fn main() { ); // Rebuild files in the given path change. println!("cargo::rerun-if-changed={}", genmc_src_path.display()); - genmc_src_path + // We disable `always_configure` when working with a local repository, + // since it increases compile times when working on `genmc-sys`. + (genmc_src_path, false) } else { + // Download GenMC if required and ensure that the correct commit is checked out. + // If anything changed in the downloaded repository (e.g., the commit), + // we set `always_configure` to ensure there are no weird configs from previous builds. downloading::download_genmc() }; // Build all required components: - compile_cpp_dependencies(&genmc_path); + compile_cpp_dependencies(&genmc_path, always_configure); // Only rebuild if anything changes: // Note that we don't add the downloaded GenMC repo, since that should never be modified diff --git a/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp index 4484f0b73cae..b07693758432 100644 --- a/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp +++ b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp @@ -33,6 +33,7 @@ struct GenmcScalar; struct SchedulingResult; struct LoadResult; struct StoreResult; +struct ReadModifyWriteResult; // GenMC uses `int` for its thread IDs. using ThreadId = int; @@ -90,6 +91,15 @@ struct MiriGenmcShim : private GenMCDriver { MemOrdering ord, GenmcScalar old_val ); + [[nodiscard]] ReadModifyWriteResult handle_read_modify_write( + ThreadId thread_id, + uint64_t address, + uint64_t size, + RMWBinOp rmw_op, + MemOrdering ordering, + GenmcScalar rhs_value, + GenmcScalar old_val + ); [[nodiscard]] StoreResult handle_store( ThreadId thread_id, uint64_t address, @@ -99,6 +109,8 @@ struct MiriGenmcShim : private GenMCDriver { MemOrdering ord ); + void handle_fence(ThreadId thread_id, MemOrdering ord); + /**** Memory (de)allocation ****/ auto handle_malloc(ThreadId thread_id, uint64_t size, uint64_t alignment) -> uint64_t; void handle_free(ThreadId thread_id, uint64_t address); @@ -271,4 +283,21 @@ inline StoreResult from_error(std::unique_ptr error) { } } // namespace StoreResultExt +namespace ReadModifyWriteResultExt { +inline ReadModifyWriteResult +ok(SVal old_value, SVal new_value, bool is_coherence_order_maximal_write) { + return ReadModifyWriteResult { /* error: */ std::unique_ptr(nullptr), + /* old_value: */ GenmcScalarExt::from_sval(old_value), + /* new_value: */ GenmcScalarExt::from_sval(new_value), + is_coherence_order_maximal_write }; +} + +inline ReadModifyWriteResult from_error(std::unique_ptr error) { + return ReadModifyWriteResult { /* error: */ std::move(error), + /* old_value: */ GenmcScalarExt::uninit(), + /* new_value: */ GenmcScalarExt::uninit(), + /* is_coherence_order_maximal_write: */ false }; +} +} // namespace ReadModifyWriteResultExt + #endif /* GENMC_MIRI_INTERFACE_HPP */ diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp index 7d8f0b16b49a..cd28e0d148f5 100644 --- a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp @@ -89,6 +89,72 @@ ); } +void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) { + const auto pos = inc_pos(thread_id); + GenMCDriver::handleFence(pos, ord, EventDeps()); +} + +[[nodiscard]] auto MiriGenmcShim::handle_read_modify_write( + ThreadId thread_id, + uint64_t address, + uint64_t size, + RMWBinOp rmw_op, + MemOrdering ordering, + GenmcScalar rhs_value, + GenmcScalar old_val +) -> ReadModifyWriteResult { + // NOTE: Both the store and load events should get the same `ordering`, it should not be split + // into a load and a store component. This means we can have for example `AcqRel` loads and + // stores, but this is intended for RMW operations. + + // Somewhat confusingly, the GenMC term for RMW read/write labels is + // `FaiRead` and `FaiWrite`. + const auto load_ret = handle_load_reset_if_none( + thread_id, + ordering, + SAddr(address), + ASize(size), + AType::Unsigned, // The type is only used for printing. + rmw_op, + GenmcScalarExt::to_sval(rhs_value), + EventDeps() + ); + if (const auto* err = std::get_if(&load_ret)) + return ReadModifyWriteResultExt::from_error(format_error(*err)); + + const auto* ret_val = std::get_if(&load_ret); + if (nullptr == ret_val) { + ERROR("Unimplemented: read-modify-write returned unexpected result."); + } + const auto read_old_val = *ret_val; + const auto new_value = + executeRMWBinOp(read_old_val, GenmcScalarExt::to_sval(rhs_value), size, rmw_op); + + const auto storePos = inc_pos(thread_id); + const auto store_ret = GenMCDriver::handleStore( + storePos, + ordering, + SAddr(address), + ASize(size), + AType::Unsigned, // The type is only used for printing. + new_value + ); + if (const auto* err = std::get_if(&store_ret)) + return ReadModifyWriteResultExt::from_error(format_error(*err)); + + const auto* store_ret_val = std::get_if(&store_ret); + ERROR_ON(nullptr == store_ret_val, "Unimplemented: RMW store returned unexpected result."); + + // FIXME(genmc,mixed-accesses): Use the value that GenMC returns from handleStore (once + // available). + const auto& g = getExec().getGraph(); + return ReadModifyWriteResultExt::ok( + /* old_value: */ read_old_val, + new_value, + /* is_coherence_order_maximal_write */ g.co_max(SAddr(address))->getPos() == storePos + ); +} + /**** Memory (de)allocation ****/ auto MiriGenmcShim::handle_malloc(ThreadId thread_id, uint64_t size, uint64_t alignment) diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp index 7194ed02da43..5a53fee05921 100644 --- a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp +++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp @@ -63,9 +63,12 @@ static auto to_genmc_verbosity_level(const LogLevel log_level) -> VerbosityLevel auto conf = std::make_shared(); // Set whether GenMC should print execution graphs after every explored/blocked execution. - // FIXME(genmc): pass these settings from Miri. - conf->printExecGraphs = false; - conf->printBlockedExecs = false; + conf->printExecGraphs = + (params.print_execution_graphs == ExecutiongraphPrinting::Explored || + params.print_execution_graphs == ExecutiongraphPrinting::ExploredAndBlocked); + conf->printBlockedExecs = + (params.print_execution_graphs == ExecutiongraphPrinting::Blocked || + params.print_execution_graphs == ExecutiongraphPrinting::ExploredAndBlocked); // `1024` is the default value that GenMC uses. // If any thread has at least this many events, a warning/tip will be printed. @@ -79,8 +82,8 @@ static auto to_genmc_verbosity_level(const LogLevel log_level) -> VerbosityLevel // Miri. conf->warnOnGraphSize = 1024 * 1024; - // We only support the RC11 memory model for Rust. - conf->model = ModelType::RC11; + // We only support the `RC11` memory model for Rust, and `SC` when weak memory emulation is disabled. + conf->model = params.disable_weak_memory_emulation ? ModelType::SC : ModelType::RC11; // This prints the seed that GenMC picks for randomized scheduling during estimation mode. conf->printRandomScheduleSeed = params.print_random_schedule_seed; diff --git a/src/tools/miri/genmc-sys/src/lib.rs b/src/tools/miri/genmc-sys/src/lib.rs index 406c90809fcd..1de4c4eb5e8e 100644 --- a/src/tools/miri/genmc-sys/src/lib.rs +++ b/src/tools/miri/genmc-sys/src/lib.rs @@ -53,7 +53,13 @@ impl GenmcScalar { impl Default for GenmcParams { fn default() -> Self { - Self { print_random_schedule_seed: false, do_symmetry_reduction: false } + Self { + print_random_schedule_seed: false, + do_symmetry_reduction: false, + // GenMC graphs can be quite large since Miri produces a lot of (non-atomic) events. + print_execution_graphs: ExecutiongraphPrinting::None, + disable_weak_memory_emulation: false, + } } } @@ -91,7 +97,10 @@ mod ffi { struct GenmcParams { pub print_random_schedule_seed: bool, pub do_symmetry_reduction: bool, - // FIXME(GenMC): Add remaining parameters. + pub print_execution_graphs: ExecutiongraphPrinting, + /// Enabling this will set the memory model used by GenMC to "Sequential Consistency" (SC). + /// This will disable any weak memory effects, which reduces the number of program executions that will be explored. + pub disable_weak_memory_emulation: bool, } /// This is mostly equivalent to GenMC `VerbosityLevel`, but the debug log levels are always present (not conditionally compiled based on `ENABLE_GENMC_DEBUG`). @@ -120,6 +129,19 @@ mod ffi { Debug3ReadsFrom, } + #[derive(Debug)] + /// Setting to control which execution graphs GenMC prints after every execution. + enum ExecutiongraphPrinting { + /// Print no graphs. + None, + /// Print graphs of all fully explored executions. + Explored, + /// Print graphs of all blocked executions. + Blocked, + /// Print graphs of all executions. + ExploredAndBlocked, + } + /// This type corresponds to `Option` (or `std::optional`), where `SVal` is the type that GenMC uses for storing values. /// CXX doesn't support `std::optional` currently, so we need to use an extra `bool` to define whether this value is initialized or not. #[derive(Debug, Clone, Copy)] @@ -163,7 +185,21 @@ mod ffi { is_coherence_order_maximal_write: bool, } - /**** Types shared between Miri/Rust and GenMC/C++ through cxx_bridge: ****/ + #[must_use] + #[derive(Debug)] + struct ReadModifyWriteResult { + /// If there was an error, it will be stored in `error`, otherwise it is `None`. + error: UniquePtr, + /// The value that was read by the RMW operation as the left operand. + old_value: GenmcScalar, + /// The value that was produced by the RMW operation. + new_value: GenmcScalar, + /// `true` if the write should also be reflected in Miri's memory representation. + is_coherence_order_maximal_write: bool, + } + + /**** These are GenMC types that we have to copy-paste here since cxx does not support + "importing" externally defined C++ types. ****/ #[derive(Debug)] /// Corresponds to GenMC's type with the same name. @@ -188,6 +224,21 @@ mod ffi { SequentiallyConsistent = 6, } + #[derive(Debug)] + enum RMWBinOp { + Xchg = 0, + Add = 1, + Sub = 2, + And = 3, + Nand = 4, + Or = 5, + Xor = 6, + Max = 7, + Min = 8, + UMax = 9, + UMin = 10, + } + // # Safety // // This block is unsafe to allow defining safe methods inside. @@ -202,9 +253,12 @@ mod ffi { /**** Types shared between Miri/Rust and Miri/C++: ****/ type MiriGenmcShim; - /**** Types shared between Miri/Rust and GenMC/C++: ****/ + /**** Types shared between Miri/Rust and GenMC/C++: + (This tells cxx that the enums defined above are already defined on the C++ side; + it will emit assertions to ensure that the two definitions agree.) ****/ type ActionKind; type MemOrdering; + type RMWBinOp; /// Set the log level for GenMC. /// @@ -249,6 +303,16 @@ mod ffi { memory_ordering: MemOrdering, old_value: GenmcScalar, ) -> LoadResult; + fn handle_read_modify_write( + self: Pin<&mut MiriGenmcShim>, + thread_id: i32, + address: u64, + size: u64, + rmw_op: RMWBinOp, + ordering: MemOrdering, + rhs_value: GenmcScalar, + old_value: GenmcScalar, + ) -> ReadModifyWriteResult; fn handle_store( self: Pin<&mut MiriGenmcShim>, thread_id: i32, @@ -258,6 +322,11 @@ mod ffi { old_value: GenmcScalar, memory_ordering: MemOrdering, ) -> StoreResult; + fn handle_fence( + self: Pin<&mut MiriGenmcShim>, + thread_id: i32, + memory_ordering: MemOrdering, + ); /**** Memory (de)allocation ****/ fn handle_malloc( diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index ff05b4b14c79..731fe283a219 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -746,24 +746,11 @@ fn main() { many_seeds.map(|seeds| ManySeedsConfig { seeds, keep_going: many_seeds_keep_going }); // Validate settings for data race detection and GenMC mode. - if miri_config.genmc_config.is_some() { - if !miri_config.data_race_detector { - fatal_error!("Cannot disable data race detection in GenMC mode"); - } else if !miri_config.weak_memory_emulation { - fatal_error!("Cannot disable weak memory emulation in GenMC mode"); - } else if !miri_config.native_lib.is_empty() { - fatal_error!("native-lib not supported in GenMC mode."); - } - if miri_config.borrow_tracker.is_some() { - eprintln!( - "warning: borrow tracking has been disabled, it is not (yet) supported in GenMC mode." - ); - miri_config.borrow_tracker = None; - } - // We enable fixed scheduling so Miri doesn't randomly yield before a terminator, which anyway - // would be a NOP in GenMC mode. - miri_config.fixed_scheduling = true; - } else if miri_config.weak_memory_emulation && !miri_config.data_race_detector { + if let Err(err) = GenmcConfig::validate_genmc_mode_settings(&mut miri_config) { + fatal_error!("Invalid settings: {err}"); + } + + if miri_config.weak_memory_emulation && !miri_config.data_race_detector { fatal_error!( "Weak memory emulation cannot be enabled when the data race detector is disabled" ); diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs index a7822d0d6fa3..30061a78594a 100644 --- a/src/tools/miri/src/concurrency/data_race.rs +++ b/src/tools/miri/src/concurrency/data_race.rs @@ -793,9 +793,9 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { this, place.ptr().addr(), place.layout.size, + atomic_op, place.layout.backend_repr.is_signed(), ord, - atomic_op, rhs.to_scalar(), old.to_scalar(), )?; @@ -901,7 +901,6 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { can_fail_spuriously, old.to_scalar(), )?; - // The store might be the latest store in coherence order (determined by GenMC). // If it is, we need to update the value in Miri's memory: if let Some(new_value) = new_value { @@ -961,6 +960,11 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { /// The closure will only be invoked if data race handling is on. fn release_clock(&self, callback: impl FnOnce(&VClock) -> R) -> Option { let this = self.eval_context_ref(); + // FIXME: make this a proper error instead of ICEing the interpreter. + assert!( + this.machine.data_race.as_genmc_ref().is_none(), + "this operation performs synchronization that is not supported in GenMC mode" + ); Some( this.machine.data_race.as_vclocks_ref()?.release_clock(&this.machine.threads, callback), ) @@ -973,7 +977,9 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { match &this.machine.data_race { GlobalDataRaceHandler::None => {} GlobalDataRaceHandler::Genmc(_genmc_ctx) => - throw_unsup_format!("acquire_clock is not (yet) supported in GenMC mode."), + throw_unsup_format!( + "this operation performs synchronization that is not supported in GenMC mode" + ), GlobalDataRaceHandler::Vclocks(data_race) => data_race.acquire_clock(clock, &this.machine.threads), } diff --git a/src/tools/miri/src/concurrency/genmc/config.rs b/src/tools/miri/src/concurrency/genmc/config.rs index 66116d5cf23c..34933d423f06 100644 --- a/src/tools/miri/src/concurrency/genmc/config.rs +++ b/src/tools/miri/src/concurrency/genmc/config.rs @@ -1,4 +1,7 @@ -use genmc_sys::{GenmcParams, LogLevel}; +use genmc_sys::LogLevel; + +use super::GenmcParams; +use crate::{IsolatedOp, MiriConfig, RejectOpWith}; /// Configuration for GenMC mode. /// The `params` field is shared with the C++ side. @@ -7,6 +10,9 @@ use genmc_sys::{GenmcParams, LogLevel}; pub struct GenmcConfig { /// Parameters sent to the C++ side to create a new handle to the GenMC model checker. pub(super) params: GenmcParams, + /// Print the output message that GenMC generates when an error occurs. + /// This error message is currently hard to use, since there is no clear mapping between the events that GenMC sees and the Rust code location where this event was produced. + pub(super) print_genmc_output: bool, /// The log level for GenMC. pub(super) log_level: LogLevel, } @@ -36,9 +42,61 @@ impl GenmcConfig { }; if let Some(log_level) = trimmed_arg.strip_prefix("log=") { genmc_config.log_level = log_level.parse()?; + } else if let Some(trimmed_arg) = trimmed_arg.strip_prefix("print-exec-graphs") { + use genmc_sys::ExecutiongraphPrinting; + genmc_config.params.print_execution_graphs = match trimmed_arg { + "=none" => ExecutiongraphPrinting::None, + // Make GenMC print explored executions. + "" | "=explored" => ExecutiongraphPrinting::Explored, + // Make GenMC print blocked executions. + "=blocked" => ExecutiongraphPrinting::Blocked, + // Make GenMC print all executions. + "=all" => ExecutiongraphPrinting::ExploredAndBlocked, + _ => + return Err(format!( + "Invalid suffix to GenMC argument '-Zmiri-genmc-print-exec-graphs', expected '', '=none', '=explored', '=blocked' or '=all'" + )), + } + } else if trimmed_arg == "print-genmc-output" { + genmc_config.print_genmc_output = true; } else { return Err(format!("Invalid GenMC argument: \"-Zmiri-genmc-{trimmed_arg}\"")); } Ok(()) } + + /// Validate settings for GenMC mode (NOP if GenMC mode disabled). + /// + /// Unsupported configurations return an error. + /// Adjusts Miri settings where required, printing a warnings if the change might be unexpected for the user. + pub fn validate_genmc_mode_settings(miri_config: &mut MiriConfig) -> Result<(), &'static str> { + let Some(genmc_config) = miri_config.genmc_config.as_mut() else { + return Ok(()); + }; + + // Check for disallowed configurations. + if !miri_config.data_race_detector { + return Err("Cannot disable data race detection in GenMC mode"); + } else if !miri_config.native_lib.is_empty() { + return Err("native-lib not supported in GenMC mode."); + } else if miri_config.isolated_op != IsolatedOp::Reject(RejectOpWith::Abort) { + return Err("Cannot disable isolation in GenMC mode"); + } + + // Adjust settings where needed. + if !miri_config.weak_memory_emulation { + genmc_config.params.disable_weak_memory_emulation = true; + } + if miri_config.borrow_tracker.is_some() { + eprintln!( + "warning: borrow tracking has been disabled, it is not (yet) supported in GenMC mode." + ); + miri_config.borrow_tracker = None; + } + // We enable fixed scheduling so Miri doesn't randomly yield before a terminator, which anyway + // would be a NOP in GenMC mode. + miri_config.fixed_scheduling = true; + + Ok(()) + } } diff --git a/src/tools/miri/src/concurrency/genmc/dummy.rs b/src/tools/miri/src/concurrency/genmc/dummy.rs index 7c420961ec54..92b34b83ee0f 100644 --- a/src/tools/miri/src/concurrency/genmc/dummy.rs +++ b/src/tools/miri/src/concurrency/genmc/dummy.rs @@ -94,9 +94,9 @@ impl GenmcCtx { _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, _address: Size, _size: Size, + _atomic_op: AtomicRmwOp, _is_signed: bool, _ordering: AtomicRwOrd, - _atomic_op: AtomicRmwOp, _rhs_scalar: Scalar, _old_value: Scalar, ) -> InterpResult<'tcx, (Scalar, Option)> { @@ -242,4 +242,10 @@ impl GenmcConfig { Err(format!("GenMC is not supported on this target")) } } + + pub fn validate_genmc_mode_settings( + _miri_config: &mut crate::MiriConfig, + ) -> Result<(), &'static str> { + Ok(()) + } } diff --git a/src/tools/miri/src/concurrency/genmc/helper.rs b/src/tools/miri/src/concurrency/genmc/helper.rs index b70ca8faadfe..2a84ca203667 100644 --- a/src/tools/miri/src/concurrency/genmc/helper.rs +++ b/src/tools/miri/src/concurrency/genmc/helper.rs @@ -1,11 +1,16 @@ -use genmc_sys::MemOrdering; +use genmc_sys::{MemOrdering, RMWBinOp}; use rustc_abi::Size; use rustc_const_eval::interpret::{InterpResult, interp_ok}; +use rustc_middle::mir; use rustc_middle::ty::ScalarInt; use tracing::debug; use super::GenmcScalar; -use crate::{AtomicReadOrd, AtomicWriteOrd, MiriInterpCx, Scalar, throw_unsup_format}; +use crate::intrinsics::AtomicRmwOp; +use crate::{ + AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, MiriInterpCx, Scalar, + throw_unsup_format, +}; /// Maximum size memory access in bytes that GenMC supports. pub(super) const MAX_ACCESS_SIZE: u64 = 8; @@ -95,3 +100,48 @@ impl AtomicWriteOrd { } } } + +impl AtomicFenceOrd { + pub(super) fn to_genmc(self) -> MemOrdering { + match self { + AtomicFenceOrd::Acquire => MemOrdering::Acquire, + AtomicFenceOrd::Release => MemOrdering::Release, + AtomicFenceOrd::AcqRel => MemOrdering::AcquireRelease, + AtomicFenceOrd::SeqCst => MemOrdering::SequentiallyConsistent, + } + } +} + +impl AtomicRwOrd { + pub(super) fn to_genmc(self) -> MemOrdering { + match self { + AtomicRwOrd::Relaxed => MemOrdering::Relaxed, + AtomicRwOrd::Acquire => MemOrdering::Acquire, + AtomicRwOrd::Release => MemOrdering::Release, + AtomicRwOrd::AcqRel => MemOrdering::AcquireRelease, + AtomicRwOrd::SeqCst => MemOrdering::SequentiallyConsistent, + } + } +} + +/// Convert an atomic binary operation to its GenMC counterpart. +pub(super) fn to_genmc_rmw_op(atomic_op: AtomicRmwOp, is_signed: bool) -> RMWBinOp { + match (atomic_op, is_signed) { + (AtomicRmwOp::Min, true) => RMWBinOp::Min, + (AtomicRmwOp::Max, true) => RMWBinOp::Max, + (AtomicRmwOp::Min, false) => RMWBinOp::UMin, + (AtomicRmwOp::Max, false) => RMWBinOp::UMax, + (AtomicRmwOp::MirOp { op, neg }, _is_signed) => + match (op, neg) { + (mir::BinOp::Add, false) => RMWBinOp::Add, + (mir::BinOp::Sub, false) => RMWBinOp::Sub, + (mir::BinOp::BitXor, false) => RMWBinOp::Xor, + (mir::BinOp::BitAnd, false) => RMWBinOp::And, + (mir::BinOp::BitAnd, true) => RMWBinOp::Nand, + (mir::BinOp::BitOr, false) => RMWBinOp::Or, + _ => { + panic!("unsupported atomic operation: bin_op: {op:?}, negate: {neg}"); + } + }, + } +} diff --git a/src/tools/miri/src/concurrency/genmc/mod.rs b/src/tools/miri/src/concurrency/genmc/mod.rs index 26ce0b9c43ac..c1da86c67336 100644 --- a/src/tools/miri/src/concurrency/genmc/mod.rs +++ b/src/tools/miri/src/concurrency/genmc/mod.rs @@ -2,7 +2,7 @@ use std::cell::{Cell, RefCell}; use std::sync::Arc; use genmc_sys::{ - GENMC_GLOBAL_ADDRESSES_MASK, GenmcScalar, MemOrdering, MiriGenmcShim, UniquePtr, + GENMC_GLOBAL_ADDRESSES_MASK, GenmcScalar, MemOrdering, MiriGenmcShim, RMWBinOp, UniquePtr, create_genmc_driver_handle, }; use rustc_abi::{Align, Size}; @@ -12,7 +12,9 @@ use rustc_middle::{throw_machine_stop, throw_ub_format, throw_unsup_format}; use tracing::{debug, info}; use self::global_allocations::{EvalContextExt as _, GlobalAllocationHandler}; -use self::helper::{MAX_ACCESS_SIZE, genmc_scalar_to_scalar, scalar_to_genmc_scalar}; +use self::helper::{ + MAX_ACCESS_SIZE, genmc_scalar_to_scalar, scalar_to_genmc_scalar, to_genmc_rmw_op, +}; use self::thread_id_map::ThreadIdMap; use crate::concurrency::genmc::helper::split_access; use crate::intrinsics::AtomicRmwOp; @@ -29,6 +31,8 @@ mod run; pub(crate) mod scheduling; mod thread_id_map; +pub use genmc_sys::GenmcParams; + pub use self::config::GenmcConfig; pub use self::run::run_genmc_mode; @@ -126,22 +130,19 @@ impl GenmcCtx { /// Get the number of blocked executions encountered by GenMC. pub fn get_blocked_execution_count(&self) -> u64 { - let mc = self.handle.borrow(); - mc.as_ref().unwrap().get_blocked_execution_count() + self.handle.borrow().get_blocked_execution_count() } /// Get the number of explored executions encountered by GenMC. pub fn get_explored_execution_count(&self) -> u64 { - let mc = self.handle.borrow(); - mc.as_ref().unwrap().get_explored_execution_count() + self.handle.borrow().get_explored_execution_count() } /// Check if GenMC encountered an error that wasn't immediately returned during execution. /// Returns a string representation of the error if one occurred. pub fn try_get_error(&self) -> Option { - let mc = self.handle.borrow(); - mc.as_ref() - .unwrap() + self.handle + .borrow() .get_error_string() .as_ref() .map(|error| error.to_string_lossy().to_string()) @@ -150,9 +151,8 @@ impl GenmcCtx { /// Check if GenMC encountered an error that wasn't immediately returned during execution. /// Returns a string representation of the error if one occurred. pub fn get_result_message(&self) -> String { - let mc = self.handle.borrow(); - mc.as_ref() - .unwrap() + self.handle + .borrow() .get_result_message() .as_ref() .map(|error| error.to_string_lossy().to_string()) @@ -163,8 +163,7 @@ impl GenmcCtx { /// /// In GenMC mode, the input program should be repeatedly executed until this function returns `true` or an error is found. pub fn is_exploration_done(&self) -> bool { - let mut mc = self.handle.borrow_mut(); - mc.as_mut().unwrap().is_exploration_done() + self.handle.borrow_mut().pin_mut().is_exploration_done() } /// Select whether data race free actions should be allowed. This function should be used carefully! @@ -196,8 +195,7 @@ impl GenmcCtx { // Reset per-execution state. self.exec_state.reset(); // Inform GenMC about the new execution. - let mut mc = self.handle.borrow_mut(); - mc.as_mut().unwrap().handle_execution_start(); + self.handle.borrow_mut().pin_mut().handle_execution_start(); } /// Inform GenMC that the program's execution has ended. @@ -214,13 +212,11 @@ impl GenmcCtx { /// /// To get the all messages (warnings, errors) that GenMC produces, use the `get_result_message` method. fn handle_execution_end(&self) -> Option { - let mut mc = self.handle.borrow_mut(); - let result = mc.as_mut().unwrap().handle_execution_end(); + let result = self.handle.borrow_mut().pin_mut().handle_execution_end(); result.as_ref().map(|msg| msg.to_string_lossy().to_string())?; // GenMC currently does not return an error value immediately in all cases. // We manually query for any errors here to ensure we don't miss any. - drop(mc); // `try_get_error` needs access to the `RefCell`. self.try_get_error() } @@ -282,11 +278,17 @@ impl GenmcCtx { /// Inform GenMC about an atomic fence. pub(crate) fn atomic_fence<'tcx>( &self, - _machine: &MiriMachine<'tcx>, - _ordering: AtomicFenceOrd, + machine: &MiriMachine<'tcx>, + ordering: AtomicFenceOrd, ) -> InterpResult<'tcx> { assert!(!self.get_alloc_data_races(), "atomic fence with data race checking disabled."); - throw_unsup_format!("FIXME(genmc): Add support for atomic fences.") + + let thread_infos = self.exec_state.thread_id_manager.borrow(); + let curr_thread = machine.threads.active_thread(); + let genmc_tid = thread_infos.get_genmc_tid(curr_thread); + + self.handle.borrow_mut().pin_mut().handle_fence(genmc_tid, ordering.to_genmc()); + interp_ok(()) } /// Inform GenMC about an atomic read-modify-write operation. @@ -296,20 +298,24 @@ impl GenmcCtx { /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized. pub(crate) fn atomic_rmw_op<'tcx>( &self, - _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - _address: Size, - _size: Size, - _is_signed: bool, - _ordering: AtomicRwOrd, - _atomic_op: AtomicRmwOp, - _rhs_scalar: Scalar, - _old_value: Scalar, + ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + address: Size, + size: Size, + atomic_op: AtomicRmwOp, + is_signed: bool, + ordering: AtomicRwOrd, + rhs_scalar: Scalar, + old_value: Scalar, ) -> InterpResult<'tcx, (Scalar, Option)> { - assert!( - !self.get_alloc_data_races(), - "atomic read-modify-write operation with data race checking disabled." - ); - throw_unsup_format!("FIXME(genmc): Add support for atomic RMW.") + self.handle_atomic_rmw_op( + ecx, + address, + size, + ordering, + to_genmc_rmw_op(atomic_op, is_signed), + scalar_to_genmc_scalar(ecx, rhs_scalar)?, + scalar_to_genmc_scalar(ecx, old_value)?, + ) } /// Returns `(old_val, Option)`. `new_val` might not be the latest write in coherence order, which is indicated by `None`. @@ -317,18 +323,22 @@ impl GenmcCtx { /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized. pub(crate) fn atomic_exchange<'tcx>( &self, - _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, - _address: Size, - _size: Size, - _rhs_scalar: Scalar, - _ordering: AtomicRwOrd, - _old_value: Scalar, + ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + address: Size, + size: Size, + rhs_scalar: Scalar, + ordering: AtomicRwOrd, + old_value: Scalar, ) -> InterpResult<'tcx, (Scalar, Option)> { - assert!( - !self.get_alloc_data_races(), - "atomic swap operation with data race checking disabled." - ); - throw_unsup_format!("FIXME(genmc): Add support for atomic swap.") + self.handle_atomic_rmw_op( + ecx, + address, + size, + ordering, + /* genmc_rmw_op */ RMWBinOp::Xchg, + scalar_to_genmc_scalar(ecx, rhs_scalar)?, + scalar_to_genmc_scalar(ecx, old_value)?, + ) } /// Inform GenMC about an atomic compare-exchange operation. @@ -493,9 +503,11 @@ impl GenmcCtx { // GenMC doesn't support ZSTs, so we set the minimum size to 1 byte let genmc_size = size.bytes().max(1); - let mut mc = self.handle.borrow_mut(); - let pinned_mc = mc.as_mut().unwrap(); - let chosen_address = pinned_mc.handle_malloc(genmc_tid, genmc_size, alignment.bytes()); + let chosen_address = self.handle.borrow_mut().pin_mut().handle_malloc( + genmc_tid, + genmc_size, + alignment.bytes(), + ); // Non-global addresses should not be in the global address space or null. assert_ne!(0, chosen_address, "GenMC malloc returned nullptr."); @@ -530,9 +542,7 @@ impl GenmcCtx { let curr_thread = machine.threads.active_thread(); let genmc_tid = thread_infos.get_genmc_tid(curr_thread); - let mut mc = self.handle.borrow_mut(); - let pinned_mc = mc.as_mut().unwrap(); - pinned_mc.handle_free(genmc_tid, address.bytes()); + self.handle.borrow_mut().pin_mut().handle_free(genmc_tid, address.bytes()); interp_ok(()) } @@ -554,9 +564,7 @@ impl GenmcCtx { let genmc_parent_tid = thread_infos.get_genmc_tid(curr_thread_id); let genmc_new_tid = thread_infos.add_thread(new_thread_id); - let mut mc = self.handle.borrow_mut(); - mc.as_mut().unwrap().handle_thread_create(genmc_new_tid, genmc_parent_tid); - + self.handle.borrow_mut().pin_mut().handle_thread_create(genmc_new_tid, genmc_parent_tid); interp_ok(()) } @@ -571,8 +579,7 @@ impl GenmcCtx { let genmc_curr_tid = thread_infos.get_genmc_tid(active_thread_id); let genmc_child_tid = thread_infos.get_genmc_tid(child_thread_id); - let mut mc = self.handle.borrow_mut(); - mc.as_mut().unwrap().handle_thread_join(genmc_curr_tid, genmc_child_tid); + self.handle.borrow_mut().pin_mut().handle_thread_join(genmc_curr_tid, genmc_child_tid); interp_ok(()) } @@ -585,9 +592,8 @@ impl GenmcCtx { let genmc_tid = thread_infos.get_genmc_tid(curr_thread_id); debug!("GenMC: thread {curr_thread_id:?} ({genmc_tid:?}) finished."); - let mut mc = self.handle.borrow_mut(); // NOTE: Miri doesn't support return values for threads, but GenMC expects one, so we return 0 - mc.as_mut().unwrap().handle_thread_finish(genmc_tid, /* ret_val */ 0); + self.handle.borrow_mut().pin_mut().handle_thread_finish(genmc_tid, /* ret_val */ 0); } /// Handle a call to `libc::exit` or the exit of the main thread. @@ -618,8 +624,7 @@ impl GenmcCtx { let thread_infos = self.exec_state.thread_id_manager.borrow(); let genmc_tid = thread_infos.get_genmc_tid(thread); - let mut mc = self.handle.borrow_mut(); - mc.as_mut().unwrap().handle_thread_kill(genmc_tid); + self.handle.borrow_mut().pin_mut().handle_thread_kill(genmc_tid); } else { assert_eq!(thread, ThreadId::MAIN_THREAD); } @@ -669,9 +674,7 @@ impl GenmcCtx { addr = address.bytes() ); - let mut mc = self.handle.borrow_mut(); - let pinned_mc = mc.as_mut().unwrap(); - let load_result = pinned_mc.handle_load( + let load_result = self.handle.borrow_mut().pin_mut().handle_load( genmc_tid, address.bytes(), size.bytes(), @@ -722,9 +725,7 @@ impl GenmcCtx { addr = address.bytes() ); - let mut mc = self.handle.borrow_mut(); - let pinned_mc = mc.as_mut().unwrap(); - let store_result = pinned_mc.handle_store( + let store_result = self.handle.borrow_mut().pin_mut().handle_store( genmc_tid, address.bytes(), size.bytes(), @@ -741,6 +742,62 @@ impl GenmcCtx { interp_ok(store_result.is_coherence_order_maximal_write) } + /// Inform GenMC about an atomic read-modify-write operation. + /// This includes atomic swap (also often called "exchange"), but does *not* + /// include compare-exchange (see `RMWBinOp` for full list of operations). + /// Returns the previous value at that memory location, and optionally the value that should be written back to Miri's memory. + fn handle_atomic_rmw_op<'tcx>( + &self, + ecx: &InterpCx<'tcx, MiriMachine<'tcx>>, + address: Size, + size: Size, + ordering: AtomicRwOrd, + genmc_rmw_op: RMWBinOp, + genmc_rhs_scalar: GenmcScalar, + genmc_old_value: GenmcScalar, + ) -> InterpResult<'tcx, (Scalar, Option)> { + assert!( + !self.get_alloc_data_races(), + "atomic read-modify-write operation with data race checking disabled." + ); + assert_ne!(0, size.bytes()); + assert!( + size.bytes() <= MAX_ACCESS_SIZE, + "GenMC currently does not support atomic accesses larger than {} bytes (got {} bytes)", + MAX_ACCESS_SIZE, + size.bytes() + ); + + let curr_thread_id = ecx.machine.threads.active_thread(); + let genmc_tid = self.exec_state.thread_id_manager.borrow().get_genmc_tid(curr_thread_id); + debug!( + "GenMC: atomic_rmw_op, thread: {curr_thread_id:?} ({genmc_tid:?}) (op: {genmc_rmw_op:?}, rhs value: {genmc_rhs_scalar:?}), address: {address:?}, size: {size:?}, ordering: {ordering:?}", + ); + let rmw_result = self.handle.borrow_mut().pin_mut().handle_read_modify_write( + genmc_tid, + address.bytes(), + size.bytes(), + genmc_rmw_op, + ordering.to_genmc(), + genmc_rhs_scalar, + genmc_old_value, + ); + + if let Some(error) = rmw_result.error.as_ref() { + // FIXME(genmc): error handling + throw_ub_format!("{}", error.to_string_lossy()); + } + + let old_value_scalar = genmc_scalar_to_scalar(ecx, rmw_result.old_value, size)?; + + let new_value_scalar = if rmw_result.is_coherence_order_maximal_write { + Some(genmc_scalar_to_scalar(ecx, rmw_result.new_value, size)?) + } else { + None + }; + interp_ok((old_value_scalar, new_value_scalar)) + } + /**** Blocking functionality ****/ /// Handle a user thread getting blocked. diff --git a/src/tools/miri/src/concurrency/genmc/run.rs b/src/tools/miri/src/concurrency/genmc/run.rs index 62553297a202..24d13e6375b1 100644 --- a/src/tools/miri/src/concurrency/genmc/run.rs +++ b/src/tools/miri/src/concurrency/genmc/run.rs @@ -18,6 +18,8 @@ pub fn run_genmc_mode<'tcx>( eval_entry: impl Fn(Rc) -> Option, tcx: TyCtxt<'tcx>, ) -> Option { + let genmc_config = config.genmc_config.as_ref().unwrap(); + // There exists only one `global_state` per full run in GenMC mode. // It is shared by all `GenmcCtx` in this run. // FIXME(genmc): implement multithreading once GenMC supports it. @@ -32,8 +34,15 @@ pub fn run_genmc_mode<'tcx>( genmc_ctx.prepare_next_execution(); // Execute the program until completion to get the return value, or return if an error happens: - // FIXME(genmc): add an option to allow the user to see the GenMC output message when the verification is done. - let return_code = eval_entry(genmc_ctx.clone())?; + let Some(return_code) = eval_entry(genmc_ctx.clone()) else { + // If requested, print the output GenMC produced: + if genmc_config.print_genmc_output { + eprintln!("== raw GenMC output ========================="); + eprintln!("{}", genmc_ctx.get_result_message()); + eprintln!("== end of raw GenMC output =================="); + } + return None; + }; // We inform GenMC that the execution is complete. If there was an error, we print it. if let Some(error) = genmc_ctx.handle_execution_end() { diff --git a/src/tools/miri/tests/genmc/fail/loom/buggy_inc.rs b/src/tools/miri/tests/genmc/fail/loom/buggy_inc.rs new file mode 100644 index 000000000000..508eae756f32 --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/loom/buggy_inc.rs @@ -0,0 +1,66 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: Copyright (c) 2019 Carl Lerche + +// This is the test `checks_fail` from loom/test/smoke.rs adapted for Miri-GenMC. +// https://github.com/tokio-rs/loom/blob/dbf32b04bae821c64be44405a0bb72ca08741558/tests/smoke.rs + +// This test checks that an incorrect implementation of an incrementing counter is detected. +// The counter behaves wrong if two threads try to increment at the same time (increments can be lost). + +#![no_main] + +#[cfg(not(any(non_genmc_std, genmc_std)))] +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +struct BuggyInc { + num: AtomicUsize, +} + +impl BuggyInc { + const fn new() -> BuggyInc { + BuggyInc { num: AtomicUsize::new(0) } + } + + fn inc(&self) { + // The bug is here: + // Another thread can increment `self.num` between the next two lines, + // which is then overridden by this thread. + let curr = self.num.load(Acquire); + self.num.store(curr + 1, Release); + } +} + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + static BUGGY_INC: BuggyInc = BuggyInc::new(); + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + BUGGY_INC.num.store(0, Relaxed); + + let ids = [ + spawn_pthread_closure(|| { + BUGGY_INC.inc(); + }), + spawn_pthread_closure(|| { + BUGGY_INC.inc(); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // We check that we can detect the incorrect counter implementation: + if 2 != BUGGY_INC.num.load(Relaxed) { + std::process::abort(); //~ ERROR: abnormal termination + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/fail/loom/buggy_inc.stderr b/src/tools/miri/tests/genmc/fail/loom/buggy_inc.stderr new file mode 100644 index 000000000000..5a8948ff9b4b --- /dev/null +++ b/src/tools/miri/tests/genmc/fail/loom/buggy_inc.stderr @@ -0,0 +1,13 @@ +error: abnormal termination: the program aborted execution + --> tests/genmc/fail/loom/buggy_inc.rs:LL:CC + | +LL | std::process::abort(); + | ^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here + | + = note: BACKTRACE: + = note: inside `miri_start` at tests/genmc/fail/loom/buggy_inc.rs:LL:CC + +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/genmc/fail/simple/2w2w_weak.relaxed4.stderr b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr index e2b114df652f..cbfb6ec833eb 100644 --- a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr +++ b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr @@ -1,11 +1,9 @@ -error: Undefined Behavior: entering unreachable code +error: abnormal termination: the program aborted execution --> tests/genmc/fail/simple/2w2w_weak.rs:LL:CC | -LL | std::hint::unreachable_unchecked(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here +LL | std::process::abort(); + | ^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here | - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE: = note: inside `miri_start` at tests/genmc/fail/simple/2w2w_weak.rs:LL:CC diff --git a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.release4.stderr b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.release4.stderr index e2b114df652f..cbfb6ec833eb 100644 --- a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.release4.stderr +++ b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.release4.stderr @@ -1,11 +1,9 @@ -error: Undefined Behavior: entering unreachable code +error: abnormal termination: the program aborted execution --> tests/genmc/fail/simple/2w2w_weak.rs:LL:CC | -LL | std::hint::unreachable_unchecked(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here +LL | std::process::abort(); + | ^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here | - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE: = note: inside `miri_start` at tests/genmc/fail/simple/2w2w_weak.rs:LL:CC diff --git a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.rs b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.rs index dea2b5f952e7..baf3584966ec 100644 --- a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.rs +++ b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.rs @@ -7,7 +7,7 @@ // When using any combination of orderings except using all 4 `SeqCst`, the memory model allows the program to result in (X, Y) == (1, 1). // The "pass" variants only check that we get the expected number of executions (3 for all SC, 4 otherwise), // and a valid outcome every execution, but do not check that we get all allowed results. -// This "fail" variant ensures we can explore the execution resulting in (1, 1), an incorrect unsafe assumption that the result (1, 1) is impossible. +// This "fail" variant ensures we can explore the execution resulting in (1, 1), with an incorrect assumption that the result (1, 1) is impossible. // // Miri without GenMC is unable to produce this program execution and thus detect the incorrect assumption, even with `-Zmiri-many-seeds`. // @@ -61,11 +61,10 @@ fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { // Join so we can read the final values. join_pthreads(ids); - // We mark the result (1, 1) as unreachable, which is incorrect. + // We incorrectly assume that the result (1, 1) as unreachable. let result = (X.load(Relaxed), Y.load(Relaxed)); if result == (1, 1) { - // FIXME(genmc): Use `std::process::abort()` once backtraces for that are improved (https://github.com/rust-lang/rust/pull/146118) - std::hint::unreachable_unchecked(); //~ ERROR: entering unreachable code + std::process::abort(); //~ ERROR: abnormal termination } 0 diff --git a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr index e2b114df652f..cbfb6ec833eb 100644 --- a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr +++ b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr @@ -1,11 +1,9 @@ -error: Undefined Behavior: entering unreachable code +error: abnormal termination: the program aborted execution --> tests/genmc/fail/simple/2w2w_weak.rs:LL:CC | -LL | std::hint::unreachable_unchecked(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here +LL | std::process::abort(); + | ^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here | - = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior - = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE: = note: inside `miri_start` at tests/genmc/fail/simple/2w2w_weak.rs:LL:CC diff --git a/src/tools/miri/tests/genmc/pass/atomics/rmw_ops.rs b/src/tools/miri/tests/genmc/pass/atomics/rmw_ops.rs new file mode 100644 index 000000000000..f48466e8ce10 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/atomics/rmw_ops.rs @@ -0,0 +1,91 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// This test check for correct handling of atomic read-modify-write operations for all integer sizes. +// Atomic max and min should return the previous value, and store the result in the atomic. +// Atomic addition and subtraction should have wrapping semantics. +// `and`, `nand`, `or`, `xor` should behave like their non-atomic counterparts. + +// FIXME(genmc): add 128 bit atomics for platforms that support it, once GenMC gets 128 bit atomic support + +#![no_main] + +use std::sync::atomic::*; + +const ORD: Ordering = Ordering::SeqCst; + +fn assert_eq(x: T, y: T) { + if x != y { + std::process::abort(); + } +} + +macro_rules! test_rmw_edge_cases { + ($int:ty, $atomic:ty) => {{ + let x = <$atomic>::new(123); + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + x.store(123, ORD); + + // MAX, ADD + assert_eq(123, x.fetch_max(0, ORD)); // `max` keeps existing value + assert_eq(123, x.fetch_max(<$int>::MAX, ORD)); // `max` stores the new value + assert_eq(<$int>::MAX, x.fetch_add(10, ORD)); // `fetch_add` should be wrapping + assert_eq(<$int>::MAX.wrapping_add(10), x.load(ORD)); + + // MIN, SUB + x.store(42, ORD); + assert_eq(42, x.fetch_min(<$int>::MAX, ORD)); // `min` keeps existing value + assert_eq(42, x.fetch_min(<$int>::MIN, ORD)); // `min` stores the new value + assert_eq(<$int>::MIN, x.fetch_sub(10, ORD)); // `fetch_sub` should be wrapping + assert_eq(<$int>::MIN.wrapping_sub(10), x.load(ORD)); + + // Small enough pattern to work for all integer sizes. + let pattern = 0b01010101; + + // AND + x.store(!0, ORD); + assert_eq(!0, x.fetch_and(pattern, ORD)); + assert_eq(!0 & pattern, x.load(ORD)); + + // NAND + x.store(!0, ORD); + assert_eq(!0, x.fetch_nand(pattern, ORD)); + assert_eq(!(!0 & pattern), x.load(ORD)); + + // OR + x.store(!0, ORD); + assert_eq(!0, x.fetch_or(pattern, ORD)); + assert_eq(!0 | pattern, x.load(ORD)); + + // XOR + x.store(!0, ORD); + assert_eq(!0, x.fetch_xor(pattern, ORD)); + assert_eq(!0 ^ pattern, x.load(ORD)); + + // SWAP + x.store(!0, ORD); + assert_eq(!0, x.swap(pattern, ORD)); + assert_eq(pattern, x.load(ORD)); + + // Check correct behavior of atomic min/max combined with overflowing add/sub. + x.store(10, ORD); + assert_eq(10, x.fetch_add(<$int>::MAX, ORD)); // definitely overflows, so new value of x is smaller than 10 + assert_eq(<$int>::MAX.wrapping_add(10), x.fetch_max(10, ORD)); // new value of x should be 10 + // assert_eq(10, x.load(ORD)); // FIXME(genmc,#4572): enable this check once GenMC correctly handles min/max truncation. + }}; +} + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + test_rmw_edge_cases!(u8, AtomicU8); + test_rmw_edge_cases!(u16, AtomicU16); + test_rmw_edge_cases!(u32, AtomicU32); + test_rmw_edge_cases!(u64, AtomicU64); + test_rmw_edge_cases!(usize, AtomicUsize); + test_rmw_edge_cases!(i8, AtomicI8); + test_rmw_edge_cases!(i16, AtomicI16); + test_rmw_edge_cases!(i32, AtomicI32); + test_rmw_edge_cases!(i64, AtomicI64); + test_rmw_edge_cases!(isize, AtomicIsize); + + 0 +} diff --git a/src/tools/miri/tests/genmc/pass/atomics/rmw_ops.stderr b/src/tools/miri/tests/genmc/pass/atomics/rmw_ops.stderr new file mode 100644 index 000000000000..3b22247ee44c --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/atomics/rmw_ops.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 1 diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_2sc_scf.rs b/src/tools/miri/tests/genmc/pass/litmus/2w2w_2sc_scf.rs new file mode 100644 index 000000000000..3b3fca02285d --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_2sc_scf.rs @@ -0,0 +1,33 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's test "2+2W+2sc+scf". +// It tests correct handling of SeqCst fences combined with relaxed accesses. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + spawn_pthread_closure(|| { + X.store(1, SeqCst); + Y.store(2, SeqCst); + }); + spawn_pthread_closure(|| { + Y.store(1, Relaxed); + std::sync::atomic::fence(SeqCst); + X.store(2, Relaxed); + }); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_2sc_scf.stderr b/src/tools/miri/tests/genmc/pass/litmus/2w2w_2sc_scf.stderr new file mode 100644 index 000000000000..115b1986ce55 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_2sc_scf.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 3 diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.rs b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.rs index bbe9995dea0b..f47f5a11c5c9 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.rs @@ -1,9 +1,13 @@ +//@revisions: weak sc //@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@[sc]compile-flags: -Zmiri-disable-weak-memory-emulation // Translated from GenMC's test "2+2W". // // The pass tests "2w2w_3sc_1rel.rs", "2w2w_4rel" and "2w2w_4sc" and the fail test "2w2w_weak.rs" are related. // Check "2w2w_weak.rs" for a more detailed description. +// +// The `sc` variant of this test checks that without weak memory emulation, only 3 instead of 4 executions are explored (like the `2w2w_4sc.rs` test). #![no_main] diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.sc.stderr b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.sc.stderr new file mode 100644 index 000000000000..115b1986ce55 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.sc.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 3 diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.stderr b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.weak.stderr similarity index 100% rename from src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.stderr rename to src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.weak.stderr diff --git a/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.rs b/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.rs index 79e7c7feb7aa..1ec62ff21342 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.rs @@ -1,6 +1,6 @@ //@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -// Translated from GenMC's "IRIW-acq-sc" test. +// Translated from GenMC's "litmus/IRIW-acq-sc" test. #![no_main] diff --git a/src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs b/src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs new file mode 100644 index 000000000000..163556dcc897 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs @@ -0,0 +1,66 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "litmus/IRIWish" test. +// This test prints the values read by the different threads to check that we get all the values we expect. + +// NOTE: the order of the lines in the output may change with changes to GenMC. +// Before blessing the new output, ensure that only the order of lines in the output changed, and none of the outputs are missing. + +// NOTE: GenMC supports instruction caching and does not need replay completed threads. +// This means that an identical test in GenMC may output fewer lines (disable instruction caching to see all results). + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; +#[path = "../../../utils/mod.rs"] +mod utils; + +use std::fmt::Write; +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; +use crate::utils::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + Y.store(0, Relaxed); + + unsafe { + let mut results = [1234; 5]; + let ids = [ + spawn_pthread_closure(|| { + X.store(1, Relaxed); + }), + spawn_pthread_closure(|| { + let r1 = X.load(Relaxed); + Y.store(r1, Release); + results[0] = r1; + }), + spawn_pthread_closure(|| { + results[1] = X.load(Relaxed); + std::sync::atomic::fence(AcqRel); + results[2] = Y.load(Relaxed); + }), + spawn_pthread_closure(|| { + results[3] = Y.load(Relaxed); + std::sync::atomic::fence(AcqRel); + results[4] = X.load(Relaxed); + }), + ]; + join_pthreads(ids); + + // Print the values to check that we get all of them: + if writeln!(MiriStderr, "{results:?}").is_err() { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/IRIWish.stderr b/src/tools/miri/tests/genmc/pass/litmus/IRIWish.stderr new file mode 100644 index 000000000000..4fc77b63f380 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/IRIWish.stderr @@ -0,0 +1,30 @@ +[1, 1, 1, 1, 1] +[1, 1, 1, 0, 1] +[1, 1, 1, 0, 0] +[1, 1, 0, 1, 1] +[1, 1, 0, 0, 1] +[1, 1, 0, 0, 0] +[1, 0, 1, 1, 1] +[1, 0, 1, 0, 1] +[1, 0, 1, 0, 0] +[1, 0, 0, 1, 1] +[1, 0, 0, 0, 1] +[1, 0, 0, 0, 0] +[0, 1, 0, 0, 1] +[0, 1, 0, 0, 0] +[0, 1, 0, 0, 1] +[0, 1, 0, 0, 0] +[0, 1, 0, 0, 1] +[0, 1, 0, 0, 0] +[0, 1, 0, 0, 1] +[0, 1, 0, 0, 0] +[0, 0, 0, 0, 1] +[0, 0, 0, 0, 0] +[0, 0, 0, 0, 1] +[0, 0, 0, 0, 0] +[0, 0, 0, 0, 1] +[0, 0, 0, 0, 0] +[0, 0, 0, 0, 1] +[0, 0, 0, 0, 0] +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 28 diff --git a/src/tools/miri/tests/genmc/pass/litmus/LB_incMPs.rs b/src/tools/miri/tests/genmc/pass/litmus/LB_incMPs.rs new file mode 100644 index 000000000000..e43d92fc6c55 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/LB_incMPs.rs @@ -0,0 +1,41 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "litmus/LB+incMPs" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); +static W: AtomicU64 = AtomicU64::new(0); +static Z: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + spawn_pthread_closure(|| { + X.load(Acquire); + Z.fetch_add(1, AcqRel); + }); + spawn_pthread_closure(|| { + Z.fetch_add(1, AcqRel); + Y.store(1, Release); + }); + spawn_pthread_closure(|| { + Y.load(Acquire); + W.fetch_add(1, AcqRel); + }); + spawn_pthread_closure(|| { + W.fetch_add(1, AcqRel); + X.store(1, Release); + }); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/LB_incMPs.stderr b/src/tools/miri/tests/genmc/pass/litmus/LB_incMPs.stderr new file mode 100644 index 000000000000..c879e95a1708 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/LB_incMPs.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 15 diff --git a/src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.rs b/src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.rs new file mode 100644 index 000000000000..b36c8a288f42 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.rs @@ -0,0 +1,42 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "litmus/MPU+rels+acq" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +// Note: the GenMC equivalent of this test (genmc/tests/correct/litmus/MPU+rels+acq/mpu+rels+acq.c) uses non-atomic accesses for `X` with disabled race detection. +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +use crate::genmc::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + Y.store(0, Relaxed); + + unsafe { + spawn_pthread_closure(|| { + X.store(1, Relaxed); + + Y.store(0, Release); + Y.store(1, Relaxed); + }); + spawn_pthread_closure(|| { + Y.fetch_add(1, Relaxed); + }); + spawn_pthread_closure(|| { + if Y.load(Acquire) > 1 { + X.store(2, Relaxed); + } + }); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.stderr b/src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.stderr new file mode 100644 index 000000000000..c5e34b00642a --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 13 diff --git a/src/tools/miri/tests/genmc/pass/litmus/MP_incMPs.rs b/src/tools/miri/tests/genmc/pass/litmus/MP_incMPs.rs new file mode 100644 index 000000000000..a08b7de27d13 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/MP_incMPs.rs @@ -0,0 +1,36 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "litmus/MP+incMP" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); +static Z: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + spawn_pthread_closure(|| { + Y.load(Acquire); + Z.fetch_add(1, AcqRel); + }); + spawn_pthread_closure(|| { + Z.fetch_add(1, AcqRel); + X.load(Acquire); + }); + spawn_pthread_closure(|| { + X.store(1, Release); + Y.store(1, Release); + }); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/MP_incMPs.stderr b/src/tools/miri/tests/genmc/pass/litmus/MP_incMPs.stderr new file mode 100644 index 000000000000..2be9a6c7fbde --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/MP_incMPs.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 7 diff --git a/src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.rs b/src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.rs new file mode 100644 index 000000000000..cf9f5f2dbfa3 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.rs @@ -0,0 +1,40 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "litmus/MP+rels+acqf" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +// Note: the GenMC equivalent of this test (genmc/tests/correct/litmus/MP+rels+acqf/mp+rels+acqf.c) uses non-atomic accesses for `X` with disabled race detection. +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +use crate::genmc::*; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + Y.store(0, Relaxed); + + unsafe { + spawn_pthread_closure(|| { + X.store(1, Relaxed); + + Y.store(0, Release); + Y.store(1, Relaxed); + }); + spawn_pthread_closure(|| { + if Y.load(Relaxed) != 0 { + std::sync::atomic::fence(Acquire); + let _x = X.load(Relaxed); + } + }); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.stderr b/src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.stderr new file mode 100644 index 000000000000..0667962f99c8 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 4 diff --git a/src/tools/miri/tests/genmc/pass/litmus/SB_2sc_scf.rs b/src/tools/miri/tests/genmc/pass/litmus/SB_2sc_scf.rs new file mode 100644 index 000000000000..ffc44de1bc7c --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/SB_2sc_scf.rs @@ -0,0 +1,32 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "litmus/SB+2sc+scf" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + spawn_pthread_closure(|| { + X.store(1, SeqCst); + Y.load(SeqCst); + }); + spawn_pthread_closure(|| { + Y.store(1, Relaxed); + std::sync::atomic::fence(SeqCst); + X.load(Relaxed); + }); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/SB_2sc_scf.stderr b/src/tools/miri/tests/genmc/pass/litmus/SB_2sc_scf.stderr new file mode 100644 index 000000000000..115b1986ce55 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/SB_2sc_scf.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 3 diff --git a/src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs new file mode 100644 index 000000000000..ac5c3724254d --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs @@ -0,0 +1,65 @@ +//@revisions: weak sc +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows +//@[sc]compile-flags: -Zmiri-disable-weak-memory-emulation + +// Translated from GenMC's "litmus/Z6.U" test. +// +// The `sc` variant of this test checks that we get fewer executions when weak memory emulation is disabled. + +// NOTE: the order of the lines in the output may change with changes to GenMC. +// Before blessing the new output, ensure that only the order of lines in the output changed, and none of the outputs are missing. + +// NOTE: GenMC supports instruction caching and does not need replay completed threads. +// This means that an identical test in GenMC may output fewer lines (disable instruction caching to see all results). + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; +#[path = "../../../utils/mod.rs"] +mod utils; + +use std::fmt::Write; +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; +use crate::utils::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + Y.store(0, Relaxed); + + unsafe { + let mut a = 1234; + let mut b = 1234; + let ids = [ + spawn_pthread_closure(|| { + X.store(1, SeqCst); + Y.store(1, Release); + }), + spawn_pthread_closure(|| { + Y.fetch_add(1, SeqCst); + a = Y.load(Relaxed); + }), + spawn_pthread_closure(|| { + Y.store(3, SeqCst); + b = X.load(SeqCst); + }), + ]; + join_pthreads(ids); + + // Print the values to check that we get all of them: + if writeln!(MiriStderr, "a={a}, b={b}, X={}, Y={}", X.load(Relaxed), Y.load(Relaxed)) + .is_err() + { + std::process::abort(); + } + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/Z6_U.sc.stderr b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.sc.stderr new file mode 100644 index 000000000000..8dd2fbe1b975 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.sc.stderr @@ -0,0 +1,20 @@ +a=2, b=1, X=1, Y=3 +a=4, b=1, X=1, Y=4 +a=3, b=1, X=1, Y=3 +a=2, b=1, X=1, Y=2 +a=2, b=0, X=1, Y=2 +a=1, b=1, X=1, Y=1 +a=1, b=0, X=1, Y=1 +a=4, b=1, X=1, Y=1 +a=4, b=0, X=1, Y=1 +a=1, b=1, X=1, Y=3 +a=3, b=1, X=1, Y=3 +a=1, b=1, X=1, Y=1 +a=1, b=0, X=1, Y=1 +a=3, b=1, X=1, Y=1 +a=3, b=0, X=1, Y=1 +a=1, b=1, X=1, Y=3 +a=1, b=1, X=1, Y=1 +a=1, b=0, X=1, Y=1 +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 18 diff --git a/src/tools/miri/tests/genmc/pass/litmus/Z6_U.weak.stderr b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.weak.stderr new file mode 100644 index 000000000000..65622575de21 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.weak.stderr @@ -0,0 +1,24 @@ +a=2, b=1, X=1, Y=3 +a=4, b=1, X=1, Y=4 +a=4, b=0, X=1, Y=4 +a=3, b=1, X=1, Y=3 +a=2, b=1, X=1, Y=2 +a=2, b=0, X=1, Y=2 +a=1, b=1, X=1, Y=1 +a=1, b=0, X=1, Y=1 +a=4, b=1, X=1, Y=1 +a=4, b=0, X=1, Y=1 +a=1, b=1, X=1, Y=3 +a=1, b=0, X=1, Y=3 +a=3, b=1, X=1, Y=3 +a=3, b=0, X=1, Y=3 +a=1, b=1, X=1, Y=1 +a=1, b=0, X=1, Y=1 +a=3, b=1, X=1, Y=1 +a=3, b=0, X=1, Y=1 +a=1, b=1, X=1, Y=3 +a=1, b=0, X=1, Y=3 +a=1, b=1, X=1, Y=1 +a=1, b=0, X=1, Y=1 +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 22 diff --git a/src/tools/miri/tests/genmc/pass/litmus/Z6_acq.rs b/src/tools/miri/tests/genmc/pass/litmus/Z6_acq.rs new file mode 100644 index 000000000000..b00f3a59ce67 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/Z6_acq.rs @@ -0,0 +1,38 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "litmus/Z6+acq" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); +static Z: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + spawn_pthread_closure(|| { + X.store(1, Relaxed); + std::sync::atomic::fence(SeqCst); + Y.store(1, Relaxed); + }); + spawn_pthread_closure(|| { + Y.load(Acquire); + Z.store(1, Relaxed); + }); + spawn_pthread_closure(|| { + Z.store(2, Relaxed); + std::sync::atomic::fence(SeqCst); + X.load(Relaxed); + }); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/Z6_acq.stderr b/src/tools/miri/tests/genmc/pass/litmus/Z6_acq.stderr new file mode 100644 index 000000000000..2be9a6c7fbde --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/Z6_acq.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 7 diff --git a/src/tools/miri/tests/genmc/pass/litmus/atomicpo.rs b/src/tools/miri/tests/genmc/pass/litmus/atomicpo.rs new file mode 100644 index 000000000000..75be89893dab --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/atomicpo.rs @@ -0,0 +1,32 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's test "litmus/atomicpo". + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + spawn_pthread_closure(|| { + X.store(1, Relaxed); + std::sync::atomic::fence(AcqRel); + Y.store(1, Relaxed); + }); + spawn_pthread_closure(|| { + Y.swap(1, Relaxed); + X.swap(1, Relaxed); + }); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/atomicpo.stderr b/src/tools/miri/tests/genmc/pass/litmus/atomicpo.stderr new file mode 100644 index 000000000000..0667962f99c8 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/atomicpo.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 4 diff --git a/src/tools/miri/tests/genmc/pass/litmus/cumul-release.rs b/src/tools/miri/tests/genmc/pass/litmus/cumul-release.rs new file mode 100644 index 000000000000..2387976a8ca6 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/cumul-release.rs @@ -0,0 +1,58 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's test "litmus/cumul-release". + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); +static Z: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + Y.store(0, Relaxed); + Z.store(0, Relaxed); + + unsafe { + let mut a = 1234; + let mut b = 1234; + let mut c = 1234; + let ids = [ + spawn_pthread_closure(|| { + X.store(1, Relaxed); + Y.store(1, Release); + }), + spawn_pthread_closure(|| { + a = Y.load(Relaxed); + Z.store(a, Relaxed); + }), + spawn_pthread_closure(|| { + b = Z.load(Relaxed); + std::sync::atomic::fence(AcqRel); + c = X.load(Relaxed); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values: + if !matches!( + (a, b, c), + (0, 0, 0) | (0, 0, 1) | (1, 0, 0) | (1, 0, 1) | (1, 1, 0) | (1, 1, 1) + ) { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/cumul-release.stderr b/src/tools/miri/tests/genmc/pass/litmus/cumul-release.stderr new file mode 100644 index 000000000000..00394048ec5c --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/cumul-release.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 8 diff --git a/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.rs b/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.rs index 9a08d517b7c6..796ffbf97f96 100644 --- a/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.rs +++ b/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.rs @@ -38,7 +38,6 @@ fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { result[3] = X.load(Relaxed); }), ]; - // Join so we can read the final values. join_pthreads(ids); diff --git a/src/tools/miri/tests/genmc/pass/litmus/inc2w.rs b/src/tools/miri/tests/genmc/pass/litmus/inc2w.rs new file mode 100644 index 000000000000..fd8574c4f7c7 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/inc2w.rs @@ -0,0 +1,45 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's test "litmus/inc2w". + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses. + X.store(0, Relaxed); + + unsafe { + let ids = [ + spawn_pthread_closure(|| { + X.fetch_add(1, Relaxed); + }), + spawn_pthread_closure(|| { + X.store(4, Release); + }), + spawn_pthread_closure(|| { + X.fetch_add(2, Relaxed); + }), + ]; + // Join so we can read the final values. + join_pthreads(ids); + + // Check that we don't get any unexpected values: + let x = X.load(Relaxed); + if !matches!(x, 4..=7) { + std::process::abort(); + } + + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/inc2w.stderr b/src/tools/miri/tests/genmc/pass/litmus/inc2w.stderr new file mode 100644 index 000000000000..be75e68fde77 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/inc2w.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 6 diff --git a/src/tools/miri/tests/genmc/pass/litmus/inc_inc_RR_W_RR.rs b/src/tools/miri/tests/genmc/pass/litmus/inc_inc_RR_W_RR.rs new file mode 100644 index 000000000000..40ca48631859 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/inc_inc_RR_W_RR.rs @@ -0,0 +1,63 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::ffi::c_void; +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); + +static mut A: u64 = 0; +static mut B: u64 = 0; +static mut C: u64 = 0; +static mut D: u64 = 0; + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + let thread_order = [thread_1, thread_2, thread_3, thread_4, thread_5]; + let ids = unsafe { spawn_pthreads_no_params(thread_order) }; + unsafe { join_pthreads(ids) }; + + if unsafe { A == 42 && B == 2 && C == 1 && D == 42 } { + std::process::abort(); + } + + 0 +} + +pub extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void { + X.fetch_add(1, Relaxed); + std::ptr::null_mut() +} + +pub extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void { + X.fetch_add(1, Relaxed); + std::ptr::null_mut() +} + +pub extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void { + unsafe { + A = X.load(Relaxed); + B = X.load(Relaxed); + } + std::ptr::null_mut() +} + +pub extern "C" fn thread_4(_value: *mut c_void) -> *mut c_void { + X.store(42, Relaxed); + std::ptr::null_mut() +} + +pub extern "C" fn thread_5(_value: *mut c_void) -> *mut c_void { + unsafe { + C = X.load(Relaxed); + D = X.load(Relaxed); + } + std::ptr::null_mut() +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/inc_inc_RR_W_RR.stderr b/src/tools/miri/tests/genmc/pass/litmus/inc_inc_RR_W_RR.stderr new file mode 100644 index 000000000000..b5f8cd15b683 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/inc_inc_RR_W_RR.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 600 diff --git a/src/tools/miri/tests/genmc/pass/litmus/riwi.rs b/src/tools/miri/tests/genmc/pass/litmus/riwi.rs new file mode 100644 index 000000000000..49564c8e4fe0 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/riwi.rs @@ -0,0 +1,31 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "litmus/riwi" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static X: AtomicU64 = AtomicU64::new(0); +static Y: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + spawn_pthread_closure(|| { + Y.load(Relaxed); + X.fetch_add(1, Relaxed); + }); + spawn_pthread_closure(|| { + X.fetch_add(1, Relaxed); + Y.store(1, Release); + }); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/riwi.stderr b/src/tools/miri/tests/genmc/pass/litmus/riwi.stderr new file mode 100644 index 000000000000..115b1986ce55 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/riwi.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 3 diff --git a/src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.rs b/src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.rs new file mode 100644 index 000000000000..a193dcf0683e --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.rs @@ -0,0 +1,36 @@ +//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows + +// Translated from GenMC's "litmus/viktor-relseq" test. + +#![no_main] + +#[path = "../../../utils/genmc.rs"] +mod genmc; + +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::*; + +use crate::genmc::*; + +static LOCK: AtomicU64 = AtomicU64::new(0); + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + unsafe { + spawn_pthread_closure(|| { + LOCK.fetch_add(1, Acquire); + LOCK.fetch_add(1, Relaxed); + }); + spawn_pthread_closure(|| { + LOCK.fetch_add(1, Relaxed); + LOCK.fetch_add(1, Relaxed); + }); + spawn_pthread_closure(|| { + LOCK.fetch_add(1, Release); + }); + spawn_pthread_closure(|| { + LOCK.fetch_add(1, Relaxed); + }); + 0 + } +} diff --git a/src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.stderr b/src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.stderr new file mode 100644 index 000000000000..d63ac5199d5d --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.stderr @@ -0,0 +1,2 @@ +(GenMC) Verification complete. No errors were detected. +Number of complete executions explored: 180 diff --git a/src/tools/miri/tests/utils/genmc.rs b/src/tools/miri/tests/utils/genmc.rs index fb6334bd0608..29b3181992b1 100644 --- a/src/tools/miri/tests/utils/genmc.rs +++ b/src/tools/miri/tests/utils/genmc.rs @@ -51,6 +51,13 @@ pub unsafe fn join_pthread(thread_id: pthread_t) { } } +/// Spawn `N` threads using `pthread_create` without any arguments, abort the process on any errors. +pub unsafe fn spawn_pthreads_no_params( + functions: [extern "C" fn(*mut c_void) -> *mut c_void; N], +) -> [pthread_t; N] { + functions.map(|func| spawn_pthread(func, std::ptr::null_mut())) +} + // Join the `N` given pthreads, abort the process on any errors. pub unsafe fn join_pthreads(thread_ids: [pthread_t; N]) { let _ = thread_ids.map(|id| join_pthread(id)); From 7fa23530bfe04fa9644c1bdaa245aaa24101a1b5 Mon Sep 17 00:00:00 2001 From: Justin Yao Du Date: Sun, 7 Sep 2025 21:16:35 -0700 Subject: [PATCH 232/682] Fix typo in default.rs --- library/core/src/default.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/default.rs b/library/core/src/default.rs index cc9289307c1d..1cc4fb6e8fdd 100644 --- a/library/core/src/default.rs +++ b/library/core/src/default.rs @@ -33,7 +33,7 @@ use crate::ascii::Char as AsciiChar; /// } /// ``` /// -/// Now, you get all of the default values. Rust implements `Default` for various primitives types. +/// Now, you get all of the default values. Rust implements `Default` for various primitive types. /// /// If you want to override a particular option, but still retain the other defaults: /// From 344b4ac427999c382f575308af3569b2148485a9 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 8 Sep 2025 07:55:10 +0200 Subject: [PATCH 233/682] print proper error when using unsupported synchronization primitive with GenMC --- src/tools/miri/src/alloc_addresses/mod.rs | 2 + src/tools/miri/src/concurrency/data_race.rs | 22 +++--- src/tools/miri/src/concurrency/init_once.rs | 10 +-- src/tools/miri/src/concurrency/sync.rs | 68 ++++++------------- .../miri/src/shims/unix/linux_like/epoll.rs | 2 +- .../miri/src/shims/unix/linux_like/eventfd.rs | 2 +- src/tools/miri/src/shims/unix/macos/sync.rs | 4 +- src/tools/miri/src/shims/unix/sync.rs | 16 ++--- .../miri/src/shims/unix/unnamed_socket.rs | 2 +- 9 files changed, 52 insertions(+), 76 deletions(-) diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs index 5d94a1f4138a..f011ee717850 100644 --- a/src/tools/miri/src/alloc_addresses/mod.rs +++ b/src/tools/miri/src/alloc_addresses/mod.rs @@ -498,6 +498,8 @@ impl<'tcx> MiriMachine<'tcx> { // Also remember this address for future reuse. let thread = self.threads.active_thread(); global_state.reuse.add_addr(rng, addr, size, align, kind, thread, || { + // We already excluded GenMC above. We cannot use `self.release_clock` as + // `self.alloc_addresses` is borrowed. if let Some(data_race) = self.data_race.as_vclocks_ref() { data_race.release_clock(&self.threads, |clock| clock.clone()) } else { diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs index 30061a78594a..de60e80b9f7e 100644 --- a/src/tools/miri/src/concurrency/data_race.rs +++ b/src/tools/miri/src/concurrency/data_race.rs @@ -958,16 +958,20 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { /// with this program point. /// /// The closure will only be invoked if data race handling is on. - fn release_clock(&self, callback: impl FnOnce(&VClock) -> R) -> Option { + fn release_clock( + &self, + callback: impl FnOnce(&VClock) -> R, + ) -> InterpResult<'tcx, Option> { let this = self.eval_context_ref(); - // FIXME: make this a proper error instead of ICEing the interpreter. - assert!( - this.machine.data_race.as_genmc_ref().is_none(), - "this operation performs synchronization that is not supported in GenMC mode" - ); - Some( - this.machine.data_race.as_vclocks_ref()?.release_clock(&this.machine.threads, callback), - ) + interp_ok(match &this.machine.data_race { + GlobalDataRaceHandler::None => None, + GlobalDataRaceHandler::Genmc(_genmc_ctx) => + throw_unsup_format!( + "this operation performs synchronization that is not supported in GenMC mode" + ), + GlobalDataRaceHandler::Vclocks(data_race) => + Some(data_race.release_clock(&this.machine.threads, callback)), + }) } /// Acquire the given clock into the current thread, establishing synchronization with diff --git a/src/tools/miri/src/concurrency/init_once.rs b/src/tools/miri/src/concurrency/init_once.rs index 3fb3d890419f..daea20b3779b 100644 --- a/src/tools/miri/src/concurrency/init_once.rs +++ b/src/tools/miri/src/concurrency/init_once.rs @@ -96,10 +96,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { init_once.status = InitOnceStatus::Complete; // Each complete happens-before the end of the wait - if let Some(data_race) = this.machine.data_race.as_vclocks_ref() { - data_race - .release_clock(&this.machine.threads, |clock| init_once.clock.clone_from(clock)); - } + this.release_clock(|clock| init_once.clock.clone_from(clock))?; // Wake up everyone. // need to take the queue to avoid having `this` be borrowed multiple times @@ -125,10 +122,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { init_once.status = InitOnceStatus::Uninitialized; // Each complete happens-before the end of the wait - if let Some(data_race) = this.machine.data_race.as_vclocks_ref() { - data_race - .release_clock(&this.machine.threads, |clock| init_once.clock.clone_from(clock)); - } + this.release_clock(|clock| init_once.clock.clone_from(clock))?; // Wake up one waiting thread, so they can go ahead and try to init this. if let Some(waiter) = init_once.waiters.pop_front() { diff --git a/src/tools/miri/src/concurrency/sync.rs b/src/tools/miri/src/concurrency/sync.rs index 179094db0feb..15d486c27e36 100644 --- a/src/tools/miri/src/concurrency/sync.rs +++ b/src/tools/miri/src/concurrency/sync.rs @@ -204,7 +204,7 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this.mutex_enqueue_and_block(mutex_ref, Some((retval, dest))); } else { // We can have it right now! - this.mutex_lock(&mutex_ref); + this.mutex_lock(&mutex_ref)?; // Don't forget to write the return value. this.write_scalar(retval, &dest)?; } @@ -338,7 +338,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } /// Lock by setting the mutex owner and increasing the lock count. - fn mutex_lock(&mut self, mutex_ref: &MutexRef) { + fn mutex_lock(&mut self, mutex_ref: &MutexRef) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let thread = this.active_thread(); let mut mutex = mutex_ref.0.borrow_mut(); @@ -352,9 +352,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { mutex.owner = Some(thread); } mutex.lock_count = mutex.lock_count.strict_add(1); - if let Some(data_race) = this.machine.data_race.as_vclocks_ref() { - data_race.acquire_clock(&mutex.clock, &this.machine.threads); - } + this.acquire_clock(&mutex.clock)?; + interp_ok(()) } /// Try unlocking by decreasing the lock count and returning the old lock @@ -377,11 +376,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // The mutex is completely unlocked. Try transferring ownership // to another thread. - if let Some(data_race) = this.machine.data_race.as_vclocks_ref() { - data_race.release_clock(&this.machine.threads, |clock| { - mutex.clock.clone_from(clock) - }); - } + this.release_clock(|clock| mutex.clock.clone_from(clock))?; let thread_id = mutex.queue.pop_front(); // We need to drop our mutex borrow before unblock_thread // because it will be borrowed again in the unblock callback. @@ -425,7 +420,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { assert_eq!(unblock, UnblockKind::Ready); assert!(mutex_ref.owner().is_none()); - this.mutex_lock(&mutex_ref); + this.mutex_lock(&mutex_ref)?; if let Some((retval, dest)) = retval_dest { this.write_scalar(retval, &dest)?; @@ -439,7 +434,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// Read-lock the lock by adding the `reader` the list of threads that own /// this lock. - fn rwlock_reader_lock(&mut self, rwlock_ref: &RwLockRef) { + fn rwlock_reader_lock(&mut self, rwlock_ref: &RwLockRef) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let thread = this.active_thread(); trace!("rwlock_reader_lock: now also held (one more time) by {:?}", thread); @@ -447,9 +442,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { assert!(!rwlock.is_write_locked(), "the lock is write locked"); let count = rwlock.readers.entry(thread).or_insert(0); *count = count.strict_add(1); - if let Some(data_race) = this.machine.data_race.as_vclocks_ref() { - data_race.acquire_clock(&rwlock.clock_unlocked, &this.machine.threads); - } + this.acquire_clock(&rwlock.clock_unlocked)?; + interp_ok(()) } /// Try read-unlock the lock for the current threads and potentially give the lock to a new owner. @@ -472,12 +466,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } Entry::Vacant(_) => return interp_ok(false), // we did not even own this lock } - if let Some(data_race) = this.machine.data_race.as_vclocks_ref() { - // Add this to the shared-release clock of all concurrent readers. - data_race.release_clock(&this.machine.threads, |clock| { - rwlock.clock_current_readers.join(clock) - }); - } + // Add this to the shared-release clock of all concurrent readers. + this.release_clock(|clock| rwlock.clock_current_readers.join(clock))?; // The thread was a reader. If the lock is not held any more, give it to a writer. if rwlock.is_locked().not() { @@ -521,7 +511,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } |this, unblock: UnblockKind| { assert_eq!(unblock, UnblockKind::Ready); - this.rwlock_reader_lock(&rwlock_ref); + this.rwlock_reader_lock(&rwlock_ref)?; this.write_scalar(retval, &dest)?; interp_ok(()) } @@ -531,7 +521,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// Lock by setting the writer that owns the lock. #[inline] - fn rwlock_writer_lock(&mut self, rwlock_ref: &RwLockRef) { + fn rwlock_writer_lock(&mut self, rwlock_ref: &RwLockRef) -> InterpResult<'tcx> { let this = self.eval_context_mut(); let thread = this.active_thread(); trace!("rwlock_writer_lock: now held by {:?}", thread); @@ -539,9 +529,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let mut rwlock = rwlock_ref.0.borrow_mut(); assert!(!rwlock.is_locked(), "the rwlock is already locked"); rwlock.writer = Some(thread); - if let Some(data_race) = this.machine.data_race.as_vclocks_ref() { - data_race.acquire_clock(&rwlock.clock_unlocked, &this.machine.threads); - } + this.acquire_clock(&rwlock.clock_unlocked)?; + interp_ok(()) } /// Try to unlock an rwlock held by the current thread. @@ -559,11 +548,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { rwlock.writer = None; trace!("rwlock_writer_unlock: unlocked by {:?}", thread); // Record release clock for next lock holder. - if let Some(data_race) = this.machine.data_race.as_vclocks_ref() { - data_race.release_clock(&this.machine.threads, |clock| { - rwlock.clock_unlocked.clone_from(clock) - }); - } + this.release_clock(|clock| rwlock.clock_unlocked.clone_from(clock))?; // The thread was a writer. // @@ -613,7 +598,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } |this, unblock: UnblockKind| { assert_eq!(unblock, UnblockKind::Ready); - this.rwlock_writer_lock(&rwlock_ref); + this.rwlock_writer_lock(&rwlock_ref)?; this.write_scalar(retval, &dest)?; interp_ok(()) } @@ -663,12 +648,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match unblock { UnblockKind::Ready => { // The condvar was signaled. Make sure we get the clock for that. - if let Some(data_race) = this.machine.data_race.as_vclocks_ref() { - data_race.acquire_clock( + this.acquire_clock( &condvar_ref.0.borrow().clock, - &this.machine.threads, - ); - } + )?; // Try to acquire the mutex. // The timeout only applies to the first wait (until the signal), not for mutex acquisition. this.condvar_reacquire_mutex(mutex_ref, retval_succ, dest) @@ -695,9 +677,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let mut condvar = condvar_ref.0.borrow_mut(); // Each condvar signal happens-before the end of the condvar wake - if let Some(data_race) = this.machine.data_race.as_vclocks_ref() { - data_race.release_clock(&this.machine.threads, |clock| condvar.clock.clone_from(clock)); - } + this.release_clock(|clock| condvar.clock.clone_from(clock))?; let Some(waiter) = condvar.waiters.pop_front() else { return interp_ok(false); }; @@ -736,9 +716,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { UnblockKind::Ready => { let futex = futex_ref.0.borrow(); // Acquire the clock of the futex. - if let Some(data_race) = this.machine.data_race.as_vclocks_ref() { - data_race.acquire_clock(&futex.clock, &this.machine.threads); - } + this.acquire_clock(&futex.clock)?; }, UnblockKind::TimedOut => { // Remove the waiter from the futex. @@ -766,9 +744,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let mut futex = futex_ref.0.borrow_mut(); // Each futex-wake happens-before the end of the futex wait - if let Some(data_race) = this.machine.data_race.as_vclocks_ref() { - data_race.release_clock(&this.machine.threads, |clock| futex.clock.clone_from(clock)); - } + this.release_clock(|clock| futex.clock.clone_from(clock))?; // Remove `count` of the threads in the queue that match any of the bits in the bitset. // We collect all of them before unblocking because the unblock callback may access the diff --git a/src/tools/miri/src/shims/unix/linux_like/epoll.rs b/src/tools/miri/src/shims/unix/linux_like/epoll.rs index aac880feda40..3133c149293d 100644 --- a/src/tools/miri/src/shims/unix/linux_like/epoll.rs +++ b/src/tools/miri/src/shims/unix/linux_like/epoll.rs @@ -608,7 +608,7 @@ fn check_and_update_one_event_interest<'tcx>( // If we are tracking data races, remember the current clock so we can sync with it later. ecx.release_clock(|clock| { event_instance.clock.clone_from(clock); - }); + })?; // Triggers the notification by inserting it to the ready list. ready_list.insert(epoll_key, event_instance); interp_ok(true) diff --git a/src/tools/miri/src/shims/unix/linux_like/eventfd.rs b/src/tools/miri/src/shims/unix/linux_like/eventfd.rs index 54cc571884dc..5d4f207d365e 100644 --- a/src/tools/miri/src/shims/unix/linux_like/eventfd.rs +++ b/src/tools/miri/src/shims/unix/linux_like/eventfd.rs @@ -199,7 +199,7 @@ fn eventfd_write<'tcx>( // Future `read` calls will synchronize with this write, so update the FD clock. ecx.release_clock(|clock| { eventfd.clock.borrow_mut().join(clock); - }); + })?; // Store new counter value. eventfd.counter.set(new_count); diff --git a/src/tools/miri/src/shims/unix/macos/sync.rs b/src/tools/miri/src/shims/unix/macos/sync.rs index 05616dd5a428..c4ddff7805ed 100644 --- a/src/tools/miri/src/shims/unix/macos/sync.rs +++ b/src/tools/miri/src/shims/unix/macos/sync.rs @@ -296,7 +296,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.mutex_enqueue_and_block(mutex_ref, None); } else { - this.mutex_lock(&mutex_ref); + this.mutex_lock(&mutex_ref)?; } interp_ok(()) @@ -321,7 +321,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // reentrancy. this.write_scalar(Scalar::from_bool(false), dest)?; } else { - this.mutex_lock(&mutex_ref); + this.mutex_lock(&mutex_ref)?; this.write_scalar(Scalar::from_bool(true), dest)?; } diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs index 5ad4fd501a60..cefd8b81ac18 100644 --- a/src/tools/miri/src/shims/unix/sync.rs +++ b/src/tools/miri/src/shims/unix/sync.rs @@ -498,14 +498,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { MutexKind::Normal => throw_machine_stop!(TerminationInfo::Deadlock), MutexKind::ErrorCheck => this.eval_libc_i32("EDEADLK"), MutexKind::Recursive => { - this.mutex_lock(&mutex.mutex_ref); + this.mutex_lock(&mutex.mutex_ref)?; 0 } } } } else { // The mutex is unlocked. Let's lock it. - this.mutex_lock(&mutex.mutex_ref); + this.mutex_lock(&mutex.mutex_ref)?; 0 }; this.write_scalar(Scalar::from_i32(ret), dest)?; @@ -525,14 +525,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { MutexKind::Default | MutexKind::Normal | MutexKind::ErrorCheck => this.eval_libc_i32("EBUSY"), MutexKind::Recursive => { - this.mutex_lock(&mutex.mutex_ref); + this.mutex_lock(&mutex.mutex_ref)?; 0 } } } } else { // The mutex is unlocked. Let's lock it. - this.mutex_lock(&mutex.mutex_ref); + this.mutex_lock(&mutex.mutex_ref)?; 0 })) } @@ -600,7 +600,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { dest.clone(), ); } else { - this.rwlock_reader_lock(&rwlock.rwlock_ref); + this.rwlock_reader_lock(&rwlock.rwlock_ref)?; this.write_null(dest)?; } @@ -615,7 +615,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if rwlock.rwlock_ref.is_write_locked() { interp_ok(Scalar::from_i32(this.eval_libc_i32("EBUSY"))) } else { - this.rwlock_reader_lock(&rwlock.rwlock_ref); + this.rwlock_reader_lock(&rwlock.rwlock_ref)?; interp_ok(Scalar::from_i32(0)) } } @@ -648,7 +648,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { dest.clone(), ); } else { - this.rwlock_writer_lock(&rwlock.rwlock_ref); + this.rwlock_writer_lock(&rwlock.rwlock_ref)?; this.write_null(dest)?; } @@ -663,7 +663,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if rwlock.rwlock_ref.is_locked() { interp_ok(Scalar::from_i32(this.eval_libc_i32("EBUSY"))) } else { - this.rwlock_writer_lock(&rwlock.rwlock_ref); + this.rwlock_writer_lock(&rwlock.rwlock_ref)?; interp_ok(Scalar::from_i32(0)) } } diff --git a/src/tools/miri/src/shims/unix/unnamed_socket.rs b/src/tools/miri/src/shims/unix/unnamed_socket.rs index 0585f3ba7471..81703d6e176b 100644 --- a/src/tools/miri/src/shims/unix/unnamed_socket.rs +++ b/src/tools/miri/src/shims/unix/unnamed_socket.rs @@ -259,7 +259,7 @@ fn anonsocket_write<'tcx>( // Remember this clock so `read` can synchronize with us. ecx.release_clock(|clock| { writebuf.clock.join(clock); - }); + })?; // Do full write / partial write based on the space available. let write_size = len.min(available_space); let actual_write_size = ecx.write_to_host(&mut writebuf.buf, write_size, ptr)?.unwrap(); From c776fbf1eb44a90aa613042c60ba48859f4949e4 Mon Sep 17 00:00:00 2001 From: Fang He Date: Mon, 8 Sep 2025 16:35:03 +0800 Subject: [PATCH 234/682] check before test for hardware capabilites in bits 32~63 of usize --- .../std_detect/src/detect/os/linux/aarch64.rs | 71 ++++++++++--------- 1 file changed, 38 insertions(+), 33 deletions(-) diff --git a/library/std_detect/src/detect/os/linux/aarch64.rs b/library/std_detect/src/detect/os/linux/aarch64.rs index 87a9d6ebb887..ba5e8f002568 100644 --- a/library/std_detect/src/detect/os/linux/aarch64.rs +++ b/library/std_detect/src/detect/os/linux/aarch64.rs @@ -140,7 +140,7 @@ struct AtHwcap { impl From for AtHwcap { /// Reads AtHwcap from the auxiliary vector. fn from(auxv: auxvec::AuxVec) -> Self { - AtHwcap { + let mut cap = AtHwcap { fp: bit::test(auxv.hwcap, 0), asimd: bit::test(auxv.hwcap, 1), // evtstrm: bit::test(auxv.hwcap, 2), @@ -207,39 +207,44 @@ impl From for AtHwcap { // smef32f32: bit::test(auxv.hwcap2, 29), smefa64: bit::test(auxv.hwcap2, 30), wfxt: bit::test(auxv.hwcap2, 31), - // ebf16: bit::test(auxv.hwcap2, 32), - // sveebf16: bit::test(auxv.hwcap2, 33), - cssc: bit::test(auxv.hwcap2, 34), - // rprfm: bit::test(auxv.hwcap2, 35), - sve2p1: bit::test(auxv.hwcap2, 36), - sme2: bit::test(auxv.hwcap2, 37), - sme2p1: bit::test(auxv.hwcap2, 38), - // smei16i32: bit::test(auxv.hwcap2, 39), - // smebi32i32: bit::test(auxv.hwcap2, 40), - smeb16b16: bit::test(auxv.hwcap2, 41), - smef16f16: bit::test(auxv.hwcap2, 42), - mops: bit::test(auxv.hwcap2, 43), - hbc: bit::test(auxv.hwcap2, 44), - sveb16b16: bit::test(auxv.hwcap2, 45), - lrcpc3: bit::test(auxv.hwcap2, 46), - lse128: bit::test(auxv.hwcap2, 47), - fpmr: bit::test(auxv.hwcap2, 48), - lut: bit::test(auxv.hwcap2, 49), - faminmax: bit::test(auxv.hwcap2, 50), - f8cvt: bit::test(auxv.hwcap2, 51), - f8fma: bit::test(auxv.hwcap2, 52), - f8dp4: bit::test(auxv.hwcap2, 53), - f8dp2: bit::test(auxv.hwcap2, 54), - f8e4m3: bit::test(auxv.hwcap2, 55), - f8e5m2: bit::test(auxv.hwcap2, 56), - smelutv2: bit::test(auxv.hwcap2, 57), - smef8f16: bit::test(auxv.hwcap2, 58), - smef8f32: bit::test(auxv.hwcap2, 59), - smesf8fma: bit::test(auxv.hwcap2, 60), - smesf8dp4: bit::test(auxv.hwcap2, 61), - smesf8dp2: bit::test(auxv.hwcap2, 62), - // pauthlr: bit::test(auxv.hwcap2, ??), + ..Default::default() + }; + #[cfg(target_pointer_width = "64")] + { + // cap.ebf16: bit::test(auxv.hwcap2, 32); + // cap.sveebf16: bit::test(auxv.hwcap2, 33); + cap.cssc = bit::test(auxv.hwcap2, 34); + // cap.rprfm: bit::test(auxv.hwcap2, 35); + cap.sve2p1 = bit::test(auxv.hwcap2, 36); + cap.sme2 = bit::test(auxv.hwcap2, 37); + cap.sme2p1 = bit::test(auxv.hwcap2, 38); + // cap.smei16i32 = bit::test(auxv.hwcap2, 39); + // cap.smebi32i32 = bit::test(auxv.hwcap2, 40); + cap.smeb16b16 = bit::test(auxv.hwcap2, 41); + cap.smef16f16 = bit::test(auxv.hwcap2, 42); + cap.mops = bit::test(auxv.hwcap2, 43); + cap.hbc = bit::test(auxv.hwcap2, 44); + cap.sveb16b16 = bit::test(auxv.hwcap2, 45); + cap.lrcpc3 = bit::test(auxv.hwcap2, 46); + cap.lse128 = bit::test(auxv.hwcap2, 47); + cap.fpmr = bit::test(auxv.hwcap2, 48); + cap.lut = bit::test(auxv.hwcap2, 49); + cap.faminmax = bit::test(auxv.hwcap2, 50); + cap.f8cvt = bit::test(auxv.hwcap2, 51); + cap.f8fma = bit::test(auxv.hwcap2, 52); + cap.f8dp4 = bit::test(auxv.hwcap2, 53); + cap.f8dp2 = bit::test(auxv.hwcap2, 54); + cap.f8e4m3 = bit::test(auxv.hwcap2, 55); + cap.f8e5m2 = bit::test(auxv.hwcap2, 56); + cap.smelutv2 = bit::test(auxv.hwcap2, 57); + cap.smef8f16 = bit::test(auxv.hwcap2, 58); + cap.smef8f32 = bit::test(auxv.hwcap2, 59); + cap.smesf8fma = bit::test(auxv.hwcap2, 60); + cap.smesf8dp4 = bit::test(auxv.hwcap2, 61); + cap.smesf8dp2 = bit::test(auxv.hwcap2, 62); + // cap.pauthlr = bit::test(auxv.hwcap2, ??); } + cap } } From 9a52a8354e50bd824bdefebc659d8c3c38eccc0a Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 8 Sep 2025 05:27:52 +1000 Subject: [PATCH 235/682] Allow static regions in `type_name`. Fixes #146249. --- compiler/rustc_const_eval/src/util/type_name.rs | 5 +++-- tests/ui/type/type-name-basic.rs | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_const_eval/src/util/type_name.rs b/compiler/rustc_const_eval/src/util/type_name.rs index 2ae6655901b6..5bcf96abd8cb 100644 --- a/compiler/rustc_const_eval/src/util/type_name.rs +++ b/compiler/rustc_const_eval/src/util/type_name.rs @@ -168,10 +168,11 @@ impl<'tcx> PrettyPrinter<'tcx> for TypeNamePrinter<'tcx> { // Bound regions are always printed (as `'_`), which gives some idea that they are special, // even though the `for` is omitted by the pretty printer. // E.g. `for<'a, 'b> fn(&'a u32, &'b u32)` is printed as "fn(&'_ u32, &'_ u32)". + let kind = region.kind(); match region.kind() { - ty::ReErased | ty::ReEarlyParam(_) => false, + ty::ReErased | ty::ReEarlyParam(_) | ty::ReStatic => false, ty::ReBound(..) => true, - _ => unreachable!(), + _ => panic!("type_name unhandled region: {kind:?}"), } } diff --git a/tests/ui/type/type-name-basic.rs b/tests/ui/type/type-name-basic.rs index 343bcae175a0..2c41cb80aea8 100644 --- a/tests/ui/type/type-name-basic.rs +++ b/tests/ui/type/type-name-basic.rs @@ -107,4 +107,19 @@ pub fn main() { } let a = Wrap(&()).get(); v!(a, "type_name_basic::main::Wrap<&()>::get::Info"); + + struct Issue146249(T); + impl Issue146249> { + pub fn bar(&self) { + let f = || {}; + v!( + f, + "type_name_basic::main::Issue146249<\ + alloc::boxed::Box\ + >::bar::{{closure}}" + ); + } + } + let v: Issue146249> = Issue146249(Box::new(|| {})); + v.bar(); } From deca4895ca0412ea1a646310c0026d66b1230b9a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 8 Sep 2025 11:57:19 +0200 Subject: [PATCH 236/682] Prepare for merging from rust-lang/rust This updates the rust-version file to a09fbe2c8372643a27a8082236120f95ed4e6bba. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index a51d86c3d6f1..5b3139b25092 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -b3cfb8faf84c5f3b7909479a9f9b6a3290d5f4d7 +a09fbe2c8372643a27a8082236120f95ed4e6bba From 24ce839fb953d1172a19479edcb2f75877cf153e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 8 Sep 2025 12:00:36 +0200 Subject: [PATCH 237/682] clippy --- src/tools/miri/src/math.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/src/math.rs b/src/tools/miri/src/math.rs index 7d2f1c083687..254495637da4 100644 --- a/src/tools/miri/src/math.rs +++ b/src/tools/miri/src/math.rs @@ -308,7 +308,7 @@ where Some(if return_nan { ecx.generate_nan(&[base]) } else { one }) } - _ => return None, + _ => None, } } From be284b697fef4c560e040a433fbe2b832ed8426d Mon Sep 17 00:00:00 2001 From: Fang He Date: Mon, 8 Sep 2025 18:39:39 +0800 Subject: [PATCH 238/682] add some comments to clarify the fix --- library/std_detect/src/detect/os/linux/aarch64.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/library/std_detect/src/detect/os/linux/aarch64.rs b/library/std_detect/src/detect/os/linux/aarch64.rs index ba5e8f002568..2c36a073b74e 100644 --- a/library/std_detect/src/detect/os/linux/aarch64.rs +++ b/library/std_detect/src/detect/os/linux/aarch64.rs @@ -209,6 +209,12 @@ impl From for AtHwcap { wfxt: bit::test(auxv.hwcap2, 31), ..Default::default() }; + + // Hardware capabilites from bits 32 to 63 should only + // be tested on LP64 targets with 64 bits `usize`. + // On ILP32 targets like `aarch64-unknown-linux-gun_ilp32`, + // these hardware capabilites will default to `false`. + // https://github.com/rust-lang/rust/issues/146230 #[cfg(target_pointer_width = "64")] { // cap.ebf16: bit::test(auxv.hwcap2, 32); From de359c569b7c3eb630fb44e9078c4bea012eac99 Mon Sep 17 00:00:00 2001 From: Fang He Date: Mon, 8 Sep 2025 18:48:19 +0800 Subject: [PATCH 239/682] fix typos in comment --- library/std_detect/src/detect/os/linux/aarch64.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std_detect/src/detect/os/linux/aarch64.rs b/library/std_detect/src/detect/os/linux/aarch64.rs index 2c36a073b74e..c53dbf3fd9ba 100644 --- a/library/std_detect/src/detect/os/linux/aarch64.rs +++ b/library/std_detect/src/detect/os/linux/aarch64.rs @@ -210,10 +210,10 @@ impl From for AtHwcap { ..Default::default() }; - // Hardware capabilites from bits 32 to 63 should only + // Hardware capabilities from bits 32 to 63 should only // be tested on LP64 targets with 64 bits `usize`. // On ILP32 targets like `aarch64-unknown-linux-gun_ilp32`, - // these hardware capabilites will default to `false`. + // these hardware capabilities will default to `false`. // https://github.com/rust-lang/rust/issues/146230 #[cfg(target_pointer_width = "64")] { From 94ab2b9f15b3c5ffe139d17fdae98cc2adff883d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 19 Jul 2025 11:57:49 +0200 Subject: [PATCH 240/682] move math intrinsics to their own file --- src/tools/miri/src/intrinsics/math.rs | 311 ++++++++++++++++++++++++++ src/tools/miri/src/intrinsics/mod.rs | 291 +----------------------- 2 files changed, 315 insertions(+), 287 deletions(-) create mode 100644 src/tools/miri/src/intrinsics/math.rs diff --git a/src/tools/miri/src/intrinsics/math.rs b/src/tools/miri/src/intrinsics/math.rs new file mode 100644 index 000000000000..21d4a92e7d28 --- /dev/null +++ b/src/tools/miri/src/intrinsics/math.rs @@ -0,0 +1,311 @@ +use rand::Rng; +use rustc_apfloat::{self, Float, Round}; +use rustc_middle::mir; +use rustc_middle::ty::{self, FloatTy}; + +use self::helpers::{ToHost, ToSoft}; +use super::check_intrinsic_arg_count; +use crate::*; + +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + fn emulate_math_intrinsic( + &mut self, + intrinsic_name: &str, + _generic_args: ty::GenericArgsRef<'tcx>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx, EmulateItemResult> { + let this = self.eval_context_mut(); + + match intrinsic_name { + // Operations we can do with soft-floats. + "sqrtf32" => { + let [f] = check_intrinsic_arg_count(args)?; + let f = this.read_scalar(f)?.to_f32()?; + // Sqrt is specified to be fully precise. + let res = math::sqrt(f); + let res = this.adjust_nan(res, &[f]); + this.write_scalar(res, dest)?; + } + "sqrtf64" => { + let [f] = check_intrinsic_arg_count(args)?; + let f = this.read_scalar(f)?.to_f64()?; + // Sqrt is specified to be fully precise. + let res = math::sqrt(f); + let res = this.adjust_nan(res, &[f]); + this.write_scalar(res, dest)?; + } + + "fmaf32" => { + let [a, b, c] = check_intrinsic_arg_count(args)?; + let a = this.read_scalar(a)?.to_f32()?; + let b = this.read_scalar(b)?.to_f32()?; + let c = this.read_scalar(c)?.to_f32()?; + let res = a.mul_add(b, c).value; + let res = this.adjust_nan(res, &[a, b, c]); + this.write_scalar(res, dest)?; + } + "fmaf64" => { + let [a, b, c] = check_intrinsic_arg_count(args)?; + let a = this.read_scalar(a)?.to_f64()?; + let b = this.read_scalar(b)?.to_f64()?; + let c = this.read_scalar(c)?.to_f64()?; + let res = a.mul_add(b, c).value; + let res = this.adjust_nan(res, &[a, b, c]); + this.write_scalar(res, dest)?; + } + + "fmuladdf32" => { + let [a, b, c] = check_intrinsic_arg_count(args)?; + let a = this.read_scalar(a)?.to_f32()?; + let b = this.read_scalar(b)?.to_f32()?; + let c = this.read_scalar(c)?.to_f32()?; + let fuse: bool = this.machine.float_nondet && this.machine.rng.get_mut().random(); + let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value }; + let res = this.adjust_nan(res, &[a, b, c]); + this.write_scalar(res, dest)?; + } + "fmuladdf64" => { + let [a, b, c] = check_intrinsic_arg_count(args)?; + let a = this.read_scalar(a)?.to_f64()?; + let b = this.read_scalar(b)?.to_f64()?; + let c = this.read_scalar(c)?.to_f64()?; + let fuse: bool = this.machine.float_nondet && this.machine.rng.get_mut().random(); + let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value }; + let res = this.adjust_nan(res, &[a, b, c]); + this.write_scalar(res, dest)?; + } + + #[rustfmt::skip] + | "fadd_fast" + | "fsub_fast" + | "fmul_fast" + | "fdiv_fast" + | "frem_fast" + => { + let [a, b] = check_intrinsic_arg_count(args)?; + let a = this.read_immediate(a)?; + let b = this.read_immediate(b)?; + let op = match intrinsic_name { + "fadd_fast" => mir::BinOp::Add, + "fsub_fast" => mir::BinOp::Sub, + "fmul_fast" => mir::BinOp::Mul, + "fdiv_fast" => mir::BinOp::Div, + "frem_fast" => mir::BinOp::Rem, + _ => bug!(), + }; + let float_finite = |x: &ImmTy<'tcx>| -> InterpResult<'tcx, bool> { + let ty::Float(fty) = x.layout.ty.kind() else { + bug!("float_finite: non-float input type {}", x.layout.ty) + }; + interp_ok(match fty { + FloatTy::F16 => x.to_scalar().to_f16()?.is_finite(), + FloatTy::F32 => x.to_scalar().to_f32()?.is_finite(), + FloatTy::F64 => x.to_scalar().to_f64()?.is_finite(), + FloatTy::F128 => x.to_scalar().to_f128()?.is_finite(), + }) + }; + match (float_finite(&a)?, float_finite(&b)?) { + (false, false) => throw_ub_format!( + "`{intrinsic_name}` intrinsic called with non-finite value as both parameters", + ), + (false, _) => throw_ub_format!( + "`{intrinsic_name}` intrinsic called with non-finite value as first parameter", + ), + (_, false) => throw_ub_format!( + "`{intrinsic_name}` intrinsic called with non-finite value as second parameter", + ), + _ => {} + } + let res = this.binary_op(op, &a, &b)?; + // This cannot be a NaN so we also don't have to apply any non-determinism. + // (Also, `binary_op` already called `generate_nan` if needed.) + if !float_finite(&res)? { + throw_ub_format!("`{intrinsic_name}` intrinsic produced non-finite value as result"); + } + // Apply a relative error of 4ULP to simulate non-deterministic precision loss + // due to optimizations. + let res = math::apply_random_float_error_to_imm(this, res, 4)?; + this.write_immediate(*res, dest)?; + } + + "float_to_int_unchecked" => { + let [val] = check_intrinsic_arg_count(args)?; + let val = this.read_immediate(val)?; + + let res = this + .float_to_int_checked(&val, dest.layout, Round::TowardZero)? + .ok_or_else(|| { + err_ub_format!( + "`float_to_int_unchecked` intrinsic called on {val} which cannot be represented in target type `{:?}`", + dest.layout.ty + ) + })?; + + this.write_immediate(*res, dest)?; + } + + // Operations that need host floats. + #[rustfmt::skip] + | "sinf32" + | "cosf32" + | "expf32" + | "exp2f32" + | "logf32" + | "log10f32" + | "log2f32" + => { + let [f] = check_intrinsic_arg_count(args)?; + let f = this.read_scalar(f)?.to_f32()?; + + let res = math::fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| { + // Using host floats (but it's fine, these operations do not have + // guaranteed precision). + let host = f.to_host(); + let res = match intrinsic_name { + "sinf32" => host.sin(), + "cosf32" => host.cos(), + "expf32" => host.exp(), + "exp2f32" => host.exp2(), + "logf32" => host.ln(), + "log10f32" => host.log10(), + "log2f32" => host.log2(), + _ => bug!(), + }; + let res = res.to_soft(); + + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + let res = math::apply_random_float_error_ulp( + this, + res, + 4, + ); + + // Clamp the result to the guaranteed range of this function according to the C standard, + // if any. + math::clamp_float_value(intrinsic_name, res) + }); + let res = this.adjust_nan(res, &[f]); + this.write_scalar(res, dest)?; + } + + #[rustfmt::skip] + | "sinf64" + | "cosf64" + | "expf64" + | "exp2f64" + | "logf64" + | "log10f64" + | "log2f64" + => { + let [f] = check_intrinsic_arg_count(args)?; + let f = this.read_scalar(f)?.to_f64()?; + + let res = math::fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| { + // Using host floats (but it's fine, these operations do not have + // guaranteed precision). + let host = f.to_host(); + let res = match intrinsic_name { + "sinf64" => host.sin(), + "cosf64" => host.cos(), + "expf64" => host.exp(), + "exp2f64" => host.exp2(), + "logf64" => host.ln(), + "log10f64" => host.log10(), + "log2f64" => host.log2(), + _ => bug!(), + }; + let res = res.to_soft(); + + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + let res = math::apply_random_float_error_ulp( + this, + res, + 4, + ); + + // Clamp the result to the guaranteed range of this function according to the C standard, + // if any. + math::clamp_float_value(intrinsic_name, res) + }); + let res = this.adjust_nan(res, &[f]); + this.write_scalar(res, dest)?; + } + + "powf32" => { + let [f1, f2] = check_intrinsic_arg_count(args)?; + let f1 = this.read_scalar(f1)?.to_f32()?; + let f2 = this.read_scalar(f2)?.to_f32()?; + + let res = + math::fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| { + // Using host floats (but it's fine, this operation does not have guaranteed precision). + let res = f1.to_host().powf(f2.to_host()).to_soft(); + + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + math::apply_random_float_error_ulp(this, res, 4) + }); + let res = this.adjust_nan(res, &[f1, f2]); + this.write_scalar(res, dest)?; + } + "powf64" => { + let [f1, f2] = check_intrinsic_arg_count(args)?; + let f1 = this.read_scalar(f1)?.to_f64()?; + let f2 = this.read_scalar(f2)?.to_f64()?; + + let res = + math::fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| { + // Using host floats (but it's fine, this operation does not have guaranteed precision). + let res = f1.to_host().powf(f2.to_host()).to_soft(); + + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + math::apply_random_float_error_ulp(this, res, 4) + }); + let res = this.adjust_nan(res, &[f1, f2]); + this.write_scalar(res, dest)?; + } + + "powif32" => { + let [f, i] = check_intrinsic_arg_count(args)?; + let f = this.read_scalar(f)?.to_f32()?; + let i = this.read_scalar(i)?.to_i32()?; + + let res = math::fixed_powi_value(this, f, i).unwrap_or_else(|| { + // Using host floats (but it's fine, this operation does not have guaranteed precision). + let res = f.to_host().powi(i).to_soft(); + + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + math::apply_random_float_error_ulp(this, res, 4) + }); + let res = this.adjust_nan(res, &[f]); + this.write_scalar(res, dest)?; + } + "powif64" => { + let [f, i] = check_intrinsic_arg_count(args)?; + let f = this.read_scalar(f)?.to_f64()?; + let i = this.read_scalar(i)?.to_i32()?; + + let res = math::fixed_powi_value(this, f, i).unwrap_or_else(|| { + // Using host floats (but it's fine, this operation does not have guaranteed precision). + let res = f.to_host().powi(i).to_soft(); + + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + math::apply_random_float_error_ulp(this, res, 4) + }); + let res = this.adjust_nan(res, &[f]); + this.write_scalar(res, dest)?; + } + + _ => return interp_ok(EmulateItemResult::NotSupported), + } + + interp_ok(EmulateItemResult::NeedsReturn) + } +} diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index 8d323ec169df..a80b939d84ea 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -1,6 +1,7 @@ #![warn(clippy::arithmetic_side_effects)] mod atomic; +mod math; mod simd; pub use self::atomic::AtomicRmwOp; @@ -8,13 +9,11 @@ pub use self::atomic::AtomicRmwOp; #[rustfmt::skip] // prevent `use` reordering use rand::Rng; use rustc_abi::Size; -use rustc_apfloat::{self, Float, Round}; -use rustc_middle::mir; -use rustc_middle::ty::{self, FloatTy}; +use rustc_middle::{mir, ty}; use rustc_span::{Symbol, sym}; use self::atomic::EvalContextExt as _; -use self::helpers::{ToHost, ToSoft}; +use self::math::EvalContextExt as _; use self::simd::EvalContextExt as _; use crate::*; @@ -179,288 +178,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(Scalar::from_bool(branch), dest)?; } - "sqrtf32" => { - let [f] = check_intrinsic_arg_count(args)?; - let f = this.read_scalar(f)?.to_f32()?; - // Sqrt is specified to be fully precise. - let res = math::sqrt(f); - let res = this.adjust_nan(res, &[f]); - this.write_scalar(res, dest)?; - } - "sqrtf64" => { - let [f] = check_intrinsic_arg_count(args)?; - let f = this.read_scalar(f)?.to_f64()?; - // Sqrt is specified to be fully precise. - let res = math::sqrt(f); - let res = this.adjust_nan(res, &[f]); - this.write_scalar(res, dest)?; - } - - #[rustfmt::skip] - | "sinf32" - | "cosf32" - | "expf32" - | "exp2f32" - | "logf32" - | "log10f32" - | "log2f32" - => { - let [f] = check_intrinsic_arg_count(args)?; - let f = this.read_scalar(f)?.to_f32()?; - - let res = math::fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| { - // Using host floats (but it's fine, these operations do not have - // guaranteed precision). - let host = f.to_host(); - let res = match intrinsic_name { - "sinf32" => host.sin(), - "cosf32" => host.cos(), - "expf32" => host.exp(), - "exp2f32" => host.exp2(), - "logf32" => host.ln(), - "log10f32" => host.log10(), - "log2f32" => host.log2(), - _ => bug!(), - }; - let res = res.to_soft(); - - // Apply a relative error of 4ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - let res = math::apply_random_float_error_ulp( - this, - res, - 4, - ); - - // Clamp the result to the guaranteed range of this function according to the C standard, - // if any. - math::clamp_float_value(intrinsic_name, res) - }); - let res = this.adjust_nan(res, &[f]); - this.write_scalar(res, dest)?; - } - - #[rustfmt::skip] - | "sinf64" - | "cosf64" - | "expf64" - | "exp2f64" - | "logf64" - | "log10f64" - | "log2f64" - => { - let [f] = check_intrinsic_arg_count(args)?; - let f = this.read_scalar(f)?.to_f64()?; - - let res = math::fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| { - // Using host floats (but it's fine, these operations do not have - // guaranteed precision). - let host = f.to_host(); - let res = match intrinsic_name { - "sinf64" => host.sin(), - "cosf64" => host.cos(), - "expf64" => host.exp(), - "exp2f64" => host.exp2(), - "logf64" => host.ln(), - "log10f64" => host.log10(), - "log2f64" => host.log2(), - _ => bug!(), - }; - let res = res.to_soft(); - - // Apply a relative error of 4ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - let res = math::apply_random_float_error_ulp( - this, - res, - 4, - ); - - // Clamp the result to the guaranteed range of this function according to the C standard, - // if any. - math::clamp_float_value(intrinsic_name, res) - }); - let res = this.adjust_nan(res, &[f]); - this.write_scalar(res, dest)?; - } - - "fmaf32" => { - let [a, b, c] = check_intrinsic_arg_count(args)?; - let a = this.read_scalar(a)?.to_f32()?; - let b = this.read_scalar(b)?.to_f32()?; - let c = this.read_scalar(c)?.to_f32()?; - let res = a.mul_add(b, c).value; - let res = this.adjust_nan(res, &[a, b, c]); - this.write_scalar(res, dest)?; - } - "fmaf64" => { - let [a, b, c] = check_intrinsic_arg_count(args)?; - let a = this.read_scalar(a)?.to_f64()?; - let b = this.read_scalar(b)?.to_f64()?; - let c = this.read_scalar(c)?.to_f64()?; - let res = a.mul_add(b, c).value; - let res = this.adjust_nan(res, &[a, b, c]); - this.write_scalar(res, dest)?; - } - - "fmuladdf32" => { - let [a, b, c] = check_intrinsic_arg_count(args)?; - let a = this.read_scalar(a)?.to_f32()?; - let b = this.read_scalar(b)?.to_f32()?; - let c = this.read_scalar(c)?.to_f32()?; - let fuse: bool = this.machine.float_nondet && this.machine.rng.get_mut().random(); - let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value }; - let res = this.adjust_nan(res, &[a, b, c]); - this.write_scalar(res, dest)?; - } - "fmuladdf64" => { - let [a, b, c] = check_intrinsic_arg_count(args)?; - let a = this.read_scalar(a)?.to_f64()?; - let b = this.read_scalar(b)?.to_f64()?; - let c = this.read_scalar(c)?.to_f64()?; - let fuse: bool = this.machine.float_nondet && this.machine.rng.get_mut().random(); - let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value }; - let res = this.adjust_nan(res, &[a, b, c]); - this.write_scalar(res, dest)?; - } - - "powf32" => { - let [f1, f2] = check_intrinsic_arg_count(args)?; - let f1 = this.read_scalar(f1)?.to_f32()?; - let f2 = this.read_scalar(f2)?.to_f32()?; - - let res = - math::fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| { - // Using host floats (but it's fine, this operation does not have guaranteed precision). - let res = f1.to_host().powf(f2.to_host()).to_soft(); - - // Apply a relative error of 4ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - math::apply_random_float_error_ulp(this, res, 4) - }); - let res = this.adjust_nan(res, &[f1, f2]); - this.write_scalar(res, dest)?; - } - "powf64" => { - let [f1, f2] = check_intrinsic_arg_count(args)?; - let f1 = this.read_scalar(f1)?.to_f64()?; - let f2 = this.read_scalar(f2)?.to_f64()?; - - let res = - math::fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| { - // Using host floats (but it's fine, this operation does not have guaranteed precision). - let res = f1.to_host().powf(f2.to_host()).to_soft(); - - // Apply a relative error of 4ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - math::apply_random_float_error_ulp(this, res, 4) - }); - let res = this.adjust_nan(res, &[f1, f2]); - this.write_scalar(res, dest)?; - } - - "powif32" => { - let [f, i] = check_intrinsic_arg_count(args)?; - let f = this.read_scalar(f)?.to_f32()?; - let i = this.read_scalar(i)?.to_i32()?; - - let res = math::fixed_powi_value(this, f, i).unwrap_or_else(|| { - // Using host floats (but it's fine, this operation does not have guaranteed precision). - let res = f.to_host().powi(i).to_soft(); - - // Apply a relative error of 4ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - math::apply_random_float_error_ulp(this, res, 4) - }); - let res = this.adjust_nan(res, &[f]); - this.write_scalar(res, dest)?; - } - "powif64" => { - let [f, i] = check_intrinsic_arg_count(args)?; - let f = this.read_scalar(f)?.to_f64()?; - let i = this.read_scalar(i)?.to_i32()?; - - let res = math::fixed_powi_value(this, f, i).unwrap_or_else(|| { - // Using host floats (but it's fine, this operation does not have guaranteed precision). - let res = f.to_host().powi(i).to_soft(); - - // Apply a relative error of 4ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - math::apply_random_float_error_ulp(this, res, 4) - }); - let res = this.adjust_nan(res, &[f]); - this.write_scalar(res, dest)?; - } - - #[rustfmt::skip] - | "fadd_fast" - | "fsub_fast" - | "fmul_fast" - | "fdiv_fast" - | "frem_fast" - => { - let [a, b] = check_intrinsic_arg_count(args)?; - let a = this.read_immediate(a)?; - let b = this.read_immediate(b)?; - let op = match intrinsic_name { - "fadd_fast" => mir::BinOp::Add, - "fsub_fast" => mir::BinOp::Sub, - "fmul_fast" => mir::BinOp::Mul, - "fdiv_fast" => mir::BinOp::Div, - "frem_fast" => mir::BinOp::Rem, - _ => bug!(), - }; - let float_finite = |x: &ImmTy<'tcx>| -> InterpResult<'tcx, bool> { - let ty::Float(fty) = x.layout.ty.kind() else { - bug!("float_finite: non-float input type {}", x.layout.ty) - }; - interp_ok(match fty { - FloatTy::F16 => x.to_scalar().to_f16()?.is_finite(), - FloatTy::F32 => x.to_scalar().to_f32()?.is_finite(), - FloatTy::F64 => x.to_scalar().to_f64()?.is_finite(), - FloatTy::F128 => x.to_scalar().to_f128()?.is_finite(), - }) - }; - match (float_finite(&a)?, float_finite(&b)?) { - (false, false) => throw_ub_format!( - "`{intrinsic_name}` intrinsic called with non-finite value as both parameters", - ), - (false, _) => throw_ub_format!( - "`{intrinsic_name}` intrinsic called with non-finite value as first parameter", - ), - (_, false) => throw_ub_format!( - "`{intrinsic_name}` intrinsic called with non-finite value as second parameter", - ), - _ => {} - } - let res = this.binary_op(op, &a, &b)?; - // This cannot be a NaN so we also don't have to apply any non-determinism. - // (Also, `binary_op` already called `generate_nan` if needed.) - if !float_finite(&res)? { - throw_ub_format!("`{intrinsic_name}` intrinsic produced non-finite value as result"); - } - // Apply a relative error of 4ULP to simulate non-deterministic precision loss - // due to optimizations. - let res = math::apply_random_float_error_to_imm(this, res, 4)?; - this.write_immediate(*res, dest)?; - } - - "float_to_int_unchecked" => { - let [val] = check_intrinsic_arg_count(args)?; - let val = this.read_immediate(val)?; - - let res = this - .float_to_int_checked(&val, dest.layout, Round::TowardZero)? - .ok_or_else(|| { - err_ub_format!( - "`float_to_int_unchecked` intrinsic called on {val} which cannot be represented in target type `{:?}`", - dest.layout.ty - ) - })?; - - this.write_immediate(*res, dest)?; - } - // Other "breakpoint" => { let [] = check_intrinsic_arg_count(args)?; @@ -472,7 +189,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Make these a NOP, so we get the better Miri-native error messages. } - _ => return interp_ok(EmulateItemResult::NotSupported), + _ => return this.emulate_math_intrinsic(intrinsic_name, generic_args, args, dest), } interp_ok(EmulateItemResult::NeedsReturn) From e00051299aa7a355307baeeac0bd593c71e8019e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 19 Jul 2025 12:11:04 +0200 Subject: [PATCH 241/682] extract core operation name instead of listing all function name variants --- src/tools/miri/src/math.rs | 43 +++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/src/tools/miri/src/math.rs b/src/tools/miri/src/math.rs index 254495637da4..f89abbaba480 100644 --- a/src/tools/miri/src/math.rs +++ b/src/tools/miri/src/math.rs @@ -210,61 +210,70 @@ where let pi_over_2 = (pi / two).value; let pi_over_4 = (pi_over_2 / two).value; - Some(match (intrinsic_name, args) { + // Remove `f32`/`f64` suffix, if any. + let name = intrinsic_name + .strip_suffix("f32") + .or_else(|| intrinsic_name.strip_suffix("f64")) + .unwrap_or(intrinsic_name); + // Also strip trailing `f` (indicates "float"), with an exception for "erf" to avoid + // removing that `f`. + let name = if name == "erf" { name } else { name.strip_suffix("f").unwrap_or(name) }; + Some(match (name, args) { // cos(±0) and cosh(±0)= 1 - ("cosf32" | "cosf64" | "coshf" | "cosh", [input]) if input.is_zero() => one, + ("cos" | "cosh", [input]) if input.is_zero() => one, // e^0 = 1 - ("expf32" | "expf64" | "exp2f32" | "exp2f64", [input]) if input.is_zero() => one, + ("exp" | "exp2", [input]) if input.is_zero() => one, // tanh(±INF) = ±1 - ("tanhf" | "tanh", [input]) if input.is_infinite() => one.copy_sign(*input), + ("tanh", [input]) if input.is_infinite() => one.copy_sign(*input), // atan(±INF) = ±π/2 - ("atanf" | "atan", [input]) if input.is_infinite() => pi_over_2.copy_sign(*input), + ("atan", [input]) if input.is_infinite() => pi_over_2.copy_sign(*input), // erf(±INF) = ±1 - ("erff" | "erf", [input]) if input.is_infinite() => one.copy_sign(*input), + ("erf", [input]) if input.is_infinite() => one.copy_sign(*input), // erfc(-INF) = 2 - ("erfcf" | "erfc", [input]) if input.is_neg_infinity() => (one + one).value, + ("erfc", [input]) if input.is_neg_infinity() => (one + one).value, // hypot(x, ±0) = abs(x), if x is not a NaN. - ("_hypotf" | "hypotf" | "_hypot" | "hypot", [x, y]) if !x.is_nan() && y.is_zero() => + // `_hypot` is the Windows name for this. + ("_hypot" | "hypot", [x, y]) if !x.is_nan() && y.is_zero() => x.abs(), // atan2(±0,−0) = ±π. // atan2(±0, y) = ±π for y < 0. // Must check for non NaN because `y.is_negative()` also applies to NaN. - ("atan2f" | "atan2", [x, y]) if (x.is_zero() && (y.is_negative() && !y.is_nan())) => + ("atan2", [x, y]) if (x.is_zero() && (y.is_negative() && !y.is_nan())) => pi.copy_sign(*x), // atan2(±x,−∞) = ±π for finite x > 0. - ("atan2f" | "atan2", [x, y]) + ("atan2", [x, y]) if (!x.is_zero() && !x.is_infinite()) && y.is_neg_infinity() => pi.copy_sign(*x), // atan2(x, ±0) = −π/2 for x < 0. // atan2(x, ±0) = π/2 for x > 0. - ("atan2f" | "atan2", [x, y]) if !x.is_zero() && y.is_zero() => pi_over_2.copy_sign(*x), + ("atan2", [x, y]) if !x.is_zero() && y.is_zero() => pi_over_2.copy_sign(*x), //atan2(±∞, −∞) = ±3π/4 - ("atan2f" | "atan2", [x, y]) if x.is_infinite() && y.is_neg_infinity() => + ("atan2", [x, y]) if x.is_infinite() && y.is_neg_infinity() => (pi_over_4 * three).value.copy_sign(*x), //atan2(±∞, +∞) = ±π/4 - ("atan2f" | "atan2", [x, y]) if x.is_infinite() && y.is_pos_infinity() => + ("atan2", [x, y]) if x.is_infinite() && y.is_pos_infinity() => pi_over_4.copy_sign(*x), // atan2(±∞, y) returns ±π/2 for finite y. - ("atan2f" | "atan2", [x, y]) if x.is_infinite() && (!y.is_infinite() && !y.is_nan()) => + ("atan2", [x, y]) if x.is_infinite() && (!y.is_infinite() && !y.is_nan()) => pi_over_2.copy_sign(*x), // (-1)^(±INF) = 1 - ("powf32" | "powf64", [base, exp]) if *base == -one && exp.is_infinite() => one, + ("pow", [base, exp]) if *base == -one && exp.is_infinite() => one, // 1^y = 1 for any y, even a NaN - ("powf32" | "powf64", [base, exp]) if *base == one => { + ("pow", [base, exp]) if *base == one => { let rng = this.machine.rng.get_mut(); // SNaN exponents get special treatment: they might return 1, or a NaN. let return_nan = exp.is_signaling() && this.machine.float_nondet && rng.random(); @@ -273,7 +282,7 @@ where } // x^(±0) = 1 for any x, even a NaN - ("powf32" | "powf64", [base, exp]) if exp.is_zero() => { + ("pow", [base, exp]) if exp.is_zero() => { let rng = this.machine.rng.get_mut(); // SNaN bases get special treatment: they might return 1, or a NaN. let return_nan = base.is_signaling() && this.machine.float_nondet && rng.random(); From 84e1950065525589f2d22860aa64c36f171fc782 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 19 Jul 2025 12:20:43 +0200 Subject: [PATCH 242/682] move math foreign_items into their own file --- src/tools/miri/src/math.rs | 12 +- src/tools/miri/src/shims/foreign_items.rs | 238 ++------------------- src/tools/miri/src/shims/math.rs | 247 ++++++++++++++++++++++ src/tools/miri/src/shims/mod.rs | 1 + 4 files changed, 266 insertions(+), 232 deletions(-) create mode 100644 src/tools/miri/src/shims/math.rs diff --git a/src/tools/miri/src/math.rs b/src/tools/miri/src/math.rs index f89abbaba480..401e6dd7aab8 100644 --- a/src/tools/miri/src/math.rs +++ b/src/tools/miri/src/math.rs @@ -239,18 +239,15 @@ where // hypot(x, ±0) = abs(x), if x is not a NaN. // `_hypot` is the Windows name for this. - ("_hypot" | "hypot", [x, y]) if !x.is_nan() && y.is_zero() => - x.abs(), + ("_hypot" | "hypot", [x, y]) if !x.is_nan() && y.is_zero() => x.abs(), // atan2(±0,−0) = ±π. // atan2(±0, y) = ±π for y < 0. // Must check for non NaN because `y.is_negative()` also applies to NaN. - ("atan2", [x, y]) if (x.is_zero() && (y.is_negative() && !y.is_nan())) => - pi.copy_sign(*x), + ("atan2", [x, y]) if (x.is_zero() && (y.is_negative() && !y.is_nan())) => pi.copy_sign(*x), // atan2(±x,−∞) = ±π for finite x > 0. - ("atan2", [x, y]) - if (!x.is_zero() && !x.is_infinite()) && y.is_neg_infinity() => + ("atan2", [x, y]) if (!x.is_zero() && !x.is_infinite()) && y.is_neg_infinity() => pi.copy_sign(*x), // atan2(x, ±0) = −π/2 for x < 0. @@ -262,8 +259,7 @@ where (pi_over_4 * three).value.copy_sign(*x), //atan2(±∞, +∞) = ±π/4 - ("atan2", [x, y]) if x.is_infinite() && y.is_pos_infinity() => - pi_over_4.copy_sign(*x), + ("atan2", [x, y]) if x.is_infinite() && y.is_pos_infinity() => pi_over_4.copy_sign(*x), // atan2(±∞, y) returns ±π/2 for finite y. ("atan2", [x, y]) if x.is_infinite() && (!y.is_infinite() && !y.is_nan()) => diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 10e28178177b..eca8cccf5efc 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -3,7 +3,6 @@ use std::io::Write; use std::path::Path; use rustc_abi::{Align, AlignFromBytesError, CanonAbi, Size}; -use rustc_apfloat::Float; use rustc_ast::expand::allocator::alloc_error_handler_name; use rustc_hir::attrs::Linkage; use rustc_hir::def::DefKind; @@ -15,7 +14,6 @@ use rustc_middle::{mir, ty}; use rustc_span::Symbol; use rustc_target::callconv::FnAbi; -use self::helpers::{ToHost, ToSoft}; use super::alloc::EvalContextExt as _; use super::backtrace::EvalContextExt as _; use crate::helpers::EvalContextExt as _; @@ -818,225 +816,6 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_pointer(ptr_dest, dest)?; } - // math functions (note that there are also intrinsics for some other functions) - #[rustfmt::skip] - | "cbrtf" - | "coshf" - | "sinhf" - | "tanf" - | "tanhf" - | "acosf" - | "asinf" - | "atanf" - | "log1pf" - | "expm1f" - | "tgammaf" - | "erff" - | "erfcf" - => { - let [f] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; - let f = this.read_scalar(f)?.to_f32()?; - - let res = math::fixed_float_value(this, link_name.as_str(), &[f]).unwrap_or_else(|| { - // Using host floats (but it's fine, these operations do not have - // guaranteed precision). - let f_host = f.to_host(); - let res = match link_name.as_str() { - "cbrtf" => f_host.cbrt(), - "coshf" => f_host.cosh(), - "sinhf" => f_host.sinh(), - "tanf" => f_host.tan(), - "tanhf" => f_host.tanh(), - "acosf" => f_host.acos(), - "asinf" => f_host.asin(), - "atanf" => f_host.atan(), - "log1pf" => f_host.ln_1p(), - "expm1f" => f_host.exp_m1(), - "tgammaf" => f_host.gamma(), - "erff" => f_host.erf(), - "erfcf" => f_host.erfc(), - _ => bug!(), - }; - let res = res.to_soft(); - // Apply a relative error of 4ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - let res = math::apply_random_float_error_ulp(this, res, 4); - - // Clamp the result to the guaranteed range of this function according to the C standard, - // if any. - math::clamp_float_value(link_name.as_str(), res) - }); - let res = this.adjust_nan(res, &[f]); - this.write_scalar(res, dest)?; - } - #[rustfmt::skip] - | "_hypotf" - | "hypotf" - | "atan2f" - | "fdimf" - => { - let [f1, f2] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; - let f1 = this.read_scalar(f1)?.to_f32()?; - let f2 = this.read_scalar(f2)?.to_f32()?; - - let res = math::fixed_float_value(this, link_name.as_str(), &[f1, f2]) - .unwrap_or_else(|| { - let res = match link_name.as_str() { - // underscore case for windows, here and below - // (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019) - // Using host floats (but it's fine, these operations do not have guaranteed precision). - "_hypotf" | "hypotf" => f1.to_host().hypot(f2.to_host()).to_soft(), - "atan2f" => f1.to_host().atan2(f2.to_host()).to_soft(), - #[allow(deprecated)] - "fdimf" => f1.to_host().abs_sub(f2.to_host()).to_soft(), - _ => bug!(), - }; - // Apply a relative error of 4ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - let res = math::apply_random_float_error_ulp(this, res, 4); - - // Clamp the result to the guaranteed range of this function according to the C standard, - // if any. - math::clamp_float_value(link_name.as_str(), res) - }); - let res = this.adjust_nan(res, &[f1, f2]); - this.write_scalar(res, dest)?; - } - #[rustfmt::skip] - | "cbrt" - | "cosh" - | "sinh" - | "tan" - | "tanh" - | "acos" - | "asin" - | "atan" - | "log1p" - | "expm1" - | "tgamma" - | "erf" - | "erfc" - => { - let [f] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; - let f = this.read_scalar(f)?.to_f64()?; - - let res = math::fixed_float_value(this, link_name.as_str(), &[f]).unwrap_or_else(|| { - // Using host floats (but it's fine, these operations do not have - // guaranteed precision). - let f_host = f.to_host(); - let res = match link_name.as_str() { - "cbrt" => f_host.cbrt(), - "cosh" => f_host.cosh(), - "sinh" => f_host.sinh(), - "tan" => f_host.tan(), - "tanh" => f_host.tanh(), - "acos" => f_host.acos(), - "asin" => f_host.asin(), - "atan" => f_host.atan(), - "log1p" => f_host.ln_1p(), - "expm1" => f_host.exp_m1(), - "tgamma" => f_host.gamma(), - "erf" => f_host.erf(), - "erfc" => f_host.erfc(), - _ => bug!(), - }; - let res = res.to_soft(); - // Apply a relative error of 4ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - let res = math::apply_random_float_error_ulp(this, res, 4); - - // Clamp the result to the guaranteed range of this function according to the C standard, - // if any. - math::clamp_float_value(link_name.as_str(), res) - }); - let res = this.adjust_nan(res, &[f]); - this.write_scalar(res, dest)?; - } - #[rustfmt::skip] - | "_hypot" - | "hypot" - | "atan2" - | "fdim" - => { - let [f1, f2] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; - let f1 = this.read_scalar(f1)?.to_f64()?; - let f2 = this.read_scalar(f2)?.to_f64()?; - - let res = math::fixed_float_value(this, link_name.as_str(), &[f1, f2]).unwrap_or_else(|| { - let res = match link_name.as_str() { - // underscore case for windows, here and below - // (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019) - // Using host floats (but it's fine, these operations do not have guaranteed precision). - "_hypot" | "hypot" => f1.to_host().hypot(f2.to_host()).to_soft(), - "atan2" => f1.to_host().atan2(f2.to_host()).to_soft(), - #[allow(deprecated)] - "fdim" => f1.to_host().abs_sub(f2.to_host()).to_soft(), - _ => bug!(), - }; - // Apply a relative error of 4ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - let res = math::apply_random_float_error_ulp(this, res, 4); - - // Clamp the result to the guaranteed range of this function according to the C standard, - // if any. - math::clamp_float_value(link_name.as_str(), res) - }); - let res = this.adjust_nan(res, &[f1, f2]); - this.write_scalar(res, dest)?; - } - #[rustfmt::skip] - | "_ldexp" - | "ldexp" - | "scalbn" - => { - let [x, exp] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; - // For radix-2 (binary) systems, `ldexp` and `scalbn` are the same. - let x = this.read_scalar(x)?.to_f64()?; - let exp = this.read_scalar(exp)?.to_i32()?; - - let res = x.scalbn(exp); - let res = this.adjust_nan(res, &[x]); - this.write_scalar(res, dest)?; - } - "lgammaf_r" => { - let [x, signp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; - let x = this.read_scalar(x)?.to_f32()?; - let signp = this.deref_pointer_as(signp, this.machine.layouts.i32)?; - - // Using host floats (but it's fine, these operations do not have guaranteed precision). - let (res, sign) = x.to_host().ln_gamma(); - this.write_int(sign, &signp)?; - - let res = res.to_soft(); - // Apply a relative error of 4ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - let res = math::apply_random_float_error_ulp(this, res, 4); - // Clamp the result to the guaranteed range of this function according to the C standard, - // if any. - let res = math::clamp_float_value(link_name.as_str(), res); - let res = this.adjust_nan(res, &[x]); - this.write_scalar(res, dest)?; - } - "lgamma_r" => { - let [x, signp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; - let x = this.read_scalar(x)?.to_f64()?; - let signp = this.deref_pointer_as(signp, this.machine.layouts.i32)?; - - // Using host floats (but it's fine, these operations do not have guaranteed precision). - let (res, sign) = x.to_host().ln_gamma(); - this.write_int(sign, &signp)?; - - let res = res.to_soft(); - // Apply a relative error of 4ULP to introduce some non-determinism - // simulating imprecise implementations and optimizations. - let res = math::apply_random_float_error_ulp(this, res, 4); - // Clamp the result to the guaranteed range of this function according to the C standard, - // if any. - let res = math::clamp_float_value(link_name.as_str(), res); - let res = this.adjust_nan(res, &[x]); - this.write_scalar(res, dest)?; - } - // LLVM intrinsics "llvm.prefetch" => { let [p, rw, loc, ty] = @@ -1118,8 +897,18 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } } - // Platform-specific shims - _ => + // Fallback to shims in submodules. + _ => { + // Math shims + #[expect(irrefutable_let_patterns)] + if let res = shims::math::EvalContextExt::emulate_foreign_item_inner( + this, link_name, abi, args, dest, + )? && !matches!(res, EmulateItemResult::NotSupported) + { + return interp_ok(res); + } + + // Platform-specific shims return match this.tcx.sess.target.os.as_ref() { _ if this.target_os_is_unix() => shims::unix::foreign_items::EvalContextExt::emulate_foreign_item_inner( @@ -1134,7 +923,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this, link_name, abi, args, dest, ), _ => interp_ok(EmulateItemResult::NotSupported), - }, + }; + } }; // We only fall through to here if we did *not* hit the `_` arm above, // i.e., if we actually emulated the function with one of the shims. diff --git a/src/tools/miri/src/shims/math.rs b/src/tools/miri/src/shims/math.rs new file mode 100644 index 000000000000..576e76494bcb --- /dev/null +++ b/src/tools/miri/src/shims/math.rs @@ -0,0 +1,247 @@ +use rustc_abi::CanonAbi; +use rustc_apfloat::Float; +use rustc_middle::ty::Ty; +use rustc_span::Symbol; +use rustc_target::callconv::FnAbi; + +use self::helpers::{ToHost, ToSoft}; +use crate::*; + +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + fn emulate_foreign_item_inner( + &mut self, + link_name: Symbol, + abi: &FnAbi<'tcx, Ty<'tcx>>, + args: &[OpTy<'tcx>], + dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx, EmulateItemResult> { + let this = self.eval_context_mut(); + + // math functions (note that there are also intrinsics for some other functions) + match link_name.as_str() { + // math functions (note that there are also intrinsics for some other functions) + #[rustfmt::skip] + | "cbrtf" + | "coshf" + | "sinhf" + | "tanf" + | "tanhf" + | "acosf" + | "asinf" + | "atanf" + | "log1pf" + | "expm1f" + | "tgammaf" + | "erff" + | "erfcf" + => { + let [f] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; + let f = this.read_scalar(f)?.to_f32()?; + + let res = math::fixed_float_value(this, link_name.as_str(), &[f]).unwrap_or_else(|| { + // Using host floats (but it's fine, these operations do not have + // guaranteed precision). + let f_host = f.to_host(); + let res = match link_name.as_str() { + "cbrtf" => f_host.cbrt(), + "coshf" => f_host.cosh(), + "sinhf" => f_host.sinh(), + "tanf" => f_host.tan(), + "tanhf" => f_host.tanh(), + "acosf" => f_host.acos(), + "asinf" => f_host.asin(), + "atanf" => f_host.atan(), + "log1pf" => f_host.ln_1p(), + "expm1f" => f_host.exp_m1(), + "tgammaf" => f_host.gamma(), + "erff" => f_host.erf(), + "erfcf" => f_host.erfc(), + _ => bug!(), + }; + let res = res.to_soft(); + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + let res = math::apply_random_float_error_ulp(this, res, 4); + + // Clamp the result to the guaranteed range of this function according to the C standard, + // if any. + math::clamp_float_value(link_name.as_str(), res) + }); + let res = this.adjust_nan(res, &[f]); + this.write_scalar(res, dest)?; + } + #[rustfmt::skip] + | "_hypotf" + | "hypotf" + | "atan2f" + | "fdimf" + => { + let [f1, f2] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; + let f1 = this.read_scalar(f1)?.to_f32()?; + let f2 = this.read_scalar(f2)?.to_f32()?; + + let res = math::fixed_float_value(this, link_name.as_str(), &[f1, f2]) + .unwrap_or_else(|| { + let res = match link_name.as_str() { + // underscore case for windows, here and below + // (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019) + // Using host floats (but it's fine, these operations do not have guaranteed precision). + "_hypotf" | "hypotf" => f1.to_host().hypot(f2.to_host()).to_soft(), + "atan2f" => f1.to_host().atan2(f2.to_host()).to_soft(), + #[allow(deprecated)] + "fdimf" => f1.to_host().abs_sub(f2.to_host()).to_soft(), + _ => bug!(), + }; + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + let res = math::apply_random_float_error_ulp(this, res, 4); + + // Clamp the result to the guaranteed range of this function according to the C standard, + // if any. + math::clamp_float_value(link_name.as_str(), res) + }); + let res = this.adjust_nan(res, &[f1, f2]); + this.write_scalar(res, dest)?; + } + #[rustfmt::skip] + | "cbrt" + | "cosh" + | "sinh" + | "tan" + | "tanh" + | "acos" + | "asin" + | "atan" + | "log1p" + | "expm1" + | "tgamma" + | "erf" + | "erfc" + => { + let [f] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; + let f = this.read_scalar(f)?.to_f64()?; + + let res = math::fixed_float_value(this, link_name.as_str(), &[f]).unwrap_or_else(|| { + // Using host floats (but it's fine, these operations do not have + // guaranteed precision). + let f_host = f.to_host(); + let res = match link_name.as_str() { + "cbrt" => f_host.cbrt(), + "cosh" => f_host.cosh(), + "sinh" => f_host.sinh(), + "tan" => f_host.tan(), + "tanh" => f_host.tanh(), + "acos" => f_host.acos(), + "asin" => f_host.asin(), + "atan" => f_host.atan(), + "log1p" => f_host.ln_1p(), + "expm1" => f_host.exp_m1(), + "tgamma" => f_host.gamma(), + "erf" => f_host.erf(), + "erfc" => f_host.erfc(), + _ => bug!(), + }; + let res = res.to_soft(); + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + let res = math::apply_random_float_error_ulp(this, res, 4); + + // Clamp the result to the guaranteed range of this function according to the C standard, + // if any. + math::clamp_float_value(link_name.as_str(), res) + }); + let res = this.adjust_nan(res, &[f]); + this.write_scalar(res, dest)?; + } + #[rustfmt::skip] + | "_hypot" + | "hypot" + | "atan2" + | "fdim" + => { + let [f1, f2] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; + let f1 = this.read_scalar(f1)?.to_f64()?; + let f2 = this.read_scalar(f2)?.to_f64()?; + + let res = math::fixed_float_value(this, link_name.as_str(), &[f1, f2]).unwrap_or_else(|| { + let res = match link_name.as_str() { + // underscore case for windows, here and below + // (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019) + // Using host floats (but it's fine, these operations do not have guaranteed precision). + "_hypot" | "hypot" => f1.to_host().hypot(f2.to_host()).to_soft(), + "atan2" => f1.to_host().atan2(f2.to_host()).to_soft(), + #[allow(deprecated)] + "fdim" => f1.to_host().abs_sub(f2.to_host()).to_soft(), + _ => bug!(), + }; + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + let res = math::apply_random_float_error_ulp(this, res, 4); + + // Clamp the result to the guaranteed range of this function according to the C standard, + // if any. + math::clamp_float_value(link_name.as_str(), res) + }); + let res = this.adjust_nan(res, &[f1, f2]); + this.write_scalar(res, dest)?; + } + #[rustfmt::skip] + | "_ldexp" + | "ldexp" + | "scalbn" + => { + let [x, exp] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; + // For radix-2 (binary) systems, `ldexp` and `scalbn` are the same. + let x = this.read_scalar(x)?.to_f64()?; + let exp = this.read_scalar(exp)?.to_i32()?; + + let res = x.scalbn(exp); + let res = this.adjust_nan(res, &[x]); + this.write_scalar(res, dest)?; + } + "lgammaf_r" => { + let [x, signp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let x = this.read_scalar(x)?.to_f32()?; + let signp = this.deref_pointer_as(signp, this.machine.layouts.i32)?; + + // Using host floats (but it's fine, these operations do not have guaranteed precision). + let (res, sign) = x.to_host().ln_gamma(); + this.write_int(sign, &signp)?; + + let res = res.to_soft(); + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + let res = math::apply_random_float_error_ulp(this, res, 4); + // Clamp the result to the guaranteed range of this function according to the C standard, + // if any. + let res = math::clamp_float_value(link_name.as_str(), res); + let res = this.adjust_nan(res, &[x]); + this.write_scalar(res, dest)?; + } + "lgamma_r" => { + let [x, signp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + let x = this.read_scalar(x)?.to_f64()?; + let signp = this.deref_pointer_as(signp, this.machine.layouts.i32)?; + + // Using host floats (but it's fine, these operations do not have guaranteed precision). + let (res, sign) = x.to_host().ln_gamma(); + this.write_int(sign, &signp)?; + + let res = res.to_soft(); + // Apply a relative error of 4ULP to introduce some non-determinism + // simulating imprecise implementations and optimizations. + let res = math::apply_random_float_error_ulp(this, res, 4); + // Clamp the result to the guaranteed range of this function according to the C standard, + // if any. + let res = math::clamp_float_value(link_name.as_str(), res); + let res = this.adjust_nan(res, &[x]); + this.write_scalar(res, dest)?; + } + + _ => return interp_ok(EmulateItemResult::NotSupported), + } + + interp_ok(EmulateItemResult::NeedsReturn) + } +} diff --git a/src/tools/miri/src/shims/mod.rs b/src/tools/miri/src/shims/mod.rs index 7f594d4fdd6b..7f7bc3b1cf72 100644 --- a/src/tools/miri/src/shims/mod.rs +++ b/src/tools/miri/src/shims/mod.rs @@ -4,6 +4,7 @@ mod aarch64; mod alloc; mod backtrace; mod files; +mod math; #[cfg(all(unix, feature = "native-lib"))] mod native_lib; mod unix; From aed0ed4c93d661fc7b66dc4a39690948476e8a4a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 8 Sep 2025 10:40:18 +0200 Subject: [PATCH 243/682] const-eval: disable pointer fragment support --- .../rustc_const_eval/src/interpret/memory.rs | 6 ++- .../src/mir/interpret/allocation.rs | 5 +++ .../interpret/allocation/provenance_map.rs | 16 +++++++- library/core/src/ptr/mod.rs | 38 ++++++++++++++++++- library/coretests/tests/ptr.rs | 9 +++-- tests/ui/consts/const-eval/ptr_fragments.rs | 1 + .../const-eval/ptr_fragments_in_final.rs | 1 + .../consts/const-eval/ptr_fragments_mixed.rs | 28 ++++++++++++++ .../const-eval/ptr_fragments_mixed.stderr | 23 +++++++++++ .../ui/consts/const-eval/read_partial_ptr.rs | 1 + 10 files changed, 119 insertions(+), 9 deletions(-) create mode 100644 tests/ui/consts/const-eval/ptr_fragments_mixed.rs create mode 100644 tests/ui/consts/const-eval/ptr_fragments_mixed.stderr diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 2c1e5087e1c6..6ec85648d6d8 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -1501,8 +1501,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // `get_bytes_mut` will clear the provenance, which is correct, // since we don't want to keep any provenance at the target. // This will also error if copying partial provenance is not supported. - let provenance = - src_alloc.provenance().prepare_copy(src_range, dest_offset, num_copies, self); + let provenance = src_alloc + .provenance() + .prepare_copy(src_range, dest_offset, num_copies, self) + .map_err(|e| e.to_interp_error(src_alloc_id))?; // Prepare a copy of the initialization mask. let init = src_alloc.init_mask().prepare_copy(src_range); diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 2ea92a39d482..67962813ae43 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -724,6 +724,11 @@ impl Allocation } // If we get here, we have to check per-byte provenance, and join them together. let prov = 'prov: { + if !Prov::OFFSET_IS_ADDR { + // FIXME(#146291): We need to ensure that we don't mix different pointers with + // the same provenance. + return Err(AllocError::ReadPartialPointer(range.start)); + } // Initialize with first fragment. Must have index 0. let Some((mut joint_prov, 0)) = self.provenance.get_byte(range.start, cx) else { break 'prov None; diff --git a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs index dbbd95408c8a..720e58d7aa0e 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs @@ -11,6 +11,7 @@ use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use tracing::trace; use super::{AllocRange, CtfeProvenance, Provenance, alloc_range}; +use crate::mir::interpret::{AllocError, AllocResult}; /// Stores the provenance information of pointers stored in memory. #[derive(Clone, PartialEq, Eq, Hash, Debug)] @@ -137,6 +138,11 @@ impl ProvenanceMap { let Some(bytes) = self.bytes.as_deref_mut() else { return true; }; + if !Prov::OFFSET_IS_ADDR { + // FIXME(#146291): We need to ensure that we don't mix different pointers with + // the same provenance. + return false; + } let ptr_size = cx.data_layout().pointer_size(); while let Some((offset, (prov, _))) = bytes.iter().next().copied() { // Check if this fragment starts a pointer. @@ -285,7 +291,7 @@ impl ProvenanceMap { dest: Size, count: u64, cx: &impl HasDataLayout, - ) -> ProvenanceCopy { + ) -> AllocResult> { let shift_offset = move |idx, offset| { // compute offset for current repetition let dest_offset = dest + src.size * idx; // `Size` operations @@ -363,6 +369,12 @@ impl ProvenanceMap { } trace!("byte provenances: {bytes:?}"); + if !bytes.is_empty() && !Prov::OFFSET_IS_ADDR { + // FIXME(#146291): We need to ensure that we don't mix different pointers with + // the same provenance. + return Err(AllocError::ReadPartialPointer(src.start)); + } + // And again a buffer for the new list on the target side. let mut dest_bytes = Vec::with_capacity(bytes.len() * (count as usize)); for i in 0..count { @@ -373,7 +385,7 @@ impl ProvenanceMap { dest_bytes_box = Some(dest_bytes.into_boxed_slice()); } - ProvenanceCopy { dest_ptrs: dest_ptrs_box, dest_bytes: dest_bytes_box } + Ok(ProvenanceCopy { dest_ptrs: dest_ptrs_box, dest_bytes: dest_bytes_box }) } /// Applies a provenance copy. diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 6b94088cb567..625024373ef4 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -1348,6 +1348,40 @@ pub const unsafe fn swap(x: *mut T, y: *mut T) { /// assert_eq!(x, [7, 8, 3, 4]); /// assert_eq!(y, [1, 2, 9]); /// ``` +/// +/// # Const evaluation limitations +/// +/// If this function is invoked during const-evaluation, the current implementation has a small (and +/// rarely relevant) limitation: if `count` is at least 2 and the data pointed to by `x` or `y` +/// contains a pointer that crosses the boundary of two `T`-sized chunks of memory, the function may +/// fail to evaluate (similar to a panic during const-evaluation). This behavior may change in the +/// future. +/// +/// The limitation is illustrated by the following example: +/// +/// ``` +/// use std::mem::size_of; +/// use std::ptr; +/// +/// const { unsafe { +/// const PTR_SIZE: usize = size_of::<*const i32>(); +/// let mut data1 = [0u8; PTR_SIZE]; +/// let mut data2 = [0u8; PTR_SIZE]; +/// // Store a pointer in `data1`. +/// data1.as_mut_ptr().cast::<*const i32>().write_unaligned(&42); +/// // Swap the contents of `data1` and `data2` by swapping `PTR_SIZE` many `u8`-sized chunks. +/// // This call will fail, because the pointer in `data1` crosses the boundary +/// // between several of the 1-byte chunks that are being swapped here. +/// //ptr::swap_nonoverlapping(data1.as_mut_ptr(), data2.as_mut_ptr(), PTR_SIZE); +/// // Swap the contents of `data1` and `data2` by swapping a single chunk of size +/// // `[u8; PTR_SIZE]`. That works, as there is no pointer crossing the boundary between +/// // two chunks. +/// ptr::swap_nonoverlapping(&mut data1, &mut data2, 1); +/// // Read the pointer from `data2` and dereference it. +/// let ptr = data2.as_ptr().cast::<*const i32>().read_unaligned(); +/// assert!(*ptr == 42); +/// } } +/// ``` #[inline] #[stable(feature = "swap_nonoverlapping", since = "1.27.0")] #[rustc_const_stable(feature = "const_swap_nonoverlapping", since = "1.88.0")] @@ -1376,7 +1410,9 @@ pub const unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { const_eval_select!( @capture[T] { x: *mut T, y: *mut T, count: usize }: if const { - // At compile-time we don't need all the special code below. + // At compile-time we want to always copy this in chunks of `T`, to ensure that if there + // are pointers inside `T` we will copy them in one go rather than trying to copy a part + // of a pointer (which would not work). // SAFETY: Same preconditions as this function unsafe { swap_nonoverlapping_const(x, y, count) } } else { diff --git a/library/coretests/tests/ptr.rs b/library/coretests/tests/ptr.rs index c13fb96a67f9..f8774833d0a5 100644 --- a/library/coretests/tests/ptr.rs +++ b/library/coretests/tests/ptr.rs @@ -936,12 +936,13 @@ fn test_const_swap_ptr() { assert!(*s1.0.ptr == 666); assert!(*s2.0.ptr == 1); - // Swap them back, byte-for-byte + // Swap them back, again as an array. + // FIXME(#146291): we should be swapping back at type `u8` but that currently does not work. unsafe { ptr::swap_nonoverlapping( - ptr::from_mut(&mut s1).cast::(), - ptr::from_mut(&mut s2).cast::(), - size_of::(), + ptr::from_mut(&mut s1).cast::(), + ptr::from_mut(&mut s2).cast::(), + 1, ); } diff --git a/tests/ui/consts/const-eval/ptr_fragments.rs b/tests/ui/consts/const-eval/ptr_fragments.rs index 04dcbe55590e..7dc5870f89e2 100644 --- a/tests/ui/consts/const-eval/ptr_fragments.rs +++ b/tests/ui/consts/const-eval/ptr_fragments.rs @@ -1,5 +1,6 @@ //! Test that various operations involving pointer fragments work as expected. //@ run-pass +//@ ignore-test: disabled due to use std::mem::{self, MaybeUninit, transmute}; use std::ptr; diff --git a/tests/ui/consts/const-eval/ptr_fragments_in_final.rs b/tests/ui/consts/const-eval/ptr_fragments_in_final.rs index e2f3f51b0863..aed33d7ed8d3 100644 --- a/tests/ui/consts/const-eval/ptr_fragments_in_final.rs +++ b/tests/ui/consts/const-eval/ptr_fragments_in_final.rs @@ -1,4 +1,5 @@ //! Test that we properly error when there is a pointer fragment in the final value. +//@ ignore-test: disabled due to use std::{mem::{self, MaybeUninit}, ptr}; diff --git a/tests/ui/consts/const-eval/ptr_fragments_mixed.rs b/tests/ui/consts/const-eval/ptr_fragments_mixed.rs new file mode 100644 index 000000000000..79a42820f501 --- /dev/null +++ b/tests/ui/consts/const-eval/ptr_fragments_mixed.rs @@ -0,0 +1,28 @@ +//! This mixes fragments from different pointers to the same allocarion, in a way +//! that we should not accept. See . +static A: u8 = 123; + +const HALF_PTR: usize = std::mem::size_of::<*const ()>() / 2; + +const fn mix_ptr() -> *const u8 { + unsafe { + let x: *const u8 = &raw const A; + let mut y = x.wrapping_add(usize::MAX / 4); + core::ptr::copy_nonoverlapping( + (&raw const x).cast::(), + (&raw mut y).cast::(), + HALF_PTR, + ); + y + } +} + +const APTR: *const u8 = mix_ptr(); //~ERROR: unable to read parts of a pointer + +fn main() { + let a = APTR; + println!("{a:p}"); + let b = mix_ptr(); + println!("{b:p}"); + assert_eq!(a, b); +} diff --git a/tests/ui/consts/const-eval/ptr_fragments_mixed.stderr b/tests/ui/consts/const-eval/ptr_fragments_mixed.stderr new file mode 100644 index 000000000000..9e991ab7a7d3 --- /dev/null +++ b/tests/ui/consts/const-eval/ptr_fragments_mixed.stderr @@ -0,0 +1,23 @@ +error[E0080]: unable to read parts of a pointer from memory at ALLOC0 + --> $DIR/ptr_fragments_mixed.rs:20:25 + | +LL | const APTR: *const u8 = mix_ptr(); + | ^^^^^^^^^ evaluation of `APTR` failed inside this call + | + = help: this code performed an operation that depends on the underlying bytes representing a pointer + = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported +note: inside `mix_ptr` + --> $DIR/ptr_fragments_mixed.rs:11:9 + | +LL | / core::ptr::copy_nonoverlapping( +LL | | (&raw const x).cast::(), +LL | | (&raw mut y).cast::(), +LL | | HALF_PTR, +LL | | ); + | |_________^ +note: inside `std::ptr::copy_nonoverlapping::` + --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-eval/read_partial_ptr.rs b/tests/ui/consts/const-eval/read_partial_ptr.rs index bccef9c0bc6c..c153e274e41f 100644 --- a/tests/ui/consts/const-eval/read_partial_ptr.rs +++ b/tests/ui/consts/const-eval/read_partial_ptr.rs @@ -1,4 +1,5 @@ //! Ensure we error when trying to load from a pointer whose provenance has been messed with. +//@ ignore-test: disabled due to const PARTIAL_OVERWRITE: () = { let mut p = &42; From e4e7e71e3dbc88df948ffede96b8d76c675496e1 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Tue, 19 Aug 2025 17:26:39 +0000 Subject: [PATCH 244/682] Add some error message tests --- tests/ui/deref/pin-deref-const.rs | 79 +++++++++++++++++++++++++ tests/ui/deref/pin-deref-const.stderr | 51 ++++++++++++++++ tests/ui/deref/pin-deref.rs | 76 ++++++++++++++++++++++++ tests/ui/deref/pin-deref.stderr | 51 ++++++++++++++++ tests/ui/deref/pin-impl-deref.rs | 40 +++++++++++++ tests/ui/deref/pin-impl-deref.stderr | 85 +++++++++++++++++++++++++++ 6 files changed, 382 insertions(+) create mode 100644 tests/ui/deref/pin-deref-const.rs create mode 100644 tests/ui/deref/pin-deref-const.stderr create mode 100644 tests/ui/deref/pin-deref.rs create mode 100644 tests/ui/deref/pin-deref.stderr create mode 100644 tests/ui/deref/pin-impl-deref.rs create mode 100644 tests/ui/deref/pin-impl-deref.stderr diff --git a/tests/ui/deref/pin-deref-const.rs b/tests/ui/deref/pin-deref-const.rs new file mode 100644 index 000000000000..6ed00177b232 --- /dev/null +++ b/tests/ui/deref/pin-deref-const.rs @@ -0,0 +1,79 @@ +// The purpose of this file is to track the error messages from Pin and DerefMut interacting. +// +// Identical to `pin-deref.rs` except for using unstable `const fn`. + +//@ check-fail + +#![feature(const_convert)] +#![feature(const_trait_impl)] + +use std::ops::DerefMut; +use std::pin::Pin; + +struct MyUnpinType {} + +impl MyUnpinType { + const fn at_self(&self) {} + const fn at_mut_self(&mut self) {} +} + +struct MyPinType(core::marker::PhantomPinned); + +impl MyPinType { + const fn at_self(&self) {} + const fn at_mut_self(&mut self) {} +} + +const fn call_mut_ref_unpin(mut r_unpin: Pin<&mut MyUnpinType>) { + r_unpin.at_self(); + r_unpin.at_mut_self(); +} + +const fn call_ref_unpin(mut r_unpin: Pin<&MyUnpinType>) { + r_unpin.at_self(); + r_unpin.at_mut_self(); //~ ERROR: cannot borrow data in dereference of `Pin<&MyUnpinType>` as mutable +} + +const fn call_mut_ref_pin(mut r_pin: Pin<&mut MyPinType>) { + r_pin.at_self(); + r_pin.at_mut_self(); //~ ERROR: cannot borrow data in dereference of `Pin<&mut MyPinType>` as mutable +} + +const fn call_ref_pin(mut r_pin: Pin<&MyPinType>) { + r_pin.at_self(); + r_pin.at_mut_self(); //~ ERROR: cannot borrow data in dereference of `Pin<&MyPinType>` as mutable +} + +const fn coerce_unpin_rr<'a>(mut r_unpin: &'a mut Pin<&MyUnpinType>) -> &'a MyUnpinType { + r_unpin +} + +const fn coerce_unpin_rm<'a>(mut r_unpin: &'a mut Pin<&MyUnpinType>) -> &'a mut MyUnpinType { + r_unpin //~ ERROR: cannot borrow data in dereference of `Pin<&MyUnpinType>` as mutable +} + +const fn coerce_unpin_mr<'a>(mut r_unpin: &'a mut Pin<&mut MyUnpinType>) -> &'a MyUnpinType { + r_unpin +} + +const fn coerce_unpin_mm<'a>(mut r_unpin: &'a mut Pin<&mut MyUnpinType>) -> &'a mut MyUnpinType { + r_unpin +} + +const fn coerce_pin_rr<'a>(mut r_pin: &'a mut Pin<&MyPinType>) -> &'a MyPinType { + r_pin +} + +const fn coerce_pin_rm<'a>(mut r_pin: &'a mut Pin<&MyPinType>) -> &'a mut MyPinType { + r_pin //~ ERROR: cannot borrow data in dereference of `Pin<&MyPinType>` as mutable +} + +const fn coerce_pin_mr<'a>(mut r_pin: &'a mut Pin<&mut MyPinType>) -> &'a MyPinType { + r_pin +} + +const fn coerce_pin_mm<'a>(mut r_pin: &'a mut Pin<&mut MyPinType>) -> &'a mut MyPinType { + r_pin //~ ERROR: cannot borrow data in dereference of `Pin<&mut MyPinType>` as mutable +} + +fn main() {} diff --git a/tests/ui/deref/pin-deref-const.stderr b/tests/ui/deref/pin-deref-const.stderr new file mode 100644 index 000000000000..caa270e64ffe --- /dev/null +++ b/tests/ui/deref/pin-deref-const.stderr @@ -0,0 +1,51 @@ +error[E0596]: cannot borrow data in dereference of `Pin<&MyUnpinType>` as mutable + --> $DIR/pin-deref-const.rs:34:5 + | +LL | r_unpin.at_mut_self(); + | ^^^^^^^ cannot borrow as mutable + | + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Pin<&MyUnpinType>` + +error[E0596]: cannot borrow data in dereference of `Pin<&mut MyPinType>` as mutable + --> $DIR/pin-deref-const.rs:39:5 + | +LL | r_pin.at_mut_self(); + | ^^^^^ cannot borrow as mutable + | + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Pin<&mut MyPinType>` + +error[E0596]: cannot borrow data in dereference of `Pin<&MyPinType>` as mutable + --> $DIR/pin-deref-const.rs:44:5 + | +LL | r_pin.at_mut_self(); + | ^^^^^ cannot borrow as mutable + | + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Pin<&MyPinType>` + +error[E0596]: cannot borrow data in dereference of `Pin<&MyUnpinType>` as mutable + --> $DIR/pin-deref-const.rs:52:5 + | +LL | r_unpin + | ^^^^^^^ cannot borrow as mutable + | + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Pin<&MyUnpinType>` + +error[E0596]: cannot borrow data in dereference of `Pin<&MyPinType>` as mutable + --> $DIR/pin-deref-const.rs:68:5 + | +LL | r_pin + | ^^^^^ cannot borrow as mutable + | + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Pin<&MyPinType>` + +error[E0596]: cannot borrow data in dereference of `Pin<&mut MyPinType>` as mutable + --> $DIR/pin-deref-const.rs:76:5 + | +LL | r_pin + | ^^^^^ cannot borrow as mutable + | + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Pin<&mut MyPinType>` + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0596`. diff --git a/tests/ui/deref/pin-deref.rs b/tests/ui/deref/pin-deref.rs new file mode 100644 index 000000000000..180831c9bc61 --- /dev/null +++ b/tests/ui/deref/pin-deref.rs @@ -0,0 +1,76 @@ +// The purpose of this file is to track the error messages from Pin and DerefMut interacting. +// +// Identical to `pin-deref-const.rs` except for being stable and not using `const fn`. + +//@ check-fail + +use std::ops::DerefMut; +use std::pin::Pin; + +struct MyUnpinType {} + +impl MyUnpinType { + fn at_self(&self) {} + fn at_mut_self(&mut self) {} +} + +struct MyPinType(core::marker::PhantomPinned); + +impl MyPinType { + fn at_self(&self) {} + fn at_mut_self(&mut self) {} +} + +fn call_mut_ref_unpin(mut r_unpin: Pin<&mut MyUnpinType>) { + r_unpin.at_self(); + r_unpin.at_mut_self(); +} + +fn call_ref_unpin(mut r_unpin: Pin<&MyUnpinType>) { + r_unpin.at_self(); + r_unpin.at_mut_self(); //~ ERROR: cannot borrow data in dereference of `Pin<&MyUnpinType>` as mutable +} + +fn call_mut_ref_pin(mut r_pin: Pin<&mut MyPinType>) { + r_pin.at_self(); + r_pin.at_mut_self(); //~ ERROR: cannot borrow data in dereference of `Pin<&mut MyPinType>` as mutable +} + +fn call_ref_pin(mut r_pin: Pin<&MyPinType>) { + r_pin.at_self(); + r_pin.at_mut_self(); //~ ERROR: cannot borrow data in dereference of `Pin<&MyPinType>` as mutable +} + +fn coerce_unpin_rr<'a>(mut r_unpin: &'a mut Pin<&MyUnpinType>) -> &'a MyUnpinType { + r_unpin +} + +fn coerce_unpin_rm<'a>(mut r_unpin: &'a mut Pin<&MyUnpinType>) -> &'a mut MyUnpinType { + r_unpin //~ ERROR: cannot borrow data in dereference of `Pin<&MyUnpinType>` as mutable +} + +fn coerce_unpin_mr<'a>(mut r_unpin: &'a mut Pin<&mut MyUnpinType>) -> &'a MyUnpinType { + r_unpin +} + +fn coerce_unpin_mm<'a>(mut r_unpin: &'a mut Pin<&mut MyUnpinType>) -> &'a mut MyUnpinType { + r_unpin +} + +fn coerce_pin_rr<'a>(mut r_pin: &'a mut Pin<&MyPinType>) -> &'a MyPinType { + r_pin +} + +fn coerce_pin_rm<'a>(mut r_pin: &'a mut Pin<&MyPinType>) -> &'a mut MyPinType { + r_pin //~ ERROR: cannot borrow data in dereference of `Pin<&MyPinType>` as mutable +} + +fn coerce_pin_mr<'a>(mut r_pin: &'a mut Pin<&mut MyPinType>) -> &'a MyPinType { + r_pin +} + +fn coerce_pin_mm<'a>(mut r_pin: &'a mut Pin<&mut MyPinType>) -> &'a mut MyPinType { + r_pin //~ ERROR: cannot borrow data in dereference of `Pin<&mut MyPinType>` as mutable +} + +fn main() {} diff --git a/tests/ui/deref/pin-deref.stderr b/tests/ui/deref/pin-deref.stderr new file mode 100644 index 000000000000..71277a10cdfb --- /dev/null +++ b/tests/ui/deref/pin-deref.stderr @@ -0,0 +1,51 @@ +error[E0596]: cannot borrow data in dereference of `Pin<&MyUnpinType>` as mutable + --> $DIR/pin-deref.rs:31:5 + | +LL | r_unpin.at_mut_self(); + | ^^^^^^^ cannot borrow as mutable + | + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Pin<&MyUnpinType>` + +error[E0596]: cannot borrow data in dereference of `Pin<&mut MyPinType>` as mutable + --> $DIR/pin-deref.rs:36:5 + | +LL | r_pin.at_mut_self(); + | ^^^^^ cannot borrow as mutable + | + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Pin<&mut MyPinType>` + +error[E0596]: cannot borrow data in dereference of `Pin<&MyPinType>` as mutable + --> $DIR/pin-deref.rs:41:5 + | +LL | r_pin.at_mut_self(); + | ^^^^^ cannot borrow as mutable + | + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Pin<&MyPinType>` + +error[E0596]: cannot borrow data in dereference of `Pin<&MyUnpinType>` as mutable + --> $DIR/pin-deref.rs:49:5 + | +LL | r_unpin + | ^^^^^^^ cannot borrow as mutable + | + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Pin<&MyUnpinType>` + +error[E0596]: cannot borrow data in dereference of `Pin<&MyPinType>` as mutable + --> $DIR/pin-deref.rs:65:5 + | +LL | r_pin + | ^^^^^ cannot borrow as mutable + | + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Pin<&MyPinType>` + +error[E0596]: cannot borrow data in dereference of `Pin<&mut MyPinType>` as mutable + --> $DIR/pin-deref.rs:73:5 + | +LL | r_pin + | ^^^^^ cannot borrow as mutable + | + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Pin<&mut MyPinType>` + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0596`. diff --git a/tests/ui/deref/pin-impl-deref.rs b/tests/ui/deref/pin-impl-deref.rs new file mode 100644 index 000000000000..b1dc8dea3f24 --- /dev/null +++ b/tests/ui/deref/pin-impl-deref.rs @@ -0,0 +1,40 @@ +// The purpose of this file is to track the error messages from Pin and DerefMut interacting. + +//@ check-fail + +use std::ops::DerefMut; +use std::pin::Pin; + +struct MyUnpinType {} + +impl MyUnpinType { + fn at_self(&self) {} + fn at_mut_self(&mut self) {} +} + +struct MyPinType(core::marker::PhantomPinned); + +impl MyPinType { + fn at_self(&self) {} + fn at_mut_self(&mut self) {} +} + +fn impl_deref_mut(_: impl DerefMut) {} +fn unpin_impl_ref(r_unpin: Pin<&MyUnpinType>) { + impl_deref_mut(r_unpin) + //~^ ERROR: the trait bound `Pin<&MyUnpinType>: DerefMut` is not satisfied +} +fn unpin_impl_mut(r_unpin: Pin<&mut MyUnpinType>) { + impl_deref_mut(r_unpin) +} +fn pin_impl_ref(r_pin: Pin<&MyPinType>) { + impl_deref_mut(r_pin) + //~^ ERROR: `PhantomPinned` cannot be unpinned + //~| ERROR: the trait bound `Pin<&MyPinType>: DerefMut` is not satisfied +} +fn pin_impl_mut(r_pin: Pin<&mut MyPinType>) { + impl_deref_mut(r_pin) + //~^ ERROR: `PhantomPinned` cannot be unpinned +} + +fn main() {} diff --git a/tests/ui/deref/pin-impl-deref.stderr b/tests/ui/deref/pin-impl-deref.stderr new file mode 100644 index 000000000000..106654641a11 --- /dev/null +++ b/tests/ui/deref/pin-impl-deref.stderr @@ -0,0 +1,85 @@ +error[E0277]: the trait bound `Pin<&MyUnpinType>: DerefMut` is not satisfied + --> $DIR/pin-impl-deref.rs:24:20 + | +LL | impl_deref_mut(r_unpin) + | -------------- ^^^^^^^ the trait `DerefMut` is not implemented for `Pin<&MyUnpinType>` + | | + | required by a bound introduced by this call + | + = note: required for `Pin<&MyUnpinType>` to implement `DerefMut` +note: required by a bound in `impl_deref_mut` + --> $DIR/pin-impl-deref.rs:22:27 + | +LL | fn impl_deref_mut(_: impl DerefMut) {} + | ^^^^^^^^ required by this bound in `impl_deref_mut` +help: consider mutably borrowing here + | +LL | impl_deref_mut(&mut r_unpin) + | ++++ + +error[E0277]: the trait bound `Pin<&MyPinType>: DerefMut` is not satisfied + --> $DIR/pin-impl-deref.rs:31:20 + | +LL | impl_deref_mut(r_pin) + | -------------- ^^^^^ the trait `DerefMut` is not implemented for `Pin<&MyPinType>` + | | + | required by a bound introduced by this call + | + = note: required for `Pin<&MyPinType>` to implement `DerefMut` +note: required by a bound in `impl_deref_mut` + --> $DIR/pin-impl-deref.rs:22:27 + | +LL | fn impl_deref_mut(_: impl DerefMut) {} + | ^^^^^^^^ required by this bound in `impl_deref_mut` +help: consider mutably borrowing here + | +LL | impl_deref_mut(&mut r_pin) + | ++++ + +error[E0277]: `PhantomPinned` cannot be unpinned + --> $DIR/pin-impl-deref.rs:31:20 + | +LL | impl_deref_mut(r_pin) + | -------------- ^^^^^ within `MyPinType`, the trait `Unpin` is not implemented for `PhantomPinned` + | | + | required by a bound introduced by this call + | + = note: consider using the `pin!` macro + consider using `Box::pin` if you need to access the pinned value outside of the current scope +note: required because it appears within the type `MyPinType` + --> $DIR/pin-impl-deref.rs:15:8 + | +LL | struct MyPinType(core::marker::PhantomPinned); + | ^^^^^^^^^ + = note: required for `Pin<&MyPinType>` to implement `DerefMut` +note: required by a bound in `impl_deref_mut` + --> $DIR/pin-impl-deref.rs:22:27 + | +LL | fn impl_deref_mut(_: impl DerefMut) {} + | ^^^^^^^^ required by this bound in `impl_deref_mut` + +error[E0277]: `PhantomPinned` cannot be unpinned + --> $DIR/pin-impl-deref.rs:36:20 + | +LL | impl_deref_mut(r_pin) + | -------------- ^^^^^ within `MyPinType`, the trait `Unpin` is not implemented for `PhantomPinned` + | | + | required by a bound introduced by this call + | + = note: consider using the `pin!` macro + consider using `Box::pin` if you need to access the pinned value outside of the current scope +note: required because it appears within the type `MyPinType` + --> $DIR/pin-impl-deref.rs:15:8 + | +LL | struct MyPinType(core::marker::PhantomPinned); + | ^^^^^^^^^ + = note: required for `Pin<&mut MyPinType>` to implement `DerefMut` +note: required by a bound in `impl_deref_mut` + --> $DIR/pin-impl-deref.rs:22:27 + | +LL | fn impl_deref_mut(_: impl DerefMut) {} + | ^^^^^^^^ required by this bound in `impl_deref_mut` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. From 94fbb2178ae5960894aaae199233b0d3ac020d79 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 27 Jul 2025 21:21:07 +0200 Subject: [PATCH 245/682] implement `va_arg` for arm in rustc itself --- compiler/rustc_codegen_llvm/src/va_arg.rs | 15 +++++++++++++ tests/assembly-llvm/c-variadic-arm.rs | 26 +++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 tests/assembly-llvm/c-variadic-arm.rs diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index 7eb5d3020588..ab08125217ff 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -908,6 +908,21 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( ) } "aarch64" => emit_aapcs_va_arg(bx, addr, target_ty), + "arm" => { + // Types wider than 16 bytes are not currently supported. Clang has special logic for + // such types, but `VaArgSafe` is not implemented for any type that is this large. + assert!(bx.cx.size_of(target_ty).bytes() <= 16); + + emit_ptr_va_arg( + bx, + addr, + target_ty, + PassMode::Direct, + SlotSize::Bytes4, + AllowHigherAlign::Yes, + ForceRightAdjust::No, + ) + } "s390x" => emit_s390x_va_arg(bx, addr, target_ty), "powerpc" => emit_powerpc_va_arg(bx, addr, target_ty), "powerpc64" | "powerpc64le" => emit_ptr_va_arg( diff --git a/tests/assembly-llvm/c-variadic-arm.rs b/tests/assembly-llvm/c-variadic-arm.rs new file mode 100644 index 000000000000..2ef307405e13 --- /dev/null +++ b/tests/assembly-llvm/c-variadic-arm.rs @@ -0,0 +1,26 @@ +//@ assembly-output: emit-asm +//@ compile-flags: -Copt-level=3 +//@ only-arm +//@ ignore-thumb +//@ ignore-android +#![no_std] +#![crate_type = "lib"] +#![feature(c_variadic)] + +// Check that the assembly that rustc generates matches what clang emits. + +#[unsafe(no_mangle)] +unsafe extern "C" fn variadic(a: f64, mut args: ...) -> f64 { + // CHECK-LABEL: variadic + // CHECK: sub sp, sp + + // CHECK: vldr + // CHECK: vadd.f64 + // CHECK: vldr + // CHECK: vadd.f64 + let b = args.arg::(); + let c = args.arg::(); + a + b + c + + // CHECK: add sp, sp +} From 78718d55700865328d1abf584956125bec7f5d9d Mon Sep 17 00:00:00 2001 From: Daria Sukhonina Date: Mon, 8 Sep 2025 14:32:26 +0300 Subject: [PATCH 246/682] Skip typeck for items w/o their own typeck context More details on about this fix: https://rust-lang.zulipchat.com/#narrow/channel/187679-t-compiler.2Fparallel-rustc/topic/ICE.3A.20typecks.20AnonConst.20under.20parallel.20rustc.28.23141951.29/with/538200980 Co-authored-by: ywxt --- compiler/rustc_hir/src/def.rs | 7 +++++++ compiler/rustc_hir_analysis/src/lib.rs | 3 ++- compiler/rustc_middle/src/ty/util.rs | 5 +---- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 8af4740f376b..d563fd0a5472 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -440,6 +440,13 @@ impl DefKind { | DefKind::ExternCrate => false, } } + + /// Returns `true` if `self` is a kind of definition that does not have its own + /// type-checking context, i.e. closure, coroutine or inline const. + #[inline] + pub fn is_typeck_child(self) -> bool { + matches!(self, DefKind::Closure | DefKind::InlineConst | DefKind::SyntheticCoroutineBody) + } } /// The resolution of a path or export. diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 2562ab7542a6..6659aff71110 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -238,7 +238,8 @@ pub fn check_crate(tcx: TyCtxt<'_>) { _ => (), } // Skip `AnonConst`s because we feed their `type_of`. - if !matches!(def_kind, DefKind::AnonConst) { + // Also skip items for which typeck forwards to parent typeck. + if !(matches!(def_kind, DefKind::AnonConst) || def_kind.is_typeck_child()) { tcx.ensure_ok().typeck(item_def_id); } // Ensure we generate the new `DefId` before finishing `check_crate`. diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index a7d07adf78f0..781a13340776 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -609,10 +609,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Returns `true` if `def_id` refers to a definition that does not have its own /// type-checking context, i.e. closure, coroutine or inline const. pub fn is_typeck_child(self, def_id: DefId) -> bool { - matches!( - self.def_kind(def_id), - DefKind::Closure | DefKind::InlineConst | DefKind::SyntheticCoroutineBody - ) + self.def_kind(def_id).is_typeck_child() } /// Returns `true` if `def_id` refers to a trait (i.e., `trait Foo { ... }`). From f099b241e20d0d9b2fc02fd66a487529bd597d72 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 26 Aug 2025 14:11:04 +0200 Subject: [PATCH 247/682] remove outdated opaque type test --- tests/ui/type-alias-impl-trait/fallback.rs | 29 ------------------- .../ui/type-alias-impl-trait/fallback.stderr | 20 ------------- 2 files changed, 49 deletions(-) delete mode 100644 tests/ui/type-alias-impl-trait/fallback.rs delete mode 100644 tests/ui/type-alias-impl-trait/fallback.stderr diff --git a/tests/ui/type-alias-impl-trait/fallback.rs b/tests/ui/type-alias-impl-trait/fallback.rs deleted file mode 100644 index a2f25acca0d3..000000000000 --- a/tests/ui/type-alias-impl-trait/fallback.rs +++ /dev/null @@ -1,29 +0,0 @@ -// Tests that we correctly handle opaque types being used opaquely, -// even within their defining scope. -// -#![feature(type_alias_impl_trait)] - -type Foo = impl Copy; - -enum Wrapper { - First(T), - Second, -} - -// This method constrains `Foo` to be `bool` -#[define_opaque(Foo)] -fn constrained_foo() -> Foo { - true -} - -// This method does not constrain `Foo`. -// Per RFC 2071, function bodies may either -// fully constrain an opaque type, or place no -// constraints on it. -#[define_opaque(Foo)] -fn unconstrained_foo() -> Wrapper { - Wrapper::Second - //~^ ERROR: type annotations needed -} - -fn main() {} diff --git a/tests/ui/type-alias-impl-trait/fallback.stderr b/tests/ui/type-alias-impl-trait/fallback.stderr deleted file mode 100644 index 1eb0afb13a8d..000000000000 --- a/tests/ui/type-alias-impl-trait/fallback.stderr +++ /dev/null @@ -1,20 +0,0 @@ -error[E0283]: type annotations needed - --> $DIR/fallback.rs:25:5 - | -LL | fn unconstrained_foo() -> Wrapper { - | ------------ type must be known at this point -LL | Wrapper::Second - | ^^^^^^^^^^^^^^^ - | | - | cannot infer type of the type parameter `T` declared on the enum `Wrapper` - | return type was inferred to be `Wrapper<_>` here - | - = note: cannot satisfy `_: Copy` -help: consider specifying the generic argument - | -LL | Wrapper::::Second - | +++++ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0283`. From 67965f817d2eb47f5238b43f21b35259806b6280 Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 27 Aug 2025 10:03:47 +0200 Subject: [PATCH 248/682] eagerly compute `sub_relations` again --- compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs | 7 -- compiler/rustc_infer/src/infer/context.rs | 4 + compiler/rustc_infer/src/infer/mod.rs | 9 ++ .../src/infer/relate/generalize.rs | 4 + .../src/infer/snapshot/undo_log.rs | 4 +- .../rustc_infer/src/infer/type_variable.rs | 111 ++++++++++++++++++ .../src/solve/eval_ctxt/mod.rs | 4 + .../rustc_next_trait_solver/src/solve/mod.rs | 14 ++- .../src/error_reporting/infer/mod.rs | 1 - .../error_reporting/infer/need_type_info.rs | 2 +- .../error_reporting/infer/sub_relations.rs | 81 ------------- .../src/error_reporting/mod.rs | 4 - .../src/error_reporting/traits/mod.rs | 4 - .../src/solve/delegate.rs | 13 +- compiler/rustc_type_ir/src/infer_ctxt.rs | 1 + 15 files changed, 152 insertions(+), 111 deletions(-) delete mode 100644 compiler/rustc_trait_selection/src/error_reporting/infer/sub_relations.rs diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 74f27e85cba2..7a060cafeab1 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -21,7 +21,6 @@ use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt}; use rustc_session::Session; use rustc_span::{self, DUMMY_SP, ErrorGuaranteed, Ident, Span, sym}; use rustc_trait_selection::error_reporting::TypeErrCtxt; -use rustc_trait_selection::error_reporting::infer::sub_relations::SubRelations; use rustc_trait_selection::traits::{ self, FulfillmentError, ObligationCause, ObligationCauseCode, ObligationCtxt, }; @@ -188,14 +187,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// [`InferCtxtErrorExt::err_ctxt`]: rustc_trait_selection::error_reporting::InferCtxtErrorExt::err_ctxt pub(crate) fn err_ctxt(&'a self) -> TypeErrCtxt<'a, 'tcx> { - let mut sub_relations = SubRelations::default(); - sub_relations.add_constraints( - self, - self.fulfillment_cx.borrow_mut().pending_obligations().iter().map(|o| o.predicate), - ); TypeErrCtxt { infcx: &self.infcx, - sub_relations: RefCell::new(sub_relations), typeck_results: Some(self.typeck_results.borrow()), fallback_has_occurred: self.fallback_has_occurred.get(), normalize_fn_sig: Box::new(|fn_sig| { diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index 8265fccabc96..efd623fac788 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -179,6 +179,10 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { self.inner.borrow_mut().type_variables().equate(a, b); } + fn sub_ty_vids_raw(&self, a: ty::TyVid, b: ty::TyVid) { + self.sub_ty_vids_raw(a, b); + } + fn equate_int_vids_raw(&self, a: ty::IntVid, b: ty::IntVid) { self.inner.borrow_mut().int_unification_table().union(a, b); } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index d105d24bed77..48dd699cd50c 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -764,6 +764,7 @@ impl<'tcx> InferCtxt<'tcx> { let r_b = self.shallow_resolve(predicate.skip_binder().b); match (r_a.kind(), r_b.kind()) { (&ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid))) => { + self.sub_ty_vids_raw(a_vid, b_vid); return Err((a_vid, b_vid)); } _ => {} @@ -1128,6 +1129,14 @@ impl<'tcx> InferCtxt<'tcx> { self.inner.borrow_mut().type_variables().root_var(var) } + pub fn sub_ty_vids_raw(&self, a: ty::TyVid, b: ty::TyVid) { + self.inner.borrow_mut().type_variables().sub(a, b); + } + + pub fn sub_root_var(&self, var: ty::TyVid) -> ty::TyVid { + self.inner.borrow_mut().type_variables().sub_root_var(var) + } + pub fn root_const_var(&self, var: ty::ConstVid) -> ty::ConstVid { self.inner.borrow_mut().const_unification_table().find(var).vid } diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index a000bb1123c1..2323add0b320 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -519,6 +519,10 @@ impl<'tcx> TypeRelation> for Generalizer<'_, 'tcx> { let origin = inner.type_variables().var_origin(vid); let new_var_id = inner.type_variables().new_var(self.for_universe, origin); + // Record that `vid` and `new_var_id` have to be subtypes + // of each other. This is currently only used for diagnostics. + // To see why, see the docs in the `type_variables` module. + inner.type_variables().sub(vid, new_var_id); // If we're in the new solver and create a new inference // variable inside of an alias we eagerly constrain that // inference variable to prevent unexpected ambiguity errors. diff --git a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs index fcc0ab3af410..22c815fb87c6 100644 --- a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs +++ b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs @@ -20,7 +20,7 @@ pub struct Snapshot<'tcx> { pub(crate) enum UndoLog<'tcx> { DuplicateOpaqueType, OpaqueTypes(OpaqueTypeKey<'tcx>, Option>), - TypeVariables(sv::UndoLog>>), + TypeVariables(type_variable::UndoLog<'tcx>), ConstUnificationTable(sv::UndoLog>>), IntUnificationTable(sv::UndoLog>), FloatUnificationTable(sv::UndoLog>), @@ -49,6 +49,8 @@ impl_from! { RegionConstraintCollector(region_constraints::UndoLog<'tcx>), TypeVariables(sv::UndoLog>>), + TypeVariables(sv::UndoLog>), + TypeVariables(type_variable::UndoLog<'tcx>), IntUnificationTable(sv::UndoLog>), FloatUnificationTable(sv::UndoLog>), diff --git a/compiler/rustc_infer/src/infer/type_variable.rs b/compiler/rustc_infer/src/infer/type_variable.rs index 6f6791804d32..613e5a5883a3 100644 --- a/compiler/rustc_infer/src/infer/type_variable.rs +++ b/compiler/rustc_infer/src/infer/type_variable.rs @@ -13,12 +13,48 @@ use tracing::debug; use crate::infer::InferCtxtUndoLogs; +/// Represents a single undo-able action that affects a type inference variable. +#[derive(Clone)] +pub(crate) enum UndoLog<'tcx> { + EqRelation(sv::UndoLog>>), + SubRelation(sv::UndoLog>), +} + +/// Convert from a specific kind of undo to the more general UndoLog +impl<'tcx> From>>> for UndoLog<'tcx> { + fn from(l: sv::UndoLog>>) -> Self { + UndoLog::EqRelation(l) + } +} + +/// Convert from a specific kind of undo to the more general UndoLog +impl<'tcx> From>> for UndoLog<'tcx> { + fn from(l: sv::UndoLog>) -> Self { + UndoLog::SubRelation(l) + } +} + impl<'tcx> Rollback>>> for TypeVariableStorage<'tcx> { fn reverse(&mut self, undo: sv::UndoLog>>) { self.eq_relations.reverse(undo) } } +impl<'tcx> Rollback>> for TypeVariableStorage<'tcx> { + fn reverse(&mut self, undo: sv::UndoLog>) { + self.sub_relations.reverse(undo) + } +} + +impl<'tcx> Rollback> for TypeVariableStorage<'tcx> { + fn reverse(&mut self, undo: UndoLog<'tcx>) { + match undo { + UndoLog::EqRelation(undo) => self.eq_relations.reverse(undo), + UndoLog::SubRelation(undo) => self.sub_relations.reverse(undo), + } + } +} + #[derive(Clone, Default)] pub(crate) struct TypeVariableStorage<'tcx> { /// The origins of each type variable. @@ -27,6 +63,23 @@ pub(crate) struct TypeVariableStorage<'tcx> { /// constraint `?X == ?Y`. This table also stores, for each key, /// the known value. eq_relations: ut::UnificationTableStorage>, + /// Only used by `-Znext-solver` and for diagnostics. + /// + /// When reporting ambiguity errors, we sometimes want to + /// treat all inference vars which are subtypes of each + /// others as if they are equal. For this case we compute + /// the transitive closure of our subtype obligations here. + /// + /// E.g. when encountering ambiguity errors, we want to suggest + /// specifying some method argument or to add a type annotation + /// to a local variable. Because subtyping cannot change the + /// shape of a type, it's fine if the cause of the ambiguity error + /// is only related to the suggested variable via subtyping. + /// + /// Even for something like `let x = returns_arg(); x.method();` the + /// type of `x` is only a supertype of the argument of `returns_arg`. We + /// still want to suggest specifying the type of the argument. + sub_relations: ut::UnificationTableStorage, } pub(crate) struct TypeVariableTable<'a, 'tcx> { @@ -109,6 +162,16 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { debug_assert!(self.probe(a).is_unknown()); debug_assert!(self.probe(b).is_unknown()); self.eq_relations().union(a, b); + self.sub_relations().union(a, b); + } + + /// Records that `a <: b`, depending on `dir`. + /// + /// Precondition: neither `a` nor `b` are known. + pub(crate) fn sub(&mut self, a: ty::TyVid, b: ty::TyVid) { + debug_assert!(self.probe(a).is_unknown()); + debug_assert!(self.probe(b).is_unknown()); + self.sub_relations().union(a, b); } /// Instantiates `vid` with the type `ty`. @@ -142,6 +205,10 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { origin: TypeVariableOrigin, ) -> ty::TyVid { let eq_key = self.eq_relations().new_key(TypeVariableValue::Unknown { universe }); + + let sub_key = self.sub_relations().new_key(()); + debug_assert_eq!(eq_key.vid, sub_key.vid); + let index = self.storage.values.push(TypeVariableData { origin }); debug_assert_eq!(eq_key.vid, index); @@ -164,6 +231,18 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { self.eq_relations().find(vid).vid } + /// Returns the "root" variable of `vid` in the `sub_relations` + /// equivalence table. All type variables that have been are + /// related via equality or subtyping will yield the same root + /// variable (per the union-find algorithm), so `sub_root_var(a) + /// == sub_root_var(b)` implies that: + /// ```text + /// exists X. (a <: X || X <: a) && (b <: X || X <: b) + /// ``` + pub(crate) fn sub_root_var(&mut self, vid: ty::TyVid) -> ty::TyVid { + self.sub_relations().find(vid).vid + } + /// Retrieves the type to which `vid` has been instantiated, if /// any. pub(crate) fn probe(&mut self, vid: ty::TyVid) -> TypeVariableValue<'tcx> { @@ -181,6 +260,11 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { self.storage.eq_relations.with_log(self.undo_log) } + #[inline] + fn sub_relations(&mut self) -> super::UnificationTable<'_, 'tcx, TyVidSubKey> { + self.storage.sub_relations.with_log(self.undo_log) + } + /// Returns a range of the type variables created during the snapshot. pub(crate) fn vars_since_snapshot( &mut self, @@ -243,6 +327,33 @@ impl<'tcx> ut::UnifyKey for TyVidEqKey<'tcx> { } } +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub(crate) struct TyVidSubKey { + vid: ty::TyVid, +} + +impl From for TyVidSubKey { + #[inline] // make this function eligible for inlining - it is quite hot. + fn from(vid: ty::TyVid) -> Self { + TyVidSubKey { vid } + } +} + +impl ut::UnifyKey for TyVidSubKey { + type Value = (); + #[inline] + fn index(&self) -> u32 { + self.vid.as_u32() + } + #[inline] + fn from_index(i: u32) -> TyVidSubKey { + TyVidSubKey { vid: ty::TyVid::from_u32(i) } + } + fn tag() -> &'static str { + "TyVidSubKey" + } +} + impl<'tcx> ut::UnifyValue for TypeVariableValue<'tcx> { type Error = ut::NoError; diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 443aebbdb4d6..f0f95e53265c 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -900,6 +900,10 @@ where && goal.param_env.visit_with(&mut visitor).is_continue() } + pub(super) fn sub_ty_vids_raw(&self, a: ty::TyVid, b: ty::TyVid) { + self.delegate.sub_ty_vids_raw(a, b) + } + #[instrument(level = "trace", skip(self, param_env), ret)] pub(super) fn eq>( &mut self, diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 85f9d852d959..a2db21796a20 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -119,11 +119,15 @@ where #[instrument(level = "trace", skip(self))] fn compute_subtype_goal(&mut self, goal: Goal>) -> QueryResult { - if goal.predicate.a.is_ty_var() && goal.predicate.b.is_ty_var() { - self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) - } else { - self.sub(goal.param_env, goal.predicate.a, goal.predicate.b)?; - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + match (goal.predicate.a.kind(), goal.predicate.b.kind()) { + (ty::Infer(ty::TyVar(a_vid)), ty::Infer(ty::TyVar(b_vid))) => { + self.sub_ty_vids_raw(a_vid, b_vid); + self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + } + _ => { + self.sub(goal.param_env, goal.predicate.a, goal.predicate.b)?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index 812e20e43382..d3a17232a40d 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -91,7 +91,6 @@ mod suggest; pub mod need_type_info; pub mod nice_region_error; pub mod region; -pub mod sub_relations; /// Makes a valid string literal from a string by escaping special characters (" and \), /// unless they are already escaped. diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs index ec2287ed5161..dff1311070ce 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs @@ -894,7 +894,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { use ty::{Infer, TyVar}; match (inner_ty.kind(), target_ty.kind()) { (&Infer(TyVar(a_vid)), &Infer(TyVar(b_vid))) => { - self.tecx.sub_relations.borrow_mut().unified(self.tecx, a_vid, b_vid) + self.tecx.sub_root_var(a_vid) == self.tecx.sub_root_var(b_vid) } _ => false, } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/sub_relations.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/sub_relations.rs deleted file mode 100644 index ef26a8ff7b86..000000000000 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/sub_relations.rs +++ /dev/null @@ -1,81 +0,0 @@ -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::undo_log::NoUndo; -use rustc_data_structures::unify as ut; -use rustc_middle::ty; - -use crate::infer::InferCtxt; - -#[derive(Debug, Copy, Clone, PartialEq)] -struct SubId(u32); -impl ut::UnifyKey for SubId { - type Value = (); - #[inline] - fn index(&self) -> u32 { - self.0 - } - #[inline] - fn from_index(i: u32) -> SubId { - SubId(i) - } - fn tag() -> &'static str { - "SubId" - } -} - -/// When reporting ambiguity errors, we sometimes want to -/// treat all inference vars which are subtypes of each -/// others as if they are equal. For this case we compute -/// the transitive closure of our subtype obligations here. -/// -/// E.g. when encountering ambiguity errors, we want to suggest -/// specifying some method argument or to add a type annotation -/// to a local variable. Because subtyping cannot change the -/// shape of a type, it's fine if the cause of the ambiguity error -/// is only related to the suggested variable via subtyping. -/// -/// Even for something like `let x = returns_arg(); x.method();` the -/// type of `x` is only a supertype of the argument of `returns_arg`. We -/// still want to suggest specifying the type of the argument. -#[derive(Default)] -pub struct SubRelations { - map: FxHashMap, - table: ut::UnificationTableStorage, -} - -impl SubRelations { - fn get_id<'tcx>(&mut self, infcx: &InferCtxt<'tcx>, vid: ty::TyVid) -> SubId { - let root_vid = infcx.root_var(vid); - *self.map.entry(root_vid).or_insert_with(|| self.table.with_log(&mut NoUndo).new_key(())) - } - - pub fn add_constraints<'tcx>( - &mut self, - infcx: &InferCtxt<'tcx>, - obls: impl IntoIterator>, - ) { - for p in obls { - let (a, b) = match p.kind().skip_binder() { - ty::PredicateKind::Subtype(ty::SubtypePredicate { a_is_expected: _, a, b }) => { - (a, b) - } - ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => (a, b), - _ => continue, - }; - - match (a.kind(), b.kind()) { - (&ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid))) => { - let a = self.get_id(infcx, a_vid); - let b = self.get_id(infcx, b_vid); - self.table.with_log(&mut NoUndo).unify_var_var(a, b).unwrap(); - } - _ => continue, - } - } - } - - pub fn unified<'tcx>(&mut self, infcx: &InferCtxt<'tcx>, a: ty::TyVid, b: ty::TyVid) -> bool { - let a = self.get_id(infcx, a); - let b = self.get_id(infcx, b); - self.table.with_log(&mut NoUndo).unioned(a, b) - } -} diff --git a/compiler/rustc_trait_selection/src/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/mod.rs index 82695688ae89..cce20b05c79a 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/mod.rs @@ -7,8 +7,6 @@ use rustc_macros::extension; use rustc_middle::bug; use rustc_middle::ty::{self, Ty}; -use crate::error_reporting::infer::sub_relations; - pub mod infer; pub mod traits; @@ -21,7 +19,6 @@ pub mod traits; /// methods which should not be used during the happy path. pub struct TypeErrCtxt<'a, 'tcx> { pub infcx: &'a InferCtxt<'tcx>, - pub sub_relations: std::cell::RefCell, pub typeck_results: Option>>, pub fallback_has_occurred: bool, @@ -38,7 +35,6 @@ impl<'tcx> InferCtxt<'tcx> { fn err_ctxt(&self) -> TypeErrCtxt<'_, 'tcx> { TypeErrCtxt { infcx: self, - sub_relations: Default::default(), typeck_results: None, fallback_has_occurred: false, normalize_fn_sig: Box::new(|fn_sig| fn_sig), diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs index c8500b2d9d4a..f794ff632c59 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -139,10 +139,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &self, mut errors: Vec>, ) -> ErrorGuaranteed { - self.sub_relations - .borrow_mut() - .add_constraints(self, errors.iter().map(|e| e.obligation.predicate)); - #[derive(Debug)] struct ErrorDescriptor<'tcx> { goal: Goal<'tcx, ty::Predicate<'tcx>>, diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index e6a2761db5a9..448fc025be66 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -126,13 +126,12 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< } ty::PredicateKind::Subtype(ty::SubtypePredicate { a, b, .. }) | ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => { - if self.shallow_resolve(a).is_ty_var() && self.shallow_resolve(b).is_ty_var() { - // FIXME: We also need to register a subtype relation between these vars - // when those are added, and if they aren't in the same sub root then - // we should mark this goal as `has_changed`. - Some(Certainty::AMBIGUOUS) - } else { - None + match (self.shallow_resolve(a).kind(), self.shallow_resolve(b).kind()) { + (&ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid))) => { + self.sub_ty_vids_raw(a_vid, b_vid); + Some(Certainty::AMBIGUOUS) + } + _ => None, } } ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, _)) => { diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index b4462294700a..3fac50ff9ccf 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -197,6 +197,7 @@ pub trait InferCtxtLike: Sized { ) -> U; fn equate_ty_vids_raw(&self, a: ty::TyVid, b: ty::TyVid); + fn sub_ty_vids_raw(&self, a: ty::TyVid, b: ty::TyVid); fn equate_int_vids_raw(&self, a: ty::IntVid, b: ty::IntVid); fn equate_float_vids_raw(&self, a: ty::FloatVid, b: ty::FloatVid); fn equate_const_vids_raw(&self, a: ty::ConstVid, b: ty::ConstVid); From 2cb04b960fb8ae7f29401cd982bdb829b6c54a03 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 28 Apr 2025 17:21:36 +0000 Subject: [PATCH 249/682] inline `CanonicalTyVarKind` --- .../src/infer/canonical/canonicalizer.rs | 17 ++--- .../rustc_infer/src/infer/canonical/mod.rs | 13 +--- compiler/rustc_middle/src/infer/canonical.rs | 1 - .../src/canonicalizer.rs | 21 +++--- compiler/rustc_type_ir/src/canonical.rs | 71 ++++++++----------- ..._of_reborrow.SimplifyCfg-initial.after.mir | 18 ++--- 6 files changed, 58 insertions(+), 83 deletions(-) diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index 3ad14dc79d51..40d3e8acaa44 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -17,7 +17,7 @@ use tracing::debug; use crate::infer::InferCtxt; use crate::infer::canonical::{ - Canonical, CanonicalQueryInput, CanonicalTyVarKind, CanonicalVarKind, OriginalQueryValues, + Canonical, CanonicalQueryInput, CanonicalVarKind, OriginalQueryValues, }; impl<'tcx> InferCtxt<'tcx> { @@ -361,10 +361,7 @@ impl<'cx, 'tcx> TypeFolder> for Canonicalizer<'cx, 'tcx> { // FIXME: perf problem described in #55921. ui = ty::UniverseIndex::ROOT; } - self.canonicalize_ty_var( - CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)), - t, - ) + self.canonicalize_ty_var(CanonicalVarKind::Ty(ui), t) } } } @@ -374,7 +371,7 @@ impl<'cx, 'tcx> TypeFolder> for Canonicalizer<'cx, 'tcx> { if nt != t { return self.fold_ty(nt); } else { - self.canonicalize_ty_var(CanonicalVarKind::Ty(CanonicalTyVarKind::Int), t) + self.canonicalize_ty_var(CanonicalVarKind::Int, t) } } ty::Infer(ty::FloatVar(vid)) => { @@ -382,7 +379,7 @@ impl<'cx, 'tcx> TypeFolder> for Canonicalizer<'cx, 'tcx> { if nt != t { return self.fold_ty(nt); } else { - self.canonicalize_ty_var(CanonicalVarKind::Ty(CanonicalTyVarKind::Float), t) + self.canonicalize_ty_var(CanonicalVarKind::Float, t) } } @@ -679,12 +676,10 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { self.variables .iter() .map(|&kind| match kind { - CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) => { + CanonicalVarKind::Int | CanonicalVarKind::Float => { return kind; } - CanonicalVarKind::Ty(CanonicalTyVarKind::General(u)) => { - CanonicalVarKind::Ty(CanonicalTyVarKind::General(reverse_universe_map[&u])) - } + CanonicalVarKind::Ty(u) => CanonicalVarKind::Ty(reverse_universe_map[&u]), CanonicalVarKind::Region(u) => CanonicalVarKind::Region(reverse_universe_map[&u]), CanonicalVarKind::Const(u) => CanonicalVarKind::Const(reverse_universe_map[&u]), CanonicalVarKind::PlaceholderTy(placeholder) => { diff --git a/compiler/rustc_infer/src/infer/canonical/mod.rs b/compiler/rustc_infer/src/infer/canonical/mod.rs index 79a2aa54ef83..223fb8419ecc 100644 --- a/compiler/rustc_infer/src/infer/canonical/mod.rs +++ b/compiler/rustc_infer/src/infer/canonical/mod.rs @@ -108,18 +108,11 @@ impl<'tcx> InferCtxt<'tcx> { universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex, ) -> GenericArg<'tcx> { match kind { - CanonicalVarKind::Ty(ty_kind) => { - let ty = match ty_kind { - CanonicalTyVarKind::General(ui) => { - self.next_ty_var_in_universe(span, universe_map(ui)) - } + CanonicalVarKind::Ty(ui) => self.next_ty_var_in_universe(span, universe_map(ui)).into(), - CanonicalTyVarKind::Int => self.next_int_var(), + CanonicalVarKind::Int => self.next_int_var().into(), - CanonicalTyVarKind::Float => self.next_float_var(), - }; - ty.into() - } + CanonicalVarKind::Float => self.next_float_var().into(), CanonicalVarKind::PlaceholderTy(ty::PlaceholderType { universe, bound }) => { let universe_mapped = universe_map(universe); diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index 4fe4c2dadee1..153605ee7f81 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -27,7 +27,6 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lock; use rustc_macros::{HashStable, TypeFoldable, TypeVisitable}; pub use rustc_type_ir as ir; -pub use rustc_type_ir::CanonicalTyVarKind; use smallvec::SmallVec; use crate::mir::ConstraintCategory; diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index da05c49756ff..7a8ee09650e7 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -2,9 +2,8 @@ use rustc_type_ir::data_structures::{HashMap, ensure_sufficient_stack}; use rustc_type_ir::inherent::*; use rustc_type_ir::solve::{Goal, QueryInput}; use rustc_type_ir::{ - self as ty, Canonical, CanonicalParamEnvCacheEntry, CanonicalTyVarKind, CanonicalVarKind, - Flags, InferCtxtLike, Interner, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable, - TypeVisitableExt, + self as ty, Canonical, CanonicalParamEnvCacheEntry, CanonicalVarKind, Flags, InferCtxtLike, + Interner, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, }; use crate::delegate::SolverDelegate; @@ -314,14 +313,12 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { ); match self.canonicalize_mode { - CanonicalizeMode::Input { .. } => CanonicalVarKind::Ty( - CanonicalTyVarKind::General(ty::UniverseIndex::ROOT), - ), + CanonicalizeMode::Input { .. } => { + CanonicalVarKind::Ty(ty::UniverseIndex::ROOT) + } CanonicalizeMode::Response { .. } => { - CanonicalVarKind::Ty(CanonicalTyVarKind::General( - self.delegate.universe_of_ty(vid).unwrap_or_else(|| { - panic!("ty var should have been resolved: {t:?}") - }), + CanonicalVarKind::Ty(self.delegate.universe_of_ty(vid).unwrap_or_else( + || panic!("ty var should have been resolved: {t:?}"), )) } } @@ -332,7 +329,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { t, "ty vid should have been resolved fully before canonicalization" ); - CanonicalVarKind::Ty(CanonicalTyVarKind::Int) + CanonicalVarKind::Int } ty::FloatVar(vid) => { debug_assert_eq!( @@ -340,7 +337,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { t, "ty vid should have been resolved fully before canonicalization" ); - CanonicalVarKind::Ty(CanonicalTyVarKind::Float) + CanonicalVarKind::Float } ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => { panic!("fresh vars not expected in canonicalization") diff --git a/compiler/rustc_type_ir/src/canonical.rs b/compiler/rustc_type_ir/src/canonical.rs index de2a9186e7c9..cd509ed3f72f 100644 --- a/compiler/rustc_type_ir/src/canonical.rs +++ b/compiler/rustc_type_ir/src/canonical.rs @@ -1,5 +1,4 @@ use std::fmt; -use std::hash::Hash; use std::ops::Index; use derive_where::derive_where; @@ -91,8 +90,14 @@ impl fmt::Display for Canonical { derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) )] pub enum CanonicalVarKind { - /// Some kind of type inference variable. - Ty(CanonicalTyVarKind), + /// General type variable `?T` that can be unified with arbitrary types. + Ty(UniverseIndex), + + /// Integral type variable `?I` (that can only be unified with integral types). + Int, + + /// Floating-point type variable `?F` (that can only be unified with float types). + Float, /// A "placeholder" that represents "any type". PlaceholderTy(I::PlaceholderTy), @@ -117,15 +122,13 @@ impl Eq for CanonicalVarKind {} impl CanonicalVarKind { pub fn universe(self) -> UniverseIndex { match self { - CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)) => ui, + CanonicalVarKind::Ty(ui) => ui, CanonicalVarKind::Region(ui) => ui, CanonicalVarKind::Const(ui) => ui, CanonicalVarKind::PlaceholderTy(placeholder) => placeholder.universe(), CanonicalVarKind::PlaceholderRegion(placeholder) => placeholder.universe(), CanonicalVarKind::PlaceholderConst(placeholder) => placeholder.universe(), - CanonicalVarKind::Ty(CanonicalTyVarKind::Float | CanonicalTyVarKind::Int) => { - UniverseIndex::ROOT - } + CanonicalVarKind::Float | CanonicalVarKind::Int => UniverseIndex::ROOT, } } @@ -135,9 +138,7 @@ impl CanonicalVarKind { /// the updated universe is not the root. pub fn with_updated_universe(self, ui: UniverseIndex) -> CanonicalVarKind { match self { - CanonicalVarKind::Ty(CanonicalTyVarKind::General(_)) => { - CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)) - } + CanonicalVarKind::Ty(_) => CanonicalVarKind::Ty(ui), CanonicalVarKind::Region(_) => CanonicalVarKind::Region(ui), CanonicalVarKind::Const(_) => CanonicalVarKind::Const(ui), @@ -150,7 +151,7 @@ impl CanonicalVarKind { CanonicalVarKind::PlaceholderConst(placeholder) => { CanonicalVarKind::PlaceholderConst(placeholder.with_updated_universe(ui)) } - CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) => { + CanonicalVarKind::Int | CanonicalVarKind::Float => { assert_eq!(ui, UniverseIndex::ROOT); self } @@ -159,12 +160,14 @@ impl CanonicalVarKind { pub fn is_existential(self) -> bool { match self { - CanonicalVarKind::Ty(_) => true, - CanonicalVarKind::PlaceholderTy(_) => false, - CanonicalVarKind::Region(_) => true, - CanonicalVarKind::PlaceholderRegion(..) => false, - CanonicalVarKind::Const(_) => true, - CanonicalVarKind::PlaceholderConst(_) => false, + CanonicalVarKind::Ty(_) + | CanonicalVarKind::Int + | CanonicalVarKind::Float + | CanonicalVarKind::Region(_) + | CanonicalVarKind::Const(_) => true, + CanonicalVarKind::PlaceholderTy(_) + | CanonicalVarKind::PlaceholderRegion(..) + | CanonicalVarKind::PlaceholderConst(_) => false, } } @@ -172,6 +175,8 @@ impl CanonicalVarKind { match self { CanonicalVarKind::Region(_) | CanonicalVarKind::PlaceholderRegion(_) => true, CanonicalVarKind::Ty(_) + | CanonicalVarKind::Int + | CanonicalVarKind::Float | CanonicalVarKind::PlaceholderTy(_) | CanonicalVarKind::Const(_) | CanonicalVarKind::PlaceholderConst(_) => false, @@ -180,7 +185,11 @@ impl CanonicalVarKind { pub fn expect_placeholder_index(self) -> usize { match self { - CanonicalVarKind::Ty(_) | CanonicalVarKind::Region(_) | CanonicalVarKind::Const(_) => { + CanonicalVarKind::Ty(_) + | CanonicalVarKind::Int + | CanonicalVarKind::Float + | CanonicalVarKind::Region(_) + | CanonicalVarKind::Const(_) => { panic!("expected placeholder: {self:?}") } @@ -191,27 +200,6 @@ impl CanonicalVarKind { } } -/// Rust actually has more than one category of type variables; -/// notably, the type variables we create for literals (e.g., 22 or -/// 22.) can only be instantiated with integral/float types (e.g., -/// usize or f32). In order to faithfully reproduce a type, we need to -/// know what set of types a given type variable can be unified with. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -#[cfg_attr( - feature = "nightly", - derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext) -)] -pub enum CanonicalTyVarKind { - /// General type variable `?T` that can be unified with arbitrary types. - General(UniverseIndex), - - /// Integral type variable `?I` (that can only be unified with integral types). - Int, - - /// Floating-point type variable `?F` (that can only be unified with float types). - Float, -} - /// A set of values corresponding to the canonical variables from some /// `Canonical`. You can give these values to /// `canonical_value.instantiate` to instantiate them into the canonical @@ -287,7 +275,10 @@ impl CanonicalVarValues { var_values: cx.mk_args_from_iter(infos.iter().enumerate().map( |(i, kind)| -> I::GenericArg { match kind { - CanonicalVarKind::Ty(_) | CanonicalVarKind::PlaceholderTy(_) => { + CanonicalVarKind::Ty(_) + | CanonicalVarKind::Int + | CanonicalVarKind::Float + | CanonicalVarKind::PlaceholderTy(_) => { Ty::new_anon_bound(cx, ty::INNERMOST, ty::BoundVar::from_usize(i)) .into() } diff --git a/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir b/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir index 8afb6ad250e0..906e5828aba8 100644 --- a/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir +++ b/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir @@ -1,30 +1,30 @@ // MIR for `address_of_reborrow` after SimplifyCfg-initial | User Type Annotations -| 0: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty(General(U0))] }, span: $DIR/address_of.rs:8:10: 8:18, inferred_ty: *const [i32; 10] +| 0: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty(U0)] }, span: $DIR/address_of.rs:8:10: 8:18, inferred_ty: *const [i32; 10] | 1: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:10:10: 10:25, inferred_ty: *const dyn std::marker::Send -| 2: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty(General(U0))] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10] -| 3: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty(General(U0))] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10] +| 2: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty(U0)] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10] +| 3: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty(U0)] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10] | 4: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:15:12: 15:28, inferred_ty: *const [i32; 10] | 5: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:15:12: 15:28, inferred_ty: *const [i32; 10] | 6: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:16:12: 16:27, inferred_ty: *const dyn std::marker::Send | 7: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:16:12: 16:27, inferred_ty: *const dyn std::marker::Send | 8: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:17:12: 17:24, inferred_ty: *const [i32] | 9: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:17:12: 17:24, inferred_ty: *const [i32] -| 10: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty(General(U0))] }, span: $DIR/address_of.rs:19:10: 19:18, inferred_ty: *const [i32; 10] +| 10: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty(U0)] }, span: $DIR/address_of.rs:19:10: 19:18, inferred_ty: *const [i32; 10] | 11: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:21:10: 21:25, inferred_ty: *const dyn std::marker::Send -| 12: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty(General(U0))] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10] -| 13: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty(General(U0))] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10] +| 12: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty(U0)] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10] +| 13: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty(U0)] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10] | 14: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:25:12: 25:28, inferred_ty: *const [i32; 10] | 15: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:25:12: 25:28, inferred_ty: *const [i32; 10] | 16: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:26:12: 26:27, inferred_ty: *const dyn std::marker::Send | 17: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:26:12: 26:27, inferred_ty: *const dyn std::marker::Send | 18: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:27:12: 27:24, inferred_ty: *const [i32] | 19: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:27:12: 27:24, inferred_ty: *const [i32] -| 20: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [Ty(General(U0))] }, span: $DIR/address_of.rs:29:10: 29:16, inferred_ty: *mut [i32; 10] +| 20: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [Ty(U0)] }, span: $DIR/address_of.rs:29:10: 29:16, inferred_ty: *mut [i32; 10] | 21: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:31:10: 31:23, inferred_ty: *mut dyn std::marker::Send -| 22: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [Ty(General(U0))] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10] -| 23: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [Ty(General(U0))] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10] +| 22: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [Ty(U0)] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10] +| 23: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [Ty(U0)] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10] | 24: user_ty: Canonical { value: Ty(*mut [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:35:12: 35:26, inferred_ty: *mut [i32; 10] | 25: user_ty: Canonical { value: Ty(*mut [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:35:12: 35:26, inferred_ty: *mut [i32; 10] | 26: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:36:12: 36:25, inferred_ty: *mut dyn std::marker::Send From 28a0e77d1318210540fa1a561b9a8af08e2ffe40 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 26 Aug 2025 15:23:57 +0200 Subject: [PATCH 250/682] pass `sub_relations` into canonical queries --- .../src/infer/canonical/canonicalizer.rs | 17 +- .../rustc_infer/src/infer/canonical/mod.rs | 25 +- .../src/infer/canonical/query_response.rs | 73 ++-- compiler/rustc_infer/src/infer/context.rs | 4 + .../src/canonicalizer.rs | 33 +- .../rustc_next_trait_solver/src/delegate.rs | 4 +- .../src/solve/eval_ctxt/canonical.rs | 71 ++-- .../rustc_next_trait_solver/src/solve/mod.rs | 5 + .../src/solve/delegate.rs | 5 +- compiler/rustc_type_ir/src/canonical.rs | 18 +- compiler/rustc_type_ir/src/infer_ctxt.rs | 1 + ..._of_reborrow.SimplifyCfg-initial.after.mir | 18 +- .../multiline-removal-suggestion.svg | 364 ++++++++---------- .../recursive-in-exhaustiveness.next.stderr | 22 +- 14 files changed, 342 insertions(+), 318 deletions(-) diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index 40d3e8acaa44..be68609502a8 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -6,6 +6,7 @@ //! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::sso::SsoHashMap; use rustc_index::Idx; use rustc_middle::bug; use rustc_middle::ty::{ @@ -293,6 +294,7 @@ struct Canonicalizer<'cx, 'tcx> { // Note that indices is only used once `var_values` is big enough to be // heap-allocated. indices: FxHashMap, BoundVar>, + sub_root_lookup_table: SsoHashMap, canonicalize_mode: &'cx dyn CanonicalizeMode, needs_canonical_flags: TypeFlags, @@ -361,7 +363,8 @@ impl<'cx, 'tcx> TypeFolder> for Canonicalizer<'cx, 'tcx> { // FIXME: perf problem described in #55921. ui = ty::UniverseIndex::ROOT; } - self.canonicalize_ty_var(CanonicalVarKind::Ty(ui), t) + let sub_root = self.get_or_insert_sub_root(vid); + self.canonicalize_ty_var(CanonicalVarKind::Ty { ui, sub_root }, t) } } } @@ -559,6 +562,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { variables: SmallVec::from_slice(base.variables), query_state, indices: FxHashMap::default(), + sub_root_lookup_table: Default::default(), binder_index: ty::INNERMOST, }; if canonicalizer.query_state.var_values.spilled() { @@ -657,6 +661,13 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { } } + fn get_or_insert_sub_root(&mut self, vid: ty::TyVid) -> ty::BoundVar { + let root_vid = self.infcx.unwrap().sub_root_var(vid); + let idx = + *self.sub_root_lookup_table.entry(root_vid).or_insert_with(|| self.variables.len()); + ty::BoundVar::from(idx) + } + /// Replaces the universe indexes used in `var_values` with their index in /// `query_state.universe_map`. This minimizes the maximum universe used in /// the canonicalized value. @@ -679,7 +690,9 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { CanonicalVarKind::Int | CanonicalVarKind::Float => { return kind; } - CanonicalVarKind::Ty(u) => CanonicalVarKind::Ty(reverse_universe_map[&u]), + CanonicalVarKind::Ty { ui, sub_root } => { + CanonicalVarKind::Ty { ui: reverse_universe_map[&ui], sub_root } + } CanonicalVarKind::Region(u) => CanonicalVarKind::Region(reverse_universe_map[&u]), CanonicalVarKind::Const(u) => CanonicalVarKind::Const(reverse_universe_map[&u]), CanonicalVarKind::PlaceholderTy(placeholder) => { diff --git a/compiler/rustc_infer/src/infer/canonical/mod.rs b/compiler/rustc_infer/src/infer/canonical/mod.rs index 223fb8419ecc..4e1ea52af4c9 100644 --- a/compiler/rustc_infer/src/infer/canonical/mod.rs +++ b/compiler/rustc_infer/src/infer/canonical/mod.rs @@ -84,13 +84,12 @@ impl<'tcx> InferCtxt<'tcx> { variables: &List>, universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex, ) -> CanonicalVarValues<'tcx> { - CanonicalVarValues { - var_values: self.tcx.mk_args_from_iter( - variables - .iter() - .map(|kind| self.instantiate_canonical_var(span, kind, &universe_map)), - ), + let mut var_values = Vec::with_capacity(variables.len()); + for info in variables.iter() { + let value = self.instantiate_canonical_var(span, info, &var_values, &universe_map); + var_values.push(value); } + CanonicalVarValues { var_values: self.tcx.mk_args(&var_values) } } /// Given the "info" about a canonical variable, creates a fresh @@ -105,10 +104,22 @@ impl<'tcx> InferCtxt<'tcx> { &self, span: Span, kind: CanonicalVarKind<'tcx>, + previous_var_values: &[GenericArg<'tcx>], universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex, ) -> GenericArg<'tcx> { match kind { - CanonicalVarKind::Ty(ui) => self.next_ty_var_in_universe(span, universe_map(ui)).into(), + CanonicalVarKind::Ty { ui, sub_root } => { + let vid = self.next_ty_vid_in_universe(span, universe_map(ui)); + // Fetch the `sub_root` in case it exists. + if let Some(prev) = previous_var_values.get(sub_root.as_usize()) { + if let &ty::Infer(ty::TyVar(sub_root)) = prev.expect_ty().kind() { + self.inner.borrow_mut().type_variables().sub(vid, sub_root); + } else { + unreachable!() + } + } + Ty::new_var(self.tcx, vid).into() + } CanonicalVarKind::Int => self.next_int_var().into(), diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index 09578598114b..22c9c4edccc4 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -13,6 +13,7 @@ use std::iter; use rustc_index::{Idx, IndexVec}; use rustc_middle::arena::ArenaAllocatable; use rustc_middle::bug; +use rustc_middle::infer::canonical::CanonicalVarKind; use rustc_middle::ty::{self, BoundVar, GenericArg, GenericArgKind, Ty, TyCtxt, TypeFoldable}; use tracing::{debug, instrument}; @@ -413,26 +414,27 @@ impl<'tcx> InferCtxt<'tcx> { let mut opt_values: IndexVec>> = IndexVec::from_elem_n(None, query_response.variables.len()); - // In terms of our example above, we are iterating over pairs like: - // [(?A, Vec), ('static, '?1), (?B, ?0)] for (original_value, result_value) in iter::zip(&original_values.var_values, result_values) { match result_value.kind() { GenericArgKind::Type(result_value) => { - // e.g., here `result_value` might be `?0` in the example above... - if let ty::Bound(debruijn, b) = *result_value.kind() { - // ...in which case we would set `canonical_vars[0]` to `Some(?U)`. - + // We disable the instantiation guess for inference variables + // and only use it for placeholders. We need to handle the + // `sub_root` of type inference variables which would make this + // more involved. They are also a lot rarer than region variables. + if let ty::Bound(debruijn, b) = *result_value.kind() + && !matches!( + query_response.variables[b.var.as_usize()], + CanonicalVarKind::Ty { .. } + ) + { // We only allow a `ty::INNERMOST` index in generic parameters. assert_eq!(debruijn, ty::INNERMOST); opt_values[b.var] = Some(*original_value); } } GenericArgKind::Lifetime(result_value) => { - // e.g., here `result_value` might be `'?1` in the example above... if let ty::ReBound(debruijn, b) = result_value.kind() { - // ... in which case we would set `canonical_vars[0]` to `Some('static)`. - // We only allow a `ty::INNERMOST` index in generic parameters. assert_eq!(debruijn, ty::INNERMOST); opt_values[b.var] = Some(*original_value); @@ -440,8 +442,6 @@ impl<'tcx> InferCtxt<'tcx> { } GenericArgKind::Const(result_value) => { if let ty::ConstKind::Bound(debruijn, b) = result_value.kind() { - // ...in which case we would set `canonical_vars[0]` to `Some(const X)`. - // We only allow a `ty::INNERMOST` index in generic parameters. assert_eq!(debruijn, ty::INNERMOST); opt_values[b.var] = Some(*original_value); @@ -453,32 +453,31 @@ impl<'tcx> InferCtxt<'tcx> { // Create result arguments: if we found a value for a // given variable in the loop above, use that. Otherwise, use // a fresh inference variable. - let result_args = CanonicalVarValues { - var_values: self.tcx.mk_args_from_iter( - query_response.variables.iter().enumerate().map(|(index, var_kind)| { - if var_kind.universe() != ty::UniverseIndex::ROOT { - // A variable from inside a binder of the query. While ideally these shouldn't - // exist at all, we have to deal with them for now. - self.instantiate_canonical_var(cause.span, var_kind, |u| { - universe_map[u.as_usize()] - }) - } else if var_kind.is_existential() { - match opt_values[BoundVar::new(index)] { - Some(k) => k, - None => self.instantiate_canonical_var(cause.span, var_kind, |u| { - universe_map[u.as_usize()] - }), - } - } else { - // For placeholders which were already part of the input, we simply map this - // universal bound variable back the placeholder of the input. - opt_values[BoundVar::new(index)].expect( - "expected placeholder to be unified with itself during response", - ) - } - }), - ), - }; + let mut var_values = Vec::with_capacity(query_response.variables.len()); + for (index, kind) in query_response.variables.iter().enumerate() { + let value = if kind.universe() != ty::UniverseIndex::ROOT { + // A variable from inside a binder of the query. While ideally these shouldn't + // exist at all, we have to deal with them for now. + self.instantiate_canonical_var(cause.span, kind, &var_values, |u| { + universe_map[u.as_usize()] + }) + } else if kind.is_existential() { + match opt_values[BoundVar::new(index)] { + Some(k) => k, + None => self.instantiate_canonical_var(cause.span, kind, &var_values, |u| { + universe_map[u.as_usize()] + }), + } + } else { + // For placeholders which were already part of the input, we simply map this + // universal bound variable back the placeholder of the input. + opt_values[BoundVar::new(index)] + .expect("expected placeholder to be unified with itself during response") + }; + var_values.push(value); + } + + let result_args = CanonicalVarValues { var_values: self.tcx.mk_args(&var_values) }; let mut obligations = PredicateObligations::new(); diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index efd623fac788..b76931caaa57 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -59,6 +59,10 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { self.root_var(var) } + fn sub_root_ty_var(&self, var: ty::TyVid) -> ty::TyVid { + self.sub_root_var(var) + } + fn root_const_var(&self, var: ty::ConstVid) -> ty::ConstVid { self.root_const_var(var) } diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index 7a8ee09650e7..d42c62439f6e 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -67,6 +67,7 @@ pub struct Canonicalizer<'a, D: SolverDelegate, I: Interner> { variables: &'a mut Vec, var_kinds: Vec>, variable_lookup_table: HashMap, + sub_root_lookup_table: HashMap, binder_index: ty::DebruijnIndex, /// We only use the debruijn index during lookup. We don't need to @@ -88,6 +89,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { variables, variable_lookup_table: Default::default(), + sub_root_lookup_table: Default::default(), var_kinds: Vec::new(), binder_index: ty::INNERMOST, @@ -132,6 +134,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { variables: &mut variables, variable_lookup_table: Default::default(), + sub_root_lookup_table: Default::default(), var_kinds: Vec::new(), binder_index: ty::INNERMOST, @@ -139,6 +142,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { }; let param_env = param_env.fold_with(&mut env_canonicalizer); debug_assert_eq!(env_canonicalizer.binder_index, ty::INNERMOST); + debug_assert!(env_canonicalizer.sub_root_lookup_table.is_empty()); CanonicalParamEnvCacheEntry { param_env, variable_lookup_table: env_canonicalizer.variable_lookup_table, @@ -164,6 +168,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { variables, variable_lookup_table: Default::default(), + sub_root_lookup_table: Default::default(), var_kinds: Vec::new(), binder_index: ty::INNERMOST, @@ -171,6 +176,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { }; let param_env = param_env.fold_with(&mut env_canonicalizer); debug_assert_eq!(env_canonicalizer.binder_index, ty::INNERMOST); + debug_assert!(env_canonicalizer.sub_root_lookup_table.is_empty()); (param_env, env_canonicalizer.variable_lookup_table, env_canonicalizer.var_kinds) } } @@ -199,6 +205,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { variables, variable_lookup_table, + sub_root_lookup_table: Default::default(), var_kinds, binder_index: ty::INNERMOST, @@ -265,6 +272,13 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { ty::BoundVar::from(idx) } + fn get_or_insert_sub_root(&mut self, vid: ty::TyVid) -> ty::BoundVar { + let root_vid = self.delegate.sub_root_ty_var(vid); + let idx = + *self.sub_root_lookup_table.entry(root_vid).or_insert_with(|| self.variables.len()); + ty::BoundVar::from(idx) + } + fn finalize(self) -> (ty::UniverseIndex, I::CanonicalVarKinds) { let mut var_kinds = self.var_kinds; // See the rustc-dev-guide section about how we deal with universes @@ -312,16 +326,15 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { "ty vid should have been resolved fully before canonicalization" ); - match self.canonicalize_mode { - CanonicalizeMode::Input { .. } => { - CanonicalVarKind::Ty(ty::UniverseIndex::ROOT) - } - CanonicalizeMode::Response { .. } => { - CanonicalVarKind::Ty(self.delegate.universe_of_ty(vid).unwrap_or_else( - || panic!("ty var should have been resolved: {t:?}"), - )) - } - } + let sub_root = self.get_or_insert_sub_root(vid); + let ui = match self.canonicalize_mode { + CanonicalizeMode::Input { .. } => ty::UniverseIndex::ROOT, + CanonicalizeMode::Response { .. } => self + .delegate + .universe_of_ty(vid) + .unwrap_or_else(|| panic!("ty var should have been resolved: {t:?}")), + }; + CanonicalVarKind::Ty { ui, sub_root } } ty::IntVar(vid) => { debug_assert_eq!( diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs index 7b932010d498..4460e9ad2f1f 100644 --- a/compiler/rustc_next_trait_solver/src/delegate.rs +++ b/compiler/rustc_next_trait_solver/src/delegate.rs @@ -57,12 +57,14 @@ pub trait SolverDelegate: Deref + Sized { where V: TypeFoldable; - fn instantiate_canonical_var_with_infer( + fn instantiate_canonical_var( &self, kind: ty::CanonicalVarKind, span: ::Span, + var_values: &[::GenericArg], universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex, ) -> ::GenericArg; + fn add_item_bounds_for_hidden_type( &self, def_id: ::DefId, diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs index 6f9f40673847..8d7a55c55be9 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs @@ -16,7 +16,8 @@ use rustc_type_ir::data_structures::HashSet; use rustc_type_ir::inherent::*; use rustc_type_ir::relate::solver_relating::RelateExt; use rustc_type_ir::{ - self as ty, Canonical, CanonicalVarValues, InferCtxtLike, Interner, TypeFoldable, + self as ty, Canonical, CanonicalVarKind, CanonicalVarValues, InferCtxtLike, Interner, + TypeFoldable, }; use tracing::{debug, instrument, trace}; @@ -336,7 +337,16 @@ where { match result_value.kind() { ty::GenericArgKind::Type(t) => { - if let ty::Bound(debruijn, b) = t.kind() { + // We disable the instantiation guess for inference variables + // and only use it for placeholders. We need to handle the + // `sub_root` of type inference variables which would make this + // more involved. They are also a lot rarer than region variables. + if let ty::Bound(debruijn, b) = t.kind() + && !matches!( + response.variables.get(b.var().as_usize()).unwrap(), + CanonicalVarKind::Ty { .. } + ) + { assert_eq!(debruijn, ty::INNERMOST); opt_values[b.var()] = Some(*original_value); } @@ -356,38 +366,37 @@ where } } - let var_values = delegate.cx().mk_args_from_iter( - response.variables.iter().enumerate().map(|(index, var_kind)| { - if var_kind.universe() != ty::UniverseIndex::ROOT { - // A variable from inside a binder of the query. While ideally these shouldn't - // exist at all (see the FIXME at the start of this method), we have to deal with - // them for now. - delegate.instantiate_canonical_var_with_infer(var_kind, span, |idx| { - prev_universe + idx.index() - }) - } else if var_kind.is_existential() { - // As an optimization we sometimes avoid creating a new inference variable here. - // - // All new inference variables we create start out in the current universe of the caller. - // This is conceptually wrong as these inference variables would be able to name - // more placeholders then they should be able to. However the inference variables have - // to "come from somewhere", so by equating them with the original values of the caller - // later on, we pull them down into their correct universe again. - if let Some(v) = opt_values[ty::BoundVar::from_usize(index)] { - v - } else { - delegate - .instantiate_canonical_var_with_infer(var_kind, span, |_| prev_universe) - } + let mut var_values = Vec::with_capacity(response.variables.len()); + for (index, kind) in response.variables.iter().enumerate() { + let value = if kind.universe() != ty::UniverseIndex::ROOT { + // A variable from inside a binder of the query. While ideally these shouldn't + // exist at all (see the FIXME at the start of this method), we have to deal with + // them for now. + delegate.instantiate_canonical_var(kind, span, &var_values, |idx| { + prev_universe + idx.index() + }) + } else if kind.is_existential() { + // As an optimization we sometimes avoid creating a new inference variable here. + // + // All new inference variables we create start out in the current universe of the caller. + // This is conceptually wrong as these inference variables would be able to name + // more placeholders then they should be able to. However the inference variables have + // to "come from somewhere", so by equating them with the original values of the caller + // later on, we pull them down into their correct universe again. + if let Some(v) = opt_values[ty::BoundVar::from_usize(index)] { + v } else { - // For placeholders which were already part of the input, we simply map this - // universal bound variable back the placeholder of the input. - original_values[var_kind.expect_placeholder_index()] + delegate.instantiate_canonical_var(kind, span, &var_values, |_| prev_universe) } - }), - ); + } else { + // For placeholders which were already part of the input, we simply map this + // universal bound variable back the placeholder of the input. + original_values[kind.expect_placeholder_index()] + }; + var_values.push(value) + } - CanonicalVarValues { var_values } + CanonicalVarValues { var_values: delegate.cx().mk_args(&var_values) } } /// Unify the `original_values` with the `var_values` returned by the canonical query.. diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index a2db21796a20..91b83e6cbbeb 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -418,6 +418,11 @@ pub struct GoalEvaluation { pub has_changed: HasChanged, /// If the [`Certainty`] was `Maybe`, then keep track of whether the goal has changed /// before rerunning it. + /// + /// We knowingly ignore the `sub_root` of our inference variables here. This means we + /// may not reevaluate a goal even though a change to the `sub_root` could cause a goal + /// to make progress. Tracking them adds additional complexity for an incredibly minor + /// type inference improvement. We could look into properly handling this in the future. pub stalled_on: Option>, } diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index 448fc025be66..44230624ddba 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -237,13 +237,14 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< canonical.instantiate(self.tcx, &values) } - fn instantiate_canonical_var_with_infer( + fn instantiate_canonical_var( &self, kind: CanonicalVarKind<'tcx>, span: Span, + var_values: &[ty::GenericArg<'tcx>], universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex, ) -> ty::GenericArg<'tcx> { - self.0.instantiate_canonical_var(span, kind, universe_map) + self.0.instantiate_canonical_var(span, kind, var_values, universe_map) } fn add_item_bounds_for_hidden_type( diff --git a/compiler/rustc_type_ir/src/canonical.rs b/compiler/rustc_type_ir/src/canonical.rs index cd509ed3f72f..04bf90080358 100644 --- a/compiler/rustc_type_ir/src/canonical.rs +++ b/compiler/rustc_type_ir/src/canonical.rs @@ -91,7 +91,11 @@ impl fmt::Display for Canonical { )] pub enum CanonicalVarKind { /// General type variable `?T` that can be unified with arbitrary types. - Ty(UniverseIndex), + /// + /// We also store the index of the first type variable which is sub-unified + /// with this one. If there is no inference variable related to this one, + /// its `sub_root` just points to itself. + Ty { ui: UniverseIndex, sub_root: ty::BoundVar }, /// Integral type variable `?I` (that can only be unified with integral types). Int, @@ -122,7 +126,7 @@ impl Eq for CanonicalVarKind {} impl CanonicalVarKind { pub fn universe(self) -> UniverseIndex { match self { - CanonicalVarKind::Ty(ui) => ui, + CanonicalVarKind::Ty { ui, sub_root: _ } => ui, CanonicalVarKind::Region(ui) => ui, CanonicalVarKind::Const(ui) => ui, CanonicalVarKind::PlaceholderTy(placeholder) => placeholder.universe(), @@ -138,7 +142,7 @@ impl CanonicalVarKind { /// the updated universe is not the root. pub fn with_updated_universe(self, ui: UniverseIndex) -> CanonicalVarKind { match self { - CanonicalVarKind::Ty(_) => CanonicalVarKind::Ty(ui), + CanonicalVarKind::Ty { ui: _, sub_root } => CanonicalVarKind::Ty { ui, sub_root }, CanonicalVarKind::Region(_) => CanonicalVarKind::Region(ui), CanonicalVarKind::Const(_) => CanonicalVarKind::Const(ui), @@ -160,7 +164,7 @@ impl CanonicalVarKind { pub fn is_existential(self) -> bool { match self { - CanonicalVarKind::Ty(_) + CanonicalVarKind::Ty { .. } | CanonicalVarKind::Int | CanonicalVarKind::Float | CanonicalVarKind::Region(_) @@ -174,7 +178,7 @@ impl CanonicalVarKind { pub fn is_region(self) -> bool { match self { CanonicalVarKind::Region(_) | CanonicalVarKind::PlaceholderRegion(_) => true, - CanonicalVarKind::Ty(_) + CanonicalVarKind::Ty { .. } | CanonicalVarKind::Int | CanonicalVarKind::Float | CanonicalVarKind::PlaceholderTy(_) @@ -185,7 +189,7 @@ impl CanonicalVarKind { pub fn expect_placeholder_index(self) -> usize { match self { - CanonicalVarKind::Ty(_) + CanonicalVarKind::Ty { .. } | CanonicalVarKind::Int | CanonicalVarKind::Float | CanonicalVarKind::Region(_) @@ -275,7 +279,7 @@ impl CanonicalVarValues { var_values: cx.mk_args_from_iter(infos.iter().enumerate().map( |(i, kind)| -> I::GenericArg { match kind { - CanonicalVarKind::Ty(_) + CanonicalVarKind::Ty { .. } | CanonicalVarKind::Int | CanonicalVarKind::Float | CanonicalVarKind::PlaceholderTy(_) => { diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index 3fac50ff9ccf..7fec869f1007 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -158,6 +158,7 @@ pub trait InferCtxtLike: Sized { fn universe_of_ct(&self, ct: ty::ConstVid) -> Option; fn root_ty_var(&self, var: ty::TyVid) -> ty::TyVid; + fn sub_root_ty_var(&self, var: ty::TyVid) -> ty::TyVid; fn root_const_var(&self, var: ty::ConstVid) -> ty::ConstVid; fn opportunistic_resolve_ty_var(&self, vid: ty::TyVid) -> ::Ty; diff --git a/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir b/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir index 906e5828aba8..a18d7e7478fc 100644 --- a/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir +++ b/tests/mir-opt/address_of.address_of_reborrow.SimplifyCfg-initial.after.mir @@ -1,30 +1,30 @@ // MIR for `address_of_reborrow` after SimplifyCfg-initial | User Type Annotations -| 0: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty(U0)] }, span: $DIR/address_of.rs:8:10: 8:18, inferred_ty: *const [i32; 10] +| 0: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:8:10: 8:18, inferred_ty: *const [i32; 10] | 1: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:10:10: 10:25, inferred_ty: *const dyn std::marker::Send -| 2: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty(U0)] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10] -| 3: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty(U0)] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10] +| 2: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10] +| 3: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:14:12: 14:20, inferred_ty: *const [i32; 10] | 4: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:15:12: 15:28, inferred_ty: *const [i32; 10] | 5: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:15:12: 15:28, inferred_ty: *const [i32; 10] | 6: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:16:12: 16:27, inferred_ty: *const dyn std::marker::Send | 7: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:16:12: 16:27, inferred_ty: *const dyn std::marker::Send | 8: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:17:12: 17:24, inferred_ty: *const [i32] | 9: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:17:12: 17:24, inferred_ty: *const [i32] -| 10: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty(U0)] }, span: $DIR/address_of.rs:19:10: 19:18, inferred_ty: *const [i32; 10] +| 10: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:19:10: 19:18, inferred_ty: *const [i32; 10] | 11: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:21:10: 21:25, inferred_ty: *const dyn std::marker::Send -| 12: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty(U0)] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10] -| 13: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty(U0)] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10] +| 12: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10] +| 13: user_ty: Canonical { value: Ty(*const ^0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:24:12: 24:20, inferred_ty: *const [i32; 10] | 14: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:25:12: 25:28, inferred_ty: *const [i32; 10] | 15: user_ty: Canonical { value: Ty(*const [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:25:12: 25:28, inferred_ty: *const [i32; 10] | 16: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:26:12: 26:27, inferred_ty: *const dyn std::marker::Send | 17: user_ty: Canonical { value: Ty(*const dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:26:12: 26:27, inferred_ty: *const dyn std::marker::Send | 18: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:27:12: 27:24, inferred_ty: *const [i32] | 19: user_ty: Canonical { value: Ty(*const [i32]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:27:12: 27:24, inferred_ty: *const [i32] -| 20: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [Ty(U0)] }, span: $DIR/address_of.rs:29:10: 29:16, inferred_ty: *mut [i32; 10] +| 20: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:29:10: 29:16, inferred_ty: *mut [i32; 10] | 21: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:31:10: 31:23, inferred_ty: *mut dyn std::marker::Send -| 22: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [Ty(U0)] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10] -| 23: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [Ty(U0)] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10] +| 22: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10] +| 23: user_ty: Canonical { value: Ty(*mut ^0), max_universe: U0, variables: [Ty { ui: U0, sub_root: 0 }] }, span: $DIR/address_of.rs:34:12: 34:18, inferred_ty: *mut [i32; 10] | 24: user_ty: Canonical { value: Ty(*mut [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:35:12: 35:26, inferred_ty: *mut [i32; 10] | 25: user_ty: Canonical { value: Ty(*mut [i32; 10]), max_universe: U0, variables: [] }, span: $DIR/address_of.rs:35:12: 35:26, inferred_ty: *mut [i32; 10] | 26: user_ty: Canonical { value: Ty(*mut dyn std::marker::Send), max_universe: U0, variables: [Region(U0)] }, span: $DIR/address_of.rs:36:12: 36:25, inferred_ty: *mut dyn std::marker::Send diff --git a/tests/ui/error-emitter/multiline-removal-suggestion.svg b/tests/ui/error-emitter/multiline-removal-suggestion.svg index 9c9bd163ecd4..7a88ac55b23e 100644 --- a/tests/ui/error-emitter/multiline-removal-suggestion.svg +++ b/tests/ui/error-emitter/multiline-removal-suggestion.svg @@ -1,4 +1,4 @@ - + { CanonicalVarValues { var_values: Default::default() } } + pub fn instantiate( + cx: I, + variables: I::CanonicalVarKinds, + mut f: impl FnMut(&[I::GenericArg], CanonicalVarKind) -> I::GenericArg, + ) -> CanonicalVarValues { + // Instantiating `CanonicalVarValues` is really hot, but limited to less than + // 4 most of the time. Avoid creating a `Vec` here. + if variables.len() <= 4 { + let mut var_values = ArrayVec::<_, 4>::new(); + for info in variables.iter() { + var_values.push(f(&var_values, info)); + } + CanonicalVarValues { var_values: cx.mk_args(&var_values) } + } else { + CanonicalVarValues::instantiate_cold(cx, variables, f) + } + } + + #[cold] + fn instantiate_cold( + cx: I, + variables: I::CanonicalVarKinds, + mut f: impl FnMut(&[I::GenericArg], CanonicalVarKind) -> I::GenericArg, + ) -> CanonicalVarValues { + let mut var_values = Vec::with_capacity(variables.len()); + for info in variables.iter() { + var_values.push(f(&var_values, info)); + } + CanonicalVarValues { var_values: cx.mk_args(&var_values) } + } + #[inline] pub fn len(&self) -> usize { self.var_values.len() From b51a3a565a056235f3864e2cefdb9449f6b0dcb1 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 8 Sep 2025 11:41:43 +0200 Subject: [PATCH 252/682] review --- .../src/infer/canonical/canonicalizer.rs | 8 +++- .../rustc_infer/src/infer/canonical/mod.rs | 5 ++- compiler/rustc_infer/src/infer/context.rs | 8 ++-- compiler/rustc_infer/src/infer/mod.rs | 10 ++--- .../src/infer/relate/generalize.rs | 2 +- .../rustc_infer/src/infer/type_variable.rs | 41 ++++++++++--------- .../src/canonicalizer.rs | 8 +++- .../src/solve/eval_ctxt/mod.rs | 4 +- .../rustc_next_trait_solver/src/solve/mod.rs | 2 +- .../error_reporting/infer/need_type_info.rs | 3 +- .../src/solve/delegate.rs | 2 +- compiler/rustc_type_ir/src/infer_ctxt.rs | 4 +- 12 files changed, 57 insertions(+), 40 deletions(-) diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index be68609502a8..3c5e4a91c98c 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -294,6 +294,12 @@ struct Canonicalizer<'cx, 'tcx> { // Note that indices is only used once `var_values` is big enough to be // heap-allocated. indices: FxHashMap, BoundVar>, + /// Maps each `sub_unification_table_root_var` to the index of the first + /// variable which used it. + /// + /// This means in case two type variables have the same sub relations root, + /// we set the `sub_root` of the second variable to the position of the first. + /// Otherwise the `sub_root` of each type variable is just its own position. sub_root_lookup_table: SsoHashMap, canonicalize_mode: &'cx dyn CanonicalizeMode, needs_canonical_flags: TypeFlags, @@ -662,7 +668,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { } fn get_or_insert_sub_root(&mut self, vid: ty::TyVid) -> ty::BoundVar { - let root_vid = self.infcx.unwrap().sub_root_var(vid); + let root_vid = self.infcx.unwrap().sub_unification_table_root_var(vid); let idx = *self.sub_root_lookup_table.entry(root_vid).or_insert_with(|| self.variables.len()); ty::BoundVar::from(idx) diff --git a/compiler/rustc_infer/src/infer/canonical/mod.rs b/compiler/rustc_infer/src/infer/canonical/mod.rs index 517331c32457..f99f228e19d8 100644 --- a/compiler/rustc_infer/src/infer/canonical/mod.rs +++ b/compiler/rustc_infer/src/infer/canonical/mod.rs @@ -93,10 +93,11 @@ impl<'tcx> InferCtxt<'tcx> { match kind { CanonicalVarKind::Ty { ui, sub_root } => { let vid = self.next_ty_vid_in_universe(span, universe_map(ui)); - // Fetch the `sub_root` in case it exists. + // If this inference variable is related to an earlier variable + // via subtyping, we need to add that info to the inference context. if let Some(prev) = previous_var_values.get(sub_root.as_usize()) { if let &ty::Infer(ty::TyVar(sub_root)) = prev.expect_ty().kind() { - self.inner.borrow_mut().type_variables().sub(vid, sub_root); + self.sub_unify_ty_vids_raw(vid, sub_root); } else { unreachable!() } diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index b76931caaa57..14cc590720ac 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -59,8 +59,8 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { self.root_var(var) } - fn sub_root_ty_var(&self, var: ty::TyVid) -> ty::TyVid { - self.sub_root_var(var) + fn sub_unification_table_root_var(&self, var: ty::TyVid) -> ty::TyVid { + self.sub_unification_table_root_var(var) } fn root_const_var(&self, var: ty::ConstVid) -> ty::ConstVid { @@ -183,8 +183,8 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { self.inner.borrow_mut().type_variables().equate(a, b); } - fn sub_ty_vids_raw(&self, a: ty::TyVid, b: ty::TyVid) { - self.sub_ty_vids_raw(a, b); + fn sub_unify_ty_vids_raw(&self, a: ty::TyVid, b: ty::TyVid) { + self.sub_unify_ty_vids_raw(a, b); } fn equate_int_vids_raw(&self, a: ty::IntVid, b: ty::IntVid) { diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 48dd699cd50c..9d3886aff1c1 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -764,7 +764,7 @@ impl<'tcx> InferCtxt<'tcx> { let r_b = self.shallow_resolve(predicate.skip_binder().b); match (r_a.kind(), r_b.kind()) { (&ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid))) => { - self.sub_ty_vids_raw(a_vid, b_vid); + self.sub_unify_ty_vids_raw(a_vid, b_vid); return Err((a_vid, b_vid)); } _ => {} @@ -1129,12 +1129,12 @@ impl<'tcx> InferCtxt<'tcx> { self.inner.borrow_mut().type_variables().root_var(var) } - pub fn sub_ty_vids_raw(&self, a: ty::TyVid, b: ty::TyVid) { - self.inner.borrow_mut().type_variables().sub(a, b); + pub fn sub_unify_ty_vids_raw(&self, a: ty::TyVid, b: ty::TyVid) { + self.inner.borrow_mut().type_variables().sub_unify(a, b); } - pub fn sub_root_var(&self, var: ty::TyVid) -> ty::TyVid { - self.inner.borrow_mut().type_variables().sub_root_var(var) + pub fn sub_unification_table_root_var(&self, var: ty::TyVid) -> ty::TyVid { + self.inner.borrow_mut().type_variables().sub_unification_table_root_var(var) } pub fn root_const_var(&self, var: ty::ConstVid) -> ty::ConstVid { diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index 2323add0b320..f0b5b164a0be 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -522,7 +522,7 @@ impl<'tcx> TypeRelation> for Generalizer<'_, 'tcx> { // Record that `vid` and `new_var_id` have to be subtypes // of each other. This is currently only used for diagnostics. // To see why, see the docs in the `type_variables` module. - inner.type_variables().sub(vid, new_var_id); + inner.type_variables().sub_unify(vid, new_var_id); // If we're in the new solver and create a new inference // variable inside of an alias we eagerly constrain that // inference variable to prevent unexpected ambiguity errors. diff --git a/compiler/rustc_infer/src/infer/type_variable.rs b/compiler/rustc_infer/src/infer/type_variable.rs index 613e5a5883a3..65f77fe8e25f 100644 --- a/compiler/rustc_infer/src/infer/type_variable.rs +++ b/compiler/rustc_infer/src/infer/type_variable.rs @@ -42,7 +42,7 @@ impl<'tcx> Rollback>>> for TypeVariabl impl<'tcx> Rollback>> for TypeVariableStorage<'tcx> { fn reverse(&mut self, undo: sv::UndoLog>) { - self.sub_relations.reverse(undo) + self.sub_unification_table.reverse(undo) } } @@ -50,7 +50,7 @@ impl<'tcx> Rollback> for TypeVariableStorage<'tcx> { fn reverse(&mut self, undo: UndoLog<'tcx>) { match undo { UndoLog::EqRelation(undo) => self.eq_relations.reverse(undo), - UndoLog::SubRelation(undo) => self.sub_relations.reverse(undo), + UndoLog::SubRelation(undo) => self.sub_unification_table.reverse(undo), } } } @@ -63,7 +63,9 @@ pub(crate) struct TypeVariableStorage<'tcx> { /// constraint `?X == ?Y`. This table also stores, for each key, /// the known value. eq_relations: ut::UnificationTableStorage>, - /// Only used by `-Znext-solver` and for diagnostics. + /// Only used by `-Znext-solver` and for diagnostics. Tracks whether + /// type variables are related via subtyping at all, ignoring which of + /// the two is the subtype. /// /// When reporting ambiguity errors, we sometimes want to /// treat all inference vars which are subtypes of each @@ -79,7 +81,7 @@ pub(crate) struct TypeVariableStorage<'tcx> { /// Even for something like `let x = returns_arg(); x.method();` the /// type of `x` is only a supertype of the argument of `returns_arg`. We /// still want to suggest specifying the type of the argument. - sub_relations: ut::UnificationTableStorage, + sub_unification_table: ut::UnificationTableStorage, } pub(crate) struct TypeVariableTable<'a, 'tcx> { @@ -155,23 +157,24 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { self.storage.values[vid].origin } - /// Records that `a == b`, depending on `dir`. + /// Records that `a == b`. /// /// Precondition: neither `a` nor `b` are known. pub(crate) fn equate(&mut self, a: ty::TyVid, b: ty::TyVid) { debug_assert!(self.probe(a).is_unknown()); debug_assert!(self.probe(b).is_unknown()); self.eq_relations().union(a, b); - self.sub_relations().union(a, b); + self.sub_unification_table().union(a, b); } - /// Records that `a <: b`, depending on `dir`. + /// Records that `a` and `b` are related via subtyping. We don't track + /// which of the two is the subtype. /// /// Precondition: neither `a` nor `b` are known. - pub(crate) fn sub(&mut self, a: ty::TyVid, b: ty::TyVid) { + pub(crate) fn sub_unify(&mut self, a: ty::TyVid, b: ty::TyVid) { debug_assert!(self.probe(a).is_unknown()); debug_assert!(self.probe(b).is_unknown()); - self.sub_relations().union(a, b); + self.sub_unification_table().union(a, b); } /// Instantiates `vid` with the type `ty`. @@ -206,7 +209,7 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { ) -> ty::TyVid { let eq_key = self.eq_relations().new_key(TypeVariableValue::Unknown { universe }); - let sub_key = self.sub_relations().new_key(()); + let sub_key = self.sub_unification_table().new_key(()); debug_assert_eq!(eq_key.vid, sub_key.vid); let index = self.storage.values.push(TypeVariableData { origin }); @@ -231,16 +234,16 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { self.eq_relations().find(vid).vid } - /// Returns the "root" variable of `vid` in the `sub_relations` - /// equivalence table. All type variables that have been are - /// related via equality or subtyping will yield the same root - /// variable (per the union-find algorithm), so `sub_root_var(a) - /// == sub_root_var(b)` implies that: + /// Returns the "root" variable of `vid` in the `sub_unification_table` + /// equivalence table. All type variables that have been are related via + /// equality or subtyping will yield the same root variable (per the + /// union-find algorithm), so `sub_unification_table_root_var(a) + /// == sub_unification_table_root_var(b)` implies that: /// ```text /// exists X. (a <: X || X <: a) && (b <: X || X <: b) /// ``` - pub(crate) fn sub_root_var(&mut self, vid: ty::TyVid) -> ty::TyVid { - self.sub_relations().find(vid).vid + pub(crate) fn sub_unification_table_root_var(&mut self, vid: ty::TyVid) -> ty::TyVid { + self.sub_unification_table().find(vid).vid } /// Retrieves the type to which `vid` has been instantiated, if @@ -261,8 +264,8 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { } #[inline] - fn sub_relations(&mut self) -> super::UnificationTable<'_, 'tcx, TyVidSubKey> { - self.storage.sub_relations.with_log(self.undo_log) + fn sub_unification_table(&mut self) -> super::UnificationTable<'_, 'tcx, TyVidSubKey> { + self.storage.sub_unification_table.with_log(self.undo_log) } /// Returns a range of the type variables created during the snapshot. diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index d42c62439f6e..a8f2b4e8db66 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -67,6 +67,12 @@ pub struct Canonicalizer<'a, D: SolverDelegate, I: Interner> { variables: &'a mut Vec, var_kinds: Vec>, variable_lookup_table: HashMap, + /// Maps each `sub_unification_table_root_var` to the index of the first + /// variable which used it. + /// + /// This means in case two type variables have the same sub relations root, + /// we set the `sub_root` of the second variable to the position of the first. + /// Otherwise the `sub_root` of each type variable is just its own position. sub_root_lookup_table: HashMap, binder_index: ty::DebruijnIndex, @@ -273,7 +279,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { } fn get_or_insert_sub_root(&mut self, vid: ty::TyVid) -> ty::BoundVar { - let root_vid = self.delegate.sub_root_ty_var(vid); + let root_vid = self.delegate.sub_unification_table_root_var(vid); let idx = *self.sub_root_lookup_table.entry(root_vid).or_insert_with(|| self.variables.len()); ty::BoundVar::from(idx) diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index f0f95e53265c..71f9481d5af8 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -900,8 +900,8 @@ where && goal.param_env.visit_with(&mut visitor).is_continue() } - pub(super) fn sub_ty_vids_raw(&self, a: ty::TyVid, b: ty::TyVid) { - self.delegate.sub_ty_vids_raw(a, b) + pub(super) fn sub_unify_ty_vids_raw(&self, a: ty::TyVid, b: ty::TyVid) { + self.delegate.sub_unify_ty_vids_raw(a, b) } #[instrument(level = "trace", skip(self, param_env), ret)] diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 91b83e6cbbeb..db3460c4ff67 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -121,7 +121,7 @@ where fn compute_subtype_goal(&mut self, goal: Goal>) -> QueryResult { match (goal.predicate.a.kind(), goal.predicate.b.kind()) { (ty::Infer(ty::TyVar(a_vid)), ty::Infer(ty::TyVar(b_vid))) => { - self.sub_ty_vids_raw(a_vid, b_vid); + self.sub_unify_ty_vids_raw(a_vid, b_vid); self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } _ => { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs index dff1311070ce..edab530590b2 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs @@ -894,7 +894,8 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { use ty::{Infer, TyVar}; match (inner_ty.kind(), target_ty.kind()) { (&Infer(TyVar(a_vid)), &Infer(TyVar(b_vid))) => { - self.tecx.sub_root_var(a_vid) == self.tecx.sub_root_var(b_vid) + self.tecx.sub_unification_table_root_var(a_vid) + == self.tecx.sub_unification_table_root_var(b_vid) } _ => false, } diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index 44230624ddba..16f444486a64 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -128,7 +128,7 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< | ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => { match (self.shallow_resolve(a).kind(), self.shallow_resolve(b).kind()) { (&ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid))) => { - self.sub_ty_vids_raw(a_vid, b_vid); + self.sub_unify_ty_vids_raw(a_vid, b_vid); Some(Certainty::AMBIGUOUS) } _ => None, diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index 7fec869f1007..56962b4597b8 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -158,7 +158,7 @@ pub trait InferCtxtLike: Sized { fn universe_of_ct(&self, ct: ty::ConstVid) -> Option; fn root_ty_var(&self, var: ty::TyVid) -> ty::TyVid; - fn sub_root_ty_var(&self, var: ty::TyVid) -> ty::TyVid; + fn sub_unification_table_root_var(&self, var: ty::TyVid) -> ty::TyVid; fn root_const_var(&self, var: ty::ConstVid) -> ty::ConstVid; fn opportunistic_resolve_ty_var(&self, vid: ty::TyVid) -> ::Ty; @@ -198,7 +198,7 @@ pub trait InferCtxtLike: Sized { ) -> U; fn equate_ty_vids_raw(&self, a: ty::TyVid, b: ty::TyVid); - fn sub_ty_vids_raw(&self, a: ty::TyVid, b: ty::TyVid); + fn sub_unify_ty_vids_raw(&self, a: ty::TyVid, b: ty::TyVid); fn equate_int_vids_raw(&self, a: ty::IntVid, b: ty::IntVid); fn equate_float_vids_raw(&self, a: ty::FloatVid, b: ty::FloatVid); fn equate_const_vids_raw(&self, a: ty::ConstVid, b: ty::ConstVid); From 7e826fb14be36b4faf879c3aa1fe1a50b5750a5b Mon Sep 17 00:00:00 2001 From: Daria Sukhonina Date: Mon, 8 Sep 2025 15:27:29 +0300 Subject: [PATCH 253/682] Make `is_typeck_child` match exhaustive --- compiler/rustc_hir/src/def.rs | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index d563fd0a5472..95abe5c40dd4 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -445,7 +445,37 @@ impl DefKind { /// type-checking context, i.e. closure, coroutine or inline const. #[inline] pub fn is_typeck_child(self) -> bool { - matches!(self, DefKind::Closure | DefKind::InlineConst | DefKind::SyntheticCoroutineBody) + match self { + DefKind::Closure | DefKind::InlineConst | DefKind::SyntheticCoroutineBody => true, + DefKind::Mod + | DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::Variant + | DefKind::Trait + | DefKind::TyAlias + | DefKind::ForeignTy + | DefKind::TraitAlias + | DefKind::AssocTy + | DefKind::TyParam + | DefKind::Fn + | DefKind::Const + | DefKind::ConstParam + | DefKind::Static { .. } + | DefKind::Ctor(_, _) + | DefKind::AssocFn + | DefKind::AssocConst + | DefKind::Macro(_) + | DefKind::ExternCrate + | DefKind::Use + | DefKind::ForeignMod + | DefKind::AnonConst + | DefKind::OpaqueTy + | DefKind::Field + | DefKind::LifetimeParam + | DefKind::GlobalAsm + | DefKind::Impl { .. } => false, + } } } From 2f0c1035ed7a1ad2348565c8d3fc4c5c3e758a24 Mon Sep 17 00:00:00 2001 From: Jeremy Smart Date: Wed, 27 Aug 2025 10:04:05 -0400 Subject: [PATCH 254/682] fix APITIT being treated as a normal generic parameter in suggestions --- compiler/rustc_hir_analysis/src/check/mod.rs | 73 ++++++++++++------- .../apitit-unimplemented-method.rs | 12 +++ .../apitit-unimplemented-method.stderr | 12 +++ tests/ui/suggestions/auxiliary/dep.rs | 4 + 4 files changed, 74 insertions(+), 27 deletions(-) create mode 100644 tests/ui/suggestions/apitit-unimplemented-method.rs create mode 100644 tests/ui/suggestions/apitit-unimplemented-method.stderr create mode 100644 tests/ui/suggestions/auxiliary/dep.rs diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 85445cb3c004..e4c1e304c4b3 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -85,7 +85,9 @@ use rustc_infer::traits::ObligationCause; use rustc_middle::query::Providers; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::print::with_types_for_signature; -use rustc_middle::ty::{self, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypingMode}; +use rustc_middle::ty::{ + self, GenericArgs, GenericArgsRef, GenericParamDefKind, Ty, TyCtxt, TypingMode, +}; use rustc_middle::{bug, span_bug}; use rustc_session::parse::feature_err; use rustc_span::def_id::CRATE_DEF_ID; @@ -232,8 +234,7 @@ fn missing_items_err( }; // Obtain the level of indentation ending in `sugg_sp`. - let padding = - tcx.sess.source_map().indentation_before(sugg_sp).unwrap_or_else(|| String::new()); + let padding = tcx.sess.source_map().indentation_before(sugg_sp).unwrap_or_else(String::new); let (mut missing_trait_item, mut missing_trait_item_none, mut missing_trait_item_label) = (Vec::new(), Vec::new(), Vec::new()); @@ -330,6 +331,7 @@ fn default_body_is_unstable( fn bounds_from_generic_predicates<'tcx>( tcx: TyCtxt<'tcx>, predicates: impl IntoIterator, Span)>, + assoc: ty::AssocItem, ) -> (String, String) { let mut types: FxIndexMap, Vec> = FxIndexMap::default(); let mut projections = vec![]; @@ -353,34 +355,50 @@ fn bounds_from_generic_predicates<'tcx>( } let mut where_clauses = vec![]; - let mut types_str = vec![]; - for (ty, bounds) in types { - if let ty::Param(_) = ty.kind() { - let mut bounds_str = vec![]; - for bound in bounds { - let mut projections_str = vec![]; - for projection in &projections { - let p = projection.skip_binder(); - if bound == tcx.parent(p.projection_term.def_id) - && p.projection_term.self_ty() == ty - { - let name = tcx.item_name(p.projection_term.def_id); - projections_str.push(format!("{} = {}", name, p.term)); + let generics = tcx.generics_of(assoc.def_id); + let types_str = generics + .own_params + .iter() + .filter(|p| matches!(p.kind, GenericParamDefKind::Type { synthetic: false, .. })) + .map(|p| { + // we just checked that it's a type, so the unwrap can't fail + let ty = tcx.mk_param_from_def(p).as_type().unwrap(); + if let Some(bounds) = types.get(&ty) { + let mut bounds_str = vec![]; + for bound in bounds.iter().copied() { + let mut projections_str = vec![]; + for projection in &projections { + let p = projection.skip_binder(); + if bound == tcx.parent(p.projection_term.def_id) + && p.projection_term.self_ty() == ty + { + let name = tcx.item_name(p.projection_term.def_id); + projections_str.push(format!("{} = {}", name, p.term)); + } + } + let bound_def_path = tcx.def_path_str(bound); + if projections_str.is_empty() { + where_clauses.push(format!("{}: {}", ty, bound_def_path)); + } else { + bounds_str.push(format!( + "{}<{}>", + bound_def_path, + projections_str.join(", ") + )); } } - let bound_def_path = tcx.def_path_str(bound); - if projections_str.is_empty() { - where_clauses.push(format!("{}: {}", ty, bound_def_path)); + if bounds_str.is_empty() { + ty.to_string() } else { - bounds_str.push(format!("{}<{}>", bound_def_path, projections_str.join(", "))); + format!("{}: {}", ty, bounds_str.join(" + ")) } - } - if bounds_str.is_empty() { - types_str.push(ty.to_string()); } else { - types_str.push(format!("{}: {}", ty, bounds_str.join(" + "))); + ty.to_string() } - } else { + }) + .collect::>(); + for (ty, bounds) in types.into_iter() { + if !matches!(ty.kind(), ty::Param(_)) { // Avoid suggesting the following: // fn foo::Bar>(_: T) where T: Trait, ::Bar: Other {} where_clauses.extend( @@ -472,10 +490,10 @@ fn fn_sig_suggestion<'tcx>( let output = if !output.is_unit() { format!(" -> {output}") } else { String::new() }; let safety = sig.safety.prefix_str(); - let (generics, where_clauses) = bounds_from_generic_predicates(tcx, predicates); + let (generics, where_clauses) = bounds_from_generic_predicates(tcx, predicates, assoc); // FIXME: this is not entirely correct, as the lifetimes from borrowed params will - // not be present in the `fn` definition, not will we account for renamed + // not be present in the `fn` definition, nor will we account for renamed // lifetimes between the `impl` and the `trait`, but this should be good enough to // fill in a significant portion of the missing code, and other subsequent // suggestions can help the user fix the code. @@ -511,6 +529,7 @@ fn suggestion_signature<'tcx>( let (generics, where_clauses) = bounds_from_generic_predicates( tcx, tcx.predicates_of(assoc.def_id).instantiate_own(tcx, args), + assoc, ); format!("type {}{generics} = /* Type */{where_clauses};", assoc.name()) } diff --git a/tests/ui/suggestions/apitit-unimplemented-method.rs b/tests/ui/suggestions/apitit-unimplemented-method.rs new file mode 100644 index 000000000000..b182e1939b3e --- /dev/null +++ b/tests/ui/suggestions/apitit-unimplemented-method.rs @@ -0,0 +1,12 @@ +//@ aux-build:dep.rs + +extern crate dep; +use dep::*; + +struct Local; +impl Trait for Local {} +//~^ ERROR not all trait items implemented +//~| HELP implement the missing item: `fn foo(_: impl Sized) { todo!() }` +//~| HELP implement the missing item: `fn bar(_: impl Sized) { todo!() }` + +fn main() {} diff --git a/tests/ui/suggestions/apitit-unimplemented-method.stderr b/tests/ui/suggestions/apitit-unimplemented-method.stderr new file mode 100644 index 000000000000..b309a64e9582 --- /dev/null +++ b/tests/ui/suggestions/apitit-unimplemented-method.stderr @@ -0,0 +1,12 @@ +error[E0046]: not all trait items implemented, missing: `foo`, `bar` + --> $DIR/apitit-unimplemented-method.rs:7:1 + | +LL | impl Trait for Local {} + | ^^^^^^^^^^^^^^^^^^^^ missing `foo`, `bar` in implementation + | + = help: implement the missing item: `fn foo(_: impl Sized) { todo!() }` + = help: implement the missing item: `fn bar(_: impl Sized) { todo!() }` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0046`. diff --git a/tests/ui/suggestions/auxiliary/dep.rs b/tests/ui/suggestions/auxiliary/dep.rs new file mode 100644 index 000000000000..ac0de418313c --- /dev/null +++ b/tests/ui/suggestions/auxiliary/dep.rs @@ -0,0 +1,4 @@ +pub trait Trait { + fn foo(_: impl Sized); + fn bar(_: impl Sized); +} From edc94e61841506c6957e1ee0ed57a2232fe339c4 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Sun, 7 Sep 2025 23:20:40 +0200 Subject: [PATCH 255/682] mark `format_args_nl!` as `#[doc(hidden)]` --- library/core/src/macros/mod.rs | 1 + src/librustdoc/html/highlight.rs | 2 +- src/librustdoc/lib.rs | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index ccf41dfb01dd..3f58fc448aa6 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1017,6 +1017,7 @@ pub(crate) mod builtin { )] #[allow_internal_unstable(fmt_internals)] #[rustc_builtin_macro] + #[doc(hidden)] #[macro_export] macro_rules! format_args_nl { ($fmt:expr) => {{ /* compiler built-in */ }}; diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index feafb41dc990..0e06361024b9 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -578,7 +578,7 @@ pub(super) fn write_code( } fn write_footer(out: &mut String, playground_button: Option<&str>) { - write_str(out, format_args_nl!("{}", playground_button.unwrap_or_default())); + write_str(out, format_args!("{}", playground_button.unwrap_or_default())); } /// How a span of text is classified. Mostly corresponds to token kinds. diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index f62eba4b3c19..9871066b9eb5 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -10,7 +10,6 @@ #![feature(box_patterns)] #![feature(debug_closure_helpers)] #![feature(file_buffered)] -#![feature(format_args_nl)] #![feature(if_let_guard)] #![feature(iter_advance_by)] #![feature(iter_intersperse)] From 469a80ec725556ec1be482983b0321a10eeb61f4 Mon Sep 17 00:00:00 2001 From: binarycat Date: Mon, 8 Sep 2025 08:08:43 -0500 Subject: [PATCH 256/682] all tidy extra checks now print what they do in present tense --- src/tools/tidy/src/extra_checks/mod.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/tools/tidy/src/extra_checks/mod.rs b/src/tools/tidy/src/extra_checks/mod.rs index 321ef65117e2..83e94b78c71f 100644 --- a/src/tools/tidy/src/extra_checks/mod.rs +++ b/src/tools/tidy/src/extra_checks/mod.rs @@ -290,10 +290,10 @@ fn check_impl( args.extend_from_slice(SPELLCHECK_DIRS); if bless { - eprintln!("spellcheck files and fix"); + eprintln!("spellchecking files and fixing typos"); args.push("--write-changes"); } else { - eprintln!("spellcheck files"); + eprintln!("spellchecking files"); } spellcheck_runner(root_path, &outdir, &cargo, &args)?; } @@ -303,11 +303,17 @@ fn check_impl( } if js_lint { + if bless { + eprintln!("linting javascript files"); + } else { + eprintln!("linting javascript files and applying suggestions"); + } rustdoc_js::lint(outdir, librustdoc_path, tools_path, bless)?; rustdoc_js::es_check(outdir, librustdoc_path)?; } if js_typecheck { + eprintln!("typechecking javascript files"); rustdoc_js::typecheck(outdir, librustdoc_path)?; } From a9d884df81a1af5983eb01a94429ffc4b2803a08 Mon Sep 17 00:00:00 2001 From: binarycat Date: Mon, 8 Sep 2025 08:24:56 -0500 Subject: [PATCH 257/682] tidy: py:lint extra check now works with --bless --- src/tools/tidy/src/extra_checks/mod.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/tools/tidy/src/extra_checks/mod.rs b/src/tools/tidy/src/extra_checks/mod.rs index 83e94b78c71f..5e7a686e775e 100644 --- a/src/tools/tidy/src/extra_checks/mod.rs +++ b/src/tools/tidy/src/extra_checks/mod.rs @@ -147,9 +147,16 @@ fn check_impl( } if python_lint { - eprintln!("linting python files"); let py_path = py_path.as_ref().unwrap(); - let res = run_ruff(root_path, outdir, py_path, &cfg_args, &file_args, &["check".as_ref()]); + let args: &[&OsStr] = if bless { + eprintln!("linting python files and applying suggestions"); + &["check".as_ref(), "--fix".as_ref()] + } else { + eprintln!("linting python files"); + &["check".as_ref()] + }; + + let res = run_ruff(root_path, outdir, py_path, &cfg_args, &file_args, args); if res.is_err() && show_diff { eprintln!("\npython linting failed! Printing diff suggestions:"); From c19d81badb87c138483e950f7adcf86639ede76c Mon Sep 17 00:00:00 2001 From: binarycat Date: Mon, 8 Sep 2025 08:53:20 -0500 Subject: [PATCH 258/682] tidy extra checks that interact with --bless now all recommend using it --- src/tools/tidy/src/extra_checks/mod.rs | 31 +++++++++++++++++++++----- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/src/tools/tidy/src/extra_checks/mod.rs b/src/tools/tidy/src/extra_checks/mod.rs index 5e7a686e775e..a6b50b5ca470 100644 --- a/src/tools/tidy/src/extra_checks/mod.rs +++ b/src/tools/tidy/src/extra_checks/mod.rs @@ -124,6 +124,12 @@ fn check_impl( }; } + let rerun_with_bless = |mode: &str, action: &str| { + if !bless { + eprintln!("rerun tidy with `--extra-checks={mode} --bless` to {action}"); + } + }; + let python_lint = extra_check!(Py, Lint); let python_fmt = extra_check!(Py, Fmt); let shell_lint = extra_check!(Shell, Lint); @@ -158,10 +164,10 @@ fn check_impl( let res = run_ruff(root_path, outdir, py_path, &cfg_args, &file_args, args); - if res.is_err() && show_diff { + if res.is_err() && show_diff && !bless { eprintln!("\npython linting failed! Printing diff suggestions:"); - let _ = run_ruff( + let diff_res = run_ruff( root_path, outdir, py_path, @@ -169,6 +175,10 @@ fn check_impl( &file_args, &["check".as_ref(), "--diff".as_ref()], ); + // `ruff check --diff` will return status 0 if there are no suggestions. + if diff_res.is_err() { + rerun_with_bless("py:lint", "apply ruff suggestions"); + } } // Rethrow error res?; @@ -199,7 +209,7 @@ fn check_impl( &["format".as_ref(), "--diff".as_ref()], ); } - eprintln!("rerun tidy with `--extra-checks=py:fmt --bless` to reformat Python code"); + rerun_with_bless("py:fmt", "reformat Python code"); } // Rethrow error @@ -232,7 +242,7 @@ fn check_impl( let args = merge_args(&cfg_args_clang_format, &file_args_clang_format); let res = py_runner(py_path.as_ref().unwrap(), false, None, "clang-format", &args); - if res.is_err() && show_diff { + if res.is_err() && show_diff && !bless { eprintln!("\nclang-format linting failed! Printing diff suggestions:"); let mut cfg_args_clang_format_diff = cfg_args.clone(); @@ -272,6 +282,7 @@ fn check_impl( ); } } + rerun_with_bless("cpp:fmt", "reformat C++ code"); } // Rethrow error res?; @@ -302,7 +313,11 @@ fn check_impl( } else { eprintln!("spellchecking files"); } - spellcheck_runner(root_path, &outdir, &cargo, &args)?; + let res = spellcheck_runner(root_path, &outdir, &cargo, &args); + if res.is_err() { + rerun_with_bless("spellcheck", "fix typos"); + } + res?; } if js_lint || js_typecheck { @@ -315,7 +330,11 @@ fn check_impl( } else { eprintln!("linting javascript files and applying suggestions"); } - rustdoc_js::lint(outdir, librustdoc_path, tools_path, bless)?; + let res = rustdoc_js::lint(outdir, librustdoc_path, tools_path, bless); + if res.is_err() { + rerun_with_bless("js:lint", "apply eslint suggestions"); + } + res?; rustdoc_js::es_check(outdir, librustdoc_path)?; } From 8222f7d95bee2b91bddfd87a43949e4583047363 Mon Sep 17 00:00:00 2001 From: binarycat Date: Mon, 8 Sep 2025 08:56:12 -0500 Subject: [PATCH 259/682] tidy extra checks: use Error::FailedCheck correctly --- src/tools/tidy/src/extra_checks/rustdoc_js.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/tidy/src/extra_checks/rustdoc_js.rs b/src/tools/tidy/src/extra_checks/rustdoc_js.rs index a6c66b8be802..5137e6183672 100644 --- a/src/tools/tidy/src/extra_checks/rustdoc_js.rs +++ b/src/tools/tidy/src/extra_checks/rustdoc_js.rs @@ -57,7 +57,7 @@ fn run_eslint( if exit_status.success() { return Ok(()); } - Err(super::Error::FailedCheck("eslint command failed")) + Err(super::Error::FailedCheck("eslint")) } Err(error) => Err(super::Error::Generic(format!("eslint command failed: {error:?}"))), } @@ -94,7 +94,7 @@ pub(super) fn typecheck(outdir: &Path, librustdoc_path: &Path) -> Result<(), sup if exit_status.success() { return Ok(()); } - Err(super::Error::FailedCheck("tsc command failed")) + Err(super::Error::FailedCheck("tsc")) } Err(error) => Err(super::Error::Generic(format!("tsc command failed: {error:?}"))), } @@ -112,7 +112,7 @@ pub(super) fn es_check(outdir: &Path, librustdoc_path: &Path) -> Result<(), supe if exit_status.success() { return Ok(()); } - Err(super::Error::FailedCheck("es-check command failed")) + Err(super::Error::FailedCheck("es-check")) } Err(error) => Err(super::Error::Generic(format!("es-check command failed: {error:?}"))), } From ef95c63816bcc6f217601abf308b1b1561340731 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Mon, 8 Sep 2025 14:08:26 +0000 Subject: [PATCH 260/682] disable core dumps for panic-uninitialized-zeroed That test causes a large amount of crashes. If a system has a /proc/sys/kernel/core_pattern that uploads core dumps enabled, it will take a long time to complete. Set dumpable to 0 to avoid that. Before: $ time ./panic-uninitialized-zeroed real 0m47.457s user 0m0.023s sys 0m0.021s After: $ ./panic-uninitialized-zeroed real 0m0.029s user 0m0.019s sys 0m0.010s --- .../ui/intrinsics/panic-uninitialized-zeroed.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/ui/intrinsics/panic-uninitialized-zeroed.rs b/tests/ui/intrinsics/panic-uninitialized-zeroed.rs index cdf8aa854826..db2b4a6ad20a 100644 --- a/tests/ui/intrinsics/panic-uninitialized-zeroed.rs +++ b/tests/ui/intrinsics/panic-uninitialized-zeroed.rs @@ -5,9 +5,10 @@ //@ [strict]compile-flags: -Zstrict-init-checks //@ needs-subprocess //@ ignore-backends: gcc +//@ edition:2024 #![allow(deprecated, invalid_value)] -#![feature(never_type)] +#![feature(never_type, rustc_private)] use std::{ mem::{self, MaybeUninit, ManuallyDrop}, @@ -15,6 +16,9 @@ use std::{ num, }; +#[cfg(target_os = "linux")] +extern crate libc; + #[allow(dead_code)] struct Foo { x: u8, @@ -108,6 +112,17 @@ fn test_panic_msg_only_if_strict(op: impl (FnOnce() -> T) + 'static, msg: &st fn main() { unsafe { + #[cfg(target_os = "linux")] + { + // This test causes a large amount of crashes. If a system + // has a /proc/sys/kernel/core_pattern that uploads core dumps enabled, + // it will take a long time to complete. Set dumpable to 0 to avoid that. + if libc::prctl(libc::PR_SET_DUMPABLE, 0) < 0 { + let err = std::io::Error::last_os_error(); + panic!("failed to disable core dumps {err:?}"); + } + } + // Uninhabited types test_panic_msg( || mem::uninitialized::(), From a21866a563836da1c47c1088f0e6085514c9595a Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Mon, 8 Sep 2025 15:56:56 +0100 Subject: [PATCH 261/682] Remove support for register_attr This was removed in rustc in 2022: https://github.com/rust-lang/rust/pull/101123 Closes #20525. --- .../crates/hir-def/src/nameres.rs | 9 ----- .../hir-def/src/nameres/attr_resolution.rs | 9 +---- .../crates/hir-def/src/nameres/collector.rs | 6 --- src/tools/rust-analyzer/crates/hir/src/lib.rs | 38 ++++--------------- .../crates/hir/src/source_analyzer.rs | 3 +- .../rust-analyzer/crates/ide-db/src/defs.rs | 6 +-- .../src/handlers/macro_error.rs | 5 +-- 7 files changed, 14 insertions(+), 62 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs index bf6fc15b7d19..7d5e627964eb 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -192,8 +192,6 @@ struct DefMapCrateData { exported_derives: FxHashMap>, fn_proc_macro_mapping: FxHashMap, - /// Custom attributes registered with `#![register_attr]`. - registered_attrs: Vec, /// Custom tool modules registered with `#![register_tool]`. registered_tools: Vec, /// Unstable features of Rust enabled with `#![feature(A, B)]`. @@ -212,7 +210,6 @@ impl DefMapCrateData { Self { exported_derives: FxHashMap::default(), fn_proc_macro_mapping: FxHashMap::default(), - registered_attrs: Vec::new(), registered_tools: PREDEFINED_TOOLS.iter().map(|it| Symbol::intern(it)).collect(), unstable_features: FxHashSet::default(), rustc_coherence_is_core: false, @@ -227,7 +224,6 @@ impl DefMapCrateData { let Self { exported_derives, fn_proc_macro_mapping, - registered_attrs, registered_tools, unstable_features, rustc_coherence_is_core: _, @@ -238,7 +234,6 @@ impl DefMapCrateData { } = self; exported_derives.shrink_to_fit(); fn_proc_macro_mapping.shrink_to_fit(); - registered_attrs.shrink_to_fit(); registered_tools.shrink_to_fit(); unstable_features.shrink_to_fit(); } @@ -529,10 +524,6 @@ impl DefMap { &self.data.registered_tools } - pub fn registered_attrs(&self) -> &[Symbol] { - &self.data.registered_attrs - } - pub fn is_unstable_feature_enabled(&self, feature: &Symbol) -> bool { self.data.unstable_features.contains(feature) } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs index e7e96804ae73..2f56d608fcbf 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs @@ -90,13 +90,8 @@ impl DefMap { return true; } - if segments.len() == 1 { - if find_builtin_attr_idx(name).is_some() { - return true; - } - if self.data.registered_attrs.iter().any(pred) { - return true; - } + if segments.len() == 1 && find_builtin_attr_idx(name).is_some() { + return true; } } false diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index 0f84728dcb4a..a2ce53835651 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -298,12 +298,6 @@ impl<'db> DefCollector<'db> { ); crate_data.unstable_features.extend(features); } - () if *attr_name == sym::register_attr => { - if let Some(ident) = attr.single_ident_value() { - crate_data.registered_attrs.push(ident.sym.clone()); - cov_mark::hit!(register_attr); - } - } () if *attr_name == sym::register_tool => { if let Some(ident) = attr.single_ident_value() { crate_data.registered_tools.push(ident.sym.clone()); diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 6610375451dd..6eb8a8bf60fd 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -4060,49 +4060,25 @@ impl DeriveHelper { } } -// FIXME: Wrong name? This is could also be a registered attribute #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct BuiltinAttr { - krate: Option, idx: u32, } impl BuiltinAttr { - // FIXME: consider crates\hir_def\src\nameres\attr_resolution.rs? - pub(crate) fn by_name(db: &dyn HirDatabase, krate: Crate, name: &str) -> Option { - if let builtin @ Some(_) = Self::builtin(name) { - return builtin; - } - let idx = crate_def_map(db, krate.id) - .registered_attrs() - .iter() - .position(|it| it.as_str() == name)? as u32; - Some(BuiltinAttr { krate: Some(krate.id), idx }) - } - fn builtin(name: &str) -> Option { hir_expand::inert_attr_macro::find_builtin_attr_idx(&Symbol::intern(name)) - .map(|idx| BuiltinAttr { krate: None, idx: idx as u32 }) + .map(|idx| BuiltinAttr { idx: idx as u32 }) } - pub fn name(&self, db: &dyn HirDatabase) -> Name { - match self.krate { - Some(krate) => Name::new_symbol_root( - crate_def_map(db, krate).registered_attrs()[self.idx as usize].clone(), - ), - None => Name::new_symbol_root(Symbol::intern( - hir_expand::inert_attr_macro::INERT_ATTRIBUTES[self.idx as usize].name, - )), - } + pub fn name(&self) -> Name { + Name::new_symbol_root(Symbol::intern( + hir_expand::inert_attr_macro::INERT_ATTRIBUTES[self.idx as usize].name, + )) } - pub fn template(&self, _: &dyn HirDatabase) -> Option { - match self.krate { - Some(_) => None, - None => { - Some(hir_expand::inert_attr_macro::INERT_ATTRIBUTES[self.idx as usize].template) - } - } + pub fn template(&self) -> Option { + Some(hir_expand::inert_attr_macro::INERT_ATTRIBUTES[self.idx as usize].template) } } diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index dbc2539a9b91..539b25387aef 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -1062,8 +1062,7 @@ impl<'db> SourceAnalyzer<'db> { // in this case we have to check for inert/builtin attributes and tools and prioritize // resolution of attributes over other namespaces if let Some(name_ref) = path.as_single_name_ref() { - let builtin = - BuiltinAttr::by_name(db, self.resolver.krate().into(), &name_ref.text()); + let builtin = BuiltinAttr::builtin(&name_ref.text()); if builtin.is_some() { return builtin.map(|it| (PathResolution::BuiltinAttr(it), None)); } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs index 61a21ccf2f77..44ff9d6006d6 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs @@ -259,8 +259,8 @@ impl Definition { Definition::ExternCrateDecl(it) => it.docs_with_rangemap(db), Definition::BuiltinAttr(it) => { - let name = it.name(db); - let AttributeTemplate { word, list, name_value_str } = it.template(db)?; + let name = it.name(); + let AttributeTemplate { word, list, name_value_str } = it.template()?; let mut docs = "Valid forms are:".to_owned(); if word { format_to!(docs, "\n - #\\[{}]", name.display(db, display_target.edition)); @@ -348,7 +348,7 @@ impl Definition { Definition::Label(it) => it.name(db).display(db, display_target.edition).to_string(), Definition::ExternCrateDecl(it) => it.display(db, display_target).to_string(), Definition::BuiltinAttr(it) => { - format!("#[{}]", it.name(db).display(db, display_target.edition)) + format!("#[{}]", it.name().display(db, display_target.edition)) } Definition::ToolModule(it) => { it.name(db).display(db, display_target.edition).to_string() diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs index c39e00e178f8..6a1ecae65150 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs @@ -144,16 +144,13 @@ macro_rules! concat { () => {} } } #[test] - fn register_attr_and_tool() { - cov_mark::check!(register_attr); + fn register_tool() { cov_mark::check!(register_tool); check_diagnostics( r#" #![register_tool(tool)] -#![register_attr(attr)] #[tool::path] -#[attr] struct S; "#, ); From 207a01e88f05f028f7a6c0db0d324fbedb8178a4 Mon Sep 17 00:00:00 2001 From: joboet Date: Tue, 12 Aug 2025 18:37:07 +0200 Subject: [PATCH 262/682] std: make address resolution weirdness local to SGX --- library/std/src/io/error.rs | 3 + library/std/src/net/mod.rs | 21 ---- library/std/src/net/tcp.rs | 4 +- library/std/src/net/tcp/tests.rs | 2 +- library/std/src/net/udp.rs | 4 +- library/std/src/net/udp/tests.rs | 1 + library/std/src/sys/net/connection/mod.rs | 57 ++++++++++ library/std/src/sys/net/connection/sgx.rs | 81 ++++++++++---- .../connection/{socket.rs => socket/mod.rs} | 100 ++++++++++-------- .../std/src/sys/net/connection/uefi/mod.rs | 27 +++-- .../std/src/sys/net/connection/unsupported.rs | 10 +- library/std/src/sys/net/connection/wasip1.rs | 10 +- .../sys/net/connection/xous/tcplistener.rs | 24 +++-- .../src/sys/net/connection/xous/tcpstream.rs | 9 +- .../std/src/sys/net/connection/xous/udp.rs | 81 +++++++------- library/std/src/sys/net/mod.rs | 48 +-------- 16 files changed, 271 insertions(+), 211 deletions(-) create mode 100644 library/std/src/sys/net/connection/mod.rs rename library/std/src/sys/net/connection/{socket.rs => socket/mod.rs} (90%) diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 57a980d6acdb..21e82d43a800 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -95,6 +95,9 @@ impl Error { pub(crate) const ZERO_TIMEOUT: Self = const_error!(ErrorKind::InvalidInput, "cannot set a 0 duration timeout"); + + pub(crate) const NO_ADDRESSES: Self = + const_error!(ErrorKind::InvalidInput, "could not resolve to any addresses"); } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/net/mod.rs b/library/std/src/net/mod.rs index ddd3b68dd2d6..40f1a93e39de 100644 --- a/library/std/src/net/mod.rs +++ b/library/std/src/net/mod.rs @@ -34,7 +34,6 @@ pub use self::tcp::IntoIncoming; pub use self::tcp::{Incoming, TcpListener, TcpStream}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::udp::UdpSocket; -use crate::io::{self, ErrorKind}; mod ip_addr; mod socket_addr; @@ -67,23 +66,3 @@ pub enum Shutdown { #[stable(feature = "rust1", since = "1.0.0")] Both, } - -fn each_addr(addr: A, mut f: F) -> io::Result -where - F: FnMut(io::Result<&SocketAddr>) -> io::Result, -{ - let addrs = match addr.to_socket_addrs() { - Ok(addrs) => addrs, - Err(e) => return f(Err(e)), - }; - let mut last_err = None; - for addr in addrs { - match f(Ok(&addr)) { - Ok(l) => return Ok(l), - Err(e) => last_err = Some(e), - } - } - Err(last_err.unwrap_or_else(|| { - io::const_error!(ErrorKind::InvalidInput, "could not resolve to any addresses") - })) -} diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs index 10685b493194..ae50f531a716 100644 --- a/library/std/src/net/tcp.rs +++ b/library/std/src/net/tcp.rs @@ -167,7 +167,7 @@ impl TcpStream { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn connect(addr: A) -> io::Result { - super::each_addr(addr, net_imp::TcpStream::connect).map(TcpStream) + net_imp::TcpStream::connect(addr).map(TcpStream) } /// Opens a TCP connection to a remote host with a timeout. @@ -782,7 +782,7 @@ impl TcpListener { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn bind(addr: A) -> io::Result { - super::each_addr(addr, net_imp::TcpListener::bind).map(TcpListener) + net_imp::TcpListener::bind(addr).map(TcpListener) } /// Returns the local socket address of this listener. diff --git a/library/std/src/net/tcp/tests.rs b/library/std/src/net/tcp/tests.rs index 03003037b295..7c7ef7b2f701 100644 --- a/library/std/src/net/tcp/tests.rs +++ b/library/std/src/net/tcp/tests.rs @@ -1,5 +1,5 @@ use crate::io::prelude::*; -use crate::io::{BorrowedBuf, IoSlice, IoSliceMut}; +use crate::io::{BorrowedBuf, ErrorKind, IoSlice, IoSliceMut}; use crate::mem::MaybeUninit; use crate::net::test::{next_test_ip4, next_test_ip6}; use crate::net::*; diff --git a/library/std/src/net/udp.rs b/library/std/src/net/udp.rs index a97b3299774b..72e292e3d157 100644 --- a/library/std/src/net/udp.rs +++ b/library/std/src/net/udp.rs @@ -120,7 +120,7 @@ impl UdpSocket { /// [`Ipv4Addr::UNSPECIFIED`] or [`Ipv6Addr::UNSPECIFIED`]. #[stable(feature = "rust1", since = "1.0.0")] pub fn bind(addr: A) -> io::Result { - super::each_addr(addr, net_imp::UdpSocket::bind).map(UdpSocket) + net_imp::UdpSocket::bind(addr).map(UdpSocket) } /// Receives a single datagram message on the socket. On success, returns the number @@ -677,7 +677,7 @@ impl UdpSocket { /// on the platform. #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn connect(&self, addr: A) -> io::Result<()> { - super::each_addr(addr, |addr| self.0.connect(addr)) + self.0.connect(addr) } /// Sends data on the socket to the remote address to which it is connected. diff --git a/library/std/src/net/udp/tests.rs b/library/std/src/net/udp/tests.rs index 91da3135f97c..0638b36c54f9 100644 --- a/library/std/src/net/udp/tests.rs +++ b/library/std/src/net/udp/tests.rs @@ -1,3 +1,4 @@ +use crate::io::ErrorKind; use crate::net::test::{compare_ignore_zoneid, next_test_ip4, next_test_ip6}; use crate::net::*; use crate::sync::mpsc::channel; diff --git a/library/std/src/sys/net/connection/mod.rs b/library/std/src/sys/net/connection/mod.rs new file mode 100644 index 000000000000..7f9636a8ccfd --- /dev/null +++ b/library/std/src/sys/net/connection/mod.rs @@ -0,0 +1,57 @@ +cfg_select! { + any( + all(target_family = "unix", not(target_os = "l4re")), + target_os = "windows", + target_os = "hermit", + all(target_os = "wasi", target_env = "p2"), + target_os = "solid_asp3", + ) => { + mod socket; + pub use socket::*; + } + all(target_vendor = "fortanix", target_env = "sgx") => { + mod sgx; + pub use sgx::*; + } + all(target_os = "wasi", target_env = "p1") => { + mod wasip1; + pub use wasip1::*; + } + target_os = "xous" => { + mod xous; + pub use xous::*; + } + target_os = "uefi" => { + mod uefi; + pub use uefi::*; + } + _ => { + mod unsupported; + pub use unsupported::*; + } +} + +#[cfg_attr( + // Make sure that this is used on some platforms at least. + not(any(target_os = "linux", target_os = "windows")), + allow(dead_code) +)] +fn each_addr(addr: A, mut f: F) -> crate::io::Result +where + F: FnMut(&crate::net::SocketAddr) -> crate::io::Result, +{ + use crate::io::Error; + + let mut last_err = None; + for addr in addr.to_socket_addrs()? { + match f(&addr) { + Ok(l) => return Ok(l), + Err(e) => last_err = Some(e), + } + } + + match last_err { + Some(err) => Err(err), + None => Err(Error::NO_ADDRESSES), + } +} diff --git a/library/std/src/sys/net/connection/sgx.rs b/library/std/src/sys/net/connection/sgx.rs index 2389fd1bcb6c..9b54571997d0 100644 --- a/library/std/src/sys/net/connection/sgx.rs +++ b/library/std/src/sys/net/connection/sgx.rs @@ -1,3 +1,5 @@ +use crate::error; +use crate::fmt::{self, Write}; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs}; use crate::sync::Arc; @@ -5,7 +7,6 @@ use crate::sys::abi::usercalls; use crate::sys::fd::FileDesc; use crate::sys::{AsInner, FromInner, IntoInner, TryIntoInner, sgx_ineffective, unsupported}; use crate::time::Duration; -use crate::{error, fmt}; const DEFAULT_FAKE_TTL: u32 = 64; @@ -63,18 +64,52 @@ impl fmt::Debug for TcpStream { } } -fn io_err_to_addr(result: io::Result<&SocketAddr>) -> io::Result { - match result { - Ok(saddr) => Ok(saddr.to_string()), - // need to downcast twice because io::Error::into_inner doesn't return the original - // value if the conversion fails - Err(e) => { - if e.get_ref().and_then(|e| e.downcast_ref::()).is_some() { - Ok(e.into_inner().unwrap().downcast::().unwrap().host) - } else { - Err(e) +/// Converts each address in `addr` into a hostname. +/// +/// SGX doesn't support DNS resolution but rather accepts hostnames in +/// the same place as socket addresses. So, to make e.g. +/// ```rust +/// TcpStream::connect("example.com:80")` +/// ``` +/// work, the DNS lookup returns a special error (`NonIpSockAddr`) instead, +/// which contains the hostname being looked up. When `.to_socket_addrs()` +/// fails, we inspect the error and try recover the hostname from it. If that +/// succeeds, we thus continue with the hostname. +/// +/// This is a terrible hack and leads to buggy code. For instance, when users +/// use the result of `.to_socket_addrs()` in their own `ToSocketAddrs` +/// implementation to select from a list of possible URLs, the only URL used +/// will be that of the last item tried. +// FIXME: This is a terrible, terrible hack. Fixing this requires Fortanix to +// add a method for resolving addresses. +fn each_addr(addr: A, mut f: F) -> io::Result +where + F: FnMut(&str) -> io::Result, +{ + match addr.to_socket_addrs() { + Ok(addrs) => { + let mut last_err = None; + let mut encoded = String::new(); + for addr in addrs { + // Format the IP address as a string, reusing the buffer. + encoded.clear(); + write!(encoded, "{}", &addr).unwrap(); + + match f(&encoded) { + Ok(val) => return Ok(val), + Err(err) => last_err = Some(err), + } + } + + match last_err { + Some(err) => Err(err), + None => Err(io::Error::NO_ADDRESSES), } } + Err(err) => match err.get_ref().and_then(|e| e.downcast_ref::()) { + Some(NonIpSockAddr { host }) => f(host), + None => Err(err), + }, } } @@ -86,17 +121,18 @@ fn addr_to_sockaddr(addr: Option<&str>) -> io::Result { } impl TcpStream { - pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result { - let addr = io_err_to_addr(addr)?; - let (fd, local_addr, peer_addr) = usercalls::connect_stream(&addr)?; - Ok(TcpStream { inner: Socket::new(fd, local_addr), peer_addr: Some(peer_addr) }) + pub fn connect(addr: A) -> io::Result { + each_addr(addr, |addr| { + let (fd, local_addr, peer_addr) = usercalls::connect_stream(addr)?; + Ok(TcpStream { inner: Socket::new(fd, local_addr), peer_addr: Some(peer_addr) }) + }) } pub fn connect_timeout(addr: &SocketAddr, dur: Duration) -> io::Result { if dur == Duration::default() { return Err(io::Error::ZERO_TIMEOUT); } - Self::connect(Ok(addr)) // FIXME: ignoring timeout + Self::connect(addr) // FIXME: ignoring timeout } pub fn set_read_timeout(&self, dur: Option) -> io::Result<()> { @@ -247,10 +283,11 @@ impl fmt::Debug for TcpListener { } impl TcpListener { - pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result { - let addr = io_err_to_addr(addr)?; - let (fd, local_addr) = usercalls::bind_stream(&addr)?; - Ok(TcpListener { inner: Socket::new(fd, local_addr) }) + pub fn bind(addr: A) -> io::Result { + each_addr(addr, |addr| { + let (fd, local_addr) = usercalls::bind_stream(addr)?; + Ok(TcpListener { inner: Socket::new(fd, local_addr) }) + }) } pub fn socket_addr(&self) -> io::Result { @@ -316,7 +353,7 @@ impl FromInner for TcpListener { pub struct UdpSocket(!); impl UdpSocket { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { + pub fn bind(_: A) -> io::Result { unsupported() } @@ -436,7 +473,7 @@ impl UdpSocket { self.0 } - pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { + pub fn connect(&self, _: A) -> io::Result<()> { self.0 } } diff --git a/library/std/src/sys/net/connection/socket.rs b/library/std/src/sys/net/connection/socket/mod.rs similarity index 90% rename from library/std/src/sys/net/connection/socket.rs rename to library/std/src/sys/net/connection/socket/mod.rs index aa83ed65d4c2..564f2e3a01f3 100644 --- a/library/std/src/sys/net/connection/socket.rs +++ b/library/std/src/sys/net/connection/socket/mod.rs @@ -3,8 +3,11 @@ mod tests; use crate::ffi::{c_int, c_void}; use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; -use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, SocketAddrV4, SocketAddrV6}; +use crate::net::{ + Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs, +}; use crate::sys::common::small_c_string::run_with_cstr; +use crate::sys::net::connection::each_addr; use crate::sys_common::{AsInner, FromInner}; use crate::time::Duration; use crate::{cmp, fmt, mem, ptr}; @@ -342,14 +345,15 @@ pub struct TcpStream { } impl TcpStream { - pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result { - let addr = addr?; - + pub fn connect(addr: A) -> io::Result { init(); + return each_addr(addr, inner); - let sock = Socket::new(addr, c::SOCK_STREAM)?; - sock.connect(addr)?; - Ok(TcpStream { inner: sock }) + fn inner(addr: &SocketAddr) -> io::Result { + let sock = Socket::new(addr, c::SOCK_STREAM)?; + sock.connect(addr)?; + Ok(TcpStream { inner: sock }) + } } pub fn connect_timeout(addr: &SocketAddr, timeout: Duration) -> io::Result { @@ -512,48 +516,45 @@ pub struct TcpListener { } impl TcpListener { - pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result { - let addr = addr?; - + pub fn bind(addr: A) -> io::Result { init(); + return each_addr(addr, inner); - let sock = Socket::new(addr, c::SOCK_STREAM)?; + fn inner(addr: &SocketAddr) -> io::Result { + let sock = Socket::new(addr, c::SOCK_STREAM)?; - // On platforms with Berkeley-derived sockets, this allows to quickly - // rebind a socket, without needing to wait for the OS to clean up the - // previous one. - // - // On Windows, this allows rebinding sockets which are actively in use, - // which allows “socket hijacking”, so we explicitly don't set it here. - // https://docs.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse - #[cfg(not(windows))] - setsockopt(&sock, c::SOL_SOCKET, c::SO_REUSEADDR, 1 as c_int)?; + // On platforms with Berkeley-derived sockets, this allows to quickly + // rebind a socket, without needing to wait for the OS to clean up the + // previous one. + // + // On Windows, this allows rebinding sockets which are actively in use, + // which allows “socket hijacking”, so we explicitly don't set it here. + // https://docs.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse + #[cfg(not(windows))] + setsockopt(&sock, c::SOL_SOCKET, c::SO_REUSEADDR, 1 as c_int)?; - // Bind our new socket - let (addr, len) = socket_addr_to_c(addr); - cvt(unsafe { c::bind(sock.as_raw(), addr.as_ptr(), len as _) })?; + // Bind our new socket + let (addr, len) = socket_addr_to_c(addr); + cvt(unsafe { c::bind(sock.as_raw(), addr.as_ptr(), len as _) })?; - cfg_select! { - target_os = "horizon" => { + let backlog = if cfg!(target_os = "horizon") { // The 3DS doesn't support a big connection backlog. Sometimes // it allows up to about 37, but other times it doesn't even // accept 32. There may be a global limitation causing this. - let backlog = 20; - } - target_os = "haiku" => { + 20 + } else if cfg!(target_os = "haiku") { // Haiku does not support a queue length > 32 // https://github.com/haiku/haiku/blob/979a0bc487864675517fb2fab28f87dc8bf43041/headers/posix/sys/socket.h#L81 - let backlog = 32; - } - _ => { + 32 + } else { // The default for all other platforms - let backlog = 128; - } - } + 128 + }; - // Start listening - cvt(unsafe { c::listen(sock.as_raw(), backlog) })?; - Ok(TcpListener { inner: sock }) + // Start listening + cvt(unsafe { c::listen(sock.as_raw(), backlog) })?; + Ok(TcpListener { inner: sock }) + } } #[inline] @@ -639,15 +640,16 @@ pub struct UdpSocket { } impl UdpSocket { - pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result { - let addr = addr?; - + pub fn bind(addr: A) -> io::Result { init(); + return each_addr(addr, inner); - let sock = Socket::new(addr, c::SOCK_DGRAM)?; - let (addr, len) = socket_addr_to_c(addr); - cvt(unsafe { c::bind(sock.as_raw(), addr.as_ptr(), len as _) })?; - Ok(UdpSocket { inner: sock }) + fn inner(addr: &SocketAddr) -> io::Result { + let sock = Socket::new(addr, c::SOCK_DGRAM)?; + let (addr, len) = socket_addr_to_c(addr); + cvt(unsafe { c::bind(sock.as_raw(), addr.as_ptr(), len as _) })?; + Ok(UdpSocket { inner: sock }) + } } #[inline] @@ -822,9 +824,13 @@ impl UdpSocket { Ok(ret as usize) } - pub fn connect(&self, addr: io::Result<&SocketAddr>) -> io::Result<()> { - let (addr, len) = socket_addr_to_c(addr?); - cvt_r(|| unsafe { c::connect(self.inner.as_raw(), addr.as_ptr(), len) }).map(drop) + pub fn connect(&self, addr: A) -> io::Result<()> { + return each_addr(addr, |addr| inner(self, addr)); + + fn inner(this: &UdpSocket, addr: &SocketAddr) -> io::Result<()> { + let (addr, len) = socket_addr_to_c(addr); + cvt_r(|| unsafe { c::connect(this.inner.as_raw(), addr.as_ptr(), len) }).map(drop) + } } } diff --git a/library/std/src/sys/net/connection/uefi/mod.rs b/library/std/src/sys/net/connection/uefi/mod.rs index 16e3487a1747..003680428735 100644 --- a/library/std/src/sys/net/connection/uefi/mod.rs +++ b/library/std/src/sys/net/connection/uefi/mod.rs @@ -1,6 +1,7 @@ +use super::each_addr; use crate::fmt; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; +use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs}; use crate::sync::{Arc, Mutex}; use crate::sys::unsupported; use crate::time::Duration; @@ -15,13 +16,17 @@ pub struct TcpStream { } impl TcpStream { - pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result { - let inner = tcp::Tcp::connect(addr?, None)?; - Ok(Self { - inner, - read_timeout: Arc::new(Mutex::new(None)), - write_timeout: Arc::new(Mutex::new(None)), - }) + pub fn connect(addr: A) -> io::Result { + return each_addr(addr, inner); + + fn inner(addr: &SocketAddr) -> io::Result { + let inner = tcp::Tcp::connect(addr, None)?; + Ok(TcpStream { + inner, + read_timeout: Arc::new(Mutex::new(None)), + write_timeout: Arc::new(Mutex::new(None)), + }) + } } pub fn connect_timeout(addr: &SocketAddr, timeout: Duration) -> io::Result { @@ -145,7 +150,7 @@ pub struct TcpListener { } impl TcpListener { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { + pub fn bind(_: A) -> io::Result { unsupported() } @@ -195,7 +200,7 @@ impl fmt::Debug for TcpListener { pub struct UdpSocket(!); impl UdpSocket { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { + pub fn bind(_: A) -> io::Result { unsupported() } @@ -315,7 +320,7 @@ impl UdpSocket { self.0 } - pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { + pub fn connect(&self, _: A) -> io::Result<()> { self.0 } } diff --git a/library/std/src/sys/net/connection/unsupported.rs b/library/std/src/sys/net/connection/unsupported.rs index da2174396266..fbc86343272f 100644 --- a/library/std/src/sys/net/connection/unsupported.rs +++ b/library/std/src/sys/net/connection/unsupported.rs @@ -1,13 +1,13 @@ use crate::fmt; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; +use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs}; use crate::sys::unsupported; use crate::time::Duration; pub struct TcpStream(!); impl TcpStream { - pub fn connect(_: io::Result<&SocketAddr>) -> io::Result { + pub fn connect(_: A) -> io::Result { unsupported() } @@ -121,7 +121,7 @@ impl fmt::Debug for TcpStream { pub struct TcpListener(!); impl TcpListener { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { + pub fn bind(_: A) -> io::Result { unsupported() } @@ -171,7 +171,7 @@ impl fmt::Debug for TcpListener { pub struct UdpSocket(!); impl UdpSocket { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { + pub fn bind(_: A) -> io::Result { unsupported() } @@ -291,7 +291,7 @@ impl UdpSocket { self.0 } - pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { + pub fn connect(&self, _: A) -> io::Result<()> { self.0 } } diff --git a/library/std/src/sys/net/connection/wasip1.rs b/library/std/src/sys/net/connection/wasip1.rs index 951dc65e5b47..cdfa25c8a440 100644 --- a/library/std/src/sys/net/connection/wasip1.rs +++ b/library/std/src/sys/net/connection/wasip1.rs @@ -2,7 +2,7 @@ use crate::fmt; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; +use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs}; use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::sys::fd::WasiFd; use crate::sys::{err2io, unsupported}; @@ -60,7 +60,7 @@ impl FromRawFd for Socket { } impl TcpStream { - pub fn connect(_: io::Result<&SocketAddr>) -> io::Result { + pub fn connect(_: A) -> io::Result { unsupported() } @@ -212,7 +212,7 @@ pub struct TcpListener { } impl TcpListener { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { + pub fn bind(_: A) -> io::Result { unsupported() } @@ -316,7 +316,7 @@ pub struct UdpSocket { } impl UdpSocket { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { + pub fn bind(_: A) -> io::Result { unsupported() } @@ -436,7 +436,7 @@ impl UdpSocket { unsupported() } - pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { + pub fn connect(&self, _: A) -> io::Result<()> { unsupported() } diff --git a/library/std/src/sys/net/connection/xous/tcplistener.rs b/library/std/src/sys/net/connection/xous/tcplistener.rs index bdf1fcd9302b..8818ef2ca9a9 100644 --- a/library/std/src/sys/net/connection/xous/tcplistener.rs +++ b/library/std/src/sys/net/connection/xous/tcplistener.rs @@ -2,9 +2,10 @@ use core::convert::TryInto; use core::sync::atomic::{Atomic, AtomicBool, AtomicU16, AtomicUsize, Ordering}; use super::*; -use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; +use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs}; use crate::os::xous::services; use crate::sync::Arc; +use crate::sys::net::connection::each_addr; use crate::{fmt, io}; macro_rules! unimpl { @@ -25,16 +26,19 @@ pub struct TcpListener { } impl TcpListener { - pub fn bind(socketaddr: io::Result<&SocketAddr>) -> io::Result { - let mut addr = *socketaddr?; + pub fn bind(addr: A) -> io::Result { + return each_addr(addr, inner); - let fd = TcpListener::bind_inner(&mut addr)?; - return Ok(TcpListener { - fd: Arc::new(AtomicU16::new(fd)), - local: addr, - handle_count: Arc::new(AtomicUsize::new(1)), - nonblocking: Arc::new(AtomicBool::new(false)), - }); + fn inner(addr: &SocketAddr) -> io::Result { + let mut addr = *addr; + let fd = TcpListener::bind_inner(&mut addr)?; + Ok(TcpListener { + fd: Arc::new(AtomicU16::new(fd)), + local: addr, + handle_count: Arc::new(AtomicUsize::new(1)), + nonblocking: Arc::new(AtomicBool::new(false)), + }) + } } /// This returns the raw fd of a Listener, so that it can also be used by the diff --git a/library/std/src/sys/net/connection/xous/tcpstream.rs b/library/std/src/sys/net/connection/xous/tcpstream.rs index 545247674526..4df75453d1f4 100644 --- a/library/std/src/sys/net/connection/xous/tcpstream.rs +++ b/library/std/src/sys/net/connection/xous/tcpstream.rs @@ -3,9 +3,12 @@ use core::sync::atomic::{Atomic, AtomicBool, AtomicU32, AtomicUsize, Ordering}; use super::*; use crate::fmt; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::net::{IpAddr, Ipv4Addr, Shutdown, SocketAddr, SocketAddrV4, SocketAddrV6}; +use crate::net::{ + IpAddr, Ipv4Addr, Shutdown, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs, +}; use crate::os::xous::services; use crate::sync::Arc; +use crate::sys::net::connection::each_addr; use crate::time::Duration; macro_rules! unimpl { @@ -79,8 +82,8 @@ impl TcpStream { } } - pub fn connect(socketaddr: io::Result<&SocketAddr>) -> io::Result { - Self::connect_timeout(socketaddr?, Duration::ZERO) + pub fn connect(addr: A) -> io::Result { + each_addr(addr, |addr| Self::connect_timeout(addr, Duration::ZERO)) } pub fn connect_timeout(addr: &SocketAddr, duration: Duration) -> io::Result { diff --git a/library/std/src/sys/net/connection/xous/udp.rs b/library/std/src/sys/net/connection/xous/udp.rs index 2127d3267ed4..ce54ea3b79ef 100644 --- a/library/std/src/sys/net/connection/xous/udp.rs +++ b/library/std/src/sys/net/connection/xous/udp.rs @@ -3,9 +3,10 @@ use core::sync::atomic::{Atomic, AtomicUsize, Ordering}; use super::*; use crate::cell::Cell; -use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; +use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs}; use crate::os::xous::services; use crate::sync::Arc; +use crate::sys::net::connection::each_addr; use crate::time::Duration; use crate::{fmt, io}; @@ -32,40 +33,45 @@ pub struct UdpSocket { } impl UdpSocket { - pub fn bind(socketaddr: io::Result<&SocketAddr>) -> io::Result { - let addr = socketaddr?; - // Construct the request - let mut connect_request = ConnectRequest { raw: [0u8; 4096] }; + pub fn bind(addr: A) -> io::Result { + return each_addr(addr, inner); - // Serialize the StdUdpBind structure. This is done "manually" because we don't want to - // make an auto-serdes (like bincode or rkyv) crate a dependency of Xous. - let port_bytes = addr.port().to_le_bytes(); - connect_request.raw[0] = port_bytes[0]; - connect_request.raw[1] = port_bytes[1]; - match addr.ip() { - IpAddr::V4(addr) => { - connect_request.raw[2] = 4; - for (dest, src) in connect_request.raw[3..].iter_mut().zip(addr.octets()) { - *dest = src; + fn inner(addr: &SocketAddr) -> io::Result { + // Construct the request + let mut connect_request = ConnectRequest { raw: [0u8; 4096] }; + + // Serialize the StdUdpBind structure. This is done "manually" because we don't want to + // make an auto-serdes (like bincode or rkyv) crate a dependency of Xous. + let port_bytes = addr.port().to_le_bytes(); + connect_request.raw[0] = port_bytes[0]; + connect_request.raw[1] = port_bytes[1]; + match addr.ip() { + IpAddr::V4(addr) => { + connect_request.raw[2] = 4; + for (dest, src) in connect_request.raw[3..].iter_mut().zip(addr.octets()) { + *dest = src; + } + } + IpAddr::V6(addr) => { + connect_request.raw[2] = 6; + for (dest, src) in connect_request.raw[3..].iter_mut().zip(addr.octets()) { + *dest = src; + } } } - IpAddr::V6(addr) => { - connect_request.raw[2] = 6; - for (dest, src) in connect_request.raw[3..].iter_mut().zip(addr.octets()) { - *dest = src; - } - } - } - let response = crate::os::xous::ffi::lend_mut( - services::net_server(), - services::NetLendMut::StdUdpBind.into(), - &mut connect_request.raw, - 0, - 4096, - ); + let response = crate::os::xous::ffi::lend_mut( + services::net_server(), + services::NetLendMut::StdUdpBind.into(), + &mut connect_request.raw, + 0, + 4096, + ); + + let Ok((_, valid)) = response else { + return Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid response")); + }; - if let Ok((_, valid)) = response { // The first four bytes should be zero upon success, and will be nonzero // for an error. let response = connect_request.raw; @@ -87,8 +93,9 @@ impl UdpSocket { )); } } + let fd = response[1] as u16; - return Ok(UdpSocket { + Ok(UdpSocket { fd, local: *addr, remote: Cell::new(None), @@ -96,9 +103,8 @@ impl UdpSocket { write_timeout: Cell::new(0), handle_count: Arc::new(AtomicUsize::new(1)), nonblocking: Cell::new(false), - }); + }) } - Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid response")) } pub fn peer_addr(&self) -> io::Result { @@ -198,10 +204,11 @@ impl UdpSocket { self.peek_from(buf).map(|(len, _addr)| len) } - pub fn connect(&self, maybe_addr: io::Result<&SocketAddr>) -> io::Result<()> { - let addr = maybe_addr?; - self.remote.set(Some(*addr)); - Ok(()) + pub fn connect(&self, addr: A) -> io::Result<()> { + each_addr(addr, |addr| { + self.remote.set(Some(*addr)); + Ok(()) + }) } pub fn send(&self, buf: &[u8]) -> io::Result { diff --git a/library/std/src/sys/net/mod.rs b/library/std/src/sys/net/mod.rs index 5df1fe138ab2..dffc4ea7f81a 100644 --- a/library/std/src/sys/net/mod.rs +++ b/library/std/src/sys/net/mod.rs @@ -1,46 +1,4 @@ -cfg_select! { - any( - all(target_family = "unix", not(target_os = "l4re")), - target_os = "windows", - target_os = "hermit", - all(target_os = "wasi", target_env = "p2"), - target_os = "solid_asp3", - ) => { - mod connection { - mod socket; - pub use socket::*; - } - } - all(target_vendor = "fortanix", target_env = "sgx") => { - mod connection { - mod sgx; - pub use sgx::*; - } - } - all(target_os = "wasi", target_env = "p1") => { - mod connection { - mod wasip1; - pub use wasip1::*; - } - } - target_os = "xous" => { - mod connection { - mod xous; - pub use xous::*; - } - } - target_os = "uefi" => { - mod connection { - mod uefi; - pub use uefi::*; - } - } - _ => { - mod connection { - mod unsupported; - pub use unsupported::*; - } - } -} - +/// This module contains the implementations of `TcpStream`, `TcpListener` and +/// `UdpSocket` as well as related functionality like DNS resolving. +mod connection; pub use connection::*; From 4b15dd5a84742f9e7641b62e490765e0c1cb415c Mon Sep 17 00:00:00 2001 From: joboet Date: Fri, 29 Aug 2025 18:01:02 +0200 Subject: [PATCH 263/682] std: move `thread` into `sys` (rename only) --- library/std/src/sys/{pal/hermit/thread.rs => thread/hermit.rs} | 0 library/std/src/sys/{pal/sgx/thread.rs => thread/sgx.rs} | 0 library/std/src/sys/{pal/itron/thread.rs => thread/solid.rs} | 0 library/std/src/sys/{pal/teeos/thread.rs => thread/teeos.rs} | 0 library/std/src/sys/{pal/uefi/thread.rs => thread/uefi.rs} | 0 library/std/src/sys/{pal/unix/thread.rs => thread/unix.rs} | 0 .../src/sys/{pal/unsupported/thread.rs => thread/unsupported.rs} | 0 library/std/src/sys/{pal/wasip1/thread.rs => thread/wasip1.rs} | 0 library/std/src/sys/{pal/wasip2/thread.rs => thread/wasip2.rs} | 0 .../std/src/sys/{pal/wasm/atomics/thread.rs => thread/wasm.rs} | 0 library/std/src/sys/{pal/windows/thread.rs => thread/windows.rs} | 0 library/std/src/sys/{pal/xous/thread.rs => thread/xous.rs} | 0 12 files changed, 0 insertions(+), 0 deletions(-) rename library/std/src/sys/{pal/hermit/thread.rs => thread/hermit.rs} (100%) rename library/std/src/sys/{pal/sgx/thread.rs => thread/sgx.rs} (100%) rename library/std/src/sys/{pal/itron/thread.rs => thread/solid.rs} (100%) rename library/std/src/sys/{pal/teeos/thread.rs => thread/teeos.rs} (100%) rename library/std/src/sys/{pal/uefi/thread.rs => thread/uefi.rs} (100%) rename library/std/src/sys/{pal/unix/thread.rs => thread/unix.rs} (100%) rename library/std/src/sys/{pal/unsupported/thread.rs => thread/unsupported.rs} (100%) rename library/std/src/sys/{pal/wasip1/thread.rs => thread/wasip1.rs} (100%) rename library/std/src/sys/{pal/wasip2/thread.rs => thread/wasip2.rs} (100%) rename library/std/src/sys/{pal/wasm/atomics/thread.rs => thread/wasm.rs} (100%) rename library/std/src/sys/{pal/windows/thread.rs => thread/windows.rs} (100%) rename library/std/src/sys/{pal/xous/thread.rs => thread/xous.rs} (100%) diff --git a/library/std/src/sys/pal/hermit/thread.rs b/library/std/src/sys/thread/hermit.rs similarity index 100% rename from library/std/src/sys/pal/hermit/thread.rs rename to library/std/src/sys/thread/hermit.rs diff --git a/library/std/src/sys/pal/sgx/thread.rs b/library/std/src/sys/thread/sgx.rs similarity index 100% rename from library/std/src/sys/pal/sgx/thread.rs rename to library/std/src/sys/thread/sgx.rs diff --git a/library/std/src/sys/pal/itron/thread.rs b/library/std/src/sys/thread/solid.rs similarity index 100% rename from library/std/src/sys/pal/itron/thread.rs rename to library/std/src/sys/thread/solid.rs diff --git a/library/std/src/sys/pal/teeos/thread.rs b/library/std/src/sys/thread/teeos.rs similarity index 100% rename from library/std/src/sys/pal/teeos/thread.rs rename to library/std/src/sys/thread/teeos.rs diff --git a/library/std/src/sys/pal/uefi/thread.rs b/library/std/src/sys/thread/uefi.rs similarity index 100% rename from library/std/src/sys/pal/uefi/thread.rs rename to library/std/src/sys/thread/uefi.rs diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/thread/unix.rs similarity index 100% rename from library/std/src/sys/pal/unix/thread.rs rename to library/std/src/sys/thread/unix.rs diff --git a/library/std/src/sys/pal/unsupported/thread.rs b/library/std/src/sys/thread/unsupported.rs similarity index 100% rename from library/std/src/sys/pal/unsupported/thread.rs rename to library/std/src/sys/thread/unsupported.rs diff --git a/library/std/src/sys/pal/wasip1/thread.rs b/library/std/src/sys/thread/wasip1.rs similarity index 100% rename from library/std/src/sys/pal/wasip1/thread.rs rename to library/std/src/sys/thread/wasip1.rs diff --git a/library/std/src/sys/pal/wasip2/thread.rs b/library/std/src/sys/thread/wasip2.rs similarity index 100% rename from library/std/src/sys/pal/wasip2/thread.rs rename to library/std/src/sys/thread/wasip2.rs diff --git a/library/std/src/sys/pal/wasm/atomics/thread.rs b/library/std/src/sys/thread/wasm.rs similarity index 100% rename from library/std/src/sys/pal/wasm/atomics/thread.rs rename to library/std/src/sys/thread/wasm.rs diff --git a/library/std/src/sys/pal/windows/thread.rs b/library/std/src/sys/thread/windows.rs similarity index 100% rename from library/std/src/sys/pal/windows/thread.rs rename to library/std/src/sys/thread/windows.rs diff --git a/library/std/src/sys/pal/xous/thread.rs b/library/std/src/sys/thread/xous.rs similarity index 100% rename from library/std/src/sys/pal/xous/thread.rs rename to library/std/src/sys/thread/xous.rs From bb5b153b7bccab4347d1a4deead6dcc9ca400a34 Mon Sep 17 00:00:00 2001 From: Ryan Mehri Date: Mon, 8 Sep 2025 11:12:57 -0400 Subject: [PATCH 264/682] feat: support navigation on primitives --- .../rust-analyzer/crates/ide-db/src/defs.rs | 2 +- .../crates/ide/src/goto_definition.rs | 27 +++++++++++++++++-- .../crates/ide/src/hover/render.rs | 15 +---------- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs index 61a21ccf2f77..43d4611b18ac 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs @@ -363,7 +363,7 @@ impl Definition { } } -fn find_std_module( +pub fn find_std_module( famous_defs: &FamousDefs<'_, '_>, name: &str, edition: Edition, diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index f768d4b68f46..002f5b3fe8f4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -6,12 +6,13 @@ use crate::{ navigation_target::{self, ToNav}, }; use hir::{ - AsAssocItem, AssocItem, CallableKind, FileRange, HasCrate, InFile, ModuleDef, Semantics, sym, + AsAssocItem, AssocItem, CallableKind, FileRange, HasCrate, InFile, ModuleDef, PathResolution, + Semantics, sym, }; use ide_db::{ RootDatabase, SymbolKind, base_db::{AnchoredPath, SourceDatabase}, - defs::{Definition, IdentClass}, + defs::{Definition, IdentClass, find_std_module}, famous_defs::FamousDefs, helpers::pick_best_token, }; @@ -90,6 +91,9 @@ pub(crate) fn goto_definition( if let Some(navs) = find_definition_for_known_blanket_dual_impls(sema, &token.value) { return Some(navs); } + if let Some(navs) = find_definition_for_builtin_types(sema, &token.value, edition) { + return Some(navs); + } let parent = token.value.parent()?; @@ -204,6 +208,25 @@ fn find_definition_for_known_blanket_dual_impls( Some(def_to_nav(sema.db, def)) } +// If the token is a builtin type search the definition from the rustdoc module shims. +fn find_definition_for_builtin_types( + sema: &Semantics<'_, RootDatabase>, + original_token: &SyntaxToken, + edition: Edition, +) -> Option> { + let path = original_token.parent_ancestors().find_map(ast::Path::cast)?; + let res = sema.resolve_path(&path)?; + let PathResolution::Def(ModuleDef::BuiltinType(builtin)) = res else { + return None; + }; + + let fd = FamousDefs(sema, sema.scope(path.syntax())?.krate()); + let primitive_mod = format!("prim_{}", builtin.name().display(fd.0.db, edition)); + let doc_owner = find_std_module(&fd, &primitive_mod, edition)?; + + Some(def_to_nav(sema.db, doc_owner.into())) +} + fn try_lookup_include_path( sema: &Semantics<'_, RootDatabase>, token: InFile, diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index 290ee80984ed..2adafbb7af6f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -10,7 +10,7 @@ use hir::{ }; use ide_db::{ RootDatabase, - defs::Definition, + defs::{Definition, find_std_module}, documentation::{DocsRangeMap, HasDocs}, famous_defs::FamousDefs, generated::lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES}, @@ -1160,19 +1160,6 @@ fn markup( } } -fn find_std_module( - famous_defs: &FamousDefs<'_, '_>, - name: &str, - edition: Edition, -) -> Option { - let db = famous_defs.0.db; - let std_crate = famous_defs.std()?; - let std_root_module = std_crate.root_module(); - std_root_module.children(db).find(|module| { - module.name(db).is_some_and(|module| module.display(db, edition).to_string() == name) - }) -} - fn render_memory_layout( config: Option, layout: impl FnOnce() -> Result, From e656e52ccb33f92ca8bfd7ad78ee5b028ff61a62 Mon Sep 17 00:00:00 2001 From: IoaNNUwU Date: Tue, 2 Sep 2025 16:15:23 +0200 Subject: [PATCH 265/682] Suggest examples of format specifiers in error messages --- compiler/rustc_builtin_macros/src/format.rs | 27 ++++++++- .../missing-format-specifiers-issue-68293.rs | 34 +++++++++++ ...ssing-format-specifiers-issue-68293.stderr | 59 +++++++++++++++++++ 3 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 tests/ui/suggestions/missing-format-specifiers-issue-68293.rs create mode 100644 tests/ui/suggestions/missing-format-specifiers-issue-68293.stderr diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 6415e55e0b03..ce9bda10721a 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -758,8 +758,31 @@ fn report_missing_placeholders( check_foreign!(shell); } } - if !found_foreign && unused.len() == 1 { - diag.span_label(fmt_span, "formatting specifier missing"); + if !found_foreign { + if unused.len() == 1 { + diag.span_label(fmt_span, "formatting specifier missing"); + } + if used.iter().all(|used| !used) { + diag.note("format specifiers use curly braces: `{}`"); + } + + let mut suggest_fixed_fmt = format!("\"{}", &fmt_str[..fmt_str.len() - 1]); + for _ in &unused { + suggest_fixed_fmt.push_str("{}"); + } + suggest_fixed_fmt.push('"'); + + let suggest_fmt_count = if unused.len() == 1 { + "consider adding format specifier".to_string() + } else { + format!("consider adding {} format specifiers", unused.len()) + }; + diag.span_suggestion_verbose( + fmt_span, + suggest_fmt_count, + suggest_fixed_fmt, + Applicability::MaybeIncorrect, + ); } diag.emit(); diff --git a/tests/ui/suggestions/missing-format-specifiers-issue-68293.rs b/tests/ui/suggestions/missing-format-specifiers-issue-68293.rs new file mode 100644 index 000000000000..e1f8710db5a3 --- /dev/null +++ b/tests/ui/suggestions/missing-format-specifiers-issue-68293.rs @@ -0,0 +1,34 @@ +fn no_format_specifier_two_unused_args() { + println!("Hello", "World"); + //~^ ERROR argument never used + //~| NOTE formatting specifier missing + //~| NOTE format specifiers use curly braces: `{}` + //~| NOTE argument never used +} + +fn no_format_specifier_multiple_unused_args() { + println!("list: ", 1, 2, 3); + //~^ ERROR multiple unused formatting arguments + //~| NOTE multiple missing formatting specifiers + //~| NOTE format specifiers use curly braces: `{}` + //~| NOTE argument never used + //~| NOTE argument never used + //~| NOTE argument never used +} + +fn missing_format_specifiers_one_unused_arg() { + println!("list: {}, {}", 1, 2, 3); + //~^ ERROR argument never used + //~| NOTE formatting specifier missing + //~| NOTE argument never used +} + +fn missing_format_specifiers_multiple_unused_args() { + println!("list: {}", 1, 2, 3); + //~^ ERROR multiple unused formatting arguments + //~| NOTE multiple missing formatting specifiers + //~| NOTE argument never used + //~| NOTE argument never used +} + +fn main() { } diff --git a/tests/ui/suggestions/missing-format-specifiers-issue-68293.stderr b/tests/ui/suggestions/missing-format-specifiers-issue-68293.stderr new file mode 100644 index 000000000000..654c8b0712a4 --- /dev/null +++ b/tests/ui/suggestions/missing-format-specifiers-issue-68293.stderr @@ -0,0 +1,59 @@ +error: argument never used + --> $DIR/missing-format-specifiers-issue-68293.rs:2:23 + | +LL | println!("Hello", "World"); + | ------- ^^^^^^^ argument never used + | | + | formatting specifier missing + | + = note: format specifiers use curly braces: `{}` +help: consider adding format specifier + | +LL | println!("Hello{}", "World"); + | ++ + +error: multiple unused formatting arguments + --> $DIR/missing-format-specifiers-issue-68293.rs:10:24 + | +LL | println!("list: ", 1, 2, 3); + | -------- ^ ^ ^ argument never used + | | | | + | | | argument never used + | | argument never used + | multiple missing formatting specifiers + | + = note: format specifiers use curly braces: `{}` +help: consider adding 3 format specifiers + | +LL | println!("list: {}{}{}", 1, 2, 3); + | ++++++ + +error: argument never used + --> $DIR/missing-format-specifiers-issue-68293.rs:20:36 + | +LL | println!("list: {}, {}", 1, 2, 3); + | -------------- ^ argument never used + | | + | formatting specifier missing + | +help: consider adding format specifier + | +LL | println!("list: {}, {}{}", 1, 2, 3); + | ++ + +error: multiple unused formatting arguments + --> $DIR/missing-format-specifiers-issue-68293.rs:27:29 + | +LL | println!("list: {}", 1, 2, 3); + | ---------- ^ ^ argument never used + | | | + | | argument never used + | multiple missing formatting specifiers + | +help: consider adding 2 format specifiers + | +LL | println!("list: {}{}{}", 1, 2, 3); + | ++++ + +error: aborting due to 4 previous errors + From 1e733b389192b48110779bc1cd8edcaed7c43b0e Mon Sep 17 00:00:00 2001 From: IoaNNUwU Date: Tue, 2 Sep 2025 23:53:54 +0200 Subject: [PATCH 266/682] Implement better suggestions based on additional tests and other code paths --- compiler/rustc_builtin_macros/src/format.rs | 58 ++++++++++++------- tests/ui/fmt/ifmt-bad-arg.stderr | 14 +++++ tests/ui/macros/format-unused-lables.stderr | 18 ++++++ tests/ui/mir/unsized-extern-static.stderr | 6 ++ .../missing-format-specifiers-issue-68293.rs | 1 + ...ssing-format-specifiers-issue-68293.stderr | 10 +--- 6 files changed, 78 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index ce9bda10721a..e5f73188dbdf 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -565,6 +565,7 @@ fn make_format_args( &used, &args, &pieces, + &invalid_refs, detect_foreign_fmt, str_style, fmt_str, @@ -645,6 +646,7 @@ fn report_missing_placeholders( used: &[bool], args: &FormatArguments, pieces: &[parse::Piece<'_>], + invalid_refs: &[(usize, Option, PositionUsedAs, FormatArgPositionKind)], detect_foreign_fmt: bool, str_style: Option, fmt_str: &str, @@ -758,31 +760,47 @@ fn report_missing_placeholders( check_foreign!(shell); } } - if !found_foreign { - if unused.len() == 1 { - diag.span_label(fmt_span, "formatting specifier missing"); + if !found_foreign && unused.len() == 1 { + diag.span_label(fmt_span, "formatting specifier missing"); + } + + if !found_foreign && invalid_refs.is_empty() { + // Show example if user didn't use any format specifiers + let show_example = used.iter().all(|used| !used); + + if !show_example && unused.len() > 1 { + diag.note(format!("consider adding {} format specifiers", unused.len())); } - if used.iter().all(|used| !used) { + + let original_fmt_str = if fmt_str.len() >= 1 { &fmt_str[..fmt_str.len() - 1] } else { "" }; + + if show_example && unused.len() == 1 { diag.note("format specifiers use curly braces: `{}`"); + + diag.span_suggestion_verbose( + fmt_span, + "consider adding format specifier", + format!("\"{}{{}}\"", original_fmt_str), + Applicability::MaybeIncorrect, + ); } - let mut suggest_fixed_fmt = format!("\"{}", &fmt_str[..fmt_str.len() - 1]); - for _ in &unused { - suggest_fixed_fmt.push_str("{}"); - } - suggest_fixed_fmt.push('"'); + if show_example && unused.len() > 1 { + diag.note("format specifiers use curly braces: `{}`"); - let suggest_fmt_count = if unused.len() == 1 { - "consider adding format specifier".to_string() - } else { - format!("consider adding {} format specifiers", unused.len()) - }; - diag.span_suggestion_verbose( - fmt_span, - suggest_fmt_count, - suggest_fixed_fmt, - Applicability::MaybeIncorrect, - ); + let mut suggest_fixed_fmt = format!("\"{}", original_fmt_str); + for _ in &unused { + suggest_fixed_fmt.push_str("{}"); + } + suggest_fixed_fmt.push('"'); + + diag.span_suggestion_verbose( + fmt_span, + format!("consider adding {} format specifiers", unused.len()), + suggest_fixed_fmt, + Applicability::MaybeIncorrect, + ); + } } diag.emit(); diff --git a/tests/ui/fmt/ifmt-bad-arg.stderr b/tests/ui/fmt/ifmt-bad-arg.stderr index 4344aee83c2b..6ddc81729015 100644 --- a/tests/ui/fmt/ifmt-bad-arg.stderr +++ b/tests/ui/fmt/ifmt-bad-arg.stderr @@ -62,6 +62,12 @@ LL | format!("", 1, 2); | | | | | argument never used | multiple missing formatting specifiers + | + = note: format specifiers use curly braces: `{}` +help: consider adding 2 format specifiers + | +LL | format!("{}{}", 1, 2); + | ++++ error: argument never used --> $DIR/ifmt-bad-arg.rs:33:22 @@ -102,6 +108,12 @@ LL | format!("", foo=2); | -- ^ named argument never used | | | formatting specifier missing + | + = note: format specifiers use curly braces: `{}` +help: consider adding format specifier + | +LL | format!("{}", foo=2); + | ++ error: multiple unused formatting arguments --> $DIR/ifmt-bad-arg.rs:38:32 @@ -111,6 +123,8 @@ LL | format!("{} {}", 1, 2, foo=1, bar=2); | | | | | named argument never used | multiple missing formatting specifiers + | + = note: consider adding 2 format specifiers error: duplicate argument named `foo` --> $DIR/ifmt-bad-arg.rs:40:29 diff --git a/tests/ui/macros/format-unused-lables.stderr b/tests/ui/macros/format-unused-lables.stderr index fad87fa2aeea..1c5cdcc2346d 100644 --- a/tests/ui/macros/format-unused-lables.stderr +++ b/tests/ui/macros/format-unused-lables.stderr @@ -7,6 +7,12 @@ LL | println!("Test", 123, 456, 789); | | | argument never used | | argument never used | multiple missing formatting specifiers + | + = note: format specifiers use curly braces: `{}` +help: consider adding 3 format specifiers + | +LL | println!("Test{}{}{}", 123, 456, 789); + | ++++++ error: multiple unused formatting arguments --> $DIR/format-unused-lables.rs:6:9 @@ -19,6 +25,12 @@ LL | 456, | ^^^ argument never used LL | 789 | ^^^ argument never used + | + = note: format specifiers use curly braces: `{}` +help: consider adding 3 format specifiers + | +LL | println!("Test2{}{}{}", + | ++++++ error: named argument never used --> $DIR/format-unused-lables.rs:11:35 @@ -27,6 +39,12 @@ LL | println!("Some stuff", UNUSED="args"); | ------------ ^^^^^^ named argument never used | | | formatting specifier missing + | + = note: format specifiers use curly braces: `{}` +help: consider adding format specifier + | +LL | println!("Some stuff{}", UNUSED="args"); + | ++ error: multiple unused formatting arguments --> $DIR/format-unused-lables.rs:14:9 diff --git a/tests/ui/mir/unsized-extern-static.stderr b/tests/ui/mir/unsized-extern-static.stderr index 93aed3549d76..005dc59d78f9 100644 --- a/tests/ui/mir/unsized-extern-static.stderr +++ b/tests/ui/mir/unsized-extern-static.stderr @@ -5,6 +5,12 @@ LL | println!("C", unsafe { &symbol }); | --- ^^^^^^^^^^^^^^^^^^ argument never used | | | formatting specifier missing + | + = note: format specifiers use curly braces: `{}` +help: consider adding format specifier + | +LL | println!("C{}", unsafe { &symbol }); + | ++ error[E0277]: the size for values of type `[i8]` cannot be known at compilation time --> $DIR/unsized-extern-static.rs:6:5 diff --git a/tests/ui/suggestions/missing-format-specifiers-issue-68293.rs b/tests/ui/suggestions/missing-format-specifiers-issue-68293.rs index e1f8710db5a3..2f5e96294415 100644 --- a/tests/ui/suggestions/missing-format-specifiers-issue-68293.rs +++ b/tests/ui/suggestions/missing-format-specifiers-issue-68293.rs @@ -29,6 +29,7 @@ fn missing_format_specifiers_multiple_unused_args() { //~| NOTE multiple missing formatting specifiers //~| NOTE argument never used //~| NOTE argument never used + //~| NOTE consider adding 2 format specifiers } fn main() { } diff --git a/tests/ui/suggestions/missing-format-specifiers-issue-68293.stderr b/tests/ui/suggestions/missing-format-specifiers-issue-68293.stderr index 654c8b0712a4..961887a7e3fa 100644 --- a/tests/ui/suggestions/missing-format-specifiers-issue-68293.stderr +++ b/tests/ui/suggestions/missing-format-specifiers-issue-68293.stderr @@ -35,11 +35,6 @@ LL | println!("list: {}, {}", 1, 2, 3); | -------------- ^ argument never used | | | formatting specifier missing - | -help: consider adding format specifier - | -LL | println!("list: {}, {}{}", 1, 2, 3); - | ++ error: multiple unused formatting arguments --> $DIR/missing-format-specifiers-issue-68293.rs:27:29 @@ -50,10 +45,7 @@ LL | println!("list: {}", 1, 2, 3); | | argument never used | multiple missing formatting specifiers | -help: consider adding 2 format specifiers - | -LL | println!("list: {}{}{}", 1, 2, 3); - | ++++ + = note: consider adding 2 format specifiers error: aborting due to 4 previous errors From 266f8c9c0073745044a8eaeef5803b90090f5ad5 Mon Sep 17 00:00:00 2001 From: Daniel Paoliello Date: Wed, 3 Sep 2025 17:12:03 -0700 Subject: [PATCH 267/682] Update tracing, again --- Cargo.lock | 5 ++--- compiler/rustc_log/Cargo.toml | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 81f19668c24b..875086163f6a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5518,11 +5518,10 @@ checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ - "cfg-if", "pin-project-lite", "tracing-attributes", "tracing-core", diff --git a/compiler/rustc_log/Cargo.toml b/compiler/rustc_log/Cargo.toml index 2332ff9b323b..72f68d685cd3 100644 --- a/compiler/rustc_log/Cargo.toml +++ b/compiler/rustc_log/Cargo.toml @@ -5,13 +5,12 @@ edition = "2024" [dependencies] # tidy-alphabetical-start -# tracing > 0.1.37 have huge binary size / instructions regression -tracing = "=0.1.37" +tracing = "0.1.41" tracing-subscriber = { version = "0.3.3", default-features = false, features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] } tracing-tree = "0.3.1" # tidy-alphabetical-end [features] # tidy-alphabetical-start -max_level_info = ['tracing/max_level_info'] +max_level_info = ['tracing/max_level_info', 'tracing/release_max_level_info'] # tidy-alphabetical-end From 1656f6c668e0603fb31dac6b2b95f1cfb1be2402 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 6 Sep 2025 15:00:19 +0200 Subject: [PATCH 268/682] disallow c-variadic coroutines --- compiler/rustc_ast_passes/messages.ftl | 4 ++++ .../rustc_ast_passes/src/ast_validation.rs | 9 +++++++++ compiler/rustc_ast_passes/src/errors.rs | 12 ++++++++++++ tests/ui/c-variadic/not-async.rs | 7 +++++++ tests/ui/c-variadic/not-async.stderr | 19 +++++++++++++++++++ .../note-and-explain-ReVar-124973.rs | 3 ++- .../note-and-explain-ReVar-124973.stderr | 8 +++++++- 7 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 tests/ui/c-variadic/not-async.rs create mode 100644 tests/ui/c-variadic/not-async.stderr diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index e5f1fcdc4b40..68ac2a05a488 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -84,6 +84,10 @@ ast_passes_const_without_body = ast_passes_constraint_on_negative_bound = associated type constraints not allowed on negative bounds +ast_passes_coroutine_and_c_variadic = functions cannot be both `{$coroutine_kind}` and C-variadic + .const = `{$coroutine_kind}` because of this + .variadic = C-variadic because of this + ast_passes_equality_in_where = equality constraints are not yet supported in `where` clauses .label = not supported .suggestion = if `{$ident}` is an associated type you're trying to set, use the associated type binding syntax diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 538918a890d5..672c1e944188 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -685,6 +685,15 @@ impl<'a> AstValidator<'a> { }); } + if let Some(coroutine_kind) = sig.header.coroutine_kind { + self.dcx().emit_err(errors::CoroutineAndCVariadic { + spans: vec![coroutine_kind.span(), variadic_param.span], + coroutine_kind: coroutine_kind.as_str(), + coroutine_span: coroutine_kind.span(), + variadic_span: variadic_param.span, + }); + } + match fn_ctxt { FnCtxt::Foreign => return, FnCtxt::Free => match sig.header.ext { diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 476ed27a10e3..f8806ef21c16 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -659,6 +659,18 @@ pub(crate) struct ConstAndCVariadic { pub variadic_span: Span, } +#[derive(Diagnostic)] +#[diag(ast_passes_coroutine_and_c_variadic)] +pub(crate) struct CoroutineAndCVariadic { + #[primary_span] + pub spans: Vec, + pub coroutine_kind: &'static str, + #[label(ast_passes_const)] + pub coroutine_span: Span, + #[label(ast_passes_variadic)] + pub variadic_span: Span, +} + #[derive(Diagnostic)] #[diag(ast_passes_pattern_in_foreign, code = E0130)] // FIXME: deduplicate with rustc_lint (`BuiltinLintDiag::PatternsInFnsWithoutBody`) diff --git a/tests/ui/c-variadic/not-async.rs b/tests/ui/c-variadic/not-async.rs new file mode 100644 index 000000000000..45a7e1f8972b --- /dev/null +++ b/tests/ui/c-variadic/not-async.rs @@ -0,0 +1,7 @@ +//@ edition: 2021 +#![feature(c_variadic)] +#![crate_type = "lib"] + +async unsafe extern "C" fn cannot_be_async(x: isize, ...) {} +//~^ ERROR functions cannot be both `async` and C-variadic +//~| ERROR hidden type for `impl Future` captures lifetime that does not appear in bounds diff --git a/tests/ui/c-variadic/not-async.stderr b/tests/ui/c-variadic/not-async.stderr new file mode 100644 index 000000000000..b8caf0d8bd85 --- /dev/null +++ b/tests/ui/c-variadic/not-async.stderr @@ -0,0 +1,19 @@ +error: functions cannot be both `async` and C-variadic + --> $DIR/not-async.rs:5:1 + | +LL | async unsafe extern "C" fn cannot_be_async(x: isize, ...) {} + | ^^^^^ `async` because of this ^^^ C-variadic because of this + +error[E0700]: hidden type for `impl Future` captures lifetime that does not appear in bounds + --> $DIR/not-async.rs:5:59 + | +LL | async unsafe extern "C" fn cannot_be_async(x: isize, ...) {} + | --------------------------------------------------------- ^^ + | | + | opaque type defined here + | + = note: hidden type `{async fn body of cannot_be_async()}` captures lifetime `'_` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0700`. diff --git a/tests/ui/inference/note-and-explain-ReVar-124973.rs b/tests/ui/inference/note-and-explain-ReVar-124973.rs index f1e246456369..aa4b909fa76c 100644 --- a/tests/ui/inference/note-and-explain-ReVar-124973.rs +++ b/tests/ui/inference/note-and-explain-ReVar-124973.rs @@ -3,6 +3,7 @@ #![feature(c_variadic)] async unsafe extern "C" fn multiple_named_lifetimes<'a, 'b>(_: u8, ...) {} -//~^ ERROR hidden type for `impl Future` captures lifetime that does not appear in bounds +//~^ ERROR functions cannot be both `async` and C-variadic +//~| ERROR hidden type for `impl Future` captures lifetime that does not appear in bounds fn main() {} diff --git a/tests/ui/inference/note-and-explain-ReVar-124973.stderr b/tests/ui/inference/note-and-explain-ReVar-124973.stderr index 574f6508e4c7..964fbc4a4fb7 100644 --- a/tests/ui/inference/note-and-explain-ReVar-124973.stderr +++ b/tests/ui/inference/note-and-explain-ReVar-124973.stderr @@ -1,3 +1,9 @@ +error: functions cannot be both `async` and C-variadic + --> $DIR/note-and-explain-ReVar-124973.rs:5:1 + | +LL | async unsafe extern "C" fn multiple_named_lifetimes<'a, 'b>(_: u8, ...) {} + | ^^^^^ `async` because of this ^^^ C-variadic because of this + error[E0700]: hidden type for `impl Future` captures lifetime that does not appear in bounds --> $DIR/note-and-explain-ReVar-124973.rs:5:73 | @@ -8,6 +14,6 @@ LL | async unsafe extern "C" fn multiple_named_lifetimes<'a, 'b>(_: u8, ...) {} | = note: hidden type `{async fn body of multiple_named_lifetimes<'a, 'b>()}` captures lifetime `'_` -error: aborting due to 1 previous error +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0700`. From a093372e5e560b1a768971c2b87b306755f2c17d Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 6 Sep 2025 15:13:02 +0200 Subject: [PATCH 269/682] disallow c-variadic associated functions (for now) there is no reason this should not work, really, we're just cutting some scope for now --- compiler/rustc_ast_passes/messages.ftl | 2 ++ .../rustc_ast_passes/src/ast_validation.rs | 14 +++++++++----- compiler/rustc_ast_passes/src/errors.rs | 7 +++++++ .../feature-gates/feature-gate-c_variadic.rs | 8 ++++---- .../feature-gate-c_variadic.stderr | 12 ++++++------ .../variadic-ffi-semantic-restrictions.rs | 18 +++++++++--------- .../variadic-ffi-semantic-restrictions.stderr | 18 +++++++++--------- 7 files changed, 46 insertions(+), 33 deletions(-) diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 68ac2a05a488..6eb4e4bc4527 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -66,6 +66,8 @@ ast_passes_body_in_extern = incorrect `{$kind}` inside `extern` block ast_passes_bound_in_context = bounds on `type`s in {$ctx} have no effect +ast_passes_c_variadic_associated_function = associated functions cannot have a C variable argument list + ast_passes_const_and_c_variadic = functions cannot be both `const` and C-variadic .const = `const` because of this .variadic = C-variadic because of this diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 672c1e944188..c5c23c47e74e 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -704,12 +704,16 @@ impl<'a> AstValidator<'a> { { return; } - _ => {} + _ => { + self.dcx().emit_err(errors::BadCVariadic { span: variadic_param.span }); + } }, - FnCtxt::Assoc(_) => {} - }; - - self.dcx().emit_err(errors::BadCVariadic { span: variadic_param.span }); + FnCtxt::Assoc(_) => { + // For now, C variable argument lists are unsupported in associated functions. + let err = errors::CVariadicAssociatedFunction { span: variadic_param.span }; + self.dcx().emit_err(err); + } + } } fn check_item_named(&self, ident: Ident, kind: &str) { diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index f8806ef21c16..a085e09fe948 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -318,6 +318,13 @@ pub(crate) struct ExternItemAscii { pub block: Span, } +#[derive(Diagnostic)] +#[diag(ast_passes_c_variadic_associated_function)] +pub(crate) struct CVariadicAssociatedFunction { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(ast_passes_bad_c_variadic)] pub(crate) struct BadCVariadic { diff --git a/tests/ui/feature-gates/feature-gate-c_variadic.rs b/tests/ui/feature-gates/feature-gate-c_variadic.rs index 45c688420931..88d91dbd081f 100644 --- a/tests/ui/feature-gates/feature-gate-c_variadic.rs +++ b/tests/ui/feature-gates/feature-gate-c_variadic.rs @@ -1,10 +1,10 @@ -#![crate_type="lib"] +#![crate_type = "lib"] -pub unsafe extern "C" fn test(_: i32, ap: ...) { } +pub unsafe extern "C" fn test(_: i32, ap: ...) {} //~^ ERROR C-variadic functions are unstable trait Trait { - unsafe extern "C" fn trait_test(_: i32, ap: ...) { } + unsafe extern "C" fn trait_test(_: i32, ap: ...) {} //~^ ERROR C-variadic functions are unstable - //~| ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention + //~| ERROR associated functions cannot have a C variable argument list } diff --git a/tests/ui/feature-gates/feature-gate-c_variadic.stderr b/tests/ui/feature-gates/feature-gate-c_variadic.stderr index e30a2f1ede36..808aa20948d0 100644 --- a/tests/ui/feature-gates/feature-gate-c_variadic.stderr +++ b/tests/ui/feature-gates/feature-gate-c_variadic.stderr @@ -1,14 +1,14 @@ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +error: associated functions cannot have a C variable argument list --> $DIR/feature-gate-c_variadic.rs:7:45 | -LL | unsafe extern "C" fn trait_test(_: i32, ap: ...) { } +LL | unsafe extern "C" fn trait_test(_: i32, ap: ...) {} | ^^^^^^^ error[E0658]: C-variadic functions are unstable --> $DIR/feature-gate-c_variadic.rs:3:1 | -LL | pub unsafe extern "C" fn test(_: i32, ap: ...) { } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | pub unsafe extern "C" fn test(_: i32, ap: ...) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #44930 for more information = help: add `#![feature(c_variadic)]` to the crate attributes to enable @@ -17,8 +17,8 @@ LL | pub unsafe extern "C" fn test(_: i32, ap: ...) { } error[E0658]: C-variadic functions are unstable --> $DIR/feature-gate-c_variadic.rs:7:5 | -LL | unsafe extern "C" fn trait_test(_: i32, ap: ...) { } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe extern "C" fn trait_test(_: i32, ap: ...) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #44930 for more information = help: add `#![feature(c_variadic)]` to the crate attributes to enable diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs index 243924e6c53e..e76bdc21fa04 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs @@ -50,30 +50,30 @@ struct X; impl X { fn i_f1(x: isize, ...) {} - //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention + //~^ ERROR associated functions cannot have a C variable argument list fn i_f2(...) {} - //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention + //~^ ERROR associated functions cannot have a C variable argument list fn i_f3(..., x: isize, ...) {} - //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention + //~^ ERROR associated functions cannot have a C variable argument list //~| ERROR `...` must be the last argument of a C-variadic function fn i_f4(..., x: isize, ...) {} - //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention + //~^ ERROR associated functions cannot have a C variable argument list //~| ERROR `...` must be the last argument of a C-variadic function const fn i_f5(x: isize, ...) {} - //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention + //~^ ERROR associated functions cannot have a C variable argument list //~| ERROR functions cannot be both `const` and C-variadic //~| ERROR destructor of `VaListImpl<'_>` cannot be evaluated at compile-time } trait T { fn t_f1(x: isize, ...) {} - //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention + //~^ ERROR associated functions cannot have a C variable argument list fn t_f2(x: isize, ...); - //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention + //~^ ERROR associated functions cannot have a C variable argument list fn t_f3(...) {} - //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention + //~^ ERROR associated functions cannot have a C variable argument list fn t_f4(...); - //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention + //~^ ERROR associated functions cannot have a C variable argument list fn t_f5(..., x: isize) {} //~^ ERROR `...` must be the last argument of a C-variadic function fn t_f6(..., x: isize); diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr index 5c55cc38b568..1317f63a88ad 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr @@ -88,13 +88,13 @@ error: `...` must be the last argument of a C-variadic function LL | fn e_f2(..., x: isize); | ^^^ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +error: associated functions cannot have a C variable argument list --> $DIR/variadic-ffi-semantic-restrictions.rs:52:23 | LL | fn i_f1(x: isize, ...) {} | ^^^ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +error: associated functions cannot have a C variable argument list --> $DIR/variadic-ffi-semantic-restrictions.rs:54:13 | LL | fn i_f2(...) {} @@ -106,7 +106,7 @@ error: `...` must be the last argument of a C-variadic function LL | fn i_f3(..., x: isize, ...) {} | ^^^ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +error: associated functions cannot have a C variable argument list --> $DIR/variadic-ffi-semantic-restrictions.rs:56:28 | LL | fn i_f3(..., x: isize, ...) {} @@ -118,7 +118,7 @@ error: `...` must be the last argument of a C-variadic function LL | fn i_f4(..., x: isize, ...) {} | ^^^ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +error: associated functions cannot have a C variable argument list --> $DIR/variadic-ffi-semantic-restrictions.rs:59:28 | LL | fn i_f4(..., x: isize, ...) {} @@ -132,31 +132,31 @@ LL | const fn i_f5(x: isize, ...) {} | | | `const` because of this -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +error: associated functions cannot have a C variable argument list --> $DIR/variadic-ffi-semantic-restrictions.rs:62:29 | LL | const fn i_f5(x: isize, ...) {} | ^^^ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +error: associated functions cannot have a C variable argument list --> $DIR/variadic-ffi-semantic-restrictions.rs:69:23 | LL | fn t_f1(x: isize, ...) {} | ^^^ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +error: associated functions cannot have a C variable argument list --> $DIR/variadic-ffi-semantic-restrictions.rs:71:23 | LL | fn t_f2(x: isize, ...); | ^^^ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +error: associated functions cannot have a C variable argument list --> $DIR/variadic-ffi-semantic-restrictions.rs:73:13 | LL | fn t_f3(...) {} | ^^^ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +error: associated functions cannot have a C variable argument list --> $DIR/variadic-ffi-semantic-restrictions.rs:75:13 | LL | fn t_f4(...); From 7075000ae72c3ead855dc660a37c6bf263b7b452 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 6 Sep 2025 17:19:01 +0200 Subject: [PATCH 270/682] clarify control flow of `check_c_variadic_type` --- .../rustc_ast_passes/src/ast_validation.rs | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index c5c23c47e74e..79947b514b10 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -697,14 +697,23 @@ impl<'a> AstValidator<'a> { match fn_ctxt { FnCtxt::Foreign => return, FnCtxt::Free => match sig.header.ext { - Extern::Explicit(StrLit { symbol_unescaped: sym::C, .. }, _) - | Extern::Explicit(StrLit { symbol_unescaped: sym::C_dash_unwind, .. }, _) - | Extern::Implicit(_) - if matches!(sig.header.safety, Safety::Unsafe(_)) => - { - return; + Extern::Implicit(_) => { + if matches!(sig.header.safety, Safety::Unsafe(_)) { + return; + } + + self.dcx().emit_err(errors::BadCVariadic { span: variadic_param.span }); } - _ => { + Extern::Explicit(StrLit { symbol_unescaped, .. }, _) => { + if matches!(symbol_unescaped, sym::C | sym::C_dash_unwind) { + if matches!(sig.header.safety, Safety::Unsafe(_)) { + return; + } + } + + self.dcx().emit_err(errors::BadCVariadic { span: variadic_param.span }); + } + Extern::None => { self.dcx().emit_err(errors::BadCVariadic { span: variadic_param.span }); } }, From f7dbd752fd9abcd419ee6bdf1cccd5e806e2d6e7 Mon Sep 17 00:00:00 2001 From: rustbot <47979223+rustbot@users.noreply.github.com> Date: Mon, 8 Sep 2025 19:01:31 +0200 Subject: [PATCH 271/682] Update books --- src/doc/nomicon | 2 +- src/doc/reference | 2 +- src/doc/rust-by-example | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/nomicon b/src/doc/nomicon index 57ed44736605..f17a018b9989 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit 57ed4473660565d9357fcae176b358d7e8724ebf +Subproject commit f17a018b9989430967d1c58e9a12c51169abc744 diff --git a/src/doc/reference b/src/doc/reference index 89f67b3c1b90..b3ce60628c6f 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit 89f67b3c1b904cbcd9ed55e443d6fc67c8ca2769 +Subproject commit b3ce60628c6f55ab8ff3dba9f3d20203df1c0dee diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example index ad27f82c1846..dd26bc8e726d 160000 --- a/src/doc/rust-by-example +++ b/src/doc/rust-by-example @@ -1 +1 @@ -Subproject commit ad27f82c18464525c761a4a8db2e01785da59e1f +Subproject commit dd26bc8e726dc2e73534c8972d4dccd1bed7495f From 2b9fce8c8cf4373ee9da938ad5eeb97e85ca2739 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 6 Sep 2025 17:23:34 +0200 Subject: [PATCH 272/682] c-variadic: reject non-extern functions --- compiler/rustc_ast_passes/messages.ftl | 2 ++ .../rustc_ast_passes/src/ast_validation.rs | 3 ++- compiler/rustc_ast_passes/src/errors.rs | 7 +++++++ tests/ui/c-variadic/issue-86053-1.rs | 2 +- tests/ui/c-variadic/issue-86053-1.stderr | 2 +- .../issue-83499-input-output-iteration-ice.rs | 5 ++--- ...ue-83499-input-output-iteration-ice.stderr | 20 +++++++------------ .../variadic-ffi-semantic-restrictions.rs | 4 ++-- .../variadic-ffi-semantic-restrictions.stderr | 4 ++-- 9 files changed, 26 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 6eb4e4bc4527..958797a60894 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -68,6 +68,8 @@ ast_passes_bound_in_context = bounds on `type`s in {$ctx} have no effect ast_passes_c_variadic_associated_function = associated functions cannot have a C variable argument list +ast_passes_c_variadic_no_extern = `...` is not supported for non-extern functions + ast_passes_const_and_c_variadic = functions cannot be both `const` and C-variadic .const = `const` because of this .variadic = C-variadic because of this diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 79947b514b10..bbba235a6d16 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -714,7 +714,8 @@ impl<'a> AstValidator<'a> { self.dcx().emit_err(errors::BadCVariadic { span: variadic_param.span }); } Extern::None => { - self.dcx().emit_err(errors::BadCVariadic { span: variadic_param.span }); + let err = errors::CVariadicNoExtern { span: variadic_param.span }; + self.dcx().emit_err(err); } }, FnCtxt::Assoc(_) => { diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index a085e09fe948..6b57e5ef3761 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -325,6 +325,13 @@ pub(crate) struct CVariadicAssociatedFunction { pub span: Span, } +#[derive(Diagnostic)] +#[diag(ast_passes_c_variadic_no_extern)] +pub(crate) struct CVariadicNoExtern { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(ast_passes_bad_c_variadic)] pub(crate) struct BadCVariadic { diff --git a/tests/ui/c-variadic/issue-86053-1.rs b/tests/ui/c-variadic/issue-86053-1.rs index 537d0263adf7..58dfee55cf88 100644 --- a/tests/ui/c-variadic/issue-86053-1.rs +++ b/tests/ui/c-variadic/issue-86053-1.rs @@ -13,6 +13,6 @@ fn ordering4 < 'a , 'b > ( a : , self , self , self , //~| ERROR unexpected `self` parameter in function //~| ERROR unexpected `self` parameter in function //~| ERROR `...` must be the last argument of a C-variadic function - //~| ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention + //~| ERROR `...` is not supported for non-extern functions //~| ERROR cannot find type `F` in this scope } diff --git a/tests/ui/c-variadic/issue-86053-1.stderr b/tests/ui/c-variadic/issue-86053-1.stderr index b58016b5a816..eb08370b4f80 100644 --- a/tests/ui/c-variadic/issue-86053-1.stderr +++ b/tests/ui/c-variadic/issue-86053-1.stderr @@ -46,7 +46,7 @@ error: `...` must be the last argument of a C-variadic function LL | self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize ) { | ^^^ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +error: `...` is not supported for non-extern functions --> $DIR/issue-86053-1.rs:11:36 | LL | self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize ) { diff --git a/tests/ui/mir/issue-83499-input-output-iteration-ice.rs b/tests/ui/mir/issue-83499-input-output-iteration-ice.rs index 9277994d9b30..dc0d14bf9d6b 100644 --- a/tests/ui/mir/issue-83499-input-output-iteration-ice.rs +++ b/tests/ui/mir/issue-83499-input-output-iteration-ice.rs @@ -4,7 +4,6 @@ fn main() {} -fn foo(_: Bar, ...) -> impl {} -//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention -//~| ERROR cannot find type `Bar` in this scope +unsafe extern "C" fn foo(_: Bar, ...) -> impl {} +//~^ ERROR cannot find type `Bar` in this scope //~| ERROR at least one trait must be specified diff --git a/tests/ui/mir/issue-83499-input-output-iteration-ice.stderr b/tests/ui/mir/issue-83499-input-output-iteration-ice.stderr index 4a1aa49eb6e6..31a393e7367a 100644 --- a/tests/ui/mir/issue-83499-input-output-iteration-ice.stderr +++ b/tests/ui/mir/issue-83499-input-output-iteration-ice.stderr @@ -1,21 +1,15 @@ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/issue-83499-input-output-iteration-ice.rs:7:16 - | -LL | fn foo(_: Bar, ...) -> impl {} - | ^^^ - error: at least one trait must be specified - --> $DIR/issue-83499-input-output-iteration-ice.rs:7:24 + --> $DIR/issue-83499-input-output-iteration-ice.rs:7:42 | -LL | fn foo(_: Bar, ...) -> impl {} - | ^^^^ +LL | unsafe extern "C" fn foo(_: Bar, ...) -> impl {} + | ^^^^ error[E0412]: cannot find type `Bar` in this scope - --> $DIR/issue-83499-input-output-iteration-ice.rs:7:11 + --> $DIR/issue-83499-input-output-iteration-ice.rs:7:29 | -LL | fn foo(_: Bar, ...) -> impl {} - | ^^^ not found in this scope +LL | unsafe extern "C" fn foo(_: Bar, ...) -> impl {} + | ^^^ not found in this scope -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0412`. diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs index e76bdc21fa04..fe5421ab2c0a 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs @@ -4,10 +4,10 @@ fn main() {} fn f1_1(x: isize, ...) {} -//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +//~^ ERROR `...` is not supported for non-extern functions fn f1_2(...) {} -//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +//~^ ERROR `...` is not supported for non-extern functions extern "C" fn f2_1(x: isize, ...) {} //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr index 1317f63a88ad..0abe15264053 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr @@ -1,10 +1,10 @@ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +error: `...` is not supported for non-extern functions --> $DIR/variadic-ffi-semantic-restrictions.rs:6:19 | LL | fn f1_1(x: isize, ...) {} | ^^^ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +error: `...` is not supported for non-extern functions --> $DIR/variadic-ffi-semantic-restrictions.rs:9:9 | LL | fn f1_2(...) {} From 2912efa879912b56f4676eaa3024382b040144f5 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Mon, 8 Sep 2025 19:07:35 +0200 Subject: [PATCH 273/682] c-variadic: update cmse-nonsecure example --- .../cmse-nonsecure-entry/generics.rs | 10 +++----- .../cmse-nonsecure-entry/generics.stderr | 24 +++++++++---------- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs index ad4fca8252d2..4ebc2bbf7612 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs @@ -53,9 +53,7 @@ extern "cmse-nonsecure-entry" fn trait_object(x: &dyn Trait) -> &dyn Trait { x } -extern "cmse-nonsecure-entry" fn static_trait_object( - x: &'static dyn Trait, -) -> &'static dyn Trait { +extern "cmse-nonsecure-entry" fn static_trait_object(x: &'static dyn Trait) -> &'static dyn Trait { //~^ ERROR return value of `"cmse-nonsecure-entry"` function too large to pass via registers [E0798] x } @@ -63,14 +61,12 @@ extern "cmse-nonsecure-entry" fn static_trait_object( #[repr(transparent)] struct WrapperTransparent<'a>(&'a dyn Trait); -extern "cmse-nonsecure-entry" fn wrapped_trait_object( - x: WrapperTransparent, -) -> WrapperTransparent { +extern "cmse-nonsecure-entry" fn wrapped_trait_object(x: WrapperTransparent) -> WrapperTransparent { //~^ ERROR return value of `"cmse-nonsecure-entry"` function too large to pass via registers [E0798] x } -extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { +unsafe extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention //~| ERROR requires `va_list` lang_item } diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr index 7aeb6969feb9..55921e764ba2 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr @@ -1,8 +1,8 @@ error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/generics.rs:73:53 + --> $DIR/generics.rs:69:60 | -LL | extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { - | ^^^^^^ +LL | unsafe extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { + | ^^^^^^ error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type --> $DIR/generics.rs:30:1 @@ -50,28 +50,28 @@ LL | extern "cmse-nonsecure-entry" fn trait_object(x: &dyn Trait) -> &dyn Trait = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"cmse-nonsecure-entry"` function too large to pass via registers - --> $DIR/generics.rs:58:6 + --> $DIR/generics.rs:56:80 | -LL | ) -> &'static dyn Trait { - | ^^^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers +LL | extern "cmse-nonsecure-entry" fn static_trait_object(x: &'static dyn Trait) -> &'static dyn Trait { + | ^^^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers | = note: functions with the `"cmse-nonsecure-entry"` ABI must pass their result via the available return registers = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"cmse-nonsecure-entry"` function too large to pass via registers - --> $DIR/generics.rs:68:6 + --> $DIR/generics.rs:64:81 | -LL | ) -> WrapperTransparent { - | ^^^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers +LL | extern "cmse-nonsecure-entry" fn wrapped_trait_object(x: WrapperTransparent) -> WrapperTransparent { + | ^^^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers | = note: functions with the `"cmse-nonsecure-entry"` ABI must pass their result via the available return registers = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error: requires `va_list` lang_item - --> $DIR/generics.rs:73:53 + --> $DIR/generics.rs:69:60 | -LL | extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { - | ^^^^^^ +LL | unsafe extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { + | ^^^^^^ error: aborting due to 9 previous errors From 5de9bc73e76f60ee27c37f20d0a9530e5a957df7 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Mon, 7 Jul 2025 11:52:14 +0200 Subject: [PATCH 274/682] Refactor how to get the span of a function header --- compiler/rustc_ast/src/ast.rs | 67 +++++++++---------- .../rustc_ast_passes/src/ast_validation.rs | 2 +- 2 files changed, 32 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 802a6fa32498..2733a2603d6d 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2284,6 +2284,33 @@ pub struct FnSig { pub span: Span, } +impl FnSig { + /// Return a span encompassing the header, or where to insert it if empty. + pub fn header_span(&self) -> Span { + match self.header.ext { + Extern::Implicit(span) | Extern::Explicit(_, span) => { + return self.span.with_hi(span.hi()); + } + Extern::None => {} + } + + match self.header.safety { + Safety::Unsafe(span) | Safety::Safe(span) => return self.span.with_hi(span.hi()), + Safety::Default => {} + }; + + if let Some(coroutine_kind) = self.header.coroutine_kind { + return self.span.with_hi(coroutine_kind.span().hi()); + } + + if let Const::Yes(span) = self.header.constness { + return self.span.with_hi(span.hi()); + } + + self.span.shrink_to_lo() + } +} + /// A constraint on an associated item. /// /// ### Examples @@ -3534,12 +3561,12 @@ impl Extern { /// included in this struct (e.g., `async unsafe fn` or `const extern "C" fn`). #[derive(Clone, Copy, Encodable, Decodable, Debug, Walkable)] pub struct FnHeader { - /// Whether this is `unsafe`, or has a default safety. - pub safety: Safety, - /// Whether this is `async`, `gen`, or nothing. - pub coroutine_kind: Option, /// The `const` keyword, if any pub constness: Const, + /// Whether this is `async`, `gen`, or nothing. + pub coroutine_kind: Option, + /// Whether this is `unsafe`, or has a default safety. + pub safety: Safety, /// The `extern` keyword and corresponding ABI string, if any. pub ext: Extern, } @@ -3553,38 +3580,6 @@ impl FnHeader { || matches!(constness, Const::Yes(_)) || !matches!(ext, Extern::None) } - - /// Return a span encompassing the header, or none if all options are default. - pub fn span(&self) -> Option { - fn append(a: &mut Option, b: Span) { - *a = match a { - None => Some(b), - Some(x) => Some(x.to(b)), - } - } - - let mut full_span = None; - - match self.safety { - Safety::Unsafe(span) | Safety::Safe(span) => append(&mut full_span, span), - Safety::Default => {} - }; - - if let Some(coroutine_kind) = self.coroutine_kind { - append(&mut full_span, coroutine_kind.span()); - } - - if let Const::Yes(span) = self.constness { - append(&mut full_span, span); - } - - match self.ext { - Extern::Implicit(span) | Extern::Explicit(_, span) => append(&mut full_span, span), - Extern::None => {} - } - - full_span - } } impl Default for FnHeader { diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index bbba235a6d16..f425acda3583 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -492,7 +492,7 @@ impl<'a> AstValidator<'a> { } if !spans.is_empty() { - let header_span = sig.header.span().unwrap_or(sig.span.shrink_to_lo()); + let header_span = sig.header_span(); let suggestion_span = header_span.shrink_to_hi().to(sig.decl.output.span()); let padding = if header_span.is_empty() { "" } else { " " }; From 43a6f56ca78295164dbf964695a43be7e35b1994 Mon Sep 17 00:00:00 2001 From: IoaNNUwU Date: Mon, 8 Sep 2025 19:15:35 +0200 Subject: [PATCH 275/682] Apply requested changes --- compiler/rustc_builtin_macros/src/format.rs | 46 +++++++------------ tests/ui/fmt/ifmt-bad-arg.stderr | 6 +-- tests/ui/macros/format-unused-lables.stderr | 9 ++-- tests/ui/mir/unsized-extern-static.stderr | 3 +- .../missing-format-specifiers-issue-68293.rs | 4 +- ...ssing-format-specifiers-issue-68293.stderr | 6 +-- 6 files changed, 26 insertions(+), 48 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index e5f73188dbdf..a6c8e7d29cc7 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -768,38 +768,24 @@ fn report_missing_placeholders( // Show example if user didn't use any format specifiers let show_example = used.iter().all(|used| !used); - if !show_example && unused.len() > 1 { - diag.note(format!("consider adding {} format specifiers", unused.len())); - } - - let original_fmt_str = if fmt_str.len() >= 1 { &fmt_str[..fmt_str.len() - 1] } else { "" }; - - if show_example && unused.len() == 1 { - diag.note("format specifiers use curly braces: `{}`"); - - diag.span_suggestion_verbose( - fmt_span, - "consider adding format specifier", - format!("\"{}{{}}\"", original_fmt_str), - Applicability::MaybeIncorrect, - ); - } - - if show_example && unused.len() > 1 { - diag.note("format specifiers use curly braces: `{}`"); - - let mut suggest_fixed_fmt = format!("\"{}", original_fmt_str); - for _ in &unused { - suggest_fixed_fmt.push_str("{}"); + if !show_example { + if unused.len() > 1 { + diag.note(format!("consider adding {} format specifiers", unused.len())); } - suggest_fixed_fmt.push('"'); + } else { + let original_fmt_str = + if fmt_str.len() >= 1 { &fmt_str[..fmt_str.len() - 1] } else { "" }; - diag.span_suggestion_verbose( - fmt_span, - format!("consider adding {} format specifiers", unused.len()), - suggest_fixed_fmt, - Applicability::MaybeIncorrect, - ); + let msg = if unused.len() == 1 { + "a format specifier".to_string() + } else { + format!("{} format specifiers", unused.len()) + }; + + let sugg = format!("\"{}{}\"", original_fmt_str, "{}".repeat(unused.len())); + let msg = format!("format specifiers use curly braces, consider adding {msg}"); + + diag.span_suggestion_verbose(fmt_span, msg, sugg, Applicability::MaybeIncorrect); } } diff --git a/tests/ui/fmt/ifmt-bad-arg.stderr b/tests/ui/fmt/ifmt-bad-arg.stderr index 6ddc81729015..b565b836f211 100644 --- a/tests/ui/fmt/ifmt-bad-arg.stderr +++ b/tests/ui/fmt/ifmt-bad-arg.stderr @@ -63,8 +63,7 @@ LL | format!("", 1, 2); | | argument never used | multiple missing formatting specifiers | - = note: format specifiers use curly braces: `{}` -help: consider adding 2 format specifiers +help: format specifiers use curly braces, consider adding 2 format specifiers | LL | format!("{}{}", 1, 2); | ++++ @@ -109,8 +108,7 @@ LL | format!("", foo=2); | | | formatting specifier missing | - = note: format specifiers use curly braces: `{}` -help: consider adding format specifier +help: format specifiers use curly braces, consider adding a format specifier | LL | format!("{}", foo=2); | ++ diff --git a/tests/ui/macros/format-unused-lables.stderr b/tests/ui/macros/format-unused-lables.stderr index 1c5cdcc2346d..90eed8dd86b5 100644 --- a/tests/ui/macros/format-unused-lables.stderr +++ b/tests/ui/macros/format-unused-lables.stderr @@ -8,8 +8,7 @@ LL | println!("Test", 123, 456, 789); | | argument never used | multiple missing formatting specifiers | - = note: format specifiers use curly braces: `{}` -help: consider adding 3 format specifiers +help: format specifiers use curly braces, consider adding 3 format specifiers | LL | println!("Test{}{}{}", 123, 456, 789); | ++++++ @@ -26,8 +25,7 @@ LL | 456, LL | 789 | ^^^ argument never used | - = note: format specifiers use curly braces: `{}` -help: consider adding 3 format specifiers +help: format specifiers use curly braces, consider adding 3 format specifiers | LL | println!("Test2{}{}{}", | ++++++ @@ -40,8 +38,7 @@ LL | println!("Some stuff", UNUSED="args"); | | | formatting specifier missing | - = note: format specifiers use curly braces: `{}` -help: consider adding format specifier +help: format specifiers use curly braces, consider adding a format specifier | LL | println!("Some stuff{}", UNUSED="args"); | ++ diff --git a/tests/ui/mir/unsized-extern-static.stderr b/tests/ui/mir/unsized-extern-static.stderr index 005dc59d78f9..c0810e650ef1 100644 --- a/tests/ui/mir/unsized-extern-static.stderr +++ b/tests/ui/mir/unsized-extern-static.stderr @@ -6,8 +6,7 @@ LL | println!("C", unsafe { &symbol }); | | | formatting specifier missing | - = note: format specifiers use curly braces: `{}` -help: consider adding format specifier +help: format specifiers use curly braces, consider adding a format specifier | LL | println!("C{}", unsafe { &symbol }); | ++ diff --git a/tests/ui/suggestions/missing-format-specifiers-issue-68293.rs b/tests/ui/suggestions/missing-format-specifiers-issue-68293.rs index 2f5e96294415..29799624d782 100644 --- a/tests/ui/suggestions/missing-format-specifiers-issue-68293.rs +++ b/tests/ui/suggestions/missing-format-specifiers-issue-68293.rs @@ -2,18 +2,18 @@ fn no_format_specifier_two_unused_args() { println!("Hello", "World"); //~^ ERROR argument never used //~| NOTE formatting specifier missing - //~| NOTE format specifiers use curly braces: `{}` //~| NOTE argument never used + //~| HELP format specifiers use curly braces, consider adding a format specifier } fn no_format_specifier_multiple_unused_args() { println!("list: ", 1, 2, 3); //~^ ERROR multiple unused formatting arguments //~| NOTE multiple missing formatting specifiers - //~| NOTE format specifiers use curly braces: `{}` //~| NOTE argument never used //~| NOTE argument never used //~| NOTE argument never used + //~| HELP format specifiers use curly braces, consider adding 3 format specifiers } fn missing_format_specifiers_one_unused_arg() { diff --git a/tests/ui/suggestions/missing-format-specifiers-issue-68293.stderr b/tests/ui/suggestions/missing-format-specifiers-issue-68293.stderr index 961887a7e3fa..081409789f5a 100644 --- a/tests/ui/suggestions/missing-format-specifiers-issue-68293.stderr +++ b/tests/ui/suggestions/missing-format-specifiers-issue-68293.stderr @@ -6,8 +6,7 @@ LL | println!("Hello", "World"); | | | formatting specifier missing | - = note: format specifiers use curly braces: `{}` -help: consider adding format specifier +help: format specifiers use curly braces, consider adding a format specifier | LL | println!("Hello{}", "World"); | ++ @@ -22,8 +21,7 @@ LL | println!("list: ", 1, 2, 3); | | argument never used | multiple missing formatting specifiers | - = note: format specifiers use curly braces: `{}` -help: consider adding 3 format specifiers +help: format specifiers use curly braces, consider adding 3 format specifiers | LL | println!("list: {}{}{}", 1, 2, 3); | ++++++ From 8b58777968a4c663d6c1293bacff6da99e6e5203 Mon Sep 17 00:00:00 2001 From: nixxo Date: Thu, 4 Sep 2025 09:33:11 +0200 Subject: [PATCH 276/682] fix partial urlencoded link support - added full urlencoding to properly check urlencoded anchor links against non-urlencoded heading IDs - added tests urlecoding provided by https://crates.io/crates/urlencoding --- Cargo.lock | 7 +++++++ src/tools/linkchecker/Cargo.toml | 1 + src/tools/linkchecker/main.rs | 13 +------------ src/tools/linkchecker/tests/valid/inner/bar.html | 3 +++ src/tools/linkchecker/tests/valid/inner/foo.html | 8 ++++++++ .../linkchecker/tests/valid/inner/redir-target.html | 3 +++ 6 files changed, 23 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 52f504811575..fd4b661c40de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2167,6 +2167,7 @@ version = "0.1.0" dependencies = [ "html5ever", "regex", + "urlencoding", ] [[package]] @@ -5825,6 +5826,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "utf-8" version = "0.7.6" diff --git a/src/tools/linkchecker/Cargo.toml b/src/tools/linkchecker/Cargo.toml index fb5bff3fe63f..f0886e31b243 100644 --- a/src/tools/linkchecker/Cargo.toml +++ b/src/tools/linkchecker/Cargo.toml @@ -10,3 +10,4 @@ path = "main.rs" [dependencies] regex = "1" html5ever = "0.29.0" +urlencoding = "2.1.3" diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs index 1dc45728c90c..e07a0784cdb3 100644 --- a/src/tools/linkchecker/main.rs +++ b/src/tools/linkchecker/main.rs @@ -232,18 +232,7 @@ enum FileEntry { type Cache = HashMap; fn small_url_encode(s: &str) -> String { - s.replace('<', "%3C") - .replace('>', "%3E") - .replace(' ', "%20") - .replace('?', "%3F") - .replace('\'', "%27") - .replace('&', "%26") - .replace(',', "%2C") - .replace(':', "%3A") - .replace(';', "%3B") - .replace('[', "%5B") - .replace(']', "%5D") - .replace('\"', "%22") + urlencoding::encode(s).to_string() } impl Checker { diff --git a/src/tools/linkchecker/tests/valid/inner/bar.html b/src/tools/linkchecker/tests/valid/inner/bar.html index 4b500d78b76e..6ffda259c40e 100644 --- a/src/tools/linkchecker/tests/valid/inner/bar.html +++ b/src/tools/linkchecker/tests/valid/inner/bar.html @@ -3,5 +3,8 @@